2018-04-12 00:22:03 +03:00
|
|
|
import CodeMirror from "codemirror";
|
|
|
|
import React from "react";
|
|
|
|
|
|
|
|
class CodeMirrorPanel extends React.Component {
|
|
|
|
constructor() {
|
|
|
|
super();
|
|
|
|
this._textareaRef = React.createRef();
|
|
|
|
this._codeMirror = null;
|
2018-04-17 20:50:53 +03:00
|
|
|
this._cached = "";
|
2018-04-20 00:57:11 +03:00
|
|
|
this._overlay = null;
|
2018-04-12 00:22:03 +03:00
|
|
|
this.handleChange = this.handleChange.bind(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
componentDidMount() {
|
2018-04-19 20:46:29 +03:00
|
|
|
const options = Object.assign({}, this.props);
|
2018-04-20 00:57:11 +03:00
|
|
|
delete options.ruler;
|
2018-04-19 20:46:29 +03:00
|
|
|
delete options.rulerColor;
|
|
|
|
delete options.value;
|
|
|
|
delete options.onChange;
|
|
|
|
|
|
|
|
options.rulers = [makeRuler(this.props)];
|
|
|
|
|
2018-04-12 00:22:03 +03:00
|
|
|
this._codeMirror = CodeMirror.fromTextArea(
|
|
|
|
this._textareaRef.current,
|
2018-04-19 20:46:29 +03:00
|
|
|
options
|
2018-04-12 00:22:03 +03:00
|
|
|
);
|
|
|
|
this._codeMirror.on("change", this.handleChange);
|
2018-04-17 20:50:53 +03:00
|
|
|
|
2018-04-19 20:46:29 +03:00
|
|
|
this.updateValue(this.props.value || "");
|
2018-04-20 00:57:11 +03:00
|
|
|
this.updateOverlay();
|
2018-04-12 00:22:03 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
componentWillUnmount() {
|
|
|
|
this._codeMirror && this._codeMirror.toTextArea();
|
|
|
|
}
|
|
|
|
|
2018-04-12 02:55:37 +03:00
|
|
|
componentDidUpdate(prevProps) {
|
2018-04-19 20:46:29 +03:00
|
|
|
if (this.props.readOnly && this.props.value !== this._cached) {
|
2018-04-17 20:50:53 +03:00
|
|
|
this.updateValue(this.props.value);
|
2018-04-20 00:57:11 +03:00
|
|
|
this.updateOverlay();
|
|
|
|
}
|
|
|
|
if (
|
|
|
|
this.props.overlayStart !== prevProps.overlayStart ||
|
|
|
|
this.props.overlayEnd !== this.props.overlayEnd
|
|
|
|
) {
|
|
|
|
this.updateOverlay();
|
2018-04-12 00:22:03 +03:00
|
|
|
}
|
2018-04-12 02:55:37 +03:00
|
|
|
if (this.props.mode !== prevProps.mode) {
|
|
|
|
this._codeMirror.setOption("mode", this.props.mode);
|
|
|
|
}
|
2018-04-19 20:26:18 +03:00
|
|
|
if (this.props.placeholder !== prevProps.placeholder) {
|
|
|
|
this._codeMirror.setOption("placeholder", this.props.placeholder);
|
|
|
|
}
|
2018-04-20 00:57:11 +03:00
|
|
|
if (this.props.ruler !== prevProps.ruler) {
|
2018-04-19 20:46:29 +03:00
|
|
|
this._codeMirror.setOption("rulers", [makeRuler(this.props)]);
|
2018-04-12 02:55:37 +03:00
|
|
|
}
|
2018-04-12 00:22:03 +03:00
|
|
|
}
|
|
|
|
|
2018-04-17 20:50:53 +03:00
|
|
|
updateValue(value) {
|
|
|
|
this._cached = value;
|
|
|
|
this._codeMirror.setValue(value);
|
|
|
|
}
|
|
|
|
|
2018-04-20 00:57:11 +03:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-12 00:22:03 +03:00
|
|
|
handleChange(doc, change) {
|
|
|
|
if (change.origin !== "setValue") {
|
2018-04-17 20:50:53 +03:00
|
|
|
this._cached = doc.getValue();
|
|
|
|
this.props.onChange(this._cached);
|
2018-04-12 00:22:03 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
|
|
|
return (
|
|
|
|
<div className="editor input">
|
|
|
|
<textarea ref={this._textareaRef} />
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-20 00:57:11 +03:00
|
|
|
function getIndexPosition(text, indexes) {
|
|
|
|
indexes = indexes.slice().sort();
|
|
|
|
let line = 0;
|
|
|
|
let count = 0;
|
|
|
|
let lineStart = 0;
|
|
|
|
const result = [];
|
|
|
|
|
|
|
|
while (indexes.length) {
|
|
|
|
const index = indexes.shift();
|
|
|
|
|
|
|
|
while (count < index && count < text.length) {
|
|
|
|
count++;
|
|
|
|
if (text[count] === "\n") {
|
|
|
|
line++;
|
|
|
|
lineStart = 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";
|
|
|
|
} else {
|
|
|
|
stream.skipToEnd();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
stream.skipToEnd();
|
|
|
|
return "searching";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2018-04-19 20:46:29 +03:00
|
|
|
function makeRuler(props) {
|
2018-04-20 00:57:11 +03:00
|
|
|
return { column: props.ruler, color: props.rulerColor };
|
2018-04-19 20:46:29 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
export function InputPanel(props) {
|
2018-04-12 00:22:03 +03:00
|
|
|
return (
|
|
|
|
<CodeMirrorPanel
|
2018-04-19 20:46:29 +03:00
|
|
|
lineNumbers={true}
|
|
|
|
keyMap="sublime"
|
|
|
|
autoCloseBrackets={true}
|
|
|
|
matchBrackets={true}
|
|
|
|
showCursorWhenSelecting={true}
|
|
|
|
tabWidth={2}
|
2018-04-12 02:55:37 +03:00
|
|
|
rulerColor="#eeeeee"
|
2018-04-20 00:57:11 +03:00
|
|
|
rangeStart={5}
|
|
|
|
rangeEnd={25}
|
2018-04-19 20:46:29 +03:00
|
|
|
{...props}
|
2018-04-12 00:22:03 +03:00
|
|
|
/>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2018-04-19 20:46:29 +03:00
|
|
|
export function OutputPanel(props) {
|
2018-04-12 00:22:03 +03:00
|
|
|
return (
|
|
|
|
<CodeMirrorPanel
|
2018-04-19 20:46:29 +03:00
|
|
|
readOnly={true}
|
|
|
|
lineNumbers={true}
|
2018-04-12 02:55:37 +03:00
|
|
|
rulerColor="#444444"
|
2018-04-19 20:46:29 +03:00
|
|
|
{...props}
|
2018-04-12 00:22:03 +03:00
|
|
|
/>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
export function DebugPanel({ value }) {
|
|
|
|
return (
|
|
|
|
<CodeMirrorPanel
|
2018-04-19 20:46:29 +03:00
|
|
|
readOnly={true}
|
|
|
|
lineNumbers={false}
|
|
|
|
mode="jsx"
|
2018-04-12 00:22:03 +03:00
|
|
|
value={value}
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
}
|
2018-04-20 00:57:11 +03:00
|
|
|
|
|
|
|
function stringify(obj, replacer, spaces, cycleReplacer) {
|
|
|
|
return JSON.stringify(obj, serializer(replacer, cycleReplacer), spaces);
|
|
|
|
}
|
|
|
|
|
|
|
|
function serializer(replacer, cycleReplacer) {
|
|
|
|
var stack = [],
|
|
|
|
keys = [];
|
|
|
|
|
|
|
|
if (cycleReplacer == null)
|
|
|
|
cycleReplacer = function(key, value) {
|
|
|
|
if (stack[0] === value) return "[Circular ~]";
|
|
|
|
return (
|
|
|
|
"[Circular ~." + keys.slice(0, stack.indexOf(value)).join(".") + "]"
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
return function(key, value) {
|
|
|
|
if (stack.length > 0) {
|
|
|
|
var thisPos = stack.indexOf(this);
|
|
|
|
~thisPos ? stack.splice(thisPos + 1) : stack.push(this);
|
|
|
|
~thisPos ? keys.splice(thisPos, Infinity, key) : keys.push(key);
|
|
|
|
if (~stack.indexOf(value)) value = cycleReplacer.call(this, key, value);
|
|
|
|
} else stack.push(value);
|
|
|
|
|
|
|
|
return replacer == null ? value : replacer.call(this, key, value);
|
|
|
|
};
|
|
|
|
}
|