/* 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 . */
// @flow
import { Component } from "react";
import { toEditorLine, endOperation, startOperation } from "../../utils/editor";
import { getDocument, hasDocument } from "../../utils/editor/source-documents";
import { connect } from "../../utils/connect";
import {
getVisibleSelectedFrame,
getSelectedLocation,
getSelectedSourceWithContent,
getPauseCommand,
getCurrentThread
} from "../../selectors";
import type {
Frame,
SourceLocation,
SourceWithContent,
SourceDocuments
} from "../../types";
import type { Command } from "../../reducers/types";
type Props = {
pauseCommand: Command,
selectedFrame: Frame,
selectedLocation: SourceLocation,
selectedSourceWithContent: ?SourceWithContent
};
function isDebugLine(selectedFrame: Frame, selectedLocation: SourceLocation) {
if (!selectedFrame) {
return;
}
return (
selectedFrame.location.sourceId == selectedLocation.sourceId &&
selectedFrame.location.line == selectedLocation.line
);
}
function isDocumentReady(
selectedSourceWithContent: ?SourceWithContent,
selectedLocation
) {
return (
selectedLocation &&
selectedSourceWithContent &&
selectedSourceWithContent.content &&
hasDocument(selectedLocation.sourceId)
);
}
export class HighlightLine extends Component {
isStepping: boolean = false;
previousEditorLine: ?number = null;
shouldComponentUpdate(nextProps: Props) {
const { selectedLocation, selectedSourceWithContent } = nextProps;
return this.shouldSetHighlightLine(
selectedLocation,
selectedSourceWithContent
);
}
componentDidUpdate(prevProps: Props) {
this.completeHighlightLine(prevProps);
}
componentDidMount() {
this.completeHighlightLine(null);
}
shouldSetHighlightLine(
selectedLocation: SourceLocation,
selectedSourceWithContent: ?SourceWithContent
) {
const { sourceId, line } = selectedLocation;
const editorLine = toEditorLine(sourceId, line);
if (!isDocumentReady(selectedSourceWithContent, selectedLocation)) {
return false;
}
if (this.isStepping && editorLine === this.previousEditorLine) {
return false;
}
return true;
}
completeHighlightLine(prevProps: Props | null) {
const {
pauseCommand,
selectedLocation,
selectedFrame,
selectedSourceWithContent
} = this.props;
if (pauseCommand) {
this.isStepping = true;
}
startOperation();
if (prevProps) {
this.clearHighlightLine(
prevProps.selectedLocation,
prevProps.selectedSourceWithContent
);
}
this.setHighlightLine(
selectedLocation,
selectedFrame,
selectedSourceWithContent
);
endOperation();
}
setHighlightLine(
selectedLocation: SourceLocation,
selectedFrame: Frame,
selectedSourceWithContent: ?SourceWithContent
) {
const { sourceId, line } = selectedLocation;
if (
!this.shouldSetHighlightLine(selectedLocation, selectedSourceWithContent)
) {
return;
}
this.isStepping = false;
const editorLine = toEditorLine(sourceId, line);
this.previousEditorLine = editorLine;
if (!line || isDebugLine(selectedFrame, selectedLocation)) {
return;
}
const doc = getDocument(sourceId);
doc.addLineClass(editorLine, "line", "highlight-line");
this.resetHighlightLine(doc, editorLine);
}
resetHighlightLine(doc: SourceDocuments, editorLine: number) {
const editorWrapper: HTMLElement | null = document.querySelector(
".editor-wrapper"
);
if (editorWrapper === null) {
return;
}
const style = getComputedStyle(editorWrapper);
const durationString = style.getPropertyValue("--highlight-line-duration");
let duration = durationString.match(/\d+/);
duration = duration.length ? Number(duration[0]) : 0;
setTimeout(
() => doc && doc.removeLineClass(editorLine, "line", "highlight-line"),
duration
);
}
clearHighlightLine(
selectedLocation: SourceLocation,
selectedSourceWithContent: ?SourceWithContent
) {
if (!isDocumentReady(selectedSourceWithContent, selectedLocation)) {
return;
}
const { line, sourceId } = selectedLocation;
const editorLine = toEditorLine(sourceId, line);
const doc = getDocument(sourceId);
doc.removeLineClass(editorLine, "line", "highlight-line");
}
render() {
return null;
}
}
export default connect(state => ({
pauseCommand: getPauseCommand(state, getCurrentThread(state)),
selectedFrame: getVisibleSelectedFrame(state),
selectedLocation: getSelectedLocation(state),
selectedSourceWithContent: getSelectedSourceWithContent(state)
}))(HighlightLine);