|
|
|
|
@@ -76,8 +76,8 @@ const Editor = createReactClass({
|
|
|
|
|
document.getElementById('BrewRenderer').addEventListener('keydown', this.handleControlKeys);
|
|
|
|
|
document.addEventListener('keydown', this.handleControlKeys);
|
|
|
|
|
|
|
|
|
|
this.codeEditor.current.codeMirror.on('cursorActivity', (cm)=>{this.updateCurrentCursorPage(cm.getCursor());});
|
|
|
|
|
this.codeEditor.current.codeMirror.on('scroll', _.throttle(()=>{this.updateCurrentViewPage(this.codeEditor.current.getTopVisibleLine());}, 200));
|
|
|
|
|
this.codeEditor.current.codeMirror?.on('cursorActivity', (cm)=>{this.updateCurrentCursorPage(cm.getCursor());});
|
|
|
|
|
this.codeEditor.current.codeMirror?.on('scroll', _.throttle(()=>{this.updateCurrentViewPage(this.codeEditor.current.getTopVisibleLine());}, 200));
|
|
|
|
|
|
|
|
|
|
const editorTheme = window.localStorage.getItem(EDITOR_THEME_KEY);
|
|
|
|
|
if(editorTheme) {
|
|
|
|
|
@@ -156,21 +156,21 @@ const Editor = createReactClass({
|
|
|
|
|
this.setState({
|
|
|
|
|
view : newView
|
|
|
|
|
}, ()=>{
|
|
|
|
|
this.codeEditor.current?.codeMirror.focus();
|
|
|
|
|
this.codeEditor.current?.codeMirror?.focus();
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
highlightCustomMarkdown : function(){
|
|
|
|
|
if(!this.codeEditor.current) return;
|
|
|
|
|
if(!this.codeEditor.current?.codeMirror) return;
|
|
|
|
|
if((this.state.view === 'text') ||(this.state.view === 'snippet')) {
|
|
|
|
|
const codeMirror = this.codeEditor.current.codeMirror;
|
|
|
|
|
|
|
|
|
|
codeMirror.operation(()=>{ // Batch CodeMirror styling
|
|
|
|
|
codeMirror?.operation(()=>{ // Batch CodeMirror styling
|
|
|
|
|
|
|
|
|
|
const foldLines = [];
|
|
|
|
|
|
|
|
|
|
//reset custom text styles
|
|
|
|
|
const customHighlights = codeMirror.getAllMarks().filter((mark)=>{
|
|
|
|
|
const customHighlights = codeMirror?.getAllMarks().filter((mark)=>{
|
|
|
|
|
// Record details of folded sections
|
|
|
|
|
if(mark.__isFold) {
|
|
|
|
|
const fold = mark.find();
|
|
|
|
|
@@ -191,10 +191,10 @@ const Editor = createReactClass({
|
|
|
|
|
const textOrSnip = this.state.view === 'text';
|
|
|
|
|
|
|
|
|
|
//reset custom line styles
|
|
|
|
|
codeMirror.removeLineClass(lineNumber, 'background', 'pageLine');
|
|
|
|
|
codeMirror.removeLineClass(lineNumber, 'background', 'snippetLine');
|
|
|
|
|
codeMirror.removeLineClass(lineNumber, 'text');
|
|
|
|
|
codeMirror.removeLineClass(lineNumber, 'wrap', 'sourceMoveFlash');
|
|
|
|
|
codeMirror?.removeLineClass(lineNumber, 'background', 'pageLine');
|
|
|
|
|
codeMirror?.removeLineClass(lineNumber, 'background', 'snippetLine');
|
|
|
|
|
codeMirror?.removeLineClass(lineNumber, 'text');
|
|
|
|
|
codeMirror?.removeLineClass(lineNumber, 'wrap', 'sourceMoveFlash');
|
|
|
|
|
|
|
|
|
|
// Don't process lines inside folded text
|
|
|
|
|
// If the current lineNumber is inside any folded marks, skip line styling
|
|
|
|
|
@@ -210,19 +210,19 @@ const Editor = createReactClass({
|
|
|
|
|
else if(this.state.view !== 'text') userSnippetCount += 1;
|
|
|
|
|
|
|
|
|
|
// add back the original class 'background' but also add the new class '.pageline'
|
|
|
|
|
codeMirror.addLineClass(lineNumber, 'background', tabHighlight);
|
|
|
|
|
codeMirror?.addLineClass(lineNumber, 'background', tabHighlight);
|
|
|
|
|
const pageCountElement = Object.assign(document.createElement('span'), {
|
|
|
|
|
className : 'editor-page-count',
|
|
|
|
|
textContent : textOrSnip ? editorPageCount : userSnippetCount
|
|
|
|
|
});
|
|
|
|
|
codeMirror.setBookmark({ line: lineNumber, ch: line.length }, pageCountElement);
|
|
|
|
|
codeMirror?.setBookmark({ line: lineNumber, ch: line.length }, pageCountElement);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// New Codemirror styling for V3 renderer
|
|
|
|
|
// New CodeMirror styling for V3 renderer
|
|
|
|
|
if(this.props.renderer === 'V3') {
|
|
|
|
|
if(line.match(/^\\column(?:break)?$/)){
|
|
|
|
|
codeMirror.addLineClass(lineNumber, 'text', 'columnSplit');
|
|
|
|
|
codeMirror?.addLineClass(lineNumber, 'text', 'columnSplit');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// definition lists
|
|
|
|
|
@@ -231,14 +231,14 @@ const Editor = createReactClass({
|
|
|
|
|
const regex = /^([^\n]*?:?\s?)(::[^\n]*)(?:\n|$)/ymd; // the `d` flag, for match indices, throws an ESLint error.
|
|
|
|
|
let match;
|
|
|
|
|
while ((match = regex.exec(line)) != null){
|
|
|
|
|
codeMirror.markText({ line: lineNumber, ch: match.indices[0][0] }, { line: lineNumber, ch: match.indices[0][1] }, { className: 'dl-highlight' });
|
|
|
|
|
codeMirror.markText({ line: lineNumber, ch: match.indices[1][0] }, { line: lineNumber, ch: match.indices[1][1] }, { className: 'dt-highlight' });
|
|
|
|
|
codeMirror.markText({ line: lineNumber, ch: match.indices[2][0] }, { line: lineNumber, ch: match.indices[2][1] }, { className: 'dd-highlight' });
|
|
|
|
|
codeMirror?.markText({ line: lineNumber, ch: match.indices[0][0] }, { line: lineNumber, ch: match.indices[0][1] }, { className: 'dl-highlight' });
|
|
|
|
|
codeMirror?.markText({ line: lineNumber, ch: match.indices[1][0] }, { line: lineNumber, ch: match.indices[1][1] }, { className: 'dt-highlight' });
|
|
|
|
|
codeMirror?.markText({ line: lineNumber, ch: match.indices[2][0] }, { line: lineNumber, ch: match.indices[2][1] }, { className: 'dd-highlight' });
|
|
|
|
|
const ddIndex = match.indices[2][0];
|
|
|
|
|
const colons = /::/g;
|
|
|
|
|
const colonMatches = colons.exec(match[2]);
|
|
|
|
|
if(colonMatches !== null){
|
|
|
|
|
codeMirror.markText({ line: lineNumber, ch: colonMatches.index + ddIndex }, { line: lineNumber, ch: colonMatches.index + colonMatches[0].length + ddIndex }, { className: 'dl-colon-highlight' });
|
|
|
|
|
codeMirror?.markText({ line: lineNumber, ch: colonMatches.index + ddIndex }, { line: lineNumber, ch: colonMatches.index + colonMatches[0].length + ddIndex }, { className: 'dl-colon-highlight' });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -255,7 +255,7 @@ const Editor = createReactClass({
|
|
|
|
|
const match = subRegex.exec(line) || superRegex.exec(line);
|
|
|
|
|
if(match) {
|
|
|
|
|
isSuper = !subRegex.lastIndex;
|
|
|
|
|
codeMirror.markText({ line: lineNumber, ch: match.index }, { line: lineNumber, ch: match.index + match[0].length }, { className: isSuper ? 'superscript' : 'subscript' });
|
|
|
|
|
codeMirror?.markText({ line: lineNumber, ch: match.index }, { line: lineNumber, ch: match.index + match[0].length }, { className: isSuper ? 'superscript' : 'subscript' });
|
|
|
|
|
}
|
|
|
|
|
startIndex = line.indexOf('^', Math.max(startIndex + 1, subRegex.lastIndex, superRegex.lastIndex));
|
|
|
|
|
}
|
|
|
|
|
@@ -266,7 +266,7 @@ const Editor = createReactClass({
|
|
|
|
|
const regex = /(?:^|[^{\n])({(?=((?:[:=](?:"[\w,\-()#%. ]*"|[\w\-()#%.]*)|[^"':={}\s]*)*))\2})/gm;
|
|
|
|
|
let match;
|
|
|
|
|
while ((match = regex.exec(line)) != null) {
|
|
|
|
|
codeMirror.markText({ line: lineNumber, ch: line.indexOf(match[1]) }, { line: lineNumber, ch: line.indexOf(match[1]) + match[1].length }, { className: 'injection' });
|
|
|
|
|
codeMirror?.markText({ line: lineNumber, ch: line.indexOf(match[1]) }, { line: lineNumber, ch: line.indexOf(match[1]) + match[1].length }, { className: 'injection' });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Highlight inline spans {{content}}
|
|
|
|
|
@@ -284,7 +284,7 @@ const Editor = createReactClass({
|
|
|
|
|
blockCount = 0;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
codeMirror.markText({ line: lineNumber, ch: match.index }, { line: lineNumber, ch: match.index + match[0].length }, { className: 'inline-block' });
|
|
|
|
|
codeMirror?.markText({ line: lineNumber, ch: match.index }, { line: lineNumber, ch: match.index + match[0].length }, { className: 'inline-block' });
|
|
|
|
|
}
|
|
|
|
|
} else if(line.trimLeft().startsWith('{{') || line.trimLeft().startsWith('}}')){
|
|
|
|
|
// Highlight block divs {{\n Content \n}}
|
|
|
|
|
@@ -293,7 +293,7 @@ const Editor = createReactClass({
|
|
|
|
|
const match = line.match(/^ *{{(?=((?:[:=](?:"[\w,\-()#%. ]*"|[\w\-()#%.]*)|[^"':={}\s]*)*))\1 *$|^ *}}$/);
|
|
|
|
|
if(match)
|
|
|
|
|
endCh = match.index+match[0].length;
|
|
|
|
|
codeMirror.markText({ line: lineNumber, ch: 0 }, { line: lineNumber, ch: endCh }, { className: 'block' });
|
|
|
|
|
codeMirror?.markText({ line: lineNumber, ch: 0 }, { line: lineNumber, ch: endCh }, { className: 'block' });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Emojis
|
|
|
|
|
@@ -314,11 +314,11 @@ const Editor = createReactClass({
|
|
|
|
|
const endPos = { line: lineNumber, ch: match.index + match[0].length };
|
|
|
|
|
|
|
|
|
|
// Iterate over conflicting marks and clear them
|
|
|
|
|
const marks = codeMirror.findMarks(startPos, endPos);
|
|
|
|
|
const marks = codeMirror?.findMarks(startPos, endPos);
|
|
|
|
|
marks.forEach(function(marker) {
|
|
|
|
|
if(!marker.__isFold) marker.clear();
|
|
|
|
|
});
|
|
|
|
|
codeMirror.markText(startPos, endPos, { className: 'emoji' });
|
|
|
|
|
codeMirror?.markText(startPos, endPos, { className: 'emoji' });
|
|
|
|
|
}
|
|
|
|
|
startIndex = line.indexOf(':', Math.max(startIndex + 1, emojiRegex.lastIndex));
|
|
|
|
|
}
|
|
|
|
|
@@ -378,44 +378,46 @@ const Editor = createReactClass({
|
|
|
|
|
const textString = this.props.brew.text.split(textSplit).slice(0, targetPage-1).join(textSplit);
|
|
|
|
|
const targetLine = textString.match('\n') ? textString.split('\n').length - 1 : -1;
|
|
|
|
|
|
|
|
|
|
let currentY = this.codeEditor.current.codeMirror.getScrollInfo().top;
|
|
|
|
|
let targetY = this.codeEditor.current.codeMirror.heightAtLine(targetLine, 'local', true);
|
|
|
|
|
let currentY = this.codeEditor.current.codeMirror?.getScrollInfo().top;
|
|
|
|
|
let targetY = this.codeEditor.current.codeMirror?.heightAtLine(targetLine, 'local', true);
|
|
|
|
|
|
|
|
|
|
let scrollingTimeout;
|
|
|
|
|
const checkIfScrollComplete = ()=>{ // Prevent interrupting a scroll in progress if user clicks multiple times
|
|
|
|
|
clearTimeout(scrollingTimeout); // Reset the timer every time a scroll event occurs
|
|
|
|
|
scrollingTimeout = setTimeout(()=>{
|
|
|
|
|
isJumping = false;
|
|
|
|
|
this.codeEditor.current.codeMirror.off('scroll', checkIfScrollComplete);
|
|
|
|
|
this.codeEditor.current.codeMirror?.off('scroll', checkIfScrollComplete);
|
|
|
|
|
}, 150); // If 150 ms pass without a scroll event, assume scrolling is done
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
isJumping = true;
|
|
|
|
|
checkIfScrollComplete();
|
|
|
|
|
this.codeEditor.current.codeMirror.on('scroll', checkIfScrollComplete);
|
|
|
|
|
if (this.codeEditor.current?.codeMirror) {
|
|
|
|
|
this.codeEditor.current.codeMirror?.on('scroll', checkIfScrollComplete);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(smooth) {
|
|
|
|
|
//Scroll 1/10 of the way every 10ms until 1px off.
|
|
|
|
|
const incrementalScroll = setInterval(()=>{
|
|
|
|
|
currentY += (targetY - currentY) / 10;
|
|
|
|
|
this.codeEditor.current.codeMirror.scrollTo(null, currentY);
|
|
|
|
|
this.codeEditor.current.codeMirror?.scrollTo(null, currentY);
|
|
|
|
|
|
|
|
|
|
// Update target: target height is not accurate until within +-10 lines of the visible window
|
|
|
|
|
if(Math.abs(targetY - currentY > 100))
|
|
|
|
|
targetY = this.codeEditor.current.codeMirror.heightAtLine(targetLine, 'local', true);
|
|
|
|
|
targetY = this.codeEditor.current.codeMirror?.heightAtLine(targetLine, 'local', true);
|
|
|
|
|
|
|
|
|
|
// End when close enough
|
|
|
|
|
if(Math.abs(targetY - currentY) < 1) {
|
|
|
|
|
this.codeEditor.current.codeMirror.scrollTo(null, targetY); // Scroll any remaining difference
|
|
|
|
|
this.codeEditor.current.codeMirror?.scrollTo(null, targetY); // Scroll any remaining difference
|
|
|
|
|
this.codeEditor.current.setCursorPosition({ line: targetLine + 1, ch: 0 });
|
|
|
|
|
this.codeEditor.current.codeMirror.addLineClass(targetLine + 1, 'wrap', 'sourceMoveFlash');
|
|
|
|
|
this.codeEditor.current.codeMirror?.addLineClass(targetLine + 1, 'wrap', 'sourceMoveFlash');
|
|
|
|
|
clearInterval(incrementalScroll);
|
|
|
|
|
}
|
|
|
|
|
}, 10);
|
|
|
|
|
} else {
|
|
|
|
|
this.codeEditor.current.codeMirror.scrollTo(null, targetY); // Scroll any remaining difference
|
|
|
|
|
this.codeEditor.current.codeMirror?.scrollTo(null, targetY); // Scroll any remaining difference
|
|
|
|
|
this.codeEditor.current.setCursorPosition({ line: targetLine + 1, ch: 0 });
|
|
|
|
|
this.codeEditor.current.codeMirror.addLineClass(targetLine + 1, 'wrap', 'sourceMoveFlash');
|
|
|
|
|
this.codeEditor.current.codeMirror?.addLineClass(targetLine + 1, 'wrap', 'sourceMoveFlash');
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|