OLD | NEW |
1 // Copyright (c) 2016 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2016 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | |
5 /** | 4 /** |
6 * @constructor | 5 * @unrestricted |
7 * @extends {WebInspector.CodeMirrorTextEditor} | |
8 * @param {!WebInspector.SourcesTextEditorDelegate} delegate | |
9 */ | 6 */ |
10 WebInspector.SourcesTextEditor = function(delegate) | 7 WebInspector.SourcesTextEditor = class extends WebInspector.CodeMirrorTextEditor
{ |
11 { | 8 /** |
12 WebInspector.CodeMirrorTextEditor.call(this, { | 9 * @param {!WebInspector.SourcesTextEditorDelegate} delegate |
13 lineNumbers: true, | 10 */ |
14 lineWrapping: false, | 11 constructor(delegate) { |
15 bracketMatchingSetting: WebInspector.moduleSetting("textEditorBracketMat
ching"), | 12 super({ |
| 13 lineNumbers: true, |
| 14 lineWrapping: false, |
| 15 bracketMatchingSetting: WebInspector.moduleSetting('textEditorBracketMatch
ing'), |
16 }); | 16 }); |
17 | 17 |
18 this.codeMirror().addKeyMap({ | 18 this.codeMirror().addKeyMap({'Enter': 'smartNewlineAndIndent', 'Esc': 'sourc
esDismiss'}); |
19 "Enter": "smartNewlineAndIndent", | |
20 "Esc": "sourcesDismiss" | |
21 }); | |
22 | 19 |
23 this._delegate = delegate; | 20 this._delegate = delegate; |
24 | 21 |
25 this.codeMirror().on("changes", this._fireTextChanged.bind(this)); | 22 this.codeMirror().on('changes', this._fireTextChanged.bind(this)); |
26 this.codeMirror().on("cursorActivity", this._cursorActivity.bind(this)); | 23 this.codeMirror().on('cursorActivity', this._cursorActivity.bind(this)); |
27 this.codeMirror().on("gutterClick", this._gutterClick.bind(this)); | 24 this.codeMirror().on('gutterClick', this._gutterClick.bind(this)); |
28 this.codeMirror().on("scroll", this._scroll.bind(this)); | 25 this.codeMirror().on('scroll', this._scroll.bind(this)); |
29 this.codeMirror().on("focus", this._focus.bind(this)); | 26 this.codeMirror().on('focus', this._focus.bind(this)); |
30 this.codeMirror().on("blur", this._blur.bind(this)); | 27 this.codeMirror().on('blur', this._blur.bind(this)); |
31 this.codeMirror().on("beforeSelectionChange", this._fireBeforeSelectionChang
ed.bind(this)); | 28 this.codeMirror().on('beforeSelectionChange', this._fireBeforeSelectionChang
ed.bind(this)); |
32 this.element.addEventListener("contextmenu", this._contextMenu.bind(this), f
alse); | 29 this.element.addEventListener('contextmenu', this._contextMenu.bind(this), f
alse); |
33 | 30 |
34 this._blockIndentController = new WebInspector.SourcesTextEditor.BlockIndent
Controller(this.codeMirror()); | 31 this.codeMirror().addKeyMap(WebInspector.SourcesTextEditor._BlockIndentContr
oller); |
35 this._tokenHighlighter = new WebInspector.SourcesTextEditor.TokenHighlighter
(this, this.codeMirror()); | 32 this._tokenHighlighter = new WebInspector.SourcesTextEditor.TokenHighlighter
(this, this.codeMirror()); |
36 | 33 |
37 /** @type {!Array<string>} */ | 34 /** @type {!Array<string>} */ |
38 this._gutters = ["CodeMirror-linenumbers"]; | 35 this._gutters = ['CodeMirror-linenumbers']; |
39 this.codeMirror().setOption("gutters", this._gutters.slice()); | 36 this.codeMirror().setOption('gutters', this._gutters.slice()); |
40 | 37 |
41 this.codeMirror().setOption("electricChars", false); | 38 this.codeMirror().setOption('electricChars', false); |
42 this.codeMirror().setOption("smartIndent", false); | 39 this.codeMirror().setOption('smartIndent', false); |
43 | 40 |
44 /** | 41 /** |
45 * @this {WebInspector.SourcesTextEditor} | 42 * @this {WebInspector.SourcesTextEditor} |
46 */ | 43 */ |
47 function updateAnticipateJumpFlag(value) | 44 function updateAnticipateJumpFlag(value) { |
48 { | 45 this._isHandlingMouseDownEvent = value; |
49 this._isHandlingMouseDownEvent = value; | 46 } |
50 } | 47 |
51 | 48 this.element.addEventListener('mousedown', updateAnticipateJumpFlag.bind(thi
s, true), true); |
52 this.element.addEventListener("mousedown", updateAnticipateJumpFlag.bind(thi
s, true), true); | 49 this.element.addEventListener('mousedown', updateAnticipateJumpFlag.bind(thi
s, false), false); |
53 this.element.addEventListener("mousedown", updateAnticipateJumpFlag.bind(thi
s, false), false); | 50 WebInspector.moduleSetting('textEditorIndent').addChangeListener(this._onUpd
ateEditorIndentation, this); |
54 WebInspector.moduleSetting("textEditorIndent").addChangeListener(this._onUpd
ateEditorIndentation, this); | 51 WebInspector.moduleSetting('textEditorAutoDetectIndent').addChangeListener(t
his._onUpdateEditorIndentation, this); |
55 WebInspector.moduleSetting("textEditorAutoDetectIndent").addChangeListener(t
his._onUpdateEditorIndentation, this); | 52 WebInspector.moduleSetting('showWhitespacesInEditor').addChangeListener(this
._updateWhitespace, this); |
56 WebInspector.moduleSetting("showWhitespacesInEditor").addChangeListener(this
._updateWhitespace, this); | |
57 | 53 |
58 this._onUpdateEditorIndentation(); | 54 this._onUpdateEditorIndentation(); |
59 this._setupWhitespaceHighlight(); | 55 this._setupWhitespaceHighlight(); |
| 56 } |
| 57 |
| 58 /** |
| 59 * @param {!Array.<string>} lines |
| 60 * @return {string} |
| 61 */ |
| 62 static _guessIndentationLevel(lines) { |
| 63 var tabRegex = /^\t+/; |
| 64 var tabLines = 0; |
| 65 var indents = {}; |
| 66 for (var lineNumber = 0; lineNumber < lines.length; ++lineNumber) { |
| 67 var text = lines[lineNumber]; |
| 68 if (text.length === 0 || !WebInspector.TextUtils.isSpaceChar(text[0])) |
| 69 continue; |
| 70 if (tabRegex.test(text)) { |
| 71 ++tabLines; |
| 72 continue; |
| 73 } |
| 74 var i = 0; |
| 75 while (i < text.length && WebInspector.TextUtils.isSpaceChar(text[i])) |
| 76 ++i; |
| 77 if (i % 2 !== 0) |
| 78 continue; |
| 79 indents[i] = 1 + (indents[i] || 0); |
| 80 } |
| 81 var linesCountPerIndentThreshold = 3 * lines.length / 100; |
| 82 if (tabLines && tabLines > linesCountPerIndentThreshold) |
| 83 return '\t'; |
| 84 var minimumIndent = Infinity; |
| 85 for (var i in indents) { |
| 86 if (indents[i] < linesCountPerIndentThreshold) |
| 87 continue; |
| 88 var indent = parseInt(i, 10); |
| 89 if (minimumIndent > indent) |
| 90 minimumIndent = indent; |
| 91 } |
| 92 if (minimumIndent === Infinity) |
| 93 return WebInspector.moduleSetting('textEditorIndent').get(); |
| 94 return ' '.repeat(minimumIndent); |
| 95 } |
| 96 |
| 97 /** |
| 98 * @return {boolean} |
| 99 */ |
| 100 _isSearchActive() { |
| 101 return !!this._tokenHighlighter.highlightedRegex(); |
| 102 } |
| 103 |
| 104 /** |
| 105 * @override |
| 106 * @param {number} lineNumber |
| 107 */ |
| 108 scrollToLine(lineNumber) { |
| 109 super.scrollToLine(lineNumber); |
| 110 this._scroll(); |
| 111 } |
| 112 |
| 113 /** |
| 114 * @param {!RegExp} regex |
| 115 * @param {?WebInspector.TextRange} range |
| 116 */ |
| 117 highlightSearchResults(regex, range) { |
| 118 /** |
| 119 * @this {WebInspector.CodeMirrorTextEditor} |
| 120 */ |
| 121 function innerHighlightRegex() { |
| 122 if (range) { |
| 123 this.scrollLineIntoView(range.startLine); |
| 124 if (range.endColumn > WebInspector.CodeMirrorTextEditor.maxHighlightLeng
th) |
| 125 this.setSelection(range); |
| 126 else |
| 127 this.setSelection(WebInspector.TextRange.createFromLocation(range.star
tLine, range.startColumn)); |
| 128 } |
| 129 this._tokenHighlighter.highlightSearchResults(regex, range); |
| 130 } |
| 131 |
| 132 if (!this._selectionBeforeSearch) |
| 133 this._selectionBeforeSearch = this.selection(); |
| 134 |
| 135 this.codeMirror().operation(innerHighlightRegex.bind(this)); |
| 136 } |
| 137 |
| 138 cancelSearchResultsHighlight() { |
| 139 this.codeMirror().operation(this._tokenHighlighter.highlightSelectedTokens.b
ind(this._tokenHighlighter)); |
| 140 |
| 141 if (this._selectionBeforeSearch) { |
| 142 this._reportJump(this._selectionBeforeSearch, this.selection()); |
| 143 delete this._selectionBeforeSearch; |
| 144 } |
| 145 } |
| 146 |
| 147 /** |
| 148 * @param {!Object} highlightDescriptor |
| 149 */ |
| 150 removeHighlight(highlightDescriptor) { |
| 151 highlightDescriptor.clear(); |
| 152 } |
| 153 |
| 154 /** |
| 155 * @param {!WebInspector.TextRange} range |
| 156 * @param {string} cssClass |
| 157 * @return {!Object} |
| 158 */ |
| 159 highlightRange(range, cssClass) { |
| 160 cssClass = 'CodeMirror-persist-highlight ' + cssClass; |
| 161 var pos = WebInspector.CodeMirrorUtils.toPos(range); |
| 162 ++pos.end.ch; |
| 163 return this.codeMirror().markText( |
| 164 pos.start, pos.end, {className: cssClass, startStyle: cssClass + '-start
', endStyle: cssClass + '-end'}); |
| 165 } |
| 166 |
| 167 /** |
| 168 * @param {number} lineNumber |
| 169 * @param {boolean} disabled |
| 170 * @param {boolean} conditional |
| 171 */ |
| 172 addBreakpoint(lineNumber, disabled, conditional) { |
| 173 if (lineNumber < 0 || lineNumber >= this.codeMirror().lineCount()) |
| 174 return; |
| 175 |
| 176 var className = 'cm-breakpoint' + (conditional ? ' cm-breakpoint-conditional
' : '') + |
| 177 (disabled ? ' cm-breakpoint-disabled' : ''); |
| 178 this.codeMirror().addLineClass(lineNumber, 'wrap', className); |
| 179 } |
| 180 |
| 181 /** |
| 182 * @param {number} lineNumber |
| 183 */ |
| 184 removeBreakpoint(lineNumber) { |
| 185 if (lineNumber < 0 || lineNumber >= this.codeMirror().lineCount()) |
| 186 return; |
| 187 |
| 188 var wrapClasses = this.codeMirror().getLineHandle(lineNumber).wrapClass; |
| 189 if (!wrapClasses) |
| 190 return; |
| 191 |
| 192 var classes = wrapClasses.split(' '); |
| 193 for (var i = 0; i < classes.length; ++i) { |
| 194 if (classes[i].startsWith('cm-breakpoint')) |
| 195 this.codeMirror().removeLineClass(lineNumber, 'wrap', classes[i]); |
| 196 } |
| 197 } |
| 198 |
| 199 /** |
| 200 * @param {string} type |
| 201 * @param {boolean} leftToNumbers |
| 202 */ |
| 203 installGutter(type, leftToNumbers) { |
| 204 if (this._gutters.indexOf(type) !== -1) |
| 205 return; |
| 206 |
| 207 if (leftToNumbers) |
| 208 this._gutters.unshift(type); |
| 209 else |
| 210 this._gutters.push(type); |
| 211 |
| 212 this.codeMirror().setOption('gutters', this._gutters.slice()); |
| 213 this.refresh(); |
| 214 } |
| 215 |
| 216 /** |
| 217 * @param {string} type |
| 218 */ |
| 219 uninstallGutter(type) { |
| 220 var index = this._gutters.indexOf(type); |
| 221 if (index === -1) |
| 222 return; |
| 223 this._gutters.splice(index, 1); |
| 224 this.codeMirror().setOption('gutters', this._gutters.slice()); |
| 225 this.refresh(); |
| 226 } |
| 227 |
| 228 /** |
| 229 * @param {number} lineNumber |
| 230 * @param {string} type |
| 231 * @param {?Element} element |
| 232 */ |
| 233 setGutterDecoration(lineNumber, type, element) { |
| 234 console.assert(this._gutters.indexOf(type) !== -1, 'Cannot decorate unexisti
ng gutter.'); |
| 235 this.codeMirror().setGutterMarker(lineNumber, type, element); |
| 236 } |
| 237 |
| 238 /** |
| 239 * @param {number} lineNumber |
| 240 * @param {number} columnNumber |
| 241 */ |
| 242 setExecutionLocation(lineNumber, columnNumber) { |
| 243 this.clearPositionHighlight(); |
| 244 |
| 245 this._executionLine = this.codeMirror().getLineHandle(lineNumber); |
| 246 if (!this._executionLine) |
| 247 return; |
| 248 |
| 249 this.codeMirror().addLineClass(this._executionLine, 'wrap', 'cm-execution-li
ne'); |
| 250 this._executionLineTailMarker = this.codeMirror().markText( |
| 251 {line: lineNumber, ch: columnNumber}, {line: lineNumber, ch: this.codeMi
rror().getLine(lineNumber).length}, |
| 252 {className: 'cm-execution-line-tail'}); |
| 253 } |
| 254 |
| 255 clearExecutionLine() { |
| 256 this.clearPositionHighlight(); |
| 257 |
| 258 if (this._executionLine) |
| 259 this.codeMirror().removeLineClass(this._executionLine, 'wrap', 'cm-executi
on-line'); |
| 260 delete this._executionLine; |
| 261 |
| 262 if (this._executionLineTailMarker) |
| 263 this._executionLineTailMarker.clear(); |
| 264 delete this._executionLineTailMarker; |
| 265 } |
| 266 |
| 267 /** |
| 268 * @param {number} lineNumber |
| 269 * @param {string} className |
| 270 * @param {boolean} toggled |
| 271 */ |
| 272 toggleLineClass(lineNumber, className, toggled) { |
| 273 if (this.hasLineClass(lineNumber, className) === toggled) |
| 274 return; |
| 275 |
| 276 var lineHandle = this.codeMirror().getLineHandle(lineNumber); |
| 277 if (!lineHandle) |
| 278 return; |
| 279 |
| 280 if (toggled) { |
| 281 this.codeMirror().addLineClass(lineHandle, 'gutter', className); |
| 282 this.codeMirror().addLineClass(lineHandle, 'wrap', className); |
| 283 } else { |
| 284 this.codeMirror().removeLineClass(lineHandle, 'gutter', className); |
| 285 this.codeMirror().removeLineClass(lineHandle, 'wrap', className); |
| 286 } |
| 287 } |
| 288 |
| 289 /** |
| 290 * @param {number} lineNumber |
| 291 * @param {string} className |
| 292 * @return {boolean} |
| 293 */ |
| 294 hasLineClass(lineNumber, className) { |
| 295 var lineInfo = this.codeMirror().lineInfo(lineNumber); |
| 296 var wrapClass = lineInfo.wrapClass || ''; |
| 297 var classNames = wrapClass.split(' '); |
| 298 return classNames.indexOf(className) !== -1; |
| 299 } |
| 300 |
| 301 _gutterClick(instance, lineNumber, gutter, event) { |
| 302 this.dispatchEventToListeners( |
| 303 WebInspector.SourcesTextEditor.Events.GutterClick, {lineNumber: lineNumb
er, event: event}); |
| 304 } |
| 305 |
| 306 _contextMenu(event) { |
| 307 var contextMenu = new WebInspector.ContextMenu(event); |
| 308 event.consume(true); // Consume event now to prevent document from handling
the async menu |
| 309 var target = event.target.enclosingNodeOrSelfWithClass('CodeMirror-gutter-el
t'); |
| 310 var promise; |
| 311 if (target) { |
| 312 promise = this._delegate.populateLineGutterContextMenu(contextMenu, parseI
nt(target.textContent, 10) - 1); |
| 313 } else { |
| 314 var textSelection = this.selection(); |
| 315 promise = |
| 316 this._delegate.populateTextAreaContextMenu(contextMenu, textSelection.
startLine, textSelection.startColumn); |
| 317 } |
| 318 promise.then(showAsync.bind(this)); |
| 319 |
| 320 /** |
| 321 * @this {WebInspector.SourcesTextEditor} |
| 322 */ |
| 323 function showAsync() { |
| 324 contextMenu.appendApplicableItems(this); |
| 325 contextMenu.show(); |
| 326 } |
| 327 } |
| 328 |
| 329 /** |
| 330 * @override |
| 331 * @param {!WebInspector.TextRange} range |
| 332 * @param {string} text |
| 333 * @param {string=} origin |
| 334 * @return {!WebInspector.TextRange} |
| 335 */ |
| 336 editRange(range, text, origin) { |
| 337 var newRange = super.editRange(range, text, origin); |
| 338 this.dispatchEventToListeners( |
| 339 WebInspector.SourcesTextEditor.Events.TextChanged, {oldRange: range, new
Range: newRange}); |
| 340 |
| 341 if (WebInspector.moduleSetting('textEditorAutoDetectIndent').get()) |
| 342 this._onUpdateEditorIndentation(); |
| 343 |
| 344 return newRange; |
| 345 } |
| 346 |
| 347 _onUpdateEditorIndentation() { |
| 348 this._setEditorIndentation(WebInspector.CodeMirrorUtils.pullLines( |
| 349 this.codeMirror(), WebInspector.SourcesTextEditor.LinesToScanForIndentat
ionGuessing)); |
| 350 } |
| 351 |
| 352 /** |
| 353 * @param {!Array.<string>} lines |
| 354 */ |
| 355 _setEditorIndentation(lines) { |
| 356 var extraKeys = {}; |
| 357 var indent = WebInspector.moduleSetting('textEditorIndent').get(); |
| 358 if (WebInspector.moduleSetting('textEditorAutoDetectIndent').get()) |
| 359 indent = WebInspector.SourcesTextEditor._guessIndentationLevel(lines); |
| 360 |
| 361 if (indent === WebInspector.TextUtils.Indent.TabCharacter) { |
| 362 this.codeMirror().setOption('indentWithTabs', true); |
| 363 this.codeMirror().setOption('indentUnit', 4); |
| 364 } else { |
| 365 this.codeMirror().setOption('indentWithTabs', false); |
| 366 this.codeMirror().setOption('indentUnit', indent.length); |
| 367 extraKeys.Tab = function(codeMirror) { |
| 368 if (codeMirror.somethingSelected()) |
| 369 return CodeMirror.Pass; |
| 370 var pos = codeMirror.getCursor('head'); |
| 371 codeMirror.replaceRange(indent.substring(pos.ch % indent.length), codeMi
rror.getCursor()); |
| 372 }; |
| 373 } |
| 374 |
| 375 this.codeMirror().setOption('extraKeys', extraKeys); |
| 376 this._indentationLevel = indent; |
| 377 } |
| 378 |
| 379 /** |
| 380 * @return {string} |
| 381 */ |
| 382 indent() { |
| 383 return this._indentationLevel; |
| 384 } |
| 385 |
| 386 _onAutoAppendedSpaces() { |
| 387 this._autoAppendedSpaces = this._autoAppendedSpaces || []; |
| 388 |
| 389 for (var i = 0; i < this._autoAppendedSpaces.length; ++i) { |
| 390 var position = this._autoAppendedSpaces[i].resolve(); |
| 391 if (!position) |
| 392 continue; |
| 393 var line = this.line(position.lineNumber); |
| 394 if (line.length === position.columnNumber && WebInspector.TextUtils.lineIn
dent(line).length === line.length) |
| 395 this.codeMirror().replaceRange( |
| 396 '', new CodeMirror.Pos(position.lineNumber, 0), |
| 397 new CodeMirror.Pos(position.lineNumber, position.columnNumber)); |
| 398 } |
| 399 |
| 400 this._autoAppendedSpaces = []; |
| 401 var selections = this.selections(); |
| 402 for (var i = 0; i < selections.length; ++i) { |
| 403 var selection = selections[i]; |
| 404 this._autoAppendedSpaces.push(this.textEditorPositionHandle(selection.star
tLine, selection.startColumn)); |
| 405 } |
| 406 } |
| 407 |
| 408 /** |
| 409 * @param {!CodeMirror} codeMirror |
| 410 * @param {!Array.<!CodeMirror.ChangeObject>} changes |
| 411 */ |
| 412 _fireTextChanged(codeMirror, changes) { |
| 413 if (!changes.length || this._muteTextChangedEvent) |
| 414 return; |
| 415 var edits = []; |
| 416 var currentEdit; |
| 417 |
| 418 for (var changeIndex = 0; changeIndex < changes.length; ++changeIndex) { |
| 419 var changeObject = changes[changeIndex]; |
| 420 var edit = WebInspector.CodeMirrorUtils.changeObjectToEditOperation(change
Object); |
| 421 if (currentEdit && edit.oldRange.equal(currentEdit.newRange)) { |
| 422 currentEdit.newRange = edit.newRange; |
| 423 } else { |
| 424 currentEdit = edit; |
| 425 edits.push(currentEdit); |
| 426 } |
| 427 } |
| 428 |
| 429 for (var i = 0; i < edits.length; ++i) |
| 430 this.dispatchEventToListeners(WebInspector.SourcesTextEditor.Events.TextCh
anged, edits[i]); |
| 431 } |
| 432 |
| 433 _cursorActivity() { |
| 434 if (!this._isSearchActive()) |
| 435 this.codeMirror().operation(this._tokenHighlighter.highlightSelectedTokens
.bind(this._tokenHighlighter)); |
| 436 |
| 437 var start = this.codeMirror().getCursor('anchor'); |
| 438 var end = this.codeMirror().getCursor('head'); |
| 439 this.dispatchEventToListeners( |
| 440 WebInspector.SourcesTextEditor.Events.SelectionChanged, WebInspector.Cod
eMirrorUtils.toRange(start, end)); |
| 441 } |
| 442 |
| 443 /** |
| 444 * @param {?WebInspector.TextRange} from |
| 445 * @param {?WebInspector.TextRange} to |
| 446 */ |
| 447 _reportJump(from, to) { |
| 448 if (from && to && from.equal(to)) |
| 449 return; |
| 450 this.dispatchEventToListeners(WebInspector.SourcesTextEditor.Events.JumpHapp
ened, {from: from, to: to}); |
| 451 } |
| 452 |
| 453 _scroll() { |
| 454 var topmostLineNumber = this.codeMirror().lineAtHeight(this.codeMirror().get
ScrollInfo().top, 'local'); |
| 455 this.dispatchEventToListeners(WebInspector.SourcesTextEditor.Events.ScrollCh
anged, topmostLineNumber); |
| 456 } |
| 457 |
| 458 _focus() { |
| 459 this.dispatchEventToListeners(WebInspector.SourcesTextEditor.Events.EditorFo
cused); |
| 460 } |
| 461 |
| 462 _blur() { |
| 463 this.dispatchEventToListeners(WebInspector.SourcesTextEditor.Events.EditorBl
urred); |
| 464 } |
| 465 |
| 466 /** |
| 467 * @param {!CodeMirror} codeMirror |
| 468 * @param {{ranges: !Array.<{head: !CodeMirror.Pos, anchor: !CodeMirror.Pos}>}
} selection |
| 469 */ |
| 470 _fireBeforeSelectionChanged(codeMirror, selection) { |
| 471 if (!this._isHandlingMouseDownEvent) |
| 472 return; |
| 473 if (!selection.ranges.length) |
| 474 return; |
| 475 |
| 476 var primarySelection = selection.ranges[0]; |
| 477 this._reportJump( |
| 478 this.selection(), WebInspector.CodeMirrorUtils.toRange(primarySelection.
anchor, primarySelection.head)); |
| 479 } |
| 480 |
| 481 /** |
| 482 * @override |
| 483 */ |
| 484 dispose() { |
| 485 super.dispose(); |
| 486 WebInspector.moduleSetting('textEditorIndent').removeChangeListener(this._on
UpdateEditorIndentation, this); |
| 487 WebInspector.moduleSetting('textEditorAutoDetectIndent') |
| 488 .removeChangeListener(this._onUpdateEditorIndentation, this); |
| 489 WebInspector.moduleSetting('showWhitespacesInEditor').removeChangeListener(t
his._updateWhitespace, this); |
| 490 } |
| 491 |
| 492 /** |
| 493 * @override |
| 494 * @param {string} text |
| 495 */ |
| 496 setText(text) { |
| 497 this._muteTextChangedEvent = true; |
| 498 this._setEditorIndentation( |
| 499 text.split('\n').slice(0, WebInspector.SourcesTextEditor.LinesToScanForI
ndentationGuessing)); |
| 500 super.setText(text); |
| 501 delete this._muteTextChangedEvent; |
| 502 } |
| 503 |
| 504 /** |
| 505 * @override |
| 506 * @param {string} mimeType |
| 507 * @return {!Promise} |
| 508 */ |
| 509 setMimeType(mimeType) { |
| 510 this._mimeType = mimeType; |
| 511 return super.setMimeType(mimeType).then( |
| 512 () => this._codeMirror.setOption('mode', this._applyWhitespaceMimetype(m
imeType))); |
| 513 } |
| 514 |
| 515 _updateWhitespace() { |
| 516 if (this._mimeType) |
| 517 this.setMimeType(this._mimeType); |
| 518 } |
| 519 |
| 520 /** |
| 521 * @param {string} mimeType |
| 522 * @return {string} |
| 523 */ |
| 524 _applyWhitespaceMimetype(mimeType) { |
| 525 this._setupWhitespaceHighlight(); |
| 526 var whitespaceMode = WebInspector.moduleSetting('showWhitespacesInEditor').g
et(); |
| 527 this.element.classList.toggle('show-whitespaces', whitespaceMode === 'all'); |
| 528 |
| 529 if (whitespaceMode === 'all') |
| 530 return this._allWhitespaceOverlayMode(mimeType); |
| 531 else if (whitespaceMode === 'trailing') |
| 532 return this._trailingWhitespaceOverlayMode(mimeType); |
| 533 |
| 534 return mimeType; |
| 535 } |
| 536 |
| 537 /** |
| 538 * @param {string} mimeType |
| 539 * @return {string} |
| 540 */ |
| 541 _allWhitespaceOverlayMode(mimeType) { |
| 542 var modeName = CodeMirror.mimeModes[mimeType] ? |
| 543 (CodeMirror.mimeModes[mimeType].name || CodeMirror.mimeModes[mimeType])
: |
| 544 CodeMirror.mimeModes['text/plain']; |
| 545 modeName += '+all-whitespaces'; |
| 546 if (CodeMirror.modes[modeName]) |
| 547 return modeName; |
| 548 |
| 549 function modeConstructor(config, parserConfig) { |
| 550 function nextToken(stream) { |
| 551 if (stream.peek() === ' ') { |
| 552 var spaces = 0; |
| 553 while (spaces < WebInspector.SourcesTextEditor.MaximumNumberOfWhitespa
cesPerSingleSpan && |
| 554 stream.peek() === ' ') { |
| 555 ++spaces; |
| 556 stream.next(); |
| 557 } |
| 558 return 'whitespace whitespace-' + spaces; |
| 559 } |
| 560 while (!stream.eol() && stream.peek() !== ' ') |
| 561 stream.next(); |
| 562 return null; |
| 563 } |
| 564 var whitespaceMode = {token: nextToken}; |
| 565 return CodeMirror.overlayMode(CodeMirror.getMode(config, mimeType), whites
paceMode, false); |
| 566 } |
| 567 CodeMirror.defineMode(modeName, modeConstructor); |
| 568 return modeName; |
| 569 } |
| 570 |
| 571 /** |
| 572 * @param {string} mimeType |
| 573 * @return {string} |
| 574 */ |
| 575 _trailingWhitespaceOverlayMode(mimeType) { |
| 576 var modeName = CodeMirror.mimeModes[mimeType] ? |
| 577 (CodeMirror.mimeModes[mimeType].name || CodeMirror.mimeModes[mimeType])
: |
| 578 CodeMirror.mimeModes['text/plain']; |
| 579 modeName += '+trailing-whitespaces'; |
| 580 if (CodeMirror.modes[modeName]) |
| 581 return modeName; |
| 582 |
| 583 function modeConstructor(config, parserConfig) { |
| 584 function nextToken(stream) { |
| 585 var pos = stream.pos; |
| 586 if (stream.match(/^\s+$/, true)) |
| 587 return true ? 'trailing-whitespace' : null; |
| 588 do { |
| 589 stream.next(); |
| 590 } while (!stream.eol() && stream.peek() !== ' '); |
| 591 return null; |
| 592 } |
| 593 var whitespaceMode = {token: nextToken}; |
| 594 return CodeMirror.overlayMode(CodeMirror.getMode(config, mimeType), whites
paceMode, false); |
| 595 } |
| 596 CodeMirror.defineMode(modeName, modeConstructor); |
| 597 return modeName; |
| 598 } |
| 599 |
| 600 _setupWhitespaceHighlight() { |
| 601 var doc = this.element.ownerDocument; |
| 602 if (doc._codeMirrorWhitespaceStyleInjected || !WebInspector.moduleSetting('s
howWhitespacesInEditor').get()) |
| 603 return; |
| 604 doc._codeMirrorWhitespaceStyleInjected = true; |
| 605 const classBase = '.show-whitespaces .CodeMirror .cm-whitespace-'; |
| 606 const spaceChar = '·'; |
| 607 var spaceChars = ''; |
| 608 var rules = ''; |
| 609 for (var i = 1; i <= WebInspector.SourcesTextEditor.MaximumNumberOfWhitespac
esPerSingleSpan; ++i) { |
| 610 spaceChars += spaceChar; |
| 611 var rule = classBase + i + '::before { content: \'' + spaceChars + '\';}\n
'; |
| 612 rules += rule; |
| 613 } |
| 614 var style = doc.createElement('style'); |
| 615 style.textContent = rules; |
| 616 doc.head.appendChild(style); |
| 617 } |
60 }; | 618 }; |
61 WebInspector.SourcesTextEditor.prototype = { | |
62 /** | |
63 * @return {boolean} | |
64 */ | |
65 _isSearchActive: function() | |
66 { | |
67 return !!this._tokenHighlighter.highlightedRegex(); | |
68 }, | |
69 | |
70 /** | |
71 * @override | |
72 * @param {number} lineNumber | |
73 */ | |
74 scrollToLine: function(lineNumber) | |
75 { | |
76 WebInspector.CodeMirrorTextEditor.prototype.scrollToLine.call(this, line
Number); | |
77 this._scroll(); | |
78 }, | |
79 | |
80 /** | |
81 * @param {!RegExp} regex | |
82 * @param {?WebInspector.TextRange} range | |
83 */ | |
84 highlightSearchResults: function(regex, range) | |
85 { | |
86 /** | |
87 * @this {WebInspector.CodeMirrorTextEditor} | |
88 */ | |
89 function innerHighlightRegex() | |
90 { | |
91 if (range) { | |
92 this.scrollLineIntoView(range.startLine); | |
93 if (range.endColumn > WebInspector.CodeMirrorTextEditor.maxHighl
ightLength) | |
94 this.setSelection(range); | |
95 else | |
96 this.setSelection(WebInspector.TextRange.createFromLocation(
range.startLine, range.startColumn)); | |
97 } | |
98 this._tokenHighlighter.highlightSearchResults(regex, range); | |
99 } | |
100 | |
101 if (!this._selectionBeforeSearch) | |
102 this._selectionBeforeSearch = this.selection(); | |
103 | |
104 this.codeMirror().operation(innerHighlightRegex.bind(this)); | |
105 }, | |
106 | |
107 cancelSearchResultsHighlight: function() | |
108 { | |
109 this.codeMirror().operation(this._tokenHighlighter.highlightSelectedToke
ns.bind(this._tokenHighlighter)); | |
110 | |
111 if (this._selectionBeforeSearch) { | |
112 this._reportJump(this._selectionBeforeSearch, this.selection()); | |
113 delete this._selectionBeforeSearch; | |
114 } | |
115 }, | |
116 | |
117 /** | |
118 * @param {!Object} highlightDescriptor | |
119 */ | |
120 removeHighlight: function(highlightDescriptor) | |
121 { | |
122 highlightDescriptor.clear(); | |
123 }, | |
124 | |
125 /** | |
126 * @param {!WebInspector.TextRange} range | |
127 * @param {string} cssClass | |
128 * @return {!Object} | |
129 */ | |
130 highlightRange: function(range, cssClass) | |
131 { | |
132 cssClass = "CodeMirror-persist-highlight " + cssClass; | |
133 var pos = WebInspector.CodeMirrorUtils.toPos(range); | |
134 ++pos.end.ch; | |
135 return this.codeMirror().markText(pos.start, pos.end, { | |
136 className: cssClass, | |
137 startStyle: cssClass + "-start", | |
138 endStyle: cssClass + "-end" | |
139 }); | |
140 }, | |
141 | |
142 /** | |
143 * @param {number} lineNumber | |
144 * @param {boolean} disabled | |
145 * @param {boolean} conditional | |
146 */ | |
147 addBreakpoint: function(lineNumber, disabled, conditional) | |
148 { | |
149 if (lineNumber < 0 || lineNumber >= this.codeMirror().lineCount()) | |
150 return; | |
151 | |
152 var className = "cm-breakpoint" + (conditional ? " cm-breakpoint-conditi
onal" : "") + (disabled ? " cm-breakpoint-disabled" : ""); | |
153 this.codeMirror().addLineClass(lineNumber, "wrap", className); | |
154 }, | |
155 | |
156 /** | |
157 * @param {number} lineNumber | |
158 */ | |
159 removeBreakpoint: function(lineNumber) | |
160 { | |
161 if (lineNumber < 0 || lineNumber >= this.codeMirror().lineCount()) | |
162 return; | |
163 | |
164 var wrapClasses = this.codeMirror().getLineHandle(lineNumber).wrapClass; | |
165 if (!wrapClasses) | |
166 return; | |
167 | |
168 var classes = wrapClasses.split(" "); | |
169 for (var i = 0; i < classes.length; ++i) { | |
170 if (classes[i].startsWith("cm-breakpoint")) | |
171 this.codeMirror().removeLineClass(lineNumber, "wrap", classes[i]
); | |
172 } | |
173 }, | |
174 | |
175 /** | |
176 * @param {string} type | |
177 * @param {boolean} leftToNumbers | |
178 */ | |
179 installGutter: function(type, leftToNumbers) | |
180 { | |
181 if (this._gutters.indexOf(type) !== -1) | |
182 return; | |
183 | |
184 if (leftToNumbers) | |
185 this._gutters.unshift(type); | |
186 else | |
187 this._gutters.push(type); | |
188 | |
189 this.codeMirror().setOption("gutters", this._gutters.slice()); | |
190 this.refresh(); | |
191 }, | |
192 | |
193 /** | |
194 * @param {string} type | |
195 */ | |
196 uninstallGutter: function(type) | |
197 { | |
198 var index = this._gutters.indexOf(type); | |
199 if (index === -1) | |
200 return; | |
201 this._gutters.splice(index,1); | |
202 this.codeMirror().setOption("gutters", this._gutters.slice()); | |
203 this.refresh(); | |
204 }, | |
205 | |
206 /** | |
207 * @param {number} lineNumber | |
208 * @param {string} type | |
209 * @param {?Element} element | |
210 */ | |
211 setGutterDecoration: function(lineNumber, type, element) | |
212 { | |
213 console.assert(this._gutters.indexOf(type) !== -1, "Cannot decorate unex
isting gutter."); | |
214 this.codeMirror().setGutterMarker(lineNumber, type, element); | |
215 }, | |
216 | |
217 /** | |
218 * @param {number} lineNumber | |
219 * @param {number} columnNumber | |
220 */ | |
221 setExecutionLocation: function(lineNumber, columnNumber) | |
222 { | |
223 this.clearPositionHighlight(); | |
224 | |
225 this._executionLine = this.codeMirror().getLineHandle(lineNumber); | |
226 if (!this._executionLine) | |
227 return; | |
228 | |
229 this.codeMirror().addLineClass(this._executionLine, "wrap", "cm-executio
n-line"); | |
230 this._executionLineTailMarker = this.codeMirror().markText({ line: lineN
umber, ch: columnNumber }, { line: lineNumber, ch: this.codeMirror().getLine(lin
eNumber).length }, { className: "cm-execution-line-tail" }); | |
231 }, | |
232 | |
233 clearExecutionLine: function() | |
234 { | |
235 this.clearPositionHighlight(); | |
236 | |
237 if (this._executionLine) | |
238 this.codeMirror().removeLineClass(this._executionLine, "wrap", "cm-e
xecution-line"); | |
239 delete this._executionLine; | |
240 | |
241 if (this._executionLineTailMarker) | |
242 this._executionLineTailMarker.clear(); | |
243 delete this._executionLineTailMarker; | |
244 }, | |
245 | |
246 /** | |
247 * @param {number} lineNumber | |
248 * @param {string} className | |
249 * @param {boolean} toggled | |
250 */ | |
251 toggleLineClass: function(lineNumber, className, toggled) | |
252 { | |
253 if (this.hasLineClass(lineNumber, className) === toggled) | |
254 return; | |
255 | |
256 var lineHandle = this.codeMirror().getLineHandle(lineNumber); | |
257 if (!lineHandle) | |
258 return; | |
259 | |
260 if (toggled) { | |
261 this.codeMirror().addLineClass(lineHandle, "gutter", className); | |
262 this.codeMirror().addLineClass(lineHandle, "wrap", className); | |
263 } else { | |
264 this.codeMirror().removeLineClass(lineHandle, "gutter", className); | |
265 this.codeMirror().removeLineClass(lineHandle, "wrap", className); | |
266 } | |
267 }, | |
268 | |
269 /** | |
270 * @param {number} lineNumber | |
271 * @param {string} className | |
272 * @return {boolean} | |
273 */ | |
274 hasLineClass: function(lineNumber, className) | |
275 { | |
276 var lineInfo = this.codeMirror().lineInfo(lineNumber); | |
277 var wrapClass = lineInfo.wrapClass || ""; | |
278 var classNames = wrapClass.split(" "); | |
279 return classNames.indexOf(className) !== -1; | |
280 }, | |
281 | |
282 _gutterClick: function(instance, lineNumber, gutter, event) | |
283 { | |
284 this.dispatchEventToListeners(WebInspector.SourcesTextEditor.Events.Gutt
erClick, { lineNumber: lineNumber, event: event }); | |
285 }, | |
286 | |
287 _contextMenu: function(event) | |
288 { | |
289 var contextMenu = new WebInspector.ContextMenu(event); | |
290 event.consume(true); // Consume event now to prevent document from handl
ing the async menu | |
291 var target = event.target.enclosingNodeOrSelfWithClass("CodeMirror-gutte
r-elt"); | |
292 var promise; | |
293 if (target) { | |
294 promise = this._delegate.populateLineGutterContextMenu(contextMenu,
parseInt(target.textContent, 10) - 1); | |
295 } else { | |
296 var textSelection = this.selection(); | |
297 promise = this._delegate.populateTextAreaContextMenu(contextMenu, te
xtSelection.startLine, textSelection.startColumn); | |
298 } | |
299 promise.then(showAsync.bind(this)); | |
300 | |
301 /** | |
302 * @this {WebInspector.SourcesTextEditor} | |
303 */ | |
304 function showAsync() | |
305 { | |
306 contextMenu.appendApplicableItems(this); | |
307 contextMenu.show(); | |
308 } | |
309 }, | |
310 | |
311 /** | |
312 * @override | |
313 * @param {!WebInspector.TextRange} range | |
314 * @param {string} text | |
315 * @param {string=} origin | |
316 * @return {!WebInspector.TextRange} | |
317 */ | |
318 editRange: function(range, text, origin) | |
319 { | |
320 var newRange = WebInspector.CodeMirrorTextEditor.prototype.editRange.cal
l(this, range, text, origin); | |
321 this.dispatchEventToListeners(WebInspector.SourcesTextEditor.Events.Text
Changed, { oldRange: range, newRange: newRange }); | |
322 | |
323 if (WebInspector.moduleSetting("textEditorAutoDetectIndent").get()) | |
324 this._onUpdateEditorIndentation(); | |
325 | |
326 return newRange; | |
327 }, | |
328 | |
329 _onUpdateEditorIndentation: function() | |
330 { | |
331 this._setEditorIndentation(WebInspector.CodeMirrorUtils.pullLines(this.c
odeMirror(), WebInspector.SourcesTextEditor.LinesToScanForIndentationGuessing)); | |
332 }, | |
333 | |
334 /** | |
335 * @param {!Array.<string>} lines | |
336 */ | |
337 _setEditorIndentation: function(lines) | |
338 { | |
339 var extraKeys = {}; | |
340 var indent = WebInspector.moduleSetting("textEditorIndent").get(); | |
341 if (WebInspector.moduleSetting("textEditorAutoDetectIndent").get()) | |
342 indent = WebInspector.SourcesTextEditor._guessIndentationLevel(lines
); | |
343 | |
344 if (indent === WebInspector.TextUtils.Indent.TabCharacter) { | |
345 this.codeMirror().setOption("indentWithTabs", true); | |
346 this.codeMirror().setOption("indentUnit", 4); | |
347 } else { | |
348 this.codeMirror().setOption("indentWithTabs", false); | |
349 this.codeMirror().setOption("indentUnit", indent.length); | |
350 extraKeys.Tab = function(codeMirror) | |
351 { | |
352 if (codeMirror.somethingSelected()) | |
353 return CodeMirror.Pass; | |
354 var pos = codeMirror.getCursor("head"); | |
355 codeMirror.replaceRange(indent.substring(pos.ch % indent.length)
, codeMirror.getCursor()); | |
356 }; | |
357 } | |
358 | |
359 this.codeMirror().setOption("extraKeys", extraKeys); | |
360 this._indentationLevel = indent; | |
361 }, | |
362 | |
363 /** | |
364 * @return {string} | |
365 */ | |
366 indent: function() | |
367 { | |
368 return this._indentationLevel; | |
369 }, | |
370 | |
371 _onAutoAppendedSpaces: function() | |
372 { | |
373 this._autoAppendedSpaces = this._autoAppendedSpaces || []; | |
374 | |
375 for (var i = 0; i < this._autoAppendedSpaces.length; ++i) { | |
376 var position = this._autoAppendedSpaces[i].resolve(); | |
377 if (!position) | |
378 continue; | |
379 var line = this.line(position.lineNumber); | |
380 if (line.length === position.columnNumber && WebInspector.TextUtils.
lineIndent(line).length === line.length) | |
381 this.codeMirror().replaceRange("", new CodeMirror.Pos(position.l
ineNumber, 0), new CodeMirror.Pos(position.lineNumber, position.columnNumber)); | |
382 } | |
383 | |
384 this._autoAppendedSpaces = []; | |
385 var selections = this.selections(); | |
386 for (var i = 0; i < selections.length; ++i) { | |
387 var selection = selections[i]; | |
388 this._autoAppendedSpaces.push(this.textEditorPositionHandle(selectio
n.startLine, selection.startColumn)); | |
389 } | |
390 }, | |
391 | |
392 /** | |
393 * @param {!CodeMirror} codeMirror | |
394 * @param {!Array.<!CodeMirror.ChangeObject>} changes | |
395 */ | |
396 _fireTextChanged: function(codeMirror, changes) | |
397 { | |
398 if (!changes.length || this._muteTextChangedEvent) | |
399 return; | |
400 var edits = []; | |
401 var currentEdit; | |
402 | |
403 for (var changeIndex = 0; changeIndex < changes.length; ++changeIndex) { | |
404 var changeObject = changes[changeIndex]; | |
405 var edit = WebInspector.CodeMirrorUtils.changeObjectToEditOperation(
changeObject); | |
406 if (currentEdit && edit.oldRange.equal(currentEdit.newRange)) { | |
407 currentEdit.newRange = edit.newRange; | |
408 } else { | |
409 currentEdit = edit; | |
410 edits.push(currentEdit); | |
411 } | |
412 } | |
413 | |
414 for (var i = 0; i < edits.length; ++i) | |
415 this.dispatchEventToListeners(WebInspector.SourcesTextEditor.Events.
TextChanged, edits[i]); | |
416 }, | |
417 | |
418 _cursorActivity: function() | |
419 { | |
420 if (!this._isSearchActive()) | |
421 this.codeMirror().operation(this._tokenHighlighter.highlightSelected
Tokens.bind(this._tokenHighlighter)); | |
422 | |
423 var start = this.codeMirror().getCursor("anchor"); | |
424 var end = this.codeMirror().getCursor("head"); | |
425 this.dispatchEventToListeners(WebInspector.SourcesTextEditor.Events.Sele
ctionChanged, WebInspector.CodeMirrorUtils.toRange(start, end)); | |
426 }, | |
427 | |
428 /** | |
429 * @param {?WebInspector.TextRange} from | |
430 * @param {?WebInspector.TextRange} to | |
431 */ | |
432 _reportJump: function(from, to) | |
433 { | |
434 if (from && to && from.equal(to)) | |
435 return; | |
436 this.dispatchEventToListeners(WebInspector.SourcesTextEditor.Events.Jump
Happened, { from: from, to: to }); | |
437 }, | |
438 | |
439 _scroll: function() | |
440 { | |
441 var topmostLineNumber = this.codeMirror().lineAtHeight(this.codeMirror()
.getScrollInfo().top, "local"); | |
442 this.dispatchEventToListeners(WebInspector.SourcesTextEditor.Events.Scro
llChanged, topmostLineNumber); | |
443 }, | |
444 | |
445 _focus: function() | |
446 { | |
447 this.dispatchEventToListeners(WebInspector.SourcesTextEditor.Events.Edit
orFocused); | |
448 }, | |
449 | |
450 _blur: function() | |
451 { | |
452 this.dispatchEventToListeners(WebInspector.SourcesTextEditor.Events.Edit
orBlurred); | |
453 }, | |
454 | |
455 /** | |
456 * @param {!CodeMirror} codeMirror | |
457 * @param {{ranges: !Array.<{head: !CodeMirror.Pos, anchor: !CodeMirror.Pos}
>}} selection | |
458 */ | |
459 _fireBeforeSelectionChanged: function(codeMirror, selection) | |
460 { | |
461 if (!this._isHandlingMouseDownEvent) | |
462 return; | |
463 if (!selection.ranges.length) | |
464 return; | |
465 | |
466 var primarySelection = selection.ranges[0]; | |
467 this._reportJump(this.selection(), WebInspector.CodeMirrorUtils.toRange(
primarySelection.anchor, primarySelection.head)); | |
468 }, | |
469 | |
470 /** | |
471 * @override | |
472 */ | |
473 dispose: function() | |
474 { | |
475 WebInspector.CodeMirrorTextEditor.prototype.dispose.call(this); | |
476 WebInspector.moduleSetting("textEditorIndent").removeChangeListener(this
._onUpdateEditorIndentation, this); | |
477 WebInspector.moduleSetting("textEditorAutoDetectIndent").removeChangeLis
tener(this._onUpdateEditorIndentation, this); | |
478 WebInspector.moduleSetting("showWhitespacesInEditor").removeChangeListen
er(this._updateWhitespace, this); | |
479 }, | |
480 | |
481 /** | |
482 * @override | |
483 * @param {string} text | |
484 */ | |
485 setText: function(text) | |
486 { | |
487 this._muteTextChangedEvent = true; | |
488 this._setEditorIndentation(text.split("\n").slice(0, WebInspector.Source
sTextEditor.LinesToScanForIndentationGuessing)); | |
489 WebInspector.CodeMirrorTextEditor.prototype.setText.call(this, text); | |
490 delete this._muteTextChangedEvent; | |
491 }, | |
492 | |
493 /** | |
494 * @override | |
495 * @param {string} mimeType | |
496 * @return {!Promise} | |
497 */ | |
498 setMimeType: function(mimeType) | |
499 { | |
500 this._mimeType = mimeType; | |
501 return WebInspector.CodeMirrorTextEditor.prototype.setMimeType.call(this
, mimeType) | |
502 .then(() => this._codeMirror.setOption("mode", this._applyWhitespace
Mimetype(mimeType))); | |
503 }, | |
504 | |
505 _updateWhitespace: function() | |
506 { | |
507 if (this._mimeType) | |
508 this.setMimeType(this._mimeType); | |
509 }, | |
510 | |
511 /** | |
512 * @param {string} mimeType | |
513 * @return {string} | |
514 */ | |
515 _applyWhitespaceMimetype: function(mimeType) | |
516 { | |
517 this._setupWhitespaceHighlight(); | |
518 var whitespaceMode = WebInspector.moduleSetting("showWhitespacesInEditor
").get(); | |
519 this.element.classList.toggle("show-whitespaces", whitespaceMode === "al
l"); | |
520 | |
521 if (whitespaceMode === "all") | |
522 return this._allWhitespaceOverlayMode(mimeType); | |
523 else if (whitespaceMode === "trailing") | |
524 return this._trailingWhitespaceOverlayMode(mimeType); | |
525 | |
526 return mimeType; | |
527 }, | |
528 | |
529 /** | |
530 * @param {string} mimeType | |
531 * @return {string} | |
532 */ | |
533 _allWhitespaceOverlayMode: function(mimeType) | |
534 { | |
535 var modeName = CodeMirror.mimeModes[mimeType] ? (CodeMirror.mimeModes[mi
meType].name || CodeMirror.mimeModes[mimeType]) : CodeMirror.mimeModes["text/pla
in"]; | |
536 modeName += "+all-whitespaces"; | |
537 if (CodeMirror.modes[modeName]) | |
538 return modeName; | |
539 | |
540 function modeConstructor(config, parserConfig) | |
541 { | |
542 function nextToken(stream) | |
543 { | |
544 if (stream.peek() === " ") { | |
545 var spaces = 0; | |
546 while (spaces < WebInspector.SourcesTextEditor.MaximumNumber
OfWhitespacesPerSingleSpan && stream.peek() === " ") { | |
547 ++spaces; | |
548 stream.next(); | |
549 } | |
550 return "whitespace whitespace-" + spaces; | |
551 } | |
552 while (!stream.eol() && stream.peek() !== " ") | |
553 stream.next(); | |
554 return null; | |
555 } | |
556 var whitespaceMode = { | |
557 token: nextToken | |
558 }; | |
559 return CodeMirror.overlayMode(CodeMirror.getMode(config, mimeType),
whitespaceMode, false); | |
560 } | |
561 CodeMirror.defineMode(modeName, modeConstructor); | |
562 return modeName; | |
563 }, | |
564 | |
565 /** | |
566 * @param {string} mimeType | |
567 * @return {string} | |
568 */ | |
569 _trailingWhitespaceOverlayMode: function(mimeType) | |
570 { | |
571 var modeName = CodeMirror.mimeModes[mimeType] ? (CodeMirror.mimeModes[mi
meType].name || CodeMirror.mimeModes[mimeType]) : CodeMirror.mimeModes["text/pla
in"]; | |
572 modeName += "+trailing-whitespaces"; | |
573 if (CodeMirror.modes[modeName]) | |
574 return modeName; | |
575 | |
576 function modeConstructor(config, parserConfig) | |
577 { | |
578 function nextToken(stream) | |
579 { | |
580 var pos = stream.pos; | |
581 if (stream.match(/^\s+$/, true)) | |
582 return true ? "trailing-whitespace" : null; | |
583 do { | |
584 stream.next(); | |
585 } while (!stream.eol() && stream.peek() !== " "); | |
586 return null; | |
587 } | |
588 var whitespaceMode = { | |
589 token: nextToken | |
590 }; | |
591 return CodeMirror.overlayMode(CodeMirror.getMode(config, mimeType),
whitespaceMode, false); | |
592 } | |
593 CodeMirror.defineMode(modeName, modeConstructor); | |
594 return modeName; | |
595 }, | |
596 | |
597 _setupWhitespaceHighlight: function() | |
598 { | |
599 var doc = this.element.ownerDocument; | |
600 if (doc._codeMirrorWhitespaceStyleInjected || !WebInspector.moduleSettin
g("showWhitespacesInEditor").get()) | |
601 return; | |
602 doc._codeMirrorWhitespaceStyleInjected = true; | |
603 const classBase = ".show-whitespaces .CodeMirror .cm-whitespace-"; | |
604 const spaceChar = "·"; | |
605 var spaceChars = ""; | |
606 var rules = ""; | |
607 for (var i = 1; i <= WebInspector.SourcesTextEditor.MaximumNumberOfWhite
spacesPerSingleSpan; ++i) { | |
608 spaceChars += spaceChar; | |
609 var rule = classBase + i + "::before { content: '" + spaceChars + "'
;}\n"; | |
610 rules += rule; | |
611 } | |
612 var style = doc.createElement("style"); | |
613 style.textContent = rules; | |
614 doc.head.appendChild(style); | |
615 }, | |
616 | |
617 __proto__: WebInspector.CodeMirrorTextEditor.prototype | |
618 }; | |
619 | 619 |
620 /** @typedef {{lineNumber: number, event: !Event}} */ | 620 /** @typedef {{lineNumber: number, event: !Event}} */ |
621 WebInspector.SourcesTextEditor.GutterClickEventData; | 621 WebInspector.SourcesTextEditor.GutterClickEventData; |
622 | 622 |
623 /** @enum {symbol} */ | 623 /** @enum {symbol} */ |
624 WebInspector.SourcesTextEditor.Events = { | 624 WebInspector.SourcesTextEditor.Events = { |
625 GutterClick: Symbol("GutterClick"), | 625 GutterClick: Symbol('GutterClick'), |
626 TextChanged: Symbol("TextChanged"), | 626 TextChanged: Symbol('TextChanged'), |
627 SelectionChanged: Symbol("SelectionChanged"), | 627 SelectionChanged: Symbol('SelectionChanged'), |
628 ScrollChanged: Symbol("ScrollChanged"), | 628 ScrollChanged: Symbol('ScrollChanged'), |
629 EditorFocused: Symbol("EditorFocused"), | 629 EditorFocused: Symbol('EditorFocused'), |
630 EditorBlurred: Symbol("EditorBlurred"), | 630 EditorBlurred: Symbol('EditorBlurred'), |
631 JumpHappened: Symbol("JumpHappened") | 631 JumpHappened: Symbol('JumpHappened') |
632 }; | 632 }; |
633 | 633 |
634 /** | 634 /** |
635 * @interface | 635 * @interface |
636 */ | 636 */ |
637 WebInspector.SourcesTextEditorDelegate = function() { }; | 637 WebInspector.SourcesTextEditorDelegate = function() {}; |
638 WebInspector.SourcesTextEditorDelegate.prototype = { | 638 WebInspector.SourcesTextEditorDelegate.prototype = { |
639 /** | 639 /** |
640 * @param {!WebInspector.ContextMenu} contextMenu | 640 * @param {!WebInspector.ContextMenu} contextMenu |
641 * @param {number} lineNumber | 641 * @param {number} lineNumber |
642 * @return {!Promise} | 642 * @return {!Promise} |
643 */ | 643 */ |
644 populateLineGutterContextMenu: function(contextMenu, lineNumber) { }, | 644 populateLineGutterContextMenu: function(contextMenu, lineNumber) {}, |
645 | 645 |
646 /** | 646 /** |
647 * @param {!WebInspector.ContextMenu} contextMenu | 647 * @param {!WebInspector.ContextMenu} contextMenu |
648 * @param {number} lineNumber | 648 * @param {number} lineNumber |
649 * @param {number} columnNumber | 649 * @param {number} columnNumber |
650 * @return {!Promise} | 650 * @return {!Promise} |
651 */ | 651 */ |
652 populateTextAreaContextMenu: function(contextMenu, lineNumber, columnNumber)
{ }, | 652 populateTextAreaContextMenu: function(contextMenu, lineNumber, columnNumber) {
}, |
653 }; | 653 }; |
654 | 654 |
655 /** | 655 /** |
656 * @param {!CodeMirror} codeMirror | 656 * @param {!CodeMirror} codeMirror |
657 */ | 657 */ |
658 CodeMirror.commands.smartNewlineAndIndent = function(codeMirror) | 658 CodeMirror.commands.smartNewlineAndIndent = function(codeMirror) { |
659 { | 659 codeMirror.operation(innerSmartNewlineAndIndent.bind(null, codeMirror)); |
660 codeMirror.operation(innerSmartNewlineAndIndent.bind(null, codeMirror)); | 660 function innerSmartNewlineAndIndent(codeMirror) { |
661 function innerSmartNewlineAndIndent(codeMirror) | 661 var selections = codeMirror.listSelections(); |
662 { | 662 var replacements = []; |
663 var selections = codeMirror.listSelections(); | 663 for (var i = 0; i < selections.length; ++i) { |
664 var replacements = []; | 664 var selection = selections[i]; |
665 for (var i = 0; i < selections.length; ++i) { | 665 var cur = CodeMirror.cmpPos(selection.head, selection.anchor) < 0 ? select
ion.head : selection.anchor; |
666 var selection = selections[i]; | 666 var line = codeMirror.getLine(cur.line); |
667 var cur = CodeMirror.cmpPos(selection.head, selection.anchor) < 0 ?
selection.head : selection.anchor; | 667 var indent = WebInspector.TextUtils.lineIndent(line); |
668 var line = codeMirror.getLine(cur.line); | 668 replacements.push('\n' + indent.substring(0, Math.min(cur.ch, indent.lengt
h))); |
669 var indent = WebInspector.TextUtils.lineIndent(line); | 669 } |
670 replacements.push("\n" + indent.substring(0, Math.min(cur.ch, indent
.length))); | 670 codeMirror.replaceSelections(replacements); |
671 } | 671 codeMirror._codeMirrorTextEditor._onAutoAppendedSpaces(); |
672 codeMirror.replaceSelections(replacements); | 672 } |
673 codeMirror._codeMirrorTextEditor._onAutoAppendedSpaces(); | |
674 } | |
675 }; | 673 }; |
676 | 674 |
677 /** | 675 /** |
678 * @return {!Object|undefined} | 676 * @return {!Object|undefined} |
679 */ | 677 */ |
680 CodeMirror.commands.sourcesDismiss = function(codemirror) | 678 CodeMirror.commands.sourcesDismiss = function(codemirror) { |
681 { | 679 if (codemirror.listSelections().length === 1 && codemirror._codeMirrorTextEdit
or._isSearchActive()) |
682 if (codemirror.listSelections().length === 1 && codemirror._codeMirrorTextEd
itor._isSearchActive()) | 680 return CodeMirror.Pass; |
683 return CodeMirror.Pass; | 681 return CodeMirror.commands.dismiss(codemirror); |
684 return CodeMirror.commands.dismiss(codemirror); | 682 }; |
685 }; | 683 |
686 | 684 WebInspector.SourcesTextEditor._BlockIndentController = { |
687 /** | 685 name: 'blockIndentKeymap', |
688 * @constructor | 686 |
689 * @param {!CodeMirror} codeMirror | 687 /** |
690 */ | 688 * @return {*} |
691 WebInspector.SourcesTextEditor.BlockIndentController = function(codeMirror) | 689 */ |
692 { | 690 Enter: function(codeMirror) { |
693 codeMirror.addKeyMap(this); | 691 var selections = codeMirror.listSelections(); |
694 }; | 692 var replacements = []; |
695 | 693 var allSelectionsAreCollapsedBlocks = false; |
696 WebInspector.SourcesTextEditor.BlockIndentController.prototype = { | 694 for (var i = 0; i < selections.length; ++i) { |
697 name: "blockIndentKeymap", | 695 var selection = selections[i]; |
698 | 696 var start = CodeMirror.cmpPos(selection.head, selection.anchor) < 0 ? sele
ction.head : selection.anchor; |
699 /** | 697 var line = codeMirror.getLine(start.line); |
700 * @return {*} | 698 var indent = WebInspector.TextUtils.lineIndent(line); |
701 */ | 699 var indentToInsert = '\n' + indent + codeMirror._codeMirrorTextEditor.inde
nt(); |
702 Enter: function(codeMirror) | 700 var isCollapsedBlock = false; |
703 { | 701 if (selection.head.ch === 0) |
704 var selections = codeMirror.listSelections(); | 702 return CodeMirror.Pass; |
705 var replacements = []; | 703 if (line.substr(selection.head.ch - 1, 2) === '{}') { |
706 var allSelectionsAreCollapsedBlocks = false; | 704 indentToInsert += '\n' + indent; |
707 for (var i = 0; i < selections.length; ++i) { | 705 isCollapsedBlock = true; |
708 var selection = selections[i]; | 706 } else if (line.substr(selection.head.ch - 1, 1) !== '{') { |
709 var start = CodeMirror.cmpPos(selection.head, selection.anchor) < 0
? selection.head : selection.anchor; | 707 return CodeMirror.Pass; |
710 var line = codeMirror.getLine(start.line); | 708 } |
711 var indent = WebInspector.TextUtils.lineIndent(line); | 709 if (i > 0 && allSelectionsAreCollapsedBlocks !== isCollapsedBlock) |
712 var indentToInsert = "\n" + indent + codeMirror._codeMirrorTextEdito
r.indent(); | 710 return CodeMirror.Pass; |
713 var isCollapsedBlock = false; | 711 replacements.push(indentToInsert); |
714 if (selection.head.ch === 0) | 712 allSelectionsAreCollapsedBlocks = isCollapsedBlock; |
715 return CodeMirror.Pass; | 713 } |
716 if (line.substr(selection.head.ch - 1, 2) === "{}") { | 714 codeMirror.replaceSelections(replacements); |
717 indentToInsert += "\n" + indent; | 715 if (!allSelectionsAreCollapsedBlocks) { |
718 isCollapsedBlock = true; | 716 codeMirror._codeMirrorTextEditor._onAutoAppendedSpaces(); |
719 } else if (line.substr(selection.head.ch - 1, 1) !== "{") { | 717 return; |
720 return CodeMirror.Pass; | 718 } |
721 } | 719 selections = codeMirror.listSelections(); |
722 if (i > 0 && allSelectionsAreCollapsedBlocks !== isCollapsedBlock) | 720 var updatedSelections = []; |
723 return CodeMirror.Pass; | 721 for (var i = 0; i < selections.length; ++i) { |
724 replacements.push(indentToInsert); | 722 var selection = selections[i]; |
725 allSelectionsAreCollapsedBlocks = isCollapsedBlock; | 723 var line = codeMirror.getLine(selection.head.line - 1); |
726 } | 724 var position = new CodeMirror.Pos(selection.head.line - 1, line.length); |
727 codeMirror.replaceSelections(replacements); | 725 updatedSelections.push({head: position, anchor: position}); |
728 if (!allSelectionsAreCollapsedBlocks) { | 726 } |
729 codeMirror._codeMirrorTextEditor._onAutoAppendedSpaces(); | 727 codeMirror.setSelections(updatedSelections); |
730 return; | 728 codeMirror._codeMirrorTextEditor._onAutoAppendedSpaces(); |
731 } | 729 }, |
732 selections = codeMirror.listSelections(); | 730 |
733 var updatedSelections = []; | 731 /** |
734 for (var i = 0; i < selections.length; ++i) { | 732 * @return {*} |
735 var selection = selections[i]; | 733 */ |
736 var line = codeMirror.getLine(selection.head.line - 1); | 734 '\'}\'': function(codeMirror) { |
737 var position = new CodeMirror.Pos(selection.head.line - 1, line.leng
th); | 735 if (codeMirror.somethingSelected()) |
738 updatedSelections.push({ | 736 return CodeMirror.Pass; |
739 head: position, | 737 var selections = codeMirror.listSelections(); |
740 anchor: position | 738 var replacements = []; |
741 }); | 739 for (var i = 0; i < selections.length; ++i) { |
742 } | 740 var selection = selections[i]; |
743 codeMirror.setSelections(updatedSelections); | 741 var line = codeMirror.getLine(selection.head.line); |
744 codeMirror._codeMirrorTextEditor._onAutoAppendedSpaces(); | 742 if (line !== WebInspector.TextUtils.lineIndent(line)) |
745 }, | 743 return CodeMirror.Pass; |
746 | 744 replacements.push('}'); |
747 /** | 745 } |
748 * @return {*} | 746 codeMirror.replaceSelections(replacements); |
749 */ | 747 selections = codeMirror.listSelections(); |
750 "'}'": function(codeMirror) | 748 replacements = []; |
751 { | 749 var updatedSelections = []; |
752 if (codeMirror.somethingSelected()) | 750 for (var i = 0; i < selections.length; ++i) { |
753 return CodeMirror.Pass; | 751 var selection = selections[i]; |
754 var selections = codeMirror.listSelections(); | 752 var matchingBracket = codeMirror.findMatchingBracket(selection.head); |
755 var replacements = []; | 753 if (!matchingBracket || !matchingBracket.match) |
756 for (var i = 0; i < selections.length; ++i) { | 754 return; |
757 var selection = selections[i]; | 755 updatedSelections.push({head: selection.head, anchor: new CodeMirror.Pos(s
election.head.line, 0)}); |
758 var line = codeMirror.getLine(selection.head.line); | 756 var line = codeMirror.getLine(matchingBracket.to.line); |
759 if (line !== WebInspector.TextUtils.lineIndent(line)) | 757 var indent = WebInspector.TextUtils.lineIndent(line); |
760 return CodeMirror.Pass; | 758 replacements.push(indent + '}'); |
761 replacements.push("}"); | 759 } |
762 } | 760 codeMirror.setSelections(updatedSelections); |
763 codeMirror.replaceSelections(replacements); | 761 codeMirror.replaceSelections(replacements); |
764 selections = codeMirror.listSelections(); | 762 } |
765 replacements = []; | 763 }; |
766 var updatedSelections = []; | 764 |
767 for (var i = 0; i < selections.length; ++i) { | 765 |
768 var selection = selections[i]; | 766 /** |
769 var matchingBracket = codeMirror.findMatchingBracket(selection.head)
; | 767 * @unrestricted |
770 if (!matchingBracket || !matchingBracket.match) | 768 */ |
771 return; | 769 WebInspector.SourcesTextEditor.TokenHighlighter = class { |
772 updatedSelections.push({ | 770 /** |
773 head: selection.head, | 771 * @param {!WebInspector.SourcesTextEditor} textEditor |
774 anchor: new CodeMirror.Pos(selection.head.line, 0) | 772 * @param {!CodeMirror} codeMirror |
775 }); | 773 */ |
776 var line = codeMirror.getLine(matchingBracket.to.line); | 774 constructor(textEditor, codeMirror) { |
777 var indent = WebInspector.TextUtils.lineIndent(line); | |
778 replacements.push(indent + "}"); | |
779 } | |
780 codeMirror.setSelections(updatedSelections); | |
781 codeMirror.replaceSelections(replacements); | |
782 } | |
783 }; | |
784 | |
785 /** | |
786 * @param {!Array.<string>} lines | |
787 * @return {string} | |
788 */ | |
789 WebInspector.SourcesTextEditor._guessIndentationLevel = function(lines) | |
790 { | |
791 var tabRegex = /^\t+/; | |
792 var tabLines = 0; | |
793 var indents = {}; | |
794 for (var lineNumber = 0; lineNumber < lines.length; ++lineNumber) { | |
795 var text = lines[lineNumber]; | |
796 if (text.length === 0 || !WebInspector.TextUtils.isSpaceChar(text[0])) | |
797 continue; | |
798 if (tabRegex.test(text)) { | |
799 ++tabLines; | |
800 continue; | |
801 } | |
802 var i = 0; | |
803 while (i < text.length && WebInspector.TextUtils.isSpaceChar(text[i])) | |
804 ++i; | |
805 if (i % 2 !== 0) | |
806 continue; | |
807 indents[i] = 1 + (indents[i] || 0); | |
808 } | |
809 var linesCountPerIndentThreshold = 3 * lines.length / 100; | |
810 if (tabLines && tabLines > linesCountPerIndentThreshold) | |
811 return "\t"; | |
812 var minimumIndent = Infinity; | |
813 for (var i in indents) { | |
814 if (indents[i] < linesCountPerIndentThreshold) | |
815 continue; | |
816 var indent = parseInt(i, 10); | |
817 if (minimumIndent > indent) | |
818 minimumIndent = indent; | |
819 } | |
820 if (minimumIndent === Infinity) | |
821 return WebInspector.moduleSetting("textEditorIndent").get(); | |
822 return " ".repeat(minimumIndent); | |
823 }; | |
824 | |
825 /** | |
826 * @constructor | |
827 * @param {!WebInspector.SourcesTextEditor} textEditor | |
828 * @param {!CodeMirror} codeMirror | |
829 */ | |
830 WebInspector.SourcesTextEditor.TokenHighlighter = function(textEditor, codeMirro
r) | |
831 { | |
832 this._textEditor = textEditor; | 775 this._textEditor = textEditor; |
833 this._codeMirror = codeMirror; | 776 this._codeMirror = codeMirror; |
834 }; | 777 } |
835 | 778 |
836 WebInspector.SourcesTextEditor.TokenHighlighter.prototype = { | 779 /** |
837 /** | 780 * @param {!RegExp} regex |
838 * @param {!RegExp} regex | 781 * @param {?WebInspector.TextRange} range |
839 * @param {?WebInspector.TextRange} range | 782 */ |
840 */ | 783 highlightSearchResults(regex, range) { |
841 highlightSearchResults: function(regex, range) | 784 var oldRegex = this._highlightRegex; |
842 { | 785 this._highlightRegex = regex; |
843 var oldRegex = this._highlightRegex; | 786 this._highlightRange = range; |
844 this._highlightRegex = regex; | 787 if (this._searchResultMarker) { |
845 this._highlightRange = range; | 788 this._searchResultMarker.clear(); |
846 if (this._searchResultMarker) { | 789 delete this._searchResultMarker; |
847 this._searchResultMarker.clear(); | 790 } |
848 delete this._searchResultMarker; | 791 if (this._highlightDescriptor && this._highlightDescriptor.selectionStart) |
849 } | 792 this._codeMirror.removeLineClass(this._highlightDescriptor.selectionStart.
line, 'wrap', 'cm-line-with-selection'); |
850 if (this._highlightDescriptor && this._highlightDescriptor.selectionStar
t) | 793 var selectionStart = this._highlightRange ? |
851 this._codeMirror.removeLineClass(this._highlightDescriptor.selection
Start.line, "wrap", "cm-line-with-selection"); | 794 new CodeMirror.Pos(this._highlightRange.startLine, this._highlightRange.
startColumn) : |
852 var selectionStart = this._highlightRange ? new CodeMirror.Pos(this._hig
hlightRange.startLine, this._highlightRange.startColumn) : null; | 795 null; |
853 if (selectionStart) | 796 if (selectionStart) |
854 this._codeMirror.addLineClass(selectionStart.line, "wrap", "cm-line-
with-selection"); | 797 this._codeMirror.addLineClass(selectionStart.line, 'wrap', 'cm-line-with-s
election'); |
855 if (this._highlightRegex === oldRegex) { | 798 if (this._highlightRegex === oldRegex) { |
856 // Do not re-add overlay mode if regex did not change for better per
formance. | 799 // Do not re-add overlay mode if regex did not change for better performan
ce. |
857 if (this._highlightDescriptor) | 800 if (this._highlightDescriptor) |
858 this._highlightDescriptor.selectionStart = selectionStart; | 801 this._highlightDescriptor.selectionStart = selectionStart; |
859 } else { | 802 } else { |
860 this._removeHighlight(); | 803 this._removeHighlight(); |
861 this._setHighlighter(this._searchHighlighter.bind(this, this._highli
ghtRegex), selectionStart); | 804 this._setHighlighter(this._searchHighlighter.bind(this, this._highlightReg
ex), selectionStart); |
862 } | 805 } |
863 if (this._highlightRange) { | 806 if (this._highlightRange) { |
864 var pos = WebInspector.CodeMirrorUtils.toPos(this._highlightRange); | 807 var pos = WebInspector.CodeMirrorUtils.toPos(this._highlightRange); |
865 this._searchResultMarker = this._codeMirror.markText(pos.start, pos.
end, {className: "cm-column-with-selection"}); | 808 this._searchResultMarker = this._codeMirror.markText(pos.start, pos.end, {
className: 'cm-column-with-selection'}); |
866 } | 809 } |
867 }, | 810 } |
868 | 811 |
869 /** | 812 /** |
870 * @return {!RegExp|undefined} | 813 * @return {!RegExp|undefined} |
871 */ | 814 */ |
872 highlightedRegex: function() | 815 highlightedRegex() { |
873 { | 816 return this._highlightRegex; |
874 return this._highlightRegex; | 817 } |
875 }, | 818 |
876 | 819 highlightSelectedTokens() { |
877 highlightSelectedTokens: function() | 820 delete this._highlightRegex; |
878 { | 821 delete this._highlightRange; |
879 delete this._highlightRegex; | 822 if (this._highlightDescriptor && this._highlightDescriptor.selectionStart) |
880 delete this._highlightRange; | 823 this._codeMirror.removeLineClass(this._highlightDescriptor.selectionStart.
line, 'wrap', 'cm-line-with-selection'); |
881 if (this._highlightDescriptor && this._highlightDescriptor.selectionStar
t) | 824 this._removeHighlight(); |
882 this._codeMirror.removeLineClass(this._highlightDescriptor.selection
Start.line, "wrap", "cm-line-with-selection"); | 825 var selectionStart = this._codeMirror.getCursor('start'); |
883 this._removeHighlight(); | 826 var selectionEnd = this._codeMirror.getCursor('end'); |
884 var selectionStart = this._codeMirror.getCursor("start"); | 827 if (selectionStart.line !== selectionEnd.line) |
885 var selectionEnd = this._codeMirror.getCursor("end"); | 828 return; |
886 if (selectionStart.line !== selectionEnd.line) | 829 if (selectionStart.ch === selectionEnd.ch) |
887 return; | 830 return; |
888 if (selectionStart.ch === selectionEnd.ch) | 831 var selections = this._codeMirror.getSelections(); |
889 return; | 832 if (selections.length > 1) |
890 var selections = this._codeMirror.getSelections(); | 833 return; |
891 if (selections.length > 1) | 834 var selectedText = selections[0]; |
892 return; | 835 if (this._isWord(selectedText, selectionStart.line, selectionStart.ch, selec
tionEnd.ch)) { |
893 var selectedText = selections[0]; | 836 if (selectionStart) |
894 if (this._isWord(selectedText, selectionStart.line, selectionStart.ch, s
electionEnd.ch)) { | 837 this._codeMirror.addLineClass(selectionStart.line, 'wrap', 'cm-line-with
-selection'); |
895 if (selectionStart) | 838 this._setHighlighter(this._tokenHighlighter.bind(this, selectedText, selec
tionStart), selectionStart); |
896 this._codeMirror.addLineClass(selectionStart.line, "wrap", "cm-l
ine-with-selection"); | 839 } |
897 this._setHighlighter(this._tokenHighlighter.bind(this, selectedText,
selectionStart), selectionStart); | 840 } |
898 } | 841 |
899 }, | 842 /** |
900 | 843 * @param {string} selectedText |
901 /** | 844 * @param {number} lineNumber |
902 * @param {string} selectedText | 845 * @param {number} startColumn |
903 * @param {number} lineNumber | 846 * @param {number} endColumn |
904 * @param {number} startColumn | 847 */ |
905 * @param {number} endColumn | 848 _isWord(selectedText, lineNumber, startColumn, endColumn) { |
906 */ | 849 var line = this._codeMirror.getLine(lineNumber); |
907 _isWord: function(selectedText, lineNumber, startColumn, endColumn) | 850 var leftBound = startColumn === 0 || !WebInspector.TextUtils.isWordChar(line
.charAt(startColumn - 1)); |
908 { | 851 var rightBound = endColumn === line.length || !WebInspector.TextUtils.isWord
Char(line.charAt(endColumn)); |
909 var line = this._codeMirror.getLine(lineNumber); | 852 return leftBound && rightBound && WebInspector.TextUtils.isWord(selectedText
); |
910 var leftBound = startColumn === 0 || !WebInspector.TextUtils.isWordChar(
line.charAt(startColumn - 1)); | 853 } |
911 var rightBound = endColumn === line.length || !WebInspector.TextUtils.is
WordChar(line.charAt(endColumn)); | 854 |
912 return leftBound && rightBound && WebInspector.TextUtils.isWord(selected
Text); | 855 _removeHighlight() { |
913 }, | 856 if (this._highlightDescriptor) { |
914 | 857 this._codeMirror.removeOverlay(this._highlightDescriptor.overlay); |
915 _removeHighlight: function() | 858 delete this._highlightDescriptor; |
916 { | 859 } |
917 if (this._highlightDescriptor) { | 860 } |
918 this._codeMirror.removeOverlay(this._highlightDescriptor.overlay); | 861 |
919 delete this._highlightDescriptor; | 862 /** |
920 } | 863 * @param {!RegExp} regex |
921 }, | 864 * @param {!CodeMirror.StringStream} stream |
922 | 865 */ |
923 /** | 866 _searchHighlighter(regex, stream) { |
924 * @param {!RegExp} regex | 867 if (stream.column() === 0) |
925 * @param {!CodeMirror.StringStream} stream | 868 delete this._searchMatchLength; |
926 */ | 869 if (this._searchMatchLength) { |
927 _searchHighlighter: function(regex, stream) | 870 if (this._searchMatchLength > 2) { |
928 { | 871 for (var i = 0; i < this._searchMatchLength - 2; ++i) |
929 if (stream.column() === 0) | 872 stream.next(); |
930 delete this._searchMatchLength; | 873 this._searchMatchLength = 1; |
931 if (this._searchMatchLength) { | 874 return 'search-highlight'; |
932 if (this._searchMatchLength > 2) { | 875 } else { |
933 for (var i = 0; i < this._searchMatchLength - 2; ++i) | 876 stream.next(); |
934 stream.next(); | 877 delete this._searchMatchLength; |
935 this._searchMatchLength = 1; | 878 return 'search-highlight search-highlight-end'; |
936 return "search-highlight"; | 879 } |
937 } else { | 880 } |
938 stream.next(); | 881 var match = stream.match(regex, false); |
939 delete this._searchMatchLength; | 882 if (match) { |
940 return "search-highlight search-highlight-end"; | 883 stream.next(); |
941 } | 884 var matchLength = match[0].length; |
942 } | 885 if (matchLength === 1) |
943 var match = stream.match(regex, false); | 886 return 'search-highlight search-highlight-full'; |
944 if (match) { | 887 this._searchMatchLength = matchLength; |
945 stream.next(); | 888 return 'search-highlight search-highlight-start'; |
946 var matchLength = match[0].length; | 889 } |
947 if (matchLength === 1) | 890 while (!stream.match(regex, false) && stream.next()) { |
948 return "search-highlight search-highlight-full"; | 891 } |
949 this._searchMatchLength = matchLength; | 892 } |
950 return "search-highlight search-highlight-start"; | 893 |
951 } | 894 /** |
952 while (!stream.match(regex, false) && stream.next()) {} | 895 * @param {string} token |
953 }, | 896 * @param {!CodeMirror.Pos} selectionStart |
954 | 897 * @param {!CodeMirror.StringStream} stream |
955 /** | 898 */ |
956 * @param {string} token | 899 _tokenHighlighter(token, selectionStart, stream) { |
957 * @param {!CodeMirror.Pos} selectionStart | 900 var tokenFirstChar = token.charAt(0); |
958 * @param {!CodeMirror.StringStream} stream | 901 if (stream.match(token) && (stream.eol() || !WebInspector.TextUtils.isWordCh
ar(stream.peek()))) |
959 */ | 902 return stream.column() === selectionStart.ch ? 'token-highlight column-wit
h-selection' : 'token-highlight'; |
960 _tokenHighlighter: function(token, selectionStart, stream) | 903 var eatenChar; |
961 { | 904 do { |
962 var tokenFirstChar = token.charAt(0); | 905 eatenChar = stream.next(); |
963 if (stream.match(token) && (stream.eol() || !WebInspector.TextUtils.isWo
rdChar(stream.peek()))) | 906 } while (eatenChar && (WebInspector.TextUtils.isWordChar(eatenChar) || strea
m.peek() !== tokenFirstChar)); |
964 return stream.column() === selectionStart.ch ? "token-highlight colu
mn-with-selection" : "token-highlight"; | 907 } |
965 var eatenChar; | 908 |
966 do { | 909 /** |
967 eatenChar = stream.next(); | 910 * @param {function(!CodeMirror.StringStream)} highlighter |
968 } while (eatenChar && (WebInspector.TextUtils.isWordChar(eatenChar) || s
tream.peek() !== tokenFirstChar)); | 911 * @param {?CodeMirror.Pos} selectionStart |
969 }, | 912 */ |
970 | 913 _setHighlighter(highlighter, selectionStart) { |
971 /** | 914 var overlayMode = {token: highlighter}; |
972 * @param {function(!CodeMirror.StringStream)} highlighter | 915 this._codeMirror.addOverlay(overlayMode); |
973 * @param {?CodeMirror.Pos} selectionStart | 916 this._highlightDescriptor = {overlay: overlayMode, selectionStart: selection
Start}; |
974 */ | 917 } |
975 _setHighlighter: function(highlighter, selectionStart) | |
976 { | |
977 var overlayMode = { | |
978 token: highlighter | |
979 }; | |
980 this._codeMirror.addOverlay(overlayMode); | |
981 this._highlightDescriptor = { | |
982 overlay: overlayMode, | |
983 selectionStart: selectionStart | |
984 }; | |
985 } | |
986 }; | 918 }; |
987 | 919 |
988 WebInspector.SourcesTextEditor.LinesToScanForIndentationGuessing = 1000; | 920 WebInspector.SourcesTextEditor.LinesToScanForIndentationGuessing = 1000; |
989 WebInspector.SourcesTextEditor.MaximumNumberOfWhitespacesPerSingleSpan = 16; | 921 WebInspector.SourcesTextEditor.MaximumNumberOfWhitespacesPerSingleSpan = 16; |
OLD | NEW |