| Index: Source/devtools/front_end/CodeMirrorTextEditor.js
|
| diff --git a/Source/devtools/front_end/CodeMirrorTextEditor.js b/Source/devtools/front_end/CodeMirrorTextEditor.js
|
| index 031948f3a771fe003168e518dc5090976dcf0349..92963e713867b6c775ffaf59a9cb53b912ae648c 100644
|
| --- a/Source/devtools/front_end/CodeMirrorTextEditor.js
|
| +++ b/Source/devtools/front_end/CodeMirrorTextEditor.js
|
| @@ -89,6 +89,7 @@ WebInspector.CodeMirrorTextEditor = function(url, delegate)
|
| "Shift-Tab": "indentLess",
|
| "Enter": "smartNewlineAndIndent",
|
| "Ctrl-Space": "autocomplete",
|
| + "Ctrl-D": "selectNextOccurrence",
|
| "Esc": "dismissMultipleSelections"
|
| };
|
|
|
| @@ -143,10 +144,11 @@ WebInspector.CodeMirrorTextEditor = function(url, delegate)
|
| this._shouldClearHistory = true;
|
| this._lineSeparator = "\n";
|
|
|
| - this._tokenHighlighter = new WebInspector.CodeMirrorTextEditor.TokenHighlighter(this._codeMirror);
|
| + this._tokenHighlighter = new WebInspector.CodeMirrorTextEditor.TokenHighlighter(this, this._codeMirror);
|
| this._blockIndentController = new WebInspector.CodeMirrorTextEditor.BlockIndentController(this._codeMirror);
|
| this._fixWordMovement = new WebInspector.CodeMirrorTextEditor.FixWordMovement(this._codeMirror);
|
| this._autocompleteController = new WebInspector.CodeMirrorTextEditor.AutocompleteController(this, this._codeMirror);
|
| + this._selectNextOccurrenceController = new WebInspector.CodeMirrorTextEditor.SelectNextOccurrenceController(this, this._codeMirror);
|
|
|
| this._codeMirror.on("changes", this._changes.bind(this));
|
| this._codeMirror.on("beforeChange", this._beforeChange.bind(this));
|
| @@ -189,12 +191,27 @@ WebInspector.CodeMirrorTextEditor.ChangeObject;
|
|
|
| WebInspector.CodeMirrorTextEditor.maxHighlightLength = 1000;
|
|
|
| +/**
|
| + * @param {!CodeMirror} codeMirror
|
| + */
|
| WebInspector.CodeMirrorTextEditor.autocompleteCommand = function(codeMirror)
|
| {
|
| codeMirror._codeMirrorTextEditor._autocompleteController.autocomplete();
|
| }
|
| CodeMirror.commands.autocomplete = WebInspector.CodeMirrorTextEditor.autocompleteCommand;
|
|
|
| +/**
|
| + * @param {!CodeMirror} codeMirror
|
| + */
|
| +WebInspector.CodeMirrorTextEditor.selectNextOccurrenceCommand = function(codeMirror)
|
| +{
|
| + codeMirror._codeMirrorTextEditor._selectNextOccurrenceController.selectNextOccurrence();
|
| +}
|
| +CodeMirror.commands.selectNextOccurrence = WebInspector.CodeMirrorTextEditor.selectNextOccurrenceCommand;
|
| +
|
| +/**
|
| + * @param {!CodeMirror} codeMirror
|
| + */
|
| CodeMirror.commands.smartNewlineAndIndent = function(codeMirror)
|
| {
|
| codeMirror.operation(innerSmartNewlineAndIndent.bind(null, codeMirror));
|
| @@ -355,7 +372,7 @@ WebInspector.CodeMirrorTextEditor.prototype = {
|
| else
|
| this.setSelection(WebInspector.TextRange.createFromLocation(range.startLine, range.startColumn));
|
| } else {
|
| - // Collapse selection to end on search start so that we jump to next occurence on the first enter press.
|
| + // Collapse selection to end on search start so that we jump to next occurrence on the first enter press.
|
| this.setSelection(this.selection().collapseToEnd());
|
| }
|
| this._tokenHighlighter.highlightSearchResults(regex, range);
|
| @@ -913,19 +930,17 @@ WebInspector.CodeMirrorTextEditor.prototype = {
|
| /**
|
| * @param {number} lineNumber
|
| * @param {number} column
|
| - * @param {boolean=} prefixOnly
|
| * @return {?WebInspector.TextRange}
|
| */
|
| - _wordRangeForCursorPosition: function(lineNumber, column, prefixOnly)
|
| + _wordRangeForCursorPosition: function(lineNumber, column)
|
| {
|
| var line = this.line(lineNumber);
|
| - if (column === 0 || !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 wordStart = column;
|
| + if (column !== 0 && WebInspector.TextUtils.isWordChar(line.charAt(column - 1))) {
|
| + wordStart = column - 1;
|
| + while (wordStart > 0 && WebInspector.TextUtils.isWordChar(line.charAt(wordStart - 1)))
|
| + --wordStart;
|
| + }
|
| var wordEnd = column;
|
| while (wordEnd < line.length && WebInspector.TextUtils.isWordChar(line.charAt(wordEnd)))
|
| ++wordEnd;
|
| @@ -1020,6 +1035,7 @@ WebInspector.CodeMirrorTextEditor.prototype = {
|
| */
|
| _beforeSelectionChange: function(codeMirror, selection)
|
| {
|
| + this._selectNextOccurrenceController.selectionWillChange();
|
| if (!this._isHandlingMouseDownEvent)
|
| return;
|
| if (!selection.ranges.length)
|
| @@ -1096,6 +1112,20 @@ WebInspector.CodeMirrorTextEditor.prototype = {
|
| },
|
|
|
| /**
|
| + * @return {!Array.<!WebInspector.TextRange>}
|
| + */
|
| + selections: function()
|
| + {
|
| + var selectionList = this._codeMirror.listSelections();
|
| + var result = [];
|
| + for (var i = 0; i < selectionList.length; ++i) {
|
| + var selection = selectionList[i];
|
| + result.push(this._toRange(selection.anchor, selection.head));
|
| + }
|
| + return result;
|
| + },
|
| +
|
| + /**
|
| * @return {?WebInspector.TextRange}
|
| */
|
| lastSelection: function()
|
| @@ -1114,6 +1144,22 @@ WebInspector.CodeMirrorTextEditor.prototype = {
|
| },
|
|
|
| /**
|
| + * @param {!Array.<!WebInspector.TextRange>} ranges
|
| + */
|
| + setSelections: function(ranges)
|
| + {
|
| + var selections = [];
|
| + for (var i = 0; i < ranges.length; ++i) {
|
| + var selection = this._toPos(ranges[i]);
|
| + selections.push({
|
| + anchor: selection.start,
|
| + head: selection.end
|
| + });
|
| + }
|
| + this._codeMirror.setSelections(selections, 0, { scroll: false });
|
| + },
|
| +
|
| + /**
|
| * @param {string} text
|
| */
|
| _detectLineSeparator: function(text)
|
| @@ -1287,10 +1333,12 @@ WebInspector.CodeMirrorPositionHandle.prototype = {
|
|
|
| /**
|
| * @constructor
|
| + * @param {!WebInspector.CodeMirrorTextEditor} textEditor
|
| * @param {!CodeMirror} codeMirror
|
| */
|
| -WebInspector.CodeMirrorTextEditor.TokenHighlighter = function(codeMirror)
|
| +WebInspector.CodeMirrorTextEditor.TokenHighlighter = function(textEditor, codeMirror)
|
| {
|
| + this._textEditor = textEditor;
|
| this._codeMirror = codeMirror;
|
| }
|
|
|
| @@ -1351,7 +1399,7 @@ WebInspector.CodeMirrorTextEditor.TokenHighlighter.prototype = {
|
| return;
|
|
|
| var selections = this._codeMirror.getSelections();
|
| - if (selections.length !== 1)
|
| + if (selections.length > 1)
|
| return;
|
| var selectedText = selections[0];
|
| if (this._isWord(selectedText, selectionStart.line, selectionStart.ch, selectionEnd.ch)) {
|
| @@ -1585,7 +1633,7 @@ WebInspector.CodeMirrorTextEditor.AutocompleteController.prototype = {
|
| {
|
| var mainSelectionContext = this._textEditor.copyRange(mainSelection);
|
| for (var i = 0; i < selections.length; ++i) {
|
| - var wordRange = this._textEditor._wordRangeForCursorPosition(selections[i].head.line, selections[i].head.ch, false);
|
| + var wordRange = this._textEditor._wordRangeForCursorPosition(selections[i].head.line, selections[i].head.ch);
|
| if (!wordRange)
|
| return false;
|
| var context = this._textEditor.copyRange(wordRange);
|
| @@ -1606,7 +1654,7 @@ WebInspector.CodeMirrorTextEditor.AutocompleteController.prototype = {
|
| var selections = this._codeMirror.listSelections().slice();
|
| var topSelection = selections.shift();
|
| var cursor = topSelection.head;
|
| - var substituteRange = this._textEditor._wordRangeForCursorPosition(cursor.line, cursor.ch, false);
|
| + var substituteRange = this._textEditor._wordRangeForCursorPosition(cursor.line, cursor.ch);
|
| if (!substituteRange || substituteRange.startColumn === cursor.ch || !this._validateSelectionsContexts(substituteRange, selections)) {
|
| this.finishAutocomplete();
|
| return;
|
| @@ -1731,6 +1779,139 @@ WebInspector.CodeMirrorTextEditor.AutocompleteController.prototype = {
|
| }
|
|
|
| /**
|
| + * @constructor
|
| + * @param {!WebInspector.CodeMirrorTextEditor} textEditor
|
| + * @param {!CodeMirror} codeMirror
|
| + */
|
| +WebInspector.CodeMirrorTextEditor.SelectNextOccurrenceController = function(textEditor, codeMirror)
|
| +{
|
| + this._textEditor = textEditor;
|
| + this._codeMirror = codeMirror;
|
| +}
|
| +
|
| +WebInspector.CodeMirrorTextEditor.SelectNextOccurrenceController.prototype = {
|
| + selectionWillChange: function()
|
| + {
|
| + if (!this._muteSelectionListener)
|
| + delete this._fullWordSelection;
|
| + },
|
| +
|
| + /**
|
| + * @param {!Array.<!WebInspector.TextRange>} selections
|
| + * @param {!WebInspector.TextRange} range
|
| + * @return {boolean}
|
| + */
|
| + _findRange: function(selections, range)
|
| + {
|
| + for (var i = 0; i < selections.length; ++i) {
|
| + if (range.equal(selections[i]))
|
| + return true;
|
| + }
|
| + return false;
|
| + },
|
| +
|
| + selectNextOccurrence: function()
|
| + {
|
| + var selections = this._textEditor.selections();
|
| + var anyEmptySelection = false;
|
| + for (var i = 0; i < selections.length; ++i) {
|
| + var selection = selections[i];
|
| + anyEmptySelection = anyEmptySelection || selection.isEmpty();
|
| + if (selection.startLine !== selection.endLine)
|
| + return;
|
| + }
|
| + if (anyEmptySelection) {
|
| + this._expandSelectionsToWords(selections);
|
| + return;
|
| + }
|
| +
|
| + var last = selections[selections.length - 1];
|
| + var next = last;
|
| + do {
|
| + next = this._findNextOccurrence(next, !!this._fullWordSelection);
|
| + } while (next && this._findRange(selections, next) && !next.equal(last));
|
| +
|
| + if (!next)
|
| + return;
|
| + selections.push(next);
|
| +
|
| + this._muteSelectionListener = true;
|
| + this._textEditor.setSelections(selections);
|
| + delete this._muteSelectionListener;
|
| +
|
| + this._textEditor._revealLine(next.startLine);
|
| + },
|
| +
|
| + /**
|
| + * @param {!Array.<!WebInspector.TextRange>} selections
|
| + */
|
| + _expandSelectionsToWords: function(selections)
|
| + {
|
| + var newSelections = [];
|
| + for (var i = 0; i < selections.length; ++i) {
|
| + var selection = selections[i];
|
| + var startRangeWord = this._textEditor._wordRangeForCursorPosition(selection.startLine, selection.startColumn)
|
| + || WebInspector.TextRange.createFromLocation(selection.startLine, selection.startColumn);
|
| + var endRangeWord = this._textEditor._wordRangeForCursorPosition(selection.endLine, selection.endColumn)
|
| + || WebInspector.TextRange.createFromLocation(selection.endLine, selection.endColumn);
|
| + var newSelection = new WebInspector.TextRange(startRangeWord.startLine, startRangeWord.startColumn, endRangeWord.endLine, endRangeWord.endColumn);
|
| + newSelections.push(newSelection);
|
| + }
|
| + this._textEditor.setSelections(newSelections);
|
| + this._fullWordSelection = true;
|
| + },
|
| +
|
| + /**
|
| + * @param {!WebInspector.TextRange} range
|
| + * @param {boolean} fullWord
|
| + * @return {?WebInspector.TextRange}
|
| + */
|
| + _findNextOccurrence: function(range, fullWord)
|
| + {
|
| + range = range.normalize();
|
| + var matchedLineNumber;
|
| + var matchedColumnNumber;
|
| + var textToFind = this._textEditor.copyRange(range);
|
| + function findWordInLine(wordRegex, lineNumber, lineText, from, to)
|
| + {
|
| + if (typeof matchedLineNumber === "number")
|
| + return true;
|
| + wordRegex.lastIndex = from;
|
| + var result = wordRegex.exec(lineText);
|
| + if (!result || result.index + textToFind.length > to)
|
| + return false;
|
| + matchedLineNumber = lineNumber;
|
| + matchedColumnNumber = result.index;
|
| + return true;
|
| + }
|
| +
|
| + var iteratedLineNumber;
|
| + function lineIterator(regex, lineHandle)
|
| + {
|
| + if (findWordInLine(regex, iteratedLineNumber++, lineHandle.text, 0, lineHandle.text.length))
|
| + return true;
|
| + }
|
| +
|
| + var regexSource = textToFind.escapeForRegExp();
|
| + if (fullWord)
|
| + regexSource = "\\b" + regexSource + "\\b";
|
| + var wordRegex = new RegExp(regexSource, "gi");
|
| + var currentLineText = this._codeMirror.getLine(range.startLine);
|
| +
|
| + findWordInLine(wordRegex, range.startLine, currentLineText, range.endColumn, currentLineText.length);
|
| + iteratedLineNumber = range.startLine + 1;
|
| + this._codeMirror.eachLine(range.startLine + 1, this._codeMirror.lineCount(), lineIterator.bind(null, wordRegex));
|
| + iteratedLineNumber = 0;
|
| + this._codeMirror.eachLine(0, range.startLine, lineIterator.bind(null, wordRegex));
|
| + findWordInLine(wordRegex, range.startLine, currentLineText, 0, range.startColumn);
|
| +
|
| + if (typeof matchedLineNumber !== "number")
|
| + return null;
|
| + return new WebInspector.TextRange(matchedLineNumber, matchedColumnNumber, matchedLineNumber, matchedColumnNumber + textToFind.length);
|
| + }
|
| +}
|
| +
|
| +/**
|
| * @param {string} modeName
|
| * @param {string} tokenPrefix
|
| */
|
|
|