Permalink
Cannot retrieve contributors at this time
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
170 lines (150 sloc)
6.23 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* global CodeMirror $ define */ | |
| // This plugin renders MathJax parts in CodeMirror instances | |
| (function (mod) { | |
| if (typeof exports === 'object' && typeof module === 'object') { // CommonJS | |
| mod(require('../../../node_modules/codemirror/lib/codemirror')) | |
| } else if (typeof define === 'function' && define.amd) { // AMD | |
| define(['../../../node_modules/codemirror/lib/codemirror'], mod) | |
| } else { // Plain browser env | |
| mod(CodeMirror) | |
| } | |
| })(function (CodeMirror) { | |
| 'use strict' | |
| // Matches all inlines according to the Pandoc documentation | |
| // on its tex_math_dollars-extension. | |
| // More information: https://pandoc.org/MANUAL.html#math | |
| // First alternative is only for single-character-equations | |
| // such as $x$. All others are captured by the second alternative. | |
| var inlineMathRE = /(?<!\\)\${1,2}([^\s\\])\${1,2}(?!\d)|(?<!\\)\${1,2}([^\s].*?[^\s\\])\${1,2}(?!\d)/g | |
| var multilineMathRE = /^\s*\$\$\s*$/ | |
| var mathMarkers = [] | |
| var currentDocID = null | |
| CodeMirror.commands.markdownRenderMath = function (cm) { | |
| let i = 0 | |
| let match | |
| if (currentDocID !== cm.doc.id) { | |
| currentDocID = cm.doc.id | |
| for (let marker of mathMarkers) { | |
| if (marker.find()) marker.clear() | |
| } | |
| mathMarkers = [] // Flush it away! | |
| } | |
| // First remove iFrames that don't exist anymore. As soon as someone | |
| // moves the cursor into the link, it will be automatically removed, | |
| // as well as if someone simply deletes the whole line. | |
| do { | |
| if (!mathMarkers[i]) { | |
| continue | |
| } | |
| if (mathMarkers[i] && mathMarkers[i].find() === undefined) { | |
| // Marker is no longer present, so splice it | |
| mathMarkers.splice(i, 1) | |
| } else { | |
| i++ | |
| } | |
| } while (i < mathMarkers.length) | |
| // Now render all potential new Math elements | |
| let isMultiline = false // Are we inside a multiline? | |
| let eq = '' | |
| let fromLine = i | |
| for (let i = 0; i < cm.lineCount(); i++) { | |
| let modeName = cm.getModeAt({ 'line': i, 'ch': 0 }).name | |
| if (![ 'markdown', 'stex' ].includes(modeName)) continue | |
| if (modeName === 'stex') { | |
| // Make sure the token list includes "multiline-equation" | |
| // because otherwise we shouldn't render this as it's within | |
| // a default LaTeX code block, not an equation. | |
| let tokenType = cm.getTokenTypeAt({ 'line': i, 'ch': 0 }) | |
| let isMultilineBeginning = multilineMathRE.test(cm.getLine(i)) | |
| let isMultilineEquation = tokenType && tokenType.indexOf('multiline-equation') >= 0 | |
| if (!isMultilineBeginning && !isMultilineEquation) continue | |
| } | |
| // Reset the index of the expression everytime we enter a new line. | |
| inlineMathRE.lastIndex = 0 | |
| // This array holds all markers to be inserted (either one in case of the | |
| // final line of a multiline-equation or multiple in case of several | |
| // inline equations). | |
| let newMarkers = [] | |
| let line = cm.getLine(i) | |
| if (!isMultiline && multilineMathRE.test(line)) { | |
| isMultiline = true | |
| fromLine = i | |
| eq = '' | |
| } else if (isMultiline && !multilineMathRE.test(line)) { | |
| // Simply add the line to the equation and continue | |
| eq += line + '\n' | |
| continue | |
| } else if (isMultiline && multilineMathRE.test(line)) { | |
| // We have left the multiline equation and can render it now. | |
| isMultiline = false | |
| newMarkers.push({ | |
| 'curFrom': { 'ch': 0, 'line': fromLine }, | |
| 'curTo': { 'ch': 2, 'line': i }, | |
| 'eq': eq | |
| }) | |
| eq = '' // Reset the equation | |
| } else { | |
| // Else: No multiline. Search for inlines. | |
| while ((match = inlineMathRE.exec(line)) != null) { | |
| newMarkers.push({ | |
| 'curFrom': { 'ch': match.index, 'line': i }, | |
| 'curTo': { 'ch': match.index + match[0].length, 'line': i }, | |
| // An equation is stored in the first capturing group or | |
| // second, depending on whether there only was | |
| // one char, or multiple ones within the equation. | |
| 'eq': match[1] || match[2] || '' | |
| }) | |
| } | |
| } | |
| // Now cycle through all new markers and insert them, if they weren't | |
| // already | |
| for (let myMarker of newMarkers) { | |
| let cur = cm.getCursor('from') | |
| let isMulti = myMarker.curFrom.line !== myMarker.curTo.line | |
| if (isMulti && cur.line >= myMarker.curFrom.line && cur.line <= myMarker.curTo.line) { | |
| // We're directly in the multiline equation, so don't render. | |
| continue | |
| } else if (!isMulti && cur.line === myMarker.curFrom.line && cur.ch >= myMarker.curFrom.ch && cur.ch <= myMarker.curTo.ch) { | |
| // Again, we're right in the middle of an inline-equation, so don't render. | |
| continue | |
| } | |
| let isRendered = false | |
| let marks = cm.findMarks(myMarker.curFrom, myMarker.curTo) | |
| for (let marx of marks) { | |
| if (mathMarkers.includes(marx)) { | |
| isRendered = true | |
| break | |
| } | |
| } | |
| // Also in this case simply skip. | |
| if (isRendered) continue | |
| // Do not render if it's inside a comment (in this case the mode will be | |
| // markdown, but comments shouldn't be included in rendering) | |
| // Final check to avoid it for as long as possible, as getTokenAt takes | |
| // considerable time. | |
| if (cm.getTokenAt(myMarker.curFrom).type === 'comment' || | |
| cm.getTokenAt(myMarker.curTo).type === 'comment') { | |
| continue | |
| } | |
| // Use jQuery for simple creation of the DOM element | |
| let elem = $('<span class="preview-math"></span>')[0] | |
| let textMarker = cm.markText( | |
| myMarker.curFrom, myMarker.curTo, | |
| { | |
| 'clearOnEnter': true, | |
| 'replacedWith': elem, | |
| 'inclusiveLeft': false, | |
| 'inclusiveRight': false | |
| } | |
| ) | |
| // Enable on-click closing of rendered Math elements. | |
| elem.onclick = (e) => { textMarker.clear() } | |
| require('katex').render(myMarker.eq, elem, { throwOnError: false }) | |
| // Now the marker has obviously changed | |
| textMarker.changed() | |
| // Finally push the marker | |
| mathMarkers.push(textMarker) | |
| } // End for all markers | |
| } // End for lines | |
| } // End command | |
| }) |