chrome-devtools-frontend 1.0.940255 → 1.0.942095
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/config/gni/all_devtools_files.gni +0 -13
- package/config/gni/devtools_grd_files.gni +13 -13
- package/config/gni/devtools_image_files.gni +1 -2
- package/front_end/.eslintrc.js +1 -0
- package/front_end/Images/src/feedback_button_icon.svg +3 -0
- package/front_end/Tests.js +15 -0
- package/front_end/core/common/Color.ts +5 -0
- package/front_end/core/i18n/locales/en-US.json +39 -30
- package/front_end/core/i18n/locales/en-XL.json +39 -30
- package/front_end/core/sdk/DOMDebuggerModel.ts +18 -1
- package/front_end/core/sdk/sdk-meta.ts +17 -3
- package/front_end/entrypoints/devtools_app/devtools_app.json +1 -7
- package/front_end/entrypoints/main/MainImpl.ts +26 -0
- package/front_end/entrypoints/shell/shell.js +0 -11
- package/front_end/entrypoints/shell/shell.json +0 -1
- package/front_end/entrypoints/worker_app/worker_app.json +0 -4
- package/front_end/generated/InspectorBackendCommands.js +1 -0
- package/front_end/generated/protocol.d.ts +2 -0
- package/front_end/global_typings/global_defs.d.ts +5 -0
- package/front_end/legacy_test_runner/console_test_runner/console_test_runner.js +14 -2
- package/front_end/legacy_test_runner/legacy_test_runner.ts +10 -1
- package/front_end/legacy_test_runner/test_runner/TestRunner.js +9 -0
- package/front_end/models/formatter/SourceFormatter.ts +0 -10
- package/front_end/models/workspace/UISourceCode.ts +9 -42
- package/front_end/panels/animation/AnimationTimeline.ts +3 -3
- package/front_end/panels/application/ApplicationPanelSidebar.ts +3 -3
- package/front_end/panels/application/application-meta.ts +0 -3
- package/front_end/panels/console/ConsolePinPane.ts +21 -26
- package/front_end/panels/coverage/CoverageDecorationManager.ts +4 -5
- package/front_end/panels/coverage/CoverageView.ts +2 -105
- package/front_end/panels/css_overview/components/CSSOverviewStartView.ts +11 -56
- package/front_end/panels/css_overview/components/cssOverviewStartView.css +1 -8
- package/front_end/panels/elements/ElementsTreeElement.ts +4 -9
- package/front_end/panels/elements/components/adornerSettingsPane.css +0 -4
- package/front_end/panels/emulation/emulation-meta.ts +2 -2
- package/front_end/panels/issues/IssueKindView.ts +22 -4
- package/front_end/panels/issues/issues-meta.ts +0 -2
- package/front_end/panels/layers/module.json +0 -1
- package/front_end/panels/lighthouse/lighthouseStartView.css +4 -0
- package/front_end/panels/media/media-meta.ts +0 -3
- package/front_end/panels/network/NetworkLogView.ts +3 -0
- package/front_end/panels/network/ResourceWebSocketFrameView.ts +2 -1
- package/front_end/panels/network/network-meta.ts +0 -3
- package/front_end/panels/profiler/module.json +1 -2
- package/front_end/panels/security/security-meta.ts +0 -3
- package/front_end/panels/sources/BreakpointEditDialog.ts +16 -30
- package/front_end/panels/sources/CSSPlugin.ts +310 -331
- package/front_end/panels/sources/CallStackSidebarPane.ts +28 -34
- package/front_end/panels/sources/CoveragePlugin.ts +121 -6
- package/front_end/panels/sources/DebuggerPlugin.ts +1166 -1243
- package/front_end/panels/sources/EditingLocationHistoryManager.ts +71 -101
- package/front_end/panels/sources/GoToLineQuickOpen.ts +4 -3
- package/front_end/panels/sources/InplaceFormatterEditorAction.ts +3 -3
- package/front_end/panels/sources/JavaScriptCompilerPlugin.ts +26 -23
- package/front_end/panels/sources/Plugin.ts +20 -4
- package/front_end/panels/sources/ProfilePlugin.ts +185 -0
- package/front_end/panels/sources/ScriptFormatterEditorAction.ts +3 -3
- package/front_end/panels/sources/ScriptOriginPlugin.ts +0 -10
- package/front_end/panels/sources/SnippetsPlugin.ts +1 -10
- package/front_end/panels/sources/SourcesPanel.ts +15 -10
- package/front_end/panels/sources/SourcesView.ts +10 -8
- package/front_end/panels/sources/TabbedEditorContainer.ts +31 -27
- package/front_end/panels/sources/UISourceCodeFrame.ts +335 -470
- package/front_end/panels/sources/WatchExpressionsSidebarPane.ts +3 -2
- package/front_end/panels/sources/sources-legacy.ts +0 -6
- package/front_end/panels/sources/sources.ts +0 -2
- package/front_end/panels/timeline/module.json +0 -2
- package/front_end/third_party/codemirror.next/bundle.ts +9 -13
- package/front_end/third_party/codemirror.next/chunk/codemirror.js +1 -1
- package/front_end/third_party/codemirror.next/chunk/javascript.js +2 -2
- package/front_end/third_party/codemirror.next/chunk/markdown.js +2 -6
- package/front_end/third_party/codemirror.next/chunk/php.js +2 -6
- package/front_end/third_party/codemirror.next/chunk/python.js +1 -1
- package/front_end/third_party/codemirror.next/chunk/wast.js +1 -1
- package/front_end/third_party/codemirror.next/chunk/xml.js +2 -2
- package/front_end/third_party/codemirror.next/codemirror.next.d.ts +279 -198
- package/front_end/third_party/codemirror.next/codemirror.next.js +1 -1
- package/front_end/third_party/codemirror.next/package.json +13 -11
- package/front_end/ui/components/code_highlighter/CodeHighlighter.ts +60 -68
- package/front_end/ui/components/data_grid/dataGrid.css +12 -10
- package/front_end/ui/components/docs/css_overview/start_view.html +25 -0
- package/front_end/ui/components/docs/css_overview/start_view.ts +14 -0
- package/front_end/ui/components/docs/panel_feedback/basic.html +25 -0
- package/front_end/ui/components/docs/panel_feedback/basic.ts +20 -0
- package/front_end/ui/components/docs/panel_feedback/button.html +25 -0
- package/front_end/ui/components/docs/panel_feedback/button.ts +18 -0
- package/front_end/ui/components/helpers/get-stylesheet.ts +1 -0
- package/front_end/ui/components/panel_feedback/FeedbackButton.ts +67 -0
- package/front_end/ui/components/panel_feedback/PanelFeedback.ts +100 -0
- package/front_end/ui/components/panel_feedback/panelFeedback.css +76 -0
- package/front_end/ui/components/panel_feedback/panel_feedback.ts +6 -0
- package/front_end/ui/components/report_view/reportValue.css +1 -0
- package/front_end/ui/components/text_editor/TextEditor.ts +79 -36
- package/front_end/ui/components/text_editor/config.ts +42 -26
- package/front_end/ui/components/text_editor/javascript.ts +2 -3
- package/front_end/ui/components/text_editor/position.ts +17 -0
- package/front_end/ui/components/text_editor/text_editor.ts +1 -0
- package/front_end/ui/components/text_editor/theme.ts +5 -1
- package/front_end/ui/legacy/Infobar.ts +2 -6
- package/front_end/ui/legacy/ShortcutRegistry.ts +11 -7
- package/front_end/ui/legacy/Widget.ts +1 -1
- package/front_end/ui/legacy/components/perf_ui/ChartViewport.ts +2 -1
- package/front_end/ui/legacy/components/perf_ui/FilmStripView.ts +3 -1
- package/front_end/ui/legacy/components/perf_ui/FlameChart.ts +2 -1
- package/front_end/ui/legacy/components/perf_ui/LineLevelProfile.ts +35 -131
- package/front_end/ui/legacy/components/perf_ui/OverviewGrid.ts +2 -1
- package/front_end/ui/legacy/components/perf_ui/TimelineGrid.ts +3 -1
- package/front_end/ui/legacy/components/perf_ui/TimelineOverviewPane.ts +2 -1
- package/front_end/ui/legacy/components/source_frame/BinaryResourceViewFactory.ts +3 -6
- package/front_end/ui/legacy/components/source_frame/ResourceSourceFrame.ts +18 -14
- package/front_end/ui/legacy/components/source_frame/SourceFrame.ts +502 -252
- package/front_end/ui/legacy/components/source_frame/source_frame-legacy.ts +0 -11
- package/front_end/ui/legacy/components/source_frame/source_frame.ts +0 -2
- package/front_end/ui/legacy/components/text_editor/cmdevtools.css +3 -1
- package/front_end/ui/legacy/radioButton.css +1 -13
- package/front_end/ui/legacy/softContextMenu.css +1 -0
- package/front_end/ui/legacy/themeColors.css +36 -0
- package/front_end/ui/legacy/utils/append-style.ts +9 -4
- package/package.json +1 -1
- package/scripts/build/generate_css_js_files.js +23 -9
- package/scripts/build/ninja/generate_css.gni +10 -1
- package/scripts/eslint_rules/lib/check_css_import.js +2 -2
- package/scripts/eslint_rules/tests/check_css_import_test.js +12 -0
- package/front_end/Images/radioDot-dark-theme.png +0 -0
- package/front_end/Images/radioDot.png +0 -0
- package/front_end/panels/application/module.json +0 -7
- package/front_end/panels/issues/module.json +0 -6
- package/front_end/panels/layer_viewer/module.json +0 -6
- package/front_end/panels/media/module.json +0 -6
- package/front_end/panels/network/module.json +0 -6
- package/front_end/panels/security/module.json +0 -5
- package/front_end/ui/legacy/components/perf_ui/module.json +0 -13
- package/front_end/ui/legacy/components/source_frame/SourcesTextEditor.ts +0 -1030
|
@@ -33,23 +33,38 @@ import * as Platform from '../../core/platform/platform.js';
|
|
|
33
33
|
import * as Root from '../../core/root/root.js';
|
|
34
34
|
import * as IssuesManager from '../../models/issues_manager/issues_manager.js';
|
|
35
35
|
import * as Persistence from '../../models/persistence/persistence.js';
|
|
36
|
-
import * as TextUtils from '../../models/text_utils/text_utils.js';
|
|
36
|
+
import type * as TextUtils from '../../models/text_utils/text_utils.js';
|
|
37
37
|
import * as Workspace from '../../models/workspace/workspace.js';
|
|
38
|
+
import * as CodeMirror from '../../third_party/codemirror.next/codemirror.next.js';
|
|
38
39
|
import * as IconButton from '../../ui/components/icon_button/icon_button.js';
|
|
39
40
|
import * as IssueCounter from '../../ui/components/issue_counter/issue_counter.js';
|
|
40
41
|
import * as SourceFrame from '../../ui/legacy/components/source_frame/source_frame.js';
|
|
41
|
-
import type * as TextEditor from '../../ui/legacy/components/text_editor/text_editor.js';
|
|
42
42
|
import * as UI from '../../ui/legacy/legacy.js';
|
|
43
43
|
|
|
44
44
|
import {CoveragePlugin} from './CoveragePlugin.js';
|
|
45
45
|
import {CSSPlugin} from './CSSPlugin.js';
|
|
46
46
|
import {DebuggerPlugin} from './DebuggerPlugin.js';
|
|
47
|
+
import {MemoryProfilePlugin, PerformanceProfilePlugin} from './ProfilePlugin.js';
|
|
47
48
|
import {JavaScriptCompilerPlugin} from './JavaScriptCompilerPlugin.js';
|
|
48
49
|
import type {Plugin} from './Plugin.js';
|
|
49
50
|
import {ScriptOriginPlugin} from './ScriptOriginPlugin.js';
|
|
50
51
|
import {SnippetsPlugin} from './SnippetsPlugin.js';
|
|
51
52
|
import {SourcesPanel} from './SourcesPanel.js';
|
|
52
53
|
|
|
54
|
+
function sourceFramePlugins(): (typeof Plugin)[] {
|
|
55
|
+
// The order of these plugins matters for toolbar items
|
|
56
|
+
return [
|
|
57
|
+
CSSPlugin,
|
|
58
|
+
DebuggerPlugin,
|
|
59
|
+
JavaScriptCompilerPlugin,
|
|
60
|
+
SnippetsPlugin,
|
|
61
|
+
ScriptOriginPlugin,
|
|
62
|
+
CoveragePlugin,
|
|
63
|
+
MemoryProfilePlugin,
|
|
64
|
+
PerformanceProfilePlugin,
|
|
65
|
+
];
|
|
66
|
+
}
|
|
67
|
+
|
|
53
68
|
export class UISourceCodeFrame extends
|
|
54
69
|
Common.ObjectWrapper.eventMixin<EventTypes, typeof SourceFrame.SourceFrame.SourceFrameImpl>(
|
|
55
70
|
SourceFrame.SourceFrame.SourceFrameImpl) {
|
|
@@ -57,13 +72,12 @@ export class UISourceCodeFrame extends
|
|
|
57
72
|
private muteSourceCodeEvents: boolean;
|
|
58
73
|
private isSettingContent: boolean;
|
|
59
74
|
private persistenceBinding: Persistence.Persistence.PersistenceBinding|null;
|
|
60
|
-
private readonly rowMessageBuckets: Map<number, RowMessageBucket>;
|
|
61
|
-
private readonly typeDecorationsPending: Set<string>;
|
|
62
75
|
private uiSourceCodeEventListeners: Common.EventTarget.EventDescriptor[];
|
|
63
76
|
private messageAndDecorationListeners: Common.EventTarget.EventDescriptor[];
|
|
64
77
|
private readonly boundOnBindingChanged: () => void;
|
|
78
|
+
private plugins: Plugin[] = [];
|
|
79
|
+
private pluginsLoaded = false;
|
|
65
80
|
private readonly errorPopoverHelper: UI.PopoverHelper.PopoverHelper;
|
|
66
|
-
private plugins: Plugin[];
|
|
67
81
|
|
|
68
82
|
constructor(uiSourceCode: Workspace.UISourceCode.UISourceCode) {
|
|
69
83
|
super(workingCopy);
|
|
@@ -74,31 +88,21 @@ export class UISourceCodeFrame extends
|
|
|
74
88
|
|
|
75
89
|
this.persistenceBinding = Persistence.Persistence.PersistenceImpl.instance().binding(uiSourceCode);
|
|
76
90
|
|
|
77
|
-
this.rowMessageBuckets = new Map();
|
|
78
|
-
this.typeDecorationsPending = new Set();
|
|
79
|
-
|
|
80
91
|
this.uiSourceCodeEventListeners = [];
|
|
81
92
|
this.messageAndDecorationListeners = [];
|
|
82
93
|
|
|
83
94
|
this.boundOnBindingChanged = this.onBindingChanged.bind(this);
|
|
84
95
|
|
|
85
|
-
this.textEditor.addEventListener(
|
|
86
|
-
SourceFrame.SourcesTextEditor.Events.EditorBlurred,
|
|
87
|
-
() => UI.Context.Context.instance().setFlavor(UISourceCodeFrame, null));
|
|
88
|
-
this.textEditor.addEventListener(
|
|
89
|
-
SourceFrame.SourcesTextEditor.Events.EditorFocused,
|
|
90
|
-
() => UI.Context.Context.instance().setFlavor(UISourceCodeFrame, this));
|
|
91
96
|
Common.Settings.Settings.instance()
|
|
92
97
|
.moduleSetting('persistenceNetworkOverridesEnabled')
|
|
93
98
|
.addChangeListener(this.onNetworkPersistenceChanged, this);
|
|
94
99
|
|
|
95
|
-
this.errorPopoverHelper =
|
|
100
|
+
this.errorPopoverHelper =
|
|
101
|
+
new UI.PopoverHelper.PopoverHelper(this.textEditor.editor.contentDOM, this.getErrorPopoverContent.bind(this));
|
|
96
102
|
this.errorPopoverHelper.setHasPadding(true);
|
|
97
103
|
|
|
98
104
|
this.errorPopoverHelper.setTimeout(100, 100);
|
|
99
105
|
|
|
100
|
-
this.plugins = [];
|
|
101
|
-
|
|
102
106
|
this.initializeUISourceCode();
|
|
103
107
|
|
|
104
108
|
function workingCopy(): Promise<TextUtils.ContentProvider.DeferredContent> {
|
|
@@ -109,6 +113,24 @@ export class UISourceCodeFrame extends
|
|
|
109
113
|
}
|
|
110
114
|
}
|
|
111
115
|
|
|
116
|
+
protected editorConfiguration(doc: string): CodeMirror.Extension {
|
|
117
|
+
return [
|
|
118
|
+
super.editorConfiguration(doc),
|
|
119
|
+
rowMessages([...this.allMessages()]),
|
|
120
|
+
pluginCompartment.of(this.plugins.map(plugin => plugin.editorExtension())),
|
|
121
|
+
];
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
protected onFocus(): void {
|
|
125
|
+
super.onFocus();
|
|
126
|
+
UI.Context.Context.instance().setFlavor(UISourceCodeFrame, this);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
protected onBlur(): void {
|
|
130
|
+
super.onBlur();
|
|
131
|
+
UI.Context.Context.instance().setFlavor(UISourceCodeFrame, null);
|
|
132
|
+
}
|
|
133
|
+
|
|
112
134
|
private installMessageAndDecorationListeners(): void {
|
|
113
135
|
if (this.persistenceBinding) {
|
|
114
136
|
const networkSourceCode = this.persistenceBinding.network;
|
|
@@ -117,9 +139,7 @@ export class UISourceCodeFrame extends
|
|
|
117
139
|
networkSourceCode.addEventListener(Workspace.UISourceCode.Events.MessageAdded, this.onMessageAdded, this),
|
|
118
140
|
networkSourceCode.addEventListener(Workspace.UISourceCode.Events.MessageRemoved, this.onMessageRemoved, this),
|
|
119
141
|
networkSourceCode.addEventListener(
|
|
120
|
-
Workspace.UISourceCode.Events.
|
|
121
|
-
networkSourceCode.addEventListener(
|
|
122
|
-
Workspace.UISourceCode.Events.LineDecorationRemoved, this.onLineDecorationRemoved, this),
|
|
142
|
+
Workspace.UISourceCode.Events.DecorationChanged, this.onDecorationChanged, this),
|
|
123
143
|
|
|
124
144
|
fileSystemSourceCode.addEventListener(Workspace.UISourceCode.Events.MessageAdded, this.onMessageAdded, this),
|
|
125
145
|
fileSystemSourceCode.addEventListener(
|
|
@@ -132,9 +152,7 @@ export class UISourceCodeFrame extends
|
|
|
132
152
|
this.uiSourceCodeInternal.addEventListener(
|
|
133
153
|
Workspace.UISourceCode.Events.MessageRemoved, this.onMessageRemoved, this),
|
|
134
154
|
this.uiSourceCodeInternal.addEventListener(
|
|
135
|
-
Workspace.UISourceCode.Events.
|
|
136
|
-
this.uiSourceCodeInternal.addEventListener(
|
|
137
|
-
Workspace.UISourceCode.Events.LineDecorationRemoved, this.onLineDecorationRemoved, this),
|
|
155
|
+
Workspace.UISourceCode.Events.DecorationChanged, this.onDecorationChanged, this),
|
|
138
156
|
];
|
|
139
157
|
}
|
|
140
158
|
}
|
|
@@ -147,7 +165,7 @@ export class UISourceCodeFrame extends
|
|
|
147
165
|
this.unloadUISourceCode();
|
|
148
166
|
this.uiSourceCodeInternal = uiSourceCode;
|
|
149
167
|
if (uiSourceCode.contentLoaded()) {
|
|
150
|
-
if (uiSourceCode.workingCopy() !== this.textEditor.
|
|
168
|
+
if (uiSourceCode.workingCopy() !== this.textEditor.state.doc.toString()) {
|
|
151
169
|
this.innerSetContent(uiSourceCode.workingCopy());
|
|
152
170
|
}
|
|
153
171
|
} else {
|
|
@@ -155,7 +173,7 @@ export class UISourceCodeFrame extends
|
|
|
155
173
|
if (this.uiSourceCodeInternal !== uiSourceCode) {
|
|
156
174
|
return;
|
|
157
175
|
}
|
|
158
|
-
if (uiSourceCode.workingCopy() !== this.textEditor.
|
|
176
|
+
if (uiSourceCode.workingCopy() !== this.textEditor.state.doc.toString()) {
|
|
159
177
|
this.innerSetContent(uiSourceCode.workingCopy());
|
|
160
178
|
}
|
|
161
179
|
});
|
|
@@ -165,9 +183,6 @@ export class UISourceCodeFrame extends
|
|
|
165
183
|
|
|
166
184
|
private unloadUISourceCode(): void {
|
|
167
185
|
this.disposePlugins();
|
|
168
|
-
for (const message of this.allMessages()) {
|
|
169
|
-
this.removeMessageFromSource(message);
|
|
170
|
-
}
|
|
171
186
|
Common.EventTarget.removeEventListeners(this.messageAndDecorationListeners);
|
|
172
187
|
Common.EventTarget.removeEventListeners(this.uiSourceCodeEventListeners);
|
|
173
188
|
this.uiSourceCodeInternal.removeWorkingCopyGetter();
|
|
@@ -181,30 +196,21 @@ export class UISourceCodeFrame extends
|
|
|
181
196
|
Workspace.UISourceCode.Events.WorkingCopyChanged, this.onWorkingCopyChanged, this),
|
|
182
197
|
this.uiSourceCodeInternal.addEventListener(
|
|
183
198
|
Workspace.UISourceCode.Events.WorkingCopyCommitted, this.onWorkingCopyCommitted, this),
|
|
184
|
-
this.uiSourceCodeInternal.addEventListener(
|
|
185
|
-
Workspace.UISourceCode.Events.TitleChanged, this.refreshHighlighterType, this),
|
|
199
|
+
this.uiSourceCodeInternal.addEventListener(Workspace.UISourceCode.Events.TitleChanged, this.onTitleChanged, this),
|
|
186
200
|
];
|
|
187
201
|
|
|
188
202
|
Persistence.Persistence.PersistenceImpl.instance().subscribeForBindingEvent(
|
|
189
203
|
this.uiSourceCodeInternal, this.boundOnBindingChanged);
|
|
190
|
-
for (const message of this.allMessages()) {
|
|
191
|
-
this.addMessageToSource(message);
|
|
192
|
-
}
|
|
193
204
|
this.installMessageAndDecorationListeners();
|
|
194
205
|
this.updateStyle();
|
|
195
|
-
this.decorateAllTypes();
|
|
196
|
-
this.refreshHighlighterType();
|
|
197
206
|
if (Root.Runtime.experiments.isEnabled('sourcesPrettyPrint')) {
|
|
198
207
|
const supportedPrettyTypes = new Set<string>(['text/html', 'text/css', 'text/javascript']);
|
|
199
|
-
this.setCanPrettyPrint(supportedPrettyTypes.has(this.
|
|
208
|
+
this.setCanPrettyPrint(supportedPrettyTypes.has(this.contentType), true);
|
|
200
209
|
}
|
|
201
|
-
this.ensurePluginsLoaded();
|
|
202
210
|
}
|
|
203
211
|
|
|
204
212
|
wasShown(): void {
|
|
205
213
|
super.wasShown();
|
|
206
|
-
// We need CodeMirrorTextEditor to be initialized prior to this call as it calls |cursorPositionToCoordinates| internally. @see crbug.com/506566
|
|
207
|
-
window.setTimeout(() => this.updateBucketDecorations(), 0);
|
|
208
214
|
this.setEditable(this.canEditSourceInternal());
|
|
209
215
|
for (const plugin of this.plugins) {
|
|
210
216
|
plugin.wasShown();
|
|
@@ -220,15 +226,9 @@ export class UISourceCodeFrame extends
|
|
|
220
226
|
this.uiSourceCodeInternal.removeWorkingCopyGetter();
|
|
221
227
|
}
|
|
222
228
|
|
|
223
|
-
|
|
229
|
+
protected getContentType(): string {
|
|
224
230
|
const binding = Persistence.Persistence.PersistenceImpl.instance().binding(this.uiSourceCodeInternal);
|
|
225
|
-
|
|
226
|
-
if (this.highlighterType() === highlighterType) {
|
|
227
|
-
return;
|
|
228
|
-
}
|
|
229
|
-
this.disposePlugins();
|
|
230
|
-
this.setHighlighterType(highlighterType);
|
|
231
|
-
this.ensurePluginsLoaded();
|
|
231
|
+
return binding ? binding.network.mimeType() : this.uiSourceCodeInternal.mimeType();
|
|
232
232
|
}
|
|
233
233
|
|
|
234
234
|
canEditSourceInternal(): boolean {
|
|
@@ -275,18 +275,20 @@ export class UISourceCodeFrame extends
|
|
|
275
275
|
this.muteSourceCodeEvents = false;
|
|
276
276
|
}
|
|
277
277
|
|
|
278
|
-
setContent(content: string
|
|
278
|
+
async setContent(content: string): Promise<void> {
|
|
279
279
|
this.disposePlugins();
|
|
280
|
-
this.rowMessageBuckets.clear();
|
|
281
|
-
super.setContent(content, loadError);
|
|
282
|
-
for (const message of this.allMessages()) {
|
|
283
|
-
this.addMessageToSource(message);
|
|
284
|
-
}
|
|
285
|
-
this.decorateAllTypes();
|
|
286
280
|
this.ensurePluginsLoaded();
|
|
281
|
+
await super.setContent(content);
|
|
287
282
|
Common.EventTarget.fireEvent('source-file-loaded', this.uiSourceCodeInternal.displayName(true));
|
|
288
283
|
}
|
|
289
284
|
|
|
285
|
+
protected editorInitialized(): void {
|
|
286
|
+
super.editorInitialized();
|
|
287
|
+
for (const plugin of this.plugins) {
|
|
288
|
+
plugin.editorInitialized(this.textEditor);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
290
292
|
private allMessages(): Set<Workspace.UISourceCode.Message> {
|
|
291
293
|
if (this.persistenceBinding) {
|
|
292
294
|
const combinedSet = this.persistenceBinding.network.messages();
|
|
@@ -296,9 +298,9 @@ export class UISourceCodeFrame extends
|
|
|
296
298
|
return this.uiSourceCodeInternal.messages();
|
|
297
299
|
}
|
|
298
300
|
|
|
299
|
-
onTextChanged(
|
|
301
|
+
onTextChanged(): void {
|
|
300
302
|
const wasPretty = this.pretty;
|
|
301
|
-
super.onTextChanged(
|
|
303
|
+
super.onTextChanged();
|
|
302
304
|
this.errorPopoverHelper.hidePopover();
|
|
303
305
|
if (this.isSettingContent) {
|
|
304
306
|
return;
|
|
@@ -308,13 +310,12 @@ export class UISourceCodeFrame extends
|
|
|
308
310
|
if (this.isClean()) {
|
|
309
311
|
this.uiSourceCodeInternal.resetWorkingCopy();
|
|
310
312
|
} else {
|
|
311
|
-
this.uiSourceCodeInternal.setWorkingCopyGetter(this.textEditor.
|
|
313
|
+
this.uiSourceCodeInternal.setWorkingCopyGetter(() => this.textEditor.state.doc.toString());
|
|
312
314
|
}
|
|
313
315
|
this.muteSourceCodeEvents = false;
|
|
314
316
|
if (wasPretty !== this.pretty) {
|
|
315
317
|
this.updateStyle();
|
|
316
|
-
this.
|
|
317
|
-
this.ensurePluginsLoaded();
|
|
318
|
+
this.reloadPlugins();
|
|
318
319
|
}
|
|
319
320
|
}
|
|
320
321
|
|
|
@@ -333,32 +334,33 @@ export class UISourceCodeFrame extends
|
|
|
333
334
|
this.updateStyle();
|
|
334
335
|
}
|
|
335
336
|
|
|
337
|
+
private reloadPlugins(): void {
|
|
338
|
+
this.disposePlugins();
|
|
339
|
+
this.ensurePluginsLoaded();
|
|
340
|
+
const editor = this.textEditor;
|
|
341
|
+
editor.dispatch({effects: pluginCompartment.reconfigure(this.plugins.map(plugin => plugin.editorExtension()))});
|
|
342
|
+
for (const plugin of this.plugins) {
|
|
343
|
+
plugin.editorInitialized(editor);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
private onTitleChanged(): void {
|
|
348
|
+
this.updateLanguageMode('').then(() => this.reloadPlugins(), console.error);
|
|
349
|
+
}
|
|
350
|
+
|
|
336
351
|
private ensurePluginsLoaded(): void {
|
|
337
|
-
if (
|
|
352
|
+
if (this.pluginsLoaded) {
|
|
338
353
|
return;
|
|
339
354
|
}
|
|
355
|
+
this.pluginsLoaded = true;
|
|
340
356
|
|
|
341
357
|
const binding = Persistence.Persistence.PersistenceImpl.instance().binding(this.uiSourceCodeInternal);
|
|
342
358
|
const pluginUISourceCode = binding ? binding.network : this.uiSourceCodeInternal;
|
|
343
359
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
if (CSSPlugin.accepts(pluginUISourceCode)) {
|
|
349
|
-
this.plugins.push(new CSSPlugin(this.textEditor));
|
|
350
|
-
}
|
|
351
|
-
if (!this.pretty && JavaScriptCompilerPlugin.accepts(pluginUISourceCode)) {
|
|
352
|
-
this.plugins.push(new JavaScriptCompilerPlugin(this.textEditor, pluginUISourceCode));
|
|
353
|
-
}
|
|
354
|
-
if (SnippetsPlugin.accepts(pluginUISourceCode)) {
|
|
355
|
-
this.plugins.push(new SnippetsPlugin(this.textEditor, pluginUISourceCode));
|
|
356
|
-
}
|
|
357
|
-
if (ScriptOriginPlugin.accepts(pluginUISourceCode)) {
|
|
358
|
-
this.plugins.push(new ScriptOriginPlugin(this.textEditor, pluginUISourceCode));
|
|
359
|
-
}
|
|
360
|
-
if (CoveragePlugin.accepts(pluginUISourceCode)) {
|
|
361
|
-
this.plugins.push(new CoveragePlugin(this.textEditor, pluginUISourceCode));
|
|
360
|
+
for (const pluginType of sourceFramePlugins()) {
|
|
361
|
+
if (pluginType.accepts(pluginUISourceCode)) {
|
|
362
|
+
this.plugins.push(new pluginType(pluginUISourceCode, this));
|
|
363
|
+
}
|
|
362
364
|
}
|
|
363
365
|
|
|
364
366
|
this.dispatchEventToListeners(Events.ToolbarItemsChanged);
|
|
@@ -368,12 +370,11 @@ export class UISourceCodeFrame extends
|
|
|
368
370
|
}
|
|
369
371
|
|
|
370
372
|
private disposePlugins(): void {
|
|
371
|
-
this.
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
}
|
|
375
|
-
});
|
|
373
|
+
for (const plugin of this.plugins) {
|
|
374
|
+
plugin.dispose();
|
|
375
|
+
}
|
|
376
376
|
this.plugins = [];
|
|
377
|
+
this.pluginsLoaded = false;
|
|
377
378
|
}
|
|
378
379
|
|
|
379
380
|
private onBindingChanged(): void {
|
|
@@ -392,30 +393,37 @@ export class UISourceCodeFrame extends
|
|
|
392
393
|
|
|
393
394
|
private innerSetContent(content: string): void {
|
|
394
395
|
this.isSettingContent = true;
|
|
395
|
-
const oldContent = this.textEditor.
|
|
396
|
+
const oldContent = this.textEditor.state.doc.toString();
|
|
396
397
|
if (oldContent !== content) {
|
|
397
|
-
this.setContent(content
|
|
398
|
+
this.setContent(content);
|
|
398
399
|
}
|
|
399
400
|
this.isSettingContent = false;
|
|
400
401
|
}
|
|
401
402
|
|
|
402
|
-
|
|
403
|
-
contextMenu: UI.ContextMenu.ContextMenu,
|
|
404
|
-
|
|
403
|
+
protected populateTextAreaContextMenu(
|
|
404
|
+
contextMenu: UI.ContextMenu.ContextMenu, lineNumber: number, columnNumber: number): void {
|
|
405
|
+
super.populateTextAreaContextMenu(contextMenu, lineNumber, columnNumber);
|
|
405
406
|
contextMenu.appendApplicableItems(this.uiSourceCodeInternal);
|
|
406
|
-
const location = this.editorLocationToUILocation(
|
|
407
|
+
const location = this.editorLocationToUILocation(lineNumber, columnNumber);
|
|
407
408
|
contextMenu.appendApplicableItems(
|
|
408
409
|
new Workspace.UISourceCode.UILocation(this.uiSourceCodeInternal, location.lineNumber, location.columnNumber));
|
|
409
410
|
contextMenu.appendApplicableItems(this);
|
|
410
411
|
for (const plugin of this.plugins) {
|
|
411
|
-
|
|
412
|
+
plugin.populateTextAreaContextMenu(contextMenu, lineNumber, columnNumber);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
protected populateLineGutterContextMenu(contextMenu: UI.ContextMenu.ContextMenu, lineNumber: number): void {
|
|
417
|
+
super.populateLineGutterContextMenu(contextMenu, lineNumber);
|
|
418
|
+
for (const plugin of this.plugins) {
|
|
419
|
+
plugin.populateLineGutterContextMenu(contextMenu, lineNumber);
|
|
412
420
|
}
|
|
413
421
|
}
|
|
414
422
|
|
|
415
423
|
dispose(): void {
|
|
416
424
|
this.errorPopoverHelper.dispose();
|
|
417
425
|
this.unloadUISourceCode();
|
|
418
|
-
this.textEditor.
|
|
426
|
+
this.textEditor.editor.destroy();
|
|
419
427
|
this.detach();
|
|
420
428
|
Common.Settings.Settings.instance()
|
|
421
429
|
.moduleSetting('persistenceNetworkOverridesEnabled')
|
|
@@ -423,108 +431,22 @@ export class UISourceCodeFrame extends
|
|
|
423
431
|
}
|
|
424
432
|
|
|
425
433
|
private onMessageAdded(event: Common.EventTarget.EventTargetEvent<Workspace.UISourceCode.Message>): void {
|
|
426
|
-
const
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
private getClampedEditorLineNumberForMessage(message: Workspace.UISourceCode.Message): number {
|
|
431
|
-
let {lineNumber} = this.uiLocationToEditorLocation(message.lineNumber(), message.columnNumber());
|
|
432
|
-
if (lineNumber >= this.textEditor.linesCount) {
|
|
433
|
-
lineNumber = this.textEditor.linesCount - 1;
|
|
434
|
+
const {editor} = this.textEditor, shownMessages = editor.state.field(showRowMessages, false);
|
|
435
|
+
if (shownMessages) {
|
|
436
|
+
editor.dispatch({effects: setRowMessages.of(shownMessages.messages.add(event.data))});
|
|
434
437
|
}
|
|
435
|
-
if (lineNumber < 0) {
|
|
436
|
-
lineNumber = 0;
|
|
437
|
-
}
|
|
438
|
-
return lineNumber;
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
private addMessageToSource(message: Workspace.UISourceCode.Message): void {
|
|
442
|
-
if (!this.loaded) {
|
|
443
|
-
return;
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
const editorLineNumber = this.getClampedEditorLineNumberForMessage(message);
|
|
447
|
-
let messageBucket = this.rowMessageBuckets.get(editorLineNumber);
|
|
448
|
-
if (!messageBucket) {
|
|
449
|
-
messageBucket = new RowMessageBucket(this, this.textEditor, editorLineNumber);
|
|
450
|
-
this.rowMessageBuckets.set(editorLineNumber, messageBucket);
|
|
451
|
-
}
|
|
452
|
-
messageBucket.addMessage(message);
|
|
453
438
|
}
|
|
454
439
|
|
|
455
440
|
private onMessageRemoved(event: Common.EventTarget.EventTargetEvent<Workspace.UISourceCode.Message>): void {
|
|
456
|
-
const
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
private removeMessageFromSource(message: Workspace.UISourceCode.Message): void {
|
|
461
|
-
if (!this.loaded) {
|
|
462
|
-
return;
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
const editorLineNumber = this.getClampedEditorLineNumberForMessage(message);
|
|
466
|
-
const messageBucket = this.rowMessageBuckets.get(editorLineNumber);
|
|
467
|
-
if (!messageBucket) {
|
|
468
|
-
return;
|
|
469
|
-
}
|
|
470
|
-
messageBucket.removeMessage(message);
|
|
471
|
-
if (!messageBucket.uniqueMessagesCount()) {
|
|
472
|
-
messageBucket.detachFromEditor();
|
|
473
|
-
this.rowMessageBuckets.delete(editorLineNumber);
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
private getErrorPopoverContent(event: Event): UI.PopoverHelper.PopoverRequest|null {
|
|
478
|
-
const mouseEvent = (event as MouseEvent);
|
|
479
|
-
const eventTarget = (mouseEvent.target as HTMLElement);
|
|
480
|
-
return RowMessageBucket.getPopover(eventTarget, mouseEvent);
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
private updateBucketDecorations(): void {
|
|
484
|
-
for (const bucket of this.rowMessageBuckets.values()) {
|
|
485
|
-
bucket.updateDecoration();
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
private onLineDecorationAdded(event: Common.EventTarget.EventTargetEvent<Workspace.UISourceCode.LineMarker>): void {
|
|
490
|
-
const marker = event.data;
|
|
491
|
-
this.decorateTypeThrottled(marker.type());
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
private onLineDecorationRemoved(event: Common.EventTarget.EventTargetEvent<Workspace.UISourceCode.LineMarker>): void {
|
|
495
|
-
const marker = event.data;
|
|
496
|
-
this.decorateTypeThrottled(marker.type());
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
private decorateTypeThrottled(type: string): void {
|
|
500
|
-
if (this.typeDecorationsPending.has(type)) {
|
|
501
|
-
return;
|
|
441
|
+
const {editor} = this.textEditor, shownMessages = editor.state.field(showRowMessages, false);
|
|
442
|
+
if (shownMessages) {
|
|
443
|
+
editor.dispatch({effects: setRowMessages.of(shownMessages.messages.remove(event.data))});
|
|
502
444
|
}
|
|
503
|
-
this.typeDecorationsPending.add(type);
|
|
504
|
-
const extension =
|
|
505
|
-
SourceFrame.SourceFrame.getRegisteredLineDecorators().find(extension => extension.decoratorType === type);
|
|
506
|
-
const decorator = extension && extension.lineDecorator();
|
|
507
|
-
if (!decorator) {
|
|
508
|
-
return;
|
|
509
|
-
}
|
|
510
|
-
this.typeDecorationsPending.delete(type);
|
|
511
|
-
// TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration)
|
|
512
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
513
|
-
(this.textEditor.codeMirror() as any).operation(() => {
|
|
514
|
-
decorator.decorate(
|
|
515
|
-
this.persistenceBinding ? this.persistenceBinding.network : this.uiSourceCode(), this.textEditor, type);
|
|
516
|
-
});
|
|
517
445
|
}
|
|
518
446
|
|
|
519
|
-
private
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
}
|
|
523
|
-
for (const extension of SourceFrame.SourceFrame.getRegisteredLineDecorators()) {
|
|
524
|
-
const type = extension.decoratorType;
|
|
525
|
-
if (type !== null && this.uiSourceCodeInternal.decorationsForType(type)) {
|
|
526
|
-
this.decorateTypeThrottled(type);
|
|
527
|
-
}
|
|
447
|
+
private onDecorationChanged(event: Common.EventTarget.EventTargetEvent<string>): void {
|
|
448
|
+
for (const plugin of this.plugins) {
|
|
449
|
+
plugin.decorationChanged(event.data as SourceFrame.SourceFrame.DecoratorType, this.textEditor);
|
|
528
450
|
}
|
|
529
451
|
}
|
|
530
452
|
|
|
@@ -543,11 +465,56 @@ export class UISourceCodeFrame extends
|
|
|
543
465
|
return [...leftToolbarItems, new UI.Toolbar.ToolbarSeparator(true), ...rightToolbarItems];
|
|
544
466
|
}
|
|
545
467
|
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
468
|
+
private getErrorPopoverContent(event: Event): UI.PopoverHelper.PopoverRequest|null {
|
|
469
|
+
const mouseEvent = event as MouseEvent;
|
|
470
|
+
const eventTarget = event.target as HTMLElement;
|
|
471
|
+
const anchorElement = eventTarget.enclosingNodeOrSelfWithClass('cm-messageIcon-error') ||
|
|
472
|
+
eventTarget.enclosingNodeOrSelfWithClass('cm-messageIcon-issue');
|
|
473
|
+
if (!anchorElement) {
|
|
474
|
+
return null;
|
|
550
475
|
}
|
|
476
|
+
|
|
477
|
+
const messageField = this.textEditor.state.field(showRowMessages, false);
|
|
478
|
+
if (!messageField || messageField.messages.rows.length === 0) {
|
|
479
|
+
return null;
|
|
480
|
+
}
|
|
481
|
+
const {editor} = this.textEditor;
|
|
482
|
+
const position = editor.posAtCoords(mouseEvent);
|
|
483
|
+
if (position === null) {
|
|
484
|
+
return null;
|
|
485
|
+
}
|
|
486
|
+
const line = editor.state.doc.lineAt(position);
|
|
487
|
+
if (position !== line.to) {
|
|
488
|
+
return null;
|
|
489
|
+
}
|
|
490
|
+
const row = messageField.messages.rows.find(row => row[0].lineNumber() === line.number - 1);
|
|
491
|
+
if (!row) {
|
|
492
|
+
return null;
|
|
493
|
+
}
|
|
494
|
+
const issues = anchorElement.classList.contains('cm-messageIcon-issue');
|
|
495
|
+
const messages = row.filter(msg => (msg.level() === Workspace.UISourceCode.Message.Level.Issue) === issues);
|
|
496
|
+
if (!messages.length) {
|
|
497
|
+
return null;
|
|
498
|
+
}
|
|
499
|
+
const anchor =
|
|
500
|
+
anchorElement ? anchorElement.boxInWindow() : new AnchorBox(mouseEvent.clientX, mouseEvent.clientY, 1, 1);
|
|
501
|
+
|
|
502
|
+
const counts = countDuplicates(messages);
|
|
503
|
+
const element = document.createElement('div');
|
|
504
|
+
element.classList.add('text-editor-messages-description-container');
|
|
505
|
+
for (let i = 0; i < messages.length; i++) {
|
|
506
|
+
if (counts[i]) {
|
|
507
|
+
element.appendChild(renderMessage(messages[i], counts[i]));
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
return {
|
|
511
|
+
box: anchor,
|
|
512
|
+
hide(): void{},
|
|
513
|
+
show: (popover: UI.GlassPane.GlassPane): Promise<true> => {
|
|
514
|
+
popover.contentElement.append(element);
|
|
515
|
+
return Promise.resolve(true);
|
|
516
|
+
},
|
|
517
|
+
};
|
|
551
518
|
}
|
|
552
519
|
}
|
|
553
520
|
|
|
@@ -575,15 +542,13 @@ function getBubbleTypePerLevel(level: Workspace.UISourceCode.Message.Level): str
|
|
|
575
542
|
}
|
|
576
543
|
}
|
|
577
544
|
|
|
578
|
-
function
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
return 'text-editor-line-with-warning';
|
|
586
|
-
}
|
|
545
|
+
function messageLevelComparator(a: Workspace.UISourceCode.Message, b: Workspace.UISourceCode.Message): number {
|
|
546
|
+
const messageLevelPriority = {
|
|
547
|
+
[Workspace.UISourceCode.Message.Level.Issue]: 2,
|
|
548
|
+
[Workspace.UISourceCode.Message.Level.Warning]: 3,
|
|
549
|
+
[Workspace.UISourceCode.Message.Level.Error]: 4,
|
|
550
|
+
};
|
|
551
|
+
return messageLevelPriority[a.level()] - messageLevelPriority[b.level()];
|
|
587
552
|
}
|
|
588
553
|
|
|
589
554
|
function getIconDataForMessage(message: Workspace.UISourceCode.Message): IconButton.Icon.IconData {
|
|
@@ -597,318 +562,218 @@ function getIconDataForMessage(message: Workspace.UISourceCode.Message): IconBut
|
|
|
597
562
|
return getIconDataForLevel(message.level());
|
|
598
563
|
}
|
|
599
564
|
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
565
|
+
// TODO(crbug.com/1167717): Make this a const enum again
|
|
566
|
+
// eslint-disable-next-line rulesdir/const_enum
|
|
567
|
+
export enum Events {
|
|
568
|
+
ToolbarItemsChanged = 'ToolbarItemsChanged',
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
export type EventTypes = {
|
|
572
|
+
[Events.ToolbarItemsChanged]: void,
|
|
573
|
+
};
|
|
606
574
|
|
|
607
|
-
|
|
608
|
-
this.message = message;
|
|
609
|
-
this.repeatCount = 1;
|
|
610
|
-
this.element = document.createElement('div');
|
|
611
|
-
this.element.classList.add('text-editor-row-message');
|
|
612
|
-
this.icon = new IconButton.Icon.Icon();
|
|
613
|
-
this.icon.data = getIconDataForMessage(message);
|
|
614
|
-
this.icon.classList.add('text-editor-row-message-icon');
|
|
615
|
-
this.icon.addEventListener('click', () => this.callClickHandler());
|
|
575
|
+
const pluginCompartment = new CodeMirror.Compartment();
|
|
616
576
|
|
|
617
|
-
|
|
618
|
-
this.repeatCountElement = document.createElement('span', {is: 'dt-small-bubble'}) as UI.UIUtils.DevToolsSmallBubble;
|
|
619
|
-
this.repeatCountElement.classList.add('text-editor-row-message-repeat-count', 'hidden');
|
|
620
|
-
this.element.appendChild(this.repeatCountElement);
|
|
621
|
-
this.repeatCountElement.type = getBubbleTypePerLevel(message.level());
|
|
622
|
-
const linesContainer = this.element.createChild('div');
|
|
623
|
-
const lines = this.message.text().split('\n');
|
|
624
|
-
for (let i = 0; i < lines.length; ++i) {
|
|
625
|
-
const messageLine = linesContainer.createChild('div');
|
|
626
|
-
messageLine.textContent = lines[i];
|
|
627
|
-
}
|
|
628
|
-
}
|
|
577
|
+
// Row message management and display logic
|
|
629
578
|
|
|
630
|
-
|
|
631
|
-
|
|
579
|
+
function addMessage(rows: Workspace.UISourceCode.Message[][], message: Workspace.UISourceCode.Message):
|
|
580
|
+
Workspace.UISourceCode.Message[][] {
|
|
581
|
+
const lineNumber = message.lineNumber();
|
|
582
|
+
let i = 0;
|
|
583
|
+
for (; i < rows.length; i++) {
|
|
584
|
+
const diff = rows[i][0].lineNumber() - lineNumber;
|
|
585
|
+
if (diff === 0) {
|
|
586
|
+
rows[i] = rows[i].concat(message);
|
|
587
|
+
return rows;
|
|
588
|
+
}
|
|
589
|
+
if (diff > 0) {
|
|
590
|
+
break;
|
|
591
|
+
}
|
|
632
592
|
}
|
|
593
|
+
rows.splice(i, 0, [message]);
|
|
594
|
+
return rows;
|
|
595
|
+
}
|
|
633
596
|
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
if (
|
|
637
|
-
|
|
597
|
+
function removeMessage(rows: Workspace.UISourceCode.Message[][], message: Workspace.UISourceCode.Message): void {
|
|
598
|
+
for (let i = 0; i < rows.length; i++) {
|
|
599
|
+
if (rows[i][0].lineNumber() === message.lineNumber()) {
|
|
600
|
+
const remaining = rows[i].filter(m => !m.isEqual(message));
|
|
601
|
+
if (remaining.length) {
|
|
602
|
+
rows[i] = remaining;
|
|
603
|
+
} else {
|
|
604
|
+
rows.splice(i, 1);
|
|
605
|
+
}
|
|
606
|
+
break;
|
|
638
607
|
}
|
|
639
608
|
}
|
|
609
|
+
}
|
|
640
610
|
|
|
641
|
-
|
|
642
|
-
|
|
611
|
+
class RowMessages {
|
|
612
|
+
constructor(readonly rows: Workspace.UISourceCode.Message[][]) {
|
|
643
613
|
}
|
|
644
614
|
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
615
|
+
static create(messages: Workspace.UISourceCode.Message[]): RowMessages {
|
|
616
|
+
const rows: Workspace.UISourceCode.Message[][] = [];
|
|
617
|
+
for (const message of messages) {
|
|
618
|
+
addMessage(rows, message);
|
|
648
619
|
}
|
|
649
|
-
|
|
650
|
-
|
|
620
|
+
return new RowMessages(rows);
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
remove(message: Workspace.UISourceCode.Message): RowMessages {
|
|
624
|
+
const rows = this.rows.slice();
|
|
625
|
+
removeMessage(rows, message);
|
|
626
|
+
return new RowMessages(rows);
|
|
651
627
|
}
|
|
652
628
|
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
const showRepeatCount = this.repeatCount > 1;
|
|
656
|
-
this.repeatCountElement.classList.toggle('hidden', !showRepeatCount);
|
|
657
|
-
this.icon.classList.toggle('hidden', showRepeatCount);
|
|
629
|
+
add(message: Workspace.UISourceCode.Message): RowMessages {
|
|
630
|
+
return new RowMessages(addMessage(this.rows.slice(), message));
|
|
658
631
|
}
|
|
659
632
|
}
|
|
660
633
|
|
|
661
|
-
const
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
private readonly decoration: HTMLDivElement;
|
|
669
|
-
private readonly wave: HTMLElement;
|
|
670
|
-
private errorIcon: IconButton.Icon.Icon;
|
|
671
|
-
private issueIcon: IconButton.Icon.Icon;
|
|
672
|
-
private decorationStartColumn: number|null;
|
|
673
|
-
private readonly messagesDescriptionElement: HTMLDivElement;
|
|
674
|
-
private messages: RowMessage[];
|
|
675
|
-
private level: Workspace.UISourceCode.Message.Level|null;
|
|
676
|
-
private bookmark?: TextEditor.CodeMirrorTextEditor.TextEditorBookMark;
|
|
677
|
-
private iconsElement: HTMLSpanElement;
|
|
678
|
-
|
|
679
|
-
constructor(
|
|
680
|
-
sourceFrame: UISourceCodeFrame, textEditor: SourceFrame.SourcesTextEditor.SourcesTextEditor,
|
|
681
|
-
editorLineNumber: number) {
|
|
682
|
-
this.sourceFrame = sourceFrame;
|
|
683
|
-
this.textEditor = textEditor;
|
|
684
|
-
this.lineHandle = textEditor.textEditorPositionHandle(editorLineNumber, 0);
|
|
685
|
-
this.decoration = document.createElement('div');
|
|
686
|
-
this.decoration.classList.add('text-editor-line-decoration');
|
|
687
|
-
elementToMessageBucket.set(this.decoration, this);
|
|
688
|
-
this.wave = this.decoration.createChild('div', 'text-editor-line-decoration-wave');
|
|
689
|
-
|
|
690
|
-
this.errorIcon = new IconButton.Icon.Icon();
|
|
691
|
-
this.errorIcon.data = getIconDataForLevel(Workspace.UISourceCode.Message.Level.Warning);
|
|
692
|
-
this.errorIcon.classList.add('text-editor-line-decoration-icon-error');
|
|
693
|
-
this.issueIcon = new IconButton.Icon.Icon();
|
|
694
|
-
this.issueIcon.data = getIconDataForLevel(Workspace.UISourceCode.Message.Level.Issue);
|
|
695
|
-
this.issueIcon.classList.add('text-editor-line-decoration-icon-issue');
|
|
696
|
-
this.issueIcon.addEventListener('click', () => this.issueClickHandler());
|
|
697
|
-
|
|
698
|
-
this.iconsElement = document.createElement('span');
|
|
699
|
-
this.iconsElement.append(this.errorIcon);
|
|
700
|
-
this.iconsElement.append(this.issueIcon);
|
|
701
|
-
this.iconsElement.classList.add('text-editor-line-decoration-icon');
|
|
702
|
-
elementToMessageBucket.set(this.iconsElement, this);
|
|
703
|
-
|
|
704
|
-
this.decorationStartColumn = null;
|
|
705
|
-
|
|
706
|
-
this.messagesDescriptionElement = document.createElement('div');
|
|
707
|
-
this.messagesDescriptionElement.classList.add('text-editor-messages-description-container');
|
|
708
|
-
this.messages = [];
|
|
709
|
-
|
|
710
|
-
this.level = null;
|
|
711
|
-
}
|
|
712
|
-
|
|
713
|
-
private updateWavePosition(editorLineNumber: number, columnNumber: number): void {
|
|
714
|
-
editorLineNumber = Math.min(editorLineNumber, this.textEditor.linesCount - 1);
|
|
715
|
-
const lineText = this.textEditor.line(editorLineNumber);
|
|
716
|
-
columnNumber = Math.min(columnNumber, lineText.length);
|
|
717
|
-
const lineIndent = TextUtils.TextUtils.Utils.lineIndent(lineText).length;
|
|
718
|
-
const startColumn = Math.max(columnNumber, lineIndent);
|
|
719
|
-
if (this.decorationStartColumn === startColumn) {
|
|
720
|
-
return;
|
|
721
|
-
}
|
|
722
|
-
if (this.decorationStartColumn !== null) {
|
|
723
|
-
this.textEditor.removeDecoration(this.decoration, editorLineNumber);
|
|
724
|
-
}
|
|
725
|
-
this.textEditor.addDecoration(this.decoration, editorLineNumber, startColumn);
|
|
726
|
-
this.decorationStartColumn = startColumn;
|
|
634
|
+
const setRowMessages = CodeMirror.StateEffect.define<RowMessages>();
|
|
635
|
+
|
|
636
|
+
const underlineMark = CodeMirror.Decoration.mark({class: 'cm-waveUnderline'});
|
|
637
|
+
|
|
638
|
+
class MessageWidget extends CodeMirror.WidgetType {
|
|
639
|
+
constructor(readonly messages: Workspace.UISourceCode.Message[]) {
|
|
640
|
+
super();
|
|
727
641
|
}
|
|
728
642
|
|
|
729
|
-
|
|
730
|
-
this.
|
|
731
|
-
UI.Utils.appendStyle(this.messagesDescriptionElement, 'ui/legacy/components/source_frame/messagesPopover.css');
|
|
732
|
-
for (const message of this.messages.filter(m => levels.has(m.getMessage().level()))) {
|
|
733
|
-
this.messagesDescriptionElement.append(message.element);
|
|
734
|
-
}
|
|
735
|
-
return this.messagesDescriptionElement;
|
|
643
|
+
eq(other: MessageWidget): boolean {
|
|
644
|
+
return other.messages === this.messages;
|
|
736
645
|
}
|
|
737
646
|
|
|
738
|
-
|
|
739
|
-
const
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
if (this.decorationStartColumn !== null) {
|
|
748
|
-
this.textEditor.removeDecoration(this.decoration, editorLineNumber);
|
|
749
|
-
this.decorationStartColumn = null;
|
|
647
|
+
toDOM(): HTMLElement {
|
|
648
|
+
const wrap = document.createElement('span');
|
|
649
|
+
wrap.classList.add('cm-messageIcon');
|
|
650
|
+
const nonIssues = this.messages.filter(msg => msg.level() !== Workspace.UISourceCode.Message.Level.Issue);
|
|
651
|
+
if (nonIssues.length) {
|
|
652
|
+
const maxIssue = nonIssues.sort(messageLevelComparator)[nonIssues.length - 1];
|
|
653
|
+
const errorIcon = wrap.appendChild(new IconButton.Icon.Icon());
|
|
654
|
+
errorIcon.data = getIconDataForLevel(maxIssue.level());
|
|
655
|
+
errorIcon.classList.add('cm-messageIcon-error');
|
|
750
656
|
}
|
|
751
|
-
|
|
752
|
-
|
|
657
|
+
const issue = this.messages.find(m => m.level() === Workspace.UISourceCode.Message.Level.Issue);
|
|
658
|
+
if (issue) {
|
|
659
|
+
const issueIcon = wrap.appendChild(new IconButton.Icon.Icon());
|
|
660
|
+
issueIcon.data = getIconDataForLevel(Workspace.UISourceCode.Message.Level.Issue);
|
|
661
|
+
issueIcon.classList.add('cm-messageIcon-issue');
|
|
662
|
+
issueIcon.addEventListener('click', () => (issue.clickHandler() || Math.min)());
|
|
753
663
|
}
|
|
664
|
+
return wrap;
|
|
754
665
|
}
|
|
755
666
|
|
|
756
|
-
|
|
757
|
-
return
|
|
667
|
+
ignoreEvents(): boolean {
|
|
668
|
+
return true;
|
|
758
669
|
}
|
|
670
|
+
}
|
|
759
671
|
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
if (firstIssue) {
|
|
763
|
-
firstIssue.callClickHandler();
|
|
764
|
-
}
|
|
672
|
+
class RowMessageDecorations {
|
|
673
|
+
constructor(readonly messages: RowMessages, readonly decorations: CodeMirror.DecorationSet) {
|
|
765
674
|
}
|
|
766
675
|
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
676
|
+
static create(messages: RowMessages, doc: CodeMirror.Text): RowMessageDecorations {
|
|
677
|
+
const builder = new CodeMirror.RangeSetBuilder<CodeMirror.Decoration>();
|
|
678
|
+
for (const row of messages.rows) {
|
|
679
|
+
const line = doc.line(row[0].lineNumber() + 1);
|
|
680
|
+
const minCol = row.reduce((col, msg) => Math.min(col, msg.columnNumber() || 0), line.length);
|
|
681
|
+
if (minCol < line.length) {
|
|
682
|
+
builder.add(line.from + minCol, line.to, underlineMark);
|
|
773
683
|
}
|
|
684
|
+
builder.add(line.to, line.to, CodeMirror.Decoration.widget({side: 1, widget: new MessageWidget(row)}));
|
|
774
685
|
}
|
|
775
|
-
|
|
776
|
-
const rowMessage = new RowMessage(message);
|
|
777
|
-
this.messages.push(rowMessage);
|
|
778
|
-
this.updateDecoration();
|
|
686
|
+
return new RowMessageDecorations(messages, builder.finish());
|
|
779
687
|
}
|
|
780
688
|
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
this.messages.splice(i, 1);
|
|
689
|
+
apply(tr: CodeMirror.Transaction): RowMessageDecorations {
|
|
690
|
+
let result: RowMessageDecorations = this;
|
|
691
|
+
if (tr.docChanged) {
|
|
692
|
+
result = new RowMessageDecorations(this.messages, this.decorations.map(tr.changes));
|
|
693
|
+
}
|
|
694
|
+
for (const effect of tr.effects) {
|
|
695
|
+
if (effect.is(setRowMessages)) {
|
|
696
|
+
result = RowMessageDecorations.create(effect.value, tr.state.doc);
|
|
790
697
|
}
|
|
791
|
-
this.updateDecoration();
|
|
792
|
-
return;
|
|
793
698
|
}
|
|
699
|
+
return result;
|
|
794
700
|
}
|
|
701
|
+
}
|
|
795
702
|
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
let maxIssueKind = IssuesManager.Issue.IssueKind.Improvement;
|
|
815
|
-
let showIssues = false;
|
|
816
|
-
let showErrors = false;
|
|
817
|
-
for (let i = 0; i < this.messages.length; ++i) {
|
|
818
|
-
const message = this.messages[i].getMessage();
|
|
819
|
-
const {columnNumber: editorColumnNumber} =
|
|
820
|
-
this.sourceFrame.uiLocationToEditorLocation(editorLineNumber, message.columnNumber());
|
|
821
|
-
columnNumber = Math.min(columnNumber, editorColumnNumber);
|
|
822
|
-
if (!maxMessage || messageLevelComparator(maxMessage, message) < 0) {
|
|
823
|
-
maxMessage = message;
|
|
824
|
-
}
|
|
825
|
-
if (message instanceof IssuesManager.SourceFrameIssuesManager.IssueMessage) {
|
|
826
|
-
maxIssueKind = IssuesManager.Issue.unionIssueKind(maxIssueKind, message.getIssueKind());
|
|
703
|
+
const showRowMessages = CodeMirror.StateField.define<RowMessageDecorations>({
|
|
704
|
+
create(state): RowMessageDecorations {
|
|
705
|
+
return RowMessageDecorations.create(new RowMessages([]), state.doc);
|
|
706
|
+
},
|
|
707
|
+
update(value, tr): RowMessageDecorations {
|
|
708
|
+
return value.apply(tr);
|
|
709
|
+
},
|
|
710
|
+
provide: field => CodeMirror.Prec.lowest(CodeMirror.EditorView.decorations.from(field, value => value.decorations)),
|
|
711
|
+
});
|
|
712
|
+
|
|
713
|
+
function countDuplicates(messages: Workspace.UISourceCode.Message[]): number[] {
|
|
714
|
+
const counts = [];
|
|
715
|
+
for (let i = 0; i < messages.length; i++) {
|
|
716
|
+
counts[i] = 0;
|
|
717
|
+
for (let j = 0; j <= i; j++) {
|
|
718
|
+
if (messages[j].isEqual(messages[i])) {
|
|
719
|
+
counts[j]++;
|
|
720
|
+
break;
|
|
827
721
|
}
|
|
828
|
-
showIssues = showIssues || message.level() === Workspace.UISourceCode.Message.Level.Issue;
|
|
829
|
-
showErrors = showErrors || message.level() !== Workspace.UISourceCode.Message.Level.Issue;
|
|
830
|
-
}
|
|
831
|
-
this.updateWavePosition(editorLineNumber, columnNumber);
|
|
832
|
-
|
|
833
|
-
if (!maxMessage) {
|
|
834
|
-
return;
|
|
835
|
-
}
|
|
836
|
-
if (this.level) {
|
|
837
|
-
this.textEditor.toggleLineClass(editorLineNumber, getLineClassPerLevel(this.level), false);
|
|
838
|
-
}
|
|
839
|
-
this.level = maxMessage.level();
|
|
840
|
-
if (!this.level) {
|
|
841
|
-
return;
|
|
842
|
-
}
|
|
843
|
-
this.textEditor.toggleLineClass(editorLineNumber, getLineClassPerLevel(this.level), true);
|
|
844
|
-
this.errorIcon.data = getIconDataForLevel(this.level);
|
|
845
|
-
this.issueIcon
|
|
846
|
-
.data = {...IssueCounter.IssueCounter.getIssueKindIconData(maxIssueKind), width: '12px', height: '12px'};
|
|
847
|
-
this.issueIcon.classList.toggle('hidden', !showIssues);
|
|
848
|
-
this.errorIcon.classList.toggle('hidden', !showErrors);
|
|
849
|
-
if (showIssues || showErrors) {
|
|
850
|
-
this.bookmark = this.textEditor.addBookmark(
|
|
851
|
-
editorLineNumber, Number.MAX_SAFE_INTEGER, this.iconsElement, bookmarkTypeRowBucket);
|
|
852
|
-
}
|
|
853
|
-
}
|
|
854
|
-
|
|
855
|
-
private getPopoverMessages(eventTarget: HTMLElement): Element|null {
|
|
856
|
-
let messagesOutline: Element|null = null;
|
|
857
|
-
if (eventTarget.classList.contains('text-editor-line-decoration-icon-error')) {
|
|
858
|
-
messagesOutline = this.messageDescription(
|
|
859
|
-
new Set([Workspace.UISourceCode.Message.Level.Error, Workspace.UISourceCode.Message.Level.Warning]));
|
|
860
|
-
} else if (eventTarget.classList.contains('text-editor-line-decoration-icon-issue')) {
|
|
861
|
-
messagesOutline = this.messageDescription(new Set([Workspace.UISourceCode.Message.Level.Issue]));
|
|
862
|
-
} else if (
|
|
863
|
-
eventTarget.classList.contains('text-editor-line-decoration-wave') &&
|
|
864
|
-
!eventTarget.classList.contains('text-editor-line-decoration-icon')) {
|
|
865
|
-
messagesOutline = this.messageDescription(
|
|
866
|
-
new Set([Workspace.UISourceCode.Message.Level.Error, Workspace.UISourceCode.Message.Level.Warning]));
|
|
867
|
-
}
|
|
868
|
-
return messagesOutline;
|
|
869
|
-
}
|
|
870
|
-
|
|
871
|
-
static getPopover(eventTarget: HTMLElement, mouseEvent: MouseEvent): UI.PopoverHelper.PopoverRequest|null {
|
|
872
|
-
const enclosingNode = eventTarget.enclosingNodeOrSelfWithClass('text-editor-line-decoration') ||
|
|
873
|
-
eventTarget.enclosingNodeOrSelfWithClass('text-editor-line-decoration-icon');
|
|
874
|
-
const messageBucket = elementToMessageBucket.get(enclosingNode);
|
|
875
|
-
if (!messageBucket) {
|
|
876
|
-
return null;
|
|
877
|
-
}
|
|
878
|
-
const anchorElement = eventTarget.enclosingNodeOrSelfWithClass('text-editor-line-decoration-icon-error') ||
|
|
879
|
-
eventTarget.enclosingNodeOrSelfWithClass('text-editor-line-decoration-icon-issue');
|
|
880
|
-
const anchor =
|
|
881
|
-
anchorElement ? anchorElement.boxInWindow() : new AnchorBox(mouseEvent.clientX, mouseEvent.clientY, 1, 1);
|
|
882
|
-
const messagesOutline = messageBucket.getPopoverMessages(eventTarget);
|
|
883
|
-
if (!messagesOutline) {
|
|
884
|
-
return null;
|
|
885
722
|
}
|
|
886
|
-
return {
|
|
887
|
-
box: anchor,
|
|
888
|
-
hide(): void{},
|
|
889
|
-
show: (popover: UI.GlassPane.GlassPane): Promise<true> => {
|
|
890
|
-
popover.contentElement.append(messagesOutline);
|
|
891
|
-
return Promise.resolve(true);
|
|
892
|
-
},
|
|
893
|
-
};
|
|
894
723
|
}
|
|
724
|
+
return counts;
|
|
895
725
|
}
|
|
896
726
|
|
|
897
|
-
function
|
|
898
|
-
const
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
727
|
+
function renderMessage(message: Workspace.UISourceCode.Message, count: number): HTMLElement {
|
|
728
|
+
const element = document.createElement('div');
|
|
729
|
+
element.classList.add('text-editor-row-message');
|
|
730
|
+
|
|
731
|
+
if (count === 1) {
|
|
732
|
+
const icon = element.appendChild(new IconButton.Icon.Icon());
|
|
733
|
+
icon.data = getIconDataForMessage(message);
|
|
734
|
+
icon.classList.add('text-editor-row-message-icon');
|
|
735
|
+
icon.addEventListener('click', () => (message.clickHandler() || Math.min)());
|
|
736
|
+
} else {
|
|
737
|
+
const repeatCountElement =
|
|
738
|
+
document.createElement('span', {is: 'dt-small-bubble'}) as UI.UIUtils.DevToolsSmallBubble;
|
|
739
|
+
repeatCountElement.textContent = String(count);
|
|
740
|
+
repeatCountElement.classList.add('text-editor-row-message-repeat-count');
|
|
741
|
+
element.appendChild(repeatCountElement);
|
|
742
|
+
repeatCountElement.type = getBubbleTypePerLevel(message.level());
|
|
743
|
+
}
|
|
744
|
+
const linesContainer = element.createChild('div');
|
|
745
|
+
for (const line of message.text().split('\n')) {
|
|
746
|
+
linesContainer.createChild('div').textContent = line;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
return element;
|
|
904
750
|
}
|
|
905
751
|
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
752
|
+
const rowMessageTheme = CodeMirror.EditorView.baseTheme({
|
|
753
|
+
'.cm-tooltip-message': {
|
|
754
|
+
padding: '4px',
|
|
755
|
+
},
|
|
756
|
+
|
|
757
|
+
'.cm-waveUnderline': {
|
|
758
|
+
backgroundImage: 'var(--image-file-errorWave)',
|
|
759
|
+
backgroundRepeat: 'repeat-x',
|
|
760
|
+
backgroundPosition: 'bottom',
|
|
761
|
+
paddingBottom: '1px',
|
|
762
|
+
},
|
|
763
|
+
|
|
764
|
+
'.cm-messageIcon': {
|
|
765
|
+
cursor: 'pointer',
|
|
766
|
+
'& > *': {
|
|
767
|
+
verticalAlign: 'text-bottom',
|
|
768
|
+
marginLeft: '2px',
|
|
769
|
+
},
|
|
770
|
+
},
|
|
771
|
+
});
|
|
772
|
+
|
|
773
|
+
function rowMessages(initialMessages: Workspace.UISourceCode.Message[]): CodeMirror.Extension {
|
|
774
|
+
return [
|
|
775
|
+
showRowMessages.init(
|
|
776
|
+
(state): RowMessageDecorations => RowMessageDecorations.create(RowMessages.create(initialMessages), state.doc)),
|
|
777
|
+
rowMessageTheme,
|
|
778
|
+
];
|
|
910
779
|
}
|
|
911
|
-
|
|
912
|
-
export type EventTypes = {
|
|
913
|
-
[Events.ToolbarItemsChanged]: void,
|
|
914
|
-
};
|