зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1913189 - [devtools] Use CodeMirror 6 in conditional breakpoint. r=bomsy.
Differential Revision: https://phabricator.services.mozilla.com/D220685
This commit is contained in:
Родитель
f3480bca05
Коммит
1aa477dd15
|
@ -47,3 +47,13 @@
|
||||||
/* Match the color of the placeholder text to existing inputs in the Debugger */
|
/* Match the color of the placeholder text to existing inputs in the Debugger */
|
||||||
color: var(--theme-text-color-alt);
|
color: var(--theme-text-color-alt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* cm6 style */
|
||||||
|
.conditional-breakpoint-panel .inline-codemirror-container {
|
||||||
|
flex: 1 1 100%;
|
||||||
|
|
||||||
|
/* We already set an outline on the conditional panel, so hide the default codemirror one */
|
||||||
|
.cm-editor.cm-focused {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -34,6 +34,7 @@ export class ConditionalPanel extends PureComponent {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.cbPanel = null;
|
this.cbPanel = null;
|
||||||
|
this.breakpointPanelEditor = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get propTypes() {
|
static get propTypes() {
|
||||||
|
@ -49,28 +50,68 @@ export class ConditionalPanel extends PureComponent {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
removeBreakpointPanelEditor() {
|
||||||
|
if (this.breakpointPanelEditor) {
|
||||||
|
this.breakpointPanelEditor.destroy();
|
||||||
|
}
|
||||||
|
this.breakpointPanelEditor = null;
|
||||||
|
}
|
||||||
|
|
||||||
keepFocusOnInput() {
|
keepFocusOnInput() {
|
||||||
if (this.input) {
|
if (this.input) {
|
||||||
this.input.focus();
|
this.input.focus();
|
||||||
|
} else if (this.breakpointPanelEditor) {
|
||||||
|
this.breakpointPanelEditor.focus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
saveAndClose = () => {
|
/**
|
||||||
if (this.input) {
|
* Set the breakpoint/logpoint if expression isn't empty, and close the panel.
|
||||||
this.setBreakpoint(this.input.value.trim());
|
*
|
||||||
|
* @param {String} expression: The expression that will be used for setting the
|
||||||
|
* conditional breakpoint/logpoint
|
||||||
|
*/
|
||||||
|
saveAndClose = (expression = null) => {
|
||||||
|
if (expression) {
|
||||||
|
this.setBreakpoint(expression.trim());
|
||||||
}
|
}
|
||||||
|
|
||||||
this.props.closeConditionalPanel();
|
this.props.closeConditionalPanel();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle inline editor keydown event
|
||||||
|
*
|
||||||
|
* @param {Event} e: The keydown event
|
||||||
|
*/
|
||||||
onKey = e => {
|
onKey = e => {
|
||||||
if (e.key === "Enter" && !e.shiftKey) {
|
if (e.key === "Enter" && !e.shiftKey) {
|
||||||
this.saveAndClose();
|
this.saveAndClose(this.input?.value);
|
||||||
} else if (e.key === "Escape") {
|
} else if (e.key === "Escape") {
|
||||||
this.props.closeConditionalPanel();
|
this.props.closeConditionalPanel();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle inline editor blur event
|
||||||
|
*
|
||||||
|
* @param {Event} e: The blur event
|
||||||
|
*/
|
||||||
|
onBlur = e => {
|
||||||
|
if (
|
||||||
|
// if there is no event
|
||||||
|
// or if the focus is the conditional panel
|
||||||
|
// do not close the conditional panel
|
||||||
|
!e ||
|
||||||
|
(e?.relatedTarget &&
|
||||||
|
e.relatedTarget.closest(".conditional-breakpoint-panel"))
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.props.closeConditionalPanel();
|
||||||
|
};
|
||||||
|
|
||||||
setBreakpoint(value) {
|
setBreakpoint(value) {
|
||||||
const { log, breakpoint } = this.props;
|
const { log, breakpoint } = this.props;
|
||||||
// If breakpoint is `pending`, props will not contain a breakpoint.
|
// If breakpoint is `pending`, props will not contain a breakpoint.
|
||||||
|
@ -105,17 +146,20 @@ export class ConditionalPanel extends PureComponent {
|
||||||
};
|
};
|
||||||
|
|
||||||
showConditionalPanel(prevProps) {
|
showConditionalPanel(prevProps) {
|
||||||
const { location, editor, breakpoint, selectedSource } = this.props;
|
const { location, log, editor, breakpoint, selectedSource } = this.props;
|
||||||
if (!selectedSource || !location) {
|
if (!selectedSource || !location) {
|
||||||
|
this.removeBreakpointPanelEditor();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// When breakpoint is removed
|
// When breakpoint is removed
|
||||||
if (prevProps?.breakpoint && !breakpoint) {
|
if (prevProps?.breakpoint && !breakpoint) {
|
||||||
editor.removeLineContentMarker(markerTypes.CONDITIONAL_BP_MARKER);
|
editor.removeLineContentMarker(markerTypes.CONDITIONAL_BP_MARKER);
|
||||||
|
this.removeBreakpointPanelEditor();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (selectedSource.id !== location.source.id) {
|
if (selectedSource.id !== location.source.id) {
|
||||||
editor.removeLineContentMarker(markerTypes.CONDITIONAL_BP_MARKER);
|
editor.removeLineContentMarker(markerTypes.CONDITIONAL_BP_MARKER);
|
||||||
|
this.removeBreakpointPanelEditor();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const line = toEditorLine(location.source.id, location.line || 0);
|
const line = toEditorLine(location.source.id, location.line || 0);
|
||||||
|
@ -124,12 +168,42 @@ export class ConditionalPanel extends PureComponent {
|
||||||
lines: [{ line }],
|
lines: [{ line }],
|
||||||
renderAsBlock: true,
|
renderAsBlock: true,
|
||||||
createLineElementNode: () => {
|
createLineElementNode: () => {
|
||||||
// Create a Codemirror 5 editor for the breakpoint panel
|
// Create a Codemirror editor for the breakpoint panel
|
||||||
// TODO: Switch to use Codemirror 6 version Bug 1890205
|
|
||||||
const breakpointPanelEditor = createEditor();
|
const onEnterKeyMapConfig = {
|
||||||
breakpointPanelEditor.appendToLocalElement(
|
preventDefault: true,
|
||||||
document.createElement("div")
|
stopPropagation: true,
|
||||||
);
|
run: () => this.saveAndClose(breakpointPanelEditor.getText(null)),
|
||||||
|
};
|
||||||
|
|
||||||
|
const breakpointPanelEditor = createEditor({
|
||||||
|
cm6: features.codemirrorNext,
|
||||||
|
readOnly: false,
|
||||||
|
lineNumbers: false,
|
||||||
|
placeholder: L10N.getStr(
|
||||||
|
log
|
||||||
|
? "editor.conditionalPanel.logPoint.placeholder2"
|
||||||
|
: "editor.conditionalPanel.placeholder2"
|
||||||
|
),
|
||||||
|
keyMap: [
|
||||||
|
{
|
||||||
|
key: "Enter",
|
||||||
|
...onEnterKeyMapConfig,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "Mod-Enter",
|
||||||
|
...onEnterKeyMapConfig,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "Escape",
|
||||||
|
preventDefault: true,
|
||||||
|
stopPropagation: true,
|
||||||
|
run: () => this.props.closeConditionalPanel(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
this.breakpointPanelEditor = breakpointPanelEditor;
|
||||||
return this.renderConditionalPanel(this.props, breakpointPanelEditor);
|
return this.renderConditionalPanel(this.props, breakpointPanelEditor);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -168,6 +242,7 @@ export class ConditionalPanel extends PureComponent {
|
||||||
} else {
|
} else {
|
||||||
this.clearConditionalPanel();
|
this.clearConditionalPanel();
|
||||||
}
|
}
|
||||||
|
this.removeBreakpointPanelEditor();
|
||||||
}
|
}
|
||||||
|
|
||||||
renderToWidget(props) {
|
renderToWidget(props) {
|
||||||
|
@ -209,57 +284,55 @@ export class ConditionalPanel extends PureComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
createEditor = (input, editor) => {
|
setupAndAppendInlineEditor = (el, editor) => {
|
||||||
const { log, closeConditionalPanel } = this.props;
|
const { log } = this.props;
|
||||||
const codeMirror = editor.CodeMirror.fromTextArea(input, {
|
|
||||||
mode: "javascript",
|
|
||||||
theme: "mozilla",
|
|
||||||
placeholder: L10N.getStr(
|
|
||||||
log
|
|
||||||
? "editor.conditionalPanel.logPoint.placeholder2"
|
|
||||||
: "editor.conditionalPanel.placeholder2"
|
|
||||||
),
|
|
||||||
cursorBlinkRate: prefs.cursorBlinkRate,
|
|
||||||
});
|
|
||||||
|
|
||||||
codeMirror.on("keydown", (cm, e) => {
|
if (features.codemirrorNext) {
|
||||||
if (e.key === "Enter") {
|
editor.appendToLocalElement(el);
|
||||||
e.codemirrorIgnore = true;
|
editor.on("blur", e => this.onBlur(e));
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
codeMirror.on("blur", (cm, e) => {
|
editor.setText(this.getDefaultValue());
|
||||||
if (
|
editor.focus();
|
||||||
// if there is no event
|
editor.selectAll();
|
||||||
// or if the focus is the conditional panel
|
} else {
|
||||||
// do not close the conditional panel
|
const codeMirror = editor.CodeMirror.fromTextArea(el, {
|
||||||
!e ||
|
mode: "javascript",
|
||||||
(e?.relatedTarget &&
|
theme: "mozilla",
|
||||||
e.relatedTarget.closest(".conditional-breakpoint-panel"))
|
placeholder: L10N.getStr(
|
||||||
) {
|
log
|
||||||
return;
|
? "editor.conditionalPanel.logPoint.placeholder2"
|
||||||
}
|
: "editor.conditionalPanel.placeholder2"
|
||||||
|
),
|
||||||
|
cursorBlinkRate: prefs.cursorBlinkRate,
|
||||||
|
});
|
||||||
|
|
||||||
closeConditionalPanel();
|
codeMirror.on("keydown", (cm, e) => {
|
||||||
});
|
if (e.key === "Enter") {
|
||||||
|
e.codemirrorIgnore = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const codeMirrorWrapper = codeMirror.getWrapperElement();
|
codeMirror.on("blur", (cm, e) => this.onBlur(e));
|
||||||
|
|
||||||
codeMirrorWrapper.addEventListener("keydown", e => {
|
const codeMirrorWrapper = codeMirror.getWrapperElement();
|
||||||
codeMirror.save();
|
|
||||||
this.onKey(e);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.input = input;
|
codeMirrorWrapper.addEventListener("keydown", e => {
|
||||||
this.codeMirror = codeMirror;
|
codeMirror.save();
|
||||||
codeMirror.focus();
|
this.onKey(e);
|
||||||
codeMirror.execCommand("selectAll");
|
});
|
||||||
|
|
||||||
|
this.input = el;
|
||||||
|
this.codeMirror = codeMirror;
|
||||||
|
codeMirror.focus();
|
||||||
|
codeMirror.execCommand("selectAll");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
getDefaultValue() {
|
getDefaultValue() {
|
||||||
const { breakpoint, log } = this.props;
|
const { breakpoint, log } = this.props;
|
||||||
const options = breakpoint?.options || {};
|
const options = breakpoint?.options || {};
|
||||||
return log ? options.logValue : options.condition;
|
const value = log ? options.logValue : options.condition;
|
||||||
|
return value || "";
|
||||||
}
|
}
|
||||||
|
|
||||||
renderConditionalPanel(props, editor) {
|
renderConditionalPanel(props, editor) {
|
||||||
|
@ -285,10 +358,15 @@ export class ConditionalPanel extends PureComponent {
|
||||||
},
|
},
|
||||||
"»"
|
"»"
|
||||||
),
|
),
|
||||||
textarea({
|
features.codemirrorNext
|
||||||
defaultValue,
|
? div({
|
||||||
ref: input => this.createEditor(input, editor),
|
className: "inline-codemirror-container",
|
||||||
})
|
ref: el => this.setupAndAppendInlineEditor(el, editor),
|
||||||
|
})
|
||||||
|
: textarea({
|
||||||
|
defaultValue,
|
||||||
|
ref: input => this.setupAndAppendInlineEditor(input, editor),
|
||||||
|
})
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,13 @@
|
||||||
import SourceEditor from "devtools/client/shared/sourceeditor/editor";
|
import SourceEditor from "devtools/client/shared/sourceeditor/editor";
|
||||||
import { features, prefs } from "../prefs";
|
import { features, prefs } from "../prefs";
|
||||||
|
|
||||||
export function createEditor(useCm6 = false) {
|
/**
|
||||||
|
* Create a SourceEditor
|
||||||
|
*
|
||||||
|
* @param {Object} config: SourceEditor config object
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function createEditor(config = { cm6: false }) {
|
||||||
const gutters = ["breakpoints", "hit-markers", "CodeMirror-linenumbers"];
|
const gutters = ["breakpoints", "hit-markers", "CodeMirror-linenumbers"];
|
||||||
|
|
||||||
if (features.codeFolding) {
|
if (features.codeFolding) {
|
||||||
|
@ -14,7 +20,6 @@ export function createEditor(useCm6 = false) {
|
||||||
|
|
||||||
return new SourceEditor({
|
return new SourceEditor({
|
||||||
mode: SourceEditor.modes.js,
|
mode: SourceEditor.modes.js,
|
||||||
cm6: useCm6,
|
|
||||||
foldGutter: features.codeFolding,
|
foldGutter: features.codeFolding,
|
||||||
enableCodeFolding: features.codeFolding,
|
enableCodeFolding: features.codeFolding,
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
|
@ -37,6 +42,7 @@ export function createEditor(useCm6 = false) {
|
||||||
"Ctrl-G": false,
|
"Ctrl-G": false,
|
||||||
},
|
},
|
||||||
cursorBlinkRate: prefs.cursorBlinkRate,
|
cursorBlinkRate: prefs.cursorBlinkRate,
|
||||||
|
...config,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,7 +53,7 @@ export function createEditor(useCm6 = false) {
|
||||||
* @returns {CodeMirror}
|
* @returns {CodeMirror}
|
||||||
*/
|
*/
|
||||||
export function createHeadlessEditor(useCm6) {
|
export function createHeadlessEditor(useCm6) {
|
||||||
const editor = createEditor(useCm6);
|
const editor = createEditor({ cm6: useCm6 });
|
||||||
editor.appendToLocalElement(document.createElement("div"));
|
editor.appendToLocalElement(document.createElement("div"));
|
||||||
return editor;
|
return editor;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ export function getEditor(useCm6) {
|
||||||
return editor;
|
return editor;
|
||||||
}
|
}
|
||||||
|
|
||||||
editor = createEditor(useCm6);
|
editor = createEditor({ cm6: useCm6 });
|
||||||
return editor;
|
return editor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -647,8 +647,14 @@ class Editor extends EventEmitter {
|
||||||
|
|
||||||
const {
|
const {
|
||||||
codemirror,
|
codemirror,
|
||||||
codemirrorView: { EditorView, lineNumbers, drawSelection },
|
codemirrorView: {
|
||||||
codemirrorState: { EditorState, Compartment },
|
drawSelection,
|
||||||
|
EditorView,
|
||||||
|
keymap,
|
||||||
|
lineNumbers,
|
||||||
|
placeholder,
|
||||||
|
},
|
||||||
|
codemirrorState: { EditorState, Compartment, Prec },
|
||||||
codemirrorSearch: { highlightSelectionMatches },
|
codemirrorSearch: { highlightSelectionMatches },
|
||||||
codemirrorLanguage: {
|
codemirrorLanguage: {
|
||||||
syntaxTreeAvailable,
|
syntaxTreeAvailable,
|
||||||
|
@ -750,6 +756,14 @@ class Editor extends EventEmitter {
|
||||||
extensions.push(codemirrorLangJavascript.javascript());
|
extensions.push(codemirrorLangJavascript.javascript());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.config.placeholder) {
|
||||||
|
extensions.push(placeholder(this.config.placeholder));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.config.keyMap) {
|
||||||
|
extensions.push(Prec.highest(keymap.of(this.config.keyMap)));
|
||||||
|
}
|
||||||
|
|
||||||
if (Services.prefs.prefHasUserValue(CARET_BLINK_TIME)) {
|
if (Services.prefs.prefHasUserValue(CARET_BLINK_TIME)) {
|
||||||
// We need to multiply the preference value by 2 to match Firefox cursor rate
|
// We need to multiply the preference value by 2 to match Firefox cursor rate
|
||||||
const cursorBlinkRate = Services.prefs.getIntPref(CARET_BLINK_TIME) * 2;
|
const cursorBlinkRate = Services.prefs.getIntPref(CARET_BLINK_TIME) * 2;
|
||||||
|
@ -768,6 +782,11 @@ class Editor extends EventEmitter {
|
||||||
cm.isDocumentLoadComplete = false;
|
cm.isDocumentLoadComplete = false;
|
||||||
this.#ownerDoc.sourceEditor = { editor: this, cm };
|
this.#ownerDoc.sourceEditor = { editor: this, cm };
|
||||||
editors.set(this, cm);
|
editors.set(this, cm);
|
||||||
|
|
||||||
|
// For now, we only need to pipe the blur event
|
||||||
|
cm.contentDOM.addEventListener("blur", e => this.emit("blur", e), {
|
||||||
|
signal: this.#abortController?.signal,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -3284,6 +3303,29 @@ class Editor extends EventEmitter {
|
||||||
}
|
}
|
||||||
return outputNode.innerHTML;
|
return outputNode.innerHTML;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Focus the CodeMirror editor
|
||||||
|
*/
|
||||||
|
focus() {
|
||||||
|
const cm = editors.get(this);
|
||||||
|
cm.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select the whole document
|
||||||
|
*/
|
||||||
|
selectAll() {
|
||||||
|
const cm = editors.get(this);
|
||||||
|
if (this.config.cm6) {
|
||||||
|
cm.dispatch({
|
||||||
|
selection: { anchor: 0, head: cm.state.doc.length },
|
||||||
|
userEvent: "select",
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
cm.execCommand("selectAll");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Since Editor is a thin layer over CodeMirror some methods
|
// Since Editor is a thin layer over CodeMirror some methods
|
||||||
|
|
Загрузка…
Ссылка в новой задаче