189 lines
4.2 KiB
JavaScript
189 lines
4.2 KiB
JavaScript
import CodeMirror from "codemirror";
|
|
import React from "react";
|
|
|
|
class CodeMirrorPanel extends React.Component {
|
|
constructor() {
|
|
super();
|
|
this._textareaRef = React.createRef();
|
|
this._codeMirror = null;
|
|
this._cached = "";
|
|
this._overlay = null;
|
|
this.handleChange = this.handleChange.bind(this);
|
|
this.handleFocus = this.handleFocus.bind(this);
|
|
}
|
|
|
|
componentDidMount() {
|
|
const options = Object.assign({}, this.props);
|
|
delete options.ruler;
|
|
delete options.rulerColor;
|
|
delete options.value;
|
|
delete options.onChange;
|
|
|
|
options.rulers = [makeRuler(this.props)];
|
|
|
|
this._codeMirror = CodeMirror.fromTextArea(
|
|
this._textareaRef.current,
|
|
options
|
|
);
|
|
this._codeMirror.on("change", this.handleChange);
|
|
this._codeMirror.on("focus", this.handleFocus);
|
|
|
|
window.CodeMirror.keyMap.pcSublime["Ctrl-L"] = false;
|
|
window.CodeMirror.keyMap.sublime["Ctrl-L"] = false;
|
|
|
|
this.updateValue(this.props.value || "");
|
|
this.updateOverlay();
|
|
}
|
|
|
|
componentWillUnmount() {
|
|
this._codeMirror && this._codeMirror.toTextArea();
|
|
}
|
|
|
|
componentDidUpdate(prevProps) {
|
|
if (this.props.value !== this._cached) {
|
|
this.updateValue(this.props.value);
|
|
}
|
|
if (
|
|
this.props.overlayStart !== prevProps.overlayStart ||
|
|
this.props.overlayEnd !== prevProps.overlayEnd
|
|
) {
|
|
this.updateOverlay();
|
|
}
|
|
if (this.props.mode !== prevProps.mode) {
|
|
this._codeMirror.setOption("mode", this.props.mode);
|
|
}
|
|
if (this.props.ruler !== prevProps.ruler) {
|
|
this._codeMirror.setOption("rulers", [makeRuler(this.props)]);
|
|
}
|
|
}
|
|
|
|
updateValue(value) {
|
|
this._cached = value;
|
|
this._codeMirror.setValue(value);
|
|
}
|
|
|
|
updateOverlay() {
|
|
if (!this.props.readOnly) {
|
|
if (this._overlay) {
|
|
this._codeMirror.removeOverlay(this._overlay);
|
|
}
|
|
const [start, end] = getIndexPosition(this.props.value, [
|
|
this.props.overlayStart,
|
|
this.props.overlayEnd
|
|
]);
|
|
this._overlay = createOverlay(start, end);
|
|
this._codeMirror.addOverlay(this._overlay);
|
|
}
|
|
}
|
|
|
|
handleFocus(/* codeMirror, event */) {
|
|
if (this._codeMirror.getValue() === this.props.codeSample) {
|
|
this._codeMirror.execCommand("selectAll");
|
|
}
|
|
}
|
|
|
|
handleChange(doc, change) {
|
|
if (change.origin !== "setValue") {
|
|
this._cached = doc.getValue();
|
|
this.props.onChange(this._cached);
|
|
this.updateOverlay();
|
|
}
|
|
}
|
|
|
|
render() {
|
|
return (
|
|
<div className="editor input">
|
|
<textarea ref={this._textareaRef} />
|
|
</div>
|
|
);
|
|
}
|
|
}
|
|
|
|
function getIndexPosition(text, indexes) {
|
|
indexes = indexes.slice();
|
|
let line = 0;
|
|
let count = 0;
|
|
let lineStart = 0;
|
|
const result = [];
|
|
|
|
while (indexes.length) {
|
|
const index = indexes.shift();
|
|
|
|
while (count < index && count < text.length) {
|
|
if (text[count] === "\n") {
|
|
line++;
|
|
lineStart = count;
|
|
}
|
|
count++;
|
|
}
|
|
|
|
result.push({ line, pos: count - lineStart });
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
function createOverlay(start, end) {
|
|
return {
|
|
token(stream) {
|
|
const line = stream.lineOracle.line;
|
|
|
|
if (line < start.line || line > end.line) {
|
|
stream.skipToEnd();
|
|
} else if (line === start.line && stream.pos < start.pos) {
|
|
stream.pos = start.pos;
|
|
} else if (line === end.line) {
|
|
if (stream.pos < end.pos) {
|
|
stream.pos = end.pos;
|
|
return "searching";
|
|
}
|
|
stream.skipToEnd();
|
|
} else {
|
|
stream.skipToEnd();
|
|
return "searching";
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
function makeRuler(props) {
|
|
return { column: props.ruler, color: props.rulerColor };
|
|
}
|
|
|
|
export function InputPanel(props) {
|
|
return (
|
|
<CodeMirrorPanel
|
|
lineNumbers={true}
|
|
keyMap="sublime"
|
|
autoCloseBrackets={true}
|
|
matchBrackets={true}
|
|
showCursorWhenSelecting={true}
|
|
tabSize={4}
|
|
rulerColor="#eeeeee"
|
|
{...props}
|
|
/>
|
|
);
|
|
}
|
|
|
|
export function OutputPanel(props) {
|
|
return (
|
|
<CodeMirrorPanel
|
|
readOnly={true}
|
|
lineNumbers={true}
|
|
rulerColor="#444444"
|
|
{...props}
|
|
/>
|
|
);
|
|
}
|
|
|
|
export function DebugPanel({ value }) {
|
|
return (
|
|
<CodeMirrorPanel
|
|
readOnly={true}
|
|
lineNumbers={false}
|
|
mode="jsx"
|
|
value={value}
|
|
/>
|
|
);
|
|
}
|