p5.js-web-editor/client/modules/IDE/components/Editor.js

132 lines
8.5 KiB
JavaScript
Raw Normal View History

2016-06-27 22:03:22 +02:00
import React, { PropTypes } from 'react';
import EditorAccessibility from '../components/EditorAccessibility';
2016-06-24 00:29:55 +02:00
import CodeMirror from 'codemirror';
import 'codemirror/mode/javascript/javascript';
import 'codemirror/mode/css/css';
import 'codemirror/mode/htmlmixed/htmlmixed';
2016-06-24 00:29:55 +02:00
import 'codemirror/addon/selection/active-line';
2016-07-12 23:38:24 +02:00
import 'codemirror/addon/lint/lint';
import 'codemirror/addon/lint/javascript-lint';
import 'codemirror/addon/lint/css-lint';
import 'codemirror/addon/lint/html-lint';
import 'codemirror/addon/comment/comment';
import 'codemirror/keymap/sublime';
2016-08-05 22:58:59 +02:00
import 'codemirror/addon/search/jump-to-line';
2016-07-12 23:38:24 +02:00
import { JSHINT } from 'jshint';
window.JSHINT = JSHINT;
import { CSSLint } from 'csslint';
window.CSSLint = CSSLint;
import { HTMLHint } from 'htmlhint';
window.HTMLHint = HTMLHint;
2016-06-24 00:29:55 +02:00
2016-07-14 03:50:59 +02:00
import { debounce } from 'throttle-debounce';
2016-06-24 00:29:55 +02:00
class Editor extends React.Component {
componentDidMount() {
2016-08-12 02:26:14 +02:00
// TODO: replace with wav file and loader
2016-08-11 22:56:39 +02:00
this.beep = new Audio('data:audio/wav;base64,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qJrx4KOQjahZxWbcZgztj2c49nKmkId44S71j0c8eV9yDK6uPRzx5X18eDvjvQ6yKo9ZSS6l//8elePK/Lf//IInrOF/FvDoADYAGBMGb7FtErm5MXMlmPAJQVgWta7Zx2go+8xJ0UiCb8LHHdftWyLJE0QIAIsI+UbXu67dZMjmgDGCGl1H+vpF4NSDckSIkk7Vd+sxEhBQMRU8j/12UIRhzSaUdQ+rQU5kGeFxm+hb1oh6pWWmv3uvmReDl0UnvtapVaIzo1jZbf/pD6ElLqSX+rUmOQNpJFa/r+sa4e/pBlAABoAAAAA3CUgShLdGIxsY7AUABPRrgCABdDuQ5GC7DqPQCgbbJUAoRSUj+NIEig0YfyWUho1VBBBA//uQZB4ABZx5zfMakeAAAAmwAAAAF5F3P0w9GtAAACfAAAAAwLhMDmAYWMgVEG1U0FIGCBgXBXAtfMH10000EEEEEECUBYln03TTTdNBDZopopYvrTTdNa325mImNg3TTPV9q3pmY0xoO6bv3r00y+IDGid/9aaaZTGMuj9mpu9Mpio1dXrr5HERTZSmqU36A3CumzN/9Robv/Xx4v9ijkSRSNLQhAWumap82WRSBUqXStV/YcS+XVLnSS+WLDroqArFkMEsAS+eWmrUzrO0oEmE40RlMZ5+ODIkAyKAGUwZ3mVKmcamcJnMW26MRPgUw6j+LkhyHGVGYjSUUKNpuJUQoOIAyDvEyG8S5yfK6dhZc0Tx1KI/gviKL6qvvFs1+bWtaz58uUNnryq6kt5RzOCkPWlVqVX2a/EEBUdU1KrXLf40GoiiFXK///qpoiDXrOgqDR38JB0bw7SoL+ZB9o1RCkQjQ2CBYZKd/+VJxZRRZlqSkKiws0WFxUyCwsKiMy7hUVFhIaCrNQsKkTIsLivwKKigsj8XYlwt/WKi2N4d//uQRCSAAjURNIHpMZBGYiaQPSYyAAABLAAAAAAAACWAAAAApUF/Mg+0aohSIRobBAsMlO//Kk4soosy1JSFRYWaLC4qZBYWFRGZdwqKiwkNBVmoWFSJkWFxX4FFRQWR+LsS4W/rFRb/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////VEFHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU291bmRib3kuZGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMjAwNGh0dHA6Ly93d3cuc291bmRib3
2016-06-24 00:29:55 +02:00
this._cm = CodeMirror(this.refs.container, { // eslint-disable-line
theme: 'p5-widget',
value: this.props.file.content,
2016-06-24 00:29:55 +02:00
lineNumbers: true,
styleActiveLine: true,
2016-07-13 17:59:47 +02:00
inputStyle: 'contenteditable',
2016-07-12 21:58:11 +02:00
mode: 'javascript',
2016-07-12 23:38:24 +02:00
lineWrapping: true,
gutters: ['CodeMirror-lint-markers'],
2016-08-05 22:58:59 +02:00
keyMap: 'sublime',
lint: {
2016-08-08 19:49:45 +02:00
onUpdateLinting: debounce(2000, (annotations) => {
2016-08-11 19:24:02 +02:00
this.props.clearLintMessage();
2016-08-06 05:08:44 +02:00
annotations.forEach((x) => {
if (x.from.line > -1) {
2016-08-11 19:24:02 +02:00
this.props.updateLintMessage(x.severity, (x.from.line + 1), x.message);
2016-08-06 05:08:44 +02:00
}
});
2016-08-11 20:09:59 +02:00
if (this.props.lintMessages.length > 0 && this.props.lintWarning) {
2016-08-06 05:08:44 +02:00
this.beep.play();
}
2016-08-08 19:49:45 +02:00
})
2016-08-05 22:58:59 +02:00
}
2016-06-24 00:29:55 +02:00
});
2016-07-14 03:50:59 +02:00
this._cm.on('change', debounce(200, () => {
this.props.updateFileContent(this.props.file.name, this._cm.getValue());
2016-07-14 03:50:59 +02:00
}));
2016-08-05 22:58:59 +02:00
this._cm.on('keyup', () => {
2016-08-11 19:24:02 +02:00
this.props.updateLineNumber(parseInt((this._cm.getCursor().line) + 1, 10));
2016-08-05 22:58:59 +02:00
});
2016-07-14 03:50:59 +02:00
// this._cm.on('change', () => { // eslint-disable-line
// // this.props.updateFileContent('sketch.js', this._cm.getValue());
// throttle(1000, () => console.log('debounce is working!'));
// this.props.updateFileContent(this.props.file.name, this._cm.getValue());
// });
2016-06-24 00:29:55 +02:00
this._cm.getWrapperElement().style['font-size'] = `${this.props.fontSize}px`;
2016-07-11 05:11:06 +02:00
this._cm.setOption('indentWithTabs', this.props.isTabIndent);
2016-07-11 15:00:44 +02:00
this._cm.setOption('tabSize', this.props.indentationAmount);
2016-06-24 00:29:55 +02:00
}
componentDidUpdate(prevProps) {
if (this.props.file.content !== prevProps.file.content &&
this.props.file.content !== this._cm.getValue()) {
this._cm.setValue(this.props.file.content); // eslint-disable-line no-underscore-dangle
2016-06-24 00:29:55 +02:00
}
if (this.props.fontSize !== prevProps.fontSize) {
this._cm.getWrapperElement().style['font-size'] = `${this.props.fontSize}px`;
}
2016-07-06 17:27:39 +02:00
if (this.props.indentationAmount !== prevProps.indentationAmount) {
this._cm.setOption('tabSize', this.props.indentationAmount);
}
2016-07-11 05:11:06 +02:00
if (this.props.isTabIndent !== prevProps.isTabIndent) {
this._cm.setOption('indentWithTabs', this.props.isTabIndent);
}
if (this.props.file.name !== prevProps.name) {
if (this.props.file.name.match(/.+\.js$/)) {
this._cm.setOption('mode', 'javascript');
} else if (this.props.file.name.match(/.+\.css$/)) {
this._cm.setOption('mode', 'css');
} else if (this.props.file.name.match(/.+\.html$/)) {
this._cm.setOption('mode', 'htmlmixed');
}
}
2016-06-24 00:29:55 +02:00
}
componentWillUnmount() {
this._cm = null;
}
_cm: CodeMirror.Editor
render() {
2016-08-05 22:58:59 +02:00
return (
<div>
<div ref="container" className="editor-holder" tabIndex="0" title="code editor" role="main">
</div>
<EditorAccessibility
lintMessages={this.props.lintMessages}
lineNo={this.props.lineNo}
/>
2016-08-05 22:58:59 +02:00
</div>
);
2016-06-24 00:29:55 +02:00
}
}
2016-06-27 22:03:22 +02:00
Editor.propTypes = {
2016-08-11 20:09:59 +02:00
lintWarning: PropTypes.bool.isRequired,
lineNo: PropTypes.string.isRequired,
2016-08-11 19:24:02 +02:00
lintMessages: PropTypes.array.isRequired,
updateLintMessage: PropTypes.func.isRequired,
clearLintMessage: PropTypes.func.isRequired,
updateLineNumber: PropTypes.func.isRequired,
2016-07-11 04:52:48 +02:00
indentationAmount: PropTypes.number.isRequired,
2016-07-12 05:40:30 +02:00
isTabIndent: PropTypes.bool.isRequired,
updateFileContent: PropTypes.func.isRequired,
fontSize: PropTypes.number.isRequired,
file: PropTypes.shape({
name: PropTypes.string.isRequired,
content: PropTypes.string.isRequired
2016-07-12 05:40:30 +02:00
})
2016-06-27 22:03:22 +02:00
};
2016-06-24 00:29:55 +02:00
export default Editor;