blob: cbc80e2f1f0d7321d6c06da592d5ac37d46c5e92 [file] [log] [blame]
Tim van der Lippe83f02be2020-01-23 11:11:401// Copyright 2018 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.
Erik Luo0b789b62018-11-21 19:31:464
Tim van der Lippe021c7572021-04-19 10:49:435import * as i18n from '../../core/i18n/i18n.js';
Marijn Haverbekeb0e3e7c2021-11-15 09:51:526import * as CodeMirror from '../../third_party/codemirror.next/codemirror.next.js';
7import * as TextEditor from '../../ui/components/text_editor/text_editor.js';
Tim van der Lippe021c7572021-04-19 10:49:438import * as UI from '../../ui/legacy/legacy.js';
Tim van der Lippefbbf9812020-02-13 14:43:469
Kriti Sapraae8f17e2021-08-18 10:28:0210import breakpointEditDialogStyles from './breakpointEditDialog.css.js';
11
Simon Zünd697fb0b2021-03-01 10:12:4212const UIStrings = {
Vidal Guillermo Diazleal Ortega83edb472021-02-16 18:39:3213 /**
Jack Franklinfd72c072022-12-21 11:45:0114 *@description Screen reader label for a select box that chooses the breakpoint type in the Sources panel when editing a breakpoint
15 */
Vidal Guillermo Diazleal Ortega83edb472021-02-16 18:39:3216 breakpointType: 'Breakpoint type',
17 /**
Jack Franklinfd72c072022-12-21 11:45:0118 *@description Text in Breakpoint Edit Dialog of the Sources panel
19 */
Vidal Guillermo Diazleal Ortega83edb472021-02-16 18:39:3220 breakpoint: 'Breakpoint',
21 /**
Jack Franklinfd72c072022-12-21 11:45:0122 *@description Text in Breakpoint Edit Dialog of the Sources panel
23 */
Vidal Guillermo Diazleal Ortega83edb472021-02-16 18:39:3224 conditionalBreakpoint: 'Conditional breakpoint',
25 /**
Jack Franklinfd72c072022-12-21 11:45:0126 *@description Text in Breakpoint Edit Dialog of the Sources panel
27 */
Vidal Guillermo Diazleal Ortega83edb472021-02-16 18:39:3228 logpoint: 'Logpoint',
29 /**
Jack Franklinfd72c072022-12-21 11:45:0130 *@description Text in Breakpoint Edit Dialog of the Sources panel
31 */
Vidal Guillermo Diazleal Ortega83edb472021-02-16 18:39:3232 expressionToCheckBeforePausingEg: 'Expression to check before pausing, e.g. x > 5',
33 /**
Jack Franklinfd72c072022-12-21 11:45:0134 *@description Type selector element title in Breakpoint Edit Dialog of the Sources panel
35 */
Vidal Guillermo Diazleal Ortega83edb472021-02-16 18:39:3236 pauseOnlyWhenTheConditionIsTrue: 'Pause only when the condition is true',
37 /**
Jack Franklinfd72c072022-12-21 11:45:0138 *@description Text in Breakpoint Edit Dialog of the Sources panel. It is used as
39 *the placeholder for a text input field before the user enters text. Provides the user with
40 *an example on how to use Logpoints. 'Log' is a verb and 'message' is a noun.
41 *See: https://developer.chrome.com/blog/new-in-devtools-73/#logpoints
42 */
Simon Zünd4cb2aab2021-06-22 05:27:1243 logMessageEgXIsX: 'Log message, e.g. `\'x is\', x`',
Vidal Guillermo Diazleal Ortega83edb472021-02-16 18:39:3244 /**
Jack Franklinfd72c072022-12-21 11:45:0145 *@description Type selector element title in Breakpoint Edit Dialog of the Sources panel
46 */
Vidal Guillermo Diazleal Ortega83edb472021-02-16 18:39:3247 logAMessageToConsoleDoNotBreak: 'Log a message to Console, do not break',
48};
Tim van der Lippe021c7572021-04-19 10:49:4349const str_ = i18n.i18n.registerUIStrings('panels/sources/BreakpointEditDialog.ts', UIStrings);
Vidal Guillermo Diazleal Ortega83edb472021-02-16 18:39:3250const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
Marijn Haverbekecf7dcfa2021-10-06 15:35:0251
Tim van der Lippefbbf9812020-02-13 14:43:4652export class BreakpointEditDialog extends UI.Widget.Widget {
Jan Schefflera222c632021-08-13 12:46:1553 private readonly onFinish: (arg0: {
Jan Scheffler35199b92021-03-17 09:51:1554 committed: boolean,
55 condition: string,
56 }) => Promise<void>;
Jan Schefflera222c632021-08-13 12:46:1557 private finished: boolean;
Marijn Haverbekecf7dcfa2021-10-06 15:35:0258 private editor: TextEditor.TextEditor.TextEditor;
Jan Schefflera222c632021-08-13 12:46:1559 private isLogpoint: boolean;
60 private readonly typeSelector: UI.Toolbar.ToolbarComboBox;
Marijn Haverbekecf7dcfa2021-10-06 15:35:0261 private placeholderCompartment: CodeMirror.Compartment;
Jan Scheffler35199b92021-03-17 09:51:1562
Marijn Haverbekecf7dcfa2021-10-06 15:35:0263 constructor(
64 editorLineNumber: number,
65 oldCondition: string,
66 preferLogpoint: boolean,
67 onFinish: (arg0: {committed: boolean, condition: string}) => Promise<void>,
Marijn Haverbekecf7dcfa2021-10-06 15:35:0268 ) {
Erik Luo0b789b62018-11-21 19:31:4669 super(true);
Kriti Sapraae8f17e2021-08-18 10:28:0270
Marijn Haverbekeb0e3e7c2021-11-15 09:51:5271 const editorConfig = [
72 CodeMirror.javascript.javascriptLanguage,
73 TextEditor.Config.baseConfiguration(oldCondition || ''),
Marijn Haverbekef034f0b2021-11-22 14:20:5274 TextEditor.Config.closeBrackets,
Benedikt Meurer89f259d2022-12-13 07:03:5575 TextEditor.Config.autocompletion.instance(),
Marijn Haverbekeb0e3e7c2021-11-15 09:51:5276 CodeMirror.EditorView.lineWrapping,
77 TextEditor.Config.showCompletionHint,
Marijn Haverbekeb0e3e7c2021-11-15 09:51:5278 TextEditor.JavaScript.argumentHints(),
79 ];
80
Jan Schefflera222c632021-08-13 12:46:1581 this.onFinish = onFinish;
82 this.finished = false;
Erik Luo8f91a0e2018-12-22 21:46:3783 this.element.tabIndex = -1;
Erik Luo0b789b62018-11-21 19:31:4684
Tim van der Lippe8987f8f2020-01-03 15:03:1685 const logpointPrefix = LogpointPrefix;
Paul Lewis39944952020-01-22 15:45:1886 const logpointSuffix = LogpointSuffix;
Jan Schefflera222c632021-08-13 12:46:1587 this.isLogpoint = oldCondition.startsWith(logpointPrefix) && oldCondition.endsWith(logpointSuffix);
88 if (this.isLogpoint) {
Erik Luo5b2b7522018-12-03 20:35:0689 oldCondition = oldCondition.substring(logpointPrefix.length, oldCondition.length - logpointSuffix.length);
Tim van der Lippe1d6e57a2019-09-30 11:55:3490 }
Jan Schefflera222c632021-08-13 12:46:1591 this.isLogpoint = this.isLogpoint || preferLogpoint;
Erik Luo5b2b7522018-12-03 20:35:0692
Pavel Feldmandb310912019-01-30 00:31:2093 this.element.classList.add('sources-edit-breakpoint-dialog');
Tim van der Lippefbbf9812020-02-13 14:43:4694 const toolbar = new UI.Toolbar.Toolbar('source-frame-breakpoint-toolbar', this.contentElement);
Pavel Feldmandb310912019-01-30 00:31:2095 toolbar.appendText(`Line ${editorLineNumber + 1}:`);
Erik Luo0941a442018-12-08 05:55:2296
Jan Schefflera222c632021-08-13 12:46:1597 this.typeSelector =
98 new UI.Toolbar.ToolbarComboBox(this.onTypeChanged.bind(this), i18nString(UIStrings.breakpointType));
99 this.typeSelector.createOption(i18nString(UIStrings.breakpoint), BreakpointType.Breakpoint);
Vidal Guillermo Diazleal Ortega83edb472021-02-16 18:39:32100 const conditionalOption =
Jan Schefflera222c632021-08-13 12:46:15101 this.typeSelector.createOption(i18nString(UIStrings.conditionalBreakpoint), BreakpointType.Conditional);
102 const logpointOption = this.typeSelector.createOption(i18nString(UIStrings.logpoint), BreakpointType.Logpoint);
103 this.typeSelector.select(this.isLogpoint ? logpointOption : conditionalOption);
104 toolbar.appendToolbarItem(this.typeSelector);
Erik Luo0b789b62018-11-21 19:31:46105
Marijn Haverbekecf7dcfa2021-10-06 15:35:02106 const content = oldCondition || '';
Marijn Haverbeke9a58ba72021-10-15 12:59:29107 const finishIfComplete = (view: CodeMirror.EditorView): boolean => {
Tim van der Lippe2d9a95c2022-01-04 15:18:03108 void TextEditor.JavaScript.isExpressionComplete(view.state.doc.toString()).then((complete): void => {
Marijn Haverbeke0adc7232021-12-08 16:56:57109 if (complete) {
110 this.finishEditing(true, this.editor.state.doc.toString());
111 } else {
112 CodeMirror.insertNewlineAndIndent(view);
113 }
114 });
115 return true;
Marijn Haverbeke9a58ba72021-10-15 12:59:29116 };
Marijn Haverbekecf7dcfa2021-10-06 15:35:02117 const keymap = [
118 {
119 key: 'Mod-Enter',
Marijn Haverbeke9a58ba72021-10-15 12:59:29120 run: finishIfComplete,
121 },
122 {
123 key: 'Enter',
124 run: finishIfComplete,
125 },
126 {
Marijn Haverbeke9a58ba72021-10-15 12:59:29127 key: 'Shift-Enter',
Marijn Haverbekeb0e3e7c2021-11-15 09:51:52128 run: CodeMirror.insertNewlineAndIndent,
Marijn Haverbekecf7dcfa2021-10-06 15:35:02129 },
130 {
131 key: 'Escape',
132 run: (): boolean => {
133 this.finishEditing(false, '');
134 return true;
135 },
136 },
137 ];
138
Marijn Haverbekeb0e3e7c2021-11-15 09:51:52139 this.placeholderCompartment = new CodeMirror.Compartment();
Marijn Haverbekecf7dcfa2021-10-06 15:35:02140
Marijn Haverbeke9a58ba72021-10-15 12:59:29141 const editorWrapper = this.contentElement.appendChild(document.createElement('div'));
142 editorWrapper.classList.add('condition-editor');
143
Ergün Erdoğmuşc08685a2022-10-06 07:24:32144 this.editor = new TextEditor.TextEditor.TextEditor(CodeMirror.EditorState.create({
145 doc: content,
146 selection: {anchor: 0, head: content.length},
147 extensions: [
148 this.placeholderCompartment.of(this.getPlaceholder()),
149 CodeMirror.keymap.of(keymap),
150 editorConfig,
151 ],
152 }));
Marijn Haverbeke9a58ba72021-10-15 12:59:29153 editorWrapper.appendChild(this.editor);
Marijn Haverbekecf7dcfa2021-10-06 15:35:02154
155 this.updateTooltip();
Marijn Haverbeke9a58ba72021-10-15 12:59:29156
Andres Olivares0ed4bef2021-03-15 17:15:51157 this.element.addEventListener('blur', event => {
Olivia Flynn989d2722021-07-07 01:06:56158 if (!event.relatedTarget ||
159 (event.relatedTarget && !(event.relatedTarget as Node).isSelfOrDescendant(this.element))) {
Marijn Haverbekecf7dcfa2021-10-06 15:35:02160 this.finishEditing(true, this.editor.state.doc.toString());
Andres Olivares0ed4bef2021-03-15 17:15:51161 }
162 }, true);
Erik Luo0b789b62018-11-21 19:31:46163 }
164
Jan Scheffler35199b92021-03-17 09:51:15165 focusEditor(): void {
Marijn Haverbekecf7dcfa2021-10-06 15:35:02166 this.editor.editor.focus();
Andres Olivares0ed4bef2021-03-15 17:15:51167 }
Jan Schefflera222c632021-08-13 12:46:15168 private static conditionForLogpoint(condition: string): string {
Paul Lewis39944952020-01-22 15:45:18169 return `${LogpointPrefix}${condition}${LogpointSuffix}`;
Erik Luo5b2b7522018-12-03 20:35:06170 }
171
Jan Schefflera222c632021-08-13 12:46:15172 private onTypeChanged(): void {
Marijn Haverbekecf7dcfa2021-10-06 15:35:02173 const type = this.breakpointType;
Jaroslav Sevcik895a8752021-12-15 18:13:04174 this.isLogpoint = type === BreakpointType.Logpoint;
Marijn Haverbekecf7dcfa2021-10-06 15:35:02175 if (type === BreakpointType.Breakpoint) {
176 this.finishEditing(true, '');
Jaroslav Sevcik895a8752021-12-15 18:13:04177 return;
Erik Luo0941a442018-12-08 05:55:22178 }
Jaroslav Sevcik895a8752021-12-15 18:13:04179 this.editor.dispatch({effects: this.placeholderCompartment.reconfigure(this.getPlaceholder())});
180 this.updateTooltip();
Erik Luo0941a442018-12-08 05:55:22181 }
182
Marijn Haverbekecf7dcfa2021-10-06 15:35:02183 private get breakpointType(): string|null {
Jan Schefflera222c632021-08-13 12:46:15184 const option = this.typeSelector.selectedOption();
Marijn Haverbekecf7dcfa2021-10-06 15:35:02185 return option ? option.value : null;
186 }
187
188 private getPlaceholder(): CodeMirror.Extension {
189 const type = this.breakpointType;
190 if (type === BreakpointType.Conditional) {
Marijn Haverbekeb0e3e7c2021-11-15 09:51:52191 return CodeMirror.placeholder(i18nString(UIStrings.expressionToCheckBeforePausingEg));
Philip Pfaffef2ef8f32020-10-26 13:06:06192 }
Marijn Haverbekecf7dcfa2021-10-06 15:35:02193 if (type === BreakpointType.Logpoint) {
Marijn Haverbekeb0e3e7c2021-11-15 09:51:52194 return CodeMirror.placeholder(i18nString(UIStrings.logMessageEgXIsX));
Marijn Haverbekecf7dcfa2021-10-06 15:35:02195 }
196 return [];
197 }
198
199 private updateTooltip(): void {
200 const type = this.breakpointType;
201 if (type === BreakpointType.Conditional) {
Jan Schefflera222c632021-08-13 12:46:15202 UI.Tooltip.Tooltip.install((this.typeSelector.element), i18nString(UIStrings.pauseOnlyWhenTheConditionIsTrue));
Marijn Haverbekecf7dcfa2021-10-06 15:35:02203 } else if (type === BreakpointType.Logpoint) {
Jan Schefflera222c632021-08-13 12:46:15204 UI.Tooltip.Tooltip.install((this.typeSelector.element), i18nString(UIStrings.logAMessageToConsoleDoNotBreak));
Erik Luo0941a442018-12-08 05:55:22205 }
206 }
207
Jaroslav Sevcik59fdfc12022-01-12 17:44:26208 finishEditing(committed: boolean, condition: string): void {
Jan Schefflera222c632021-08-13 12:46:15209 if (this.finished) {
Erik Luo0b789b62018-11-21 19:31:46210 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34211 }
Jan Schefflera222c632021-08-13 12:46:15212 this.finished = true;
Marijn Haverbekecf7dcfa2021-10-06 15:35:02213 this.editor.remove();
Jan Schefflera222c632021-08-13 12:46:15214 if (this.isLogpoint) {
215 condition = BreakpointEditDialog.conditionForLogpoint(condition);
Tim van der Lippe1d6e57a2019-09-30 11:55:34216 }
Tim van der Lippe2d9a95c2022-01-04 15:18:03217 void this.onFinish({committed, condition});
Erik Luo0b789b62018-11-21 19:31:46218 }
219
Kriti Sapraae8f17e2021-08-18 10:28:02220 wasShown(): void {
221 super.wasShown();
222 this.registerCSSFiles([breakpointEditDialogStyles]);
223 }
Tim van der Lippe8987f8f2020-01-03 15:03:16224}
Erik Luo5b2b7522018-12-03 20:35:06225
Tim van der Lippe8987f8f2020-01-03 15:03:16226export const LogpointPrefix = '/** DEVTOOLS_LOGPOINT */ console.log(';
Paul Lewis39944952020-01-22 15:45:18227export const LogpointSuffix = ')';
Erik Luo0941a442018-12-08 05:55:22228
Tim van der Lippe8987f8f2020-01-03 15:03:16229export const BreakpointType = {
Erik Luo0941a442018-12-08 05:55:22230 Breakpoint: 'Breakpoint',
231 Conditional: 'Conditional',
232 Logpoint: 'Logpoint',
233};