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

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

Issue 2238883004: DevTools: Split off SourcesTextEditor from CodeMirrorTextEditor (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 4 months 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
(Empty)
1 /*
dgozman 2016/08/15 23:16:07 New-style copyright.
einbinder 2016/08/16 19:00:58 Done.
2 * Copyright (C) 2016 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 /**
32 * @constructor
33 * @extends {WebInspector.CodeMirrorTextEditor}
34 * @param {!WebInspector.SourcesTextEditorDelegate} delegate
35 */
36 WebInspector.SourcesTextEditor = function(delegate)
37 {
38 WebInspector.CodeMirrorTextEditor.call(this);
39 this._codeMirror.addKeyMap({
40 "Enter": "smartNewlineAndIndent",
41 "Esc": "sourcesDismiss"
42 });
43 this._delegate = delegate;
44 this.codeMirror().on("changes", this._removeWidgetsOnChanges.bind(this));
45 this.codeMirror().on("changes", this._changesForDelegate.bind(this));
46 this.codeMirror().on("cursorActivity", this._cursorActivity.bind(this));
47 this.codeMirror().on("gutterClick", this._gutterClick.bind(this));
48 this.codeMirror().on("scroll", this._scroll.bind(this));
49 this.codeMirror().on("focus", this._focus.bind(this));
50 this.codeMirror().on("beforeSelectionChange", this._beforeSelectionChangeFor Delegate.bind(this));
51 this.element.addEventListener("contextmenu", this._contextMenu.bind(this), f alse);
52 this._blockIndentController = new WebInspector.SourcesTextEditor.BlockIndent Controller(this.codeMirror());
53 this._tokenHighlighter = new WebInspector.SourcesTextEditor.TokenHighlighter (this, this._codeMirror);
54 /** @type {!Array<string>} */
55 this._gutters = ["CodeMirror-linenumbers"];
56 this._elementToWidget = new Map();
57 /**
58 * @this {WebInspector.SourcesTextEditor}
59 */
60 function updateAnticipateJumpFlag(value)
61 {
62 this._isHandlingMouseDownEvent = value;
63 }
64 this.element.addEventListener("mousedown", updateAnticipateJumpFlag.bind(thi s, true), true);
dgozman 2016/08/15 23:16:07 style: empty lines please.
einbinder 2016/08/16 19:00:58 Done.
65 this.element.addEventListener("mousedown", updateAnticipateJumpFlag.bind(thi s, false), false);
66 WebInspector.moduleSetting("textEditorIndent").addChangeListener(this._onUpd ateEditorIndentation, this);
67 WebInspector.moduleSetting("textEditorAutoDetectIndent").addChangeListener(t his._onUpdateEditorIndentation, this);
68 this._onUpdateEditorIndentation();
69 }
70 WebInspector.SourcesTextEditor.prototype = {
71
72 /**
73 * @return {boolean}
74 */
75 _isSearchActive: function()
76 {
77 return !!this._tokenHighlighter.highlightedRegex();
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.scrollIntoView(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 if (!this._selectionBeforeSearch)
101 this._selectionBeforeSearch = this.selection();
102 this.codeMirror().operation(innerHighlightRegex.bind(this));
103 },
104
105 cancelSearchResultsHighlight: function()
106 {
107 this.codeMirror().operation(this._tokenHighlighter.highlightSelectedToke ns.bind(this._tokenHighlighter));
108 if (this._selectionBeforeSearch) {
109 this._reportJump(this._selectionBeforeSearch, this.selection());
110 delete this._selectionBeforeSearch;
111 }
112 },
113
114 /**
115 * @param {!Object} highlightDescriptor
116 */
117 removeHighlight: function(highlightDescriptor)
118 {
119 highlightDescriptor.clear();
120 },
121
122 /**
123 * @param {!WebInspector.TextRange} range
124 * @param {string} cssClass
125 * @return {!Object}
126 */
127 highlightRange: function(range, cssClass)
128 {
129 cssClass = "CodeMirror-persist-highlight " + cssClass;
130 var pos = WebInspector.CodeMirrorUtils.toPos(range);
131 ++pos.end.ch;
132 return this.codeMirror().markText(pos.start, pos.end, {
133 className: cssClass,
134 startStyle: cssClass + "-start",
135 endStyle: cssClass + "-end"
136 });
137 },
138
139 /**
140 * @param {number} lineNumber
141 * @param {boolean} disabled
142 * @param {boolean} conditional
143 */
144 addBreakpoint: function(lineNumber, disabled, conditional)
145 {
146 if (lineNumber < 0 || lineNumber >= this.codeMirror().lineCount())
147 return;
148 var className = "cm-breakpoint" + (conditional ? " cm-breakpoint-conditi onal" : "") + (disabled ? " cm-breakpoint-disabled" : "");
149 this.codeMirror().addLineClass(lineNumber, "wrap", className);
150 },
151
152 /**
153 * @param {number} lineNumber
154 */
155 removeBreakpoint: function(lineNumber)
156 {
157 if (lineNumber < 0 || lineNumber >= this.codeMirror().lineCount())
158 return;
159 var wrapClasses = this.codeMirror().getLineHandle(lineNumber).wrapClass;
160 if (!wrapClasses)
161 return;
162 var classes = wrapClasses.split(" ");
163 for (var i = 0; i < classes.length; ++i) {
164 if (classes[i].startsWith("cm-breakpoint"))
165 this.codeMirror().removeLineClass(lineNumber, "wrap", classes[i] );
166 }
167 },
168
169 /**
170 * @param {string} type
171 * @param {boolean} leftToNumbers
172 */
173 installGutter: function(type, leftToNumbers)
174 {
175 if (this._gutters.indexOf(type) !== -1)
176 return;
177 if (leftToNumbers)
178 this._gutters.unshift(type);
179 else
180 this._gutters.push(type);
181 this.codeMirror().setOption("gutters", this._gutters.slice());
182 this.codeMirror().refresh();
183 },
184
185 /**
186 * @param {string} type
187 */
188 uninstallGutter: function(type)
189 {
190 this._gutters = this._gutters.filter(gutter => gutter !== type);
191 this.codeMirror().setOption("gutters", this._gutters.slice());
192 this.codeMirror().refresh();
193 },
194
195 /**
196 * @param {number} lineNumber
197 * @param {string} type
198 * @param {?Element} element
199 */
200 setGutterDecoration: function(lineNumber, type, element)
201 {
202 console.assert(this._gutters.indexOf(type) !== -1, "Cannot decorate unex isting gutter.")
203 this.codeMirror().setGutterMarker(lineNumber, type, element);
204 },
205
206 /**
207 * @param {number} lineNumber
208 * @param {number} columnNumber
209 */
210 setExecutionLocation: function(lineNumber, columnNumber)
211 {
212 this.clearPositionHighlight();
213 this._executionLine = this.codeMirror().getLineHandle(lineNumber);
214 if (!this._executionLine)
215 return;
216 this.codeMirror().addLineClass(this._executionLine, "wrap", "cm-executio n-line");
217 this._executionLineTailMarker = this.codeMirror().markText({ line: lineN umber, ch: columnNumber }, { line: lineNumber, ch: this.codeMirror().getLine(lin eNumber).length }, { className: "cm-execution-line-tail" });
218 },
219
220 clearExecutionLine: function()
221 {
222 this.clearPositionHighlight();
223 if (this._executionLine)
224 this.codeMirror().removeLineClass(this._executionLine, "wrap", "cm-e xecution-line");
225 delete this._executionLine;
226 if (this._executionLineTailMarker)
227 this._executionLineTailMarker.clear();
228 delete this._executionLineTailMarker;
229 },
230
231 /**
232 * @param {number} lineNumber
233 * @param {string} className
234 * @param {boolean} toggled
235 */
236 toggleLineClass: function(lineNumber, className, toggled)
237 {
238 if (this.hasLineClass(lineNumber, className) === toggled)
239 return;
240 var lineHandle = this.codeMirror().getLineHandle(lineNumber);
241 if (!lineHandle)
242 return;
243 if (toggled) {
244 this.codeMirror().addLineClass(lineHandle, "gutter", className);
245 this.codeMirror().addLineClass(lineHandle, "wrap", className);
246 } else {
247 this.codeMirror().removeLineClass(lineHandle, "gutter", className);
248 this.codeMirror().removeLineClass(lineHandle, "wrap", className);
249 }
250 },
251
252 /**
253 * @param {number} lineNumber
254 * @param {string} className
255 * @return {boolean}
256 */
257 hasLineClass: function(lineNumber, className)
258 {
259 var lineInfo = this.codeMirror().lineInfo(lineNumber);
260 var wrapClass = lineInfo.wrapClass || "";
261 var classNames = wrapClass.split(" ");
262 return classNames.indexOf(className) !== -1;
263 },
264
265 /**
266 * @param {number} lineNumber
267 * @param {!Element} element
268 */
269 addDecoration: function(lineNumber, element)
270 {
271 var widget = this.codeMirror().addLineWidget(lineNumber, element);
272 this._elementToWidget.set(element, widget);
273 },
274
275 /**
276 * @param {number} lineNumber
277 * @param {!Element} element
278 */
279 removeDecoration: function(lineNumber, element)
280 {
281 var widget = this._elementToWidget.remove(element);
282 if (widget)
283 this.codeMirror().removeLineWidget(widget);
284 },
285
286 _removeWidgetsOnChanges: function(){
287 var widgets = this._elementToWidget.valuesArray();
288 for (var i = 0; i < widgets.length; ++i)
289 this.codeMirror().removeLineWidget(widgets[i]);
290 this._elementToWidget.clear();
291 },
292
293 _gutterClick: function(instance, lineNumber, gutter, event)
294 {
295 this.dispatchEventToListeners(WebInspector.SourcesTextEditor.Events.Gutt erClick, { lineNumber: lineNumber, event: event });
296 },
297
298 _contextMenu: function(event)
299 {
300 var contextMenu = new WebInspector.ContextMenu(event);
301 event.consume(true); // Consume event now to prevent document from handl ing the async menu
302 var target = event.target.enclosingNodeOrSelfWithClass("CodeMirror-gutte r-elt");
303 var promise;
304 if (target) {
305 promise = this._delegate.populateLineGutterContextMenu(contextMenu, parseInt(target.textContent, 10) - 1);
306 } else {
307 var textSelection = this.selection();
308 promise = this._delegate.populateTextAreaContextMenu(contextMenu, te xtSelection.startLine, textSelection.startColumn);
309 }
310 promise.then(showAsync.bind(this));
311 /**
312 * @this {WebInspector.SourcesTextEditor}
313 */
314 function showAsync()
315 {
316 contextMenu.appendApplicableItems(this);
317 contextMenu.show();
318 }
319 },
320
321 /**
322 * @param {!WebInspector.TextRange} range
323 * @param {string} text
324 * @param {string=} origin
325 * @return {!WebInspector.TextRange}
326 */
327 editRange: function(range, text, origin)
328 {
329 var pos = WebInspector.CodeMirrorUtils.toPos(range);
330 this.codeMirror().replaceRange(text, pos.start, pos.end, origin);
331 var newRange = WebInspector.CodeMirrorUtils.toRange(pos.start, this.code Mirror().posFromIndex(this.codeMirror().indexFromPos(pos.start) + text.length));
332 this._delegate.onTextChanged(range, newRange);
333 if (WebInspector.moduleSetting("textEditorAutoDetectIndent").get())
334 this._onUpdateEditorIndentation();
335 return newRange;
336 },
337
338 _onUpdateEditorIndentation: function()
339 {
340 this._setEditorIndentation(WebInspector.CodeMirrorUtils.pullLines(this._ codeMirror, WebInspector.SourcesTextEditor.LinesToScanForIndentationGuessing));
341 },
342
343 /**
344 * @param {!Array.<string>} lines
345 */
346 _setEditorIndentation: function(lines)
347 {
348 var extraKeys = {};
349 var indent = WebInspector.moduleSetting("textEditorIndent").get();
350 if (WebInspector.moduleSetting("textEditorAutoDetectIndent").get())
351 indent = WebInspector.SourcesTextEditor._guessIndentationLevel(lines );
352 if (indent === WebInspector.TextUtils.Indent.TabCharacter) {
353 this.codeMirror().setOption("indentWithTabs", true);
354 this.codeMirror().setOption("indentUnit", 4);
355 } else {
356 this.codeMirror().setOption("indentWithTabs", false);
357 this.codeMirror().setOption("indentUnit", indent.length);
358 extraKeys.Tab = function(codeMirror)
359 {
360 if (codeMirror.somethingSelected())
361 return CodeMirror.Pass;
362 var pos = codeMirror.getCursor("head");
363 codeMirror.replaceRange(indent.substring(pos.ch % indent.length) , codeMirror.getCursor());
364 }
365 }
366 this.codeMirror().setOption("extraKeys", extraKeys);
367 this._indentationLevel = indent;
368 },
369
370 /**
371 * @return {string}
372 */
373 _indent: function()
374 {
375 return this._indentationLevel;
376 },
377
378 _onAutoAppendedSpaces: function()
379 {
380 this._autoAppendedSpaces = this._autoAppendedSpaces || [];
381 for (var i = 0; i < this._autoAppendedSpaces.length; ++i) {
382 var position = this._autoAppendedSpaces[i].resolve();
383 if (!position)
384 continue;
385 var line = this.line(position.lineNumber);
386 if (line.length === position.columnNumber && WebInspector.TextUtils. lineIndent(line).length === line.length)
387 this._codeMirror.replaceRange("", new CodeMirror.Pos(position.li neNumber, 0), new CodeMirror.Pos(position.lineNumber, position.columnNumber));
388 }
389 this._autoAppendedSpaces = [];
390 var selections = this.selections();
391 for (var i = 0; i < selections.length; ++i) {
392 var selection = selections[i];
393 this._autoAppendedSpaces.push(this.textEditorPositionHandle(selectio n.startLine, selection.startColumn));
394 }
395 },
396
397 /**
398 * @param {!CodeMirror} codeMirror
399 * @param {!Array.<!CodeMirror.ChangeObject>} changes
400 */
401 _changesForDelegate: function(codeMirror, changes)
402 {
403 if (!changes.length || this._muteTextChangedEvent)
404 return;
405 var edits = [];
406 var currentEdit;
407 for (var changeIndex = 0; changeIndex < changes.length; ++changeIndex) {
408 var changeObject = changes[changeIndex];
409 var edit = WebInspector.CodeMirrorUtils.changeObjectToEditOperation( changeObject);
410 if (currentEdit && edit.oldRange.equal(currentEdit.newRange)) {
411 currentEdit.newRange = edit.newRange;
412 } else {
413 currentEdit = edit;
414 edits.push(currentEdit);
415 }
416 }
417 for (var i = 0; i < edits.length; ++i) {
418 var edit = edits[i];
419 this._delegate.onTextChanged(edit.oldRange, edit.newRange);
420 }
421 },
422
423 _cursorActivity: function()
424 {
425 if (!this._isSearchActive())
426 this._codeMirror.operation(this._tokenHighlighter.highlightSelectedT okens.bind(this._tokenHighlighter));
427 var start = this._codeMirror.getCursor("anchor");
428 var end = this._codeMirror.getCursor("head");
429 this._delegate.selectionChanged(WebInspector.CodeMirrorUtils.toRange(sta rt, end));
430 },
431
432 /**
433 * @param {?WebInspector.TextRange} from
434 * @param {?WebInspector.TextRange} to
435 */
436 _reportJump: function(from, to)
437 {
438 if (from && to && from.equal(to))
439 return;
440 this._delegate.onJumpToPosition(from, to);
441 },
442
443 _scroll: function()
444 {
445 var topmostLineNumber = this._codeMirror.lineAtHeight(this._codeMirror.g etScrollInfo().top, "local");
446 this._delegate.scrollChanged(topmostLineNumber);
447 },
448
449 _focus: function()
450 {
451 this._delegate.editorFocused();
452 },
453
454 /**
455 * @param {!CodeMirror} codeMirror
456 * @param {{ranges: !Array.<{head: !CodeMirror.Pos, anchor: !CodeMirror.Pos} >}} selection
457 */
458 _beforeSelectionChangeForDelegate: function(codeMirror, selection)
459 {
460 if (!this._isHandlingMouseDownEvent)
461 return;
462 if (!selection.ranges.length)
463 return;
464 var primarySelection = selection.ranges[0];
465 this._reportJump(this.selection(), WebInspector.CodeMirrorUtils.toRange( primarySelection.anchor, primarySelection.head));
466 },
467
468 /**
469 * @override
470 */
471 dispose: function()
472 {
473 WebInspector.CodeMirrorTextEditor.prototype.dispose.call(this);
474 WebInspector.moduleSetting("textEditorIndent").removeChangeListener(this ._onUpdateEditorIndentation, this);
475 WebInspector.moduleSetting("textEditorAutoDetectIndent").removeChangeLis tener(this._onUpdateEditorIndentation, this);
476 },
477
478 /**
479 * @override
480 * @param {string} text
481 */
482 setText: function(text)
483 {
484 this._muteTextChangedEvent = true;
485 this._setEditorIndentation(text.split("\n").slice(0, WebInspector.Source sTextEditor.LinesToScanForIndentationGuessing));
486 WebInspector.CodeMirrorTextEditor.prototype.setText.call(this,text);
487 delete this._muteTextChangedEvent;
488 },
489 __proto__: WebInspector.CodeMirrorTextEditor.prototype
490 }
491
492 /** @typedef {{lineNumber: number, event: !Event}} */
493 WebInspector.SourcesTextEditor.GutterClickEventData;
494
495 /** @enum {string} */
496 WebInspector.SourcesTextEditor.Events = {
497 GutterClick: "GutterClick"
498 }
499 /**
500 * @interface
501 */
502 WebInspector.SourcesTextEditorDelegate = function() { }
503 WebInspector.SourcesTextEditorDelegate.prototype = {
504
505 /**
506 * @param {!WebInspector.TextRange} oldRange
507 * @param {!WebInspector.TextRange} newRange
508 */
509 onTextChanged: function(oldRange, newRange) { },
510
511 /**
512 * @param {!WebInspector.TextRange} textRange
513 */
514 selectionChanged: function(textRange) { },
515
516 /**
517 * @param {number} lineNumber
518 */
519 scrollChanged: function(lineNumber) { },
520
521 editorFocused: function() { },
522
523 /**
524 * @param {!WebInspector.ContextMenu} contextMenu
525 * @param {number} lineNumber
526 * @return {!Promise}
527 */
528 populateLineGutterContextMenu: function(contextMenu, lineNumber) { },
529
530 /**
531 * @param {!WebInspector.ContextMenu} contextMenu
532 * @param {number} lineNumber
533 * @param {number} columnNumber
534 * @return {!Promise}
535 */
536 populateTextAreaContextMenu: function(contextMenu, lineNumber, columnNumber) { },
537
538 /**
539 * @param {?WebInspector.TextRange} from
540 * @param {?WebInspector.TextRange} to
541 */
542 onJumpToPosition: function(from, to) { }
543 }
544
545 /**
546 * @param {!CodeMirror} codeMirror
547 */
548 CodeMirror.commands.smartNewlineAndIndent = function(codeMirror)
549 {
550 codeMirror.operation(innerSmartNewlineAndIndent.bind(null, codeMirror));
551 function innerSmartNewlineAndIndent(codeMirror)
552 {
553 var selections = codeMirror.listSelections();
554 var replacements = [];
555 for (var i = 0; i < selections.length; ++i) {
556 var selection = selections[i];
557 var cur = CodeMirror.cmpPos(selection.head, selection.anchor) < 0 ? selection.head : selection.anchor;
558 var line = codeMirror.getLine(cur.line);
559 var indent = WebInspector.TextUtils.lineIndent(line);
560 replacements.push("\n" + indent.substring(0, Math.min(cur.ch, indent .length)));
561 }
562 codeMirror.replaceSelections(replacements);
563 codeMirror._codeMirrorTextEditor._onAutoAppendedSpaces();
564 }
565 }
566
567 /**
568 * @return {!Object|undefined}
569 */
570 CodeMirror.commands.sourcesDismiss = function(codemirror)
571 {
572 if (codemirror.listSelections().length === 1 && codemirror._codeMirrorTextEd itor._isSearchActive())
573 return CodeMirror.Pass;
574 return CodeMirror.commands.dismiss(codemirror);
575 }
576
577 /**
578 * @constructor
579 * @param {!CodeMirror} codeMirror
580 */
581 WebInspector.SourcesTextEditor.BlockIndentController = function(codeMirror)
582 {
583 codeMirror.addKeyMap(this);
584 }
585
586 WebInspector.SourcesTextEditor.BlockIndentController.prototype = {
587 name: "blockIndentKeymap",
588
589 /**
590 * @return {*}
591 */
592 Enter: function(codeMirror)
593 {
594 var selections = codeMirror.listSelections();
595 var replacements = [];
596 var allSelectionsAreCollapsedBlocks = false;
597 for (var i = 0; i < selections.length; ++i) {
598 var selection = selections[i];
599 var start = CodeMirror.cmpPos(selection.head, selection.anchor) < 0 ? selection.head : selection.anchor;
600 var line = codeMirror.getLine(start.line);
601 var indent = WebInspector.TextUtils.lineIndent(line);
602 var indentToInsert = "\n" + indent + codeMirror._codeMirrorTextEdito r._indent();
603 var isCollapsedBlock = false;
604 if (selection.head.ch === 0)
605 return CodeMirror.Pass;
606 if (line.substr(selection.head.ch - 1, 2) === "{}") {
607 indentToInsert += "\n" + indent;
608 isCollapsedBlock = true;
609 } else if (line.substr(selection.head.ch - 1, 1) !== "{") {
610 return CodeMirror.Pass;
611 }
612 if (i > 0 && allSelectionsAreCollapsedBlocks !== isCollapsedBlock)
613 return CodeMirror.Pass;
614 replacements.push(indentToInsert);
615 allSelectionsAreCollapsedBlocks = isCollapsedBlock;
616 }
617 codeMirror.replaceSelections(replacements);
618 if (!allSelectionsAreCollapsedBlocks) {
619 codeMirror._codeMirrorTextEditor._onAutoAppendedSpaces();
620 return;
621 }
622 selections = codeMirror.listSelections();
623 var updatedSelections = [];
624 for (var i = 0; i < selections.length; ++i) {
625 var selection = selections[i];
626 var line = codeMirror.getLine(selection.head.line - 1);
627 var position = new CodeMirror.Pos(selection.head.line - 1, line.leng th);
628 updatedSelections.push({
629 head: position,
630 anchor: position
631 });
632 }
633 codeMirror.setSelections(updatedSelections);
634 codeMirror._codeMirrorTextEditor._onAutoAppendedSpaces();
635 },
636
637 /**
638 * @return {*}
639 */
640 "'}'": function(codeMirror)
641 {
642 if (codeMirror.somethingSelected())
643 return CodeMirror.Pass;
644 var selections = codeMirror.listSelections();
645 var replacements = [];
646 for (var i = 0; i < selections.length; ++i) {
647 var selection = selections[i];
648 var line = codeMirror.getLine(selection.head.line);
649 if (line !== WebInspector.TextUtils.lineIndent(line))
650 return CodeMirror.Pass;
651 replacements.push("}");
652 }
653 codeMirror.replaceSelections(replacements);
654 selections = codeMirror.listSelections();
655 replacements = [];
656 var updatedSelections = [];
657 for (var i = 0; i < selections.length; ++i) {
658 var selection = selections[i];
659 var matchingBracket = codeMirror.findMatchingBracket(selection.head) ;
660 if (!matchingBracket || !matchingBracket.match)
661 return;
662 updatedSelections.push({
663 head: selection.head,
664 anchor: new CodeMirror.Pos(selection.head.line, 0)
665 });
666 var line = codeMirror.getLine(matchingBracket.to.line);
667 var indent = WebInspector.TextUtils.lineIndent(line);
668 replacements.push(indent + "}");
669 }
670 codeMirror.setSelections(updatedSelections);
671 codeMirror.replaceSelections(replacements);
672 }
673 }
674
675 /**
676 * @param {!Array.<string>} lines
677 * @return {string}
678 */
679 WebInspector.SourcesTextEditor._guessIndentationLevel = function(lines)
680 {
681 var tabRegex = /^\t+/;
682 var tabLines = 0;
683 var indents = {};
684 for (var lineNumber = 0; lineNumber < lines.length; ++lineNumber) {
685 var text = lines[lineNumber];
686 if (text.length === 0 || !WebInspector.TextUtils.isSpaceChar(text[0]))
687 continue;
688 if (tabRegex.test(text)) {
689 ++tabLines;
690 continue;
691 }
692 var i = 0;
693 while (i < text.length && WebInspector.TextUtils.isSpaceChar(text[i]))
694 ++i;
695 if (i % 2 !== 0)
696 continue;
697 indents[i] = 1 + (indents[i] || 0);
698 }
699 var linesCountPerIndentThreshold = 3 * lines.length / 100;
700 if (tabLines && tabLines > linesCountPerIndentThreshold)
701 return "\t";
702 var minimumIndent = Infinity;
703 for (var i in indents) {
704 if (indents[i] < linesCountPerIndentThreshold)
705 continue;
706 var indent = parseInt(i, 10);
707 if (minimumIndent > indent)
708 minimumIndent = indent;
709 }
710 if (minimumIndent === Infinity)
711 return WebInspector.moduleSetting("textEditorIndent").get();
712 return " ".repeat(minimumIndent);
713 }
714
715 /**
716 * @constructor
717 * @param {!WebInspector.SourcesTextEditor} textEditor
718 * @param {!CodeMirror} codeMirror
719 */
720 WebInspector.SourcesTextEditor.TokenHighlighter = function(textEditor, codeMirro r)
721 {
722 this._textEditor = textEditor;
723 this._codeMirror = codeMirror;
724 }
725
726 WebInspector.SourcesTextEditor.TokenHighlighter.prototype = {
727 /**
728 * @param {!RegExp} regex
729 * @param {?WebInspector.TextRange} range
730 */
731 highlightSearchResults: function(regex, range)
732 {
733 var oldRegex = this._highlightRegex;
734 this._highlightRegex = regex;
735 this._highlightRange = range;
736 if (this._searchResultMarker) {
737 this._searchResultMarker.clear();
738 delete this._searchResultMarker;
739 }
740 if (this._highlightDescriptor && this._highlightDescriptor.selectionStar t)
741 this._codeMirror.removeLineClass(this._highlightDescriptor.selection Start.line, "wrap", "cm-line-with-selection");
742 var selectionStart = this._highlightRange ? new CodeMirror.Pos(this._hig hlightRange.startLine, this._highlightRange.startColumn) : null;
743 if (selectionStart)
744 this._codeMirror.addLineClass(selectionStart.line, "wrap", "cm-line- with-selection");
745 if (this._highlightRegex === oldRegex) {
746 // Do not re-add overlay mode if regex did not change for better per formance.
747 if (this._highlightDescriptor)
748 this._highlightDescriptor.selectionStart = selectionStart;
749 } else {
750 this._removeHighlight();
751 this._setHighlighter(this._searchHighlighter.bind(this, this._highli ghtRegex), selectionStart);
752 }
753 if (this._highlightRange) {
754 var pos = WebInspector.CodeMirrorUtils.toPos(this._highlightRange);
755 this._searchResultMarker = this._codeMirror.markText(pos.start, pos. end, {className: "cm-column-with-selection"});
756 }
757 },
758
759 /**
760 * @return {!RegExp|undefined}
761 */
762 highlightedRegex: function()
763 {
764 return this._highlightRegex;
765 },
766
767 highlightSelectedTokens: function()
768 {
769 delete this._highlightRegex;
770 delete this._highlightRange;
771 if (this._highlightDescriptor && this._highlightDescriptor.selectionStar t)
772 this._codeMirror.removeLineClass(this._highlightDescriptor.selection Start.line, "wrap", "cm-line-with-selection");
773 this._removeHighlight();
774 var selectionStart = this._codeMirror.getCursor("start");
775 var selectionEnd = this._codeMirror.getCursor("end");
776 if (selectionStart.line !== selectionEnd.line)
777 return;
778 if (selectionStart.ch === selectionEnd.ch)
779 return;
780 var selections = this._codeMirror.getSelections();
781 if (selections.length > 1)
782 return;
783 var selectedText = selections[0];
784 if (this._isWord(selectedText, selectionStart.line, selectionStart.ch, s electionEnd.ch)) {
785 if (selectionStart)
786 this._codeMirror.addLineClass(selectionStart.line, "wrap", "cm-l ine-with-selection");
787 this._setHighlighter(this._tokenHighlighter.bind(this, selectedText, selectionStart), selectionStart);
788 }
789 },
790
791 /**
792 * @param {string} selectedText
793 * @param {number} lineNumber
794 * @param {number} startColumn
795 * @param {number} endColumn
796 */
797 _isWord: function(selectedText, lineNumber, startColumn, endColumn)
798 {
799 var line = this._codeMirror.getLine(lineNumber);
800 var leftBound = startColumn === 0 || !WebInspector.TextUtils.isWordChar( line.charAt(startColumn - 1));
801 var rightBound = endColumn === line.length || !WebInspector.TextUtils.is WordChar(line.charAt(endColumn));
802 return leftBound && rightBound && WebInspector.TextUtils.isWord(selected Text);
803 },
804
805 _removeHighlight: function()
806 {
807 if (this._highlightDescriptor) {
808 this._codeMirror.removeOverlay(this._highlightDescriptor.overlay);
809 delete this._highlightDescriptor;
810 }
811 },
812
813 /**
814 * @param {!RegExp} regex
815 * @param {!CodeMirror.StringStream} stream
816 */
817 _searchHighlighter: function(regex, stream)
818 {
819 if (stream.column() === 0)
820 delete this._searchMatchLength;
821 if (this._searchMatchLength) {
822 if (this._searchMatchLength > 2) {
823 for (var i = 0; i < this._searchMatchLength - 2; ++i)
824 stream.next();
825 this._searchMatchLength = 1;
826 return "search-highlight";
827 } else {
828 stream.next();
829 delete this._searchMatchLength;
830 return "search-highlight search-highlight-end";
831 }
832 }
833 var match = stream.match(regex, false);
834 if (match) {
835 stream.next();
836 var matchLength = match[0].length;
837 if (matchLength === 1)
838 return "search-highlight search-highlight-full";
839 this._searchMatchLength = matchLength;
840 return "search-highlight search-highlight-start";
841 }
842 while (!stream.match(regex, false) && stream.next()) {}
843 },
844
845 /**
846 * @param {string} token
847 * @param {!CodeMirror.Pos} selectionStart
848 * @param {!CodeMirror.StringStream} stream
849 */
850 _tokenHighlighter: function(token, selectionStart, stream)
851 {
852 var tokenFirstChar = token.charAt(0);
853 if (stream.match(token) && (stream.eol() || !WebInspector.TextUtils.isWo rdChar(stream.peek())))
854 return stream.column() === selectionStart.ch ? "token-highlight colu mn-with-selection" : "token-highlight";
855 var eatenChar;
856 do {
857 eatenChar = stream.next();
858 } while (eatenChar && (WebInspector.TextUtils.isWordChar(eatenChar) || s tream.peek() !== tokenFirstChar));
859 },
860
861 /**
862 * @param {function(!CodeMirror.StringStream)} highlighter
863 * @param {?CodeMirror.Pos} selectionStart
864 */
865 _setHighlighter: function(highlighter, selectionStart)
866 {
867 var overlayMode = {
868 token: highlighter
869 };
870 this._codeMirror.addOverlay(overlayMode);
871 this._highlightDescriptor = {
872 overlay: overlayMode,
873 selectionStart: selectionStart
874 };
875 }
876 }
877
878 WebInspector.SourcesTextEditor.LinesToScanForIndentationGuessing = 1000;
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698