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

Side by Side Diff: Source/devtools/front_end/TextEditorModel.js

Issue 22638008: DevTools: Use CodeMirror modes instead of highlight tokenizers (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: address comments Created 7 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 | Annotate | Revision Log
OLDNEW
(Empty)
1 /*
2 * Copyright (C) 2009 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 * @param {WebInspector.TextRange} newRange
34 * @param {string} originalText
35 * @param {WebInspector.TextRange} originalSelection
36 */
37 WebInspector.TextEditorCommand = function(newRange, originalText, originalSelect ion)
38 {
39 this.newRange = newRange;
40 this.originalText = originalText;
41 this.originalSelection = originalSelection;
42 }
43
44 /**
45 * @constructor
46 * @extends {WebInspector.Object}
47 */
48 WebInspector.TextEditorModel = function()
49 {
50 this._lines = [""];
51 this._attributes = [];
52 /** @type {Array.<WebInspector.TextEditorCommand>} */
53 this._undoStack = [];
54 this._noPunctuationRegex = /[^ !%&()*+,-.:;<=>?\[\]\^{|}~]+/;
55 this._lineBreak = "\n";
56 }
57
58 WebInspector.TextEditorModel.Events = {
59 TextChanged: "TextChanged"
60 }
61
62 WebInspector.TextEditorModel.endsWithBracketRegex = /[{(\[]\s*$/;
63
64 WebInspector.TextEditorModel.prototype = {
65 /**
66 * @return {boolean}
67 */
68 isClean: function()
69 {
70 return this._cleanState === this._undoStack.length;
71 },
72
73 markClean: function()
74 {
75 this._cleanState = this._undoStack.length;
76 },
77
78 /**
79 * @return {number}
80 */
81 get linesCount()
82 {
83 return this._lines.length;
84 },
85
86 /**
87 * @return {string}
88 */
89 text: function()
90 {
91 return this._lines.join(this._lineBreak);
92 },
93
94 /**
95 * @return {WebInspector.TextRange}
96 */
97 range: function()
98 {
99 return new WebInspector.TextRange(0, 0, this._lines.length - 1, this._li nes[this._lines.length - 1].length);
100 },
101
102 /**
103 * @return {string}
104 */
105 get lineBreak()
106 {
107 return this._lineBreak;
108 },
109
110 /**
111 * @param {number} lineNumber
112 * @return {string}
113 */
114 line: function(lineNumber)
115 {
116 if (lineNumber >= this._lines.length)
117 throw "Out of bounds:" + lineNumber;
118 return this._lines[lineNumber];
119 },
120
121 /**
122 * @param {number} lineNumber
123 * @return {number}
124 */
125 lineLength: function(lineNumber)
126 {
127 return this._lines[lineNumber].length;
128 },
129
130 /**
131 * @param {string} text
132 */
133 setText: function(text)
134 {
135 this._resetUndoStack();
136 text = text || "";
137 this._attributes = [];
138 var range = this.range();
139 this._lineBreak = /\r\n/.test(text) ? "\r\n" : "\n";
140 var newRange = this._innerSetText(range, text);
141 this.dispatchEventToListeners(WebInspector.TextEditorModel.Events.TextCh anged, { oldRange: range, newRange: newRange});
142 },
143
144 /**
145 * @param {WebInspector.TextRange} range
146 * @return {boolean}
147 */
148 _rangeHasOneCharacter: function(range)
149 {
150 if (range.startLine === range.endLine && range.endColumn - range.startCo lumn === 1)
151 return true;
152 if (range.endLine - range.startLine === 1 && range.endColumn === 0 && ra nge.startColumn === this.lineLength(range.startLine))
153 return true;
154 return false;
155 },
156
157 /**
158 * @param {WebInspector.TextRange} range
159 * @param {string} text
160 * @param {WebInspector.TextRange=} originalSelection
161 * @return {boolean}
162 */
163 _isEditRangeUndoBoundary: function(range, text, originalSelection)
164 {
165 if (originalSelection && !originalSelection.isEmpty())
166 return true;
167 if (text)
168 return text.length > 1 || !range.isEmpty();
169 return !this._rangeHasOneCharacter(range);
170 },
171
172 /**
173 * @param {WebInspector.TextRange} range
174 * @param {string} text
175 * @return {boolean}
176 */
177 _isEditRangeAdjacentToLastCommand: function(range, text)
178 {
179 if (!this._lastCommand)
180 return true;
181 if (!text) {
182 // FIXME: Distinguish backspace and delete in lastCommand.
183 return this._lastCommand.newRange.immediatelyPrecedes(range) || this ._lastCommand.newRange.immediatelyFollows(range);
184 }
185 return text.indexOf("\n") === -1 && this._lastCommand.newRange.immediate lyPrecedes(range);
186 },
187
188 /**
189 * @param {WebInspector.TextRange} range
190 * @param {string} text
191 * @param {WebInspector.TextRange=} originalSelection
192 * @return {WebInspector.TextRange}
193 */
194 editRange: function(range, text, originalSelection)
195 {
196 var undoBoundary = this._isEditRangeUndoBoundary(range, text, originalSe lection);
197 if (undoBoundary || !this._isEditRangeAdjacentToLastCommand(range, text) )
198 this._markUndoableState();
199 var newRange = this._innerEditRange(range, text, originalSelection);
200 if (undoBoundary)
201 this._markUndoableState();
202 return newRange;
203 },
204
205 /**
206 * @param {WebInspector.TextRange} range
207 * @param {string} text
208 * @param {WebInspector.TextRange=} originalSelection
209 * @return {WebInspector.TextRange}
210 */
211 _innerEditRange: function(range, text, originalSelection)
212 {
213 var originalText = this.copyRange(range);
214 var newRange = this._innerSetText(range, text);
215 this._lastCommand = this._pushUndoableCommand(newRange, originalText, or iginalSelection || range);
216 this.dispatchEventToListeners(WebInspector.TextEditorModel.Events.TextCh anged, { oldRange: range, newRange: newRange, editRange: true });
217 return newRange;
218 },
219
220 /**
221 * @param {WebInspector.TextRange} range
222 * @param {string} text
223 * @return {WebInspector.TextRange}
224 */
225 _innerSetText: function(range, text)
226 {
227 this._eraseRange(range);
228 if (text === "")
229 return new WebInspector.TextRange(range.startLine, range.startColumn , range.startLine, range.startColumn);
230
231 var newLines = text.split(/\r?\n/);
232
233 var prefix = this._lines[range.startLine].substring(0, range.startColumn );
234 var suffix = this._lines[range.startLine].substring(range.startColumn);
235
236 var postCaret = prefix.length;
237 // Insert text.
238 if (newLines.length === 1) {
239 this._setLine(range.startLine, prefix + newLines[0] + suffix);
240 postCaret += newLines[0].length;
241 } else {
242 this._setLine(range.startLine, prefix + newLines[0]);
243 this._insertLines(range, newLines);
244 this._setLine(range.startLine + newLines.length - 1, newLines[newLin es.length - 1] + suffix);
245 postCaret = newLines[newLines.length - 1].length;
246 }
247
248 return new WebInspector.TextRange(range.startLine, range.startColumn,
249 range.startLine + newLines.length - 1, postCaret);
250 },
251
252 /**
253 * @param {WebInspector.TextRange} range
254 * @param {Array.<string>} newLines
255 */
256 _insertLines: function(range, newLines)
257 {
258 var lines = new Array(this._lines.length + newLines.length - 1);
259 for (var i = 0; i <= range.startLine; ++i)
260 lines[i] = this._lines[i];
261 // Line at [0] is already set via setLine.
262 for (var i = 1; i < newLines.length; ++i)
263 lines[range.startLine + i] = newLines[i];
264 for (var i = range.startLine + newLines.length; i < lines.length; ++i)
265 lines[i] = this._lines[i - newLines.length + 1];
266 this._lines = lines;
267
268 // Adjust attributes, attributes move with the first character of line.
269 var attributes = new Array(lines.length);
270 var insertionIndex = range.startColumn ? range.startLine + 1 : range.sta rtLine;
271 for (var i = 0; i < insertionIndex; ++i)
272 attributes[i] = this._attributes[i];
273 for (var i = insertionIndex + newLines.length - 1; i < attributes.length ; ++i)
274 attributes[i] = this._attributes[i - newLines.length + 1];
275 this._attributes = attributes;
276 },
277
278 /**
279 * @param {WebInspector.TextRange} range
280 */
281 _eraseRange: function(range)
282 {
283 if (range.isEmpty())
284 return;
285
286 var prefix = this._lines[range.startLine].substring(0, range.startColumn );
287 var suffix = this._lines[range.endLine].substring(range.endColumn);
288
289 if (range.endLine > range.startLine) {
290 this._lines.splice(range.startLine + 1, range.endLine - range.startL ine);
291 // Adjust attributes, attributes move with the first character of li ne.
292 this._attributes.splice(range.startColumn ? range.startLine + 1 : ra nge.startLine, range.endLine - range.startLine);
293 }
294 this._setLine(range.startLine, prefix + suffix);
295 },
296
297 /**
298 * @param {number} lineNumber
299 * @param {string} text
300 */
301 _setLine: function(lineNumber, text)
302 {
303 this._lines[lineNumber] = text;
304 },
305
306 /**
307 * @param {number} lineNumber
308 * @param {number} column
309 * @return {WebInspector.TextRange}
310 */
311 wordRange: function(lineNumber, column)
312 {
313 return new WebInspector.TextRange(lineNumber, this.wordStart(lineNumber, column, true), lineNumber, this.wordEnd(lineNumber, column, true));
314 },
315
316 /**
317 * @param {number} lineNumber
318 * @param {number} column
319 * @param {boolean} gapless
320 * @return {number}
321 */
322 wordStart: function(lineNumber, column, gapless)
323 {
324 var line = this._lines[lineNumber];
325 var prefix = line.substring(0, column).split("").reverse().join("");
326 var prefixMatch = this._noPunctuationRegex.exec(prefix);
327 return prefixMatch && (!gapless || prefixMatch.index === 0) ? column - p refixMatch.index - prefixMatch[0].length : column;
328 },
329
330 /**
331 * @param {number} lineNumber
332 * @param {number} column
333 * @param {boolean} gapless
334 * @return {number}
335 */
336 wordEnd: function(lineNumber, column, gapless)
337 {
338 var line = this._lines[lineNumber];
339 var suffix = line.substring(column);
340 var suffixMatch = this._noPunctuationRegex.exec(suffix);
341 return suffixMatch && (!gapless || suffixMatch.index === 0) ? column + s uffixMatch.index + suffixMatch[0].length : column;
342 },
343
344 /**
345 * @param {WebInspector.TextRange} range
346 * @return {string}
347 */
348 copyRange: function(range)
349 {
350 if (!range)
351 range = this.range();
352
353 var clip = [];
354 if (range.startLine === range.endLine) {
355 clip.push(this._lines[range.startLine].substring(range.startColumn, range.endColumn));
356 return clip.join(this._lineBreak);
357 }
358 clip.push(this._lines[range.startLine].substring(range.startColumn));
359 for (var i = range.startLine + 1; i < range.endLine; ++i)
360 clip.push(this._lines[i]);
361 clip.push(this._lines[range.endLine].substring(0, range.endColumn));
362 return clip.join(this._lineBreak);
363 },
364
365 /**
366 * @param {number} line
367 * @param {string} name
368 * @param {Object?} value
369 */
370 setAttribute: function(line, name, value)
371 {
372 var attrs = this._attributes[line];
373 if (!attrs) {
374 attrs = {};
375 this._attributes[line] = attrs;
376 }
377 attrs[name] = value;
378 },
379
380 /**
381 * @param {number} line
382 * @param {string} name
383 * @return {Object|null} value
384 */
385 getAttribute: function(line, name)
386 {
387 var attrs = this._attributes[line];
388 return attrs ? attrs[name] : null;
389 },
390
391 /**
392 * @param {number} line
393 * @param {string} name
394 */
395 removeAttribute: function(line, name)
396 {
397 var attrs = this._attributes[line];
398 if (attrs)
399 delete attrs[name];
400 },
401
402 /**
403 * @param {WebInspector.TextRange} newRange
404 * @param {string} originalText
405 * @param {WebInspector.TextRange} originalSelection
406 * @return {WebInspector.TextEditorCommand}
407 */
408 _pushUndoableCommand: function(newRange, originalText, originalSelection)
409 {
410 var command = new WebInspector.TextEditorCommand(newRange.clone(), origi nalText, originalSelection);
411 if (this._inUndo)
412 this._redoStack.push(command);
413 else {
414 if (!this._inRedo) {
415 this._redoStack = [];
416 if (typeof this._cleanState === "number" && this._cleanState > t his._undoStack.length)
417 delete this._cleanState;
418 }
419 this._undoStack.push(command);
420 }
421 return command;
422 },
423
424 /**
425 * @return {?WebInspector.TextRange}
426 */
427 undo: function()
428 {
429 if (!this._undoStack.length)
430 return null;
431
432 this._markRedoableState();
433
434 this._inUndo = true;
435 var range = this._doUndo(this._undoStack);
436 delete this._inUndo;
437
438 return range;
439 },
440
441 /**
442 * @return {WebInspector.TextRange}
443 */
444 redo: function()
445 {
446 if (!this._redoStack || !this._redoStack.length)
447 return null;
448 this._markUndoableState();
449
450 this._inRedo = true;
451 var range = this._doUndo(this._redoStack);
452 delete this._inRedo;
453
454 return range ? range.collapseToEnd() : null;
455 },
456
457 /**
458 * @param {Array.<WebInspector.TextEditorCommand>} stack
459 * @return {WebInspector.TextRange}
460 */
461 _doUndo: function(stack)
462 {
463 var range = null;
464 for (var i = stack.length - 1; i >= 0; --i) {
465 var command = stack[i];
466 stack.length = i;
467 this._innerEditRange(command.newRange, command.originalText);
468 range = command.originalSelection;
469 if (i > 0 && stack[i - 1].explicit)
470 return range;
471 }
472 return range;
473 },
474
475 _markUndoableState: function()
476 {
477 if (this._undoStack.length)
478 this._undoStack[this._undoStack.length - 1].explicit = true;
479 },
480
481 _markRedoableState: function()
482 {
483 if (this._redoStack.length)
484 this._redoStack[this._redoStack.length - 1].explicit = true;
485 },
486
487 _resetUndoStack: function()
488 {
489 delete this._cleanState;
490 this._undoStack = [];
491 this._redoStack = [];
492 },
493
494 /**
495 * @param {WebInspector.TextRange} range
496 * @return {WebInspector.TextRange}
497 */
498 indentLines: function(range)
499 {
500 this._markUndoableState();
501
502 var indent = WebInspector.settings.textEditorIndent.get();
503 var newRange = range.clone();
504 // Do not change a selection start position when it is at the beginning of a line
505 if (range.startColumn)
506 newRange.startColumn += indent.length;
507
508 var indentEndLine = range.endLine;
509 if (range.endColumn)
510 newRange.endColumn += indent.length;
511 else
512 indentEndLine--;
513
514 for (var lineNumber = range.startLine; lineNumber <= indentEndLine; line Number++)
515 this._innerEditRange(WebInspector.TextRange.createFromLocation(lineN umber, 0), indent);
516
517 return newRange;
518 },
519
520 /**
521 * @param {WebInspector.TextRange} range
522 * @return {WebInspector.TextRange}
523 */
524 unindentLines: function(range)
525 {
526 this._markUndoableState();
527
528 var indent = WebInspector.settings.textEditorIndent.get();
529 var indentLength = indent === WebInspector.TextUtils.Indent.TabCharacter ? 4 : indent.length;
530 var lineIndentRegex = new RegExp("^ {1," + indentLength + "}");
531 var newRange = range.clone();
532
533 var indentEndLine = range.endLine;
534 if (!range.endColumn)
535 indentEndLine--;
536
537 for (var lineNumber = range.startLine; lineNumber <= indentEndLine; line Number++) {
538 var line = this.line(lineNumber);
539 var firstCharacter = line.charAt(0);
540 var lineIndentLength;
541
542 if (firstCharacter === " ")
543 lineIndentLength = line.match(lineIndentRegex)[0].length;
544 else if (firstCharacter === "\t")
545 lineIndentLength = 1;
546 else
547 continue;
548
549 this._innerEditRange(new WebInspector.TextRange(lineNumber, 0, lineN umber, lineIndentLength), "");
550
551 if (lineNumber === range.startLine)
552 newRange.startColumn = Math.max(0, newRange.startColumn - lineIn dentLength);
553 if (lineNumber === range.endLine)
554 newRange.endColumn = Math.max(0, newRange.endColumn - lineIndent Length);
555 }
556
557 return newRange;
558 },
559
560 /**
561 * @param {number=} from
562 * @param {number=} to
563 * @return {WebInspector.TextEditorModel}
564 */
565 slice: function(from, to)
566 {
567 var textModel = new WebInspector.TextEditorModel();
568 textModel._lines = this._lines.slice(from, to);
569 textModel._lineBreak = this._lineBreak;
570 return textModel;
571 },
572
573 /**
574 * @param {WebInspector.TextRange} range
575 * @return {WebInspector.TextRange}
576 */
577 growRangeLeft: function(range)
578 {
579 var result = range.clone();
580 if (result.startColumn)
581 --result.startColumn;
582 else if (result.startLine)
583 result.startColumn = this.lineLength(--result.startLine);
584 return result;
585 },
586
587 /**
588 * @param {WebInspector.TextRange} range
589 * @return {WebInspector.TextRange}
590 */
591 growRangeRight: function(range)
592 {
593 var result = range.clone();
594 if (result.endColumn < this.lineLength(result.endLine))
595 ++result.endColumn;
596 else if (result.endLine < this.linesCount) {
597 result.endColumn = 0;
598 ++result.endLine;
599 }
600 return result;
601 },
602
603 __proto__: WebInspector.Object.prototype
604 }
605
606 /**
607 * @constructor
608 * @param {WebInspector.TextEditorModel} textModel
609 */
610 WebInspector.TextEditorModel.BraceMatcher = function(textModel)
611 {
612 this._textModel = textModel;
613 }
614
615 WebInspector.TextEditorModel.BraceMatcher.prototype = {
616 /**
617 * @param {number} lineNumber
618 * @return {Array.<{startColumn: number, endColumn: number, token: string}>}
619 */
620 _braceRanges: function(lineNumber)
621 {
622 if (lineNumber >= this._textModel.linesCount || lineNumber < 0)
623 return null;
624
625 var attribute = this._textModel.getAttribute(lineNumber, "highlight");
626 if (!attribute)
627 return null;
628 else
629 return attribute.braces;
630 },
631
632 /**
633 * @param {string} braceTokenLeft
634 * @param {string} braceTokenRight
635 * @return {boolean}
636 */
637 _matches: function(braceTokenLeft, braceTokenRight)
638 {
639 return ((braceTokenLeft === "brace-start" && braceTokenRight === "brace- end") || (braceTokenLeft === "block-start" && braceTokenRight === "block-end"));
640 },
641
642 /**
643 * @param {number} lineNumber
644 * @param {number} column
645 * @param {number=} maxBraceIteration
646 * @return {?{lineNumber: number, column: number, token: string}}
647 */
648 findLeftCandidate: function(lineNumber, column, maxBraceIteration)
649 {
650 var braces = this._braceRanges(lineNumber);
651 if (!braces)
652 return null;
653
654 var braceIndex = braces.length - 1;
655 while (braceIndex >= 0 && braces[braceIndex].startColumn > column)
656 --braceIndex;
657
658 var brace = braceIndex >= 0 ? braces[braceIndex] : null;
659 if (brace && brace.startColumn === column && (brace.token === "block-end " || brace.token === "brace-end"))
660 --braceIndex;
661
662 var stack = [];
663 maxBraceIteration = maxBraceIteration || Number.MAX_VALUE;
664 while (--maxBraceIteration) {
665 if (braceIndex < 0) {
666 while ((braces = this._braceRanges(--lineNumber)) && !braces.len gth) {};
667 if (!braces)
668 return null;
669 braceIndex = braces.length - 1;
670 }
671 brace = braces[braceIndex];
672 if (brace.token === "block-end" || brace.token === "brace-end")
673 stack.push(brace.token);
674 else if (stack.length === 0)
675 return {
676 lineNumber: lineNumber,
677 column: brace.startColumn,
678 token: brace.token
679 };
680 else if (!this._matches(brace.token, stack.pop()))
681 return null;
682
683 --braceIndex;
684 }
685 return null;
686 },
687
688 /**
689 * @param {number} lineNumber
690 * @param {number} column
691 * @param {number=} maxBraceIteration
692 * @return {?{lineNumber: number, column: number, token: string}}
693 */
694 findRightCandidate: function(lineNumber, column, maxBraceIteration)
695 {
696 var braces = this._braceRanges(lineNumber);
697 if (!braces)
698 return null;
699
700 var braceIndex = 0;
701 while (braceIndex < braces.length && braces[braceIndex].startColumn < co lumn)
702 ++braceIndex;
703
704 var brace = braceIndex < braces.length ? braces[braceIndex] : null;
705 if (brace && brace.startColumn === column && (brace.token === "block-sta rt" || brace.token === "brace-start"))
706 ++braceIndex;
707
708 var stack = [];
709 maxBraceIteration = maxBraceIteration || Number.MAX_VALUE;
710 while (--maxBraceIteration) {
711 if (braceIndex >= braces.length) {
712 while ((braces = this._braceRanges(++lineNumber)) && !braces.len gth) {};
713 if (!braces)
714 return null;
715 braceIndex = 0;
716 }
717 brace = braces[braceIndex];
718 if (brace.token === "block-start" || brace.token === "brace-start")
719 stack.push(brace.token);
720 else if (stack.length === 0)
721 return {
722 lineNumber: lineNumber,
723 column: brace.startColumn,
724 token: brace.token
725 };
726 else if (!this._matches(stack.pop(), brace.token))
727 return null;
728 ++braceIndex;
729 }
730 return null;
731 },
732
733 /**
734 * @param {number} lineNumber
735 * @param {number} column
736 * @param {number=} maxBraceIteration
737 * @return {?{leftBrace: {lineNumber: number, column: number, token: string} , rightBrace: {lineNumber: number, column: number, token: string}}}
738 */
739 enclosingBraces: function(lineNumber, column, maxBraceIteration)
740 {
741 var leftBraceLocation = this.findLeftCandidate(lineNumber, column, maxBr aceIteration);
742 if (!leftBraceLocation)
743 return null;
744
745 var rightBraceLocation = this.findRightCandidate(lineNumber, column, max BraceIteration);
746 if (!rightBraceLocation)
747 return null;
748
749 if (!this._matches(leftBraceLocation.token, rightBraceLocation.token))
750 return null;
751
752 return {
753 leftBrace: leftBraceLocation,
754 rightBrace: rightBraceLocation
755 };
756 },
757 }
OLDNEW
« no previous file with comments | « Source/devtools/front_end/TextEditorHighlighter.js ('k') | Source/devtools/front_end/cm/cmdevtools.css » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698