Fix rerender on text insertion

This commit is contained in:
Garen Torikian 2013-01-04 15:07:23 -08:00 committed by nightwing
commit b811f1dc3f
4 changed files with 167 additions and 115 deletions

View file

@ -163,6 +163,8 @@ var Autocomplete = function() {
el.style.left = pos.left + "px";
el.style.display = "";
renderer.updateText();
};
this.detach = function() {
@ -173,10 +175,15 @@ var Autocomplete = function() {
if (this.popup)
this.popup.container.style.display = "none";
this.editor.Autocomplete.activated = false;
};
this.changeListener = function(e) {
//console.log(e)
if (this.editor.Autocomplete.activated)
Autocomplete.startCommand.exec(this.editor);
else
this.detach();
};
this.blurListener = function() {
@ -215,6 +222,8 @@ var Autocomplete = function() {
};
this.insertMatch = function(row) {
this.detach();
if (row == undefined)
row = this.popup.getRow();
var text = this.completions.filtered[row];
@ -224,7 +233,6 @@ var Autocomplete = function() {
// should be good enough, otherwise we can use getDocument().removeInLine
this.editor.removeWordLeft();
this.editor.insert(text);
this.detach();
};
this.commands = {
@ -237,16 +245,7 @@ var Autocomplete = function() {
"space": function(editor) { editor.Autocomplete.detach(); editor.insert(" ");},
"Return": function(editor) { editor.Autocomplete.insertMatch(); },
"Shift-Return": function(editor) { editor.Autocomplete.insertMatch(true); },
"Tab": function(editor) { editor.Autocomplete.insertMatch(); },
"backspace": function(editor) {
var doc = editor.session.getDocument(),
cursor = editor.getCursorPosition();
editor.Autocomplete.detach();
// delete one char, and reevaluate
editor.remove("left");
editor.Autocomplete.complete(editor);
}
"Tab": function(editor) { editor.Autocomplete.insertMatch(); }
};
this.complete = function(editor) {
@ -266,15 +265,19 @@ var Autocomplete = function() {
editor.on("blur", this.$blurListener);
editor.on("mousedown", this.$mousedownListener);
worker.attachToDocument(editor.session.getDocument(), {cursor: editor.getCursorPosition(), keywords: editor.session.getMode().getKeywords(true)}, true);
worker.attachToDocument(editor.session.getDocument(), {cursor: editor.getCursorPosition(), keywords: editor.session.getMode().getKeywords()}, true);
worker.on("complete", function(data) {
var matches = data.data.matches;
_self.completions = new FilteredList(matches);
_self.completions.setFilter("a");
if (matches.length) {
_self.openPopup(editor); }
_self.completions = new FilteredList(matches);
_self.completions.setFilter("a");
_self.openPopup(editor);
}
else {
_self.detach();
}
});
};
@ -377,6 +380,7 @@ Autocomplete.startCommand = {
if (!editor.Autocomplete)
editor.Autocomplete = new Autocomplete();
editor.Autocomplete.complete(editor);
editor.Autocomplete.activated = true;
},
bindKey: "Ctrl-Space|Shift-Space|Alt-Space"
}

View file

@ -45,29 +45,6 @@ var AutocompleteWorker = exports.AutocompleteWorker = function(sender) {
oop.inherits(AutocompleteWorker, Mirror);
(function() {
// For code completion
function removeDuplicateMatches(matches) {
// First sort
matches.sort(function(a, b) {
if (a.name < b.name)
return 1;
else if (a.name > b.name)
return -1;
else
return 0;
});
for(var i = 1; i < matches.length; ){
if (matches[i - 1] == matches[i]){
matches.splice(i, 1);
} else {
i++;
}
}
return matches;
};
this.onUpdate = function() {
var _self = this;
@ -75,43 +52,25 @@ oop.inherits(AutocompleteWorker, Mirror);
var pos = this.data.cursor;
var currentPos = { line: pos.row, col: pos.column };
var matches = [];
completer.complete(_self.doc, this.data.cursor, this.data.keywords, function(identifier, completions) {
if (completions)
matches = matches.concat(completions);
removeDuplicateMatches(matches);
// Sort by priority, score
matches.sort(function(a, b) {
if (a.priority < b.priority)
return 1;
else if (a.priority > b.priority)
return -1;
else if (a.score < b.score)
return 1;
else if (a.score > b.score)
return -1;
else if (a.id && a.id === b.id) {
if (a.isFunction)
return -1;
else if (b.isFunction)
return 1;
}
if (a.name < b.name)
return -1;
else if(a.name > b.name)
return 1;
else
return 0;
});
_self.sender.emit("complete", {
startRow: pos.row,
startColumn: pos.column - identifier.length,
endRow: pos.row,
endColumn: Infinity,
matches: matches,
line: _self.doc.getLine(pos.row)
});
if (!identifier) {
_self.sender.emit("complete", {
matches: []
});
}
else {
_self.sender.emit("complete", {
startRow: pos.row,
startColumn: pos.column - identifier.length,
endRow: pos.row,
endColumn: Infinity,
matches: completions,
line: _self.doc.getLine(pos.row)
});
}
return;
});
};

View file

@ -1,3 +1,33 @@
/* ***** BEGIN LICENSE BLOCK *****
* Distributed under the BSD license:
*
* Copyright (c) 2012, Ajax.org B.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Ajax.org B.V. nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* ***** END LICENSE BLOCK ***** */
define(function(require, exports, module) {
var ID_REGEX = /[a-zA-Z_0-9\$]/;
@ -26,36 +56,71 @@ function retrieveFollowingIdentifier(text, pos, regex) {
return buf;
}
function prefixBinarySearch(items, prefix) {
var startIndex = 0;
var stopIndex = items.length - 1;
var middle = Math.floor((stopIndex + startIndex) / 2);
while (stopIndex > startIndex && middle >= 0 && items[middle].indexOf(prefix) !== 0) {
if (prefix < items[middle]) {
stopIndex = middle - 1;
}
else if (prefix > items[middle]) {
startIndex = middle + 1;
}
middle = Math.floor((stopIndex + stopIndex) / 2);
}
// Look back to make sure we haven't skipped any
while (middle > 0 && items[middle-1].indexOf(prefix) === 0)
middle--;
return middle >= 0 ? middle : 0; // ensure we're not returning a negative index
}
// filched from jQuery
function grep( elems, callback, inv ) {
var retVal,
ret = [],
i = 0,
length = elems.length;
inv = !!inv;
// Go through the array, only saving the items
// that pass the validator function
for ( ; i < length; i++ ) {
retVal = !!callback( elems[ i ], i );
if ( inv !== retVal ) {
ret.push( elems[ i ] );
}
}
return ret;
};
function sortByScore(items, identDict) {
return items.sort(function(a, b) {
var scoreA = identDict[a],
scoreB = identDict[b];
if (a < b)
return 1;
else if (a > b)
return -1;
else
return 0;
});
};
function findCompletions(prefix, identDict, allIdentifiers) {
var _self = this,
fuzzyMatcher = function (prefix, item) {
return ~item.toLowerCase().indexOf(prefix.toLowerCase());
};
var matches = grep(allIdentifiers, function (item) {
return fuzzyMatcher(prefix, item);
});
matches = sortByScore(matches, identDict);
function findCompletions(prefix, allIdentifiers) {
allIdentifiers.sort();
var startIdx = prefixBinarySearch(allIdentifiers, prefix);
var matches = [];
for (var i = startIdx; i < allIdentifiers.length && allIdentifiers[i].indexOf(prefix) === 0; i++)
matches.push(allIdentifiers[i]);
return matches;
}
exports.removeDuplicateWords = function(matches) {
// First, sort
matches = matches.sort();
for (var i = 1; i < matches.length; ){
if (matches[i - 1] == matches[i]){
matches.splice(i, 1);
} else {
i++;
}
}
return matches;
};
exports.retrievePrecedingIdentifier = retrievePrecedingIdentifier;
exports.retrieveFollowingIdentifier = retrieveFollowingIdentifier;
exports.findCompletions = findCompletions;

View file

@ -1,3 +1,33 @@
/* ***** BEGIN LICENSE BLOCK *****
* Distributed under the BSD license:
*
* Copyright (c) 2012, Ajax.org B.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Ajax.org B.V. nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* ***** END LICENSE BLOCK ***** */
define(function(require, exports, module) {
var completeUtil = require("./complete_util");
var SPLIT_REGEX = /[^a-zA-Z_0-9\$]+/;
@ -10,7 +40,7 @@ define(function(require, exports, module) {
var text = doc.getValue().trim();
// Determine cursor's word index
var textBefore = doc.getLines(0, pos.row-1).join("\n") + "\n";
var textBefore = doc.getLines(0, pos.row - 1).join("\n") + "\n";
var currentLine = doc.getLine(pos.row);
textBefore += currentLine.substr(0, pos.column);
var prefixPosition = textBefore.trim().split(SPLIT_REGEX).length - 1;
@ -27,11 +57,11 @@ define(function(require, exports, module) {
if (ident.length === 0)
continue;
var distance = Math.max(prefixPosition, i) - Math.min(prefixPosition, i);
// Score substracted from 100000 to force descending ordering
// Score substracted from MAX to force descending ordering
if (Object.prototype.hasOwnProperty.call(identDict, ident))
identDict[ident] = Math.max(MAX_SCORE-distance, identDict[ident]);
identDict[ident] = Math.max(MAX_SCORE - distance, identDict[ident]);
else
identDict[ident] = MAX_SCORE-distance;
identDict[ident] = MAX_SCORE - distance;
}
@ -42,14 +72,6 @@ define(function(require, exports, module) {
return identDict;
}
function analyze(doc, pos, keywords) {
var line = doc.getLine(pos.row);
var identifier = completeUtil.retrievePrecedingIdentifier(line, pos.column);
var analysisCache = wordDistanceAnalyzer(doc, pos, identifier, keywords);
return analysisCache;
}
completer.complete = function(doc, pos, keywords, callback) {
var line = doc.getLine(pos.row);
var identifier = completeUtil.retrievePrecedingIdentifier(line, pos.column);
@ -58,15 +80,17 @@ define(function(require, exports, module) {
if (identifier === "")
return callback(null);
var identDict = analyze(doc, pos, keywords);
var identDict = wordDistanceAnalyzer(doc, pos, identifier, keywords);
var allIdentifiers = [];
for (var ident in identDict) {
allIdentifiers.push(ident);
}
// find matches based on text in doc
var matches = completeUtil.findCompletions(identifier, allIdentifiers);
allIdentifiers = completeUtil.removeDuplicateWords(allIdentifiers);
// find fuzzy matches based on text in doc, as well as mode keywords
var matches = completeUtil.findCompletions(identifier, identDict, allIdentifiers);
callback(identifier, matches);
};