diff options
Diffstat (limited to 'js/codeq')
-rw-r--r-- | js/codeq/hint.js | 149 |
1 files changed, 149 insertions, 0 deletions
diff --git a/js/codeq/hint.js b/js/codeq/hint.js new file mode 100644 index 0000000..ffae638 --- /dev/null +++ b/js/codeq/hint.js @@ -0,0 +1,149 @@ +/** + * Creates a hint handler, displaying hints inside the provided jqHints <div>. + */ + +(function () { + // constants + var firstCharacterPos = {'line': 0, 'ch': 0}, + sel_no_scroll = {'scroll': false}; + + codeq.makeHinter = function (jqHints, jqEditor, editor, hintDefs) { + var hintCounter = 0, // for generating unique class-names + hintCleaners = [], + clearHints = function () { + var i; + for (i = hintCleaners.length - 1; i >= 0; i--) { + hintCleaners[i](); + } + hintCleaners.length = 0; + hintCounter = 0; + }, + addMark = function (start, end) { + var posStart = editor.posFromIndex(start), + posEnd = editor.posFromIndex(end), + doc = editor.getDoc(), + mark = doc.markText(posStart, posEnd, {'className': 'editor-mark _emark_' + hintCounter}), + result = {'start': posStart, 'end': posEnd, 'mark': mark, 'className': '_emark_' + hintCounter}; + hintCleaners.push(function () { mark.clear(); mark = null; doc = null; result.mark = null; }); + hintCounter++; + return result; + }, + processTemplate = function (template, args) { + if (!args) + return template; + return template.replace(/\[%=(\w+)%\]/g, function(match, name) { + return args[name]; + }); + }, + typeHandlers = { + 'static': function (type, template, serverHint) { + codeq.log.debug('Processing static hint'); + var message = processTemplate(template, serverHint.args); + jqHints.append('<div class="hint-static">' + message + '</div>'); + // no hint cleaner here, a static hint remains on the screen + }, + + 'popup': function (type, template, serverHint) { + codeq.log.debug('Processing popup hint'); + var message = processTemplate(template, serverHint.args), + mark = addMark(serverHint.start, serverHint.end), // add the mark + jqMark = jqEditor.find('.' + mark.className); + + jqMark.popover({'content': message, 'html': true, 'placement': 'auto bottom', 'trigger': 'hover focus click', 'container': 'body'}); + hintCleaners.push(function () { if (jqMark) { jqMark.popover('destroy'); jqMark = null; } }); + + mark.mark.on('', function () {}); + }, + + 'dropdown': function (type, template, serverHint) { + codeq.log.debug('Processing dropdown hint'); + var completion = null, // the completion object, created in showHint() + close = function () { + if (completion) { + completion.close(); + completion = null; + } + }; + + if (/*(editor.listSelections().length > 1) ||*/ editor.somethingSelected()) { + // showHint() doesn't work if a selection is activeparts + editor.setSelection(firstCharacterPos, firstCharacterPos, sel_no_scroll); // deselect anything + } + + editor.showHint({ + hint: function () { + var hints = { + list: serverHint.choices, + from: editor.posFromIndex(serverHint.start), + to: editor.posFromIndex(serverHint.end) + }; + completion = editor.state.completionActive; + return hints; + }, + completeOnSingleClick: true, + completeSingle: false + }); + + hintCleaners.push(close); + } + }; + + return { + /** + * Processes and display appropriately the server hints. + * TODO: sort hints so static and popup hints come first, and a (single) drop-down hint last + * + * @param {ServerHint[]} serverHints an array of hints from the server + */ + 'handle': function (serverHints) { + var n = serverHints.length, + /** number */ i, + /** ServerHint */ serverHint, + /** HintDefinition */ hintDef, + hintType, hintTemplate, t, fn, indices; + clearHints(); + for (i = 0; i < n; i++) { + serverHint = serverHints[i]; + hintDef = hintDefs[serverHint.id]; + if (serverHint.indices) { + indices = serverHint.indices + for (i = 0; i < indices.length; i++) { + hintDef = hintDef[indices[i]]; + if (!hintDef) + break; + } + } + if (!hintDef) { + codeq.log.error('Undefined hint ' + serverHint.id + ' with indices ' + serverHint.indices); + continue; + } + t = typeof hintDef; + if (t === 'object') { + hintType = hintDef.type; + hintTemplate = hintDef.message; + } + else if (t === 'string') { + hintType = 'static'; + hintTemplate = hintDef; + } + else { + codeq.log.error('Unsupported hint definition: ' + t); + continue; + } + + fn = typeHandlers[hintType]; + if (!fn) codeq.log.error('Unsupported hint type: ' + hintType); + else fn(hintType, hintTemplate, serverHint); + } + }, + + 'destroy': function () { + clearHints(); + jqHints.empty(); + jqHints = null; + jqEditor = null; + editor = null; + } + }; + }; +})();
\ No newline at end of file |