blob: 125efec75303591f3411cc76ef2d17ff664ca450 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import * as ObjectUI from '../object_ui/object_ui.js';
import * as Root from '../root/root.js';
import * as UI from '../ui/ui.js';
export class BreakpointEditDialog extends UI.Widget.Widget {
/**
* @param {number} editorLineNumber
* @param {string} oldCondition
* @param {boolean} preferLogpoint
* @param {function({committed: boolean, condition: string}): !Promise<void>} onFinish
*/
constructor(editorLineNumber, oldCondition, preferLogpoint, onFinish) {
super(true);
this.registerRequiredCSS('sources/breakpointEditDialog.css', {enableLegacyPatching: true});
this._onFinish = onFinish;
this._finished = false;
/** @type {?UI.TextEditor.TextEditor} */
this._editor = null;
this.element.tabIndex = -1;
const logpointPrefix = LogpointPrefix;
const logpointSuffix = LogpointSuffix;
this._isLogpoint = oldCondition.startsWith(logpointPrefix) && oldCondition.endsWith(logpointSuffix);
if (this._isLogpoint) {
oldCondition = oldCondition.substring(logpointPrefix.length, oldCondition.length - logpointSuffix.length);
}
this._isLogpoint = this._isLogpoint || preferLogpoint;
this.element.classList.add('sources-edit-breakpoint-dialog');
const toolbar = new UI.Toolbar.Toolbar('source-frame-breakpoint-toolbar', this.contentElement);
toolbar.appendText(`Line ${editorLineNumber + 1}:`);
this._typeSelector = new UI.Toolbar.ToolbarComboBox(this._onTypeChanged.bind(this), ls`Breakpoint type`);
this._typeSelector.createOption(ls`Breakpoint`, BreakpointType.Breakpoint);
const conditionalOption = this._typeSelector.createOption(ls`Conditional breakpoint`, BreakpointType.Conditional);
const logpointOption = this._typeSelector.createOption(ls`Logpoint`, BreakpointType.Logpoint);
this._typeSelector.select(this._isLogpoint ? logpointOption : conditionalOption);
toolbar.appendToolbarItem(this._typeSelector);
const extension = Root.Runtime.Runtime.instance().extension(UI.TextEditor.TextEditorFactory);
if (extension) {
extension.instance().then(factory => {
const editorOptions = {
lineNumbers: false,
lineWrapping: true,
mimeType: 'javascript',
autoHeight: true,
bracketMatchingSetting: undefined,
devtoolsAccessibleName: undefined,
padBottom: undefined,
maxHighlightLength: undefined,
placeholder: undefined,
lineWiseCopyCut: undefined,
inputStyle: undefined,
};
this._editor = /** @type {!UI.TextEditor.TextEditorFactory} */ (factory).createEditor(editorOptions);
this._updatePlaceholder();
this._editor.widget().element.classList.add('condition-editor');
this._editor.configureAutocomplete(
ObjectUI.JavaScriptAutocomplete.JavaScriptAutocompleteConfig.createConfigForEditor(this._editor));
if (oldCondition) {
this._editor.setText(oldCondition);
}
this._editor.widget().markAsExternallyManaged();
this._editor.widget().show(this.contentElement);
this._editor.setSelection(this._editor.fullRange());
this._editor.widget().focus();
this._editor.widget().element.addEventListener('keydown', this._onKeyDown.bind(this), true);
this.element.addEventListener('blur', event => {
if (event.relatedTarget && !/** @type {!Node} */ (event.relatedTarget).isSelfOrDescendant(this.element)) {
this._finishEditing(true);
}
}, true);
});
}
}
/**
* @param {string} condition
* @return {string}
*/
static _conditionForLogpoint(condition) {
return `${LogpointPrefix}${condition}${LogpointSuffix}`;
}
_onTypeChanged() {
const option = this._typeSelector.selectedOption();
if (!option || !this._editor) {
return;
}
const value = option.value;
this._isLogpoint = value === BreakpointType.Logpoint;
this._updatePlaceholder();
if (value === BreakpointType.Breakpoint) {
this._editor.setText('');
this._finishEditing(true);
}
}
_updatePlaceholder() {
const option = this._typeSelector.selectedOption();
if (!option || !this._editor) {
return;
}
const selectedValue = option.value;
if (selectedValue === BreakpointType.Conditional) {
this._editor.setPlaceholder(ls`Expression to check before pausing, e.g. x > 5`);
/** @type {!HTMLSpanElement} */ UI.Tooltip.Tooltip.install(
(this._typeSelector.element), ls`Pause only when the condition is true`);
} else if (selectedValue === BreakpointType.Logpoint) {
this._editor.setPlaceholder(ls`Log message, e.g. 'x is', x`);
/** @type {!HTMLSpanElement} */ UI.Tooltip.Tooltip.install(
(this._typeSelector.element), ls`Log a message to Console, do not break`);
}
}
/**
* @param {boolean} committed
*/
_finishEditing(committed) {
if (this._finished) {
return;
}
this._finished = true;
if (!this._editor) {
return;
}
this._editor.widget().detach();
let condition = this._editor.text();
if (this._isLogpoint) {
condition = BreakpointEditDialog._conditionForLogpoint(condition);
}
this._onFinish({committed, condition});
}
/**
* @param {!Event} event
*/
async _onKeyDown(event) {
if (!(event instanceof KeyboardEvent) || !this._editor) {
return;
}
if (isEnterKey(event) && !event.shiftKey) {
event.consume(true);
const expression = this._editor.text();
if (event.ctrlKey ||
await ObjectUI.JavaScriptAutocomplete.JavaScriptAutocomplete.isExpressionComplete(expression)) {
this._finishEditing(true);
} else {
this._editor.newlineAndIndent();
}
}
if (isEscKey(event)) {
this._finishEditing(false);
event.stopImmediatePropagation();
}
}
}
export const LogpointPrefix = '/** DEVTOOLS_LOGPOINT */ console.log(';
export const LogpointSuffix = ')';
export const BreakpointType = {
Breakpoint: 'Breakpoint',
Conditional: 'Conditional',
Logpoint: 'Logpoint',
};