0
0
mirror of https://github.com/naturalcrit/homebrewery.git synced 2025-12-24 18:32:41 +00:00

Replace copied code with require methods to import the CodeMirror helpers

Put back the correct require notation for importing the foldcode and foldgutter helpers

#629
This commit is contained in:
Charlie Humphreys
2021-11-07 21:35:35 -06:00
parent c0b9f4488f
commit eec6e66543
3 changed files with 46 additions and 316 deletions

View File

@@ -14,8 +14,11 @@ if(typeof navigator !== 'undefined'){
require('codemirror/mode/css/css.js');
require('codemirror/mode/javascript/javascript.js');
// this should add the foldcode helpers and such to the CodeMirror object, but seemingly is adding them to a new instance of CodeMirror
require('codemirror/addon/fold/foldcode.js');
require('codemirror/addon/fold/foldgutter.js');
const foldCode = require('./fold-code');
foldCode.enableCodeFolding(CodeMirror);
foldCode.registerHomebreweryHelper(CodeMirror);
}
@@ -40,8 +43,10 @@ const CodeEditor = createClass({
const newDoc = CodeMirror.Doc(this.props.value, this.props.language);
this.codeMirror.swapDoc(newDoc);
},
componentDidUpdate : function(prevProps) {
getSnapshotBeforeUpdate : function() {
return _.uniq(this.codeMirror.getAllMarks().filter((mark)=>mark.__isFold).map((mark)=>mark.find().from));
},
componentDidUpdate : function(prevProps, prevState, snapshot) {
if(prevProps.view !== this.props.view){ //view changed; swap documents
let newDoc;
@@ -61,6 +66,12 @@ const CodeEditor = createClass({
} else if(this.codeMirror?.getValue() != this.props.value) { //update editor contents if brew.text is changed from outside
this.codeMirror.setValue(this.props.value);
}
setTimeout(()=>{
snapshot.forEach((fold)=>{
this.codeMirror.foldCode(fold, { scanUp: false }, 'fold');
});
}, 0);
},
buildEditor : function() {
@@ -79,12 +90,30 @@ const CodeEditor = createClass({
'Cmd-M' : this.makeSpan,
'Ctrl-/' : this.makeComment,
'Cmd-/' : this.makeComment,
'Ctrl-,' : this.toggleCodeFolded,
'Cmd-,' : this.toggleCodeFolded
'Ctrl-\\' : this.toggleCodeFolded,
'Cmd-\\' : this.toggleCodeFolded,
'Ctrl-[' : this.foldAllCode,
'Cmd-[' : this.foldAllCode,
'Ctrl-]' : this.unfoldAllCode,
'Cmd-]' : this.unfoldAllCode
},
foldGutter : true,
foldOptions : {
rangeFinder : CodeMirror.fold.homebrewery,
widget : (from, to)=>{
let text = '';
let currentLine = from.line;
const maxLength = 50;
while (currentLine <= to.line && text.length <= maxLength) {
text += this.codeMirror.getLine(currentLine);
if(currentLine < to.line) {
text += ' ';
}
currentLine += 1;
}
return `\u21A4${text.substr(0, maxLength)}\u21A6`;
}
},
gutters : ['CodeMirror-linenumbers', 'CodeMirror-foldgutter']
});
@@ -134,6 +163,14 @@ const CodeEditor = createClass({
this.codeMirror.foldCode(this.codeMirror.getCursor());
},
foldAllCode : function() {
this.codeMirror.execCommand('foldAll');
},
unfoldAllCode : function() {
this.codeMirror.execCommand('unfoldAll');
},
//=-- Externally used -==//
setCursorPosition : function(line, char){
setTimeout(()=>{

View File

@@ -2,5 +2,7 @@
@import (less) 'codemirror/addon/fold/foldgutter.css';
.codeEditor{
.CodeMirror-foldmarker {
font-family: inherit;
}
}

View File

@@ -1,313 +1,4 @@
/* eslint-disable max-lines */
module.exports = {
enableCodeFolding : function (CodeMirror) {
// foldcode.js
const makeWidget = function(cm, options, range) {
let widget = getOption(cm, options, 'widget');
if(typeof widget == 'function') {
widget = widget(range.from, range.to);
}
if(typeof widget == 'string') {
const text = document.createTextNode(widget);
widget = document.createElement('span');
widget.appendChild(text);
widget.className = 'CodeMirror-foldmarker';
} else if(widget) {
widget = widget.cloneNode(true);
}
return widget;
};
const doFold = function(cm, pos, options, force) {
let finder;
if(options && options.call) {
finder = options;
options = null;
} else {
finder = getOption(cm, options, 'rangeFinder');
}
if(typeof pos == 'number') pos = CodeMirror.Pos(pos, 0);
const minSize = getOption(cm, options, 'minFoldSize');
const getRange = function(allowFolded) {
const range = finder(cm, pos);
if(!range || range.to.line - range.from.line < minSize) return null;
if(force === 'fold') return range;
const marks = cm.findMarksAt(range.from);
for (let i = 0; i < marks.length; ++i) {
if(marks[i].__isFold) {
if(!allowFolded) return null;
range.cleared = true;
marks[i].clear();
}
}
return range;
};
let range = getRange(true);
if(getOption(cm, options, 'scanUp')) while (!range && pos.line > cm.firstLine()) {
pos = CodeMirror.Pos(pos.line - 1, 0);
range = getRange(false);
}
if(!range || range.cleared || force === 'unfold') return;
const myWidget = makeWidget(cm, options, range);
CodeMirror.on(myWidget, 'mousedown', function (e) {
myRange.clear();
CodeMirror.e_preventDefault(e);
});
const myRange = cm.markText(range.from, range.to, {
replacedWith : myWidget,
clearOnEnter : getOption(cm, options, 'clearOnEnter'),
__isFold : true
});
myRange.on('clear', function (from, to) {
CodeMirror.signal(cm, 'unfold', cm, from, to);
});
CodeMirror.signal(cm, 'fold', cm, range.from, range.to);
};
// Clumsy backwards-compatible interface
CodeMirror.newFoldFunction = function (rangeFinder, widget) {
return function (cm, pos) {
doFold(cm, pos, { rangeFinder: rangeFinder, widget: widget });
};
};
// New-style interface
CodeMirror.defineExtension('foldCode', function (pos, options, force) {
doFold(this, pos, options, force);
});
CodeMirror.defineExtension('isFolded', function (pos) {
const marks = this.findMarksAt(pos);
for (let i = 0; i < marks.length; ++i)
if(marks[i].__isFold) return true;
});
CodeMirror.commands.toggleFold = function (cm) {
cm.foldCode(cm.getCursor());
};
CodeMirror.commands.fold = function (cm) {
cm.foldCode(cm.getCursor(), null, 'fold');
};
CodeMirror.commands.unfold = function (cm) {
cm.foldCode(cm.getCursor(), { scanUp: false }, 'unfold');
};
CodeMirror.commands.foldAll = function (cm) {
cm.operation(function () {
for (let i = cm.firstLine(), e = cm.lastLine(); i <= e; i++)
cm.foldCode(CodeMirror.Pos(i, 0), { scanUp: false }, 'fold');
});
};
CodeMirror.commands.unfoldAll = function (cm) {
cm.operation(function () {
for (let i = cm.firstLine(), e = cm.lastLine(); i <= e; i++)
cm.foldCode(CodeMirror.Pos(i, 0), { scanUp: false }, 'unfold');
});
};
CodeMirror.registerHelper('fold', 'combine', function () {
const funcs = Array.prototype.slice.call(arguments, 0);
return function (cm, start) {
for (let i = 0; i < funcs.length; ++i) {
const found = funcs[i](cm, start);
if(found) return found;
}
};
});
CodeMirror.registerHelper('fold', 'auto', function (cm, start) {
const helpers = cm.getHelpers(start, 'fold');
for (let i = 0; i < helpers.length; i++) {
const cur = helpers[i](cm, start);
if(cur) return cur;
}
});
const defaultOptions = {
rangeFinder : CodeMirror.fold.auto,
widget : '\u2194',
minFoldSize : 0,
scanUp : false,
clearOnEnter : true
};
CodeMirror.defineOption('foldOptions', null);
const getOption = function(cm, options, name) {
if(options && options[name] !== undefined)
return options[name];
const editorOptions = cm.options.foldOptions;
if(editorOptions && editorOptions[name] !== undefined)
return editorOptions[name];
return defaultOptions[name];
};
CodeMirror.defineExtension('foldOption', function (options, name) {
return getOption(this, options, name);
});
// foldgutter.js
const State = function(options) {
this.options = options;
this.from = this.to = 0;
};
const parseOptions = function(opts) {
if(opts === true) opts = {};
if(opts.gutter == null) opts.gutter = 'CodeMirror-foldgutter';
if(opts.indicatorOpen == null) opts.indicatorOpen = 'CodeMirror-foldgutter-open';
if(opts.indicatorFolded == null) opts.indicatorFolded = 'CodeMirror-foldgutter-folded';
return opts;
};
CodeMirror.defineOption('foldGutter', false, function (cm, val, old) {
if(old && old != CodeMirror.Init) {
cm.clearGutter(cm.state.foldGutter.options.gutter);
cm.state.foldGutter = null;
cm.off('gutterClick', onGutterClick);
cm.off('changes', onChange);
cm.off('viewportChange', onViewportChange);
cm.off('fold', onFold);
cm.off('unfold', onFold);
cm.off('swapDoc', onChange);
}
if(val) {
cm.state.foldGutter = new State(parseOptions(val));
updateInViewport(cm);
cm.on('gutterClick', onGutterClick);
cm.on('changes', onChange);
cm.on('viewportChange', onViewportChange);
cm.on('fold', onFold);
cm.on('unfold', onFold);
cm.on('swapDoc', onChange);
}
});
const Pos = CodeMirror.Pos;
const isFolded = function(cm, line) {
const marks = cm.findMarks(Pos(line, 0), Pos(line + 1, 0));
for (let i = 0; i < marks.length; ++i) {
if(marks[i].__isFold) {
const fromPos = marks[i].find(-1);
if(fromPos && fromPos.line === line)
return marks[i];
}
}
};
const marker = function(spec) {
if(typeof spec == 'string') {
const elt = document.createElement('div');
elt.className = `${spec} CodeMirror-guttermarker-subtle`;
return elt;
} else {
return spec.cloneNode(true);
}
};
const updateFoldInfo = function(cm, from, to) {
const opts = cm.state.foldGutter.options;
let cur = from - 1;
const minSize = cm.foldOption(opts, 'minFoldSize');
const func = cm.foldOption(opts, 'rangeFinder');
// we can reuse the built-in indicator element if its className matches the new state
const clsFolded = typeof opts.indicatorFolded == 'string' && classTest(opts.indicatorFolded);
const clsOpen = typeof opts.indicatorOpen == 'string' && classTest(opts.indicatorOpen);
cm.eachLine(from, to, function (line) {
++cur;
let mark = null;
let old = line.gutterMarkers;
if(old) old = old[opts.gutter];
if(isFolded(cm, cur)) {
if(clsFolded && old && clsFolded.test(old.className)) return;
mark = marker(opts.indicatorFolded);
} else {
const pos = Pos(cur, 0);
const range = func && func(cm, pos);
if(range && range.to.line - range.from.line >= minSize) {
if(clsOpen && old && clsOpen.test(old.className)) return;
mark = marker(opts.indicatorOpen);
}
}
if(!mark && !old) return;
cm.setGutterMarker(line, opts.gutter, mark);
});
};
// copied from CodeMirror/src/util/dom.js
const classTest = function(cls) {
return new RegExp(`(^|\\s)${cls}(?:$|\\s)\\s*`);
};
const updateInViewport = function(cm) {
const vp = cm.getViewport(), state = cm.state.foldGutter;
if(!state) return;
cm.operation(function () {
updateFoldInfo(cm, vp.from, vp.to);
});
state.from = vp.from;
state.to = vp.to;
};
const onGutterClick = function(cm, line, gutter) {
const state = cm.state.foldGutter;
if(!state) return;
const opts = state.options;
if(gutter != opts.gutter) return;
const folded = isFolded(cm, line);
if(folded) folded.clear();
else cm.foldCode(Pos(line, 0), opts);
};
const onChange = function(cm) {
const state = cm.state.foldGutter;
if(!state) return;
const opts = state.options;
state.from = state.to = 0;
clearTimeout(state.changeUpdate);
state.changeUpdate = setTimeout(function () {
updateInViewport(cm);
}, opts.foldOnChangeTimeSpan || 600);
};
const onViewportChange = function(cm) {
const state = cm.state.foldGutter;
if(!state) return;
const opts = state.options;
clearTimeout(state.changeUpdate);
state.changeUpdate = setTimeout(function () {
const vp = cm.getViewport();
if(state.from == state.to || vp.from - state.to > 20 || state.from - vp.to > 20) {
updateInViewport(cm);
} else {
cm.operation(function () {
if(vp.from < state.from) {
updateFoldInfo(cm, vp.from, state.from);
state.from = vp.from;
}
if(vp.to > state.to) {
updateFoldInfo(cm, state.to, vp.to);
state.to = vp.to;
}
});
}
}, opts.updateViewportTimeSpan || 400);
};
const onFold = function(cm, from) {
const state = cm.state.foldGutter;
if(!state) return;
const line = from.line;
if(line >= state.from && line < state.to)
updateFoldInfo(cm, line, line + 1);
};
},
registerHomebreweryHelper : function(CodeMirror) {
CodeMirror.registerHelper('fold', 'homebrewery', function(cm, start) {
const matcher = /^\\page.*/;
@@ -321,7 +12,7 @@ module.exports = {
while (end < lastLineNo) {
if(nextLine.match(matcher)) {
return {
from : CodeMirror.Pos(start.line, firstLine.length),
from : CodeMirror.Pos(start.line, 0),
to : CodeMirror.Pos(end, cm.getLine(end).length)
};
}