summaryrefslogtreecommitdiffstats
path: root/devtools/client/debugger/src/components/Editor
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/debugger/src/components/Editor')
-rw-r--r--devtools/client/debugger/src/components/Editor/ConditionalPanel.js2
-rw-r--r--devtools/client/debugger/src/components/Editor/Footer.css39
-rw-r--r--devtools/client/debugger/src/components/Editor/Footer.js208
-rw-r--r--devtools/client/debugger/src/components/Editor/InlinePreview.js2
-rw-r--r--devtools/client/debugger/src/components/Editor/InlinePreviewRow.js8
-rw-r--r--devtools/client/debugger/src/components/Editor/InlinePreviews.js2
-rw-r--r--devtools/client/debugger/src/components/Editor/Preview/Popup.js6
-rw-r--r--devtools/client/debugger/src/components/Editor/Preview/index.js4
-rw-r--r--devtools/client/debugger/src/components/Editor/SearchInFileBar.js14
-rw-r--r--devtools/client/debugger/src/components/Editor/Tab.js12
-rw-r--r--devtools/client/debugger/src/components/Editor/Tabs.js7
-rw-r--r--devtools/client/debugger/src/components/Editor/index.js272
-rw-r--r--devtools/client/debugger/src/components/Editor/tests/DebugLine.spec.js2
-rw-r--r--devtools/client/debugger/src/components/Editor/tests/__snapshots__/ConditionalPanel.spec.js.snap24
-rw-r--r--devtools/client/debugger/src/components/Editor/tests/__snapshots__/Footer.spec.js.snap20
15 files changed, 479 insertions, 143 deletions
diff --git a/devtools/client/debugger/src/components/Editor/ConditionalPanel.js b/devtools/client/debugger/src/components/Editor/ConditionalPanel.js
index 8ff84c287a..97876f2f00 100644
--- a/devtools/client/debugger/src/components/Editor/ConditionalPanel.js
+++ b/devtools/client/debugger/src/components/Editor/ConditionalPanel.js
@@ -121,7 +121,7 @@ export class ConditionalPanel extends PureComponent {
return this.clearConditionalPanel();
}
- componentDidUpdate(prevProps) {
+ componentDidUpdate() {
this.keepFocusOnInput();
}
diff --git a/devtools/client/debugger/src/components/Editor/Footer.css b/devtools/client/debugger/src/components/Editor/Footer.css
index 4a3272879b..f3382e94b5 100644
--- a/devtools/client/debugger/src/components/Editor/Footer.css
+++ b/devtools/client/debugger/src/components/Editor/Footer.css
@@ -67,6 +67,45 @@
opacity: 0.6;
}
+.devtools-button.debugger-source-map-button {
+ display: inline-flex;
+ align-items: center;
+ margin: 0;
+ --menuitem-icon-image: url("chrome://devtools/content/debugger/images/sourcemap.svg");
+
+ &.not-mapped {
+ --icon-color: var(--theme-icon-dimmed-color);
+ }
+
+ &.original {
+ --icon-color: var(--theme-icon-checked-color);
+ --menuitem-icon-image: url("chrome://devtools/content/debugger/images/sourcemap-active.svg");
+ }
+
+ &.error {
+ --icon-color: var(--theme-icon-warning-color);
+ }
+
+ &.disabled {
+ --icon-color: var(--theme-icon-dimmed-color);
+ --menuitem-icon-image: url("chrome://devtools/content/debugger/images/sourcemap-disabled.svg");
+ }
+
+ &.loading {
+ --menuitem-icon-image: url("chrome://devtools/content/debugger/images/loader.svg");
+ }
+
+ &::before {
+ /* override default style to have similar left and right margins */
+ margin-inline-end: 3px;
+ color: var(--icon-color, currentColor);
+ }
+
+ &.loading::before {
+ animation: spin 2s linear infinite;
+ }
+}
+
.source-footer .mapped-source,
.source-footer .cursor-position {
color: var(--theme-body-color);
diff --git a/devtools/client/debugger/src/components/Editor/Footer.js b/devtools/client/debugger/src/components/Editor/Footer.js
index c4ff02caf4..69c7b52b68 100644
--- a/devtools/client/debugger/src/components/Editor/Footer.js
+++ b/devtools/client/debugger/src/components/Editor/Footer.js
@@ -7,6 +7,7 @@ import {
div,
button,
span,
+ hr,
} from "devtools/client/shared/vendor/react-dom-factories";
import PropTypes from "devtools/client/shared/vendor/react-prop-types";
import { connect } from "devtools/client/shared/vendor/react-redux";
@@ -23,14 +24,23 @@ import {
isSourceOnSourceMapIgnoreList,
isSourceMapIgnoreListEnabled,
getSelectedMappedSource,
+ getSourceMapErrorForSourceActor,
+ areSourceMapsEnabled,
+ getShouldSelectOriginalLocation,
+ isSourceActorWithSourceMap,
+ getSourceMapResolvedURL,
+ isSelectedMappedSourceLoading,
} from "../../selectors/index";
-import { isPretty, getFilename, shouldBlackbox } from "../../utils/source";
+import { isPretty, shouldBlackbox } from "../../utils/source";
import { PaneToggleButton } from "../shared/Button/index";
import AccessibleImage from "../shared/AccessibleImage";
const classnames = require("resource://devtools/client/shared/classnames.js");
+const MenuButton = require("resource://devtools/client/shared/components/menu/MenuButton.js");
+const MenuItem = require("resource://devtools/client/shared/components/menu/MenuItem.js");
+const MenuList = require("resource://devtools/client/shared/components/menu/MenuList.js");
class SourceFooter extends PureComponent {
static get propTypes() {
@@ -155,9 +165,11 @@ class SourceFooter extends PureComponent {
}
renderCommands() {
- const commands = [this.blackBoxButton(), this.prettyPrintButton()].filter(
- Boolean
- );
+ const commands = [
+ this.blackBoxButton(),
+ this.prettyPrintButton(),
+ this.renderSourceMapButton(),
+ ].filter(Boolean);
return commands.length
? div(
@@ -169,7 +181,7 @@ class SourceFooter extends PureComponent {
: null;
}
- renderSourceSummary() {
+ renderMappedSource() {
const { mappedSource, jumpToMappedLocation, selectedLocation } = this.props;
if (!mappedSource) {
@@ -182,12 +194,11 @@ class SourceFooter extends PureComponent {
: "sourceFooter.mappedGeneratedSource.tooltip",
mappedSource.url
);
- const filename = getFilename(mappedSource);
const label = L10N.getFormatStr(
mappedSource.isOriginal
? "sourceFooter.mappedOriginalSource.title"
: "sourceFooter.mappedGeneratedSource.title",
- filename
+ mappedSource.shortName
);
return button(
{
@@ -227,6 +238,148 @@ class SourceFooter extends PureComponent {
);
}
+ getSourceMapLabel() {
+ if (!this.props.selectedLocation) {
+ return undefined;
+ }
+ if (!this.props.areSourceMapsEnabled) {
+ return L10N.getStr("sourceFooter.sourceMapButton.disabled");
+ }
+ if (this.props.sourceMapError) {
+ return undefined;
+ }
+ if (!this.props.isSourceActorWithSourceMap) {
+ return L10N.getStr("sourceFooter.sourceMapButton.sourceNotMapped");
+ }
+ if (this.props.selectedLocation.source.isOriginal) {
+ return L10N.getStr("sourceFooter.sourceMapButton.isOriginalSource");
+ }
+ return L10N.getStr("sourceFooter.sourceMapButton.isBundleSource");
+ }
+
+ getSourceMapTitle() {
+ if (this.props.sourceMapError) {
+ return L10N.getFormatStr(
+ "sourceFooter.sourceMapButton.errorTitle",
+ this.props.sourceMapError
+ );
+ }
+ if (this.props.isSourceMapLoading) {
+ return L10N.getStr("sourceFooter.sourceMapButton.loadingTitle");
+ }
+ return L10N.getStr("sourceFooter.sourceMapButton.title");
+ }
+
+ renderSourceMapButton() {
+ const { toolboxDoc } = this.context;
+
+ return React.createElement(
+ MenuButton,
+ {
+ menuId: "debugger-source-map-button",
+ toolboxDoc,
+ className: classnames("devtools-button", "debugger-source-map-button", {
+ error: !!this.props.sourceMapError,
+ loading: this.props.isSourceMapLoading,
+ disabled: !this.props.areSourceMapsEnabled,
+ "not-mapped":
+ !this.props.selectedLocation?.source.isOriginal &&
+ !this.props.isSourceActorWithSourceMap,
+ original: this.props.selectedLocation?.source.isOriginal,
+ }),
+ title: this.getSourceMapTitle(),
+ label: this.getSourceMapLabel(),
+ icon: true,
+ },
+ () => this.renderSourceMapMenuItems()
+ );
+ }
+
+ renderSourceMapMenuItems() {
+ const items = [
+ React.createElement(MenuItem, {
+ className: "menu-item debugger-source-map-enabled",
+ checked: this.props.areSourceMapsEnabled,
+ label: L10N.getStr("sourceFooter.sourceMapButton.enable"),
+ onClick: this.toggleSourceMaps,
+ }),
+ hr(),
+ React.createElement(MenuItem, {
+ className: "menu-item debugger-source-map-open-original",
+ checked: this.props.shouldSelectOriginalLocation,
+ label: L10N.getStr(
+ "sourceFooter.sourceMapButton.showOriginalSourceByDefault"
+ ),
+ onClick: this.toggleSelectOriginalByDefault,
+ }),
+ ];
+
+ if (this.props.mappedSource) {
+ items.push(
+ React.createElement(MenuItem, {
+ className: "menu-item debugger-jump-mapped-source",
+ label: this.props.mappedSource.isOriginal
+ ? L10N.getStr("sourceFooter.sourceMapButton.jumpToGeneratedSource")
+ : L10N.getStr("sourceFooter.sourceMapButton.jumpToOriginalSource"),
+ tooltip: this.props.mappedSource.url,
+ onClick: () =>
+ this.props.jumpToMappedLocation(this.props.selectedLocation),
+ })
+ );
+ }
+
+ if (this.props.resolvedSourceMapURL) {
+ items.push(
+ React.createElement(MenuItem, {
+ className: "menu-item debugger-source-map-link",
+ label: L10N.getStr(
+ "sourceFooter.sourceMapButton.openSourceMapInNewTab"
+ ),
+ onClick: this.openSourceMap,
+ })
+ );
+ }
+ return React.createElement(
+ MenuList,
+ {
+ id: "debugger-source-map-list",
+ },
+ items
+ );
+ }
+
+ openSourceMap = () => {
+ let line, column;
+ if (
+ this.props.sourceMapError &&
+ this.props.sourceMapError.includes("JSON.parse")
+ ) {
+ const match = this.props.sourceMapError.match(
+ /at line (\d+) column (\d+)/
+ );
+ if (match) {
+ line = match[1];
+ column = match[2];
+ }
+ }
+ this.props.openSourceMap(
+ this.props.resolvedSourceMapURL || this.props.selectedLocation.source.url,
+ line,
+ column
+ );
+ };
+
+ toggleSourceMaps = () => {
+ this.props.toggleSourceMapsEnabled(!this.props.areSourceMapsEnabled);
+ };
+
+ toggleSelectOriginalByDefault = () => {
+ this.props.setDefaultSelectedLocation(
+ !this.props.shouldSelectOriginalLocation
+ );
+ this.props.jumpToMappedSelectedLocation();
+ };
+
render() {
return div(
{
@@ -242,19 +395,40 @@ class SourceFooter extends PureComponent {
{
className: "source-footer-end",
},
- this.renderSourceSummary(),
+ this.renderMappedSource(),
this.renderCursorPosition(),
this.renderToggleButton()
)
);
}
}
+SourceFooter.contextTypes = {
+ toolboxDoc: PropTypes.object,
+};
const mapStateToProps = state => {
const selectedSource = getSelectedSource(state);
const selectedLocation = getSelectedLocation(state);
const sourceTextContent = getSelectedSourceTextContent(state);
+ const areSourceMapsEnabledProp = areSourceMapsEnabled(state);
+ const isSourceActorWithSourceMapProp = isSourceActorWithSourceMap(
+ state,
+ selectedLocation?.sourceActor.id
+ );
+ const sourceMapError = selectedLocation?.sourceActor
+ ? getSourceMapErrorForSourceActor(state, selectedLocation.sourceActor.id)
+ : null;
+ const mappedSource = getSelectedMappedSource(state);
+
+ const isSourceMapLoading =
+ areSourceMapsEnabledProp &&
+ isSourceActorWithSourceMapProp &&
+ // `mappedSource` will be null while loading, we need another way to know when it is done computing
+ !mappedSource &&
+ isSelectedMappedSourceLoading(state) &&
+ !sourceMapError;
+
return {
selectedSource,
selectedLocation,
@@ -265,7 +439,8 @@ const mapStateToProps = state => {
isSourceMapIgnoreListEnabled(state) &&
isSourceOnSourceMapIgnoreList(state, selectedSource),
sourceLoaded: !!sourceTextContent,
- mappedSource: getSelectedMappedSource(state),
+ mappedSource,
+ isSourceMapLoading,
prettySource: getPrettySource(
state,
selectedSource ? selectedSource.id : null
@@ -277,6 +452,17 @@ const mapStateToProps = state => {
prettyPrintMessage: selectedLocation
? getPrettyPrintMessage(state, selectedLocation)
: null,
+
+ sourceMapError,
+ resolvedSourceMapURL: selectedLocation?.sourceActor
+ ? getSourceMapResolvedURL(state, selectedLocation.sourceActor.id)
+ : null,
+ isSourceActorWithSourceMap: isSourceActorWithSourceMapProp,
+
+ sourceMapURL: selectedLocation?.sourceActor.sourceMapURL,
+
+ areSourceMapsEnabled: areSourceMapsEnabledProp,
+ shouldSelectOriginalLocation: getShouldSelectOriginalLocation(state),
};
};
@@ -285,4 +471,8 @@ export default connect(mapStateToProps, {
toggleBlackBox: actions.toggleBlackBox,
jumpToMappedLocation: actions.jumpToMappedLocation,
togglePaneCollapse: actions.togglePaneCollapse,
+ toggleSourceMapsEnabled: actions.toggleSourceMapsEnabled,
+ setDefaultSelectedLocation: actions.setDefaultSelectedLocation,
+ jumpToMappedSelectedLocation: actions.jumpToMappedSelectedLocation,
+ openSourceMap: actions.openSourceMap,
})(SourceFooter);
diff --git a/devtools/client/debugger/src/components/Editor/InlinePreview.js b/devtools/client/debugger/src/components/Editor/InlinePreview.js
index 552143dcf2..60303e38b5 100644
--- a/devtools/client/debugger/src/components/Editor/InlinePreview.js
+++ b/devtools/client/debugger/src/components/Editor/InlinePreview.js
@@ -27,7 +27,7 @@ class InlinePreview extends PureComponent {
};
}
- showInScopes(variable) {
+ showInScopes() {
// TODO: focus on variable value in the scopes sidepanel
// we will need more info from parent comp
}
diff --git a/devtools/client/debugger/src/components/Editor/InlinePreviewRow.js b/devtools/client/debugger/src/components/Editor/InlinePreviewRow.js
index bc54fc5b4d..18fe98ff17 100644
--- a/devtools/client/debugger/src/components/Editor/InlinePreviewRow.js
+++ b/devtools/client/debugger/src/components/Editor/InlinePreviewRow.js
@@ -67,13 +67,13 @@ class InlinePreviewRow extends PureComponent {
null,
previews.map(preview =>
React.createElement(InlinePreview, {
- line: line,
+ line,
key: `${line}-${preview.name}`,
variable: preview.name,
value: preview.value,
- openElementInInspector: openElementInInspector,
- highlightDomElement: highlightDomElement,
- unHighlightDomElement: unHighlightDomElement,
+ openElementInInspector,
+ highlightDomElement,
+ unHighlightDomElement,
})
)
),
diff --git a/devtools/client/debugger/src/components/Editor/InlinePreviews.js b/devtools/client/debugger/src/components/Editor/InlinePreviews.js
index 18616ae3ed..ba8b08669a 100644
--- a/devtools/client/debugger/src/components/Editor/InlinePreviews.js
+++ b/devtools/client/debugger/src/components/Editor/InlinePreviews.js
@@ -49,7 +49,7 @@ class InlinePreviews extends Component {
inlinePreviewRows = Object.keys(previewsObj).map(line => {
const lineNum = parseInt(line, 10);
return React.createElement(InlinePreviewRow, {
- editor: editor,
+ editor,
key: line,
line: lineNum,
previews: previewsObj[line],
diff --git a/devtools/client/debugger/src/components/Editor/Preview/Popup.js b/devtools/client/debugger/src/components/Editor/Preview/Popup.js
index a010358dc1..431cb52729 100644
--- a/devtools/client/debugger/src/components/Editor/Preview/Popup.js
+++ b/devtools/client/debugger/src/components/Editor/Preview/Popup.js
@@ -100,7 +100,7 @@ export class Popup extends Component {
renderExceptionPreview(exception) {
return React.createElement(ExceptionPopup, {
- exception: exception,
+ exception,
clearPreview: this.props.clearPreview,
});
}
@@ -182,8 +182,8 @@ export class Popup extends Component {
Popover,
{
targetPosition: cursorPos,
- type: type,
- editorRef: editorRef,
+ type,
+ editorRef,
target: this.props.preview.target,
mouseout: this.props.clearPreview,
},
diff --git a/devtools/client/debugger/src/components/Editor/Preview/index.js b/devtools/client/debugger/src/components/Editor/Preview/index.js
index 218d33007f..10c600670e 100644
--- a/devtools/client/debugger/src/components/Editor/Preview/index.js
+++ b/devtools/client/debugger/src/components/Editor/Preview/index.js
@@ -44,7 +44,7 @@ class Preview extends PureComponent {
codeMirrorWrapper.removeEventListener("mousedown", this.onMouseDown);
}
- updateListeners(prevProps) {
+ updateListeners() {
const { codeMirror } = this.props.editor;
const codeMirrorWrapper = codeMirror.getWrapperElement();
codeMirror.on("tokenenter", this.onTokenEnter);
@@ -107,7 +107,7 @@ class Preview extends PureComponent {
return null;
}
return React.createElement(Popup, {
- preview: preview,
+ preview,
editor: this.props.editor,
editorRef: this.props.editorRef,
clearPreview: this.clearPreview,
diff --git a/devtools/client/debugger/src/components/Editor/SearchInFileBar.js b/devtools/client/debugger/src/components/Editor/SearchInFileBar.js
index a3491a3fef..26f95ce75d 100644
--- a/devtools/client/debugger/src/components/Editor/SearchInFileBar.js
+++ b/devtools/client/debugger/src/components/Editor/SearchInFileBar.js
@@ -97,7 +97,7 @@ class SearchInFileBar extends Component {
shortcuts.on("Escape", this.onEscape);
}
- componentDidUpdate(prevProps, prevState) {
+ componentDidUpdate() {
if (this.refs.resultList && this.refs.resultList.refs) {
scrollList(this.refs.resultList.refs, this.state.selectedResultIndex);
}
@@ -111,7 +111,7 @@ class SearchInFileBar extends Component {
const { editor: ed } = this.props;
if (ed) {
const ctx = { ed, cm: ed.codeMirror };
- removeOverlay(ctx, this.state.query);
+ removeOverlay(ctx);
}
};
@@ -165,7 +165,7 @@ class SearchInFileBar extends Component {
const ctx = { ed: editor, cm: editor.codeMirror };
if (!query) {
- clearSearch(ctx.cm, query);
+ clearSearch(ctx.cm);
return;
}
@@ -249,11 +249,11 @@ class SearchInFileBar extends Component {
return this.doSearch(e.target.value);
};
- onFocus = e => {
+ onFocus = () => {
this.setState({ inputFocused: true });
};
- onBlur = e => {
+ onBlur = () => {
this.setState({ inputFocused: false });
};
@@ -321,7 +321,7 @@ class SearchInFileBar extends Component {
},
React.createElement(SearchInput, {
query: this.state.query,
- count: count,
+ count,
placeholder: L10N.getStr("sourceSearch.search.placeholder2"),
summaryMsg: this.buildSummaryMsg(),
isLoading: false,
@@ -349,7 +349,7 @@ SearchInFileBar.contextTypes = {
shortcuts: PropTypes.object,
};
-const mapStateToProps = (state, p) => {
+const mapStateToProps = state => {
const selectedSource = getSelectedSource(state);
return {
diff --git a/devtools/client/debugger/src/components/Editor/Tab.js b/devtools/client/debugger/src/components/Editor/Tab.js
index ba5e1c1934..98aca90cd2 100644
--- a/devtools/client/debugger/src/components/Editor/Tab.js
+++ b/devtools/client/debugger/src/components/Editor/Tab.js
@@ -15,7 +15,6 @@ import actions from "../../actions/index";
import {
getDisplayPath,
getFileURL,
- getSourceQueryString,
getTruncatedFileName,
isPretty,
} from "../../utils/source";
@@ -87,14 +86,13 @@ class Tab extends PureComponent {
});
const path = getDisplayPath(source, tabSources);
- const query = getSourceQueryString(source);
return div(
{
draggable: true,
- onDragOver: onDragOver,
- onDragStart: onDragStart,
- onDragEnd: onDragEnd,
- className: className,
+ onDragOver,
+ onDragStart,
+ onDragEnd,
+ className,
"data-index": index,
"data-source-id": sourceId,
onClick: handleTabClick,
@@ -115,7 +113,7 @@ class Tab extends PureComponent {
{
className: "filename",
},
- getTruncatedFileName(source, query),
+ getTruncatedFileName(source),
path && span(null, `../${path}/..`)
),
React.createElement(CloseButton, {
diff --git a/devtools/client/debugger/src/components/Editor/Tabs.js b/devtools/client/debugger/src/components/Editor/Tabs.js
index 3577a4909c..d93f7d3b18 100644
--- a/devtools/client/debugger/src/components/Editor/Tabs.js
+++ b/devtools/client/debugger/src/components/Editor/Tabs.js
@@ -23,7 +23,7 @@ import {
import { isVisible } from "../../utils/ui";
import { getHiddenTabs } from "../../utils/tabs";
-import { getFilename, isPretty, getFileURL } from "../../utils/source";
+import { isPretty, getFileURL } from "../../utils/source";
import actions from "../../actions/index";
import Tab from "./Tab";
@@ -144,13 +144,12 @@ class Tabs extends PureComponent {
renderDropdownSource = source => {
const { selectSource } = this.props;
- const filename = getFilename(source);
const onClick = () => selectSource(source);
return li(
{
key: source.id,
- onClick: onClick,
+ onClick,
title: getFileURL(source, false),
},
React.createElement(AccessibleImage, {
@@ -160,7 +159,7 @@ class Tabs extends PureComponent {
{
className: "dropdown-label",
},
- filename
+ source.shortName
)
);
};
diff --git a/devtools/client/debugger/src/components/Editor/index.js b/devtools/client/debugger/src/components/Editor/index.js
index c659de77d2..ae9bde7657 100644
--- a/devtools/client/debugger/src/components/Editor/index.js
+++ b/devtools/client/debugger/src/components/Editor/index.js
@@ -12,6 +12,7 @@ import { connect } from "devtools/client/shared/vendor/react-redux";
import { getLineText, isLineBlackboxed } from "./../../utils/source";
import { createLocation } from "./../../utils/location";
import { getIndentation } from "../../utils/indentation";
+import { features } from "../../utils/prefs";
import {
getActiveSearch,
@@ -50,10 +51,9 @@ import BlackboxLines from "./BlackboxLines";
import {
showSourceText,
- showLoading,
- showErrorMessage,
+ setDocument,
+ resetLineNumberFormat,
getEditor,
- clearEditor,
getCursorLine,
getCursorColumn,
lineAtHeight,
@@ -143,37 +143,45 @@ class Editor extends PureComponent {
this.props.selectedSourceTextContent?.value ||
nextProps.symbols !== this.props.symbols;
- const shouldUpdateSize =
- nextProps.startPanelSize !== this.props.startPanelSize ||
- nextProps.endPanelSize !== this.props.endPanelSize;
-
- const shouldScroll =
- nextProps.selectedLocation &&
- this.shouldScrollToLocation(nextProps, editor);
+ if (!features.codemirrorNext) {
+ const shouldUpdateSize =
+ nextProps.startPanelSize !== this.props.startPanelSize ||
+ nextProps.endPanelSize !== this.props.endPanelSize;
+
+ const shouldScroll =
+ nextProps.selectedLocation &&
+ this.shouldScrollToLocation(nextProps, editor);
+
+ if (shouldUpdateText || shouldUpdateSize || shouldScroll) {
+ startOperation();
+ if (shouldUpdateText) {
+ this.setText(nextProps, editor);
+ }
+ if (shouldUpdateSize) {
+ editor.codeMirror.setSize();
+ }
+ if (shouldScroll) {
+ this.scrollToLocation(nextProps, editor);
+ }
+ endOperation();
+ }
- if (shouldUpdateText || shouldUpdateSize || shouldScroll) {
- startOperation();
+ if (this.props.selectedSource != nextProps.selectedSource) {
+ this.props.updateViewport();
+ resizeBreakpointGutter(editor.codeMirror);
+ resizeToggleButton(editor.codeMirror);
+ }
+ } else {
+ // For codemirror 6
+ // eslint-disable-next-line no-lonely-if
if (shouldUpdateText) {
this.setText(nextProps, editor);
}
- if (shouldUpdateSize) {
- editor.codeMirror.setSize();
- }
- if (shouldScroll) {
- this.scrollToLocation(nextProps, editor);
- }
- endOperation();
- }
-
- if (this.props.selectedSource != nextProps.selectedSource) {
- this.props.updateViewport();
- resizeBreakpointGutter(editor.codeMirror);
- resizeToggleButton(editor.codeMirror);
}
}
setupEditor() {
- const editor = getEditor();
+ const editor = getEditor(features.codemirrorNext);
// disables the default search shortcuts
editor._initShortcuts = () => {};
@@ -183,71 +191,84 @@ class Editor extends PureComponent {
editor.appendToLocalElement(node.querySelector(".editor-mount"));
}
- const { codeMirror } = editor;
-
- this.abortController = new window.AbortController();
-
- // CodeMirror refreshes its internal state on window resize, but we need to also
- // refresh it when the side panels are resized.
- // We could have a ResizeObserver instead, but we wouldn't be able to differentiate
- // between window resize and side panel resize and as a result, might refresh
- // codeMirror twice, which is wasteful.
- window.document
- .querySelector(".editor-pane")
- .addEventListener("resizeend", () => codeMirror.refresh(), {
- signal: this.abortController.signal,
- });
-
- codeMirror.on("gutterClick", this.onGutterClick);
- codeMirror.on("cursorActivity", this.onCursorChange);
-
- const codeMirrorWrapper = codeMirror.getWrapperElement();
- // Set code editor wrapper to be focusable
- codeMirrorWrapper.tabIndex = 0;
- codeMirrorWrapper.addEventListener("keydown", e => this.onKeyDown(e));
- codeMirrorWrapper.addEventListener("click", e => this.onClick(e));
- codeMirrorWrapper.addEventListener("mouseover", onMouseOver(codeMirror));
-
- const toggleFoldMarkerVisibility = e => {
- if (node instanceof HTMLElement) {
- node
- .querySelectorAll(".CodeMirror-guttermarker-subtle")
- .forEach(elem => {
- elem.classList.toggle("visible");
- });
- }
- };
+ if (!features.codemirrorNext) {
+ const { codeMirror } = editor;
+
+ this.abortController = new window.AbortController();
+
+ // CodeMirror refreshes its internal state on window resize, but we need to also
+ // refresh it when the side panels are resized.
+ // We could have a ResizeObserver instead, but we wouldn't be able to differentiate
+ // between window resize and side panel resize and as a result, might refresh
+ // codeMirror twice, which is wasteful.
+ window.document
+ .querySelector(".editor-pane")
+ .addEventListener("resizeend", () => codeMirror.refresh(), {
+ signal: this.abortController.signal,
+ });
+
+ codeMirror.on("gutterClick", this.onGutterClick);
+ codeMirror.on("cursorActivity", this.onCursorChange);
+
+ const codeMirrorWrapper = codeMirror.getWrapperElement();
+ // Set code editor wrapper to be focusable
+ codeMirrorWrapper.tabIndex = 0;
+ codeMirrorWrapper.addEventListener("keydown", e => this.onKeyDown(e));
+ codeMirrorWrapper.addEventListener("click", e => this.onClick(e));
+ codeMirrorWrapper.addEventListener("mouseover", onMouseOver(codeMirror));
+
+ const toggleFoldMarkerVisibility = () => {
+ if (node instanceof HTMLElement) {
+ node
+ .querySelectorAll(".CodeMirror-guttermarker-subtle")
+ .forEach(elem => {
+ elem.classList.toggle("visible");
+ });
+ }
+ };
- const codeMirrorGutter = codeMirror.getGutterElement();
- codeMirrorGutter.addEventListener("mouseleave", toggleFoldMarkerVisibility);
- codeMirrorGutter.addEventListener("mouseenter", toggleFoldMarkerVisibility);
- codeMirrorWrapper.addEventListener("contextmenu", event =>
- this.openMenu(event)
- );
+ const codeMirrorGutter = codeMirror.getGutterElement();
+ codeMirrorGutter.addEventListener(
+ "mouseleave",
+ toggleFoldMarkerVisibility
+ );
+ codeMirrorGutter.addEventListener(
+ "mouseenter",
+ toggleFoldMarkerVisibility
+ );
+ codeMirrorWrapper.addEventListener("contextmenu", event =>
+ this.openMenu(event)
+ );
- codeMirror.on("scroll", this.onEditorScroll);
- this.onEditorScroll();
+ codeMirror.on("scroll", this.onEditorScroll);
+ this.onEditorScroll();
+ }
this.setState({ editor });
return editor;
}
componentDidMount() {
- const { shortcuts } = this.context;
+ if (!features.codemirrorNext) {
+ const { shortcuts } = this.context;
- shortcuts.on(L10N.getStr("toggleBreakpoint.key"), this.onToggleBreakpoint);
- shortcuts.on(
- L10N.getStr("toggleCondPanel.breakpoint.key"),
- this.onToggleConditionalPanel
- );
- shortcuts.on(
- L10N.getStr("toggleCondPanel.logPoint.key"),
- this.onToggleConditionalPanel
- );
- shortcuts.on(
- L10N.getStr("sourceTabs.closeTab.key"),
- this.onCloseShortcutPress
- );
- shortcuts.on("Esc", this.onEscape);
+ shortcuts.on(
+ L10N.getStr("toggleBreakpoint.key"),
+ this.onToggleBreakpoint
+ );
+ shortcuts.on(
+ L10N.getStr("toggleCondPanel.breakpoint.key"),
+ this.onToggleConditionalPanel
+ );
+ shortcuts.on(
+ L10N.getStr("toggleCondPanel.logPoint.key"),
+ this.onToggleConditionalPanel
+ );
+ shortcuts.on(
+ L10N.getStr("sourceTabs.closeTab.key"),
+ this.onCloseShortcutPress
+ );
+ shortcuts.on("Esc", this.onEscape);
+ }
}
onCloseShortcutPress = e => {
@@ -260,22 +281,24 @@ class Editor extends PureComponent {
};
componentWillUnmount() {
- const { editor } = this.state;
- if (editor) {
- editor.destroy();
- editor.codeMirror.off("scroll", this.onEditorScroll);
- this.setState({ editor: null });
- }
+ if (!features.codemirrorNext) {
+ const { editor } = this.state;
+ if (editor) {
+ editor.destroy();
+ editor.codeMirror.off("scroll", this.onEditorScroll);
+ this.setState({ editor: null });
+ }
- const { shortcuts } = this.context;
- shortcuts.off(L10N.getStr("sourceTabs.closeTab.key"));
- shortcuts.off(L10N.getStr("toggleBreakpoint.key"));
- shortcuts.off(L10N.getStr("toggleCondPanel.breakpoint.key"));
- shortcuts.off(L10N.getStr("toggleCondPanel.logPoint.key"));
+ const { shortcuts } = this.context;
+ shortcuts.off(L10N.getStr("sourceTabs.closeTab.key"));
+ shortcuts.off(L10N.getStr("toggleBreakpoint.key"));
+ shortcuts.off(L10N.getStr("toggleCondPanel.breakpoint.key"));
+ shortcuts.off(L10N.getStr("toggleCondPanel.logPoint.key"));
- if (this.abortController) {
- this.abortController.abort();
- this.abortController = null;
+ if (this.abortController) {
+ this.abortController.abort();
+ this.abortController = null;
+ }
}
}
@@ -542,7 +565,7 @@ class Editor extends PureComponent {
}
}
- shouldScrollToLocation(nextProps, editor) {
+ shouldScrollToLocation(nextProps) {
if (
!nextProps.selectedLocation?.line ||
!nextProps.selectedSourceTextContent
@@ -583,12 +606,14 @@ class Editor extends PureComponent {
// check if we previously had a selected source
if (!selectedSource) {
- this.clearEditor();
+ if (!features.codemirrorNext) {
+ this.clearEditor();
+ }
return;
}
if (!selectedSourceTextContent?.value) {
- showLoading(editor);
+ this.showLoadingMessage(editor);
return;
}
@@ -602,7 +627,16 @@ class Editor extends PureComponent {
return;
}
- showSourceText(editor, selectedSource, selectedSourceTextContent, symbols);
+ if (!features.codemirrorNext) {
+ showSourceText(
+ editor,
+ selectedSource,
+ selectedSourceTextContent,
+ symbols
+ );
+ } else {
+ editor.setText(selectedSourceTextContent.value.value);
+ }
}
clearEditor() {
@@ -611,7 +645,9 @@ class Editor extends PureComponent {
return;
}
- clearEditor(editor);
+ const doc = editor.createDocument("", { name: "text" });
+ editor.replaceDocument(doc);
+ resetLineNumberFormat(editor);
}
showErrorMessage(msg) {
@@ -620,7 +656,37 @@ class Editor extends PureComponent {
return;
}
- showErrorMessage(editor, msg);
+ let error;
+ if (msg.includes("WebAssembly binary source is not available")) {
+ error = L10N.getStr("wasmIsNotAvailable");
+ } else {
+ error = L10N.getFormatStr("errorLoadingText3", msg);
+ }
+ if (!features.codemirrorNext) {
+ const doc = editor.createDocument(error, { name: "text" });
+ editor.replaceDocument(doc);
+ resetLineNumberFormat(editor);
+ } else {
+ editor.setText(error);
+ }
+ }
+
+ showLoadingMessage(editor) {
+ if (!features.codemirrorNext) {
+ // Create the "loading message" document only once
+ let doc = getDocument("loading");
+ if (!doc) {
+ doc = editor.createDocument(L10N.getStr("loadingText"), {
+ name: "text",
+ });
+ setDocument("loading", doc);
+ }
+ // `createDocument` won't be used right away in the editor, we still need to
+ // explicitely update it
+ editor.replaceDocument(doc);
+ } else {
+ editor.setText(L10N.getStr("loadingText"));
+ }
}
getInlineEditorStyles() {
diff --git a/devtools/client/debugger/src/components/Editor/tests/DebugLine.spec.js b/devtools/client/debugger/src/components/Editor/tests/DebugLine.spec.js
index 767dde9e6d..38bb318611 100644
--- a/devtools/client/debugger/src/components/Editor/tests/DebugLine.spec.js
+++ b/devtools/client/debugger/src/components/Editor/tests/DebugLine.spec.js
@@ -15,7 +15,7 @@ function createMockDocument(clear) {
addLineClass: jest.fn(),
removeLineClass: jest.fn(),
markText: jest.fn(() => ({ clear })),
- getLine: line => "",
+ getLine: () => "",
};
return doc;
diff --git a/devtools/client/debugger/src/components/Editor/tests/__snapshots__/ConditionalPanel.spec.js.snap b/devtools/client/debugger/src/components/Editor/tests/__snapshots__/ConditionalPanel.spec.js.snap
index 58e86f5009..86d848ec3e 100644
--- a/devtools/client/debugger/src/components/Editor/tests/__snapshots__/ConditionalPanel.spec.js.snap
+++ b/devtools/client/debugger/src/components/Editor/tests/__snapshots__/ConditionalPanel.spec.js.snap
@@ -22,6 +22,8 @@ exports[`ConditionalPanel should render at location of selected breakpoint 1`] =
"isOriginal": false,
"isPrettyPrinted": false,
"isWasm": false,
+ "longName": "url",
+ "shortName": "url",
"thread": "FakeThread",
"url": "url",
},
@@ -44,6 +46,8 @@ exports[`ConditionalPanel should render at location of selected breakpoint 1`] =
"isOriginal": false,
"isPrettyPrinted": false,
"isWasm": false,
+ "longName": "url",
+ "shortName": "url",
"thread": "FakeThread",
"url": "url",
},
@@ -217,6 +221,8 @@ exports[`ConditionalPanel should render at location of selected breakpoint 1`] =
"isOriginal": false,
"isPrettyPrinted": false,
"isWasm": false,
+ "longName": "url",
+ "shortName": "url",
"thread": "FakeThread",
"url": "url",
},
@@ -239,6 +245,8 @@ exports[`ConditionalPanel should render at location of selected breakpoint 1`] =
"isOriginal": false,
"isPrettyPrinted": false,
"isWasm": false,
+ "longName": "url",
+ "shortName": "url",
"thread": "FakeThread",
"url": "url",
}
@@ -268,6 +276,8 @@ exports[`ConditionalPanel should render with condition at selected breakpoint lo
"isOriginal": false,
"isPrettyPrinted": false,
"isWasm": false,
+ "longName": "url",
+ "shortName": "url",
"thread": "FakeThread",
"url": "url",
},
@@ -290,6 +300,8 @@ exports[`ConditionalPanel should render with condition at selected breakpoint lo
"isOriginal": false,
"isPrettyPrinted": false,
"isWasm": false,
+ "longName": "url",
+ "shortName": "url",
"thread": "FakeThread",
"url": "url",
},
@@ -467,6 +479,8 @@ exports[`ConditionalPanel should render with condition at selected breakpoint lo
"isOriginal": false,
"isPrettyPrinted": false,
"isWasm": false,
+ "longName": "url",
+ "shortName": "url",
"thread": "FakeThread",
"url": "url",
},
@@ -489,6 +503,8 @@ exports[`ConditionalPanel should render with condition at selected breakpoint lo
"isOriginal": false,
"isPrettyPrinted": false,
"isWasm": false,
+ "longName": "url",
+ "shortName": "url",
"thread": "FakeThread",
"url": "url",
}
@@ -518,6 +534,8 @@ exports[`ConditionalPanel should render with logpoint at selected breakpoint loc
"isOriginal": false,
"isPrettyPrinted": false,
"isWasm": false,
+ "longName": "url",
+ "shortName": "url",
"thread": "FakeThread",
"url": "url",
},
@@ -540,6 +558,8 @@ exports[`ConditionalPanel should render with logpoint at selected breakpoint loc
"isOriginal": false,
"isPrettyPrinted": false,
"isWasm": false,
+ "longName": "url",
+ "shortName": "url",
"thread": "FakeThread",
"url": "url",
},
@@ -717,6 +737,8 @@ exports[`ConditionalPanel should render with logpoint at selected breakpoint loc
"isOriginal": false,
"isPrettyPrinted": false,
"isWasm": false,
+ "longName": "url",
+ "shortName": "url",
"thread": "FakeThread",
"url": "url",
},
@@ -739,6 +761,8 @@ exports[`ConditionalPanel should render with logpoint at selected breakpoint loc
"isOriginal": false,
"isPrettyPrinted": false,
"isWasm": false,
+ "longName": "url",
+ "shortName": "url",
"thread": "FakeThread",
"url": "url",
}
diff --git a/devtools/client/debugger/src/components/Editor/tests/__snapshots__/Footer.spec.js.snap b/devtools/client/debugger/src/components/Editor/tests/__snapshots__/Footer.spec.js.snap
index a453b034ff..6c56ad33e7 100644
--- a/devtools/client/debugger/src/components/Editor/tests/__snapshots__/Footer.spec.js.snap
+++ b/devtools/client/debugger/src/components/Editor/tests/__snapshots__/Footer.spec.js.snap
@@ -31,6 +31,16 @@ exports[`SourceFooter Component default case should render 1`] = `
className="prettyPrint"
/>
</button>
+ <MenuButton
+ className="devtools-button debugger-source-map-button disabled not-mapped"
+ icon={true}
+ menuId="debugger-source-map-button"
+ menuOffset={-5}
+ menuPosition="bottom"
+ title="Source Map status"
+ >
+ <Component />
+ </MenuButton>
</div>
</div>
<div
@@ -77,6 +87,16 @@ exports[`SourceFooter Component move cursor should render new cursor position 1`
className="prettyPrint"
/>
</button>
+ <MenuButton
+ className="devtools-button debugger-source-map-button disabled not-mapped"
+ icon={true}
+ menuId="debugger-source-map-button"
+ menuOffset={-5}
+ menuPosition="bottom"
+ title="Source Map status"
+ >
+ <Component />
+ </MenuButton>
</div>
</div>
<div