Skip to content

Commit

Permalink
Desktop: Fixes laurent22#3560: Modify the CodeMirror linter plugin to…
Browse files Browse the repository at this point in the history
… fix katex (laurent22#3582)

Rewrite the joplin mode to manually handle parsing.

This gives us more control over katex parsing and the ability to upgrade
in the future
  • Loading branch information
CalebJohn committed Aug 2, 2020
1 parent bbfed9b commit 7f73931
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,9 @@ import 'codemirror/mode/stex/stex';
// Joplin markdown is a the same as markdown mode, but it has configured defaults
// and support for katex math blocks
export default function useJoplinMode(CodeMirror: any) {
CodeMirror.defineMode('joplin-markdown', (config: any) => {
const stex = CodeMirror.getMode(config, { name: 'stex', inMathMode: true });
const blocks = [{ open: '$$', close: '$$', mode: stex, delimStyle: 'katex-marker' },
{ open: '$', close: '$', mode: stex, delimStyle: 'katex-marker' }];

const markdownOptions = {
CodeMirror.defineMode('joplin-markdown', (config: any) => {
const markdownConfig = {
name: 'markdown',
taskLists: true,
strikethrough: true,
Expand All @@ -19,7 +16,105 @@ export default function useJoplinMode(CodeMirror: any) {
},
};

return CodeMirror.multiplexingMode(CodeMirror.getMode(config, markdownOptions), ...blocks);
const markdownMode = CodeMirror.getMode(config, markdownConfig);
const stex = CodeMirror.getMode(config, { name: 'stex', inMathMode: true });

const inlineKatexOpenRE = /(?<!\S)\$(?=[^\s$].*?[^\\\s$]\$(?!\S))/;
const inlineKatexCloseRE = /(?<![\\\s$])\$(?!\S)/;
const blockKatexRE = /(?<!\\)\$\$/;

// Find token will search for a valid katex start or end token
// If found then it will return the index, otherwise -1
function findToken(stream: any, token: RegExp) {
const match = token.exec(stream.string.slice(stream.pos));

return match ? match.index + stream.pos : -1;
}

return {
startState: function(): {outer: any, openCharacter: string, inner: any} {
return {
outer: CodeMirror.startState(markdownMode),
openCharacter: '',
inner: CodeMirror.startState(stex),
};
},

copyState: function(state: any) {
return {
outer: CodeMirror.copyState(markdownMode, state.outer),
openCharacter: state.openCharacter,
inner: CodeMirror.copyState(stex, state.inner),
};
},

token: function(stream: any, state: any) {
let currentMode = markdownMode;
let currentState = state.outer;
let tokenLabel = 'katex-marker-open';
let nextTokenPos = stream.string.length;
let closing = false;

const blockPos = findToken(stream, blockKatexRE);

if (state.openCharacter) {
currentMode = stex;
currentState = state.inner;
tokenLabel = 'katex-marker-close';
closing = true;

const inlinePos = findToken(stream, inlineKatexCloseRE);

if (state.openCharacter === '$$' && blockPos !== -1) nextTokenPos = blockPos;
if (state.openCharacter === '$' && inlinePos !== -1) nextTokenPos = inlinePos;
} else {
const inlinePos = findToken(stream, inlineKatexOpenRE);

if (blockPos !== -1) nextTokenPos = blockPos;
if (inlinePos !== -1 && inlinePos < nextTokenPos) nextTokenPos = inlinePos;

if (blockPos === stream.pos) state.openCharacter = '$$';
if (inlinePos === stream.pos) state.openCharacter = '$';
}

if (nextTokenPos === stream.pos) {
stream.match(state.openCharacter);

if (closing) state.openCharacter = '';

return tokenLabel;
}

// If we found a token in this stream but haven;t reached it yet, then we will
// pass all the characters leading up to our token to markdown mode
const oldString = stream.string;

stream.string = oldString.slice(0, nextTokenPos);
const token = currentMode.token(stream, currentState);
stream.string = oldString;

return token;
},

indent: function(state: any, textAfter: string, line: any) {
const mode = state.openCharacter ? stex : markdownMode;
if (!mode.indent) return CodeMirror.Pass;
return mode.indent(state.openCharacter ? state.inner : state.outer, textAfter, line);
},

blankLine: function(state: any) {
const mode = state.openCharacter ? stex : markdownMode;
if (mode.blankLine) {
mode.blankLine(state.openCharacter ? state.inner : state.outer);
}
},

electricChars: markdownMode.electricChars,

innerMode: function(state: any) {
return state.openCharacter ? { state: state.inner, mode: stex } : { state: state.outer, mode: markdownMode };
},

};
});
}
2 changes: 1 addition & 1 deletion ElectronClient/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@
"base64-stream": "^1.0.0",
"chokidar": "^3.0.0",
"clean-html": "^1.5.0",
"codemirror": "^5.54.0",
"codemirror": "^5.56.0",
"color": "^3.1.2",
"compare-versions": "^3.2.1",
"countable": "^3.0.1",
Expand Down

0 comments on commit 7f73931

Please sign in to comment.