chrome-devtools-frontend 1.0.927127 → 1.0.928589
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/AUTHORS +1 -0
- package/config/gni/all_devtools_files.gni +0 -1
- package/config/gni/devtools_grd_files.gni +30 -4
- package/config/gni/devtools_image_files.gni +1 -0
- package/front_end/Images/src/ic_preview_feature.svg +3 -0
- package/front_end/Tests.js +2 -3
- package/front_end/core/common/Settings.ts +26 -45
- package/front_end/core/host/UserMetrics.ts +2 -2
- package/front_end/core/i18n/locales/en-US.json +60 -15
- package/front_end/core/i18n/locales/en-XL.json +60 -15
- package/front_end/core/platform/keyboard-utilities.ts +1 -0
- package/front_end/core/root/Runtime.ts +62 -61
- package/front_end/core/sdk/AccessibilityModel.ts +73 -73
- package/front_end/core/sdk/CPUProfileDataModel.ts +14 -14
- package/front_end/core/sdk/CPUProfilerModel.ts +33 -33
- package/front_end/core/sdk/CPUThrottlingManager.ts +8 -8
- package/front_end/core/sdk/CSSFontFace.ts +10 -10
- package/front_end/core/sdk/CSSMatchedStyles.ts +114 -114
- package/front_end/core/sdk/CSSMedia.ts +22 -22
- package/front_end/core/sdk/CSSMetadata.ts +53 -49
- package/front_end/core/sdk/CSSModel.ts +139 -135
- package/front_end/core/sdk/CSSProperty.ts +18 -18
- package/front_end/core/sdk/CSSRule.ts +15 -15
- package/front_end/core/sdk/CSSStyleDeclaration.ts +49 -47
- package/front_end/core/sdk/CSSStyleSheetHeader.ts +12 -12
- package/front_end/core/sdk/ChildTargetManager.ts +41 -40
- package/front_end/core/sdk/CompilerSourceMappingContentProvider.ts +10 -10
- package/front_end/core/sdk/Connections.ts +81 -81
- package/front_end/core/sdk/ConsoleModel.ts +68 -68
- package/front_end/core/sdk/Cookie.ts +48 -48
- package/front_end/core/sdk/CookieModel.ts +13 -13
- package/front_end/core/sdk/CookieParser.ts +45 -45
- package/front_end/core/sdk/DOMDebuggerModel.ts +131 -131
- package/front_end/core/sdk/DOMModel.ts +264 -252
- package/front_end/core/sdk/DebuggerModel.ts +209 -205
- package/front_end/core/sdk/EmulationModel.ts +76 -76
- package/front_end/core/sdk/FilmStripModel.ts +29 -29
- package/front_end/core/sdk/FrameManager.ts +43 -42
- package/front_end/core/sdk/HeapProfilerModel.ts +36 -36
- package/front_end/core/sdk/IsolateManager.ts +82 -82
- package/front_end/core/sdk/IssuesModel.ts +6 -6
- package/front_end/core/sdk/LayerTreeBase.ts +37 -37
- package/front_end/core/sdk/LogModel.ts +5 -5
- package/front_end/core/sdk/NetworkManager.ts +229 -225
- package/front_end/core/sdk/NetworkRequest.ts +368 -360
- package/front_end/core/sdk/OverlayColorGenerator.ts +9 -9
- package/front_end/core/sdk/OverlayModel.ts +155 -153
- package/front_end/core/sdk/OverlayPersistentHighlighter.ts +100 -101
- package/front_end/core/sdk/PageResourceLoader.ts +30 -30
- package/front_end/core/sdk/PaintProfiler.ts +16 -16
- package/front_end/core/sdk/PerformanceMetricsModel.ts +12 -12
- package/front_end/core/sdk/ProfileTreeModel.ts +3 -3
- package/front_end/core/sdk/RemoteObject.ts +108 -104
- package/front_end/core/sdk/Resource.ts +85 -84
- package/front_end/core/sdk/ResourceTreeModel.ts +150 -145
- package/front_end/core/sdk/RuntimeModel.ts +38 -34
- package/front_end/core/sdk/SDKModel.ts +3 -3
- package/front_end/core/sdk/ScreenCaptureModel.ts +19 -19
- package/front_end/core/sdk/Script.ts +29 -29
- package/front_end/core/sdk/SecurityOriginManager.ts +19 -19
- package/front_end/core/sdk/ServerTiming.ts +2 -2
- package/front_end/core/sdk/ServiceWorkerCacheModel.ts +43 -43
- package/front_end/core/sdk/ServiceWorkerManager.ts +72 -68
- package/front_end/core/sdk/SourceMap.ts +40 -36
- package/front_end/core/sdk/SourceMapManager.ts +57 -57
- package/front_end/core/sdk/Target.ts +64 -63
- package/front_end/core/sdk/TargetManager.ts +60 -56
- package/front_end/core/sdk/TracingManager.ts +39 -39
- package/front_end/core/sdk/TracingModel.ts +125 -125
- package/front_end/core/sdk/WebAuthnModel.ts +9 -9
- package/front_end/entrypoints/lighthouse_worker/{LighthouseService.js → LighthouseService.ts} +20 -45
- package/front_end/entrypoints/lighthouse_worker/{lighthouse_worker.js → lighthouse_worker.ts} +0 -0
- package/front_end/entrypoints/main/MainImpl.ts +7 -2
- package/front_end/legacy_test_runner/elements_test_runner/ElementsTestRunner.js +4 -4
- package/front_end/legacy_test_runner/sdk_test_runner/sdk_test_runner.js +1 -1
- package/front_end/models/bindings/DebuggerWorkspaceBinding.ts +0 -6
- package/front_end/models/issues_manager/GenericIssue.ts +86 -0
- package/front_end/models/issues_manager/Issue.ts +24 -0
- package/front_end/models/issues_manager/IssuesManager.ts +18 -6
- package/front_end/models/issues_manager/descriptions/genericCrossOriginPortalPostMessageError.md +3 -0
- package/front_end/models/issues_manager/issues_manager.ts +2 -0
- package/front_end/models/javascript_metadata/NativeFunctions.js +5422 -1
- package/front_end/panels/console/ConsoleSidebar.ts +0 -3
- package/front_end/panels/elements/ElementsTreeElement.ts +53 -61
- package/front_end/panels/elements/ElementsTreeOutline.ts +0 -1
- package/front_end/panels/elements/components/LayoutPane.ts +5 -1
- package/front_end/panels/issues/GenericIssueDetailsView.ts +68 -0
- package/front_end/panels/issues/IssueAggregator.ts +16 -0
- package/front_end/panels/issues/IssueKindView.ts +95 -0
- package/front_end/panels/issues/IssueView.ts +6 -0
- package/front_end/panels/issues/IssuesPane.ts +81 -18
- package/front_end/panels/issues/issuesTree.css +8 -3
- package/front_end/panels/lighthouse/LighthouseController.ts +3 -1
- package/front_end/panels/network/NetworkItemView.ts +1 -1
- package/front_end/panels/network/networkLogView.css +5 -0
- package/front_end/panels/sensors/LocationsSettingsTab.ts +1 -1
- package/front_end/panels/settings/SettingsScreen.ts +1 -0
- package/front_end/panels/settings/settingsScreen.css +24 -0
- package/front_end/panels/snippets/SnippetsQuickOpen.ts +8 -3
- package/front_end/panels/sources/TabbedEditorContainer.ts +1 -1
- package/front_end/panels/sources/sources-meta.ts +22 -7
- package/front_end/third_party/codemirror.next/chunk/codemirror.js +1 -1
- package/front_end/third_party/codemirror.next/package.json +4 -4
- package/front_end/ui/components/code_highlighter/CodeHighlighter.ts +137 -0
- package/front_end/ui/components/code_highlighter/codeHighlighter.css +51 -0
- package/front_end/ui/components/code_highlighter/code_highlighter.ts +11 -0
- package/front_end/ui/components/docs/text_editor/basic.html +28 -0
- package/front_end/ui/components/docs/text_editor/basic.ts +14 -0
- package/front_end/ui/components/docs/text_prompt/basic.html +35 -0
- package/front_end/ui/components/docs/text_prompt/basic.ts +19 -0
- package/front_end/ui/components/issue_counter/IssueLinkIcon.ts +1 -0
- package/front_end/ui/components/render_coordinator/RenderCoordinator.ts +17 -0
- package/front_end/ui/components/request_link_icon/RequestLinkIcon.ts +1 -0
- package/front_end/ui/components/text_editor/TextEditor.ts +161 -0
- package/front_end/ui/components/text_editor/config.ts +264 -0
- package/front_end/{panels/console/components/components.ts → ui/components/text_editor/text_editor.ts} +2 -5
- package/front_end/ui/components/text_editor/theme.ts +113 -0
- package/front_end/ui/components/text_prompt/TextPrompt.ts +144 -0
- package/front_end/ui/components/text_prompt/textPrompt.css +33 -0
- package/front_end/ui/components/text_prompt/text_prompt.ts +9 -0
- package/front_end/ui/legacy/ARIAUtils.ts +14 -11
- package/front_end/ui/legacy/TabbedPane.ts +32 -3
- package/front_end/ui/legacy/UIUtils.ts +3 -1
- package/front_end/ui/legacy/View.ts +6 -0
- package/front_end/ui/legacy/ViewManager.ts +5 -1
- package/front_end/ui/legacy/ViewRegistration.ts +5 -0
- package/front_end/ui/legacy/XLink.ts +1 -1
- package/front_end/ui/legacy/closeButton.css +6 -0
- package/front_end/ui/legacy/components/quick_open/CommandMenu.ts +8 -3
- package/front_end/ui/legacy/components/quick_open/FilteredListWidget.ts +38 -38
- package/front_end/ui/legacy/components/quick_open/HelpQuickOpen.ts +10 -4
- package/front_end/ui/legacy/components/quick_open/QuickOpen.ts +23 -6
- package/front_end/ui/legacy/components/quick_open/filteredListWidget.css +14 -16
- package/front_end/ui/legacy/filter.css +1 -0
- package/front_end/ui/legacy/tabbedPane.css +24 -0
- package/front_end/ui/legacy/toolbar.css +5 -0
- package/inspector_overlay/main.ts +2 -1
- package/inspector_overlay/tool_screenshot.ts +8 -1
- package/package.json +1 -1
- package/scripts/build/rollup.config.js +9 -0
- package/scripts/migration/class-fields/migrate.js +56 -0
- package/scripts/migration/class-fields/package.json +5 -0
- package/front_end/panels/console/components/SidebarDeprecation.ts +0 -58
- package/front_end/panels/console/components/sidebarDeprecation.css +0 -17
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
// Copyright 2021 The Chromium Authors. All rights reserved.
|
|
2
|
+
// Use of this source code is governed by a BSD-style license that can be
|
|
3
|
+
// found in the LICENSE file.
|
|
4
|
+
|
|
5
|
+
import * as Common from '../../../core/common/common.js';
|
|
6
|
+
import * as i18n from '../../../core/i18n/i18n.js';
|
|
7
|
+
import * as CM from '../../../third_party/codemirror.next/codemirror.next.js';
|
|
8
|
+
import * as CodeHighlighter from '../code_highlighter/code_highlighter.js';
|
|
9
|
+
|
|
10
|
+
import {editorTheme} from './theme.js';
|
|
11
|
+
|
|
12
|
+
const LINES_TO_SCAN_FOR_INDENTATION_GUESSING = 1000;
|
|
13
|
+
|
|
14
|
+
const UIStrings = {
|
|
15
|
+
/**
|
|
16
|
+
*@description Label text for the editor
|
|
17
|
+
*/
|
|
18
|
+
codeEditor: 'Code editor',
|
|
19
|
+
};
|
|
20
|
+
const str_ = i18n.i18n.registerUIStrings('ui/components/text_editor/config.ts', UIStrings);
|
|
21
|
+
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
|
22
|
+
|
|
23
|
+
const empty: CM.Extension = [];
|
|
24
|
+
|
|
25
|
+
export const dynamicSetting = CM.Facet.define<DynamicSetting<unknown>>();
|
|
26
|
+
|
|
27
|
+
// The code below is used to wire up dynamic settings to editors. When
|
|
28
|
+
// you include one of these objects in an editor configuration, the
|
|
29
|
+
// TextEditor class will take care of listening to changes in the
|
|
30
|
+
// setting, and updating the configuration as appropriate.
|
|
31
|
+
|
|
32
|
+
export class DynamicSetting<T> {
|
|
33
|
+
compartment = new CM.Compartment();
|
|
34
|
+
extension: CM.Extension;
|
|
35
|
+
|
|
36
|
+
constructor(
|
|
37
|
+
readonly settingName: string,
|
|
38
|
+
private readonly getExtension: (value: T, state: CM.EditorState) => CM.Extension,
|
|
39
|
+
) {
|
|
40
|
+
this.extension = [this.compartment.of(empty), dynamicSetting.of(this as DynamicSetting<unknown>)];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
sync(state: CM.EditorState, value: T): CM.StateEffect<unknown>|null {
|
|
44
|
+
const cur = this.compartment.get(state);
|
|
45
|
+
const needed = this.getExtension(value, state);
|
|
46
|
+
return cur === needed ? null : this.compartment.reconfigure(needed);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
static bool(name: string, enabled: CM.Extension, disabled: CM.Extension = empty): DynamicSetting<boolean> {
|
|
50
|
+
return new DynamicSetting<boolean>(name, val => val ? enabled : disabled);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export const tabMovesFocus = DynamicSetting.bool('textEditorTabMovesFocus', CM.keymap.of([{
|
|
55
|
+
key: 'Tab',
|
|
56
|
+
run: (view: CM.EditorView): boolean => view.state.doc.length ? CM.indentMore(view) : false,
|
|
57
|
+
shift: (view: CM.EditorView): boolean => view.state.doc.length ? CM.indentLess(view) : false,
|
|
58
|
+
}]));
|
|
59
|
+
|
|
60
|
+
export const bracketMatching = DynamicSetting.bool('textEditorBracketMatching', CM.bracketMatching());
|
|
61
|
+
|
|
62
|
+
export function guessIndent(doc: CM.Text): string {
|
|
63
|
+
const values: {[indent: string]: number} = Object.create(null);
|
|
64
|
+
let scanned = 0;
|
|
65
|
+
for (let cur = doc.iterLines(1, Math.min(doc.lines + 1, LINES_TO_SCAN_FOR_INDENTATION_GUESSING)); !cur.next().done;) {
|
|
66
|
+
let space = (/^\s*/.exec(cur.value) as string[])[0];
|
|
67
|
+
if (space.length === cur.value.length || !space.length) {
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
if (space[0] === '\t') {
|
|
71
|
+
space = '\t';
|
|
72
|
+
} else if (/[^ ]/.test(space)) {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
scanned++;
|
|
76
|
+
values[space] = (values[space] || 0) + 1;
|
|
77
|
+
}
|
|
78
|
+
const minOccurrence = scanned * 0.05;
|
|
79
|
+
const sorted = Object.entries(values).filter(e => e[1] > minOccurrence).sort((a, b) => a[1] - b[1]);
|
|
80
|
+
return sorted.length ? sorted[0][0] : Common.Settings.Settings.instance().moduleSetting('textEditorIndent').get();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const cachedIndentUnit: {[indent: string]: CM.Extension} = Object.create(null);
|
|
84
|
+
|
|
85
|
+
function getIndentUnit(indent: string): CM.Extension {
|
|
86
|
+
let value = cachedIndentUnit[indent];
|
|
87
|
+
if (!value) {
|
|
88
|
+
value = cachedIndentUnit[indent] = CM.indentUnit.of(indent);
|
|
89
|
+
}
|
|
90
|
+
return value;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export const autoDetectIndent = new DynamicSetting<boolean>('textEditorAutoDetectIndent', (on, state) => {
|
|
94
|
+
return on ? CM.Prec.override(getIndentUnit(guessIndent(state.doc))) : empty;
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
function matcher(decorator: CM.MatchDecorator): CM.Extension {
|
|
98
|
+
return CM.ViewPlugin.define(
|
|
99
|
+
view => ({
|
|
100
|
+
decorations: decorator.createDeco(view),
|
|
101
|
+
update(u): void {
|
|
102
|
+
this.decorations = decorator.updateDeco(u, this.decorations);
|
|
103
|
+
},
|
|
104
|
+
}),
|
|
105
|
+
{
|
|
106
|
+
decorations: v => v.decorations,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const WhitespaceDeco = new Map<string, CM.Decoration>();
|
|
111
|
+
|
|
112
|
+
function getWhitespaceDeco(space: string): CM.Decoration {
|
|
113
|
+
const cached = WhitespaceDeco.get(space);
|
|
114
|
+
if (cached) {
|
|
115
|
+
return cached;
|
|
116
|
+
}
|
|
117
|
+
const result = CM.Decoration.mark({
|
|
118
|
+
attributes: space === '\t' ? {
|
|
119
|
+
class: 'cm-highlightedTab',
|
|
120
|
+
} :
|
|
121
|
+
{class: 'cm-highlightedSpaces', 'data-display': '·'.repeat(space.length)},
|
|
122
|
+
});
|
|
123
|
+
WhitespaceDeco.set(space, result);
|
|
124
|
+
return result;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const showAllWhitespace = matcher(new CM.MatchDecorator({
|
|
128
|
+
regexp: /\t| +/g,
|
|
129
|
+
decoration: (match: RegExpExecArray): CM.Decoration => getWhitespaceDeco(match[0]),
|
|
130
|
+
boundary: /\S/,
|
|
131
|
+
}));
|
|
132
|
+
|
|
133
|
+
const showTrailingWhitespace = matcher(new CM.MatchDecorator({
|
|
134
|
+
regexp: /\s+$/g,
|
|
135
|
+
decoration: CM.Decoration.mark({class: 'cm-trailingWhitespace'}),
|
|
136
|
+
boundary: /\S/,
|
|
137
|
+
}));
|
|
138
|
+
|
|
139
|
+
export const showWhitespace = new DynamicSetting<string>('showWhitespacesInEditor', value => {
|
|
140
|
+
if (value === 'all') {
|
|
141
|
+
return showAllWhitespace;
|
|
142
|
+
}
|
|
143
|
+
if (value === 'trailing') {
|
|
144
|
+
return showTrailingWhitespace;
|
|
145
|
+
}
|
|
146
|
+
return empty;
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
export const allowScrollPastEof = DynamicSetting.bool('allowScrollPastEof', CM.scrollPastEnd());
|
|
150
|
+
|
|
151
|
+
export const indentUnit = new DynamicSetting<string>('textEditorIndent', getIndentUnit);
|
|
152
|
+
|
|
153
|
+
export const domWordWrap = DynamicSetting.bool('domWordWrap', CM.EditorView.lineWrapping);
|
|
154
|
+
|
|
155
|
+
function detectLineSeparator(text: string): CM.Extension {
|
|
156
|
+
if (/\r\n/.test(text) && !/(^|[^\r])\n/.test(text)) {
|
|
157
|
+
return CM.EditorState.lineSeparator.of('\r\n');
|
|
158
|
+
}
|
|
159
|
+
return [];
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const baseKeymap = CM.keymap.of([
|
|
163
|
+
{key: 'Ctrl-m', run: CM.cursorMatchingBracket, shift: CM.selectMatchingBracket},
|
|
164
|
+
{key: 'Mod-/', run: CM.toggleComment},
|
|
165
|
+
{key: 'Mod-d', run: CM.selectNextOccurrence},
|
|
166
|
+
{key: 'Alt-ArrowLeft', mac: 'Ctrl-ArrowLeft', run: CM.cursorSubwordBackward, shift: CM.selectSubwordBackward},
|
|
167
|
+
{key: 'Alt-ArrowRight', mac: 'Ctrl-ArrowRight', run: CM.cursorSubwordForward, shift: CM.selectSubwordForward},
|
|
168
|
+
...CM.closeBracketsKeymap,
|
|
169
|
+
...CM.standardKeymap,
|
|
170
|
+
...CM.historyKeymap,
|
|
171
|
+
]);
|
|
172
|
+
|
|
173
|
+
function themeIsDark(): boolean {
|
|
174
|
+
const setting = Common.Settings.Settings.instance().moduleSetting('uiTheme').get();
|
|
175
|
+
return setting === 'systemPreferred' ? window.matchMedia('(prefers-color-scheme: dark)').matches : setting === 'dark';
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const dummyDarkTheme = CM.EditorView.theme({}, {dark: true});
|
|
179
|
+
|
|
180
|
+
export function baseConfiguration(text: string): CM.Extension {
|
|
181
|
+
return [
|
|
182
|
+
editorTheme,
|
|
183
|
+
themeIsDark() ? dummyDarkTheme : [],
|
|
184
|
+
CM.highlightSpecialChars(),
|
|
185
|
+
CM.history(),
|
|
186
|
+
CM.drawSelection(),
|
|
187
|
+
CM.EditorState.allowMultipleSelections.of(true),
|
|
188
|
+
CM.indentOnInput(),
|
|
189
|
+
CodeHighlighter.CodeHighlighter.getHighlightStyle(CM),
|
|
190
|
+
CM.closeBrackets(),
|
|
191
|
+
baseKeymap,
|
|
192
|
+
tabMovesFocus,
|
|
193
|
+
bracketMatching,
|
|
194
|
+
indentUnit,
|
|
195
|
+
CM.Prec.fallback(CM.EditorView.contentAttributes.of({'aria-label': i18nString(UIStrings.codeEditor)})),
|
|
196
|
+
detectLineSeparator(text),
|
|
197
|
+
CM.tooltips({position: 'absolute'}),
|
|
198
|
+
];
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
class CompletionHint extends CM.WidgetType {
|
|
202
|
+
constructor(readonly text: string) {
|
|
203
|
+
super();
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
eq(other: CompletionHint): boolean {
|
|
207
|
+
return this.text === other.text;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
toDOM(): HTMLElement {
|
|
211
|
+
const span = document.createElement('span');
|
|
212
|
+
span.className = 'cm-completionHint';
|
|
213
|
+
span.textContent = this.text;
|
|
214
|
+
return span;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export const showCompletionHint = CM.ViewPlugin.fromClass(class {
|
|
219
|
+
decorations: CM.DecorationSet = CM.Decoration.none;
|
|
220
|
+
currentHint: string|null = null;
|
|
221
|
+
|
|
222
|
+
update(update: CM.ViewUpdate): void {
|
|
223
|
+
const top = this.currentHint = this.topCompletion(update.state);
|
|
224
|
+
if (!top) {
|
|
225
|
+
this.decorations = CM.Decoration.none;
|
|
226
|
+
} else {
|
|
227
|
+
this.decorations = CM.Decoration.set(
|
|
228
|
+
[CM.Decoration.widget({widget: new CompletionHint(top), side: 1}).range(update.state.selection.main.head)]);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
topCompletion(state: CM.EditorState): string|null {
|
|
233
|
+
const completions = CM.currentCompletions(state);
|
|
234
|
+
if (!completions.length) {
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
const {label} = completions[0];
|
|
238
|
+
if (label.length > 100 || label.indexOf('\n') > -1) {
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
const pos = state.selection.main.head;
|
|
242
|
+
const lineBefore = state.doc.lineAt(pos);
|
|
243
|
+
if (pos !== lineBefore.to) {
|
|
244
|
+
return null;
|
|
245
|
+
}
|
|
246
|
+
const textBefore = lineBefore.text.slice(0, pos - lineBefore.from);
|
|
247
|
+
for (let i = label.length - 1; i > 0; i--) {
|
|
248
|
+
if (textBefore.endsWith(label.slice(0, i)) && !/\w/.test(textBefore.charAt(textBefore.length - i - 1))) {
|
|
249
|
+
return label.slice(i);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return null;
|
|
253
|
+
}
|
|
254
|
+
}, {decorations: p => p.decorations});
|
|
255
|
+
|
|
256
|
+
export function contentIncludingHint(view: CM.EditorView): string {
|
|
257
|
+
const plugin = view.plugin(showCompletionHint);
|
|
258
|
+
let content = view.state.doc.toString();
|
|
259
|
+
if (plugin && plugin.currentHint) {
|
|
260
|
+
const {head} = view.state.selection.main;
|
|
261
|
+
content = content.slice(0, head) + plugin.currentHint + content.slice(head);
|
|
262
|
+
}
|
|
263
|
+
return content;
|
|
264
|
+
}
|
|
@@ -2,8 +2,5 @@
|
|
|
2
2
|
// Use of this source code is governed by a BSD-style license that can be
|
|
3
3
|
// found in the LICENSE file.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
export {
|
|
8
|
-
SidebarDeprecation,
|
|
9
|
-
};
|
|
5
|
+
export * as Config from './config.js';
|
|
6
|
+
export * as TextEditor from './TextEditor.js';
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// Copyright 2021 The Chromium Authors. All rights reserved.
|
|
2
|
+
// Use of this source code is governed by a BSD-style license that can be
|
|
3
|
+
// found in the LICENSE file.
|
|
4
|
+
|
|
5
|
+
import * as CM from '../../../third_party/codemirror.next/codemirror.next.js';
|
|
6
|
+
|
|
7
|
+
export const editorTheme = CM.EditorView.theme({
|
|
8
|
+
'&.cm-editor': {
|
|
9
|
+
color: 'color: var(--color-text-primary)',
|
|
10
|
+
},
|
|
11
|
+
|
|
12
|
+
'.cm-scroller': {
|
|
13
|
+
lineHeight: '1.2em',
|
|
14
|
+
fontFamily: 'var(--source-code-font-family)',
|
|
15
|
+
fontSize: 'var(--source-code-font-size)',
|
|
16
|
+
},
|
|
17
|
+
|
|
18
|
+
'.cm-panels, .cm-tooltip': {
|
|
19
|
+
backgroundColor: 'var(--color-background-elevation-1)',
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
'.cm-selectionMatch': {
|
|
23
|
+
backgroundColor: 'var(--color-selection-highlight)',
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
'.cm-cursor': {
|
|
27
|
+
borderLeft: '1px solid var(--color-background-inverted)',
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
'&.cm-readonly .cm-cursor': {
|
|
31
|
+
display: 'none',
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
'.cm-cursor-secondary': {
|
|
35
|
+
borderLeft: '1px solid var(--color-secondary-cursor)',
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
'.cm-selectionBackground': {
|
|
39
|
+
background: 'var(--color-editor-selection-selection)',
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
'&.cm-focused .cm-selectionBackground': {
|
|
43
|
+
background: 'var(--color-editor-selection)',
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
'.cm-gutters': {
|
|
47
|
+
borderRight: '1px solid var(--color-details-hairline)',
|
|
48
|
+
whiteSpace: 'nowrap',
|
|
49
|
+
backgroundColor: 'var(--color-background)',
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
'.cm-lineNumbers .cm-gutterElement': {
|
|
53
|
+
color: 'var(--color-line-number)',
|
|
54
|
+
padding: '0 3px 0 9px',
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
'&:focus-within .cm-matchingBracket': {
|
|
58
|
+
color: 'inherit',
|
|
59
|
+
backgroundColor: 'var(--color-matching-bracket-background)',
|
|
60
|
+
borderBottom: '1px solid var(--color-matching-bracket-underline)',
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
'&:focus-within .cm-nonmatchingBracket': {
|
|
64
|
+
backgroundColor: 'var(--color-nonmatching-bracket-background)',
|
|
65
|
+
borderBottom: '1px solid var(--color-nonmatching-bracket-underline)',
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
'.cm-trailingWhitespace': {
|
|
69
|
+
backgroundColor: 'var(--color-error-text)',
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
'.cm-highlightedTab': {
|
|
73
|
+
display: 'inline-block',
|
|
74
|
+
position: 'relative',
|
|
75
|
+
'&:before': {
|
|
76
|
+
content: '""',
|
|
77
|
+
borderBottom: '1px solid var(--color-text-secondary)',
|
|
78
|
+
position: 'absolute',
|
|
79
|
+
left: '5%',
|
|
80
|
+
bottom: '50%',
|
|
81
|
+
width: '90%',
|
|
82
|
+
pointerEvents: 'none',
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
'.cm-highlightedSpaces:before': {
|
|
87
|
+
color: 'var(--color-text-secondary)',
|
|
88
|
+
content: 'attr(data-display)',
|
|
89
|
+
position: 'absolute',
|
|
90
|
+
pointerEvents: 'none',
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
'.cm-placeholder': {
|
|
94
|
+
color: 'var(--color-text-secondary)',
|
|
95
|
+
},
|
|
96
|
+
|
|
97
|
+
'.cm-completionHint': {
|
|
98
|
+
color: 'var(--color-text-secondary)',
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
'.cm-highlightedLine': {
|
|
102
|
+
animation: 'cm-fading-highlight 2s 0s',
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
'@keyframes cm-fading-highlight': {
|
|
106
|
+
from: {
|
|
107
|
+
backgroundColor: 'var(--color-highlighted-line)',
|
|
108
|
+
},
|
|
109
|
+
to: {
|
|
110
|
+
backgroundColor: 'transparent',
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
});
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
// Copyright 2021 The Chromium Authors. All rights reserved.
|
|
2
|
+
// Use of this source code is governed by a BSD-style license that can be
|
|
3
|
+
// found in the LICENSE file.
|
|
4
|
+
|
|
5
|
+
import * as Platform from '../../../core/platform/platform.js';
|
|
6
|
+
import * as ComponentHelpers from '../../components/helpers/helpers.js';
|
|
7
|
+
import * as LitHtml from '../../lit-html/lit-html.js';
|
|
8
|
+
|
|
9
|
+
import textPromptStyles from './textPrompt.css.js';
|
|
10
|
+
|
|
11
|
+
export interface TextPromptData {
|
|
12
|
+
ariaLabel: string;
|
|
13
|
+
prefix: string;
|
|
14
|
+
suggestion: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class PromptInputEvent extends Event {
|
|
18
|
+
static readonly eventName = 'promptinputchanged';
|
|
19
|
+
data: string;
|
|
20
|
+
|
|
21
|
+
constructor(value: string) {
|
|
22
|
+
super(PromptInputEvent.eventName);
|
|
23
|
+
this.data = value;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export class TextPrompt extends HTMLElement {
|
|
28
|
+
static readonly litTagName = LitHtml.literal`devtools-text-prompt`;
|
|
29
|
+
private readonly shadow = this.attachShadow({mode: 'open'});
|
|
30
|
+
private ariaLabelText = '';
|
|
31
|
+
private prefixText = '';
|
|
32
|
+
private suggestionText = '';
|
|
33
|
+
|
|
34
|
+
connectedCallback(): void {
|
|
35
|
+
this.shadow.adoptedStyleSheets = [textPromptStyles];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
set data(data: TextPromptData) {
|
|
39
|
+
this.ariaLabelText = data.ariaLabel;
|
|
40
|
+
this.prefixText = data.prefix;
|
|
41
|
+
this.suggestionText = data.suggestion;
|
|
42
|
+
this.render();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
get data(): TextPromptData {
|
|
46
|
+
return {
|
|
47
|
+
ariaLabel: this.ariaLabelText,
|
|
48
|
+
prefix: this.prefixText,
|
|
49
|
+
suggestion: this.suggestionText,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
focus(): void {
|
|
54
|
+
this.input().focus();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
private input(): HTMLElement {
|
|
58
|
+
const inputElement = this.shadow.querySelector('.text-prompt-input');
|
|
59
|
+
if (!inputElement) {
|
|
60
|
+
throw new Error('Expected an input element!');
|
|
61
|
+
}
|
|
62
|
+
return /** @type {!HTMLElement} */ inputElement as HTMLElement;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
moveCaretToEndOfInput(): void {
|
|
66
|
+
this.setSelectedRange(this.text().length, this.text().length);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
onInput(): void {
|
|
70
|
+
this.dispatchEvent(new PromptInputEvent(this.text().trim()));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
onKeyDown(event: KeyboardEvent): void {
|
|
74
|
+
if (event.key === Platform.KeyboardUtilities.ENTER_KEY) {
|
|
75
|
+
event.preventDefault();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
setSelectedRange(startIndex: number, endIndex: number): void {
|
|
80
|
+
if (startIndex < 0) {
|
|
81
|
+
throw new RangeError('Selected range start must be a nonnegative integer');
|
|
82
|
+
}
|
|
83
|
+
const textContentLength = this.text().length;
|
|
84
|
+
if (endIndex > textContentLength) {
|
|
85
|
+
endIndex = textContentLength;
|
|
86
|
+
}
|
|
87
|
+
if (endIndex < startIndex) {
|
|
88
|
+
endIndex = startIndex;
|
|
89
|
+
}
|
|
90
|
+
const inputBox = this.input();
|
|
91
|
+
const range = document.createRange();
|
|
92
|
+
range.setStart(inputBox, startIndex);
|
|
93
|
+
range.setEnd(inputBox, endIndex);
|
|
94
|
+
const selection = window.getSelection();
|
|
95
|
+
if (selection) {
|
|
96
|
+
selection.removeAllRanges();
|
|
97
|
+
selection.addRange(range);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
setPrefix(prefix: string): void {
|
|
102
|
+
this.prefixText = prefix;
|
|
103
|
+
this.render();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
setSuggestion(suggestion: string): void {
|
|
107
|
+
this.suggestionText = suggestion;
|
|
108
|
+
this.render();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
setText(text: string): void {
|
|
112
|
+
this.input().textContent = text;
|
|
113
|
+
if (this.input().hasFocus()) {
|
|
114
|
+
this.moveCaretToEndOfInput();
|
|
115
|
+
this.input().scrollIntoView();
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
private text(): string {
|
|
120
|
+
return this.input().textContent || '';
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
private render(): void {
|
|
124
|
+
const output = LitHtml.html`
|
|
125
|
+
<span class="prefix">${this.prefixText}</span>
|
|
126
|
+
<input aria-label=${this.ariaLabelText}>
|
|
127
|
+
<div class="text-prompt-input" spellcheck="false" contenteditable="plaintext-only" @keydown=${
|
|
128
|
+
this.onKeyDown} @input=${this.onInput} suggestion=" ${this.suggestionText}"></div>
|
|
129
|
+
</input>`;
|
|
130
|
+
LitHtml.render(output, this.shadow, {host: this});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
ComponentHelpers.CustomElements.defineComponent('devtools-text-prompt', TextPrompt);
|
|
135
|
+
|
|
136
|
+
declare global {
|
|
137
|
+
interface HTMLElementTagNameMap {
|
|
138
|
+
'devtools-text-prompt': TextPrompt;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
interface HTMLElementEventMap {
|
|
142
|
+
'promptinputchanged': PromptInputEvent;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2021 The Chromium Authors. All rights reserved.
|
|
3
|
+
* Use of this source code is governed by a BSD-style license that can be
|
|
4
|
+
* found in the LICENSE file.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
:host {
|
|
8
|
+
white-space: pre;
|
|
9
|
+
overflow: hidden;
|
|
10
|
+
display: inline-flex;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.prefix {
|
|
14
|
+
color: var(--color-primary);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
input {
|
|
18
|
+
width: 0;
|
|
19
|
+
border: none;
|
|
20
|
+
outline: none;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.text-prompt-input {
|
|
24
|
+
border: none;
|
|
25
|
+
outline: none;
|
|
26
|
+
display: inline;
|
|
27
|
+
flex: 1 0 auto;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.text-prompt-input::after {
|
|
31
|
+
content: attr(suggestion);
|
|
32
|
+
color: var(--color-background-highlight);
|
|
33
|
+
}
|
|
@@ -462,23 +462,26 @@ function hideFromLayout(element: HTMLElement): void {
|
|
|
462
462
|
|
|
463
463
|
let alertElement: HTMLElement|undefined;
|
|
464
464
|
|
|
465
|
-
function
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
465
|
+
export function alertElementInstance(): HTMLElement {
|
|
466
|
+
if (!alertElement) {
|
|
467
|
+
const element = document.body.createChild('div') as HTMLElement;
|
|
468
|
+
hideFromLayout(element);
|
|
469
|
+
element.setAttribute('role', 'alert');
|
|
470
|
+
element.setAttribute('aria-atomic', 'true');
|
|
471
|
+
alertElement = element;
|
|
472
|
+
}
|
|
473
|
+
return alertElement;
|
|
471
474
|
}
|
|
475
|
+
|
|
472
476
|
/**
|
|
473
477
|
* This function is used to announce a message with the screen reader.
|
|
474
478
|
* Setting the textContent would allow the SR to access the offscreen element via browse mode
|
|
475
479
|
*/
|
|
476
480
|
export function alert(message: string): void {
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
}
|
|
481
|
+
const element = alertElementInstance();
|
|
482
|
+
|
|
480
483
|
// We first set the textContent to blank so that the string will announce even if it is replaced
|
|
481
484
|
// with the same string.
|
|
482
|
-
|
|
483
|
-
|
|
485
|
+
element.textContent = '';
|
|
486
|
+
element.textContent = Platform.StringUtilities.trimEndWithMaxLength(message, 10000);
|
|
484
487
|
}
|
|
@@ -70,6 +70,10 @@ const UIStrings = {
|
|
|
70
70
|
*@description Text on a menu option to close all the drawers except Console when right click on a drawer title
|
|
71
71
|
*/
|
|
72
72
|
closeAll: 'Close all',
|
|
73
|
+
/**
|
|
74
|
+
*@description Indicates that a tab contains a preview feature (i.e., a beta / experimental feature).
|
|
75
|
+
*/
|
|
76
|
+
previewFeature: 'Preview feature',
|
|
73
77
|
};
|
|
74
78
|
const str_ = i18n.i18n.registerUIStrings('ui/legacy/TabbedPane.ts', UIStrings);
|
|
75
79
|
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
|
@@ -224,9 +228,9 @@ export class TabbedPane extends Common.ObjectWrapper.eventMixin<EventTypes, type
|
|
|
224
228
|
|
|
225
229
|
appendTab(
|
|
226
230
|
id: string, tabTitle: string, view: Widget, tabTooltip?: string, userGesture?: boolean, isCloseable?: boolean,
|
|
227
|
-
index?: number): void {
|
|
231
|
+
isPreviewFeature?: boolean, index?: number): void {
|
|
228
232
|
const closeable = typeof isCloseable === 'boolean' ? isCloseable : Boolean(this.closeableTabs);
|
|
229
|
-
const tab = new TabbedPaneTab(this, id, tabTitle, closeable, view, tabTooltip);
|
|
233
|
+
const tab = new TabbedPaneTab(this, id, tabTitle, closeable, Boolean(isPreviewFeature), view, tabTooltip);
|
|
230
234
|
tab.setDelegate((this.delegate as TabbedPaneTabDelegate));
|
|
231
235
|
console.assert(!this.tabsById.has(id), `Tabbed pane already contains a tab with id '${id}'`);
|
|
232
236
|
this.tabsById.set(id, tab);
|
|
@@ -966,6 +970,7 @@ export type EventTypes = {
|
|
|
966
970
|
|
|
967
971
|
export class TabbedPaneTab {
|
|
968
972
|
closeable: boolean;
|
|
973
|
+
previewFeature = false;
|
|
969
974
|
private readonly tabbedPane: TabbedPane;
|
|
970
975
|
idInternal: string;
|
|
971
976
|
private titleInternal: string;
|
|
@@ -980,8 +985,11 @@ export class TabbedPaneTab {
|
|
|
980
985
|
private delegate?: TabbedPaneTabDelegate;
|
|
981
986
|
private titleElement?: HTMLElement;
|
|
982
987
|
private dragStartX?: number;
|
|
983
|
-
constructor(
|
|
988
|
+
constructor(
|
|
989
|
+
tabbedPane: TabbedPane, id: string, title: string, closeable: boolean, previewFeature: boolean, view: Widget,
|
|
990
|
+
tooltip?: string) {
|
|
984
991
|
this.closeable = closeable;
|
|
992
|
+
this.previewFeature = previewFeature;
|
|
985
993
|
this.tabbedPane = tabbedPane;
|
|
986
994
|
this.idInternal = id;
|
|
987
995
|
this.titleInternal = title;
|
|
@@ -1107,6 +1115,12 @@ export class TabbedPaneTab {
|
|
|
1107
1115
|
this.titleElement = titleElement;
|
|
1108
1116
|
}
|
|
1109
1117
|
|
|
1118
|
+
if (this.previewFeature) {
|
|
1119
|
+
const previewIcon = this.createPreviewIcon();
|
|
1120
|
+
tabElement.appendChild(previewIcon);
|
|
1121
|
+
tabElement.classList.add('preview');
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1110
1124
|
if (this.closeable) {
|
|
1111
1125
|
const closeIcon = this.createCloseIconButton();
|
|
1112
1126
|
tabElement.appendChild(closeIcon);
|
|
@@ -1148,6 +1162,21 @@ export class TabbedPaneTab {
|
|
|
1148
1162
|
return closeIconContainer;
|
|
1149
1163
|
}
|
|
1150
1164
|
|
|
1165
|
+
private createPreviewIcon(): HTMLDivElement {
|
|
1166
|
+
const previewIcon = document.createElement('div');
|
|
1167
|
+
previewIcon.classList.add('preview-icon');
|
|
1168
|
+
const closeIcon = new IconButton.Icon.Icon();
|
|
1169
|
+
closeIcon.data = {
|
|
1170
|
+
iconName: 'ic_preview_feature',
|
|
1171
|
+
color: 'var(--override-tabbed-pane-preview-icon-color)',
|
|
1172
|
+
width: '14px',
|
|
1173
|
+
};
|
|
1174
|
+
previewIcon.appendChild(closeIcon);
|
|
1175
|
+
previewIcon.setAttribute('title', i18nString(UIStrings.previewFeature));
|
|
1176
|
+
previewIcon.setAttribute('aria-label', i18nString(UIStrings.previewFeature));
|
|
1177
|
+
return previewIcon;
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1151
1180
|
private isCloseIconClicked(element: HTMLElement): boolean {
|
|
1152
1181
|
return element?.classList.contains('tabbed-pane-close-button') ||
|
|
1153
1182
|
element?.parentElement?.classList.contains('tabbed-pane-close-button') || false;
|