Index: third_party/WebKit/Source/devtools/front_end/sources/UISourceCodeFrame.js |
diff --git a/third_party/WebKit/Source/devtools/front_end/sources/UISourceCodeFrame.js b/third_party/WebKit/Source/devtools/front_end/sources/UISourceCodeFrame.js |
index 60617f396623f94838a257c25e50cad66bf195b3..af503388e301ea172e26a82dde6800621a489d4b 100644 |
--- a/third_party/WebKit/Source/devtools/front_end/sources/UISourceCodeFrame.js |
+++ b/third_party/WebKit/Source/devtools/front_end/sources/UISourceCodeFrame.js |
@@ -36,10 +36,13 @@ WebInspector.UISourceCodeFrame = function(uiSourceCode) |
this._uiSourceCode = uiSourceCode; |
WebInspector.SourceFrame.call(this, this._uiSourceCode); |
this.textEditor.setAutocompleteDelegate(new WebInspector.SimpleAutocompleteDelegate()); |
- |
+ this._rowMessageBuckets = {}; |
this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyChanged, this._onWorkingCopyChanged, this); |
this._uiSourceCode.addEventListener(WebInspector.UISourceCode.Events.WorkingCopyCommitted, this._onWorkingCopyCommitted, this); |
this._updateStyle(); |
+ |
+ this._errorPopoverHelper = new WebInspector.PopoverHelper(this.element, this._getErrorAnchor.bind(this), this._showErrorPopover.bind(this)); |
+ this._errorPopoverHelper.setTimeout(100, 100); |
} |
WebInspector.UISourceCodeFrame.prototype = { |
@@ -57,6 +60,8 @@ WebInspector.UISourceCodeFrame.prototype = { |
this._boundWindowFocused = this._windowFocused.bind(this); |
this.element.ownerDocument.defaultView.addEventListener("focus", this._boundWindowFocused, false); |
this._checkContentUpdated(); |
+ // We need CodeMirrorTextEditor to be initialized prior to this call as it calls |cursorPositionToCoordinates| internally. @see crbug.com/506566 |
+ setImmediate(this._updateBucketDecorations.bind(this)); |
}, |
willHide: function() |
@@ -106,6 +111,7 @@ WebInspector.UISourceCodeFrame.prototype = { |
onTextChanged: function(oldRange, newRange) |
{ |
WebInspector.SourceFrame.prototype.onTextChanged.call(this, oldRange, newRange); |
+ this.clearMessages(); |
if (this._isSettingContent) |
return; |
this._muteSourceCodeEvents = true; |
@@ -116,6 +122,12 @@ WebInspector.UISourceCodeFrame.prototype = { |
delete this._muteSourceCodeEvents; |
}, |
+ onTextEditorContentLoaded: function() |
+ { |
+ WebInspector.SourceFrame.prototype.onTextEditorContentLoaded.call(this); |
+ this.clearMessages(); |
+ }, |
+ |
/** |
* @param {!WebInspector.Event} event |
*/ |
@@ -200,6 +212,90 @@ WebInspector.UISourceCodeFrame.prototype = { |
this.detach(); |
}, |
+ /** |
+ * @param {!WebInspector.UISourceCode.Message} message |
+ */ |
+ addMessageToSource: function(message) |
+ { |
+ var lineNumber = message.lineNumber(); |
+ if (lineNumber >= this._textEditor.linesCount) |
+ lineNumber = this._textEditor.linesCount - 1; |
+ if (lineNumber < 0) |
+ lineNumber = 0; |
+ |
+ if (!this._rowMessageBuckets[lineNumber]) |
+ this._rowMessageBuckets[lineNumber] = new WebInspector.UISourceCodeFrame.RowMessageBucket(this, this._textEditor, lineNumber); |
+ var messageBucket = this._rowMessageBuckets[lineNumber]; |
+ messageBucket.addMessage(message); |
+ }, |
+ |
+ /** |
+ * @param {!WebInspector.UISourceCode.Message} message |
+ */ |
+ removeMessageFromSource: function(message) |
+ { |
+ var lineNumber = message.lineNumber(); |
+ if (lineNumber >= this._textEditor.linesCount) |
+ lineNumber = this._textEditor.linesCount - 1; |
+ if (lineNumber < 0) |
+ lineNumber = 0; |
+ |
+ var messageBucket = this._rowMessageBuckets[lineNumber]; |
+ if (!messageBucket) |
+ return; |
+ messageBucket.removeMessage(message); |
+ if (!messageBucket.uniqueMessagesCount()) { |
+ messageBucket.detachFromEditor(); |
+ delete this._rowMessageBuckets[lineNumber]; |
+ } |
+ }, |
+ |
+ clearMessages: function() |
+ { |
+ for (var line in this._rowMessageBuckets) { |
+ var bubble = this._rowMessageBuckets[line]; |
+ bubble.detachFromEditor(); |
+ } |
+ |
+ this._rowMessageBuckets = {}; |
+ this._errorPopoverHelper.hidePopover(); |
+ }, |
+ |
+ /** |
+ * @param {!Element} target |
+ * @param {!Event} event |
+ * @return {(!Element|undefined)} |
+ */ |
+ _getErrorAnchor: function(target, event) |
+ { |
+ var element = target.enclosingNodeOrSelfWithClass("text-editor-line-decoration-icon") |
+ || target.enclosingNodeOrSelfWithClass("text-editor-line-decoration-wave"); |
+ if (!element) |
+ return; |
+ this._errorWavePopoverAnchor = new AnchorBox(event.clientX, event.clientY, 1, 1); |
+ return element; |
+ }, |
+ |
+ /** |
+ * @param {!Element} anchor |
+ * @param {!WebInspector.Popover} popover |
+ */ |
+ _showErrorPopover: function(anchor, popover) |
+ { |
+ var messageBucket = anchor.enclosingNodeOrSelfWithClass("text-editor-line-decoration")._messageBucket; |
+ var messagesOutline = messageBucket.messagesDescription(); |
+ var popoverAnchor = anchor.enclosingNodeOrSelfWithClass("text-editor-line-decoration-icon") ? anchor : this._errorWavePopoverAnchor; |
+ popover.showForAnchor(messagesOutline, popoverAnchor); |
+ }, |
+ |
+ _updateBucketDecorations: function() |
+ { |
+ for (var line in this._rowMessageBuckets) { |
+ var bucket = this._rowMessageBuckets[line]; |
+ bucket._updateDecoration(); |
+ } |
+ }, |
+ |
__proto__: WebInspector.SourceFrame.prototype |
} |
@@ -276,3 +372,238 @@ WebInspector.UISourceCodeFrame.Infobar.prototype = { |
__proto__: WebInspector.Infobar.prototype |
} |
+ |
+/** |
+ * @param {!WebInspector.ConsoleMessage} consoleMessage |
+ * @param {number} lineNumber |
+ * @param {number} columnNumber |
+ * @return {!WebInspector.UISourceCode.Message} |
+ */ |
+WebInspector.UISourceCodeFrame.uiMessageFromConsoleMessage = function(consoleMessage, lineNumber, columnNumber) |
+{ |
+ console.assert(consoleMessage.level === WebInspector.ConsoleMessage.MessageLevel.Error || consoleMessage.level === WebInspector.ConsoleMessage.MessageLevel.Warning); |
+ var level = consoleMessage.level === WebInspector.ConsoleMessage.MessageLevel.Error ? WebInspector.UISourceCode.Message.Level.Error : WebInspector.UISourceCode.Message.Level.Warning; |
+ return new WebInspector.UISourceCode.Message(level, consoleMessage.messageText, lineNumber, columnNumber); |
+} |
+ |
+WebInspector.UISourceCodeFrame._iconClassPerLevel = {}; |
+WebInspector.UISourceCodeFrame._iconClassPerLevel[WebInspector.UISourceCode.Message.Level.Error] = "error-icon"; |
+WebInspector.UISourceCodeFrame._iconClassPerLevel[WebInspector.UISourceCode.Message.Level.Warning] = "warning-icon"; |
+ |
+WebInspector.UISourceCodeFrame._lineClassPerLevel = {}; |
+WebInspector.UISourceCodeFrame._lineClassPerLevel[WebInspector.UISourceCode.Message.Level.Error] = "text-editor-line-with-error"; |
+WebInspector.UISourceCodeFrame._lineClassPerLevel[WebInspector.UISourceCode.Message.Level.Warning] = "text-editor-line-with-warning"; |
+ |
+/** |
+ * @constructor |
+ * @param {!WebInspector.UISourceCode.Message} message |
+ */ |
+WebInspector.UISourceCodeFrame.RowMessage = function(message) |
+{ |
+ this._message = message; |
+ this._repeatCount = 1; |
+ this.element = createElementWithClass("div", "text-editor-row-message"); |
+ this._icon = this.element.createChild("label", "", "dt-icon-label"); |
+ this._icon.type = WebInspector.UISourceCodeFrame._iconClassPerLevel[message.level()]; |
+ this._repeatCountElement = this.element.createChild("span", "bubble-repeat-count hidden error"); |
+ var linesContainer = this.element.createChild("div", "text-editor-row-message-lines"); |
+ var lines = this._message.text().split("\n"); |
+ for (var i = 0; i < lines.length; ++i) { |
+ var messageLine = linesContainer.createChild("div"); |
+ messageLine.textContent = lines[i]; |
+ } |
+} |
+ |
+WebInspector.UISourceCodeFrame.RowMessage.prototype = { |
+ /** |
+ * @return {!WebInspector.UISourceCode.Message} |
+ */ |
+ message: function() |
+ { |
+ return this._message; |
+ }, |
+ |
+ /** |
+ * @return {number} |
+ */ |
+ repeatCount: function() |
+ { |
+ return this._repeatCount; |
+ }, |
+ |
+ setRepeatCount: function(repeatCount) |
+ { |
+ if (this._repeatCount === repeatCount) |
+ return; |
+ this._repeatCount = repeatCount; |
+ this._updateMessageRepeatCount(); |
+ }, |
+ |
+ _updateMessageRepeatCount: function() |
+ { |
+ this._repeatCountElement.textContent = this._repeatCount; |
+ var showRepeatCount = this._repeatCount > 1; |
+ this._repeatCountElement.classList.toggle("hidden", !showRepeatCount); |
+ this._icon.classList.toggle("hidden", showRepeatCount); |
+ } |
+} |
+ |
+/** |
+ * @constructor |
+ * @param {!WebInspector.UISourceCodeFrame} sourceFrame |
+ * @param {!WebInspector.CodeMirrorTextEditor} textEditor |
+ * @param {number} lineNumber |
+ */ |
+WebInspector.UISourceCodeFrame.RowMessageBucket = function(sourceFrame, textEditor, lineNumber) |
+{ |
+ this._sourceFrame = sourceFrame; |
+ this._textEditor = textEditor; |
+ this._lineHandle = textEditor.textEditorPositionHandle(lineNumber, 0); |
+ this._decoration = createElementWithClass("div", "text-editor-line-decoration"); |
+ this._decoration._messageBucket = this; |
+ this._wave = this._decoration.createChild("div", "text-editor-line-decoration-wave"); |
+ this._icon = this._wave.createChild("label", "text-editor-line-decoration-icon", "dt-icon-label"); |
+ |
+ this._textEditor.addDecoration(lineNumber, this._decoration); |
+ |
+ this._messagesDescriptionElement = createElementWithClass("div", "text-editor-messages-description-container"); |
+ /** @type {!Array.<!WebInspector.UISourceCodeFrame.RowMessage>} */ |
+ this._messages = []; |
+ |
+ this._level = null; |
+} |
+ |
+WebInspector.UISourceCodeFrame.RowMessageBucket.prototype = { |
+ /** |
+ * @param {number} lineNumber |
+ * @param {number} columnNumber |
+ */ |
+ _updateWavePosition: function(lineNumber, columnNumber) |
+ { |
+ lineNumber = Math.min(lineNumber, this._textEditor.linesCount - 1); |
+ var lineText = this._textEditor.line(lineNumber); |
+ columnNumber = Math.min(columnNumber, lineText.length); |
+ var lineIndent = WebInspector.TextUtils.lineIndent(lineText).length; |
+ var base = this._textEditor.cursorPositionToCoordinates(lineNumber, 0); |
+ |
+ var start = this._textEditor.cursorPositionToCoordinates(lineNumber, Math.max(columnNumber - 1, lineIndent)); |
+ var end = this._textEditor.cursorPositionToCoordinates(lineNumber, lineText.length); |
+ /** @const */ |
+ var codeMirrorLinesLeftPadding = 4; |
+ this._wave.style.left = (start.x - base.x + codeMirrorLinesLeftPadding) + "px"; |
+ this._wave.style.width = (end.x - start.x) + "px"; |
+ }, |
+ |
+ /** |
+ * @return {!Element} |
+ */ |
+ messagesDescription: function() |
+ { |
+ this._messagesDescriptionElement.removeChildren(); |
+ for (var i = 0; i < this._messages.length; ++i) { |
+ this._messagesDescriptionElement.appendChild(this._messages[i].element); |
+ } |
+ return this._messagesDescriptionElement; |
+ }, |
+ |
+ detachFromEditor: function() |
+ { |
+ var position = this._lineHandle.resolve(); |
+ if (!position) |
+ return; |
+ var lineNumber = position.lineNumber; |
+ if (this._level) |
+ this._textEditor.toggleLineClass(lineNumber, WebInspector.UISourceCodeFrame._lineClassPerLevel[this._level], false); |
+ this._textEditor.removeDecoration(lineNumber, this._decoration); |
+ }, |
+ |
+ /** |
+ * @return {number} |
+ */ |
+ uniqueMessagesCount: function() |
+ { |
+ return this._messages.length; |
+ }, |
+ |
+ /** |
+ * @param {!WebInspector.UISourceCode.Message} message |
+ */ |
+ addMessage: function(message) |
+ { |
+ for (var i = 0; i < this._messages.length; ++i) { |
+ var rowMessage = this._messages[i]; |
+ if (rowMessage.message().isEqual(message)) { |
+ rowMessage.setRepeatCount(rowMessage.repeatCount() + 1); |
+ return; |
+ } |
+ } |
+ |
+ var rowMessage = new WebInspector.UISourceCodeFrame.RowMessage(message); |
+ this._messages.push(rowMessage); |
+ this._updateDecoration(); |
+ }, |
+ |
+ /** |
+ * @param {!WebInspector.UISourceCode.Message} message |
+ */ |
+ removeMessage: function(message) |
+ { |
+ for (var i = 0; i < this._messages.length; ++i) { |
+ var rowMessage = this._messages[i]; |
+ if (!rowMessage.message().isEqual(message)) |
+ continue; |
+ rowMessage.setRepeatCount(rowMessage.repeatCount() - 1); |
+ if (!rowMessage.repeatCount()) |
+ this._messages.splice(i, 1); |
+ this._updateDecoration(); |
+ return; |
+ } |
+ }, |
+ |
+ _updateDecoration: function() |
+ { |
+ if (!this._sourceFrame.isEditorShowing()) |
+ return; |
+ if (!this._messages.length) |
+ return; |
+ var position = this._lineHandle.resolve(); |
+ if (!position) |
+ return; |
+ |
+ var lineNumber = position.lineNumber; |
+ var columnNumber = Number.MAX_VALUE; |
+ var maxMessage = null; |
+ for (var i = 0; i < this._messages.length; ++i) { |
+ var message = this._messages[i].message(); |
+ columnNumber = Math.min(columnNumber, message.columnNumber()); |
+ if (!maxMessage || WebInspector.UISourceCode.Message.messageLevelComparator(maxMessage, message) < 0) |
+ maxMessage = message; |
+ } |
+ this._updateWavePosition(lineNumber, columnNumber); |
+ |
+ if (this._level) { |
+ this._textEditor.toggleLineClass(lineNumber, WebInspector.UISourceCodeFrame._lineClassPerLevel[this._level], false); |
+ this._icon.type = ""; |
+ } |
+ this._level = maxMessage.level(); |
+ if (!this._level) |
+ return; |
+ this._textEditor.toggleLineClass(lineNumber, WebInspector.UISourceCodeFrame._lineClassPerLevel[this._level], true); |
+ this._icon.type = WebInspector.UISourceCodeFrame._iconClassPerLevel[this._level]; |
+ } |
+} |
+ |
+WebInspector.UISourceCode.Message._messageLevelPriority = { |
+ "Warning": 3, |
+ "Error": 4 |
+}; |
+ |
+/** |
+ * @param {!WebInspector.UISourceCode.Message} a |
+ * @param {!WebInspector.UISourceCode.Message} b |
+ * @return {number} |
+ */ |
+WebInspector.UISourceCode.Message.messageLevelComparator = function(a, b) |
+{ |
+ return WebInspector.UISourceCode.Message._messageLevelPriority[a.level()] - WebInspector.UISourceCode.Message._messageLevelPriority[b.level()]; |
+} |