forked from mirrors/gecko-dev
In `getTokenLocation`, the call to `coordsChar` needed an extra parameter so the coordinates are relative to the top left corner fo the currently visible window. We were passing the center of the bounding client rect to `getTokenLocation`, but a token could wrap and have it's bounding rect center not being on the token. To fix this we're passing the center of the first box quads of the token. The inline preview widgets were causing some troubles on CodeMirror internal state too. Since we're using React to populate the bookmark we set on the line, and given that React.render is asynchronous, we were setting an empty element as a bookmark on the editor, which React populates later. But CodeMirror probably update its internal state when the bookmark is added, so that was causing wrong computation later. To avoid that, we only set the bookmark once React actually rendered the element in the DOM. Finally, while codeMirror already listen for window resize to update its internal state, the Editor can be resized without the window being resized, when primary/secondary panels are resized. In such case, we manually trigger a codeMirror refresh. We add a test to cover this, and also modify test helpers so we would query the DOM instead of relying on CodeMirror methods. Differential Revision: https://phabricator.services.mozilla.com/D190272
756 lines
20 KiB
JavaScript
756 lines
20 KiB
JavaScript
/* 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 PropTypes from "prop-types";
|
|
import React, { PureComponent } from "react";
|
|
import { div } from "react-dom-factories";
|
|
import { bindActionCreators } from "redux";
|
|
import ReactDOM from "react-dom";
|
|
import { connect } from "../../utils/connect";
|
|
|
|
import { getLineText, isLineBlackboxed } from "./../../utils/source";
|
|
import { createLocation } from "./../../utils/location";
|
|
import { getIndentation } from "../../utils/indentation";
|
|
|
|
import {
|
|
getActiveSearch,
|
|
getSelectedLocation,
|
|
getSelectedSource,
|
|
getSelectedSourceTextContent,
|
|
getSelectedBreakableLines,
|
|
getConditionalPanelLocation,
|
|
getSymbols,
|
|
getIsCurrentThreadPaused,
|
|
getSkipPausing,
|
|
getInlinePreview,
|
|
getBlackBoxRanges,
|
|
isSourceBlackBoxed,
|
|
getHighlightedLineRangeForSelectedSource,
|
|
isSourceMapIgnoreListEnabled,
|
|
isSourceOnSourceMapIgnoreList,
|
|
} from "../../selectors";
|
|
|
|
// Redux actions
|
|
import actions from "../../actions";
|
|
|
|
import SearchInFileBar from "./SearchInFileBar";
|
|
import HighlightLines from "./HighlightLines";
|
|
import Preview from "./Preview";
|
|
import Breakpoints from "./Breakpoints";
|
|
import ColumnBreakpoints from "./ColumnBreakpoints";
|
|
import DebugLine from "./DebugLine";
|
|
import HighlightLine from "./HighlightLine";
|
|
import EmptyLines from "./EmptyLines";
|
|
import ConditionalPanel from "./ConditionalPanel";
|
|
import InlinePreviews from "./InlinePreviews";
|
|
import Exceptions from "./Exceptions";
|
|
import BlackboxLines from "./BlackboxLines";
|
|
|
|
import {
|
|
showSourceText,
|
|
showLoading,
|
|
showErrorMessage,
|
|
getEditor,
|
|
clearEditor,
|
|
getCursorLine,
|
|
getCursorColumn,
|
|
lineAtHeight,
|
|
toSourceLine,
|
|
getDocument,
|
|
scrollToPosition,
|
|
toEditorPosition,
|
|
getSourceLocationFromMouseEvent,
|
|
hasDocument,
|
|
onMouseOver,
|
|
startOperation,
|
|
endOperation,
|
|
} from "../../utils/editor";
|
|
|
|
import { resizeToggleButton, resizeBreakpointGutter } from "../../utils/ui";
|
|
|
|
const { debounce } = require("devtools/shared/debounce");
|
|
const classnames = require("devtools/client/shared/classnames.js");
|
|
|
|
const { appinfo } = Services;
|
|
const isMacOS = appinfo.OS === "Darwin";
|
|
|
|
function isSecondary(ev) {
|
|
return isMacOS && ev.ctrlKey && ev.button === 0;
|
|
}
|
|
|
|
function isCmd(ev) {
|
|
return isMacOS ? ev.metaKey : ev.ctrlKey;
|
|
}
|
|
|
|
import "./Editor.css";
|
|
import "./Breakpoints.css";
|
|
import "./InlinePreview.css";
|
|
|
|
const cssVars = {
|
|
searchbarHeight: "var(--editor-searchbar-height)",
|
|
};
|
|
|
|
class Editor extends PureComponent {
|
|
static get propTypes() {
|
|
return {
|
|
selectedSource: PropTypes.object,
|
|
selectedSourceTextContent: PropTypes.object,
|
|
selectedSourceIsBlackBoxed: PropTypes.bool,
|
|
closeTab: PropTypes.func.isRequired,
|
|
toggleBreakpointAtLine: PropTypes.func.isRequired,
|
|
conditionalPanelLocation: PropTypes.object,
|
|
closeConditionalPanel: PropTypes.func.isRequired,
|
|
openConditionalPanel: PropTypes.func.isRequired,
|
|
updateViewport: PropTypes.func.isRequired,
|
|
isPaused: PropTypes.bool.isRequired,
|
|
addBreakpointAtLine: PropTypes.func.isRequired,
|
|
continueToHere: PropTypes.func.isRequired,
|
|
updateCursorPosition: PropTypes.func.isRequired,
|
|
jumpToMappedLocation: PropTypes.func.isRequired,
|
|
selectedLocation: PropTypes.object,
|
|
symbols: PropTypes.object,
|
|
startPanelSize: PropTypes.number.isRequired,
|
|
endPanelSize: PropTypes.number.isRequired,
|
|
searchInFileEnabled: PropTypes.bool.isRequired,
|
|
inlinePreviewEnabled: PropTypes.bool.isRequired,
|
|
skipPausing: PropTypes.bool.isRequired,
|
|
blackboxedRanges: PropTypes.object.isRequired,
|
|
breakableLines: PropTypes.object.isRequired,
|
|
highlightedLineRange: PropTypes.object,
|
|
isSourceOnIgnoreList: PropTypes.bool,
|
|
};
|
|
}
|
|
|
|
$editorWrapper;
|
|
constructor(props) {
|
|
super(props);
|
|
|
|
this.state = {
|
|
editor: null,
|
|
};
|
|
}
|
|
|
|
// FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=1774507
|
|
UNSAFE_componentWillReceiveProps(nextProps) {
|
|
let { editor } = this.state;
|
|
|
|
if (!editor && nextProps.selectedSource) {
|
|
editor = this.setupEditor();
|
|
}
|
|
|
|
const shouldUpdateText =
|
|
nextProps.selectedSource !== this.props.selectedSource ||
|
|
nextProps.selectedSourceTextContent?.value !==
|
|
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 (shouldUpdateText || shouldUpdateSize || shouldScroll) {
|
|
startOperation();
|
|
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();
|
|
|
|
// disables the default search shortcuts
|
|
editor._initShortcuts = () => {};
|
|
|
|
const node = ReactDOM.findDOMNode(this);
|
|
if (node instanceof HTMLElement) {
|
|
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);
|
|
|
|
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");
|
|
});
|
|
}
|
|
};
|
|
|
|
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();
|
|
this.setState({ editor });
|
|
return editor;
|
|
}
|
|
|
|
componentDidMount() {
|
|
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);
|
|
}
|
|
|
|
onCloseShortcutPress = e => {
|
|
const { selectedSource } = this.props;
|
|
if (selectedSource) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
this.props.closeTab(selectedSource, "shortcut");
|
|
}
|
|
};
|
|
|
|
componentWillUnmount() {
|
|
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"));
|
|
|
|
if (this.abortController) {
|
|
this.abortController.abort();
|
|
this.abortController = null;
|
|
}
|
|
}
|
|
|
|
getCurrentLine() {
|
|
const { codeMirror } = this.state.editor;
|
|
const { selectedSource } = this.props;
|
|
if (!selectedSource) {
|
|
return null;
|
|
}
|
|
|
|
const line = getCursorLine(codeMirror);
|
|
return toSourceLine(selectedSource.id, line);
|
|
}
|
|
|
|
onToggleBreakpoint = e => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
|
|
const line = this.getCurrentLine();
|
|
if (typeof line !== "number") {
|
|
return;
|
|
}
|
|
|
|
this.props.toggleBreakpointAtLine(line);
|
|
};
|
|
|
|
onToggleConditionalPanel = e => {
|
|
e.stopPropagation();
|
|
e.preventDefault();
|
|
|
|
const {
|
|
conditionalPanelLocation,
|
|
closeConditionalPanel,
|
|
openConditionalPanel,
|
|
selectedSource,
|
|
} = this.props;
|
|
|
|
const line = this.getCurrentLine();
|
|
|
|
const { codeMirror } = this.state.editor;
|
|
// add one to column for correct position in editor.
|
|
const column = getCursorColumn(codeMirror) + 1;
|
|
|
|
if (conditionalPanelLocation) {
|
|
return closeConditionalPanel();
|
|
}
|
|
|
|
if (!selectedSource || typeof line !== "number") {
|
|
return null;
|
|
}
|
|
|
|
return openConditionalPanel(
|
|
createLocation({
|
|
line,
|
|
column,
|
|
source: selectedSource,
|
|
}),
|
|
false
|
|
);
|
|
};
|
|
|
|
onEditorScroll = debounce(this.props.updateViewport, 75);
|
|
|
|
onKeyDown(e) {
|
|
const { codeMirror } = this.state.editor;
|
|
const { key, target } = e;
|
|
const codeWrapper = codeMirror.getWrapperElement();
|
|
const textArea = codeWrapper.querySelector("textArea");
|
|
|
|
if (key === "Escape" && target == textArea) {
|
|
e.stopPropagation();
|
|
e.preventDefault();
|
|
codeWrapper.focus();
|
|
} else if (key === "Enter" && target == codeWrapper) {
|
|
e.preventDefault();
|
|
// Focus into editor's text area
|
|
textArea.focus();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* The default Esc command is overridden in the CodeMirror keymap to allow
|
|
* the Esc keypress event to be catched by the toolbox and trigger the
|
|
* split console. Restore it here, but preventDefault if and only if there
|
|
* is a multiselection.
|
|
*/
|
|
onEscape = e => {
|
|
if (!this.state.editor) {
|
|
return;
|
|
}
|
|
|
|
const { codeMirror } = this.state.editor;
|
|
if (codeMirror.listSelections().length > 1) {
|
|
codeMirror.execCommand("singleSelection");
|
|
e.preventDefault();
|
|
}
|
|
};
|
|
|
|
openMenu(event) {
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
|
|
const {
|
|
selectedSource,
|
|
selectedSourceTextContent,
|
|
conditionalPanelLocation,
|
|
closeConditionalPanel,
|
|
} = this.props;
|
|
|
|
const { editor } = this.state;
|
|
|
|
if (!selectedSource || !editor) {
|
|
return;
|
|
}
|
|
|
|
// only allow one conditionalPanel location.
|
|
if (conditionalPanelLocation) {
|
|
closeConditionalPanel();
|
|
}
|
|
|
|
const target = event.target;
|
|
const { id: sourceId } = selectedSource;
|
|
const line = lineAtHeight(editor, sourceId, event);
|
|
|
|
if (typeof line != "number") {
|
|
return;
|
|
}
|
|
|
|
if (target.classList.contains("CodeMirror-linenumber")) {
|
|
const location = createLocation({
|
|
line,
|
|
column: undefined,
|
|
source: selectedSource,
|
|
});
|
|
|
|
const lineText = getLineText(
|
|
sourceId,
|
|
selectedSourceTextContent,
|
|
line
|
|
).trim();
|
|
|
|
this.props.showEditorGutterContextMenu(event, editor, location, lineText);
|
|
return;
|
|
}
|
|
|
|
if (target.getAttribute("id") === "columnmarker") {
|
|
return;
|
|
}
|
|
|
|
const location = getSourceLocationFromMouseEvent(
|
|
editor,
|
|
selectedSource,
|
|
event
|
|
);
|
|
|
|
this.props.showEditorContextMenu(event, editor, location);
|
|
}
|
|
|
|
onGutterClick = (cm, line, gutter, ev) => {
|
|
const {
|
|
selectedSource,
|
|
conditionalPanelLocation,
|
|
closeConditionalPanel,
|
|
addBreakpointAtLine,
|
|
continueToHere,
|
|
breakableLines,
|
|
blackboxedRanges,
|
|
isSourceOnIgnoreList,
|
|
} = this.props;
|
|
|
|
// ignore right clicks in the gutter
|
|
if (isSecondary(ev) || ev.button === 2 || !selectedSource) {
|
|
return;
|
|
}
|
|
|
|
if (conditionalPanelLocation) {
|
|
closeConditionalPanel();
|
|
return;
|
|
}
|
|
|
|
if (gutter === "CodeMirror-foldgutter") {
|
|
return;
|
|
}
|
|
|
|
const sourceLine = toSourceLine(selectedSource.id, line);
|
|
if (typeof sourceLine !== "number") {
|
|
return;
|
|
}
|
|
|
|
// ignore clicks on a non-breakable line
|
|
if (!breakableLines.has(sourceLine)) {
|
|
return;
|
|
}
|
|
|
|
if (isCmd(ev)) {
|
|
continueToHere(
|
|
createLocation({
|
|
line: sourceLine,
|
|
column: undefined,
|
|
source: selectedSource,
|
|
})
|
|
);
|
|
return;
|
|
}
|
|
|
|
addBreakpointAtLine(
|
|
sourceLine,
|
|
ev.altKey,
|
|
ev.shiftKey ||
|
|
isLineBlackboxed(
|
|
blackboxedRanges[selectedSource.url],
|
|
sourceLine,
|
|
isSourceOnIgnoreList
|
|
)
|
|
);
|
|
};
|
|
|
|
onGutterContextMenu = event => {
|
|
this.openMenu(event);
|
|
};
|
|
|
|
onClick(e) {
|
|
const { selectedSource, updateCursorPosition, jumpToMappedLocation } =
|
|
this.props;
|
|
|
|
if (selectedSource) {
|
|
const sourceLocation = getSourceLocationFromMouseEvent(
|
|
this.state.editor,
|
|
selectedSource,
|
|
e
|
|
);
|
|
|
|
if (e.metaKey && e.altKey) {
|
|
jumpToMappedLocation(sourceLocation);
|
|
}
|
|
|
|
updateCursorPosition(sourceLocation);
|
|
}
|
|
}
|
|
|
|
shouldScrollToLocation(nextProps, editor) {
|
|
if (
|
|
!nextProps.selectedLocation?.line ||
|
|
!nextProps.selectedSourceTextContent
|
|
) {
|
|
return false;
|
|
}
|
|
|
|
const { selectedLocation, selectedSourceTextContent } = this.props;
|
|
const contentChanged =
|
|
!selectedSourceTextContent?.value &&
|
|
nextProps.selectedSourceTextContent?.value;
|
|
const locationChanged = selectedLocation !== nextProps.selectedLocation;
|
|
const symbolsChanged = nextProps.symbols != this.props.symbols;
|
|
|
|
return contentChanged || locationChanged || symbolsChanged;
|
|
}
|
|
|
|
scrollToLocation(nextProps, editor) {
|
|
const { selectedLocation, selectedSource } = nextProps;
|
|
|
|
let { line, column } = toEditorPosition(selectedLocation);
|
|
|
|
if (selectedSource && hasDocument(selectedSource.id)) {
|
|
const doc = getDocument(selectedSource.id);
|
|
const lineText = doc.getLine(line);
|
|
column = Math.max(column, getIndentation(lineText));
|
|
}
|
|
|
|
scrollToPosition(editor.codeMirror, line, column);
|
|
}
|
|
|
|
setText(props, editor) {
|
|
const { selectedSource, selectedSourceTextContent, symbols } = props;
|
|
|
|
if (!editor) {
|
|
return;
|
|
}
|
|
|
|
// check if we previously had a selected source
|
|
if (!selectedSource) {
|
|
this.clearEditor();
|
|
return;
|
|
}
|
|
|
|
if (!selectedSourceTextContent?.value) {
|
|
showLoading(editor);
|
|
return;
|
|
}
|
|
|
|
if (selectedSourceTextContent.state === "rejected") {
|
|
let { value } = selectedSourceTextContent;
|
|
if (typeof value !== "string") {
|
|
value = "Unexpected source error";
|
|
}
|
|
|
|
this.showErrorMessage(value);
|
|
return;
|
|
}
|
|
|
|
showSourceText(editor, selectedSource, selectedSourceTextContent, symbols);
|
|
}
|
|
|
|
clearEditor() {
|
|
const { editor } = this.state;
|
|
if (!editor) {
|
|
return;
|
|
}
|
|
|
|
clearEditor(editor);
|
|
}
|
|
|
|
showErrorMessage(msg) {
|
|
const { editor } = this.state;
|
|
if (!editor) {
|
|
return;
|
|
}
|
|
|
|
showErrorMessage(editor, msg);
|
|
}
|
|
|
|
getInlineEditorStyles() {
|
|
const { searchInFileEnabled } = this.props;
|
|
|
|
if (searchInFileEnabled) {
|
|
return {
|
|
height: `calc(100% - ${cssVars.searchbarHeight})`,
|
|
};
|
|
}
|
|
|
|
return {
|
|
height: "100%",
|
|
};
|
|
}
|
|
|
|
renderItems() {
|
|
const {
|
|
selectedSource,
|
|
conditionalPanelLocation,
|
|
isPaused,
|
|
inlinePreviewEnabled,
|
|
highlightedLineRange,
|
|
blackboxedRanges,
|
|
isSourceOnIgnoreList,
|
|
selectedSourceIsBlackBoxed,
|
|
} = this.props;
|
|
const { editor } = this.state;
|
|
|
|
if (!selectedSource || !editor || !getDocument(selectedSource.id)) {
|
|
return null;
|
|
}
|
|
return div(
|
|
null,
|
|
React.createElement(DebugLine, null),
|
|
React.createElement(HighlightLine, null),
|
|
React.createElement(EmptyLines, {
|
|
editor,
|
|
}),
|
|
React.createElement(Breakpoints, {
|
|
editor,
|
|
}),
|
|
React.createElement(Preview, {
|
|
editor,
|
|
editorRef: this.$editorWrapper,
|
|
}),
|
|
highlightedLineRange
|
|
? React.createElement(HighlightLines, {
|
|
editor,
|
|
range: highlightedLineRange,
|
|
})
|
|
: null,
|
|
isSourceOnIgnoreList || selectedSourceIsBlackBoxed
|
|
? React.createElement(BlackboxLines, {
|
|
editor,
|
|
selectedSource,
|
|
isSourceOnIgnoreList,
|
|
blackboxedRangesForSelectedSource:
|
|
blackboxedRanges[selectedSource.url],
|
|
})
|
|
: null,
|
|
React.createElement(Exceptions, null),
|
|
conditionalPanelLocation
|
|
? React.createElement(ConditionalPanel, {
|
|
editor,
|
|
})
|
|
: null,
|
|
React.createElement(ColumnBreakpoints, {
|
|
editor,
|
|
}),
|
|
isPaused && inlinePreviewEnabled
|
|
? React.createElement(InlinePreviews, {
|
|
editor,
|
|
selectedSource,
|
|
})
|
|
: null
|
|
);
|
|
}
|
|
|
|
renderSearchInFileBar() {
|
|
if (!this.props.selectedSource) {
|
|
return null;
|
|
}
|
|
return React.createElement(SearchInFileBar, {
|
|
editor: this.state.editor,
|
|
});
|
|
}
|
|
|
|
render() {
|
|
const { selectedSourceIsBlackBoxed, skipPausing } = this.props;
|
|
return div(
|
|
{
|
|
className: classnames("editor-wrapper", {
|
|
blackboxed: selectedSourceIsBlackBoxed,
|
|
"skip-pausing": skipPausing,
|
|
}),
|
|
ref: c => (this.$editorWrapper = c),
|
|
},
|
|
div({
|
|
className: "editor-mount devtools-monospace",
|
|
style: this.getInlineEditorStyles(),
|
|
}),
|
|
this.renderSearchInFileBar(),
|
|
this.renderItems()
|
|
);
|
|
}
|
|
}
|
|
|
|
Editor.contextTypes = {
|
|
shortcuts: PropTypes.object,
|
|
};
|
|
|
|
const mapStateToProps = state => {
|
|
const selectedSource = getSelectedSource(state);
|
|
const selectedLocation = getSelectedLocation(state);
|
|
|
|
return {
|
|
selectedLocation,
|
|
selectedSource,
|
|
selectedSourceTextContent: getSelectedSourceTextContent(state),
|
|
selectedSourceIsBlackBoxed: selectedSource
|
|
? isSourceBlackBoxed(state, selectedSource)
|
|
: null,
|
|
isSourceOnIgnoreList:
|
|
isSourceMapIgnoreListEnabled(state) &&
|
|
isSourceOnSourceMapIgnoreList(state, selectedSource),
|
|
searchInFileEnabled: getActiveSearch(state) === "file",
|
|
conditionalPanelLocation: getConditionalPanelLocation(state),
|
|
symbols: getSymbols(state, selectedLocation),
|
|
isPaused: getIsCurrentThreadPaused(state),
|
|
skipPausing: getSkipPausing(state),
|
|
inlinePreviewEnabled: getInlinePreview(state),
|
|
blackboxedRanges: getBlackBoxRanges(state),
|
|
breakableLines: getSelectedBreakableLines(state),
|
|
highlightedLineRange: getHighlightedLineRangeForSelectedSource(state),
|
|
};
|
|
};
|
|
|
|
const mapDispatchToProps = dispatch => ({
|
|
...bindActionCreators(
|
|
{
|
|
openConditionalPanel: actions.openConditionalPanel,
|
|
closeConditionalPanel: actions.closeConditionalPanel,
|
|
continueToHere: actions.continueToHere,
|
|
toggleBreakpointAtLine: actions.toggleBreakpointAtLine,
|
|
addBreakpointAtLine: actions.addBreakpointAtLine,
|
|
jumpToMappedLocation: actions.jumpToMappedLocation,
|
|
updateViewport: actions.updateViewport,
|
|
updateCursorPosition: actions.updateCursorPosition,
|
|
closeTab: actions.closeTab,
|
|
showEditorContextMenu: actions.showEditorContextMenu,
|
|
showEditorGutterContextMenu: actions.showEditorGutterContextMenu,
|
|
},
|
|
dispatch
|
|
),
|
|
});
|
|
|
|
export default connect(mapStateToProps, mapDispatchToProps)(Editor);
|