2 * Copyright (C) 2011 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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
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.
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.
32 * @extends {WebInspector.VBox}
34 * @implements {WebInspector.Replaceable}
35 * @param {!WebInspector.ContentProvider} contentProvider
37 WebInspector
.SourceFrame = function(contentProvider
)
39 WebInspector
.VBox
.call(this);
41 this._url
= contentProvider
.contentURL();
42 this._contentProvider
= contentProvider
;
44 var textEditorDelegate
= new WebInspector
.TextEditorDelegateForSourceFrame(this);
46 this._textEditor
= new WebInspector
.CodeMirrorTextEditor(this._url
, textEditorDelegate
);
48 this._currentSearchResultIndex
= -1;
49 this._searchResults
= [];
51 this._rowMessageBuckets
= {};
53 this._textEditor
.setReadOnly(!this.canEditSource());
56 this.element
.addEventListener("keydown", this._handleKeyDown
.bind(this), false);
58 this._sourcePosition
= new WebInspector
.ToolbarText("", "source-frame-cursor-position");
60 this._errorPopoverHelper
= new WebInspector
.PopoverHelper(this.element
, this._getErrorAnchor
.bind(this), this._showErrorPopover
.bind(this));
61 this._errorPopoverHelper
.setTimeout(100, 100);
64 WebInspector
.SourceFrame
.Events
= {
65 ScrollChanged
: "ScrollChanged",
66 SelectionChanged
: "SelectionChanged",
67 JumpHappened
: "JumpHappened"
70 WebInspector
.SourceFrame
.prototype = {
72 * @param {!Element} target
73 * @param {!Event} event
74 * @return {(!Element|undefined)}
76 _getErrorAnchor: function(target
, event
)
78 var element
= target
.enclosingNodeOrSelfWithClass("text-editor-line-decoration-icon")
79 || target
.enclosingNodeOrSelfWithClass("text-editor-line-decoration-wave");
82 this._errorWavePopoverAnchor
= new AnchorBox(event
.clientX
, event
.clientY
, 1, 1);
87 * @param {!Element} anchor
88 * @param {!WebInspector.Popover} popover
90 _showErrorPopover: function(anchor
, popover
)
92 var messageBucket
= anchor
.enclosingNodeOrSelfWithClass("text-editor-line-decoration")._messageBucket
;
93 var messagesOutline
= messageBucket
.messagesDescription();
94 var popoverAnchor
= anchor
.enclosingNodeOrSelfWithClass("text-editor-line-decoration-icon") ? anchor
: this._errorWavePopoverAnchor
;
95 popover
.showForAnchor(messagesOutline
, popoverAnchor
);
100 * @param {function():boolean} handler
102 addShortcut: function(key
, handler
)
104 this._shortcuts
[key
] = handler
;
109 this._ensureContentLoaded();
110 this._textEditor
.show(this.element
);
111 this._editorAttached
= true;
112 // We need CodeMirrorTextEditor to be initialized prior to this call as it calls |cursorPositionToCoordinates| internally. @see crbug.com/506566
113 setImmediate(this._updateBucketDecorations
.bind(this));
114 this._wasShownOrLoaded();
117 _updateBucketDecorations: function()
119 for (var line
in this._rowMessageBuckets
) {
120 var bucket
= this._rowMessageBuckets
[line
];
121 bucket
._updateDecoration();
128 _isEditorShowing: function()
130 return this.isShowing() && this._editorAttached
;
135 WebInspector
.Widget
.prototype.willHide
.call(this);
137 this._clearPositionToReveal();
141 * @return {!WebInspector.ToolbarText}
143 toolbarText: function()
145 return this._sourcePosition
;
152 defaultFocusedElement: function()
154 return this._textEditor
.defaultFocusedElement();
164 return this._textEditor
;
167 _ensureContentLoaded: function()
169 if (!this._contentRequested
) {
170 this._contentRequested
= true;
171 this._contentProvider
.requestContent(this.setContent
.bind(this));
175 clearMessages: function()
177 for (var line
in this._rowMessageBuckets
) {
178 var bubble
= this._rowMessageBuckets
[line
];
179 bubble
.detachFromEditor();
182 this._rowMessageBuckets
= {};
183 this._errorPopoverHelper
.hidePopover();
187 * @param {number} line 0-based
188 * @param {number=} column
189 * @param {boolean=} shouldHighlight
191 revealPosition: function(line
, column
, shouldHighlight
)
193 this._clearLineToScrollTo();
194 this._clearSelectionToSet();
195 this._positionToReveal
= { line
: line
, column
: column
, shouldHighlight
: shouldHighlight
};
196 this._innerRevealPositionIfNeeded();
199 _innerRevealPositionIfNeeded: function()
201 if (!this._positionToReveal
)
204 if (!this.loaded
|| !this._isEditorShowing())
207 this._textEditor
.revealPosition(this._positionToReveal
.line
, this._positionToReveal
.column
, this._positionToReveal
.shouldHighlight
);
208 delete this._positionToReveal
;
211 _clearPositionToReveal: function()
213 this._textEditor
.clearPositionHighlight();
214 delete this._positionToReveal
;
218 * @param {number} line
220 scrollToLine: function(line
)
222 this._clearPositionToReveal();
223 this._lineToScrollTo
= line
;
224 this._innerScrollToLineIfNeeded();
227 _innerScrollToLineIfNeeded: function()
229 if (typeof this._lineToScrollTo
=== "number") {
230 if (this.loaded
&& this._isEditorShowing()) {
231 this._textEditor
.scrollToLine(this._lineToScrollTo
);
232 delete this._lineToScrollTo
;
237 _clearLineToScrollTo: function()
239 delete this._lineToScrollTo
;
243 * @return {!WebInspector.TextRange}
245 selection: function()
247 return this.textEditor
.selection();
251 * @param {!WebInspector.TextRange} textRange
253 setSelection: function(textRange
)
255 this._selectionToSet
= textRange
;
256 this._innerSetSelectionIfNeeded();
259 _innerSetSelectionIfNeeded: function()
261 if (this._selectionToSet
&& this.loaded
&& this._isEditorShowing()) {
262 this._textEditor
.setSelection(this._selectionToSet
);
263 delete this._selectionToSet
;
267 _clearSelectionToSet: function()
269 delete this._selectionToSet
;
272 _wasShownOrLoaded: function()
274 this._innerRevealPositionIfNeeded();
275 this._innerSetSelectionIfNeeded();
276 this._innerScrollToLineIfNeeded();
279 onTextChanged: function(oldRange
, newRange
)
281 if (this._searchResultsChangedCallback
)
282 this._searchResultsChangedCallback();
283 this.clearMessages();
287 * @param {string} content
288 * @param {string} mimeType
291 _simplifyMimeType: function(content
, mimeType
)
295 if (mimeType
.indexOf("javascript") >= 0 ||
296 mimeType
.indexOf("jscript") >= 0 ||
297 mimeType
.indexOf("ecmascript") >= 0)
298 return "text/javascript";
299 // A hack around the fact that files with "php" extension might be either standalone or html embedded php scripts.
300 if (mimeType
=== "text/x-php" && content
.match(/\<\?.*\?\>/g
))
301 return "application/x-httpd-php";
306 * @param {string} highlighterType
308 setHighlighterType: function(highlighterType
)
310 this._highlighterType
= highlighterType
;
311 this._updateHighlighterType("");
315 * @param {string} content
317 _updateHighlighterType: function(content
)
319 this._textEditor
.setMimeType(this._simplifyMimeType(content
, this._highlighterType
));
323 * @param {?string} content
325 setContent: function(content
)
329 this._textEditor
.setText(content
|| "");
330 this._textEditor
.markClean();
332 var firstLine
= this._textEditor
.firstVisibleLine();
333 var selection
= this._textEditor
.selection();
334 this._textEditor
.setText(content
|| "");
335 this._textEditor
.scrollToLine(firstLine
);
336 this._textEditor
.setSelection(selection
);
339 this._updateHighlighterType(content
|| "");
340 this.clearMessages();
341 this._wasShownOrLoaded();
343 if (this._delayedFindSearchMatches
) {
344 this._delayedFindSearchMatches();
345 delete this._delayedFindSearchMatches
;
347 this.onTextEditorContentLoaded();
350 onTextEditorContentLoaded: function() {},
353 * @param {!WebInspector.SearchableView.SearchConfig} searchConfig
354 * @param {boolean} shouldJump
355 * @param {boolean} jumpBackwards
356 * @param {function(!WebInspector.Widget, number)} searchFinishedCallback
358 _doFindSearchMatches: function(searchConfig
, shouldJump
, jumpBackwards
, searchFinishedCallback
)
360 this._currentSearchResultIndex
= -1;
361 this._searchResults
= [];
363 var regex
= searchConfig
.toSearchRegex();
364 this._searchRegex
= regex
;
365 this._searchResults
= this._collectRegexMatches(regex
);
366 searchFinishedCallback(this, this._searchResults
.length
);
367 if (!this._searchResults
.length
)
368 this._textEditor
.cancelSearchResultsHighlight();
369 else if (shouldJump
&& jumpBackwards
)
370 this.jumpToPreviousSearchResult();
372 this.jumpToNextSearchResult();
374 this._textEditor
.highlightSearchResults(regex
, null);
378 * @param {!WebInspector.SearchableView.SearchConfig} searchConfig
379 * @param {boolean} shouldJump
380 * @param {boolean} jumpBackwards
381 * @param {function(!WebInspector.Widget, number)} searchFinishedCallback
382 * @param {function(number)} currentMatchChangedCallback
383 * @param {function()} searchResultsChangedCallback
385 performSearch: function(searchConfig
, shouldJump
, jumpBackwards
, searchFinishedCallback
, currentMatchChangedCallback
, searchResultsChangedCallback
)
388 this._currentSearchMatchChangedCallback
= currentMatchChangedCallback
;
389 this._searchResultsChangedCallback
= searchResultsChangedCallback
;
390 var searchFunction
= this._doFindSearchMatches
.bind(this, searchConfig
, shouldJump
, jumpBackwards
, searchFinishedCallback
);
392 searchFunction
.call(this);
394 this._delayedFindSearchMatches
= searchFunction
;
396 this._ensureContentLoaded();
399 _editorFocused: function()
401 this._resetCurrentSearchResultIndex();
404 _resetCurrentSearchResultIndex: function()
406 if (!this._searchResults
.length
)
408 this._currentSearchResultIndex
= -1;
409 if (this._currentSearchMatchChangedCallback
)
410 this._currentSearchMatchChangedCallback(this._currentSearchResultIndex
);
411 this._textEditor
.highlightSearchResults(this._searchRegex
, null);
414 _resetSearch: function()
416 delete this._delayedFindSearchMatches
;
417 delete this._currentSearchMatchChangedCallback
;
418 delete this._searchResultsChangedCallback
;
419 this._currentSearchResultIndex
= -1;
420 this._searchResults
= [];
421 delete this._searchRegex
;
424 searchCanceled: function()
426 var range
= this._currentSearchResultIndex
!== -1 ? this._searchResults
[this._currentSearchResultIndex
] : null;
430 this._textEditor
.cancelSearchResultsHighlight();
432 this.setSelection(range
);
438 hasSearchResults: function()
440 return this._searchResults
.length
> 0;
443 jumpToFirstSearchResult: function()
445 this.jumpToSearchResult(0);
448 jumpToLastSearchResult: function()
450 this.jumpToSearchResult(this._searchResults
.length
- 1);
456 _searchResultIndexForCurrentSelection: function()
458 return insertionIndexForObjectInListSortedByFunction(this._textEditor
.selection().collapseToEnd(), this._searchResults
, WebInspector
.TextRange
.comparator
);
461 jumpToNextSearchResult: function()
463 var currentIndex
= this._searchResultIndexForCurrentSelection();
464 var nextIndex
= this._currentSearchResultIndex
=== -1 ? currentIndex
: currentIndex
+ 1;
465 this.jumpToSearchResult(nextIndex
);
468 jumpToPreviousSearchResult: function()
470 var currentIndex
= this._searchResultIndexForCurrentSelection();
471 this.jumpToSearchResult(currentIndex
- 1);
474 get currentSearchResultIndex()
476 return this._currentSearchResultIndex
;
479 jumpToSearchResult: function(index
)
481 if (!this.loaded
|| !this._searchResults
.length
)
483 this._currentSearchResultIndex
= (index
+ this._searchResults
.length
) % this._searchResults
.length
;
484 if (this._currentSearchMatchChangedCallback
)
485 this._currentSearchMatchChangedCallback(this._currentSearchResultIndex
);
486 this._textEditor
.highlightSearchResults(this._searchRegex
, this._searchResults
[this._currentSearchResultIndex
]);
491 * @param {!WebInspector.SearchableView.SearchConfig} searchConfig
492 * @param {string} replacement
494 replaceSelectionWith: function(searchConfig
, replacement
)
496 var range
= this._searchResults
[this._currentSearchResultIndex
];
499 this._textEditor
.highlightSearchResults(this._searchRegex
, null);
501 var oldText
= this._textEditor
.copyRange(range
);
502 var regex
= searchConfig
.toSearchRegex();
504 if (regex
.__fromRegExpQuery
)
505 text
= oldText
.replace(regex
, replacement
);
507 text
= oldText
.replace(regex
, function() { return replacement
; });
509 var newRange
= this._textEditor
.editRange(range
, text
);
510 this._textEditor
.setSelection(newRange
.collapseToEnd());
515 * @param {!WebInspector.SearchableView.SearchConfig} searchConfig
516 * @param {string} replacement
518 replaceAllWith: function(searchConfig
, replacement
)
520 this._resetCurrentSearchResultIndex();
522 var text
= this._textEditor
.text();
523 var range
= this._textEditor
.range();
525 var regex
= searchConfig
.toSearchRegex(true);
526 if (regex
.__fromRegExpQuery
)
527 text
= text
.replace(regex
, replacement
);
529 text
= text
.replace(regex
, function() { return replacement
; });
531 var ranges
= this._collectRegexMatches(regex
);
535 // Calculate the position of the end of the last range to be edited.
536 var currentRangeIndex
= insertionIndexForObjectInListSortedByFunction(this._textEditor
.selection(), ranges
, WebInspector
.TextRange
.comparator
);
537 var lastRangeIndex
= mod(currentRangeIndex
- 1, ranges
.length
);
538 var lastRange
= ranges
[lastRangeIndex
];
539 var replacementLineEndings
= replacement
.lineEndings();
540 var replacementLineCount
= replacementLineEndings
.length
;
541 var lastLineNumber
= lastRange
.startLine
+ replacementLineEndings
.length
- 1;
542 var lastColumnNumber
= lastRange
.startColumn
;
543 if (replacementLineEndings
.length
> 1)
544 lastColumnNumber
= replacementLineEndings
[replacementLineCount
- 1] - replacementLineEndings
[replacementLineCount
- 2] - 1;
546 this._textEditor
.editRange(range
, text
);
547 this._textEditor
.revealPosition(lastLineNumber
, lastColumnNumber
);
548 this._textEditor
.setSelection(WebInspector
.TextRange
.createFromLocation(lastLineNumber
, lastColumnNumber
));
551 _collectRegexMatches: function(regexObject
)
554 for (var i
= 0; i
< this._textEditor
.linesCount
; ++i
) {
555 var line
= this._textEditor
.line(i
);
558 var match
= regexObject
.exec(line
);
560 var matchEndIndex
= match
.index
+ Math
.max(match
[0].length
, 1);
562 ranges
.push(new WebInspector
.TextRange(i
, offset
+ match
.index
, i
, offset
+ matchEndIndex
));
563 offset
+= matchEndIndex
;
564 line
= line
.substring(matchEndIndex
);
566 } while (match
&& line
);
572 * @param {!WebInspector.SourceFrameMessage} message
574 addMessageToSource: function(message
)
576 var lineNumber
= message
.lineNumber();
577 if (lineNumber
>= this._textEditor
.linesCount
)
578 lineNumber
= this._textEditor
.linesCount
- 1;
582 if (!this._rowMessageBuckets
[lineNumber
])
583 this._rowMessageBuckets
[lineNumber
] = new WebInspector
.SourceFrame
.RowMessageBucket(this, this._textEditor
, lineNumber
);
584 var messageBucket
= this._rowMessageBuckets
[lineNumber
];
585 messageBucket
.addMessage(message
);
589 * @param {!WebInspector.SourceFrameMessage} message
591 removeMessageFromSource: function(message
)
593 var lineNumber
= message
.lineNumber();
594 if (lineNumber
>= this._textEditor
.linesCount
)
595 lineNumber
= this._textEditor
.linesCount
- 1;
599 var messageBucket
= this._rowMessageBuckets
[lineNumber
];
602 messageBucket
.removeMessage(message
);
603 if (!messageBucket
.uniqueMessagesCount()) {
604 messageBucket
.detachFromEditor();
605 delete this._rowMessageBuckets
[lineNumber
];
609 populateLineGutterContextMenu: function(contextMenu
, lineNumber
)
613 populateTextAreaContextMenu: function(contextMenu
, lineNumber
, columnNumber
)
618 * @param {?WebInspector.TextRange} from
619 * @param {?WebInspector.TextRange} to
621 onJumpToPosition: function(from, to
)
623 this.dispatchEventToListeners(WebInspector
.SourceFrame
.Events
.JumpHappened
, {
632 canEditSource: function()
638 * @param {!WebInspector.TextRange} textRange
640 selectionChanged: function(textRange
)
642 this._updateSourcePosition();
643 this.dispatchEventToListeners(WebInspector
.SourceFrame
.Events
.SelectionChanged
, textRange
);
644 WebInspector
.notifications
.dispatchEventToListeners(WebInspector
.SourceFrame
.Events
.SelectionChanged
, textRange
);
647 _updateSourcePosition: function()
649 var selections
= this._textEditor
.selections();
650 if (!selections
.length
)
652 if (selections
.length
> 1) {
653 this._sourcePosition
.setText(WebInspector
.UIString("%d selection regions", selections
.length
));
656 var textRange
= selections
[0];
657 if (textRange
.isEmpty()) {
658 this._sourcePosition
.setText(WebInspector
.UIString("Line %d, Column %d", textRange
.endLine
+ 1, textRange
.endColumn
+ 1));
661 textRange
= textRange
.normalize();
663 var selectedText
= this._textEditor
.copyRange(textRange
);
664 if (textRange
.startLine
=== textRange
.endLine
)
665 this._sourcePosition
.setText(WebInspector
.UIString("%d characters selected", selectedText
.length
));
667 this._sourcePosition
.setText(WebInspector
.UIString("%d lines, %d characters selected", textRange
.endLine
- textRange
.startLine
+ 1, selectedText
.length
));
671 * @param {number} lineNumber
673 scrollChanged: function(lineNumber
)
675 this.dispatchEventToListeners(WebInspector
.SourceFrame
.Events
.ScrollChanged
, lineNumber
);
678 _handleKeyDown: function(e
)
680 var shortcutKey
= WebInspector
.KeyboardShortcut
.makeKeyFromEvent(e
);
681 var handler
= this._shortcuts
[shortcutKey
];
682 if (handler
&& handler())
686 __proto__
: WebInspector
.VBox
.prototype
691 * @param {string} messageText
692 * @param {!WebInspector.SourceFrameMessage.Level} level
693 * @param {number} lineNumber
694 * @param {number=} columnNumber
696 WebInspector
.SourceFrameMessage = function(messageText
, level
, lineNumber
, columnNumber
)
698 this._messageText
= messageText
;
700 this._lineNumber
= lineNumber
;
701 this._columnNumber
= columnNumber
;
707 WebInspector
.SourceFrameMessage
.Level
= {
713 * @param {!WebInspector.ConsoleMessage} consoleMessage
714 * @param {number} lineNumber
715 * @param {number} columnNumber
716 * @return {!WebInspector.SourceFrameMessage}
718 WebInspector
.SourceFrameMessage
.fromConsoleMessage = function(consoleMessage
, lineNumber
, columnNumber
)
720 console
.assert(consoleMessage
.level
=== WebInspector
.ConsoleMessage
.MessageLevel
.Error
|| consoleMessage
.level
=== WebInspector
.ConsoleMessage
.MessageLevel
.Warning
);
721 var level
= consoleMessage
.level
=== WebInspector
.ConsoleMessage
.MessageLevel
.Error
? WebInspector
.SourceFrameMessage
.Level
.Error
: WebInspector
.SourceFrameMessage
.Level
.Warning
;
722 return new WebInspector
.SourceFrameMessage(consoleMessage
.messageText
, level
, lineNumber
, columnNumber
);
725 WebInspector
.SourceFrameMessage
.prototype = {
729 messageText: function()
731 return this._messageText
;
735 * @return {!WebInspector.SourceFrameMessage.Level}
745 lineNumber: function()
747 return this._lineNumber
;
751 * @return {(number|undefined)}
753 columnNumber: function()
755 return this._columnNumber
;
759 * @param {!WebInspector.SourceFrameMessage} another
762 isEqual: function(another
)
764 return this.messageText() === another
.messageText() && this.level() === another
.level() && this.lineNumber() === another
.lineNumber() && this.columnNumber() === another
.columnNumber();
768 WebInspector
.SourceFrame
._iconClassPerLevel
= {};
769 WebInspector
.SourceFrame
._iconClassPerLevel
[WebInspector
.SourceFrameMessage
.Level
.Error
] = "error-icon";
770 WebInspector
.SourceFrame
._iconClassPerLevel
[WebInspector
.SourceFrameMessage
.Level
.Warning
] = "warning-icon";
772 WebInspector
.SourceFrame
._lineClassPerLevel
= {};
773 WebInspector
.SourceFrame
._lineClassPerLevel
[WebInspector
.SourceFrameMessage
.Level
.Error
] = "text-editor-line-with-error";
774 WebInspector
.SourceFrame
._lineClassPerLevel
[WebInspector
.SourceFrameMessage
.Level
.Warning
] = "text-editor-line-with-warning";
778 * @param {!WebInspector.SourceFrameMessage} message
780 WebInspector
.SourceFrame
.RowMessage = function(message
)
782 this._message
= message
;
783 this._repeatCount
= 1;
784 this.element
= createElementWithClass("div", "text-editor-row-message");
785 this._icon
= this.element
.createChild("label", "", "dt-icon-label");
786 this._icon
.type
= WebInspector
.SourceFrame
._iconClassPerLevel
[message
.level()];
787 this._repeatCountElement
= this.element
.createChild("span", "bubble-repeat-count hidden error");
788 var linesContainer
= this.element
.createChild("div", "text-editor-row-message-lines");
789 var lines
= this._message
.messageText().split("\n");
790 for (var i
= 0; i
< lines
.length
; ++i
) {
791 var messageLine
= linesContainer
.createChild("div");
792 messageLine
.textContent
= lines
[i
];
796 WebInspector
.SourceFrame
.RowMessage
.prototype = {
798 * @return {!WebInspector.SourceFrameMessage}
802 return this._message
;
808 repeatCount: function()
810 return this._repeatCount
;
813 setRepeatCount: function(repeatCount
)
815 if (this._repeatCount
=== repeatCount
)
817 this._repeatCount
= repeatCount
;
818 this._updateMessageRepeatCount();
821 _updateMessageRepeatCount: function()
823 this._repeatCountElement
.textContent
= this._repeatCount
;
824 var showRepeatCount
= this._repeatCount
> 1;
825 this._repeatCountElement
.classList
.toggle("hidden", !showRepeatCount
);
826 this._icon
.classList
.toggle("hidden", showRepeatCount
);
832 * @param {!WebInspector.SourceFrame} sourceFrame
833 * @param {!WebInspector.CodeMirrorTextEditor} textEditor
834 * @param {number} lineNumber
836 WebInspector
.SourceFrame
.RowMessageBucket = function(sourceFrame
, textEditor
, lineNumber
)
838 this._sourceFrame
= sourceFrame
;
839 this._textEditor
= textEditor
;
840 this._lineHandle
= textEditor
.textEditorPositionHandle(lineNumber
, 0);
841 this._decoration
= createElementWithClass("div", "text-editor-line-decoration");
842 this._decoration
._messageBucket
= this;
843 this._wave
= this._decoration
.createChild("div", "text-editor-line-decoration-wave");
844 this._icon
= this._wave
.createChild("label", "text-editor-line-decoration-icon", "dt-icon-label");
846 this._textEditor
.addDecoration(lineNumber
, this._decoration
);
848 this._messagesDescriptionElement
= createElementWithClass("div", "text-editor-messages-description-container");
849 /** @type {!Array.<!WebInspector.SourceFrame.RowMessage>} */
855 WebInspector
.SourceFrame
.RowMessageBucket
.prototype = {
857 * @param {number} lineNumber
858 * @param {number} columnNumber
860 _updateWavePosition: function(lineNumber
, columnNumber
)
862 lineNumber
= Math
.min(lineNumber
, this._textEditor
.linesCount
- 1);
863 var lineText
= this._textEditor
.line(lineNumber
);
864 columnNumber
= Math
.min(columnNumber
, lineText
.length
);
865 var lineIndent
= WebInspector
.TextUtils
.lineIndent(lineText
).length
;
866 var base
= this._textEditor
.cursorPositionToCoordinates(lineNumber
, 0);
868 var start
= this._textEditor
.cursorPositionToCoordinates(lineNumber
, Math
.max(columnNumber
- 1, lineIndent
));
869 var end
= this._textEditor
.cursorPositionToCoordinates(lineNumber
, lineText
.length
);
871 var codeMirrorLinesLeftPadding
= 4;
872 this._wave
.style
.left
= (start
.x
- base
.x
+ codeMirrorLinesLeftPadding
) + "px";
873 this._wave
.style
.width
= (end
.x
- start
.x
) + "px";
879 messagesDescription: function()
881 this._messagesDescriptionElement
.removeChildren();
882 for (var i
= 0; i
< this._messages
.length
; ++i
) {
883 this._messagesDescriptionElement
.appendChild(this._messages
[i
].element
);
885 return this._messagesDescriptionElement
;
888 detachFromEditor: function()
890 var position
= this._lineHandle
.resolve();
893 var lineNumber
= position
.lineNumber
;
895 this._textEditor
.toggleLineClass(lineNumber
, WebInspector
.SourceFrame
._lineClassPerLevel
[this._level
], false);
896 this._textEditor
.removeDecoration(lineNumber
, this._decoration
);
902 uniqueMessagesCount: function()
904 return this._messages
.length
;
908 * @param {!WebInspector.SourceFrameMessage} message
910 addMessage: function(message
)
912 for (var i
= 0; i
< this._messages
.length
; ++i
) {
913 var rowMessage
= this._messages
[i
];
914 if (rowMessage
.message().isEqual(message
)) {
915 rowMessage
.setRepeatCount(rowMessage
.repeatCount() + 1);
920 var rowMessage
= new WebInspector
.SourceFrame
.RowMessage(message
);
921 this._messages
.push(rowMessage
);
922 this._updateDecoration();
926 * @param {!WebInspector.SourceFrameMessage} message
928 removeMessage: function(message
)
930 for (var i
= 0; i
< this._messages
.length
; ++i
) {
931 var rowMessage
= this._messages
[i
];
932 if (!rowMessage
.message().isEqual(message
))
934 rowMessage
.setRepeatCount(rowMessage
.repeatCount() - 1);
935 if (!rowMessage
.repeatCount())
936 this._messages
.splice(i
, 1);
937 this._updateDecoration();
942 _updateDecoration: function()
944 if (!this._sourceFrame
._isEditorShowing())
946 if (!this._messages
.length
)
948 var position
= this._lineHandle
.resolve();
952 var lineNumber
= position
.lineNumber
;
953 var columnNumber
= Number
.MAX_VALUE
;
954 var maxMessage
= null;
955 for (var i
= 0; i
< this._messages
.length
; ++i
) {
956 var message
= this._messages
[i
].message();
957 columnNumber
= Math
.min(columnNumber
, message
.columnNumber());
958 if (!maxMessage
|| WebInspector
.SourceFrameMessage
.messageLevelComparator(maxMessage
, message
) < 0)
959 maxMessage
= message
;
961 this._updateWavePosition(lineNumber
, columnNumber
);
964 this._textEditor
.toggleLineClass(lineNumber
, WebInspector
.SourceFrame
._lineClassPerLevel
[this._level
], false);
965 this._icon
.type
= "";
967 this._level
= maxMessage
.level();
970 this._textEditor
.toggleLineClass(lineNumber
, WebInspector
.SourceFrame
._lineClassPerLevel
[this._level
], true);
971 this._icon
.type
= WebInspector
.SourceFrame
._iconClassPerLevel
[this._level
];
976 * @implements {WebInspector.TextEditorDelegate}
979 WebInspector
.TextEditorDelegateForSourceFrame = function(sourceFrame
)
981 this._sourceFrame
= sourceFrame
;
984 WebInspector
.TextEditorDelegateForSourceFrame
.prototype = {
988 onTextChanged: function(oldRange
, newRange
)
990 this._sourceFrame
.onTextChanged(oldRange
, newRange
);
995 * @param {!WebInspector.TextRange} textRange
997 selectionChanged: function(textRange
)
999 this._sourceFrame
.selectionChanged(textRange
);
1004 * @param {number} lineNumber
1006 scrollChanged: function(lineNumber
)
1008 this._sourceFrame
.scrollChanged(lineNumber
);
1014 editorFocused: function()
1016 this._sourceFrame
._editorFocused();
1022 populateLineGutterContextMenu: function(contextMenu
, lineNumber
)
1024 this._sourceFrame
.populateLineGutterContextMenu(contextMenu
, lineNumber
);
1030 populateTextAreaContextMenu: function(contextMenu
, lineNumber
, columnNumber
)
1032 this._sourceFrame
.populateTextAreaContextMenu(contextMenu
, lineNumber
, columnNumber
);
1037 * @param {?WebInspector.TextRange} from
1038 * @param {?WebInspector.TextRange} to
1040 onJumpToPosition: function(from, to
)
1042 this._sourceFrame
.onJumpToPosition(from, to
);
1046 WebInspector
.SourceFrameMessage
._messageLevelPriority
= {
1052 * @param {!WebInspector.SourceFrameMessage} a
1053 * @param {!WebInspector.SourceFrameMessage} b
1056 WebInspector
.SourceFrameMessage
.messageLevelComparator = function(a
, b
)
1058 return WebInspector
.SourceFrameMessage
._messageLevelPriority
[a
.level()] - WebInspector
.SourceFrameMessage
._messageLevelPriority
[b
.level()];