diff --git a/src/autocomplete.js b/src/autocomplete.js index 5dadc4e5a4b..baff2b17218 100644 --- a/src/autocomplete.js +++ b/src/autocomplete.js @@ -666,8 +666,14 @@ class CompletionProvider { var matches = []; this.completers = editor.completers; - var total = editor.completers.length; - editor.completers.forEach(function(completer, i) { + var matchingCompleters = util.getCompletersMatchingTriggerCharacter(editor); + // if there are no matches or there is a prefix, and the prefix is longer than the threshold, + // then use all completers + if (matchingCompleters.length === 0 || (prefix && prefix.length >= editor.$liveAutocompletionThreshold)) { + matchingCompleters = editor.completers; + } + var total = matchingCompleters.length; + matchingCompleters.forEach(function(completer, i) { completer.getCompletions(editor, session, pos, prefix, function(err, results) { if (!err && results) matches = matches.concat(results); diff --git a/src/autocomplete/util.js b/src/autocomplete/util.js index 97228e94376..388c502d8e5 100644 --- a/src/autocomplete/util.js +++ b/src/autocomplete/util.js @@ -55,12 +55,12 @@ exports.getCompletionPrefix = function (editor) { return prefix || this.retrievePrecedingIdentifier(line, pos.column); }; -exports.triggerAutocomplete = function (editor) { +exports.getCompletersMatchingTriggerCharacter = function (editor) { var pos = editor.getCursorPosition(); var line = editor.session.getLine(pos.row); var column = (pos.column === 0) ? 0 : pos.column - 1; var previousChar = line[column]; - return editor.completers.some((el) => { + return editor.completers.filter((el) => { if (el.triggerCharacters && Array.isArray(el.triggerCharacters)) { return el.triggerCharacters.includes(previousChar); } diff --git a/src/autocomplete_test.js b/src/autocomplete_test.js index 34a1e479e00..4799c484eed 100644 --- a/src/autocomplete_test.js +++ b/src/autocomplete_test.js @@ -330,6 +330,161 @@ module.exports = { }); } }, + "test: gets completions only from matching completers for trigger characters": function (done) { + var editor = initEditor("document"); + + var dotCompleterCalls = 0; + var twoBracketsCompleterCalls = 0; + var oneBracketCompleterCalls = 0; + + editor.completers = [ + { + getCompletions: function (editor, session, pos, prefix, callback) { + dotCompleterCalls++; + var completions = [ + { + caption: "append", + value: "append" + }, { + caption: "all", + value: "all" + } + ]; + callback(null, completions); + }, + triggerCharacters: ["."] + }, + { + getCompletions: function (editor, session, pos, prefix, callback) { + oneBracketCompleterCalls++; + var completions = [ + { + caption: "banana", + value: "banana" + }, { + caption: "apple", + value: "apple" + }, { + caption: "orange", + value: "orange" + } + ]; + callback(null, completions); + }, + triggerCharacters: ["{"] + }, + { + getCompletions: function (editor, session, pos, prefix, callback) { + twoBracketsCompleterCalls++; + var completions = [ + { + caption: "fruit", + value: "fruit" + } + ]; + callback(null, completions); + }, + triggerCharacters: ["{", "["] + } + ]; + + editor.moveCursorTo(0, 8); + sendKey("."); + var popup = editor.completer.popup; + check(function () { + assert.equal(popup.data.length, 2); + editor.onCommandKey(null, 0, 13); + assert.equal(editor.getValue(), "document.all"); + assert.equal(dotCompleterCalls, 1); + assert.equal(twoBracketsCompleterCalls, 0); + assert.equal(oneBracketCompleterCalls, 0); + + editor.moveCursorTo(0, 12); + sendKey("{"); + check(function () { + assert.equal(popup.data.length, 4); + editor.onCommandKey(null, 0, 13); + assert.equal(editor.getValue(), "document.all{apple"); + assert.equal(dotCompleterCalls, 1); + assert.equal(twoBracketsCompleterCalls, 1); + assert.equal(oneBracketCompleterCalls, 1); + + editor.moveCursorTo(0, 18); + sendKey("["); + check(function () { + assert.equal(popup.data.length, 1); + editor.onCommandKey(null, 0, 13); + assert.equal(editor.getValue(), "document.all{apple[fruit"); + assert.equal(dotCompleterCalls, 1); + assert.equal(twoBracketsCompleterCalls, 2); + assert.equal(oneBracketCompleterCalls, 1); + + done(); + }); + }); + }); + + function check(callback) { + popup = editor.completer.popup; + popup.renderer.on("afterRender", function wait() { + popup.renderer.off("afterRender", wait); + callback(); + }); + } + }, + "test: trigger all completers if there is a textual(A-Za-z) trigger character": function (done) { + var editor = initEditor(""); + editor.setOptions({ + liveAutocompletionThreshold: 0 + }); + + editor.completers = [ + { + getCompletions: function (editor, session, pos, prefix, callback) { + var completions = [ + { + caption: "agent", + value: "agent" + }, { + caption: "alarm", + value: "alarm" + } + ]; + callback(null, completions); + }, + triggerCharacters: ["a"] + }, + { + getCompletions: function (editor, session, pos, prefix, callback) { + var completions = [ + { + caption: "abbath", + value: "abbath" + } + ]; + callback(null, completions); + } + } + ]; + + editor.moveCursorTo(0, 0); + user.type("a"); + var popup = editor.completer.popup; + check(function () { + assert.equal(popup.data.length, 3); + editor.onCommandKey(null, 0, 13); + assert.equal(editor.getValue(), "abbath"); + done(); + }); + + function check(callback) { + popup = editor.completer.popup; + popup.renderer.on("afterRender", function wait() { + popup.renderer.off("afterRender", wait); + callback(); + }); + } + }, "test: empty message if no suggestions available": function(done) { var editor = initEditor(""); var emptyMessageText = "No suggestions."; diff --git a/src/ext/language_tools.js b/src/ext/language_tools.js index 0f6b9941b5e..0174e1f6c08 100644 --- a/src/ext/language_tools.js +++ b/src/ext/language_tools.js @@ -161,7 +161,7 @@ var showLiveAutocomplete = function(e) { var editor = e.editor; var prefix = util.getCompletionPrefix(editor); // Only autocomplete if there's a prefix that can be matched or previous char is trigger character - var triggerAutocomplete = util.triggerAutocomplete(editor); + var triggerAutocomplete = util.getCompletersMatchingTriggerCharacter(editor).length > 0; if ((prefix || triggerAutocomplete) && prefix.length >= editor.$liveAutocompletionThreshold) { var completer = Autocomplete.for(editor); // Set a flag for auto shown