blob: 4153e94dc0dbd972d61867cfe0f497877e14627e [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 /**
14 *@description Screen reader label for a select box that chooses the breakpoint type in the Sources panel when editing a breakpoint
15 */
16 breakpointType: 'Breakpoint type',
17 /**
18 *@description Text in Breakpoint Edit Dialog of the Sources panel
19 */
20 breakpoint: 'Breakpoint',
21 /**
22 *@description Text in Breakpoint Edit Dialog of the Sources panel
23 */
24 conditionalBreakpoint: 'Conditional breakpoint',
25 /**
26 *@description Text in Breakpoint Edit Dialog of the Sources panel
27 */
28 logpoint: 'Logpoint',
29 /**
30 *@description Text in Breakpoint Edit Dialog of the Sources panel
31 */
32 expressionToCheckBeforePausingEg: 'Expression to check before pausing, e.g. x > 5',
33 /**
34 *@description Type selector element title in Breakpoint Edit Dialog of the Sources panel
35 */
36 pauseOnlyWhenTheConditionIsTrue: 'Pause only when the condition is true',
37 /**
Simon Zünd059aa762021-07-21 05:44:5438 *@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
Simon Zünd4cb2aab2021-06-22 05:27:1240 *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
Vidal Guillermo Diazleal Ortega83edb472021-02-16 18:39:3242 */
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 /**
45 *@description Type selector element title in Breakpoint Edit Dialog of the Sources panel
46 */
47 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,
Marijn Haverbekeb0e3e7c2021-11-15 09:51:5275 TextEditor.Config.autocompletion,
76 CodeMirror.EditorView.lineWrapping,
77 TextEditor.Config.showCompletionHint,
78 TextEditor.JavaScript.completion(),
79 TextEditor.JavaScript.argumentHints(),
80 ];
81
Jan Schefflera222c632021-08-13 12:46:1582 this.onFinish = onFinish;
83 this.finished = false;
Erik Luo8f91a0e2018-12-22 21:46:3784 this.element.tabIndex = -1;
Erik Luo0b789b62018-11-21 19:31:4685
Tim van der Lippe8987f8f2020-01-03 15:03:1686 const logpointPrefix = LogpointPrefix;
Paul Lewis39944952020-01-22 15:45:1887 const logpointSuffix = LogpointSuffix;
Jan Schefflera222c632021-08-13 12:46:1588 this.isLogpoint = oldCondition.startsWith(logpointPrefix) && oldCondition.endsWith(logpointSuffix);
89 if (this.isLogpoint) {
Erik Luo5b2b7522018-12-03 20:35:0690 oldCondition = oldCondition.substring(logpointPrefix.length, oldCondition.length - logpointSuffix.length);
Tim van der Lippe1d6e57a2019-09-30 11:55:3491 }
Jan Schefflera222c632021-08-13 12:46:1592 this.isLogpoint = this.isLogpoint || preferLogpoint;
Erik Luo5b2b7522018-12-03 20:35:0693
Pavel Feldmandb310912019-01-30 00:31:2094 this.element.classList.add('sources-edit-breakpoint-dialog');
Tim van der Lippefbbf9812020-02-13 14:43:4695 const toolbar = new UI.Toolbar.Toolbar('source-frame-breakpoint-toolbar', this.contentElement);
Pavel Feldmandb310912019-01-30 00:31:2096 toolbar.appendText(`Line ${editorLineNumber + 1}:`);
Erik Luo0941a442018-12-08 05:55:2297
Jan Schefflera222c632021-08-13 12:46:1598 this.typeSelector =
99 new UI.Toolbar.ToolbarComboBox(this.onTypeChanged.bind(this), i18nString(UIStrings.breakpointType));
100 this.typeSelector.createOption(i18nString(UIStrings.breakpoint), BreakpointType.Breakpoint);
Vidal Guillermo Diazleal Ortega83edb472021-02-16 18:39:32101 const conditionalOption =
Jan Schefflera222c632021-08-13 12:46:15102 this.typeSelector.createOption(i18nString(UIStrings.conditionalBreakpoint), BreakpointType.Conditional);
103 const logpointOption = this.typeSelector.createOption(i18nString(UIStrings.logpoint), BreakpointType.Logpoint);
104 this.typeSelector.select(this.isLogpoint ? logpointOption : conditionalOption);
105 toolbar.appendToolbarItem(this.typeSelector);
Erik Luo0b789b62018-11-21 19:31:46106
Marijn Haverbekecf7dcfa2021-10-06 15:35:02107 const content = oldCondition || '';
Marijn Haverbeke9a58ba72021-10-15 12:59:29108 const finishIfComplete = (view: CodeMirror.EditorView): boolean => {
Marijn Haverbekeb0e3e7c2021-11-15 09:51:52109 if (TextEditor.JavaScript.isExpressionComplete(view.state)) {
Marijn Haverbeke9a58ba72021-10-15 12:59:29110 this.finishEditing(true, this.editor.state.doc.toString());
111 return true;
112 }
113 return false;
114 };
Marijn Haverbekecf7dcfa2021-10-06 15:35:02115 const keymap = [
116 {
117 key: 'Mod-Enter',
Marijn Haverbeke9a58ba72021-10-15 12:59:29118 run: finishIfComplete,
119 },
120 {
121 key: 'Enter',
122 run: finishIfComplete,
123 },
124 {
Marijn Haverbeke9a58ba72021-10-15 12:59:29125 key: 'Shift-Enter',
Marijn Haverbekeb0e3e7c2021-11-15 09:51:52126 run: CodeMirror.insertNewlineAndIndent,
Marijn Haverbekecf7dcfa2021-10-06 15:35:02127 },
128 {
129 key: 'Escape',
130 run: (): boolean => {
131 this.finishEditing(false, '');
132 return true;
133 },
134 },
135 ];
136
Marijn Haverbekeb0e3e7c2021-11-15 09:51:52137 this.placeholderCompartment = new CodeMirror.Compartment();
Marijn Haverbekecf7dcfa2021-10-06 15:35:02138
Marijn Haverbeke9a58ba72021-10-15 12:59:29139 const editorWrapper = this.contentElement.appendChild(document.createElement('div'));
140 editorWrapper.classList.add('condition-editor');
141
Marijn Haverbekeb0e3e7c2021-11-15 09:51:52142 this.editor = new TextEditor.TextEditor.TextEditor(CodeMirror.EditorState.create({
Marijn Haverbekecf7dcfa2021-10-06 15:35:02143 doc: content,
144 selection: {anchor: 0, head: content.length},
145 extensions: [
146 this.placeholderCompartment.of(this.getPlaceholder()),
Marijn Haverbekeb0e3e7c2021-11-15 09:51:52147 CodeMirror.keymap.of(keymap),
Marijn Haverbekecf7dcfa2021-10-06 15:35:02148 editorConfig,
149 ],
150 }));
Marijn Haverbeke9a58ba72021-10-15 12:59:29151 editorWrapper.appendChild(this.editor);
Marijn Haverbekecf7dcfa2021-10-06 15:35:02152
153 this.updateTooltip();
Marijn Haverbeke9a58ba72021-10-15 12:59:29154
Andres Olivares0ed4bef2021-03-15 17:15:51155 this.element.addEventListener('blur', event => {
Olivia Flynn989d2722021-07-07 01:06:56156 if (!event.relatedTarget ||
157 (event.relatedTarget && !(event.relatedTarget as Node).isSelfOrDescendant(this.element))) {
Marijn Haverbekecf7dcfa2021-10-06 15:35:02158 this.finishEditing(true, this.editor.state.doc.toString());
Andres Olivares0ed4bef2021-03-15 17:15:51159 }
160 }, true);
Erik Luo0b789b62018-11-21 19:31:46161 }
162
Jan Scheffler35199b92021-03-17 09:51:15163 focusEditor(): void {
Marijn Haverbekecf7dcfa2021-10-06 15:35:02164 this.editor.editor.focus();
Andres Olivares0ed4bef2021-03-15 17:15:51165 }
Jan Schefflera222c632021-08-13 12:46:15166 private static conditionForLogpoint(condition: string): string {
Paul Lewis39944952020-01-22 15:45:18167 return `${LogpointPrefix}${condition}${LogpointSuffix}`;
Erik Luo5b2b7522018-12-03 20:35:06168 }
169
Jan Schefflera222c632021-08-13 12:46:15170 private onTypeChanged(): void {
Marijn Haverbekecf7dcfa2021-10-06 15:35:02171 const type = this.breakpointType;
172 if (type === BreakpointType.Breakpoint) {
173 this.finishEditing(true, '');
174 } else {
Tim van der Lippec1ab1122021-11-12 16:10:31175 this.editor.dispatch({effects: this.placeholderCompartment.reconfigure(this.getPlaceholder())});
Marijn Haverbekecf7dcfa2021-10-06 15:35:02176 this.updateTooltip();
Erik Luo0941a442018-12-08 05:55:22177 }
178 }
179
Marijn Haverbekecf7dcfa2021-10-06 15:35:02180 private get breakpointType(): string|null {
Jan Schefflera222c632021-08-13 12:46:15181 const option = this.typeSelector.selectedOption();
Marijn Haverbekecf7dcfa2021-10-06 15:35:02182 return option ? option.value : null;
183 }
184
185 private getPlaceholder(): CodeMirror.Extension {
186 const type = this.breakpointType;
187 if (type === BreakpointType.Conditional) {
Marijn Haverbekeb0e3e7c2021-11-15 09:51:52188 return CodeMirror.placeholder(i18nString(UIStrings.expressionToCheckBeforePausingEg));
Philip Pfaffef2ef8f32020-10-26 13:06:06189 }
Marijn Haverbekecf7dcfa2021-10-06 15:35:02190 if (type === BreakpointType.Logpoint) {
Marijn Haverbekeb0e3e7c2021-11-15 09:51:52191 return CodeMirror.placeholder(i18nString(UIStrings.logMessageEgXIsX));
Marijn Haverbekecf7dcfa2021-10-06 15:35:02192 }
193 return [];
194 }
195
196 private updateTooltip(): void {
197 const type = this.breakpointType;
198 if (type === BreakpointType.Conditional) {
Jan Schefflera222c632021-08-13 12:46:15199 UI.Tooltip.Tooltip.install((this.typeSelector.element), i18nString(UIStrings.pauseOnlyWhenTheConditionIsTrue));
Marijn Haverbekecf7dcfa2021-10-06 15:35:02200 } else if (type === BreakpointType.Logpoint) {
Jan Schefflera222c632021-08-13 12:46:15201 UI.Tooltip.Tooltip.install((this.typeSelector.element), i18nString(UIStrings.logAMessageToConsoleDoNotBreak));
Erik Luo0941a442018-12-08 05:55:22202 }
203 }
204
Marijn Haverbekecf7dcfa2021-10-06 15:35:02205 private finishEditing(committed: boolean, condition: string): void {
Jan Schefflera222c632021-08-13 12:46:15206 if (this.finished) {
Erik Luo0b789b62018-11-21 19:31:46207 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34208 }
Jan Schefflera222c632021-08-13 12:46:15209 this.finished = true;
Marijn Haverbekecf7dcfa2021-10-06 15:35:02210 this.editor.remove();
Jan Schefflera222c632021-08-13 12:46:15211 if (this.isLogpoint) {
212 condition = BreakpointEditDialog.conditionForLogpoint(condition);
Tim van der Lippe1d6e57a2019-09-30 11:55:34213 }
Jan Schefflera222c632021-08-13 12:46:15214 this.onFinish({committed, condition});
Erik Luo0b789b62018-11-21 19:31:46215 }
216
Kriti Sapraae8f17e2021-08-18 10:28:02217 wasShown(): void {
218 super.wasShown();
219 this.registerCSSFiles([breakpointEditDialogStyles]);
220 }
Tim van der Lippe8987f8f2020-01-03 15:03:16221}
Erik Luo5b2b7522018-12-03 20:35:06222
Tim van der Lippe8987f8f2020-01-03 15:03:16223export const LogpointPrefix = '/** DEVTOOLS_LOGPOINT */ console.log(';
Paul Lewis39944952020-01-22 15:45:18224export const LogpointSuffix = ')';
Erik Luo0941a442018-12-08 05:55:22225
Tim van der Lippe8987f8f2020-01-03 15:03:16226export const BreakpointType = {
Erik Luo0941a442018-12-08 05:55:22227 Breakpoint: 'Breakpoint',
228 Conditional: 'Conditional',
229 Logpoint: 'Logpoint',
230};