2016-06-27 20:03:22 +00:00
import React , { PropTypes } from 'react' ;
2016-06-23 22:29:55 +00:00
import CodeMirror from 'codemirror' ;
import 'codemirror/mode/javascript/javascript' ;
2016-07-13 23:52:50 +00:00
import 'codemirror/mode/css/css' ;
import 'codemirror/mode/htmlmixed/htmlmixed' ;
2016-06-23 22:29:55 +00:00
import 'codemirror/addon/selection/active-line' ;
2016-07-12 21:38:24 +00:00
import 'codemirror/addon/lint/lint' ;
import 'codemirror/addon/lint/javascript-lint' ;
2016-07-13 23:52:50 +00:00
import 'codemirror/addon/lint/css-lint' ;
import 'codemirror/addon/lint/html-lint' ;
2016-07-21 18:33:23 +00:00
import 'codemirror/addon/comment/comment' ;
import 'codemirror/keymap/sublime' ;
2016-08-05 20:58:59 +00:00
import 'codemirror/addon/search/jump-to-line' ;
2016-07-12 21:38:24 +00:00
import { JSHINT } from 'jshint' ;
window . JSHINT = JSHINT ;
2016-07-13 23:52:50 +00:00
import { CSSLint } from 'csslint' ;
window . CSSLint = CSSLint ;
import { HTMLHint } from 'htmlhint' ;
window . HTMLHint = HTMLHint ;
2016-06-23 22:29:55 +00:00
2016-07-14 01:50:59 +00:00
import { debounce } from 'throttle-debounce' ;
2016-06-23 22:29:55 +00:00
class Editor extends React . Component {
componentDidMount ( ) {
2016-08-06 03:08:44 +00: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-23 22:29:55 +00:00
this . _cm = CodeMirror ( this . refs . container , { // eslint-disable-line
theme : 'p5-widget' ,
2016-07-08 19:58:49 +00:00
value : this . props . file . content ,
2016-06-23 22:29:55 +00:00
lineNumbers : true ,
styleActiveLine : true ,
2016-07-13 15:59:47 +00:00
inputStyle : 'contenteditable' ,
2016-07-12 19:58:11 +00:00
mode : 'javascript' ,
2016-07-12 21:38:24 +00:00
lineWrapping : true ,
gutters : [ 'CodeMirror-lint-markers' ] ,
2016-08-05 20:58:59 +00:00
keyMap : 'sublime' ,
lint : {
2016-08-08 17:49:45 +00:00
onUpdateLinting : debounce ( 2000 , ( annotations ) => {
2016-08-06 03:08:44 +00:00
let isVisible = false ;
document . getElementById ( 'editor-lintmessages' ) . innerHTML = '' ;
annotations . forEach ( ( x ) => {
if ( x . from . line > - 1 ) {
document . getElementById ( 'editor-lintmessages' ) . innerHTML += ( x . severity + ' in line number ' + ( x . from . line + 1 ) + ' : ' + x . message ) ; // eslint-disable-line
isVisible = true ;
}
} ) ;
2016-08-10 15:13:17 +00:00
if ( isVisible && this . props . enableBeep ) {
2016-08-06 03:08:44 +00:00
this . beep . play ( ) ;
}
2016-08-08 17:49:45 +00:00
} )
2016-08-05 20:58:59 +00:00
}
2016-06-23 22:29:55 +00:00
} ) ;
2016-07-14 01:50:59 +00:00
this . _cm . on ( 'change' , debounce ( 200 , ( ) => {
2016-07-08 19:58:49 +00:00
this . props . updateFileContent ( this . props . file . name , this . _cm . getValue ( ) ) ;
2016-07-14 01:50:59 +00:00
} ) ) ;
2016-08-05 20:58:59 +00:00
this . _cm . on ( 'keyup' , ( ) => {
2016-08-06 03:08:44 +00:00
document . getElementById ( 'editor-linenumber' ) . innerHTML = 'line ' + parseInt ( ( this . _cm . getCursor ( ) . line ) + 1 , 10 ) ;
2016-08-05 20:58:59 +00:00
} ) ;
2016-07-14 01:50:59 +00: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-23 22:29:55 +00:00
this . _cm . getWrapperElement ( ) . style [ 'font-size' ] = ` ${ this . props . fontSize } px ` ;
2016-07-11 03:11:06 +00:00
this . _cm . setOption ( 'indentWithTabs' , this . props . isTabIndent ) ;
2016-07-11 13:00:44 +00:00
this . _cm . setOption ( 'tabSize' , this . props . indentationAmount ) ;
2016-06-23 22:29:55 +00:00
}
componentDidUpdate ( prevProps ) {
2016-08-05 20:58:59 +00:00
const annotations = this . _cm . state . lint . marked ;
annotations . forEach ( function ( x ) { console . log ( x . _ _annotation . severity + ' in line number ' + ( x . _ _annotation . from . line + 1 ) + ' : ' + x . _ _annotation . message ) ; } ) ; // eslint-disable-line
2016-07-08 19:58:49 +00:00
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-23 22:29:55 +00:00
}
if ( this . props . fontSize !== prevProps . fontSize ) {
this . _cm . getWrapperElement ( ) . style [ 'font-size' ] = ` ${ this . props . fontSize } px ` ;
}
2016-07-06 15:27:39 +00:00
if ( this . props . indentationAmount !== prevProps . indentationAmount ) {
this . _cm . setOption ( 'tabSize' , this . props . indentationAmount ) ;
}
2016-07-11 03:11:06 +00:00
if ( this . props . isTabIndent !== prevProps . isTabIndent ) {
this . _cm . setOption ( 'indentWithTabs' , this . props . isTabIndent ) ;
}
2016-07-13 23:52:50 +00:00
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-23 22:29:55 +00:00
}
componentWillUnmount ( ) {
this . _cm = null ;
}
_cm : CodeMirror . Editor
render ( ) {
2016-08-05 20:58:59 +00:00
return (
< div ref = "container" className = "editor-holder" tabIndex = "0" title = "code editor" role = "region" >
< / d i v >
) ;
2016-06-23 22:29:55 +00:00
}
}
2016-06-27 20:03:22 +00:00
Editor . propTypes = {
2016-08-10 15:13:17 +00:00
enableBeep : PropTypes . bool . isRequired ,
2016-07-11 02:52:48 +00:00
indentationAmount : PropTypes . number . isRequired ,
2016-07-12 03:40:30 +00:00
isTabIndent : PropTypes . bool . isRequired ,
updateFileContent : PropTypes . func . isRequired ,
fontSize : PropTypes . number . isRequired ,
2016-07-08 19:58:49 +00:00
file : PropTypes . shape ( {
name : PropTypes . string . isRequired ,
content : PropTypes . string . isRequired
2016-07-12 03:40:30 +00:00
} )
2016-06-27 20:03:22 +00:00
} ;
2016-06-23 22:29:55 +00:00
export default Editor ;