Index: Source/devtools/front_end/CodeMirrorTextEditor.js |
diff --git a/Source/devtools/front_end/CodeMirrorTextEditor.js b/Source/devtools/front_end/CodeMirrorTextEditor.js |
index 89c132aed488d513ce33fd506b49b60553fd0a43..c2a50b0e2642bf63cfda4e3636fd8bebdf7a0758 100644 |
--- a/Source/devtools/front_end/CodeMirrorTextEditor.js |
+++ b/Source/devtools/front_end/CodeMirrorTextEditor.js |
@@ -55,6 +55,14 @@ WebInspector.CodeMirrorTextEditor = function(url, delegate) |
this.registerRequiredCSS("cm/showhint.css"); |
this.registerRequiredCSS("cm/cmdevtools.css"); |
+ function autocompleteCommand() |
+ { |
+ if (!this._dictionary || this._codeMirror.somethingSelected()) |
+ return; |
+ CodeMirror.showHint(this._codeMirror, this._autocomplete.bind(this)); |
+ } |
+ CodeMirror.commands.autocomplete = autocompleteCommand.bind(this); |
+ |
this._codeMirror = window.CodeMirror(this.element, { |
lineNumbers: true, |
gutters: ["CodeMirror-linenumbers"], |
@@ -65,7 +73,7 @@ WebInspector.CodeMirrorTextEditor = function(url, delegate) |
autoCloseBrackets: true |
}); |
- var extraKeys = {}; |
+ var extraKeys = {"Ctrl-Space": "autocomplete"}; |
var indent = WebInspector.settings.textEditorIndent.get(); |
if (indent === WebInspector.TextUtils.Indent.TabCharacter) { |
this._codeMirror.setOption("indentWithTabs", true); |
@@ -87,6 +95,7 @@ WebInspector.CodeMirrorTextEditor = function(url, delegate) |
this._fixWordMovement = new WebInspector.CodeMirrorTextEditor.FixWordMovement(this._codeMirror); |
this._codeMirror.on("change", this._change.bind(this)); |
+ this._codeMirror.on("beforeChange", this._beforeChange.bind(this)); |
this._codeMirror.on("gutterClick", this._gutterClick.bind(this)); |
this._codeMirror.on("cursorActivity", this._cursorActivity.bind(this)); |
this._codeMirror.on("scroll", this._scroll.bind(this)); |
@@ -98,6 +107,7 @@ WebInspector.CodeMirrorTextEditor = function(url, delegate) |
this._nestedUpdatesCounter = 0; |
this.element.addEventListener("focus", this._handleElementFocus.bind(this), false); |
+ this.element.addEventListener("keydown", this._handleKeyDown.bind(this), false); |
this.element.tabIndex = 0; |
this._setupSelectionColor(); |
} |
@@ -131,10 +141,69 @@ WebInspector.CodeMirrorTextEditor.prototype = { |
document.head.appendChild(style); |
}, |
+ _autocomplete: function(codeMirror) |
+ { |
+ var cursor = codeMirror.getCursor(); |
+ var prefixRange = this._wordRangeForCursorPosition(cursor.line, cursor.ch, true); |
+ if (!prefixRange) |
+ return null; |
+ var prefix = this.copyRange(prefixRange); |
+ this._dictionary.removeWord(prefix); |
+ var wordsWithPrefix = this._dictionary.wordsWithPrefix(this.copyRange(prefixRange)); |
+ this._dictionary.addWord(prefix); |
+ |
+ var data = { |
+ list: wordsWithPrefix, |
+ from: new CodeMirror.Pos(prefixRange.startLine, prefixRange.startColumn), |
+ to: new CodeMirror.Pos(prefixRange.endLine, prefixRange.endColumn) |
+ }; |
+ CodeMirror.on(data, "close", this._handleAutocompletionClose.bind(this)); |
+ |
+ return data; |
+ }, |
+ |
+ _handleKeyDown: function(e) |
+ { |
+ if (!!this._consumeEsc && e.keyCode === WebInspector.KeyboardShortcut.Keys.Esc.code) |
+ e.consume(true); |
+ delete this._consumeEsc; |
+ }, |
+ |
+ _handleAutocompletionClose: function() |
+ { |
+ this._consumeEsc = true; |
+ }, |
+ |
+ /** |
+ * @param {string} text |
+ */ |
+ _addTextToCompletionDictionary: function(text) |
+ { |
+ var words = WebInspector.TextUtils.textToWords(text); |
+ for(var i = 0; i < words.length; ++i) { |
+ this._dictionary.addWord(words[i]); |
+ } |
+ }, |
+ |
+ /** |
+ * @param {string} text |
+ */ |
+ _removeTextFromCompletionDictionary: function(text) |
+ { |
+ var words = WebInspector.TextUtils.textToWords(text); |
+ for(var i = 0; i < words.length; ++i) { |
+ this._dictionary.removeWord(words[i]); |
+ } |
+ }, |
+ |
/** |
* @param {WebInspector.CompletionDictionary} dictionary |
*/ |
- setCompletionDictionary: function(dictionary) { }, |
+ setCompletionDictionary: function(dictionary) |
+ { |
+ this._dictionary = dictionary; |
+ this._addTextToCompletionDictionary(this.text()); |
+ }, |
/** |
* @param {number} lineNumber |
@@ -494,6 +563,37 @@ WebInspector.CodeMirrorTextEditor.prototype = { |
return newRange; |
}, |
+ /** |
+ * @param {number} lineNumber |
+ * @param {number} column |
+ * @param {boolean=} prefixOnly |
+ * @return {?WebInspector.TextRange} |
+ */ |
+ _wordRangeForCursorPosition: function(lineNumber, column, prefixOnly) |
+ { |
+ var line = this.line(lineNumber); |
+ if (!WebInspector.TextUtils.isWordChar(line.charAt(column - 1))) |
+ return null; |
+ var wordStart = column - 1; |
+ while(wordStart > 0 && WebInspector.TextUtils.isWordChar(line.charAt(wordStart - 1))) |
+ --wordStart; |
+ if (prefixOnly) |
+ return new WebInspector.TextRange(lineNumber, wordStart, lineNumber, column); |
+ var wordEnd = column; |
+ while(wordEnd < line.length && WebInspector.TextUtils.isWordChar(line.charAt(wordEnd))) |
+ ++wordEnd; |
+ return new WebInspector.TextRange(lineNumber, wordStart, lineNumber, wordEnd); |
+ }, |
+ |
+ _beforeChange: function(codeMirror, changeObject) |
+ { |
+ if (!this._dictionary) |
+ return; |
+ this._updatedLines = this._updatedLines || {}; |
+ for(var i = changeObject.from.line; i <= changeObject.to.line; ++i) |
+ this._updatedLines[i] = this.line(i); |
+ }, |
+ |
_change: function(codeMirror, changeObject) |
{ |
var widgets = this._elementToWidget.values(); |
@@ -501,6 +601,13 @@ WebInspector.CodeMirrorTextEditor.prototype = { |
this._codeMirror.removeLineWidget(widgets[i]); |
this._elementToWidget.clear(); |
+ if (this._updatedLines) { |
+ for(var lineNumber in this._updatedLines) |
+ this._removeTextFromCompletionDictionary(this._updatedLines[lineNumber]); |
+ delete this._updatedLines; |
+ } |
+ |
+ var linesToUpdate = {}; |
do { |
var oldRange = this._toRange(changeObject.from, changeObject.to); |
var newRange = oldRange.clone(); |
@@ -519,7 +626,18 @@ WebInspector.CodeMirrorTextEditor.prototype = { |
if (!this._muteTextChangedEvent) |
this._delegate.onTextChanged(oldRange, newRange); |
+ for(var i = newRange.startLine; i <= newRange.endLine; ++i) { |
+ linesToUpdate[i] = true; |
+ } |
+ if (this._dictionary) { |
+ for(var i = newRange.startLine; i <= newRange.endLine; ++i) |
+ linesToUpdate[i] = this.line(i); |
+ } |
} while (changeObject = changeObject.next); |
+ if (this._dictionary) { |
+ for(var lineNumber in linesToUpdate) |
+ this._addTextToCompletionDictionary(linesToUpdate[lineNumber]); |
+ } |
}, |
_cursorActivity: function() |