stex.js
264 lines
| 7.6 KiB
| application/javascript
|
JavascriptLexer
r1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others | |||
r4105 | // Distributed under an MIT license: https://codemirror.net/LICENSE | |||
r1 | ||||
/* | ||||
* Author: Constantin Jucovschi (c.jucovschi@jacobs-university.de) | ||||
* Licence: MIT | ||||
*/ | ||||
(function(mod) { | ||||
if (typeof exports == "object" && typeof module == "object") // CommonJS | ||||
mod(require("../../lib/codemirror")); | ||||
else if (typeof define == "function" && define.amd) // AMD | ||||
define(["../../lib/codemirror"], mod); | ||||
else // Plain browser env | ||||
mod(CodeMirror); | ||||
})(function(CodeMirror) { | ||||
"use strict"; | ||||
r4105 | CodeMirror.defineMode("stex", function(_config, parserConfig) { | |||
r1 | "use strict"; | |||
function pushCommand(state, command) { | ||||
state.cmdState.push(command); | ||||
} | ||||
function peekCommand(state) { | ||||
if (state.cmdState.length > 0) { | ||||
return state.cmdState[state.cmdState.length - 1]; | ||||
} else { | ||||
return null; | ||||
} | ||||
} | ||||
function popCommand(state) { | ||||
var plug = state.cmdState.pop(); | ||||
if (plug) { | ||||
plug.closeBracket(); | ||||
} | ||||
} | ||||
// returns the non-default plugin closest to the end of the list | ||||
function getMostPowerful(state) { | ||||
var context = state.cmdState; | ||||
for (var i = context.length - 1; i >= 0; i--) { | ||||
var plug = context[i]; | ||||
if (plug.name == "DEFAULT") { | ||||
continue; | ||||
} | ||||
return plug; | ||||
} | ||||
return { styleIdentifier: function() { return null; } }; | ||||
} | ||||
function addPluginPattern(pluginName, cmdStyle, styles) { | ||||
return function () { | ||||
this.name = pluginName; | ||||
this.bracketNo = 0; | ||||
this.style = cmdStyle; | ||||
this.styles = styles; | ||||
this.argument = null; // \begin and \end have arguments that follow. These are stored in the plugin | ||||
this.styleIdentifier = function() { | ||||
return this.styles[this.bracketNo - 1] || null; | ||||
}; | ||||
this.openBracket = function() { | ||||
this.bracketNo++; | ||||
return "bracket"; | ||||
}; | ||||
this.closeBracket = function() {}; | ||||
}; | ||||
} | ||||
var plugins = {}; | ||||
plugins["importmodule"] = addPluginPattern("importmodule", "tag", ["string", "builtin"]); | ||||
plugins["documentclass"] = addPluginPattern("documentclass", "tag", ["", "atom"]); | ||||
plugins["usepackage"] = addPluginPattern("usepackage", "tag", ["atom"]); | ||||
plugins["begin"] = addPluginPattern("begin", "tag", ["atom"]); | ||||
plugins["end"] = addPluginPattern("end", "tag", ["atom"]); | ||||
r4105 | plugins["label" ] = addPluginPattern("label" , "tag", ["atom"]); | |||
plugins["ref" ] = addPluginPattern("ref" , "tag", ["atom"]); | ||||
plugins["eqref" ] = addPluginPattern("eqref" , "tag", ["atom"]); | ||||
plugins["cite" ] = addPluginPattern("cite" , "tag", ["atom"]); | ||||
plugins["bibitem" ] = addPluginPattern("bibitem" , "tag", ["atom"]); | ||||
plugins["Bibitem" ] = addPluginPattern("Bibitem" , "tag", ["atom"]); | ||||
plugins["RBibitem" ] = addPluginPattern("RBibitem" , "tag", ["atom"]); | ||||
r1 | plugins["DEFAULT"] = function () { | |||
this.name = "DEFAULT"; | ||||
this.style = "tag"; | ||||
this.styleIdentifier = this.openBracket = this.closeBracket = function() {}; | ||||
}; | ||||
function setState(state, f) { | ||||
state.f = f; | ||||
} | ||||
// called when in a normal (no environment) context | ||||
function normal(source, state) { | ||||
var plug; | ||||
// Do we look like '\command' ? If so, attempt to apply the plugin 'command' | ||||
if (source.match(/^\\[a-zA-Z@]+/)) { | ||||
var cmdName = source.current().slice(1); | ||||
plug = plugins[cmdName] || plugins["DEFAULT"]; | ||||
plug = new plug(); | ||||
pushCommand(state, plug); | ||||
setState(state, beginParams); | ||||
return plug.style; | ||||
} | ||||
// escape characters | ||||
if (source.match(/^\\[$&%#{}_]/)) { | ||||
return "tag"; | ||||
} | ||||
// white space control characters | ||||
if (source.match(/^\\[,;!\/\\]/)) { | ||||
return "tag"; | ||||
} | ||||
// find if we're starting various math modes | ||||
if (source.match("\\[")) { | ||||
setState(state, function(source, state){ return inMathMode(source, state, "\\]"); }); | ||||
return "keyword"; | ||||
} | ||||
r4105 | if (source.match("\\(")) { | |||
setState(state, function(source, state){ return inMathMode(source, state, "\\)"); }); | ||||
return "keyword"; | ||||
} | ||||
r1 | if (source.match("$$")) { | |||
setState(state, function(source, state){ return inMathMode(source, state, "$$"); }); | ||||
return "keyword"; | ||||
} | ||||
if (source.match("$")) { | ||||
setState(state, function(source, state){ return inMathMode(source, state, "$"); }); | ||||
return "keyword"; | ||||
} | ||||
var ch = source.next(); | ||||
if (ch == "%") { | ||||
source.skipToEnd(); | ||||
return "comment"; | ||||
} else if (ch == '}' || ch == ']') { | ||||
plug = peekCommand(state); | ||||
if (plug) { | ||||
plug.closeBracket(ch); | ||||
setState(state, beginParams); | ||||
} else { | ||||
return "error"; | ||||
} | ||||
return "bracket"; | ||||
} else if (ch == '{' || ch == '[') { | ||||
plug = plugins["DEFAULT"]; | ||||
plug = new plug(); | ||||
pushCommand(state, plug); | ||||
return "bracket"; | ||||
} else if (/\d/.test(ch)) { | ||||
source.eatWhile(/[\w.%]/); | ||||
return "atom"; | ||||
} else { | ||||
source.eatWhile(/[\w\-_]/); | ||||
plug = getMostPowerful(state); | ||||
if (plug.name == 'begin') { | ||||
plug.argument = source.current(); | ||||
} | ||||
return plug.styleIdentifier(); | ||||
} | ||||
} | ||||
function inMathMode(source, state, endModeSeq) { | ||||
if (source.eatSpace()) { | ||||
return null; | ||||
} | ||||
r4105 | if (endModeSeq && source.match(endModeSeq)) { | |||
r1 | setState(state, normal); | |||
return "keyword"; | ||||
} | ||||
if (source.match(/^\\[a-zA-Z@]+/)) { | ||||
return "tag"; | ||||
} | ||||
if (source.match(/^[a-zA-Z]+/)) { | ||||
return "variable-2"; | ||||
} | ||||
// escape characters | ||||
if (source.match(/^\\[$&%#{}_]/)) { | ||||
return "tag"; | ||||
} | ||||
// white space control characters | ||||
if (source.match(/^\\[,;!\/]/)) { | ||||
return "tag"; | ||||
} | ||||
// special math-mode characters | ||||
if (source.match(/^[\^_&]/)) { | ||||
return "tag"; | ||||
} | ||||
// non-special characters | ||||
if (source.match(/^[+\-<>|=,\/@!*:;'"`~#?]/)) { | ||||
return null; | ||||
} | ||||
if (source.match(/^(\d+\.\d*|\d*\.\d+|\d+)/)) { | ||||
return "number"; | ||||
} | ||||
var ch = source.next(); | ||||
if (ch == "{" || ch == "}" || ch == "[" || ch == "]" || ch == "(" || ch == ")") { | ||||
return "bracket"; | ||||
} | ||||
if (ch == "%") { | ||||
source.skipToEnd(); | ||||
return "comment"; | ||||
} | ||||
return "error"; | ||||
} | ||||
function beginParams(source, state) { | ||||
var ch = source.peek(), lastPlug; | ||||
if (ch == '{' || ch == '[') { | ||||
lastPlug = peekCommand(state); | ||||
lastPlug.openBracket(ch); | ||||
source.eat(ch); | ||||
setState(state, normal); | ||||
return "bracket"; | ||||
} | ||||
if (/[ \t\r]/.test(ch)) { | ||||
source.eat(ch); | ||||
return null; | ||||
} | ||||
setState(state, normal); | ||||
popCommand(state); | ||||
return normal(source, state); | ||||
} | ||||
return { | ||||
startState: function() { | ||||
r4105 | var f = parserConfig.inMathMode ? function(source, state){ return inMathMode(source, state); } : normal; | |||
r1 | return { | |||
cmdState: [], | ||||
r4105 | f: f | |||
r1 | }; | |||
}, | ||||
copyState: function(s) { | ||||
return { | ||||
cmdState: s.cmdState.slice(), | ||||
f: s.f | ||||
}; | ||||
}, | ||||
token: function(stream, state) { | ||||
return state.f(stream, state); | ||||
}, | ||||
blankLine: function(state) { | ||||
state.f = normal; | ||||
state.cmdState.length = 0; | ||||
}, | ||||
lineComment: "%" | ||||
}; | ||||
}); | ||||
CodeMirror.defineMIME("text/x-stex", "stex"); | ||||
CodeMirror.defineMIME("text/x-latex", "stex"); | ||||
}); | ||||