mirror of
https://github.com/naturalcrit/homebrewery.git
synced 2025-12-24 20:42:43 +00:00
Instead of re-building the whole editor box with every tab switch, we just swap in and out CodeMirror "documents". Maintains undo history, scroll position, highlight coloring, etc.
152 lines
4.4 KiB
JavaScript
152 lines
4.4 KiB
JavaScript
require('./codeEditor.less');
|
|
const React = require('react');
|
|
const createClass = require('create-react-class');
|
|
const _ = require('lodash');
|
|
const cx = require('classnames');
|
|
|
|
|
|
let CodeMirror;
|
|
if(typeof navigator !== 'undefined'){
|
|
CodeMirror = require('codemirror');
|
|
|
|
//Language Modes
|
|
require('codemirror/mode/gfm/gfm.js'); //Github flavoured markdown
|
|
require('codemirror/mode/css/css.js');
|
|
require('codemirror/mode/javascript/javascript.js');
|
|
}
|
|
|
|
const CodeEditor = createClass({
|
|
getDefaultProps : function() {
|
|
return {
|
|
language : '',
|
|
value : '',
|
|
wrap : true,
|
|
onChange : ()=>{}
|
|
};
|
|
},
|
|
|
|
getInitialState : function() {
|
|
return {
|
|
docs : {}
|
|
};
|
|
},
|
|
|
|
componentDidMount : function() {
|
|
this.buildEditor();
|
|
this.codeMirror.setValue(this.props.value);
|
|
this.codeMirror.clearHistory();
|
|
},
|
|
|
|
componentDidUpdate : function(prevProps) {
|
|
if(prevProps.view !== this.props.view){ //view changed; swap documents
|
|
let newDoc;
|
|
|
|
if(!this.state.docs[this.props.view]) {
|
|
newDoc = CodeMirror.Doc(this.props.value, this.props.language);
|
|
} else {
|
|
newDoc = this.state.docs[this.props.view];
|
|
}
|
|
|
|
const oldDoc = { [prevProps.view]: this.codeMirror.swapDoc(newDoc) };
|
|
|
|
this.setState((prevState)=>({
|
|
docs : _.merge({}, prevState.docs, oldDoc)
|
|
}));
|
|
|
|
this.props.rerenderParent();
|
|
} else if(this.codeMirror?.getValue() != this.props.value) { //update editor contents if brew.text is changed from outside
|
|
this.codeMirror.setValue(this.props.value);
|
|
}
|
|
},
|
|
|
|
buildEditor : function() {
|
|
this.codeMirror = CodeMirror(this.refs.editor, {
|
|
lineNumbers : true,
|
|
lineWrapping : this.props.wrap,
|
|
indentWithTabs : true,
|
|
tabSize : 2,
|
|
historyEventDelay : 250,
|
|
extraKeys : {
|
|
'Ctrl-B' : this.makeBold,
|
|
'Cmd-B' : this.makeBold,
|
|
'Ctrl-I' : this.makeItalic,
|
|
'Cmd-I' : this.makeItalic,
|
|
'Ctrl-M' : this.makeSpan,
|
|
'Cmd-M' : this.makeSpan,
|
|
'Ctrl-/' : this.makeComment,
|
|
'Cmd-/' : this.makeComment
|
|
}
|
|
});
|
|
|
|
// Note: codeMirror passes a copy of itself in this callback. cm === this.codeMirror. Either one works.
|
|
this.codeMirror.on('change', (cm)=>{this.props.onChange(cm.getValue());});
|
|
this.updateSize();
|
|
},
|
|
|
|
makeBold : function() {
|
|
const selection = this.codeMirror.getSelection(), t = selection.slice(0, 2) === '**' && selection.slice(-2) === '**';
|
|
this.codeMirror.replaceSelection(t ? selection.slice(2, -2) : `**${selection}**`, 'around');
|
|
if(selection.length === 0){
|
|
const cursor = this.codeMirror.getCursor();
|
|
this.codeMirror.setCursor({ line: cursor.line, ch: cursor.ch - 2 });
|
|
}
|
|
},
|
|
|
|
makeItalic : function() {
|
|
const selection = this.codeMirror.getSelection(), t = selection.slice(0, 1) === '_' && selection.slice(-1) === '_';
|
|
this.codeMirror.replaceSelection(t ? selection.slice(1, -1) : `_${selection}_`, 'around');
|
|
if(selection.length === 0){
|
|
const cursor = this.codeMirror.getCursor();
|
|
this.codeMirror.setCursor({ line: cursor.line, ch: cursor.ch - 1 });
|
|
}
|
|
},
|
|
|
|
makeSpan : function() {
|
|
const selection = this.codeMirror.getSelection(), t = selection.slice(0, 2) === '{{' && selection.slice(-2) === '}}';
|
|
this.codeMirror.replaceSelection(t ? selection.slice(2, -2) : `{{ ${selection}}}`, 'around');
|
|
if(selection.length === 0){
|
|
const cursor = this.codeMirror.getCursor();
|
|
this.codeMirror.setCursor({ line: cursor.line, ch: cursor.ch - 2 });
|
|
}
|
|
},
|
|
|
|
makeComment : function() {
|
|
const selection = this.codeMirror.getSelection(), t = selection.slice(0, 4) === '<!--' && selection.slice(-3) === '-->';
|
|
this.codeMirror.replaceSelection(t ? selection.slice(4, -3) : `<!-- ${selection} -->`, 'around');
|
|
if(selection.length === 0){
|
|
const cursor = this.codeMirror.getCursor();
|
|
this.codeMirror.setCursor({ line: cursor.line, ch: cursor.ch - 4 });
|
|
}
|
|
},
|
|
|
|
//=-- Externally used -==//
|
|
setCursorPosition : function(line, char){
|
|
setTimeout(()=>{
|
|
this.codeMirror.focus();
|
|
this.codeMirror.doc.setCursor(line, char);
|
|
}, 10);
|
|
},
|
|
getCursorPosition : function(){
|
|
return this.codeMirror.getCursor();
|
|
},
|
|
updateSize : function(){
|
|
this.codeMirror.refresh();
|
|
},
|
|
redo : function(){
|
|
return this.codeMirror.redo();
|
|
},
|
|
undo : function(){
|
|
return this.codeMirror.undo();
|
|
},
|
|
historySize : function(){
|
|
return this.codeMirror.doc.historySize();
|
|
},
|
|
//----------------------//
|
|
|
|
render : function(){
|
|
return <div className='codeEditor' ref='editor' style={this.props.style}/>;
|
|
}
|
|
});
|
|
|
|
module.exports = CodeEditor;
|