Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(90)

Side by Side Diff: third_party/WebKit/Source/devtools/front_end/source_frame/SourcesTextEditor.js

Issue 2466123002: DevTools: reformat front-end code to match chromium style. (Closed)
Patch Set: all done Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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;
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698