Merge remote-tracking branch 'remotes/pull/1789'

Conflicts:
	lib/ace/autocomplete.js
This commit is contained in:
nightwing 2014-03-21 15:35:04 +04:00
commit 8e5bdc512c
4 changed files with 145 additions and 29 deletions

View file

@ -251,7 +251,7 @@ commands.addCommand({
} }
}); });
var keybindings = { var keybindings = {
ace: null, // Null = use "default" keymapping ace: null, // Null = use "default" keymapping
vim: require("ace/keyboard/vim").handler, vim: require("ace/keyboard/vim").handler,
emacs: "ace/keyboard/emacs", emacs: "ace/keyboard/emacs",
@ -317,7 +317,7 @@ doclist.history.index = 0;
doclist.cycleOpen = function(editor, dir) { doclist.cycleOpen = function(editor, dir) {
var h = this.history; var h = this.history;
h.index += dir; h.index += dir;
if (h.index >= h.length) if (h.index >= h.length)
h.index = 0; h.index = 0;
else if (h.index <= 0) else if (h.index <= 0)
h.index = h.length - 1; h.index = h.length - 1;
@ -499,7 +499,7 @@ bindDropdown("split", function(value) {
sp.setSplits(1); sp.setSplits(1);
} else { } else {
var newEditor = (sp.getSplits() == 1); var newEditor = (sp.getSplits() == 1);
sp.setOrientation(value == "below" ? sp.BELOW : sp.BESIDE); sp.setOrientation(value == "below" ? sp.BELOW : sp.BESIDE);
sp.setSplits(2); sp.setSplits(2);
if (newEditor) { if (newEditor) {
@ -592,6 +592,7 @@ env.editSnippets = function() {
require("ace/ext/language_tools"); require("ace/ext/language_tools");
env.editor.setOptions({ env.editor.setOptions({
enableBasicAutocompletion: true, enableBasicAutocompletion: true,
enableLiveAutocomplete: true,
enableSnippets: true enableSnippets: true
}); });

View file

@ -55,6 +55,8 @@ var Autocomplete = function() {
}; };
(function() { (function() {
this.gatherCompletionsId = 0;
this.$init = function() { this.$init = function() {
this.popup = new AcePopup(document.body || document.documentElement); this.popup = new AcePopup(document.body || document.documentElement);
this.popup.on("click", function(e) { this.popup.on("click", function(e) {
@ -72,6 +74,7 @@ var Autocomplete = function() {
var renderer = editor.renderer; var renderer = editor.renderer;
this.popup.setRow(this.autoSelect ? 0 : -1); this.popup.setRow(this.autoSelect ? 0 : -1);
if (!keepPopupPosition) { if (!keepPopupPosition) {
this.popup.setTheme(editor.getTheme());
this.popup.setFontSize(editor.getFontSize()); this.popup.setFontSize(editor.getFontSize());
var lineHeight = renderer.layerConfig.lineHeight; var lineHeight = renderer.layerConfig.lineHeight;
@ -96,6 +99,10 @@ var Autocomplete = function() {
this.editor.off("mousewheel", this.mousewheelListener); this.editor.off("mousewheel", this.mousewheelListener);
this.changeTimer.cancel(); this.changeTimer.cancel();
if (this.popup && this.popup.isOpen) {
this.gatherCompletionsId = this.gatherCompletionsId + 1;
}
if (this.popup) if (this.popup)
this.popup.hide(); this.popup.hide();
@ -146,6 +153,7 @@ var Autocomplete = function() {
data = this.popup.getData(this.popup.getRow()); data = this.popup.getData(this.popup.getRow());
if (!data) if (!data)
return false; return false;
if (data.completer && data.completer.insertMatch) { if (data.completer && data.completer.insertMatch) {
data.completer.insertMatch(this.editor); data.completer.insertMatch(this.editor);
} else { } else {
@ -197,18 +205,18 @@ var Autocomplete = function() {
this.base.column -= prefix.length; this.base.column -= prefix.length;
var matches = []; var matches = [];
util.parForEach(editor.completers, function(completer, next) { var total = editor.completers.length;
editor.completers.forEach(function(completer, i) {
completer.getCompletions(editor, session, pos, prefix, function(err, results) { completer.getCompletions(editor, session, pos, prefix, function(err, results) {
if (!err) if (!err)
matches = matches.concat(results); matches = matches.concat(results);
next();
});
}, function() {
callback(null, { callback(null, {
prefix: prefix, prefix: prefix,
matches: matches matches: matches,
finished: (--total === 0)
}); });
}); });
});
return true; return true;
}; };
@ -243,10 +251,30 @@ var Autocomplete = function() {
this.completions.setFilter(prefix); this.completions.setFilter(prefix);
if (!this.completions.filtered.length) if (!this.completions.filtered.length)
return this.detach(); return this.detach();
if (this.completions.filtered.length == 1
&& this.completions.filtered[0].value == prefix
&& !this.completions.filtered[0].snippet)
return this.detach();
this.openPopup(this.editor, prefix, keepPopupPosition); this.openPopup(this.editor, prefix, keepPopupPosition);
return; return;
} }
// Save current gatherCompletions session, session is close when a match is insert
var _id = this.gatherCompletionsId;
this.gatherCompletions(this.editor, function(err, results) { this.gatherCompletions(this.editor, function(err, results) {
// Only detach if result gathering is finished
var doDetach = function() {
if (!results.finished) return;
return this.detach();
}.bind(this);
// Calcul prefix
var session = this.editor.getSession();
var pos = this.editor.getCursorPosition();
var line = session.getLine(pos.row);
var prefix = util.retrievePrecedingIdentifier(line, pos.column);
// Results matches
var matches = results && results.matches; var matches = results && results.matches;
if (!matches || !matches.length) if (!matches || !matches.length)
return this.detach(); return this.detach();
@ -254,14 +282,32 @@ var Autocomplete = function() {
// if (matches.length == 1) // if (matches.length == 1)
// return this.insertMatch(matches[0]); // return this.insertMatch(matches[0]);
// No prefix or no results -> close
if (!prefix || !prefix.length || !matches || !matches.length)
return doDetach();
// Wrong prefix or wrong session -> ignore
if (prefix.indexOf(results.prefix) != 0
|| _id != this.gatherCompletionsId)
return;
this.completions = new FilteredList(matches); this.completions = new FilteredList(matches);
this.completions.setFilter(results.prefix); this.completions.setFilter(prefix);
var filtered = this.completions.filtered; var filtered = this.completions.filtered;
// No results
if (!filtered.length) if (!filtered.length)
return this.detach(); return doDetach();
// One result equals to the prefix
if (filtered.length == 1 && filtered[0].value == prefix && !filtered[0].snippet)
return doDetach();
// Autoinsert if one result
if (this.autoInsert && filtered.length == 1) if (this.autoInsert && filtered.length == 1)
return this.insertMatch(filtered[0]); return this.insertMatch(filtered[0]);
this.openPopup(this.editor, results.prefix, keepPopupPosition);
this.openPopup(this.editor, prefix, keepPopupPosition);
}.bind(this)); }.bind(this));
}; };

View file

@ -43,7 +43,7 @@ var $singleLineEditor = function(el) {
var renderer = new Renderer(el); var renderer = new Renderer(el);
renderer.$maxLines = 4; renderer.$maxLines = 4;
var editor = new Editor(renderer); var editor = new Editor(renderer);
editor.setHighlightActiveLine(false); editor.setHighlightActiveLine(false);
@ -59,13 +59,13 @@ var $singleLineEditor = function(el) {
var AcePopup = function(parentNode) { var AcePopup = function(parentNode) {
var el = dom.createElement("div"); var el = dom.createElement("div");
var popup = new $singleLineEditor(el); var popup = new $singleLineEditor(el);
if (parentNode) if (parentNode)
parentNode.appendChild(el); parentNode.appendChild(el);
el.style.display = "none"; el.style.display = "none";
popup.renderer.content.style.cursor = "default"; popup.renderer.content.style.cursor = "default";
popup.renderer.setStyle("ace_autocomplete"); popup.renderer.setStyle("ace_autocomplete");
popup.setOption("displayIndentGuides", false); popup.setOption("displayIndentGuides", false);
var noop = function(){}; var noop = function(){};
@ -154,11 +154,11 @@ var AcePopup = function(parentNode) {
popup.getHoveredRow = function() { popup.getHoveredRow = function() {
return hoverMarker.start.row; return hoverMarker.start.row;
}; };
event.addListener(popup.container, "mouseout", hideHoverMarker); event.addListener(popup.container, "mouseout", hideHoverMarker);
popup.on("hide", hideHoverMarker); popup.on("hide", hideHoverMarker);
popup.on("changeSelection", hideHoverMarker); popup.on("changeSelection", hideHoverMarker);
popup.session.doc.getLength = function() { popup.session.doc.getLength = function() {
return popup.data.length; return popup.data.length;
}; };
@ -202,7 +202,7 @@ var AcePopup = function(parentNode) {
}; };
bgTokenizer.$updateOnChange = noop; bgTokenizer.$updateOnChange = noop;
bgTokenizer.start = noop; bgTokenizer.start = noop;
popup.session.$computeWidth = function() { popup.session.$computeWidth = function() {
return this.screenWidth = 0; return this.screenWidth = 0;
} }
@ -210,7 +210,7 @@ var AcePopup = function(parentNode) {
// public // public
popup.isOpen = false; popup.isOpen = false;
popup.isTopdown = false; popup.isTopdown = false;
popup.data = []; popup.data = [];
popup.setData = function(list) { popup.setData = function(list) {
popup.data = list || []; popup.data = list || [];
@ -235,7 +235,7 @@ var AcePopup = function(parentNode) {
popup._signal("select"); popup._signal("select");
} }
}; };
popup.on("changeSelection", function() { popup.on("changeSelection", function() {
if (popup.isOpen) if (popup.isOpen)
popup.setRow(popup.selection.lead.row); popup.setRow(popup.selection.lead.row);
@ -267,22 +267,22 @@ var AcePopup = function(parentNode) {
el.style.display = ""; el.style.display = "";
this.renderer.$textLayer.checkForSizeChanges(); this.renderer.$textLayer.checkForSizeChanges();
var left = pos.left; var left = pos.left;
if (left + el.offsetWidth > screenWidth) if (left + el.offsetWidth > screenWidth)
left = screenWidth - el.offsetWidth; left = screenWidth - el.offsetWidth;
el.style.left = left + "px"; el.style.left = left + "px";
this._signal("show"); this._signal("show");
lastMouseEvent = null; lastMouseEvent = null;
popup.isOpen = true; popup.isOpen = true;
}; };
popup.getTextLeftOffset = function() { popup.getTextLeftOffset = function() {
return this.$borderSize + this.renderer.$padding + this.$imageSize; return this.$borderSize + this.renderer.$padding + this.$imageSize;
}; };
popup.$imageSize = 0; popup.$imageSize = 0;
popup.$borderSize = 1; popup.$borderSize = 1;
@ -290,19 +290,24 @@ var AcePopup = function(parentNode) {
}; };
dom.importCssString("\ dom.importCssString("\
.ace_autocomplete.ace-tm .ace_marker-layer .ace_active-line {\ .ace_editor.ace_autocomplete .ace_marker-layer .ace_active-line {\
background-color: #CAD6FA;\ background-color: #CAD6FA;\
z-index: 1;\ z-index: 1;\
}\ }\
.ace_autocomplete.ace-tm .ace_line-hover {\ .ace_editor.ace_autocomplete .ace_line-hover {\
border: 1px solid #abbffe;\ border: 1px solid #abbffe;\
margin-top: -1px;\ margin-top: -1px;\
background: rgba(233,233,253,0.4);\ background: rgba(233,233,253,0.4);\
}\ }\
.ace_autocomplete .ace_line-hover {\ .ace_editor.ace_autocomplete .ace_line-hover {\
position: absolute;\ position: absolute;\
z-index: 2;\ z-index: 2;\
}\ }\
.ace_editor.ace_autocomplete .ace_scroller {\
background: none;\
border: none;\
box-shadow: none;\
}\
.ace_rightAlignedText {\ .ace_rightAlignedText {\
color: gray;\ color: gray;\
display: inline-block;\ display: inline-block;\
@ -311,11 +316,11 @@ dom.importCssString("\
text-align: right;\ text-align: right;\
z-index: -1;\ z-index: -1;\
}\ }\
.ace_autocomplete .ace_completion-highlight{\ .ace_editor.ace_autocomplete .ace_completion-highlight{\
color: #000;\ color: #000;\
text-shadow: 0 0 0.01em;\ text-shadow: 0 0 0.01em;\
}\ }\
.ace_autocomplete {\ .ace_editor.ace_autocomplete {\
width: 280px;\ width: 280px;\
z-index: 200000;\ z-index: 200000;\
background: #fbfbfb;\ background: #fbfbfb;\

View file

@ -34,6 +34,7 @@ define(function(require, exports, module) {
var snippetManager = require("../snippets").snippetManager; var snippetManager = require("../snippets").snippetManager;
var Autocomplete = require("../autocomplete").Autocomplete; var Autocomplete = require("../autocomplete").Autocomplete;
var config = require("../config"); var config = require("../config");
var util = require("../autocomplete/util");
var textCompleter = require("../autocomplete/text_completer"); var textCompleter = require("../autocomplete/text_completer");
var keyWordCompleter = { var keyWordCompleter = {
@ -114,6 +115,58 @@ var loadSnippetFile = function(id) {
}); });
}; };
var doLiveAutocomplete = function(e) {
var editor = e.editor;
var session = editor.getSession();
var pos = editor.getCursorPosition();
var line = session.getLine(pos.row);
var hasCompleter = (editor.completer && editor.completer.activated);
var text = e.args || "";
// Is the user entering text
// we only want to automatically show the autocomplete dialog
// whenever the user is typing in text not pasting, deleting, ...
var typing = (e.command.name === "insertstring" && text.length === 1);
// We don't want to autocomplete with no prefix
if(
e.command.name === 'backspace' &&
util.retrievePrecedingIdentifier(line, pos.column) === ''
) {
if(hasCompleter) editor.completer.detach();
return;
}
// we don't want to autocomplete on paste events
if(!typing) {
return;
}
// The prefix to autocomplete for
var prefix = util.retrievePrecedingIdentifier(line, pos.column);
// Only autocomplete if there's a prefix that can be matched
if(prefix !== '' && !(hasCompleter)) {
if (!editor.completer) {
// Create new autocompleter
editor.completer = new Autocomplete();
// Disable autoInsert
editor.completer.autoInsert = false;
}
editor.completer.showPopup(editor);
// needed for firefox on mac
editor.completer.cancelContextMenu();
} else if(prefix === '' && hasCompleter) {
// When the prefix is empty
// close the autocomplete dialog
editor.completer.detach();
}
};
var Editor = require("../editor").Editor; var Editor = require("../editor").Editor;
require("../config").defineOptions(Editor.prototype, "editor", { require("../config").defineOptions(Editor.prototype, "editor", {
enableBasicAutocompletion: { enableBasicAutocompletion: {
@ -127,6 +180,17 @@ require("../config").defineOptions(Editor.prototype, "editor", {
}, },
value: false value: false
}, },
enableLiveAutocomplete: {
set: function(val) {
if (val) {
// On each change automatically trigger the autocomplete
this.commands.on('afterExec', doLiveAutocomplete);
} else {
this.commands.removeListener('afterExec', doLiveAutocomplete);
}
},
value: false
},
enableSnippets: { enableSnippets: {
set: function(val) { set: function(val) {
if (val) { if (val) {