@quick-game/cli
Version:
Command line interface for rapid qg development
729 lines • 30.6 kB
JavaScript
/*
* 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
* OWNER OR 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.
*/
import * as Common from '../../core/common/common.js';
import * as Host from '../../core/host/host.js';
import * as Root from '../../core/root/root.js';
import * as FormatterActions from '../../entrypoints/formatter_worker/FormatterActions.js'; // eslint-disable-line rulesdir/es_modules_import
import * as IssuesManager from '../../models/issues_manager/issues_manager.js';
import * as Persistence from '../../models/persistence/persistence.js';
import * as TextUtils from '../../models/text_utils/text_utils.js';
import * as Workspace from '../../models/workspace/workspace.js';
import * as CodeMirror from '../../third_party/codemirror.next/codemirror.next.js';
import * as IconButton from '../../ui/components/icon_button/icon_button.js';
import * as IssueCounter from '../../ui/components/issue_counter/issue_counter.js';
import * as SourceFrame from '../../ui/legacy/components/source_frame/source_frame.js';
import * as UI from '../../ui/legacy/legacy.js';
import { CoveragePlugin } from './CoveragePlugin.js';
import { CSSPlugin } from './CSSPlugin.js';
import { DebuggerPlugin } from './DebuggerPlugin.js';
import { MemoryProfilePlugin, PerformanceProfilePlugin } from './ProfilePlugin.js';
import { ResourceOriginPlugin } from './ResourceOriginPlugin.js';
import { SnippetsPlugin } from './SnippetsPlugin.js';
import { SourcesPanel } from './SourcesPanel.js';
function sourceFramePlugins() {
// The order of these plugins matters for toolbar items and editor
// extension precedence
return [
CSSPlugin,
DebuggerPlugin,
SnippetsPlugin,
ResourceOriginPlugin,
CoveragePlugin,
MemoryProfilePlugin,
PerformanceProfilePlugin,
];
}
export class UISourceCodeFrame extends Common.ObjectWrapper.eventMixin(SourceFrame.SourceFrame.SourceFrameImpl) {
uiSourceCodeInternal;
muteSourceCodeEvents;
persistenceBinding;
uiSourceCodeEventListeners;
messageAndDecorationListeners;
boundOnBindingChanged;
// The active plugins. These are created in setContent, and
// recreated when the binding changes
plugins = [];
errorPopoverHelper;
#sourcesPanelOpenedMetricsRecorded = false;
constructor(uiSourceCode) {
super(() => this.workingCopy());
this.uiSourceCodeInternal = uiSourceCode;
this.muteSourceCodeEvents = false;
this.persistenceBinding = Persistence.Persistence.PersistenceImpl.instance().binding(uiSourceCode);
this.uiSourceCodeEventListeners = [];
this.messageAndDecorationListeners = [];
this.boundOnBindingChanged = this.onBindingChanged.bind(this);
Common.Settings.Settings.instance()
.moduleSetting('persistenceNetworkOverridesEnabled')
.addChangeListener(this.onNetworkPersistenceChanged, this);
this.errorPopoverHelper =
new UI.PopoverHelper.PopoverHelper(this.textEditor.editor.contentDOM, this.getErrorPopoverContent.bind(this));
this.errorPopoverHelper.setHasPadding(true);
this.errorPopoverHelper.setTimeout(100, 100);
this.initializeUISourceCode();
}
async workingCopy() {
if (this.uiSourceCodeInternal.isDirty()) {
return { content: this.uiSourceCodeInternal.workingCopy(), isEncoded: false };
}
return this.uiSourceCodeInternal.requestContent();
}
editorConfiguration(doc) {
return [
super.editorConfiguration(doc),
rowMessages(this.allMessages()),
// Inject editor extensions from plugins
pluginCompartment.of(this.plugins.map(plugin => plugin.editorExtension())),
];
}
onFocus() {
super.onFocus();
UI.Context.Context.instance().setFlavor(UISourceCodeFrame, this);
}
onBlur() {
super.onBlur();
UI.Context.Context.instance().setFlavor(UISourceCodeFrame, null);
}
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.DecorationChanged, this.onDecorationChanged, this),
fileSystemSourceCode.addEventListener(Workspace.UISourceCode.Events.MessageAdded, this.onMessageAdded, this),
fileSystemSourceCode.addEventListener(Workspace.UISourceCode.Events.MessageRemoved, this.onMessageRemoved, this),
];
}
else {
this.messageAndDecorationListeners = [
this.uiSourceCodeInternal.addEventListener(Workspace.UISourceCode.Events.MessageAdded, this.onMessageAdded, this),
this.uiSourceCodeInternal.addEventListener(Workspace.UISourceCode.Events.MessageRemoved, this.onMessageRemoved, this),
this.uiSourceCodeInternal.addEventListener(Workspace.UISourceCode.Events.DecorationChanged, this.onDecorationChanged, this),
];
}
}
uiSourceCode() {
return this.uiSourceCodeInternal;
}
setUISourceCode(uiSourceCode) {
const loaded = uiSourceCode.contentLoaded() ? Promise.resolve() : uiSourceCode.requestContent();
const startUISourceCode = this.uiSourceCodeInternal;
loaded.then(async () => {
if (this.uiSourceCodeInternal !== startUISourceCode) {
return;
}
this.unloadUISourceCode();
this.uiSourceCodeInternal = uiSourceCode;
if (uiSourceCode.workingCopy() !== this.textEditor.state.doc.toString()) {
await this.setDeferredContent(Promise.resolve(uiSourceCode.workingCopyContent()));
}
else {
this.reloadPlugins();
}
this.initializeUISourceCode();
}, console.error);
}
unloadUISourceCode() {
Common.EventTarget.removeEventListeners(this.messageAndDecorationListeners);
Common.EventTarget.removeEventListeners(this.uiSourceCodeEventListeners);
this.uiSourceCodeInternal.removeWorkingCopyGetter();
Persistence.Persistence.PersistenceImpl.instance().unsubscribeFromBindingEvent(this.uiSourceCodeInternal, this.boundOnBindingChanged);
}
initializeUISourceCode() {
this.uiSourceCodeEventListeners = [
this.uiSourceCodeInternal.addEventListener(Workspace.UISourceCode.Events.WorkingCopyChanged, this.onWorkingCopyChanged, this),
this.uiSourceCodeInternal.addEventListener(Workspace.UISourceCode.Events.WorkingCopyCommitted, this.onWorkingCopyCommitted, this),
this.uiSourceCodeInternal.addEventListener(Workspace.UISourceCode.Events.TitleChanged, this.onTitleChanged, this),
];
Persistence.Persistence.PersistenceImpl.instance().subscribeForBindingEvent(this.uiSourceCodeInternal, this.boundOnBindingChanged);
this.installMessageAndDecorationListeners();
this.updateStyle();
const canPrettyPrint = FormatterActions.FORMATTABLE_MEDIA_TYPES.includes(this.contentType) &&
!this.uiSourceCodeInternal.project().canSetFileContent() &&
Persistence.Persistence.PersistenceImpl.instance().binding(this.uiSourceCodeInternal) === null;
const autoPrettyPrint = Root.Runtime.experiments.isEnabled('sourcesPrettyPrint') &&
!this.uiSourceCodeInternal.contentType().isFromSourceMap();
this.setCanPrettyPrint(canPrettyPrint, autoPrettyPrint);
}
wasShown() {
super.wasShown();
this.setEditable(this.canEditSourceInternal());
}
willHide() {
for (const plugin of this.plugins) {
plugin.willHide();
}
super.willHide();
UI.Context.Context.instance().setFlavor(UISourceCodeFrame, null);
this.uiSourceCodeInternal.removeWorkingCopyGetter();
}
getContentType() {
const binding = Persistence.Persistence.PersistenceImpl.instance().binding(this.uiSourceCodeInternal);
const mimeType = binding ? binding.network.mimeType() : this.uiSourceCodeInternal.mimeType();
return Common.ResourceType.ResourceType.simplifyContentType(mimeType);
}
canEditSourceInternal() {
if (this.hasLoadError()) {
return false;
}
if (this.uiSourceCodeInternal.editDisabled()) {
return false;
}
if (this.uiSourceCodeInternal.mimeType() === 'application/wasm') {
return false;
}
if (Persistence.Persistence.PersistenceImpl.instance().binding(this.uiSourceCodeInternal)) {
return true;
}
if (this.uiSourceCodeInternal.project().canSetFileContent()) {
return true;
}
if (this.uiSourceCodeInternal.project().isServiceProject()) {
return false;
}
if (this.uiSourceCodeInternal.project().type() === Workspace.Workspace.projectTypes.Network &&
Persistence.NetworkPersistenceManager.NetworkPersistenceManager.instance().active()) {
return true;
}
// Because live edit fails on large whitespace changes, pretty printed scripts are not editable.
if (this.pretty && this.uiSourceCodeInternal.contentType().hasScripts()) {
return false;
}
return this.uiSourceCodeInternal.contentType() !== Common.ResourceType.resourceTypes.Document;
}
onNetworkPersistenceChanged() {
this.setEditable(this.canEditSourceInternal());
}
commitEditing() {
if (!this.uiSourceCodeInternal.isDirty()) {
return;
}
this.muteSourceCodeEvents = true;
this.uiSourceCodeInternal.commitWorkingCopy();
this.muteSourceCodeEvents = false;
}
async setContent(content) {
this.disposePlugins();
this.loadPlugins();
await super.setContent(content);
for (const plugin of this.plugins) {
plugin.editorInitialized(this.textEditor);
}
this.#recordSourcesPanelOpenedMetrics();
Common.EventTarget.fireEvent('source-file-loaded', this.uiSourceCodeInternal.displayName(true));
}
createMessage(origin) {
const { lineNumber, columnNumber } = this.uiLocationToEditorLocation(origin.lineNumber(), origin.columnNumber());
return new RowMessage(origin, lineNumber, columnNumber);
}
allMessages() {
const origins = this.persistenceBinding !== null ?
[...this.persistenceBinding.network.messages(), ...this.persistenceBinding.fileSystem.messages()] :
[...this.uiSourceCodeInternal.messages()];
return origins.map(origin => this.createMessage(origin));
}
onTextChanged() {
const wasPretty = this.pretty;
super.onTextChanged();
this.errorPopoverHelper.hidePopover();
SourcesPanel.instance().updateLastModificationTime();
this.muteSourceCodeEvents = true;
if (this.isClean()) {
this.uiSourceCodeInternal.resetWorkingCopy();
}
else {
this.uiSourceCodeInternal.setWorkingCopyGetter(() => this.textEditor.state.sliceDoc());
}
this.muteSourceCodeEvents = false;
if (wasPretty !== this.pretty) {
this.updateStyle();
this.reloadPlugins();
}
}
onWorkingCopyChanged() {
if (this.muteSourceCodeEvents) {
return;
}
this.maybeSetContent(this.uiSourceCodeInternal.workingCopyContent());
}
onWorkingCopyCommitted() {
if (!this.muteSourceCodeEvents) {
this.maybeSetContent(this.uiSourceCode().workingCopyContent());
}
this.contentCommitted();
this.updateStyle();
}
reloadPlugins() {
this.disposePlugins();
this.loadPlugins();
const editor = this.textEditor;
editor.dispatch({ effects: pluginCompartment.reconfigure(this.plugins.map(plugin => plugin.editorExtension())) });
for (const plugin of this.plugins) {
plugin.editorInitialized(editor);
}
}
onTitleChanged() {
this.updateLanguageMode('').then(() => this.reloadPlugins(), console.error);
}
loadPlugins() {
const binding = Persistence.Persistence.PersistenceImpl.instance().binding(this.uiSourceCodeInternal);
const pluginUISourceCode = binding ? binding.network : this.uiSourceCodeInternal;
for (const pluginType of sourceFramePlugins()) {
if (pluginType.accepts(pluginUISourceCode)) {
this.plugins.push(new pluginType(pluginUISourceCode, this));
}
}
this.dispatchEventToListeners(Events.ToolbarItemsChanged);
}
disposePlugins() {
for (const plugin of this.plugins) {
plugin.dispose();
}
this.plugins = [];
}
onBindingChanged() {
const binding = Persistence.Persistence.PersistenceImpl.instance().binding(this.uiSourceCodeInternal);
if (binding === this.persistenceBinding) {
return;
}
this.unloadUISourceCode();
this.persistenceBinding = binding;
this.initializeUISourceCode();
this.reloadMessages();
this.reloadPlugins();
}
reloadMessages() {
const messages = this.allMessages();
const { editor } = this.textEditor;
editor.dispatch({ effects: setRowMessages.of(RowMessages.create(messages)) });
}
updateStyle() {
this.setEditable(this.canEditSourceInternal());
}
maybeSetContent(content) {
if (this.textEditor.state.doc.toString() !== content.content) {
void this.setDeferredContent(Promise.resolve(content));
}
}
populateTextAreaContextMenu(contextMenu, lineNumber, columnNumber) {
super.populateTextAreaContextMenu(contextMenu, lineNumber, columnNumber);
contextMenu.appendApplicableItems(this.uiSourceCodeInternal);
const location = this.editorLocationToUILocation(lineNumber, columnNumber);
contextMenu.appendApplicableItems(new Workspace.UISourceCode.UILocation(this.uiSourceCodeInternal, location.lineNumber, location.columnNumber));
for (const plugin of this.plugins) {
plugin.populateTextAreaContextMenu(contextMenu, lineNumber, columnNumber);
}
}
populateLineGutterContextMenu(contextMenu, lineNumber) {
super.populateLineGutterContextMenu(contextMenu, lineNumber);
for (const plugin of this.plugins) {
plugin.populateLineGutterContextMenu(contextMenu, lineNumber);
}
}
dispose() {
this.errorPopoverHelper.dispose();
this.disposePlugins();
this.unloadUISourceCode();
this.textEditor.editor.destroy();
this.detach();
Common.Settings.Settings.instance()
.moduleSetting('persistenceNetworkOverridesEnabled')
.removeChangeListener(this.onNetworkPersistenceChanged, this);
}
onMessageAdded(event) {
const { editor } = this.textEditor, shownMessages = editor.state.field(showRowMessages, false);
if (shownMessages) {
const message = this.createMessage(event.data);
editor.dispatch({ effects: setRowMessages.of(shownMessages.messages.add(message)) });
}
}
onMessageRemoved(event) {
const { editor } = this.textEditor, shownMessages = editor.state.field(showRowMessages, false);
if (shownMessages) {
const message = this.createMessage(event.data);
editor.dispatch({ effects: setRowMessages.of(shownMessages.messages.remove(message)) });
}
}
onDecorationChanged(event) {
for (const plugin of this.plugins) {
plugin.decorationChanged(event.data, this.textEditor);
}
}
async toolbarItems() {
const leftToolbarItems = await super.toolbarItems();
const rightToolbarItems = [];
for (const plugin of this.plugins) {
leftToolbarItems.push(...plugin.leftToolbarItems());
rightToolbarItems.push(...plugin.rightToolbarItems());
}
if (!rightToolbarItems.length) {
return leftToolbarItems;
}
return [...leftToolbarItems, new UI.Toolbar.ToolbarSeparator(true), ...rightToolbarItems];
}
getErrorPopoverContent(event) {
const mouseEvent = event;
const eventTarget = event.target;
const anchorElement = eventTarget.enclosingNodeOrSelfWithClass('cm-messageIcon-error') ||
eventTarget.enclosingNodeOrSelfWithClass('cm-messageIcon-issue');
if (!anchorElement) {
return null;
}
const messageField = this.textEditor.state.field(showRowMessages, false);
if (!messageField || messageField.messages.rows.length === 0) {
return null;
}
const { editor } = this.textEditor;
const position = editor.posAtCoords(mouseEvent);
if (position === null) {
return null;
}
const line = editor.state.doc.lineAt(position);
if (position !== line.to) {
return null;
}
const row = messageField.messages.rows.find(row => row[0].lineNumber() === line.number - 1);
if (!row) {
return null;
}
const issues = anchorElement.classList.contains('cm-messageIcon-issue');
const messages = row.filter(msg => (msg.level() === Workspace.UISourceCode.Message.Level.Issue) === issues);
if (!messages.length) {
return null;
}
const anchor = anchorElement ? anchorElement.boxInWindow() : new AnchorBox(mouseEvent.clientX, mouseEvent.clientY, 1, 1);
const counts = countDuplicates(messages);
const element = document.createElement('div');
element.classList.add('text-editor-messages-description-container');
for (let i = 0; i < messages.length; i++) {
if (counts[i]) {
element.appendChild(renderMessage(messages[i], counts[i]));
}
}
return {
box: anchor,
hide() { },
show: async (popover) => {
popover.contentElement.append(element);
return true;
},
};
}
/**
* Only records metrics once per UISourceCodeFrame instance and must only be
* called once the content of the UISourceCode is available.
*/
#recordSourcesPanelOpenedMetrics() {
if (this.#sourcesPanelOpenedMetricsRecorded) {
return;
}
this.#sourcesPanelOpenedMetricsRecorded = true;
const mimeType = Common.ResourceType.ResourceType.mimeFromURL(this.uiSourceCodeInternal.url());
const mediaType = Common.ResourceType.ResourceType.mediaTypeForMetrics(mimeType ?? '', this.uiSourceCodeInternal.contentType().isFromSourceMap(), TextUtils.TextUtils.isMinified(this.uiSourceCodeInternal.content()));
Host.userMetrics.sourcesPanelFileOpened(mediaType);
}
}
function getIconDataForLevel(level) {
if (level === Workspace.UISourceCode.Message.Level.Error) {
return { color: 'var(--icon-error)', width: '16px', height: '14px', iconName: 'cross-circle-filled' };
}
if (level === Workspace.UISourceCode.Message.Level.Warning) {
return { color: 'var(--icon-warning)', width: '18px', height: '14px', iconName: 'warning-filled' };
}
if (level === Workspace.UISourceCode.Message.Level.Issue) {
return { color: 'var(--icon-warning)', width: '17px', height: '14px', iconName: 'issue-exclamation-filled' };
}
return { color: 'var(--icon-error)', width: '16px', height: '14px', iconName: 'cross-circle-filled' };
}
function getBubbleTypePerLevel(level) {
switch (level) {
case Workspace.UISourceCode.Message.Level.Error:
return 'error';
case Workspace.UISourceCode.Message.Level.Warning:
return 'warning';
case Workspace.UISourceCode.Message.Level.Issue:
return 'warning';
}
}
function messageLevelComparator(a, b) {
const messageLevelPriority = {
[Workspace.UISourceCode.Message.Level.Issue]: 2,
[Workspace.UISourceCode.Message.Level.Warning]: 3,
[Workspace.UISourceCode.Message.Level.Error]: 4,
};
return messageLevelPriority[a.level()] - messageLevelPriority[b.level()];
}
function getIconDataForMessage(message) {
if (message.origin instanceof IssuesManager.SourceFrameIssuesManager.IssueMessage) {
return {
...IssueCounter.IssueCounter.getIssueKindIconData(message.origin.getIssueKind()),
width: '12px',
height: '12px',
};
}
return getIconDataForLevel(message.level());
}
// TODO(crbug.com/1167717): Make this a const enum again
// eslint-disable-next-line rulesdir/const_enum
export var Events;
(function (Events) {
Events["ToolbarItemsChanged"] = "ToolbarItemsChanged";
})(Events || (Events = {}));
const pluginCompartment = new CodeMirror.Compartment();
// Row message management and display logic. The frame manages a
// collection of messages, organized by line (row), as a wavy
// underline starting at the start of the first message, up to the end
// of the line, with icons indicating the message severity and content
// at the end of the line.
class RowMessage {
origin;
#lineNumber;
#columnNumber;
constructor(origin, lineNumber, columnNumber) {
this.origin = origin;
this.#lineNumber = lineNumber;
this.#columnNumber = columnNumber;
}
level() {
return this.origin.level();
}
text() {
return this.origin.text();
}
clickHandler() {
return this.origin.clickHandler();
}
lineNumber() {
return this.#lineNumber;
}
columnNumber() {
return this.#columnNumber;
}
isEqual(that) {
return this.origin.isEqual(that.origin);
}
}
function addMessage(rows, message) {
const lineNumber = message.lineNumber();
let i = 0;
for (; i < rows.length; i++) {
const diff = rows[i][0].lineNumber() - lineNumber;
if (diff === 0) {
rows[i] = rows[i].concat(message);
return rows;
}
if (diff > 0) {
break;
}
}
rows.splice(i, 0, [message]);
return rows;
}
function removeMessage(rows, message) {
for (let i = 0; i < rows.length; i++) {
if (rows[i][0].lineNumber() === message.lineNumber()) {
const remaining = rows[i].filter(m => !m.isEqual(message));
if (remaining.length) {
rows[i] = remaining;
}
else {
rows.splice(i, 1);
}
break;
}
}
}
class RowMessages {
rows;
constructor(rows) {
this.rows = rows;
}
static create(messages) {
const rows = [];
for (const message of messages) {
addMessage(rows, message);
}
return new RowMessages(rows);
}
remove(message) {
const rows = this.rows.slice();
removeMessage(rows, message);
return new RowMessages(rows);
}
add(message) {
return new RowMessages(addMessage(this.rows.slice(), message));
}
}
const setRowMessages = CodeMirror.StateEffect.define();
const underlineMark = CodeMirror.Decoration.mark({ class: 'cm-waveUnderline' });
// The widget shown at the end of a message annotation.
class MessageWidget extends CodeMirror.WidgetType {
messages;
constructor(messages) {
super();
this.messages = messages;
}
eq(other) {
return other.messages === this.messages;
}
toDOM() {
const wrap = document.createElement('span');
wrap.classList.add('cm-messageIcon');
const nonIssues = this.messages.filter(msg => msg.level() !== Workspace.UISourceCode.Message.Level.Issue);
if (nonIssues.length) {
const maxIssue = nonIssues.sort(messageLevelComparator)[nonIssues.length - 1];
const errorIcon = wrap.appendChild(new IconButton.Icon.Icon());
errorIcon.data = getIconDataForLevel(maxIssue.level());
errorIcon.classList.add('cm-messageIcon-error');
}
const issue = this.messages.find(m => m.level() === Workspace.UISourceCode.Message.Level.Issue);
if (issue) {
const issueIcon = wrap.appendChild(new IconButton.Icon.Icon());
issueIcon.data = getIconDataForLevel(Workspace.UISourceCode.Message.Level.Issue);
issueIcon.classList.add('cm-messageIcon-issue');
issueIcon.addEventListener('click', () => (issue.clickHandler() || Math.min)());
}
return wrap;
}
ignoreEvents() {
return true;
}
}
class RowMessageDecorations {
messages;
decorations;
constructor(messages, decorations) {
this.messages = messages;
this.decorations = decorations;
}
static create(messages, doc) {
const builder = new CodeMirror.RangeSetBuilder();
for (const row of messages.rows) {
const line = doc.line(row[0].lineNumber() + 1);
const minCol = row.reduce((col, msg) => Math.min(col, msg.columnNumber() || 0), line.length);
if (minCol < line.length) {
builder.add(line.from + minCol, line.to, underlineMark);
}
builder.add(line.to, line.to, CodeMirror.Decoration.widget({ side: 1, widget: new MessageWidget(row) }));
}
return new RowMessageDecorations(messages, builder.finish());
}
apply(tr) {
let result = this;
if (tr.docChanged) {
result = new RowMessageDecorations(this.messages, this.decorations.map(tr.changes));
}
for (const effect of tr.effects) {
if (effect.is(setRowMessages)) {
result = RowMessageDecorations.create(effect.value, tr.state.doc);
}
}
return result;
}
}
const showRowMessages = CodeMirror.StateField.define({
create(state) {
return RowMessageDecorations.create(new RowMessages([]), state.doc);
},
update(value, tr) {
return value.apply(tr);
},
provide: field => CodeMirror.Prec.lowest(CodeMirror.EditorView.decorations.from(field, value => value.decorations)),
});
function countDuplicates(messages) {
const counts = [];
for (let i = 0; i < messages.length; i++) {
counts[i] = 0;
for (let j = 0; j <= i; j++) {
if (messages[j].isEqual(messages[i])) {
counts[j]++;
break;
}
}
}
return counts;
}
function renderMessage(message, count) {
const element = document.createElement('div');
element.classList.add('text-editor-row-message');
element.style.display = 'flex';
element.style.alignItems = 'center';
element.style.gap = '4px';
if (count === 1) {
const icon = element.appendChild(new IconButton.Icon.Icon());
icon.data = getIconDataForMessage(message);
icon.classList.add('text-editor-row-message-icon');
icon.addEventListener('click', () => (message.clickHandler() || Math.min)());
}
else {
const repeatCountElement = document.createElement('span', { is: 'dt-small-bubble' });
repeatCountElement.textContent = String(count);
repeatCountElement.classList.add('text-editor-row-message-repeat-count');
repeatCountElement.style.flexShrink = '0';
element.appendChild(repeatCountElement);
repeatCountElement.type = getBubbleTypePerLevel(message.level());
}
const linesContainer = element.createChild('div');
for (const line of message.text().split('\n')) {
linesContainer.createChild('div').textContent = line;
}
return element;
}
const rowMessageTheme = CodeMirror.EditorView.baseTheme({
'.cm-tooltip-message': {
padding: '4px',
},
'.cm-waveUnderline': {
backgroundImage: 'var(--image-file-errorWave)',
backgroundRepeat: 'repeat-x',
backgroundPosition: 'bottom',
paddingBottom: '1px',
},
'.cm-messageIcon': {
cursor: 'pointer',
'& > *': {
verticalAlign: 'text-bottom',
marginLeft: '2px',
},
},
'.cm-messageIcon-issue, .cm-messageIcon-error': {
marginTop: '-1px',
marginBottom: '-1px',
},
});
function rowMessages(initialMessages) {
return [
showRowMessages.init((state) => RowMessageDecorations.create(RowMessages.create(initialMessages), state.doc)),
rowMessageTheme,
];
}
//# sourceMappingURL=UISourceCodeFrame.js.map