diff --git a/src/autocomplete.js b/src/autocomplete.js index 520c74ec070..1076c157da6 100644 --- a/src/autocomplete.js +++ b/src/autocomplete.js @@ -1029,7 +1029,6 @@ class FilteredList { filterCompletions(items, needle) { var results = []; - var upper = needle.toUpperCase(); var lower = needle.toLowerCase(); loop: for (var i = 0, item; item = items[i]; i++) { var caption = (!this.ignoreCaption && item.caption) || item.value || item.snippet; @@ -1043,23 +1042,32 @@ class FilteredList { if (needle !== caption.substr(0, needle.length)) continue loop; } else { + var captionLower = caption.toLowerCase(); /** * It is for situation then, for example, we find some like 'tab' in item.value="Check the table" * and want to see "Check the TABle" but see "Check The tABle". */ - var fullMatchIndex = caption.toLowerCase().indexOf(lower); + var fullMatchIndex = captionLower.indexOf(lower); if (fullMatchIndex > -1) { penalty = fullMatchIndex; + for (var j = 0; j < needle.length; j++) { + index = fullMatchIndex + j; + // Adding a penalty on case mismatch + if ((lower[j] == needle[j]) != (captionLower[index] == caption[index])) { + penalty += 3; + } + } } else { // caption char iteration is faster in Chrome but slower in Firefox, so lets use indexOf for (var j = 0; j < needle.length; j++) { - // TODO add penalty on case mismatch - var i1 = caption.indexOf(lower[j], lastIndex + 1); - var i2 = caption.indexOf(upper[j], lastIndex + 1); - index = (i1 >= 0) ? ((i2 < 0 || i1 < i2) ? i1 : i2) : i2; + index = captionLower.indexOf(lower[j], lastIndex + 1); if (index < 0) continue loop; distance = index - lastIndex - 1; + // Adding a penalty on case mismatch + if ((lower[j] == needle[j]) != (captionLower[index] == caption[index])) { + penalty += 3; + } if (distance > 0) { // first char mismatch should be more sensitive if (lastIndex === -1) diff --git a/src/autocomplete_test.js b/src/autocomplete_test.js index b48ec080b21..f5ef2785ce7 100644 --- a/src/autocomplete_test.js +++ b/src/autocomplete_test.js @@ -1542,6 +1542,49 @@ module.exports = { // Popup should be closed now assert.equal(completer.popup.isOpen, false); + }, + "test: penalty on case mismatch": function (done) { + var editor = initEditor(""); + editor.completers = [ + { + getCompletions: function (editor, session, pos, prefix, callback) { + var completions = [ + { + caption: "array", + value: "array" + }, { + caption: "aRray", + value: "aRray" + } + ]; + callback(null, completions); + } + } + ]; + + sendKey("R"); + var popup = editor.completer.popup; + + check(function() { + assert.equal(popup.data.length, 2); + assert.equal(popup.container.querySelector(".ace_selected").textContent.trim(), "aRray"); + + sendKey("y"); + + check(function() { + assert.equal(popup.container.querySelector(".ace_selected").textContent.trim(), "aRray"); + + editor.destroy(); + editor.container.remove(); + done(); + }); + }); + + function check(callback) { + setTimeout(function wait() { + callback(); + }, 10); + } } };