зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1745940 - [devtools] Implement blackboxing lines UI functionality r=nchevobbe
Basic UI functionality for blackboxing Enable this feature pref `devtools.debugger.features.blackbox-lines` This is mainly to gather feeback from the team and webcompat, things like the selection colors will likely change, maybe we can add a context menu to the editor gutter as well etc. Differential Revision: https://phabricator.services.mozilla.com/D132368
This commit is contained in:
Родитель
f1fedb6b37
Коммит
1490eb2f73
|
@ -0,0 +1,134 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
import { connect } from "../../utils/connect";
|
||||
import PropTypes from "prop-types";
|
||||
import { Component } from "react";
|
||||
import { toEditorLine, fromEditorLine } from "../../utils/editor";
|
||||
import { getBlackBoxRanges, getSelectedSource } from "../../selectors";
|
||||
|
||||
// This renders blackbox line highlighting in the editor
|
||||
class BlackboxLines extends Component {
|
||||
static get propTypes() {
|
||||
return {
|
||||
editor: PropTypes.object,
|
||||
selectedSource: PropTypes.object,
|
||||
blackboxedRanges: PropTypes.object,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { selectedSource, blackboxedRanges, editor } = this.props;
|
||||
const ranges = blackboxedRanges[selectedSource.url];
|
||||
if (!selectedSource.isBlackBoxed && !ranges) {
|
||||
return;
|
||||
}
|
||||
if (ranges) {
|
||||
if (!ranges.length) {
|
||||
// The whole source was blackboxed
|
||||
this.setAllBlackboxLines(editor);
|
||||
} else {
|
||||
editor.codeMirror.operation(() => {
|
||||
ranges.forEach(range => {
|
||||
const start = toEditorLine(selectedSource.id, range.start.line);
|
||||
const end = toEditorLine(selectedSource.id, range.end.line);
|
||||
editor.codeMirror.eachLine(start, end, lineHandle => {
|
||||
this.setBlackboxLine(editor, lineHandle);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
const { selectedSource, blackboxedRanges, editor } = this.props;
|
||||
const ranges = blackboxedRanges[selectedSource.url];
|
||||
|
||||
// when unblackboxed
|
||||
if (!ranges) {
|
||||
this.clearAllBlackboxLines(editor);
|
||||
return;
|
||||
}
|
||||
|
||||
// When the whole source is blackboxed
|
||||
if (!ranges.length) {
|
||||
this.setAllBlackboxLines(editor);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Possible perf improvement. Instead of going
|
||||
// over all the lines each time get diffs of what has
|
||||
// changed and update those.
|
||||
editor.codeMirror.operation(() => {
|
||||
editor.codeMirror.eachLine(lineHandle => {
|
||||
const line = fromEditorLine(
|
||||
selectedSource.id,
|
||||
editor.codeMirror.getLineNumber(lineHandle)
|
||||
);
|
||||
|
||||
if (this.isLineBlackboxed(ranges, line)) {
|
||||
this.setBlackboxLine(editor, lineHandle);
|
||||
} else {
|
||||
this.clearBlackboxLine(editor, lineHandle);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
// Lets make sure we remove everything relating to
|
||||
// blackboxing lines when this component is unmounted.
|
||||
this.clearAllBlackboxLines(this.props.editor);
|
||||
}
|
||||
|
||||
isLineBlackboxed(ranges, line) {
|
||||
return !!ranges.find(
|
||||
range => line >= range.start.line && line <= range.end.line
|
||||
);
|
||||
}
|
||||
|
||||
clearAllBlackboxLines(editor) {
|
||||
editor.codeMirror.operation(() => {
|
||||
editor.codeMirror.eachLine(lineHandle => {
|
||||
this.clearBlackboxLine(editor, lineHandle);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
setAllBlackboxLines(editor) {
|
||||
//TODO:We might be able to handle the whole source
|
||||
// than adding the blackboxing line by line
|
||||
editor.codeMirror.operation(() => {
|
||||
editor.codeMirror.eachLine(lineHandle => {
|
||||
this.setBlackboxLine(editor, lineHandle);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
clearBlackboxLine(editor, lineHandle) {
|
||||
editor.codeMirror.removeLineClass(
|
||||
lineHandle,
|
||||
"wrapClass",
|
||||
"blackboxed-line"
|
||||
);
|
||||
}
|
||||
|
||||
setBlackboxLine(editor, lineHandle) {
|
||||
editor.codeMirror.addLineClass(lineHandle, "wrapClass", "blackboxed-line");
|
||||
}
|
||||
|
||||
render() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
blackboxedRanges: getBlackBoxRanges(state),
|
||||
selectedSource: getSelectedSource(state),
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps)(BlackboxLines);
|
|
@ -84,7 +84,8 @@
|
|||
stroke: var(--logpoint-stroke);
|
||||
}
|
||||
|
||||
.editor.new-breakpoint.breakpoint-disabled svg {
|
||||
.editor.new-breakpoint.breakpoint-disabled svg,
|
||||
.blackboxed-line .editor.new-breakpoint svg {
|
||||
fill-opacity: var(--breakpoint-disabled-opacity);
|
||||
stroke-opacity: var(--breakpoint-disabled-opacity);
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
import PropTypes from "prop-types";
|
||||
import React, { Component } from "react";
|
||||
import Breakpoint from "./Breakpoint";
|
||||
|
||||
|
@ -12,6 +13,16 @@ import { breakpointItemActions } from "./menus/breakpoints";
|
|||
import { editorItemActions } from "./menus/editor";
|
||||
|
||||
class Breakpoints extends Component {
|
||||
static get propTypes() {
|
||||
return {
|
||||
cx: PropTypes.object,
|
||||
breakpoints: PropTypes.array,
|
||||
editor: PropTypes.object,
|
||||
breakpointActions: PropTypes.object,
|
||||
editorActions: PropTypes.object,
|
||||
selectedSource: PropTypes.object,
|
||||
};
|
||||
}
|
||||
render() {
|
||||
const {
|
||||
cx,
|
||||
|
@ -22,7 +33,7 @@ class Breakpoints extends Component {
|
|||
editorActions,
|
||||
} = this.props;
|
||||
|
||||
if (!selectedSource || !breakpoints || selectedSource.isBlackBoxed) {
|
||||
if (!selectedSource || !breakpoints) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -53,14 +53,6 @@ html[dir="rtl"] .editor-mount {
|
|||
direction: ltr;
|
||||
}
|
||||
|
||||
.theme-light .cm-s-mozilla .empty-line .CodeMirror-linenumber {
|
||||
color: var(--grey-40);
|
||||
}
|
||||
|
||||
.theme-dark .cm-s-mozilla .empty-line .CodeMirror-linenumber {
|
||||
color: var(--grey-50);
|
||||
}
|
||||
|
||||
.function-search {
|
||||
max-height: 300px;
|
||||
overflow: hidden;
|
||||
|
|
|
@ -13,6 +13,7 @@ import {
|
|||
getIsCurrentThreadPaused,
|
||||
getThreadContext,
|
||||
isSourceWithMap,
|
||||
getBlackBoxRanges,
|
||||
} from "../../selectors";
|
||||
|
||||
import { editorMenuItems, editorItemActions } from "./menus/editor";
|
||||
|
@ -30,6 +31,7 @@ class EditorMenu extends Component {
|
|||
cx,
|
||||
editor,
|
||||
selectedSource,
|
||||
blackboxedRanges,
|
||||
editorActions,
|
||||
hasMappedLocation,
|
||||
isPaused,
|
||||
|
@ -50,12 +52,14 @@ class EditorMenu extends Component {
|
|||
cx,
|
||||
editorActions,
|
||||
selectedSource,
|
||||
blackboxedRanges,
|
||||
hasMappedLocation,
|
||||
location,
|
||||
isPaused,
|
||||
editorWrappingEnabled,
|
||||
selectionText: editor.codeMirror.getSelection().trim(),
|
||||
isTextSelected: editor.codeMirror.somethingSelected(),
|
||||
editor,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
@ -67,6 +71,7 @@ class EditorMenu extends Component {
|
|||
|
||||
const mapStateToProps = (state, props) => ({
|
||||
cx: getThreadContext(state),
|
||||
blackboxedRanges: getBlackBoxRanges(state),
|
||||
isPaused: getIsCurrentThreadPaused(state),
|
||||
hasMappedLocation:
|
||||
(props.selectedSource.isOriginal ||
|
||||
|
|
|
@ -52,6 +52,7 @@ import ConditionalPanel from "./ConditionalPanel";
|
|||
import InlinePreviews from "./InlinePreviews";
|
||||
import HighlightCalls from "./HighlightCalls";
|
||||
import Exceptions from "./Exceptions";
|
||||
import BlackboxLines from "./BlackboxLines";
|
||||
|
||||
import {
|
||||
showSourceText,
|
||||
|
@ -98,6 +99,37 @@ const cssVars = {
|
|||
};
|
||||
|
||||
class Editor extends PureComponent {
|
||||
static get propTypes() {
|
||||
return {
|
||||
selectedSource: PropTypes.object,
|
||||
cx: PropTypes.object,
|
||||
closeTab: PropTypes.func,
|
||||
toggleBreakpointAtLine: PropTypes.func,
|
||||
conditionalPanelLocation: PropTypes.object,
|
||||
closeConditionalPanel: PropTypes.func,
|
||||
openConditionalPanel: PropTypes.func,
|
||||
updateViewport: PropTypes.func,
|
||||
isPaused: PropTypes.bool,
|
||||
highlightCalls: PropTypes.func,
|
||||
unhighlightCalls: PropTypes.func,
|
||||
breakpointActions: PropTypes.object,
|
||||
editorActions: PropTypes.object,
|
||||
addBreakpointAtLine: PropTypes.func,
|
||||
continueToHere: PropTypes.func,
|
||||
toggleBlackBox: PropTypes.func,
|
||||
updateCursorPosition: PropTypes.func,
|
||||
jumpToMappedLocation: PropTypes.func,
|
||||
selectedLocation: PropTypes.object,
|
||||
symbols: PropTypes.object,
|
||||
startPanelSize: PropTypes.number,
|
||||
endPanelSize: PropTypes.number,
|
||||
searchOn: PropTypes.bool,
|
||||
inlinePreviewEnabled: PropTypes.bool,
|
||||
editorWrappingEnabled: PropTypes.bool,
|
||||
skipPausing: PropTypes.bool,
|
||||
};
|
||||
}
|
||||
|
||||
$editorWrapper;
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
@ -606,6 +638,7 @@ class Editor extends PureComponent {
|
|||
<Breakpoints editor={editor} cx={cx} />
|
||||
<Preview editor={editor} editorRef={this.$editorWrapper} />
|
||||
<HighlightLines editor={editor} />
|
||||
{features.blackboxLines ? <BlackboxLines editor={editor} /> : null}
|
||||
<Exceptions />
|
||||
{
|
||||
<EditorMenu
|
||||
|
|
|
@ -10,7 +10,7 @@ import {
|
|||
getFilename,
|
||||
shouldBlackbox,
|
||||
} from "../../../utils/source";
|
||||
|
||||
import { toSourceLine } from "../../../utils/editor";
|
||||
import { downloadFile } from "../../../utils/utils";
|
||||
import { features } from "../../../utils/prefs";
|
||||
|
||||
|
@ -90,6 +90,85 @@ const blackBoxMenuItem = (cx, selectedSource, editorActions) => ({
|
|||
disabled: !shouldBlackbox(selectedSource),
|
||||
click: () => editorActions.toggleBlackBox(cx, selectedSource),
|
||||
});
|
||||
/**
|
||||
* Checks if a blackbox range exist for the line range.
|
||||
* That is if any start and end lines overlap any of the
|
||||
* blackbox ranges
|
||||
*
|
||||
* @param {Object} source - The current selected source
|
||||
* @param {Object} blackboxedRanges - the store of blackboxedRanges
|
||||
* @param {Object} line - The start and end line range
|
||||
*/
|
||||
function findBlackBoxRange(source, blackboxedRanges, line) {
|
||||
const ranges = blackboxedRanges[source.url];
|
||||
if (!ranges || !ranges.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return ranges.find(
|
||||
range =>
|
||||
(line.start >= range.start.line && line.start <= range.end.line) ||
|
||||
(line.end >= range.start.line && line.end <= range.end.line)
|
||||
);
|
||||
}
|
||||
|
||||
const blackBoxLinesMenuItem = (
|
||||
cx,
|
||||
selectedSource,
|
||||
editorActions,
|
||||
editor,
|
||||
blackboxedRanges
|
||||
) => {
|
||||
const { codeMirror } = editor;
|
||||
const from = codeMirror.getCursor("from");
|
||||
const to = codeMirror.getCursor("to");
|
||||
|
||||
const startLine = toSourceLine(selectedSource.id, from.line);
|
||||
const endLine = toSourceLine(selectedSource.id, to.line);
|
||||
|
||||
const shouldDisable =
|
||||
selectedSource.isBlackBoxed && !blackboxedRanges[selectedSource.url].length;
|
||||
|
||||
const blackboxRange = findBlackBoxRange(selectedSource, blackboxedRanges, {
|
||||
start: startLine,
|
||||
end: endLine,
|
||||
});
|
||||
|
||||
return {
|
||||
id: "node-menu-blackbox-lines",
|
||||
label: !blackboxRange
|
||||
? L10N.getStr("ignoreContextItem.ignoreLines")
|
||||
: L10N.getStr("ignoreContextItem.unignoreLines"),
|
||||
accesskey: !blackboxRange
|
||||
? L10N.getStr("ignoreContextItem.ignoreLines.accesskey")
|
||||
: L10N.getStr("ignoreContextItem.unignoreLines.accesskey"),
|
||||
disabled: shouldDisable,
|
||||
click: () => {
|
||||
const selectionRange = {
|
||||
start: {
|
||||
line: startLine,
|
||||
column: from.ch,
|
||||
},
|
||||
end: {
|
||||
line: endLine,
|
||||
column: to.ch,
|
||||
},
|
||||
};
|
||||
|
||||
// removes the current selection
|
||||
editor.codeMirror.replaceSelection(
|
||||
editor.codeMirror.getSelection(),
|
||||
"start"
|
||||
);
|
||||
editorActions.toggleBlackBox(
|
||||
cx,
|
||||
selectedSource,
|
||||
!blackboxRange,
|
||||
blackboxRange ? [blackboxRange] : [selectionRange]
|
||||
);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const watchExpressionItem = (
|
||||
cx,
|
||||
|
@ -140,12 +219,14 @@ export function editorMenuItems({
|
|||
cx,
|
||||
editorActions,
|
||||
selectedSource,
|
||||
blackboxedRanges,
|
||||
location,
|
||||
selectionText,
|
||||
hasMappedLocation,
|
||||
isTextSelected,
|
||||
isPaused,
|
||||
editorWrappingEnabled,
|
||||
editor,
|
||||
}) {
|
||||
const items = [];
|
||||
|
||||
|
@ -176,9 +257,22 @@ export function editorMenuItems({
|
|||
: []),
|
||||
{ type: "separator" },
|
||||
showSourceMenuItem(cx, selectedSource, editorActions),
|
||||
{ type: "separator" },
|
||||
blackBoxMenuItem(cx, selectedSource, editorActions)
|
||||
);
|
||||
|
||||
if (features.blackboxLines) {
|
||||
items.push(
|
||||
blackBoxLinesMenuItem(
|
||||
cx,
|
||||
selectedSource,
|
||||
editorActions,
|
||||
editor,
|
||||
blackboxedRanges
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (isTextSelected) {
|
||||
items.push(
|
||||
{ type: "separator" },
|
||||
|
@ -206,6 +300,7 @@ export function editorItemActions(dispatch) {
|
|||
jumpToMappedLocation: actions.jumpToMappedLocation,
|
||||
showSource: actions.showSource,
|
||||
toggleBlackBox: actions.toggleBlackBox,
|
||||
toggleBlackBoxLines: actions.toggleBlackBoxLines,
|
||||
toggleInlinePreview: actions.toggleInlinePreview,
|
||||
toggleEditorWrapping: actions.toggleEditorWrapping,
|
||||
},
|
||||
|
|
|
@ -9,6 +9,7 @@ DIRS += [
|
|||
]
|
||||
|
||||
CompiledModules(
|
||||
"BlackboxLines.js",
|
||||
"Breakpoint.js",
|
||||
"Breakpoints.js",
|
||||
"ColumnBreakpoint.js",
|
||||
|
|
|
@ -642,6 +642,16 @@ ignoreContextItem.ignore.accesskey=I
|
|||
ignoreContextItem.unignore=Unignore source
|
||||
ignoreContextItem.unignore.accesskey=U
|
||||
|
||||
# LOCALIZATION NOTE (ignoreContextItem.ignoreLines): Text associated
|
||||
# with the ignore lines context menu item
|
||||
ignoreContextItem.ignoreLines=Ignore lines
|
||||
ignoreContextItem.ignoreLines.accesskey=i
|
||||
|
||||
# LOCALIZATION NOTE (ignoreContextItem.unignoreLines): Text associated
|
||||
# with the unignore lines context menu item
|
||||
ignoreContextItem.unignoreLines=Unignore lines
|
||||
ignoreContextItem.unignoreLines.accesskey=u
|
||||
|
||||
# LOCALIZATION NOTE (sourceFooter.mappedSource): Text associated
|
||||
# with a mapped source. %S is replaced by the source map origin.
|
||||
sourceFooter.mappedSource=(From %S)
|
||||
|
|
|
@ -232,6 +232,21 @@ div.CodeMirror span.marked-text {
|
|||
margin-inline-end: -1px;
|
||||
}
|
||||
|
||||
|
||||
.cm-s-mozilla .empty-line .CodeMirror-linenumber {
|
||||
color: var(--grey-50);
|
||||
}
|
||||
|
||||
/* Blackboxing lines */
|
||||
|
||||
.cm-s-mozilla .CodeMirror-lines .blackboxed-line {
|
||||
background-color: hsl(211, 89%, 17%);
|
||||
}
|
||||
|
||||
.cm-s-mozilla .blackboxed-line .CodeMirror-linenumber {
|
||||
color: var(--theme-icon-checked-color);
|
||||
}
|
||||
|
||||
/* Highlight for evaluating current statement. */
|
||||
div.CodeMirror span.eval-text {
|
||||
background-color: #556;
|
||||
|
|
|
@ -220,6 +220,20 @@ div.CodeMirror span.marked-text {
|
|||
margin-inline-end: -1px;
|
||||
}
|
||||
|
||||
.cm-s-mozilla .empty-line .CodeMirror-linenumber {
|
||||
color: var(--grey-40);
|
||||
}
|
||||
|
||||
/* Blackboxing lines */
|
||||
|
||||
.cm-s-mozilla .CodeMirror-lines .blackboxed-line {
|
||||
background-color: hsl(211, 89%, 95%);
|
||||
}
|
||||
|
||||
.theme-light .cm-s-mozilla .blackboxed-line .CodeMirror-linenumber {
|
||||
color: var(--theme-icon-checked-color);
|
||||
}
|
||||
|
||||
/* Highlight for evaluating current statement. */
|
||||
div.CodeMirror span.eval-text {
|
||||
background-color: #ccd;
|
||||
|
|
Загрузка…
Ссылка в новой задаче