summaryrefslogtreecommitdiffstats
path: root/chromium/third_party/WebKit/Source/devtools/front_end/source_frame/CodeMirrorTextEditor.js
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/third_party/WebKit/Source/devtools/front_end/source_frame/CodeMirrorTextEditor.js')
-rw-r--r--chromium/third_party/WebKit/Source/devtools/front_end/source_frame/CodeMirrorTextEditor.js2136
1 files changed, 2136 insertions, 0 deletions
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/source_frame/CodeMirrorTextEditor.js b/chromium/third_party/WebKit/Source/devtools/front_end/source_frame/CodeMirrorTextEditor.js
new file mode 100644
index 00000000000..74c01b64bae
--- /dev/null
+++ b/chromium/third_party/WebKit/Source/devtools/front_end/source_frame/CodeMirrorTextEditor.js
@@ -0,0 +1,2136 @@
+/*
+ * Copyright (C) 2012 Google Inc. 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 Google Inc. 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 THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS 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.
+ */
+
+/**
+ * @constructor
+ * @extends {WebInspector.VBox}
+ * @implements {WebInspector.TextEditor}
+ * @param {?string} url
+ * @param {!WebInspector.TextEditorDelegate} delegate
+ */
+WebInspector.CodeMirrorTextEditor = function(url, delegate)
+{
+ WebInspector.VBox.call(this);
+ this._delegate = delegate;
+ this._url = url;
+
+ this.registerRequiredCSS("cm/codemirror.css");
+ this.registerRequiredCSS("cm/cmdevtools.css");
+
+ this._codeMirror = new window.CodeMirror(this.element, {
+ lineNumbers: true,
+ gutters: ["CodeMirror-linenumbers"],
+ matchBrackets: true,
+ smartIndent: false,
+ styleSelectedText: true,
+ electricChars: false,
+ });
+ this._codeMirror._codeMirrorTextEditor = this;
+
+ CodeMirror.keyMap["devtools-common"] = {
+ "Left": "goCharLeft",
+ "Right": "goCharRight",
+ "Up": "goLineUp",
+ "Down": "goLineDown",
+ "End": "goLineEnd",
+ "Home": "goLineStartSmart",
+ "PageUp": "goPageUp",
+ "PageDown": "goPageDown",
+ "Delete": "delCharAfter",
+ "Backspace": "delCharBefore",
+ "Tab": "defaultTab",
+ "Shift-Tab": "indentLess",
+ "Enter": "smartNewlineAndIndent",
+ "Ctrl-Space": "autocomplete",
+ "Esc": "dismissMultipleSelections"
+ };
+
+ CodeMirror.keyMap["devtools-pc"] = {
+ "Ctrl-A": "selectAll",
+ "Ctrl-Z": "undoAndReveal",
+ "Shift-Ctrl-Z": "redoAndReveal",
+ "Ctrl-Y": "redo",
+ "Ctrl-Home": "goDocStart",
+ "Ctrl-Up": "goDocStart",
+ "Ctrl-End": "goDocEnd",
+ "Ctrl-Down": "goDocEnd",
+ "Ctrl-Left": "goGroupLeft",
+ "Ctrl-Right": "goGroupRight",
+ "Alt-Left": "goLineStart",
+ "Alt-Right": "goLineEnd",
+ "Ctrl-Backspace": "delGroupBefore",
+ "Ctrl-Delete": "delGroupAfter",
+ "Ctrl-/": "toggleComment",
+ "Ctrl-D": "selectNextOccurrence",
+ "Ctrl-U": "undoLastSelection",
+ fallthrough: "devtools-common"
+ };
+
+ CodeMirror.keyMap["devtools-mac"] = {
+ "Cmd-A" : "selectAll",
+ "Cmd-Z" : "undoAndReveal",
+ "Shift-Cmd-Z": "redoAndReveal",
+ "Cmd-Up": "goDocStart",
+ "Cmd-Down": "goDocEnd",
+ "Alt-Left": "goGroupLeft",
+ "Alt-Right": "goGroupRight",
+ "Cmd-Left": "goLineStartSmart",
+ "Cmd-Right": "goLineEnd",
+ "Alt-Backspace": "delGroupBefore",
+ "Alt-Delete": "delGroupAfter",
+ "Cmd-/": "toggleComment",
+ "Cmd-D": "selectNextOccurrence",
+ "Cmd-U": "undoLastSelection",
+ fallthrough: "devtools-common"
+ };
+
+ WebInspector.settings.textEditorIndent.addChangeListener(this._updateEditorIndentation, this);
+ this._updateEditorIndentation();
+ WebInspector.settings.showWhitespacesInEditor.addChangeListener(this._updateCodeMirrorMode, this);
+ WebInspector.settings.textEditorBracketMatching.addChangeListener(this._enableBracketMatchingIfNeeded, this);
+ this._enableBracketMatchingIfNeeded();
+
+ this._codeMirror.setOption("keyMap", WebInspector.isMac() ? "devtools-mac" : "devtools-pc");
+ this._codeMirror.setOption("flattenSpans", false);
+
+ this._codeMirror.setOption("maxHighlightLength", WebInspector.CodeMirrorTextEditor.maxHighlightLength);
+ this._codeMirror.setOption("mode", null);
+ this._codeMirror.setOption("crudeMeasuringFrom", 1000);
+
+ this._shouldClearHistory = true;
+ this._lineSeparator = "\n";
+
+ this._autocompleteController = WebInspector.CodeMirrorTextEditor.AutocompleteController.Dummy;
+ 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._selectNextOccurrenceController = new WebInspector.CodeMirrorTextEditor.SelectNextOccurrenceController(this, this._codeMirror);
+
+ this._codeMirror.on("changes", this._changes.bind(this));
+ this._codeMirror.on("gutterClick", this._gutterClick.bind(this));
+ this._codeMirror.on("cursorActivity", this._cursorActivity.bind(this));
+ this._codeMirror.on("beforeSelectionChange", this._beforeSelectionChange.bind(this));
+ this._codeMirror.on("scroll", this._scroll.bind(this));
+ this._codeMirror.on("focus", this._focus.bind(this));
+ this.element.addEventListener("contextmenu", this._contextMenu.bind(this), false);
+ /**
+ * @this {WebInspector.CodeMirrorTextEditor}
+ */
+ function updateAnticipateJumpFlag(value)
+ {
+ this._isHandlingMouseDownEvent = value;
+ }
+ this.element.addEventListener("mousedown", updateAnticipateJumpFlag.bind(this, true), true);
+ this.element.addEventListener("mousedown", updateAnticipateJumpFlag.bind(this, false), false);
+
+ this.element.style.overflow = "hidden";
+ this.element.firstChild.classList.add("source-code");
+ this.element.firstChild.classList.add("fill");
+ this._elementToWidget = new Map();
+ this._nestedUpdatesCounter = 0;
+
+ this.element.addEventListener("focus", this._handleElementFocus.bind(this), false);
+ this.element.addEventListener("keydown", this._handleKeyDown.bind(this), true);
+ this.element.addEventListener("keydown", this._handlePostKeyDown.bind(this), false);
+ this.element.tabIndex = 0;
+
+ this._setupWhitespaceHighlight();
+}
+
+/** @typedef {{canceled: boolean, from: !CodeMirror.Pos, to: !CodeMirror.Pos, text: string, origin: string, cancel: function()}} */
+WebInspector.CodeMirrorTextEditor.BeforeChangeObject;
+
+/** @typedef {{from: !CodeMirror.Pos, to: !CodeMirror.Pos, origin: string, text: !Array.<string>, removed: !Array.<string>}} */
+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.undoLastSelectionCommand = function(codeMirror)
+{
+ codeMirror._codeMirrorTextEditor._selectNextOccurrenceController.undoLastSelection();
+}
+CodeMirror.commands.undoLastSelection = WebInspector.CodeMirrorTextEditor.undoLastSelectionCommand;
+
+/**
+ * @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));
+
+ function countIndent(line)
+ {
+ for (var i = 0; i < line.length; ++i) {
+ if (!WebInspector.TextUtils.isSpaceChar(line[i]))
+ return i;
+ }
+ return line.length;
+ }
+
+ function innerSmartNewlineAndIndent(codeMirror)
+ {
+ var cur = codeMirror.getCursor("start");
+ var line = codeMirror.getLine(cur.line);
+ var indent = cur.line > 0 ? countIndent(line) : 0;
+ if (cur.ch <= indent) {
+ codeMirror.replaceSelection("\n" + line.substring(0, cur.ch), "end", "+input");
+ codeMirror.setSelection(new CodeMirror.Pos(cur.line + 1, cur.ch));
+ } else
+ codeMirror.execCommand("newlineAndIndent");
+ }
+}
+
+CodeMirror.commands.undoAndReveal = function(codemirror)
+{
+ var scrollInfo = codemirror.getScrollInfo();
+ codemirror.execCommand("undo");
+ var cursor = codemirror.getCursor("start");
+ codemirror._codeMirrorTextEditor._innerRevealLine(cursor.line, scrollInfo);
+ codemirror._codeMirrorTextEditor._autocompleteController.finishAutocomplete();
+}
+
+CodeMirror.commands.redoAndReveal = function(codemirror)
+{
+ var scrollInfo = codemirror.getScrollInfo();
+ codemirror.execCommand("redo");
+ var cursor = codemirror.getCursor("start");
+ codemirror._codeMirrorTextEditor._innerRevealLine(cursor.line, scrollInfo);
+ codemirror._codeMirrorTextEditor._autocompleteController.finishAutocomplete();
+}
+
+/**
+ * @return {!Object|undefined}
+ */
+CodeMirror.commands.dismissMultipleSelections = function(codemirror)
+{
+ var selections = codemirror.listSelections();
+ var selection = selections[0];
+ if (selections.length === 1) {
+ if (codemirror._codeMirrorTextEditor._isSearchActive())
+ return CodeMirror.Pass;
+ if (WebInspector.CodeMirrorUtils.toRange(selection.anchor, selection.head).isEmpty())
+ return CodeMirror.Pass;
+ codemirror.setSelection(selection.anchor, selection.anchor, {scroll: false});
+ codemirror._codeMirrorTextEditor._revealLine(selection.anchor.line);
+ return;
+ }
+
+ codemirror.setSelection(selection.anchor, selection.head, {scroll: false});
+ codemirror._codeMirrorTextEditor._revealLine(selection.anchor.line);
+}
+
+WebInspector.CodeMirrorTextEditor.LongLineModeLineLengthThreshold = 2000;
+WebInspector.CodeMirrorTextEditor.MaximumNumberOfWhitespacesPerSingleSpan = 16;
+WebInspector.CodeMirrorTextEditor.MaxEditableTextSize = 1024 * 1024 * 10;
+
+WebInspector.CodeMirrorTextEditor.prototype = {
+ dispose: function()
+ {
+ WebInspector.settings.textEditorIndent.removeChangeListener(this._updateEditorIndentation, this);
+ WebInspector.settings.showWhitespacesInEditor.removeChangeListener(this._updateCodeMirrorMode, this);
+ WebInspector.settings.textEditorBracketMatching.removeChangeListener(this._enableBracketMatchingIfNeeded, this);
+ },
+
+ _enableBracketMatchingIfNeeded: function()
+ {
+ this._codeMirror.setOption("autoCloseBrackets", WebInspector.settings.textEditorBracketMatching.get() ? { explode: false } : false);
+ },
+
+ wasShown: function()
+ {
+ if (this._wasOnceShown)
+ return;
+ this._wasOnceShown = true;
+ this._codeMirror.refresh();
+ },
+
+ _guessIndentationLevel: function()
+ {
+ var tabRegex = /^\t+/;
+ var tabLines = 0;
+ var indents = {};
+ function processLine(lineHandle)
+ {
+ var text = lineHandle.text;
+ if (text.length === 0 || !WebInspector.TextUtils.isSpaceChar(text[0]))
+ return;
+ if (tabRegex.test(text)) {
+ ++tabLines;
+ return;
+ }
+ var i = 0;
+ while (i < text.length && WebInspector.TextUtils.isSpaceChar(text[i]))
+ ++i;
+ if (i % 2 !== 0)
+ return;
+ indents[i] = 1 + (indents[i] || 0);
+ }
+ this._codeMirror.eachLine(0, 1000, processLine);
+
+ var onePercentFilterThreshold = this.linesCount / 100;
+ if (tabLines && tabLines > onePercentFilterThreshold)
+ return "\t";
+ var minimumIndent = Infinity;
+ for (var i in indents) {
+ if (indents[i] < onePercentFilterThreshold)
+ continue;
+ var indent = parseInt(i, 10);
+ if (minimumIndent > indent)
+ minimumIndent = indent;
+ }
+ if (minimumIndent === Infinity)
+ return WebInspector.TextUtils.Indent.FourSpaces;
+ return new Array(minimumIndent + 1).join(" ");
+ },
+
+ _updateEditorIndentation: function()
+ {
+ var extraKeys = {};
+ var indent = WebInspector.settings.textEditorIndent.get();
+ if (WebInspector.settings.textEditorAutoDetectIndent.get())
+ indent = this._guessIndentationLevel();
+ if (indent === WebInspector.TextUtils.Indent.TabCharacter) {
+ this._codeMirror.setOption("indentWithTabs", true);
+ this._codeMirror.setOption("indentUnit", 4);
+ } else {
+ this._codeMirror.setOption("indentWithTabs", false);
+ this._codeMirror.setOption("indentUnit", indent.length);
+ extraKeys.Tab = function(codeMirror)
+ {
+ if (codeMirror.somethingSelected())
+ return CodeMirror.Pass;
+ var pos = codeMirror.getCursor("head");
+ codeMirror.replaceRange(indent.substring(pos.ch % indent.length), codeMirror.getCursor());
+ }
+ }
+ this._codeMirror.setOption("extraKeys", extraKeys);
+ this._indentationLevel = indent;
+ },
+
+ /**
+ * @return {string}
+ */
+ indent: function()
+ {
+ return this._indentationLevel;
+ },
+
+ /**
+ * @return {boolean}
+ */
+ _isSearchActive: function()
+ {
+ return !!this._tokenHighlighter.highlightedRegex();
+ },
+
+ /**
+ * @param {!RegExp} regex
+ * @param {?WebInspector.TextRange} range
+ */
+ highlightSearchResults: function(regex, range)
+ {
+ /**
+ * @this {WebInspector.CodeMirrorTextEditor}
+ */
+ function innerHighlightRegex()
+ {
+ if (range) {
+ this._revealLine(range.startLine);
+ if (range.endColumn > WebInspector.CodeMirrorTextEditor.maxHighlightLength)
+ this.setSelection(range);
+ else
+ this.setSelection(WebInspector.TextRange.createFromLocation(range.startLine, range.startColumn));
+ } else {
+ // 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);
+ }
+ if (!this._selectionBeforeSearch)
+ this._selectionBeforeSearch = this.selection();
+ this._codeMirror.operation(innerHighlightRegex.bind(this));
+ },
+
+ cancelSearchResultsHighlight: function()
+ {
+ this._codeMirror.operation(this._tokenHighlighter.highlightSelectedTokens.bind(this._tokenHighlighter));
+ if (this._selectionBeforeSearch) {
+ this._reportJump(this._selectionBeforeSearch, this.selection());
+ delete this._selectionBeforeSearch;
+ }
+ },
+
+ undo: function()
+ {
+ this._codeMirror.undo();
+ },
+
+ redo: function()
+ {
+ this._codeMirror.redo();
+ },
+
+ _setupWhitespaceHighlight: function()
+ {
+ if (WebInspector.CodeMirrorTextEditor._whitespaceStyleInjected || !WebInspector.settings.showWhitespacesInEditor.get())
+ return;
+ WebInspector.CodeMirrorTextEditor._whitespaceStyleInjected = true;
+ const classBase = ".show-whitespaces .CodeMirror .cm-whitespace-";
+ const spaceChar = "ยท";
+ var spaceChars = "";
+ var rules = "";
+ for (var i = 1; i <= WebInspector.CodeMirrorTextEditor.MaximumNumberOfWhitespacesPerSingleSpan; ++i) {
+ spaceChars += spaceChar;
+ var rule = classBase + i + "::before { content: '" + spaceChars + "';}\n";
+ rules += rule;
+ }
+ var style = document.createElement("style");
+ style.textContent = rules;
+ document.head.appendChild(style);
+ },
+
+ _handleKeyDown: function(e)
+ {
+ if (this._autocompleteController.keyDown(e))
+ e.consume(true);
+ },
+
+ _handlePostKeyDown: function(e)
+ {
+ if (e.defaultPrevented)
+ e.consume(true);
+ },
+
+ /**
+ * @param {?WebInspector.CompletionDictionary} dictionary
+ */
+ setCompletionDictionary: function(dictionary)
+ {
+ this._autocompleteController.dispose();
+ if (dictionary)
+ this._autocompleteController = new WebInspector.CodeMirrorTextEditor.AutocompleteController(this, this._codeMirror, dictionary);
+ else
+ this._autocompleteController = WebInspector.CodeMirrorTextEditor.AutocompleteController.Dummy;
+ },
+
+ /**
+ * @param {number} lineNumber
+ * @param {number} column
+ * @return {?{x: number, y: number, height: number}}
+ */
+ cursorPositionToCoordinates: function(lineNumber, column)
+ {
+ if (lineNumber >= this._codeMirror.lineCount() || lineNumber < 0 || column < 0 || column > this._codeMirror.getLine(lineNumber).length)
+ return null;
+
+ var metrics = this._codeMirror.cursorCoords(new CodeMirror.Pos(lineNumber, column));
+
+ return {
+ x: metrics.left,
+ y: metrics.top,
+ height: metrics.bottom - metrics.top
+ };
+ },
+
+ /**
+ * @param {number} x
+ * @param {number} y
+ * @return {?WebInspector.TextRange}
+ */
+ coordinatesToCursorPosition: function(x, y)
+ {
+ var element = document.elementFromPoint(x, y);
+ if (!element || !element.isSelfOrDescendant(this._codeMirror.getWrapperElement()))
+ return null;
+ var gutterBox = this._codeMirror.getGutterElement().boxInWindow();
+ if (x >= gutterBox.x && x <= gutterBox.x + gutterBox.width &&
+ y >= gutterBox.y && y <= gutterBox.y + gutterBox.height)
+ return null;
+ var coords = this._codeMirror.coordsChar({left: x, top: y});
+ return WebInspector.CodeMirrorUtils.toRange(coords, coords);
+ },
+
+ /**
+ * @param {number} lineNumber
+ * @param {number} column
+ * @return {?{startColumn: number, endColumn: number, type: string}}
+ */
+ tokenAtTextPosition: function(lineNumber, column)
+ {
+ if (lineNumber < 0 || lineNumber >= this._codeMirror.lineCount())
+ return null;
+ var token = this._codeMirror.getTokenAt(new CodeMirror.Pos(lineNumber, (column || 0) + 1));
+ if (!token || !token.type)
+ return null;
+ return {
+ startColumn: token.start,
+ endColumn: token.end - 1,
+ type: token.type
+ };
+ },
+
+ /**
+ * @param {!WebInspector.TextRange} textRange
+ * @return {string}
+ */
+ copyRange: function(textRange)
+ {
+ var pos = WebInspector.CodeMirrorUtils.toPos(textRange.normalize());
+ return this._codeMirror.getRange(pos.start, pos.end);
+ },
+
+ /**
+ * @return {boolean}
+ */
+ isClean: function()
+ {
+ return this._codeMirror.isClean();
+ },
+
+ markClean: function()
+ {
+ this._codeMirror.markClean();
+ },
+
+ _hasLongLines: function()
+ {
+ function lineIterator(lineHandle)
+ {
+ if (lineHandle.text.length > WebInspector.CodeMirrorTextEditor.LongLineModeLineLengthThreshold)
+ hasLongLines = true;
+ return hasLongLines;
+ }
+ var hasLongLines = false;
+ this._codeMirror.eachLine(lineIterator);
+ return hasLongLines;
+ },
+
+ /**
+ * @param {string} mimeType
+ * @return {string}
+ */
+ _whitespaceOverlayMode: function(mimeType)
+ {
+ var modeName = CodeMirror.mimeModes[mimeType] ? (CodeMirror.mimeModes[mimeType].name || CodeMirror.mimeModes[mimeType]) : CodeMirror.mimeModes["text/plain"];
+ modeName += "+whitespaces";
+ if (CodeMirror.modes[modeName])
+ return modeName;
+
+ function modeConstructor(config, parserConfig)
+ {
+ function nextToken(stream)
+ {
+ if (stream.peek() === " ") {
+ var spaces = 0;
+ while (spaces < WebInspector.CodeMirrorTextEditor.MaximumNumberOfWhitespacesPerSingleSpan && stream.peek() === " ") {
+ ++spaces;
+ stream.next();
+ }
+ return "whitespace whitespace-" + spaces;
+ }
+ while (!stream.eol() && stream.peek() !== " ")
+ stream.next();
+ return null;
+ }
+ var whitespaceMode = {
+ token: nextToken
+ };
+ return CodeMirror.overlayMode(CodeMirror.getMode(config, mimeType), whitespaceMode, false);
+ }
+ CodeMirror.defineMode(modeName, modeConstructor);
+ return modeName;
+ },
+
+ _enableLongLinesMode: function()
+ {
+ this._codeMirror.setOption("styleSelectedText", false);
+ this._longLinesMode = true;
+ },
+
+ _disableLongLinesMode: function()
+ {
+ this._codeMirror.setOption("styleSelectedText", true);
+ this._longLinesMode = false;
+ },
+
+ _updateCodeMirrorMode: function()
+ {
+ var showWhitespaces = WebInspector.settings.showWhitespacesInEditor.get();
+ this.element.classList.toggle("show-whitespaces", showWhitespaces);
+ this._codeMirror.setOption("mode", showWhitespaces ? this._whitespaceOverlayMode(this._mimeType) : this._mimeType);
+ },
+
+ /**
+ * @param {string} mimeType
+ */
+ setMimeType: function(mimeType)
+ {
+ this._mimeType = mimeType;
+ if (this._hasLongLines())
+ this._enableLongLinesMode();
+ else
+ this._disableLongLinesMode();
+ this._updateCodeMirrorMode();
+ this._autocompleteController.setMimeType(mimeType);
+ },
+
+ /**
+ * @param {boolean} readOnly
+ */
+ setReadOnly: function(readOnly)
+ {
+ this.element.classList.toggle("CodeMirror-readonly", readOnly)
+ this._codeMirror.setOption("readOnly", readOnly);
+ },
+
+ /**
+ * @return {boolean}
+ */
+ readOnly: function()
+ {
+ return !!this._codeMirror.getOption("readOnly");
+ },
+
+ /**
+ * @param {!Object} highlightDescriptor
+ */
+ removeHighlight: function(highlightDescriptor)
+ {
+ highlightDescriptor.clear();
+ },
+
+ /**
+ * @param {!WebInspector.TextRange} range
+ * @param {string} cssClass
+ * @return {!Object}
+ */
+ highlightRange: function(range, cssClass)
+ {
+ cssClass = "CodeMirror-persist-highlight " + cssClass;
+ var pos = WebInspector.CodeMirrorUtils.toPos(range);
+ ++pos.end.ch;
+ return this._codeMirror.markText(pos.start, pos.end, {
+ className: cssClass,
+ startStyle: cssClass + "-start",
+ endStyle: cssClass + "-end"
+ });
+ },
+
+ /**
+ * @return {!Element}
+ */
+ defaultFocusedElement: function()
+ {
+ return this.element;
+ },
+
+ focus: function()
+ {
+ this._codeMirror.focus();
+ },
+
+ _handleElementFocus: function()
+ {
+ this._codeMirror.focus();
+ },
+
+ beginUpdates: function()
+ {
+ ++this._nestedUpdatesCounter;
+ },
+
+ endUpdates: function()
+ {
+ if (!--this._nestedUpdatesCounter)
+ this._codeMirror.refresh();
+ },
+
+ /**
+ * @param {number} lineNumber
+ */
+ _revealLine: function(lineNumber)
+ {
+ this._innerRevealLine(lineNumber, this._codeMirror.getScrollInfo());
+ },
+
+ /**
+ * @param {number} lineNumber
+ * @param {!{left: number, top: number, width: number, height: number, clientWidth: number, clientHeight: number}} scrollInfo
+ */
+ _innerRevealLine: function(lineNumber, scrollInfo)
+ {
+ var topLine = this._codeMirror.lineAtHeight(scrollInfo.top, "local");
+ var bottomLine = this._codeMirror.lineAtHeight(scrollInfo.top + scrollInfo.clientHeight, "local");
+ var linesPerScreen = bottomLine - topLine + 1;
+ if (lineNumber < topLine) {
+ var topLineToReveal = Math.max(lineNumber - (linesPerScreen / 2) + 1, 0) | 0;
+ this._codeMirror.scrollIntoView(new CodeMirror.Pos(topLineToReveal, 0));
+ } else if (lineNumber > bottomLine) {
+ var bottomLineToReveal = Math.min(lineNumber + (linesPerScreen / 2) - 1, this.linesCount - 1) | 0;
+ this._codeMirror.scrollIntoView(new CodeMirror.Pos(bottomLineToReveal, 0));
+ }
+ },
+
+ _gutterClick: function(instance, lineNumber, gutter, event)
+ {
+ this.dispatchEventToListeners(WebInspector.TextEditor.Events.GutterClick, { lineNumber: lineNumber, event: event });
+ },
+
+ _contextMenu: function(event)
+ {
+ var contextMenu = new WebInspector.ContextMenu(event);
+ var target = event.target.enclosingNodeOrSelfWithClass("CodeMirror-gutter-elt");
+ if (target)
+ this._delegate.populateLineGutterContextMenu(contextMenu, parseInt(target.textContent, 10) - 1);
+ else
+ this._delegate.populateTextAreaContextMenu(contextMenu, 0);
+ contextMenu.show();
+ },
+
+ /**
+ * @param {number} lineNumber
+ * @param {boolean} disabled
+ * @param {boolean} conditional
+ */
+ addBreakpoint: function(lineNumber, disabled, conditional)
+ {
+ if (lineNumber < 0 || lineNumber >= this._codeMirror.lineCount())
+ return;
+ var className = "cm-breakpoint" + (conditional ? " cm-breakpoint-conditional" : "") + (disabled ? " cm-breakpoint-disabled" : "");
+ this._codeMirror.addLineClass(lineNumber, "wrap", className);
+ },
+
+ /**
+ * @param {number} lineNumber
+ */
+ removeBreakpoint: function(lineNumber)
+ {
+ if (lineNumber < 0 || lineNumber >= this._codeMirror.lineCount())
+ return;
+ var wrapClasses = this._codeMirror.getLineHandle(lineNumber).wrapClass;
+ if (!wrapClasses)
+ return;
+ var classes = wrapClasses.split(" ");
+ for (var i = 0; i < classes.length; ++i) {
+ if (classes[i].startsWith("cm-breakpoint"))
+ this._codeMirror.removeLineClass(lineNumber, "wrap", classes[i]);
+ }
+ },
+
+ /**
+ * @param {number} lineNumber
+ */
+ setExecutionLine: function(lineNumber)
+ {
+ this.clearPositionHighlight();
+ this._executionLine = this._codeMirror.getLineHandle(lineNumber);
+ if (!this._executionLine)
+ return;
+ this._codeMirror.addLineClass(this._executionLine, "wrap", "cm-execution-line");
+ },
+
+ clearExecutionLine: function()
+ {
+ this.clearPositionHighlight();
+ if (this._executionLine)
+ this._codeMirror.removeLineClass(this._executionLine, "wrap", "cm-execution-line");
+ delete this._executionLine;
+ },
+
+ /**
+ * @param {number} lineNumber
+ * @param {!Element} element
+ */
+ addDecoration: function(lineNumber, element)
+ {
+ var widget = this._codeMirror.addLineWidget(lineNumber, element);
+ this._elementToWidget.put(element, widget);
+ },
+
+ /**
+ * @param {number} lineNumber
+ * @param {!Element} element
+ */
+ removeDecoration: function(lineNumber, element)
+ {
+ var widget = this._elementToWidget.remove(element);
+ if (widget)
+ this._codeMirror.removeLineWidget(widget);
+ },
+
+ /**
+ * @param {number} lineNumber
+ * @param {number=} columnNumber
+ * @param {boolean=} shouldHighlight
+ */
+ revealPosition: function(lineNumber, columnNumber, shouldHighlight)
+ {
+ lineNumber = Number.constrain(lineNumber, 0, this._codeMirror.lineCount() - 1);
+ if (typeof columnNumber !== "number")
+ columnNumber = 0;
+ columnNumber = Number.constrain(columnNumber, 0, this._codeMirror.getLine(lineNumber).length);
+
+ this.clearPositionHighlight();
+ this._highlightedLine = this._codeMirror.getLineHandle(lineNumber);
+ if (!this._highlightedLine)
+ return;
+ this._revealLine(lineNumber);
+ if (shouldHighlight) {
+ this._codeMirror.addLineClass(this._highlightedLine, null, "cm-highlight");
+ this._clearHighlightTimeout = setTimeout(this.clearPositionHighlight.bind(this), 2000);
+ }
+ this.setSelection(WebInspector.TextRange.createFromLocation(lineNumber, columnNumber));
+ },
+
+ clearPositionHighlight: function()
+ {
+ if (this._clearHighlightTimeout)
+ clearTimeout(this._clearHighlightTimeout);
+ delete this._clearHighlightTimeout;
+
+ if (this._highlightedLine)
+ this._codeMirror.removeLineClass(this._highlightedLine, null, "cm-highlight");
+ delete this._highlightedLine;
+ },
+
+ /**
+ * @return {!Array.<!Element>}
+ */
+ elementsToRestoreScrollPositionsFor: function()
+ {
+ return [];
+ },
+
+ /**
+ * @param {!WebInspector.TextEditor} textEditor
+ */
+ inheritScrollPositions: function(textEditor)
+ {
+ },
+
+ /**
+ * @param {number} width
+ * @param {number} height
+ */
+ _updatePaddingBottom: function(width, height)
+ {
+ var scrollInfo = this._codeMirror.getScrollInfo();
+ var newPaddingBottom;
+ var linesElement = this.element.firstElementChild.querySelector(".CodeMirror-lines");
+ var lineCount = this._codeMirror.lineCount();
+ if (lineCount <= 1)
+ newPaddingBottom = 0;
+ else
+ newPaddingBottom = Math.max(scrollInfo.clientHeight - this._codeMirror.getLineHandle(this._codeMirror.lastLine()).height, 0);
+ newPaddingBottom += "px";
+ linesElement.style.paddingBottom = newPaddingBottom;
+ this._codeMirror.setSize(width, height);
+ },
+
+ _resizeEditor: function()
+ {
+ var parentElement = this.element.parentElement;
+ if (!parentElement || !this.isShowing())
+ return;
+ var scrollLeft = this._codeMirror.doc.scrollLeft;
+ var scrollTop = this._codeMirror.doc.scrollTop;
+ var width = parentElement.offsetWidth;
+ var height = parentElement.offsetHeight;
+ this._codeMirror.setSize(width, height);
+ this._updatePaddingBottom(width, height);
+ this._codeMirror.scrollTo(scrollLeft, scrollTop);
+ },
+
+ onResize: function()
+ {
+ this._autocompleteController.finishAutocomplete();
+ this._resizeEditor();
+ },
+
+ /**
+ * @param {!WebInspector.TextRange} range
+ * @param {string} text
+ * @return {!WebInspector.TextRange}
+ */
+ editRange: function(range, text)
+ {
+ var pos = WebInspector.CodeMirrorUtils.toPos(range);
+ this._codeMirror.replaceRange(text, pos.start, pos.end);
+ var newRange = WebInspector.CodeMirrorUtils.toRange(pos.start, this._codeMirror.posFromIndex(this._codeMirror.indexFromPos(pos.start) + text.length));
+ this._delegate.onTextChanged(range, newRange);
+ if (WebInspector.settings.textEditorAutoDetectIndent.get())
+ this._updateEditorIndentation();
+ return newRange;
+ },
+
+ /**
+ * @param {number} lineNumber
+ * @param {number} column
+ * @param {function(string):boolean} isWordChar
+ * @return {!WebInspector.TextRange}
+ */
+ _wordRangeForCursorPosition: function(lineNumber, column, isWordChar)
+ {
+ var line = this.line(lineNumber);
+ var wordStart = column;
+ if (column !== 0 && isWordChar(line.charAt(column - 1))) {
+ wordStart = column - 1;
+ while (wordStart > 0 && isWordChar(line.charAt(wordStart - 1)))
+ --wordStart;
+ }
+ var wordEnd = column;
+ while (wordEnd < line.length && isWordChar(line.charAt(wordEnd)))
+ ++wordEnd;
+ return new WebInspector.TextRange(lineNumber, wordStart, lineNumber, wordEnd);
+ },
+
+ /**
+ * @param {!WebInspector.CodeMirrorTextEditor.ChangeObject} changeObject
+ * @return {{oldRange: !WebInspector.TextRange, newRange: !WebInspector.TextRange}}
+ */
+ _changeObjectToEditOperation: function(changeObject)
+ {
+ var oldRange = WebInspector.CodeMirrorUtils.toRange(changeObject.from, changeObject.to);
+ var newRange = oldRange.clone();
+ var linesAdded = changeObject.text.length;
+ if (linesAdded === 0) {
+ newRange.endLine = newRange.startLine;
+ newRange.endColumn = newRange.startColumn;
+ } else if (linesAdded === 1) {
+ newRange.endLine = newRange.startLine;
+ newRange.endColumn = newRange.startColumn + changeObject.text[0].length;
+ } else {
+ newRange.endLine = newRange.startLine + linesAdded - 1;
+ newRange.endColumn = changeObject.text[linesAdded - 1].length;
+ }
+ return {
+ oldRange: oldRange,
+ newRange: newRange
+ };
+ },
+
+ /**
+ * @param {!CodeMirror} codeMirror
+ * @param {!Array.<!WebInspector.CodeMirrorTextEditor.ChangeObject>} changes
+ */
+ _changes: function(codeMirror, changes)
+ {
+ if (!changes.length)
+ return;
+ // We do not show "scroll beyond end of file" span for one line documents, so we need to check if "document has one line" changed.
+ var hasOneLine = this._codeMirror.lineCount() === 1;
+ if (hasOneLine !== this._hasOneLine)
+ this._resizeEditor();
+ this._hasOneLine = hasOneLine;
+ var widgets = this._elementToWidget.values();
+ for (var i = 0; i < widgets.length; ++i)
+ this._codeMirror.removeLineWidget(widgets[i]);
+ this._elementToWidget.clear();
+
+ for (var changeIndex = 0; changeIndex < changes.length; ++changeIndex) {
+ var changeObject = changes[changeIndex];
+
+ var editInfo = this._changeObjectToEditOperation(changeObject);
+ if (!this._muteTextChangedEvent)
+ this._delegate.onTextChanged(editInfo.oldRange, editInfo.newRange);
+ }
+ },
+
+ _cursorActivity: function()
+ {
+ var start = this._codeMirror.getCursor("anchor");
+ var end = this._codeMirror.getCursor("head");
+ this._delegate.selectionChanged(WebInspector.CodeMirrorUtils.toRange(start, end));
+ if (!this._isSearchActive())
+ this._codeMirror.operation(this._tokenHighlighter.highlightSelectedTokens.bind(this._tokenHighlighter));
+ },
+
+ /**
+ * @param {!CodeMirror} codeMirror
+ * @param {{ranges: !Array.<{head: !CodeMirror.Pos, anchor: !CodeMirror.Pos}>}} selection
+ */
+ _beforeSelectionChange: function(codeMirror, selection)
+ {
+ this._selectNextOccurrenceController.selectionWillChange();
+ if (!this._isHandlingMouseDownEvent)
+ return;
+ if (!selection.ranges.length)
+ return;
+ var primarySelection = selection.ranges[0];
+ this._reportJump(this.selection(), WebInspector.CodeMirrorUtils.toRange(primarySelection.anchor, primarySelection.head));
+ },
+
+ /**
+ * @param {?WebInspector.TextRange} from
+ * @param {?WebInspector.TextRange} to
+ */
+ _reportJump: function(from, to)
+ {
+ if (from && to && from.equal(to))
+ return;
+ this._delegate.onJumpToPosition(from, to);
+ },
+
+ _scroll: function()
+ {
+ if (this._scrollTimer)
+ clearTimeout(this._scrollTimer);
+ var topmostLineNumber = this._codeMirror.lineAtHeight(this._codeMirror.getScrollInfo().top, "local");
+ this._scrollTimer = setTimeout(this._delegate.scrollChanged.bind(this._delegate, topmostLineNumber), 100);
+ },
+
+ _focus: function()
+ {
+ this._delegate.editorFocused();
+ },
+
+ /**
+ * @param {number} lineNumber
+ */
+ scrollToLine: function(lineNumber)
+ {
+ var pos = new CodeMirror.Pos(lineNumber, 0);
+ var coords = this._codeMirror.charCoords(pos, "local");
+ this._codeMirror.scrollTo(0, coords.top);
+ },
+
+ /**
+ * @return {number}
+ */
+ firstVisibleLine: function()
+ {
+ return this._codeMirror.lineAtHeight(this._codeMirror.getScrollInfo().top, "local");
+ },
+
+ /**
+ * @return {number}
+ */
+ lastVisibleLine: function()
+ {
+ var scrollInfo = this._codeMirror.getScrollInfo();
+ return this._codeMirror.lineAtHeight(scrollInfo.top + scrollInfo.clientHeight, "local");
+ },
+
+ /**
+ * @return {!WebInspector.TextRange}
+ */
+ selection: function()
+ {
+ var start = this._codeMirror.getCursor("anchor");
+ var end = this._codeMirror.getCursor("head");
+
+ return WebInspector.CodeMirrorUtils.toRange(start, end);
+ },
+
+ /**
+ * @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(WebInspector.CodeMirrorUtils.toRange(selection.anchor, selection.head));
+ }
+ return result;
+ },
+
+ /**
+ * @return {?WebInspector.TextRange}
+ */
+ lastSelection: function()
+ {
+ return this._lastSelection;
+ },
+
+ /**
+ * @param {!WebInspector.TextRange} textRange
+ */
+ setSelection: function(textRange)
+ {
+ this._lastSelection = textRange;
+ var pos = WebInspector.CodeMirrorUtils.toPos(textRange);
+ this._codeMirror.setSelection(pos.start, pos.end);
+ },
+
+ /**
+ * @param {!Array.<!WebInspector.TextRange>} ranges
+ * @param {number=} primarySelectionIndex
+ */
+ setSelections: function(ranges, primarySelectionIndex)
+ {
+ var selections = [];
+ for (var i = 0; i < ranges.length; ++i) {
+ var selection = WebInspector.CodeMirrorUtils.toPos(ranges[i]);
+ selections.push({
+ anchor: selection.start,
+ head: selection.end
+ });
+ }
+ primarySelectionIndex = primarySelectionIndex || 0;
+ this._codeMirror.setSelections(selections, primarySelectionIndex, { scroll: false });
+ },
+
+ /**
+ * @param {string} text
+ */
+ _detectLineSeparator: function(text)
+ {
+ this._lineSeparator = text.indexOf("\r\n") >= 0 ? "\r\n" : "\n";
+ },
+
+ /**
+ * @param {string} text
+ */
+ setText: function(text)
+ {
+ this._muteTextChangedEvent = true;
+ if (text.length > WebInspector.CodeMirrorTextEditor.MaxEditableTextSize) {
+ this._autocompleteController.setEnabled(false);
+ this.setReadOnly(true);
+ }
+ this._codeMirror.setValue(text);
+ this._updateEditorIndentation();
+ if (this._shouldClearHistory) {
+ this._codeMirror.clearHistory();
+ this._shouldClearHistory = false;
+ }
+ this._detectLineSeparator(text);
+ delete this._muteTextChangedEvent;
+ },
+
+ /**
+ * @return {string}
+ */
+ text: function()
+ {
+ return this._codeMirror.getValue().replace(/\n/g, this._lineSeparator);
+ },
+
+ /**
+ * @return {!WebInspector.TextRange}
+ */
+ range: function()
+ {
+ var lineCount = this.linesCount;
+ var lastLine = this._codeMirror.getLine(lineCount - 1);
+ return WebInspector.CodeMirrorUtils.toRange(new CodeMirror.Pos(0, 0), new CodeMirror.Pos(lineCount - 1, lastLine.length));
+ },
+
+ /**
+ * @param {number} lineNumber
+ * @return {string}
+ */
+ line: function(lineNumber)
+ {
+ return this._codeMirror.getLine(lineNumber);
+ },
+
+ /**
+ * @return {number}
+ */
+ get linesCount()
+ {
+ return this._codeMirror.lineCount();
+ },
+
+ /**
+ * @param {number} line
+ * @param {string} name
+ * @param {?Object} value
+ */
+ setAttribute: function(line, name, value)
+ {
+ if (line < 0 || line >= this._codeMirror.lineCount())
+ return;
+ var handle = this._codeMirror.getLineHandle(line);
+ if (handle.attributes === undefined) handle.attributes = {};
+ handle.attributes[name] = value;
+ },
+
+ /**
+ * @param {number} line
+ * @param {string} name
+ * @return {?Object} value
+ */
+ getAttribute: function(line, name)
+ {
+ if (line < 0 || line >= this._codeMirror.lineCount())
+ return null;
+ var handle = this._codeMirror.getLineHandle(line);
+ return handle.attributes && handle.attributes[name] !== undefined ? handle.attributes[name] : null;
+ },
+
+ /**
+ * @param {number} line
+ * @param {string} name
+ */
+ removeAttribute: function(line, name)
+ {
+ if (line < 0 || line >= this._codeMirror.lineCount())
+ return;
+ var handle = this._codeMirror.getLineHandle(line);
+ if (handle && handle.attributes)
+ delete handle.attributes[name];
+ },
+
+ /**
+ * @param {number} lineNumber
+ * @param {number} columnNumber
+ * @return {!WebInspector.TextEditorPositionHandle}
+ */
+ textEditorPositionHandle: function(lineNumber, columnNumber)
+ {
+ return new WebInspector.CodeMirrorPositionHandle(this._codeMirror, new CodeMirror.Pos(lineNumber, columnNumber));
+ },
+
+ __proto__: WebInspector.VBox.prototype
+}
+
+/**
+ * @constructor
+ * @implements {WebInspector.TextEditorPositionHandle}
+ * @param {!CodeMirror} codeMirror
+ * @param {!CodeMirror.Pos} pos
+ */
+WebInspector.CodeMirrorPositionHandle = function(codeMirror, pos)
+{
+ this._codeMirror = codeMirror;
+ this._lineHandle = codeMirror.getLineHandle(pos.line);
+ this._columnNumber = pos.ch;
+}
+
+WebInspector.CodeMirrorPositionHandle.prototype = {
+ /**
+ * @return {?{lineNumber: number, columnNumber: number}}
+ */
+ resolve: function()
+ {
+ var lineNumber = this._codeMirror.getLineNumber(this._lineHandle);
+ if (typeof lineNumber !== "number")
+ return null;
+ return {
+ lineNumber: lineNumber,
+ columnNumber: this._columnNumber
+ };
+ },
+
+ /**
+ * @param {!WebInspector.TextEditorPositionHandle} positionHandle
+ * @return {boolean}
+ */
+ equal: function(positionHandle)
+ {
+ return positionHandle._lineHandle === this._lineHandle && positionHandle._columnNumber == this._columnNumber && positionHandle._codeMirror === this._codeMirror;
+ }
+}
+
+/**
+ * @constructor
+ * @param {!WebInspector.CodeMirrorTextEditor} textEditor
+ * @param {!CodeMirror} codeMirror
+ */
+WebInspector.CodeMirrorTextEditor.TokenHighlighter = function(textEditor, codeMirror)
+{
+ this._textEditor = textEditor;
+ this._codeMirror = codeMirror;
+}
+
+WebInspector.CodeMirrorTextEditor.TokenHighlighter.prototype = {
+ /**
+ * @param {!RegExp} regex
+ * @param {?WebInspector.TextRange} range
+ */
+ highlightSearchResults: function(regex, range)
+ {
+ var oldRegex = this._highlightRegex;
+ this._highlightRegex = regex;
+ this._highlightRange = range;
+ if (this._searchResultMarker) {
+ this._searchResultMarker.clear();
+ delete this._searchResultMarker;
+ }
+ if (this._highlightDescriptor && this._highlightDescriptor.selectionStart)
+ this._codeMirror.removeLineClass(this._highlightDescriptor.selectionStart.line, "wrap", "cm-line-with-selection");
+ var selectionStart = this._highlightRange ? new CodeMirror.Pos(this._highlightRange.startLine, this._highlightRange.startColumn) : null;
+ if (selectionStart)
+ this._codeMirror.addLineClass(selectionStart.line, "wrap", "cm-line-with-selection");
+ if (this._highlightRegex === oldRegex) {
+ // Do not re-add overlay mode if regex did not change for better performance.
+ if (this._highlightDescriptor)
+ this._highlightDescriptor.selectionStart = selectionStart;
+ } else {
+ this._removeHighlight();
+ this._setHighlighter(this._searchHighlighter.bind(this, this._highlightRegex), selectionStart);
+ }
+ if (this._highlightRange) {
+ var pos = WebInspector.CodeMirrorUtils.toPos(this._highlightRange);
+ this._searchResultMarker = this._codeMirror.markText(pos.start, pos.end, {className: "cm-column-with-selection"});
+ }
+ },
+
+ /**
+ * @return {!RegExp|undefined}
+ */
+ highlightedRegex: function()
+ {
+ return this._highlightRegex;
+ },
+
+ highlightSelectedTokens: function()
+ {
+ delete this._highlightRegex;
+ delete this._highlightRange;
+
+ if (this._highlightDescriptor && this._highlightDescriptor.selectionStart)
+ this._codeMirror.removeLineClass(this._highlightDescriptor.selectionStart.line, "wrap", "cm-line-with-selection");
+ this._removeHighlight();
+ var selectionStart = this._codeMirror.getCursor("start");
+ var selectionEnd = this._codeMirror.getCursor("end");
+ if (selectionStart.line !== selectionEnd.line)
+ return;
+ if (selectionStart.ch === selectionEnd.ch)
+ return;
+
+ var selections = this._codeMirror.getSelections();
+ if (selections.length > 1)
+ return;
+ var selectedText = selections[0];
+ if (this._isWord(selectedText, selectionStart.line, selectionStart.ch, selectionEnd.ch)) {
+ if (selectionStart)
+ this._codeMirror.addLineClass(selectionStart.line, "wrap", "cm-line-with-selection")
+ this._setHighlighter(this._tokenHighlighter.bind(this, selectedText, selectionStart), selectionStart);
+ }
+ },
+
+ /**
+ * @param {string} selectedText
+ * @param {number} lineNumber
+ * @param {number} startColumn
+ * @param {number} endColumn
+ */
+ _isWord: function(selectedText, lineNumber, startColumn, endColumn)
+ {
+ var line = this._codeMirror.getLine(lineNumber);
+ var leftBound = startColumn === 0 || !WebInspector.TextUtils.isWordChar(line.charAt(startColumn - 1));
+ var rightBound = endColumn === line.length || !WebInspector.TextUtils.isWordChar(line.charAt(endColumn));
+ return leftBound && rightBound && WebInspector.TextUtils.isWord(selectedText);
+ },
+
+ _removeHighlight: function()
+ {
+ if (this._highlightDescriptor) {
+ this._codeMirror.removeOverlay(this._highlightDescriptor.overlay);
+ delete this._highlightDescriptor;
+ }
+ },
+
+ /**
+ * @param {!RegExp} regex
+ * @param {!CodeMirror.StringStream} stream
+ */
+ _searchHighlighter: function(regex, stream)
+ {
+ if (stream.column() === 0)
+ delete this._searchMatchLength;
+ if (this._searchMatchLength) {
+ if (this._searchMatchLength > 2) {
+ for (var i = 0; i < this._searchMatchLength - 2; ++i)
+ stream.next();
+ this._searchMatchLength = 1;
+ return "search-highlight";
+ } else {
+ stream.next();
+ delete this._searchMatchLength;
+ return "search-highlight search-highlight-end";
+ }
+ }
+ var match = stream.match(regex, false);
+ if (match) {
+ stream.next();
+ var matchLength = match[0].length;
+ if (matchLength === 1)
+ return "search-highlight search-highlight-full";
+ this._searchMatchLength = matchLength;
+ return "search-highlight search-highlight-start";
+ }
+
+ while (!stream.match(regex, false) && stream.next()) {};
+ },
+
+ /**
+ * @param {string} token
+ * @param {!CodeMirror.Pos} selectionStart
+ * @param {!CodeMirror.StringStream} stream
+ */
+ _tokenHighlighter: function(token, selectionStart, stream)
+ {
+ var tokenFirstChar = token.charAt(0);
+ if (stream.match(token) && (stream.eol() || !WebInspector.TextUtils.isWordChar(stream.peek())))
+ return stream.column() === selectionStart.ch ? "token-highlight column-with-selection" : "token-highlight";
+
+ var eatenChar;
+ do {
+ eatenChar = stream.next();
+ } while (eatenChar && (WebInspector.TextUtils.isWordChar(eatenChar) || stream.peek() !== tokenFirstChar));
+ },
+
+ /**
+ * @param {function(!CodeMirror.StringStream)} highlighter
+ * @param {?CodeMirror.Pos} selectionStart
+ */
+ _setHighlighter: function(highlighter, selectionStart)
+ {
+ var overlayMode = {
+ token: highlighter
+ };
+ this._codeMirror.addOverlay(overlayMode);
+ this._highlightDescriptor = {
+ overlay: overlayMode,
+ selectionStart: selectionStart
+ };
+ }
+}
+
+/**
+ * @constructor
+ * @param {!CodeMirror} codeMirror
+ */
+WebInspector.CodeMirrorTextEditor.BlockIndentController = function(codeMirror)
+{
+ codeMirror.addKeyMap(this);
+}
+
+WebInspector.CodeMirrorTextEditor.BlockIndentController.prototype = {
+ name: "blockIndentKeymap",
+
+ /**
+ * @return {*}
+ */
+ Enter: function(codeMirror)
+ {
+ if (codeMirror.somethingSelected())
+ return CodeMirror.Pass;
+ var cursor = codeMirror.getCursor();
+ if (cursor.ch === 0)
+ return CodeMirror.Pass;
+ var line = codeMirror.getLine(cursor.line);
+ if (line.substr(cursor.ch - 1, 2) === "{}") {
+ codeMirror.execCommand("newlineAndIndent");
+ codeMirror.setCursor(cursor);
+ codeMirror.execCommand("newlineAndIndent");
+ codeMirror.execCommand("indentMore");
+ } else if (line.substr(cursor.ch - 1, 1) === "{") {
+ codeMirror.execCommand("newlineAndIndent");
+ codeMirror.execCommand("indentMore");
+ } else
+ return CodeMirror.Pass;
+ },
+
+ /**
+ * @return {*}
+ */
+ "'}'": function(codeMirror)
+ {
+ var cursor = codeMirror.getCursor();
+ var line = codeMirror.getLine(cursor.line);
+ for (var i = 0 ; i < line.length; ++i) {
+ if (!WebInspector.TextUtils.isSpaceChar(line.charAt(i)))
+ return CodeMirror.Pass;
+ }
+
+ codeMirror.replaceRange("}", cursor);
+ var matchingBracket = codeMirror.findMatchingBracket(cursor);
+ if (!matchingBracket || !matchingBracket.match)
+ return;
+
+ line = codeMirror.getLine(matchingBracket.to.line);
+ var desiredIndentation = 0;
+ while (desiredIndentation < line.length && WebInspector.TextUtils.isSpaceChar(line.charAt(desiredIndentation)))
+ ++desiredIndentation;
+
+ codeMirror.replaceRange(line.substr(0, desiredIndentation) + "}", new CodeMirror.Pos(cursor.line, 0), new CodeMirror.Pos(cursor.line, cursor.ch + 1));
+ }
+}
+
+/**
+ * @constructor
+ * @param {!CodeMirror} codeMirror
+ */
+WebInspector.CodeMirrorTextEditor.FixWordMovement = function(codeMirror)
+{
+ function moveLeft(shift, codeMirror)
+ {
+ codeMirror.setExtending(shift);
+ var cursor = codeMirror.getCursor("head");
+ codeMirror.execCommand("goGroupLeft");
+ var newCursor = codeMirror.getCursor("head");
+ if (newCursor.ch === 0 && newCursor.line !== 0) {
+ codeMirror.setExtending(false);
+ return;
+ }
+
+ var skippedText = codeMirror.getRange(newCursor, cursor, "#");
+ if (/^\s+$/.test(skippedText))
+ codeMirror.execCommand("goGroupLeft");
+ codeMirror.setExtending(false);
+ }
+
+ function moveRight(shift, codeMirror)
+ {
+ codeMirror.setExtending(shift);
+ var cursor = codeMirror.getCursor("head");
+ codeMirror.execCommand("goGroupRight");
+ var newCursor = codeMirror.getCursor("head");
+ if (newCursor.ch === 0 && newCursor.line !== 0) {
+ codeMirror.setExtending(false);
+ return;
+ }
+
+ var skippedText = codeMirror.getRange(cursor, newCursor, "#");
+ if (/^\s+$/.test(skippedText))
+ codeMirror.execCommand("goGroupRight");
+ codeMirror.setExtending(false);
+ }
+
+ var modifierKey = WebInspector.isMac() ? "Alt" : "Ctrl";
+ var leftKey = modifierKey + "-Left";
+ var rightKey = modifierKey + "-Right";
+ var keyMap = {};
+ keyMap[leftKey] = moveLeft.bind(null, false);
+ keyMap[rightKey] = moveRight.bind(null, false);
+ keyMap["Shift-" + leftKey] = moveLeft.bind(null, true);
+ keyMap["Shift-" + rightKey] = moveRight.bind(null, true);
+ codeMirror.addKeyMap(keyMap);
+}
+
+/**
+ * @interface
+ */
+WebInspector.CodeMirrorTextEditor.AutocompleteControllerAPI = function() {}
+
+WebInspector.CodeMirrorTextEditor.AutocompleteControllerAPI.prototype = {
+ dispose: function() { },
+
+ /**
+ * @param {boolean} enabled
+ */
+ setEnabled: function(enabled) { },
+
+ /**
+ * @param {string} mimeType
+ */
+ setMimeType: function(mimeType) { },
+
+ autocomplete: function() { },
+
+ finishAutocomplete: function() { },
+
+ /**
+ * @param {?Event} e
+ * @return {boolean}
+ */
+ keyDown: function(e) { }
+}
+
+/**
+ * @constructor
+ * @implements {WebInspector.CodeMirrorTextEditor.AutocompleteControllerAPI}
+ */
+WebInspector.CodeMirrorTextEditor.DummyAutocompleteController = function() { }
+
+WebInspector.CodeMirrorTextEditor.DummyAutocompleteController.prototype = {
+ dispose: function() { },
+
+ /**
+ * @param {boolean} enabled
+ */
+ setEnabled: function(enabled) { },
+
+ /**
+ * @param {string} mimeType
+ */
+ setMimeType: function(mimeType) { },
+
+ autocomplete: function() { },
+
+ finishAutocomplete: function() { },
+
+ /**
+ * @param {?Event} e
+ * @return {boolean}
+ */
+ keyDown: function(e)
+ {
+ return false;
+ }
+}
+
+/**
+ * @constructor
+ * @implements {WebInspector.SuggestBoxDelegate}
+ * @implements {WebInspector.CodeMirrorTextEditor.AutocompleteControllerAPI}
+ * @param {!WebInspector.CodeMirrorTextEditor} textEditor
+ * @param {!CodeMirror} codeMirror
+ * @param {?WebInspector.CompletionDictionary} dictionary
+ */
+WebInspector.CodeMirrorTextEditor.AutocompleteController = function(textEditor, codeMirror, dictionary)
+{
+ this._textEditor = textEditor;
+ this._codeMirror = codeMirror;
+
+ this._onScroll = this._onScroll.bind(this);
+ this._onCursorActivity = this._onCursorActivity.bind(this);
+ this._changes = this._changes.bind(this);
+ this._beforeChange = this._beforeChange.bind(this);
+ this._blur = this._blur.bind(this);
+ this._codeMirror.on("scroll", this._onScroll);
+ this._codeMirror.on("cursorActivity", this._onCursorActivity);
+ this._codeMirror.on("changes", this._changes);
+ this._codeMirror.on("beforeChange", this._beforeChange);
+ this._codeMirror.on("blur", this._blur);
+
+ this._additionalWordChars = WebInspector.CodeMirrorTextEditor._NoAdditionalWordChars;
+ this._enabled = true;
+
+ this._dictionary = dictionary;
+ this._addTextToCompletionDictionary(this._textEditor.text());
+}
+
+WebInspector.CodeMirrorTextEditor.AutocompleteController.Dummy = new WebInspector.CodeMirrorTextEditor.DummyAutocompleteController();
+WebInspector.CodeMirrorTextEditor._NoAdditionalWordChars = {};
+WebInspector.CodeMirrorTextEditor._CSSAdditionalWordChars = { ".": true, "-": true };
+
+WebInspector.CodeMirrorTextEditor.AutocompleteController.prototype = {
+ dispose: function()
+ {
+ this._codeMirror.off("scroll", this._onScroll);
+ this._codeMirror.off("cursorActivity", this._onCursorActivity);
+ this._codeMirror.off("changes", this._changes);
+ this._codeMirror.off("beforeChange", this._beforeChange);
+ this._codeMirror.off("blur", this._blur);
+ },
+
+ /**
+ * @param {boolean} enabled
+ */
+ setEnabled: function(enabled)
+ {
+ if (enabled === this._enabled)
+ return;
+ this._enabled = enabled;
+ if (!enabled)
+ this._dictionary.reset();
+ else
+ this._addTextToCompletionDictionary(this._textEditor.text());
+ },
+
+ /**
+ * @param {string} mimeType
+ */
+ setMimeType: function(mimeType)
+ {
+ var additionalWordChars = mimeType.indexOf("css") !== -1 ? WebInspector.CodeMirrorTextEditor._CSSAdditionalWordChars : WebInspector.CodeMirrorTextEditor._NoAdditionalWordChars;
+ if (additionalWordChars !== this._additionalWordChars) {
+ this._additionalWordChars = additionalWordChars;
+ this._dictionary.reset();
+ this._addTextToCompletionDictionary(this._textEditor.text());
+ }
+ },
+
+ /**
+ * @param {string} char
+ * @return {boolean}
+ */
+ _isWordChar: function(char)
+ {
+ return WebInspector.TextUtils.isWordChar(char) || !!this._additionalWordChars[char];
+ },
+
+ /**
+ * @param {string} word
+ * @return {boolean}
+ */
+ _shouldProcessWordForAutocompletion: function(word)
+ {
+ return !!word.length && (word[0] < '0' || word[0] > '9');
+ },
+
+ /**
+ * @param {string} text
+ */
+ _addTextToCompletionDictionary: function(text)
+ {
+ if (!this._enabled)
+ return;
+ var words = WebInspector.TextUtils.textToWords(text, this._isWordChar.bind(this));
+ for (var i = 0; i < words.length; ++i) {
+ if (this._shouldProcessWordForAutocompletion(words[i]))
+ this._dictionary.addWord(words[i]);
+ }
+ },
+
+ /**
+ * @param {string} text
+ */
+ _removeTextFromCompletionDictionary: function(text)
+ {
+ if (!this._enabled)
+ return;
+ var words = WebInspector.TextUtils.textToWords(text, this._isWordChar.bind(this));
+ for (var i = 0; i < words.length; ++i) {
+ if (this._shouldProcessWordForAutocompletion(words[i]))
+ this._dictionary.removeWord(words[i]);
+ }
+ },
+
+ /**
+ * @param {!CodeMirror} codeMirror
+ * @param {!WebInspector.CodeMirrorTextEditor.BeforeChangeObject} changeObject
+ */
+ _beforeChange: function(codeMirror, changeObject)
+ {
+ if (!this._enabled)
+ return;
+ this._updatedLines = this._updatedLines || {};
+ for (var i = changeObject.from.line; i <= changeObject.to.line; ++i)
+ this._updatedLines[i] = this._textEditor.line(i);
+ },
+
+ /**
+ * @param {!CodeMirror} codeMirror
+ * @param {!Array.<!WebInspector.CodeMirrorTextEditor.ChangeObject>} changes
+ */
+ _changes: function(codeMirror, changes)
+ {
+ if (!changes.length || !this._enabled)
+ return;
+
+ if (this._updatedLines) {
+ for (var lineNumber in this._updatedLines)
+ this._removeTextFromCompletionDictionary(this._updatedLines[lineNumber]);
+ delete this._updatedLines;
+ }
+
+ var linesToUpdate = {};
+ var singleCharInput = false;
+ for (var changeIndex = 0; changeIndex < changes.length; ++changeIndex) {
+ var changeObject = changes[changeIndex];
+ singleCharInput = (changeObject.origin === "+input" && changeObject.text.length === 1 && changeObject.text[0].length === 1) ||
+ (changeObject.origin === "+delete" && changeObject.removed.length === 1 && changeObject.removed[0].length === 1);
+
+ var editInfo = this._textEditor._changeObjectToEditOperation(changeObject);
+ for (var i = editInfo.newRange.startLine; i <= editInfo.newRange.endLine; ++i)
+ linesToUpdate[i] = this._textEditor.line(i);
+ }
+ for (var lineNumber in linesToUpdate)
+ this._addTextToCompletionDictionary(linesToUpdate[lineNumber]);
+
+ if (singleCharInput)
+ this.autocomplete();
+ },
+
+ _blur: function()
+ {
+ this.finishAutocomplete();
+ },
+
+ /**
+ * @param {number} lineNumber
+ * @param {number} columnNumber
+ * @return {!WebInspector.TextRange}
+ */
+ _autocompleteWordRange: function(lineNumber, columnNumber)
+ {
+ return this._textEditor._wordRangeForCursorPosition(lineNumber, columnNumber, this._isWordChar.bind(this));
+ },
+
+ /**
+ * @param {!WebInspector.TextRange} mainSelection
+ * @param {!Array.<!{head: !CodeMirror.Pos, anchor: !CodeMirror.Pos}>} selections
+ * @return {boolean}
+ */
+ _validateSelectionsContexts: function(mainSelection, selections)
+ {
+ var mainSelectionContext = this._textEditor.copyRange(mainSelection);
+ for (var i = 0; i < selections.length; ++i) {
+ var wordRange = this._autocompleteWordRange(selections[i].head.line, selections[i].head.ch);
+ if (!wordRange)
+ return false;
+ var context = this._textEditor.copyRange(wordRange);
+ if (context !== mainSelectionContext)
+ return false;
+ }
+ return true;
+ },
+
+ autocomplete: function()
+ {
+ var dictionary = this._dictionary;
+ if (this._codeMirror.somethingSelected()) {
+ this.finishAutocomplete();
+ return;
+ }
+
+ var selections = this._codeMirror.listSelections().slice();
+ var topSelection = selections.shift();
+ var cursor = topSelection.head;
+ var substituteRange = this._autocompleteWordRange(cursor.line, cursor.ch);
+ if (!substituteRange || substituteRange.startColumn === cursor.ch || !this._validateSelectionsContexts(substituteRange, selections)) {
+ this.finishAutocomplete();
+ return;
+ }
+
+ var prefixRange = substituteRange.clone();
+ prefixRange.endColumn = cursor.ch;
+
+ var substituteWord = this._textEditor.copyRange(substituteRange);
+ var hasPrefixInDictionary = dictionary.hasWord(substituteWord);
+ if (hasPrefixInDictionary)
+ dictionary.removeWord(substituteWord);
+ var wordsWithPrefix = dictionary.wordsWithPrefix(this._textEditor.copyRange(prefixRange));
+ if (hasPrefixInDictionary)
+ dictionary.addWord(substituteWord);
+
+ function sortSuggestions(a, b)
+ {
+ return dictionary.wordCount(b) - dictionary.wordCount(a) || a.length - b.length;
+ }
+
+ wordsWithPrefix.sort(sortSuggestions);
+
+ if (!this._suggestBox)
+ this._suggestBox = new WebInspector.SuggestBox(this, 6);
+ var oldPrefixRange = this._prefixRange;
+ this._prefixRange = prefixRange;
+ if (!oldPrefixRange || prefixRange.startLine !== oldPrefixRange.startLine || prefixRange.startColumn !== oldPrefixRange.startColumn)
+ this._updateAnchorBox();
+ this._suggestBox.updateSuggestions(this._anchorBox, wordsWithPrefix, 0, true, this._textEditor.copyRange(prefixRange));
+ if (!this._suggestBox.visible())
+ this.finishAutocomplete();
+ },
+
+ finishAutocomplete: function()
+ {
+ if (!this._suggestBox)
+ return;
+ this._suggestBox.hide();
+ this._suggestBox = null;
+ this._prefixRange = null;
+ this._anchorBox = null;
+ },
+
+ /**
+ * @param {?Event} e
+ * @return {boolean}
+ */
+ keyDown: function(e)
+ {
+ if (!this._suggestBox)
+ return false;
+ if (e.keyCode === WebInspector.KeyboardShortcut.Keys.Esc.code) {
+ this.finishAutocomplete();
+ return true;
+ }
+ if (e.keyCode === WebInspector.KeyboardShortcut.Keys.Tab.code) {
+ this._suggestBox.acceptSuggestion();
+ this.finishAutocomplete();
+ return true;
+ }
+ return this._suggestBox.keyPressed(e);
+ },
+
+ /**
+ * @param {string} suggestion
+ * @param {boolean=} isIntermediateSuggestion
+ */
+ applySuggestion: function(suggestion, isIntermediateSuggestion)
+ {
+ this._currentSuggestion = suggestion;
+ },
+
+ acceptSuggestion: function()
+ {
+ if (this._prefixRange.endColumn - this._prefixRange.startColumn === this._currentSuggestion.length)
+ return;
+
+ var selections = this._codeMirror.listSelections().slice();
+ var prefixLength = this._prefixRange.endColumn - this._prefixRange.startColumn;
+ for (var i = selections.length - 1; i >= 0; --i) {
+ var start = selections[i].head;
+ var end = new CodeMirror.Pos(start.line, start.ch - prefixLength);
+ this._codeMirror.replaceRange(this._currentSuggestion, start, end, "+autocomplete");
+ }
+ },
+
+ _onScroll: function()
+ {
+ if (!this._suggestBox)
+ return;
+ var cursor = this._codeMirror.getCursor();
+ var scrollInfo = this._codeMirror.getScrollInfo();
+ var topmostLineNumber = this._codeMirror.lineAtHeight(scrollInfo.top, "local");
+ var bottomLine = this._codeMirror.lineAtHeight(scrollInfo.top + scrollInfo.clientHeight, "local");
+ if (cursor.line < topmostLineNumber || cursor.line > bottomLine)
+ this.finishAutocomplete();
+ else {
+ this._updateAnchorBox();
+ this._suggestBox.setPosition(this._anchorBox);
+ }
+ },
+
+ _onCursorActivity: function()
+ {
+ if (!this._suggestBox)
+ return;
+ var cursor = this._codeMirror.getCursor();
+ if (cursor.line !== this._prefixRange.startLine || cursor.ch > this._prefixRange.endColumn || cursor.ch <= this._prefixRange.startColumn)
+ this.finishAutocomplete();
+ },
+
+ _updateAnchorBox: function()
+ {
+ var line = this._prefixRange.startLine;
+ var column = this._prefixRange.startColumn;
+ var metrics = this._textEditor.cursorPositionToCoordinates(line, column);
+ this._anchorBox = metrics ? new AnchorBox(metrics.x, metrics.y, 0, metrics.height) : null;
+ },
+}
+
+/**
+ * @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;
+ },
+
+ undoLastSelection: function()
+ {
+ this._muteSelectionListener = true;
+ this._codeMirror.execCommand("undoSelection");
+ this._muteSelectionListener = 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, selections.length - 1);
+ 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.TextUtils.isWordChar)
+ || WebInspector.TextRange.createFromLocation(selection.startLine, selection.startColumn);
+ var endRangeWord = this._textEditor._wordRangeForCursorPosition(selection.endLine, selection.endColumn, WebInspector.TextUtils.isWordChar)
+ || 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, newSelections.length - 1);
+ 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
+ */
+WebInspector.CodeMirrorTextEditor._overrideModeWithPrefixedTokens = function(modeName, tokenPrefix)
+{
+ var oldModeName = modeName + "-old";
+ if (CodeMirror.modes[oldModeName])
+ return;
+
+ CodeMirror.defineMode(oldModeName, CodeMirror.modes[modeName]);
+ CodeMirror.defineMode(modeName, modeConstructor);
+
+ function modeConstructor(config, parserConfig)
+ {
+ var innerConfig = {};
+ for (var i in parserConfig)
+ innerConfig[i] = parserConfig[i];
+ innerConfig.name = oldModeName;
+ var codeMirrorMode = CodeMirror.getMode(config, innerConfig);
+ codeMirrorMode.name = modeName;
+ codeMirrorMode.token = tokenOverride.bind(null, codeMirrorMode.token);
+ return codeMirrorMode;
+ }
+
+ function tokenOverride(superToken, stream, state)
+ {
+ var token = superToken(stream, state);
+ return token ? tokenPrefix + token : token;
+ }
+}
+
+WebInspector.CodeMirrorTextEditor._overrideModeWithPrefixedTokens("css", "css-");
+WebInspector.CodeMirrorTextEditor._overrideModeWithPrefixedTokens("javascript", "js-");
+WebInspector.CodeMirrorTextEditor._overrideModeWithPrefixedTokens("xml", "xml-");
+
+(function() {
+ var backgroundColor = InspectorFrontendHost.getSelectionBackgroundColor();
+ var backgroundColorRule = backgroundColor ? ".CodeMirror .CodeMirror-selected { background-color: " + backgroundColor + ";}" : "";
+ var foregroundColor = InspectorFrontendHost.getSelectionForegroundColor();
+ var foregroundColorRule = foregroundColor ? ".CodeMirror .CodeMirror-selectedtext:not(.CodeMirror-persist-highlight) { color: " + foregroundColor + "!important;}" : "";
+ if (!foregroundColorRule && !backgroundColorRule)
+ return;
+
+ var style = document.createElement("style");
+ style.textContent = backgroundColorRule + foregroundColorRule;
+ document.head.appendChild(style);
+})();