| /* |
| * Copyright (C) 2012 Google Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * |
| * 2. Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. AND ITS CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC. |
| * OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| Sources.UISourceCodeFrame = class extends SourceFrame.SourceFrame { |
| /** |
| * @param {!Workspace.UISourceCode} uiSourceCode |
| */ |
| constructor(uiSourceCode) { |
| super(workingCopy); |
| this._uiSourceCode = uiSourceCode; |
| |
| if (Root.Runtime.experiments.isEnabled('sourceDiff')) { |
| this._diff = new SourceFrame.SourceCodeDiff(this.textEditor); |
| } |
| |
| this._muteSourceCodeEvents = false; |
| this._isSettingContent = false; |
| |
| /** @type {?Persistence.PersistenceBinding} */ |
| this._persistenceBinding = Persistence.persistence.binding(uiSourceCode); |
| |
| /** @type {!Map<number, !Sources.UISourceCodeFrame.RowMessageBucket>} */ |
| this._rowMessageBuckets = new Map(); |
| /** @type {!Set<string>} */ |
| this._typeDecorationsPending = new Set(); |
| |
| this._uiSourceCodeEventListeners = []; |
| this._messageAndDecorationListeners = []; |
| |
| this._boundOnBindingChanged = this._onBindingChanged.bind(this); |
| |
| this.textEditor.addEventListener( |
| SourceFrame.SourcesTextEditor.Events.EditorBlurred, |
| () => UI.context.setFlavor(Sources.UISourceCodeFrame, null)); |
| this.textEditor.addEventListener( |
| SourceFrame.SourcesTextEditor.Events.EditorFocused, |
| () => UI.context.setFlavor(Sources.UISourceCodeFrame, this)); |
| Common.settings.moduleSetting('persistenceNetworkOverridesEnabled') |
| .addChangeListener(this._onNetworkPersistenceChanged, this); |
| |
| |
| this._errorPopoverHelper = new UI.PopoverHelper(this.element, this._getErrorPopoverContent.bind(this)); |
| this._errorPopoverHelper.setHasPadding(true); |
| |
| this._errorPopoverHelper.setTimeout(100, 100); |
| |
| /** @type {!Array<!Sources.UISourceCodeFrame.Plugin>} */ |
| this._plugins = []; |
| |
| this._initializeUISourceCode(); |
| |
| /** |
| * @return {!Promise<!Common.DeferredContent>} |
| */ |
| function workingCopy() { |
| if (uiSourceCode.isDirty()) { |
| return Promise.resolve({content: uiSourceCode.workingCopy(), isEncoded: false}); |
| } |
| return uiSourceCode.requestContent(); |
| } |
| } |
| |
| _installMessageAndDecorationListeners() { |
| if (this._persistenceBinding) { |
| const networkSourceCode = this._persistenceBinding.network; |
| const fileSystemSourceCode = this._persistenceBinding.fileSystem; |
| this._messageAndDecorationListeners = [ |
| networkSourceCode.addEventListener(Workspace.UISourceCode.Events.MessageAdded, this._onMessageAdded, this), |
| networkSourceCode.addEventListener(Workspace.UISourceCode.Events.MessageRemoved, this._onMessageRemoved, this), |
| networkSourceCode.addEventListener( |
| Workspace.UISourceCode.Events.LineDecorationAdded, this._onLineDecorationAdded, this), |
| networkSourceCode.addEventListener( |
| Workspace.UISourceCode.Events.LineDecorationRemoved, this._onLineDecorationRemoved, this), |
| |
| fileSystemSourceCode.addEventListener(Workspace.UISourceCode.Events.MessageAdded, this._onMessageAdded, this), |
| fileSystemSourceCode.addEventListener( |
| Workspace.UISourceCode.Events.MessageRemoved, this._onMessageRemoved, this), |
| ]; |
| } else { |
| this._messageAndDecorationListeners = [ |
| this._uiSourceCode.addEventListener(Workspace.UISourceCode.Events.MessageAdded, this._onMessageAdded, this), |
| this._uiSourceCode.addEventListener(Workspace.UISourceCode.Events.MessageRemoved, this._onMessageRemoved, this), |
| this._uiSourceCode.addEventListener( |
| Workspace.UISourceCode.Events.LineDecorationAdded, this._onLineDecorationAdded, this), |
| this._uiSourceCode.addEventListener( |
| Workspace.UISourceCode.Events.LineDecorationRemoved, this._onLineDecorationRemoved, this) |
| ]; |
| } |
| } |
| |
| /** |
| * @return {!Workspace.UISourceCode} |
| */ |
| uiSourceCode() { |
| return this._uiSourceCode; |
| } |
| |
| /** |
| * @param {!Workspace.UISourceCode} uiSourceCode |
| */ |
| setUISourceCode(uiSourceCode) { |
| this._unloadUISourceCode(); |
| this._uiSourceCode = uiSourceCode; |
| if (uiSourceCode.contentLoaded()) { |
| if (uiSourceCode.workingCopy() !== this.textEditor.text()) { |
| this._innerSetContent(uiSourceCode.workingCopy()); |
| } |
| } else { |
| uiSourceCode.requestContent().then(() => { |
| if (this._uiSourceCode !== uiSourceCode) { |
| return; |
| } |
| if (uiSourceCode.workingCopy() !== this.textEditor.text()) { |
| this._innerSetContent(uiSourceCode.workingCopy()); |
| } |
| }); |
| } |
| this._initializeUISourceCode(); |
| } |
| |
| _unloadUISourceCode() { |
| this._disposePlugins(); |
| for (const message of this._allMessages()) { |
| this._removeMessageFromSource(message); |
| } |
| Common.EventTarget.removeEventListeners(this._messageAndDecorationListeners); |
| Common.EventTarget.removeEventListeners(this._uiSourceCodeEventListeners); |
| this._uiSourceCode.removeWorkingCopyGetter(); |
| Persistence.persistence.unsubscribeFromBindingEvent(this._uiSourceCode, this._boundOnBindingChanged); |
| } |
| |
| _initializeUISourceCode() { |
| this._uiSourceCodeEventListeners = [ |
| this._uiSourceCode.addEventListener( |
| Workspace.UISourceCode.Events.WorkingCopyChanged, this._onWorkingCopyChanged, this), |
| this._uiSourceCode.addEventListener( |
| Workspace.UISourceCode.Events.WorkingCopyCommitted, this._onWorkingCopyCommitted, this), |
| this._uiSourceCode.addEventListener( |
| Workspace.UISourceCode.Events.TitleChanged, this._refreshHighlighterType, this) |
| ]; |
| |
| Persistence.persistence.subscribeForBindingEvent(this._uiSourceCode, this._boundOnBindingChanged); |
| for (const message of this._allMessages()) { |
| this._addMessageToSource(message); |
| } |
| this._installMessageAndDecorationListeners(); |
| this._updateStyle(); |
| this._decorateAllTypes(); |
| this._refreshHighlighterType(); |
| if (Root.Runtime.experiments.isEnabled('sourcesPrettyPrint')) { |
| const supportedPrettyTypes = new Set(['text/html', 'text/css', 'text/javascript']); |
| this.setCanPrettyPrint(supportedPrettyTypes.has(this.highlighterType()), true); |
| } |
| this._ensurePluginsLoaded(); |
| } |
| |
| /** |
| * @override |
| */ |
| wasShown() { |
| super.wasShown(); |
| // We need CodeMirrorTextEditor to be initialized prior to this call as it calls |cursorPositionToCoordinates| internally. @see crbug.com/506566 |
| setImmediate(this._updateBucketDecorations.bind(this)); |
| this.setEditable(this._canEditSource()); |
| for (const plugin of this._plugins) { |
| plugin.wasShown(); |
| } |
| } |
| |
| /** |
| * @override |
| */ |
| willHide() { |
| for (const plugin of this._plugins) { |
| plugin.willHide(); |
| } |
| super.willHide(); |
| UI.context.setFlavor(Sources.UISourceCodeFrame, null); |
| this._uiSourceCode.removeWorkingCopyGetter(); |
| } |
| |
| _refreshHighlighterType() { |
| const binding = Persistence.persistence.binding(this._uiSourceCode); |
| const highlighterType = binding ? binding.network.mimeType() : this._uiSourceCode.mimeType(); |
| if (this.highlighterType() === highlighterType) { |
| return; |
| } |
| this._disposePlugins(); |
| this.setHighlighterType(highlighterType); |
| this._ensurePluginsLoaded(); |
| } |
| |
| /** |
| * @return {boolean} |
| */ |
| _canEditSource() { |
| if (this.hasLoadError()) { |
| return false; |
| } |
| if (Persistence.persistence.binding(this._uiSourceCode)) { |
| return true; |
| } |
| if (this._uiSourceCode.project().canSetFileContent()) { |
| return true; |
| } |
| if (this._uiSourceCode.project().isServiceProject()) { |
| return false; |
| } |
| if (this._uiSourceCode.project().type() === Workspace.projectTypes.Network && |
| Persistence.networkPersistenceManager.active()) { |
| return true; |
| } |
| // Because live edit fails on large whitespace changes, pretty printed scripts are not editable. |
| if (this.pretty && this._uiSourceCode.contentType().hasScripts()) { |
| return false; |
| } |
| return this._uiSourceCode.contentType() !== Common.resourceTypes.Document; |
| } |
| |
| _onNetworkPersistenceChanged() { |
| this.setEditable(this._canEditSource()); |
| } |
| |
| commitEditing() { |
| if (!this._uiSourceCode.isDirty()) { |
| return; |
| } |
| |
| this._muteSourceCodeEvents = true; |
| this._uiSourceCode.commitWorkingCopy(); |
| this._muteSourceCodeEvents = false; |
| } |
| |
| /** |
| * @override |
| * @param {?string} content |
| * @param {?string} loadError |
| */ |
| setContent(content, loadError) { |
| this._disposePlugins(); |
| this._rowMessageBuckets.clear(); |
| super.setContent(content, loadError); |
| for (const message of this._allMessages()) { |
| this._addMessageToSource(message); |
| } |
| this._decorateAllTypes(); |
| this._ensurePluginsLoaded(); |
| } |
| |
| /** |
| * @return {!Set<!Workspace.UISourceCode.Message>} |
| */ |
| _allMessages() { |
| if (this._persistenceBinding) { |
| const combinedSet = this._persistenceBinding.network.messages(); |
| combinedSet.addAll(this._persistenceBinding.fileSystem.messages()); |
| return combinedSet; |
| } |
| return this._uiSourceCode.messages(); |
| } |
| |
| /** |
| * @override |
| * @param {!TextUtils.TextRange} oldRange |
| * @param {!TextUtils.TextRange} newRange |
| */ |
| onTextChanged(oldRange, newRange) { |
| const wasPretty = this.pretty; |
| super.onTextChanged(oldRange, newRange); |
| this._errorPopoverHelper.hidePopover(); |
| if (this._isSettingContent) { |
| return; |
| } |
| Sources.SourcesPanel.instance().updateLastModificationTime(); |
| this._muteSourceCodeEvents = true; |
| if (this.isClean()) { |
| this._uiSourceCode.resetWorkingCopy(); |
| } else { |
| this._uiSourceCode.setWorkingCopyGetter(this.textEditor.text.bind(this.textEditor)); |
| } |
| this._muteSourceCodeEvents = false; |
| if (wasPretty !== this.pretty) { |
| this._updateStyle(); |
| this._disposePlugins(); |
| this._ensurePluginsLoaded(); |
| } |
| } |
| |
| /** |
| * @param {!Common.Event} event |
| */ |
| _onWorkingCopyChanged(event) { |
| if (this._muteSourceCodeEvents) { |
| return; |
| } |
| this._innerSetContent(this._uiSourceCode.workingCopy()); |
| } |
| |
| /** |
| * @param {!Common.Event} event |
| */ |
| _onWorkingCopyCommitted(event) { |
| if (!this._muteSourceCodeEvents) { |
| this._innerSetContent(this._uiSourceCode.workingCopy()); |
| } |
| this.contentCommitted(); |
| this._updateStyle(); |
| } |
| |
| _ensurePluginsLoaded() { |
| if (!this.loaded || this._plugins.length) { |
| return; |
| } |
| |
| const binding = Persistence.persistence.binding(this._uiSourceCode); |
| const pluginUISourceCode = binding ? binding.network : this._uiSourceCode; |
| |
| // The order of these plugins matters for toolbar items |
| if (Sources.DebuggerPlugin.accepts(pluginUISourceCode)) { |
| this._plugins.push(new Sources.DebuggerPlugin(this.textEditor, pluginUISourceCode, this.transformer())); |
| } |
| if (Sources.CSSPlugin.accepts(pluginUISourceCode)) { |
| this._plugins.push(new Sources.CSSPlugin(this.textEditor)); |
| } |
| if (!this.pretty && Sources.JavaScriptCompilerPlugin.accepts(pluginUISourceCode)) { |
| this._plugins.push(new Sources.JavaScriptCompilerPlugin(this.textEditor, pluginUISourceCode)); |
| } |
| if (Sources.SnippetsPlugin.accepts(pluginUISourceCode)) { |
| this._plugins.push(new Sources.SnippetsPlugin(this.textEditor, pluginUISourceCode)); |
| } |
| if (Sources.ScriptOriginPlugin.accepts(pluginUISourceCode)) { |
| this._plugins.push(new Sources.ScriptOriginPlugin(this.textEditor, pluginUISourceCode)); |
| } |
| if (!this.pretty && Root.Runtime.experiments.isEnabled('sourceDiff') && |
| Sources.GutterDiffPlugin.accepts(pluginUISourceCode)) { |
| this._plugins.push(new Sources.GutterDiffPlugin(this.textEditor, pluginUISourceCode)); |
| } |
| if (Sources.CoveragePlugin.accepts(pluginUISourceCode)) { |
| this._plugins.push(new Sources.CoveragePlugin(this.textEditor, pluginUISourceCode)); |
| } |
| |
| this.dispatchEventToListeners(Sources.UISourceCodeFrame.Events.ToolbarItemsChanged); |
| for (const plugin of this._plugins) { |
| plugin.wasShown(); |
| } |
| } |
| |
| _disposePlugins() { |
| this.textEditor.operation(() => { |
| for (const plugin of this._plugins) { |
| plugin.dispose(); |
| } |
| }); |
| this._plugins = []; |
| } |
| |
| _onBindingChanged() { |
| const binding = Persistence.persistence.binding(this._uiSourceCode); |
| if (binding === this._persistenceBinding) { |
| return; |
| } |
| this._unloadUISourceCode(); |
| this._persistenceBinding = binding; |
| this._initializeUISourceCode(); |
| } |
| |
| _updateStyle() { |
| this.setEditable(this._canEditSource()); |
| } |
| |
| /** |
| * @param {string} content |
| */ |
| _innerSetContent(content) { |
| this._isSettingContent = true; |
| const oldContent = this.textEditor.text(); |
| if (this._diff) { |
| this._diff.highlightModifiedLines(oldContent, content); |
| } |
| if (oldContent !== content) { |
| this.setContent(content, null); |
| } |
| this._isSettingContent = false; |
| } |
| |
| /** |
| * @override |
| * @return {!Promise} |
| */ |
| async populateTextAreaContextMenu(contextMenu, editorLineNumber, editorColumnNumber) { |
| await super.populateTextAreaContextMenu(contextMenu, editorLineNumber, editorColumnNumber); |
| contextMenu.appendApplicableItems(this._uiSourceCode); |
| const location = this.transformer().editorToRawLocation(editorLineNumber, editorColumnNumber); |
| contextMenu.appendApplicableItems(new Workspace.UILocation(this._uiSourceCode, location[0], location[1])); |
| contextMenu.appendApplicableItems(this); |
| for (const plugin of this._plugins) { |
| await plugin.populateTextAreaContextMenu(contextMenu, editorLineNumber, editorColumnNumber); |
| } |
| } |
| |
| dispose() { |
| this._errorPopoverHelper.dispose(); |
| this._unloadUISourceCode(); |
| this.textEditor.dispose(); |
| this.detach(); |
| Common.settings.moduleSetting('persistenceNetworkOverridesEnabled') |
| .removeChangeListener(this._onNetworkPersistenceChanged, this); |
| } |
| |
| /** |
| * @param {!Common.Event} event |
| */ |
| _onMessageAdded(event) { |
| const message = /** @type {!Workspace.UISourceCode.Message} */ (event.data); |
| this._addMessageToSource(message); |
| } |
| |
| /** |
| * @param {!Workspace.UISourceCode.Message} message |
| */ |
| _addMessageToSource(message) { |
| if (!this.loaded) { |
| return; |
| } |
| const editorLocation = this.transformer().rawToEditorLocation(message.lineNumber(), message.columnNumber()); |
| let editorLineNumber = editorLocation[0]; |
| if (editorLineNumber >= this.textEditor.linesCount) { |
| editorLineNumber = this.textEditor.linesCount - 1; |
| } |
| if (editorLineNumber < 0) { |
| editorLineNumber = 0; |
| } |
| |
| let messageBucket = this._rowMessageBuckets.get(editorLineNumber); |
| if (!messageBucket) { |
| messageBucket = new Sources.UISourceCodeFrame.RowMessageBucket(this, this.textEditor, editorLineNumber); |
| this._rowMessageBuckets.set(editorLineNumber, messageBucket); |
| } |
| messageBucket.addMessage(message); |
| } |
| |
| /** |
| * @param {!Common.Event} event |
| */ |
| _onMessageRemoved(event) { |
| const message = /** @type {!Workspace.UISourceCode.Message} */ (event.data); |
| this._removeMessageFromSource(message); |
| } |
| |
| /** |
| * @param {!Workspace.UISourceCode.Message} message |
| */ |
| _removeMessageFromSource(message) { |
| if (!this.loaded) { |
| return; |
| } |
| |
| const editorLocation = this.transformer().rawToEditorLocation(message.lineNumber(), message.columnNumber()); |
| let editorLineNumber = editorLocation[0]; |
| if (editorLineNumber >= this.textEditor.linesCount) { |
| editorLineNumber = this.textEditor.linesCount - 1; |
| } |
| if (editorLineNumber < 0) { |
| editorLineNumber = 0; |
| } |
| |
| const messageBucket = this._rowMessageBuckets.get(editorLineNumber); |
| if (!messageBucket) { |
| return; |
| } |
| messageBucket.removeMessage(message); |
| if (!messageBucket.uniqueMessagesCount()) { |
| messageBucket.detachFromEditor(); |
| this._rowMessageBuckets.delete(editorLineNumber); |
| } |
| } |
| |
| /** |
| * @param {!Event} event |
| * @return {?UI.PopoverRequest} |
| */ |
| _getErrorPopoverContent(event) { |
| const element = event.target.enclosingNodeOrSelfWithClass('text-editor-line-decoration-icon') || |
| event.target.enclosingNodeOrSelfWithClass('text-editor-line-decoration-wave'); |
| if (!element) { |
| return null; |
| } |
| const anchor = element.enclosingNodeOrSelfWithClass('text-editor-line-decoration-icon') ? |
| element.boxInWindow() : |
| new AnchorBox(event.clientX, event.clientY, 1, 1); |
| return { |
| box: anchor, |
| show: popover => { |
| const messageBucket = element.enclosingNodeOrSelfWithClass('text-editor-line-decoration')._messageBucket; |
| const messagesOutline = messageBucket.messagesDescription(); |
| popover.contentElement.appendChild(messagesOutline); |
| return Promise.resolve(true); |
| } |
| }; |
| } |
| |
| _updateBucketDecorations() { |
| for (const bucket of this._rowMessageBuckets.values()) { |
| bucket._updateDecoration(); |
| } |
| } |
| |
| /** |
| * @param {!Common.Event} event |
| */ |
| _onLineDecorationAdded(event) { |
| const marker = /** @type {!Workspace.UISourceCode.LineMarker} */ (event.data); |
| this._decorateTypeThrottled(marker.type()); |
| } |
| |
| /** |
| * @param {!Common.Event} event |
| */ |
| _onLineDecorationRemoved(event) { |
| const marker = /** @type {!Workspace.UISourceCode.LineMarker} */ (event.data); |
| this._decorateTypeThrottled(marker.type()); |
| } |
| |
| /** |
| * @param {string} type |
| */ |
| async _decorateTypeThrottled(type) { |
| if (this._typeDecorationsPending.has(type)) { |
| return; |
| } |
| this._typeDecorationsPending.add(type); |
| const decorator = await self.runtime.extensions(SourceFrame.LineDecorator) |
| .find(extension => extension.descriptor()['decoratorType'] === type) |
| .instance(); |
| this._typeDecorationsPending.delete(type); |
| this.textEditor.codeMirror().operation(() => { |
| decorator.decorate( |
| this._persistenceBinding ? this._persistenceBinding.network : this.uiSourceCode(), this.textEditor, type); |
| }); |
| } |
| |
| _decorateAllTypes() { |
| if (!this.loaded) { |
| return; |
| } |
| for (const extension of self.runtime.extensions(SourceFrame.LineDecorator)) { |
| const type = extension.descriptor()['decoratorType']; |
| if (this._uiSourceCode.decorationsForType(type)) { |
| this._decorateTypeThrottled(type); |
| } |
| } |
| } |
| |
| /** |
| * @override |
| * @return {!Array<!UI.ToolbarItem>} |
| */ |
| syncToolbarItems() { |
| const leftToolbarItems = super.syncToolbarItems(); |
| const rightToolbarItems = []; |
| for (const plugin of this._plugins) { |
| leftToolbarItems.pushAll(plugin.leftToolbarItems()); |
| rightToolbarItems.pushAll(plugin.rightToolbarItems()); |
| } |
| |
| if (!rightToolbarItems.length) { |
| return leftToolbarItems; |
| } |
| |
| return [...leftToolbarItems, new UI.ToolbarSeparator(true), ...rightToolbarItems]; |
| } |
| |
| /** |
| * @override |
| * @param {!UI.ContextMenu} contextMenu |
| * @param {number} lineNumber |
| * @return {!Promise} |
| */ |
| async populateLineGutterContextMenu(contextMenu, lineNumber) { |
| await super.populateLineGutterContextMenu(contextMenu, lineNumber); |
| for (const plugin of this._plugins) { |
| await plugin.populateLineGutterContextMenu(contextMenu, lineNumber); |
| } |
| } |
| }; |
| |
| Sources.UISourceCodeFrame._iconClassPerLevel = {}; |
| Sources.UISourceCodeFrame._iconClassPerLevel[Workspace.UISourceCode.Message.Level.Error] = 'smallicon-error'; |
| Sources.UISourceCodeFrame._iconClassPerLevel[Workspace.UISourceCode.Message.Level.Warning] = 'smallicon-warning'; |
| |
| Sources.UISourceCodeFrame._bubbleTypePerLevel = {}; |
| Sources.UISourceCodeFrame._bubbleTypePerLevel[Workspace.UISourceCode.Message.Level.Error] = 'error'; |
| Sources.UISourceCodeFrame._bubbleTypePerLevel[Workspace.UISourceCode.Message.Level.Warning] = 'warning'; |
| |
| Sources.UISourceCodeFrame._lineClassPerLevel = {}; |
| Sources.UISourceCodeFrame._lineClassPerLevel[Workspace.UISourceCode.Message.Level.Error] = |
| 'text-editor-line-with-error'; |
| Sources.UISourceCodeFrame._lineClassPerLevel[Workspace.UISourceCode.Message.Level.Warning] = |
| 'text-editor-line-with-warning'; |
| |
| /** |
| * @unrestricted |
| */ |
| Sources.UISourceCodeFrame.RowMessage = class { |
| /** |
| * @param {!Workspace.UISourceCode.Message} message |
| */ |
| constructor(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 = Sources.UISourceCodeFrame._iconClassPerLevel[message.level()]; |
| this._repeatCountElement = |
| this.element.createChild('span', 'text-editor-row-message-repeat-count hidden', 'dt-small-bubble'); |
| this._repeatCountElement.type = Sources.UISourceCodeFrame._bubbleTypePerLevel[message.level()]; |
| const linesContainer = this.element.createChild('div'); |
| const lines = this._message.text().split('\n'); |
| for (let i = 0; i < lines.length; ++i) { |
| const messageLine = linesContainer.createChild('div'); |
| messageLine.textContent = lines[i]; |
| } |
| } |
| |
| /** |
| * @return {!Workspace.UISourceCode.Message} |
| */ |
| message() { |
| return this._message; |
| } |
| |
| /** |
| * @return {number} |
| */ |
| repeatCount() { |
| return this._repeatCount; |
| } |
| |
| setRepeatCount(repeatCount) { |
| if (this._repeatCount === repeatCount) { |
| return; |
| } |
| this._repeatCount = repeatCount; |
| this._updateMessageRepeatCount(); |
| } |
| |
| _updateMessageRepeatCount() { |
| this._repeatCountElement.textContent = this._repeatCount; |
| const showRepeatCount = this._repeatCount > 1; |
| this._repeatCountElement.classList.toggle('hidden', !showRepeatCount); |
| this._icon.classList.toggle('hidden', showRepeatCount); |
| } |
| }; |
| |
| Sources.UISourceCodeFrame.RowMessageBucket = class { |
| /** |
| * @param {!Sources.UISourceCodeFrame} sourceFrame |
| * @param {!TextEditor.CodeMirrorTextEditor} textEditor |
| * @param {number} editorLineNumber |
| */ |
| constructor(sourceFrame, textEditor, editorLineNumber) { |
| this._sourceFrame = sourceFrame; |
| this.textEditor = textEditor; |
| this._lineHandle = textEditor.textEditorPositionHandle(editorLineNumber, 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('span', 'text-editor-line-decoration-icon', 'dt-icon-label'); |
| /** @type {?number} */ |
| this._decorationStartColumn = null; |
| |
| this._messagesDescriptionElement = createElementWithClass('div', 'text-editor-messages-description-container'); |
| /** @type {!Array.<!Sources.UISourceCodeFrame.RowMessage>} */ |
| this._messages = []; |
| |
| this._level = null; |
| } |
| |
| /** |
| * @param {number} editorLineNumber |
| * @param {number} columnNumber |
| */ |
| _updateWavePosition(editorLineNumber, columnNumber) { |
| editorLineNumber = Math.min(editorLineNumber, this.textEditor.linesCount - 1); |
| const lineText = this.textEditor.line(editorLineNumber); |
| columnNumber = Math.min(columnNumber, lineText.length); |
| const lineIndent = TextUtils.TextUtils.lineIndent(lineText).length; |
| const startColumn = Math.max(columnNumber - 1, lineIndent); |
| if (this._decorationStartColumn === startColumn) { |
| return; |
| } |
| if (this._decorationStartColumn !== null) { |
| this.textEditor.removeDecoration(this._decoration, editorLineNumber); |
| } |
| this.textEditor.addDecoration(this._decoration, editorLineNumber, startColumn); |
| this._decorationStartColumn = startColumn; |
| } |
| |
| /** |
| * @return {!Element} |
| */ |
| messagesDescription() { |
| this._messagesDescriptionElement.removeChildren(); |
| UI.appendStyle(this._messagesDescriptionElement, 'source_frame/messagesPopover.css'); |
| for (let i = 0; i < this._messages.length; ++i) { |
| this._messagesDescriptionElement.appendChild(this._messages[i].element); |
| } |
| |
| return this._messagesDescriptionElement; |
| } |
| |
| detachFromEditor() { |
| const position = this._lineHandle.resolve(); |
| if (!position) { |
| return; |
| } |
| const editorLineNumber = position.lineNumber; |
| if (this._level) { |
| this.textEditor.toggleLineClass( |
| editorLineNumber, Sources.UISourceCodeFrame._lineClassPerLevel[this._level], false); |
| } |
| if (this._decorationStartColumn !== null) { |
| this.textEditor.removeDecoration(this._decoration, editorLineNumber); |
| this._decorationStartColumn = null; |
| } |
| } |
| |
| /** |
| * @return {number} |
| */ |
| uniqueMessagesCount() { |
| return this._messages.length; |
| } |
| |
| /** |
| * @param {!Workspace.UISourceCode.Message} message |
| */ |
| addMessage(message) { |
| for (let i = 0; i < this._messages.length; ++i) { |
| const rowMessage = this._messages[i]; |
| if (rowMessage.message().isEqual(message)) { |
| rowMessage.setRepeatCount(rowMessage.repeatCount() + 1); |
| return; |
| } |
| } |
| |
| const rowMessage = new Sources.UISourceCodeFrame.RowMessage(message); |
| this._messages.push(rowMessage); |
| this._updateDecoration(); |
| } |
| |
| /** |
| * @param {!Workspace.UISourceCode.Message} message |
| */ |
| removeMessage(message) { |
| for (let i = 0; i < this._messages.length; ++i) { |
| const 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() { |
| if (!this._sourceFrame.isShowing()) { |
| return; |
| } |
| if (!this._messages.length) { |
| return; |
| } |
| const position = this._lineHandle.resolve(); |
| if (!position) { |
| return; |
| } |
| |
| const editorLineNumber = position.lineNumber; |
| let columnNumber = Number.MAX_VALUE; |
| let maxMessage = null; |
| for (let i = 0; i < this._messages.length; ++i) { |
| const message = this._messages[i].message(); |
| const editorLocation = |
| this._sourceFrame.transformer().rawToEditorLocation(editorLineNumber, message.columnNumber()); |
| columnNumber = Math.min(columnNumber, editorLocation[1]); |
| if (!maxMessage || Workspace.UISourceCode.Message.messageLevelComparator(maxMessage, message) < 0) { |
| maxMessage = message; |
| } |
| } |
| this._updateWavePosition(editorLineNumber, columnNumber); |
| |
| if (this._level === maxMessage.level()) { |
| return; |
| } |
| if (this._level) { |
| this.textEditor.toggleLineClass( |
| editorLineNumber, Sources.UISourceCodeFrame._lineClassPerLevel[this._level], false); |
| this._icon.type = ''; |
| } |
| this._level = maxMessage.level(); |
| if (!this._level) { |
| return; |
| } |
| this.textEditor.toggleLineClass(editorLineNumber, Sources.UISourceCodeFrame._lineClassPerLevel[this._level], true); |
| this._icon.type = Sources.UISourceCodeFrame._iconClassPerLevel[this._level]; |
| } |
| }; |
| |
| Workspace.UISourceCode.Message._messageLevelPriority = { |
| 'Warning': 3, |
| 'Error': 4 |
| }; |
| |
| /** |
| * @param {!Workspace.UISourceCode.Message} a |
| * @param {!Workspace.UISourceCode.Message} b |
| * @return {number} |
| */ |
| Workspace.UISourceCode.Message.messageLevelComparator = function(a, b) { |
| return Workspace.UISourceCode.Message._messageLevelPriority[a.level()] - |
| Workspace.UISourceCode.Message._messageLevelPriority[b.level()]; |
| }; |
| |
| Sources.UISourceCodeFrame.Plugin = class { |
| /** |
| * @param {!Workspace.UISourceCode} uiSourceCode |
| * @return {boolean} |
| */ |
| static accepts(uiSourceCode) { |
| return false; |
| } |
| |
| wasShown() { |
| } |
| |
| willHide() { |
| } |
| |
| /** |
| * @return {!Array<!UI.ToolbarItem>} |
| */ |
| rightToolbarItems() { |
| return []; |
| } |
| |
| /** |
| * @return {!Array<!UI.ToolbarItem>} |
| */ |
| leftToolbarItems() { |
| return []; |
| } |
| |
| /** |
| * @param {!UI.ContextMenu} contextMenu |
| * @param {number} lineNumber |
| * @return {!Promise} |
| */ |
| populateLineGutterContextMenu(contextMenu, lineNumber) { |
| return Promise.resolve(); |
| } |
| |
| /** |
| * @param {!UI.ContextMenu} contextMenu |
| * @param {number} lineNumber |
| * @param {number} columnNumber |
| * @return {!Promise} |
| */ |
| populateTextAreaContextMenu(contextMenu, lineNumber, columnNumber) { |
| return Promise.resolve(); |
| } |
| |
| dispose() { |
| } |
| }; |
| |
| /** @enum {symbol} */ |
| Sources.UISourceCodeFrame.Events = { |
| ToolbarItemsChanged: Symbol('ToolbarItemsChanged') |
| }; |