diff options
Diffstat (limited to 'devtools/client/debugger/src/components/Editor/DebugLine.js')
-rw-r--r-- | devtools/client/debugger/src/components/Editor/DebugLine.js | 138 |
1 files changed, 138 insertions, 0 deletions
diff --git a/devtools/client/debugger/src/components/Editor/DebugLine.js b/devtools/client/debugger/src/components/Editor/DebugLine.js new file mode 100644 index 0000000000..95cfc5a94d --- /dev/null +++ b/devtools/client/debugger/src/components/Editor/DebugLine.js @@ -0,0 +1,138 @@ +/* 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 { PureComponent } from "react"; +import PropTypes from "prop-types"; +import { + toEditorPosition, + getDocument, + hasDocument, + startOperation, + endOperation, + getTokenEnd, +} from "../../utils/editor"; +import { isException } from "../../utils/pause"; +import { getIndentation } from "../../utils/indentation"; +import { connect } from "../../utils/connect"; +import { + getVisibleSelectedFrame, + getPauseReason, + getSourceTextContent, + getCurrentThread, +} from "../../selectors"; + +export class DebugLine extends PureComponent { + debugExpression; + + static get propTypes() { + return { + location: PropTypes.object, + why: PropTypes.object, + }; + } + + componentDidMount() { + const { why, location } = this.props; + this.setDebugLine(why, location); + } + + componentWillUnmount() { + const { why, location } = this.props; + this.clearDebugLine(why, location); + } + + componentDidUpdate(prevProps) { + const { why, location } = this.props; + + startOperation(); + this.clearDebugLine(prevProps.why, prevProps.location); + this.setDebugLine(why, location); + endOperation(); + } + + setDebugLine(why, location) { + if (!location) { + return; + } + const { sourceId } = location; + const doc = getDocument(sourceId); + + let { line, column } = toEditorPosition(location); + let { markTextClass, lineClass } = this.getTextClasses(why); + doc.addLineClass(line, "wrap", lineClass); + + const lineText = doc.getLine(line); + column = Math.max(column, getIndentation(lineText)); + + // If component updates because user clicks on + // another source tab, codeMirror will be null. + const columnEnd = doc.cm ? getTokenEnd(doc.cm, line, column) : null; + + if (columnEnd === null) { + markTextClass += " to-line-end"; + } + + this.debugExpression = doc.markText( + { ch: column, line }, + { ch: columnEnd, line }, + { className: markTextClass } + ); + } + + clearDebugLine(why, location) { + // Avoid clearing the line if we didn't set a debug line before, + // or, if the document is no longer available + if (!location || !hasDocument(location.sourceId)) { + return; + } + + if (this.debugExpression) { + this.debugExpression.clear(); + } + + const { line } = toEditorPosition(location); + const doc = getDocument(location.sourceId); + const { lineClass } = this.getTextClasses(why); + doc.removeLineClass(line, "wrap", lineClass); + } + + getTextClasses(why) { + if (why && isException(why)) { + return { + markTextClass: "debug-expression-error", + lineClass: "new-debug-line-error", + }; + } + + return { markTextClass: "debug-expression", lineClass: "new-debug-line" }; + } + + render() { + return null; + } +} + +function isDocumentReady(location, sourceTextContent) { + return location && sourceTextContent && hasDocument(location.sourceId); +} + +const mapStateToProps = state => { + // Avoid unecessary intermediate updates when there is no location + // or the source text content isn't yet fully loaded + const frame = getVisibleSelectedFrame(state); + const location = frame?.location; + if (!location) { + return {}; + } + const sourceTextContent = getSourceTextContent(state, location); + if (!isDocumentReady(location, sourceTextContent)) { + return {}; + } + return { + location, + why: getPauseReason(state, getCurrentThread(state)), + }; +}; + +export default connect(mapStateToProps)(DebugLine); |