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

View file

@ -45,29 +45,6 @@ var AutocompleteWorker = exports.AutocompleteWorker = function(sender) {
oop.inherits(AutocompleteWorker, Mirror); oop.inherits(AutocompleteWorker, Mirror);
(function() { (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() { this.onUpdate = function() {
var _self = this; var _self = this;
@ -75,43 +52,25 @@ oop.inherits(AutocompleteWorker, Mirror);
var pos = this.data.cursor; var pos = this.data.cursor;
var currentPos = { line: pos.row, col: pos.column }; var currentPos = { line: pos.row, col: pos.column };
var matches = [];
completer.complete(_self.doc, this.data.cursor, this.data.keywords, function(identifier, completions) { completer.complete(_self.doc, this.data.cursor, this.data.keywords, function(identifier, completions) {
if (completions) if (!identifier) {
matches = matches.concat(completions); _self.sender.emit("complete", {
removeDuplicateMatches(matches); matches: []
// Sort by priority, score });
matches.sort(function(a, b) { }
if (a.priority < b.priority)
return 1; else {
else if (a.priority > b.priority) _self.sender.emit("complete", {
return -1; startRow: pos.row,
else if (a.score < b.score) startColumn: pos.column - identifier.length,
return 1; endRow: pos.row,
else if (a.score > b.score) endColumn: Infinity,
return -1; matches: completions,
else if (a.id && a.id === b.id) { line: _self.doc.getLine(pos.row)
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)
});
return; 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) { define(function(require, exports, module) {
var ID_REGEX = /[a-zA-Z_0-9\$]/; var ID_REGEX = /[a-zA-Z_0-9\$]/;
@ -26,36 +56,71 @@ function retrieveFollowingIdentifier(text, pos, regex) {
return buf; return buf;
} }
function prefixBinarySearch(items, prefix) { // filched from jQuery
var startIndex = 0; function grep( elems, callback, inv ) {
var stopIndex = items.length - 1; var retVal,
var middle = Math.floor((stopIndex + startIndex) / 2); ret = [],
i = 0,
while (stopIndex > startIndex && middle >= 0 && items[middle].indexOf(prefix) !== 0) { length = elems.length;
if (prefix < items[middle]) { inv = !!inv;
stopIndex = middle - 1;
} // Go through the array, only saving the items
else if (prefix > items[middle]) { // that pass the validator function
startIndex = middle + 1; for ( ; i < length; i++ ) {
} retVal = !!callback( elems[ i ], i );
middle = Math.floor((stopIndex + stopIndex) / 2); if ( inv !== retVal ) {
} ret.push( elems[ i ] );
}
// Look back to make sure we haven't skipped any }
while (middle > 0 && items[middle-1].indexOf(prefix) === 0)
middle--; return ret;
return middle >= 0 ? middle : 0; // ensure we're not returning a negative index };
}
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; 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.retrievePrecedingIdentifier = retrievePrecedingIdentifier;
exports.retrieveFollowingIdentifier = retrieveFollowingIdentifier; exports.retrieveFollowingIdentifier = retrieveFollowingIdentifier;
exports.findCompletions = findCompletions; 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) { define(function(require, exports, module) {
var completeUtil = require("./complete_util"); var completeUtil = require("./complete_util");
var SPLIT_REGEX = /[^a-zA-Z_0-9\$]+/; var SPLIT_REGEX = /[^a-zA-Z_0-9\$]+/;
@ -10,7 +40,7 @@ define(function(require, exports, module) {
var text = doc.getValue().trim(); var text = doc.getValue().trim();
// Determine cursor's word index // 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); var currentLine = doc.getLine(pos.row);
textBefore += currentLine.substr(0, pos.column); textBefore += currentLine.substr(0, pos.column);
var prefixPosition = textBefore.trim().split(SPLIT_REGEX).length - 1; var prefixPosition = textBefore.trim().split(SPLIT_REGEX).length - 1;
@ -27,11 +57,11 @@ define(function(require, exports, module) {
if (ident.length === 0) if (ident.length === 0)
continue; continue;
var distance = Math.max(prefixPosition, i) - Math.min(prefixPosition, i); 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)) 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 else
identDict[ident] = MAX_SCORE-distance; identDict[ident] = MAX_SCORE - distance;
} }
@ -42,14 +72,6 @@ define(function(require, exports, module) {
return identDict; 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) { completer.complete = function(doc, pos, keywords, callback) {
var line = doc.getLine(pos.row); var line = doc.getLine(pos.row);
var identifier = completeUtil.retrievePrecedingIdentifier(line, pos.column); var identifier = completeUtil.retrievePrecedingIdentifier(line, pos.column);
@ -58,15 +80,17 @@ define(function(require, exports, module) {
if (identifier === "") if (identifier === "")
return callback(null); return callback(null);
var identDict = analyze(doc, pos, keywords); var identDict = wordDistanceAnalyzer(doc, pos, identifier, keywords);
var allIdentifiers = []; var allIdentifiers = [];
for (var ident in identDict) { for (var ident in identDict) {
allIdentifiers.push(ident); allIdentifiers.push(ident);
} }
// find matches based on text in doc allIdentifiers = completeUtil.removeDuplicateWords(allIdentifiers);
var matches = completeUtil.findCompletions(identifier, allIdentifiers);
// find fuzzy matches based on text in doc, as well as mode keywords
var matches = completeUtil.findCompletions(identifier, identDict, allIdentifiers);
callback(identifier, matches); callback(identifier, matches);
}; };