2 * Author: Constantin Jucovschi (c.jucovschi@jacobs-university.de)
6 CodeMirror.defineMode("stex", function() {
9 function pushCommand(state, command) {
10 state.cmdState.push(command);
13 function peekCommand(state) {
14 if (state.cmdState.length > 0) {
15 return state.cmdState[state.cmdState.length - 1];
21 function popCommand(state) {
22 var plug = state.cmdState.pop();
28 // returns the non-default plugin closest to the end of the list
29 function getMostPowerful(state) {
30 var context = state.cmdState;
31 for (var i = context.length - 1; i >= 0; i--) {
32 var plug = context[i];
33 if (plug.name == "DEFAULT") {
38 return { styleIdentifier: function() { return null; } };
41 function addPluginPattern(pluginName, cmdStyle, styles) {
43 this.name = pluginName;
45 this.style = cmdStyle;
47 this.argument = null; // \begin and \end have arguments that follow. These are stored in the plugin
49 this.styleIdentifier = function() {
50 return this.styles[this.bracketNo - 1] || null;
52 this.openBracket = function() {
56 this.closeBracket = function() {};
62 plugins["importmodule"] = addPluginPattern("importmodule", "tag", ["string", "builtin"]);
63 plugins["documentclass"] = addPluginPattern("documentclass", "tag", ["", "atom"]);
64 plugins["usepackage"] = addPluginPattern("usepackage", "tag", ["atom"]);
65 plugins["begin"] = addPluginPattern("begin", "tag", ["atom"]);
66 plugins["end"] = addPluginPattern("end", "tag", ["atom"]);
68 plugins["DEFAULT"] = function () {
69 this.name = "DEFAULT";
72 this.styleIdentifier = this.openBracket = this.closeBracket = function() {};
75 function setState(state, f) {
79 // called when in a normal (no environment) context
80 function normal(source, state) {
82 // Do we look like '\command' ? If so, attempt to apply the plugin 'command'
83 if (source.match(/^\\[a-zA-Z@]+/)) {
84 var cmdName = source.current().slice(1);
85 plug = plugins[cmdName] || plugins["DEFAULT"];
87 pushCommand(state, plug);
88 setState(state, beginParams);
93 if (source.match(/^\\[$&%#{}_]/)) {
97 // white space control characters
98 if (source.match(/^\\[,;!\/\\]/)) {
102 // find if we're starting various math modes
103 if (source.match("\\[")) {
104 setState(state, function(source, state){ return inMathMode(source, state, "\\]"); });
107 if (source.match("$$")) {
108 setState(state, function(source, state){ return inMathMode(source, state, "$$"); });
111 if (source.match("$")) {
112 setState(state, function(source, state){ return inMathMode(source, state, "$"); });
116 var ch = source.next();
118 // special case: % at end of its own line; stay in same state
120 setState(state, inCComment);
124 else if (ch == '}' || ch == ']') {
125 plug = peekCommand(state);
127 plug.closeBracket(ch);
128 setState(state, beginParams);
133 } else if (ch == '{' || ch == '[') {
134 plug = plugins["DEFAULT"];
136 pushCommand(state, plug);
139 else if (/\d/.test(ch)) {
140 source.eatWhile(/[\w.%]/);
144 source.eatWhile(/[\w\-_]/);
145 plug = getMostPowerful(state);
146 if (plug.name == 'begin') {
147 plug.argument = source.current();
149 return plug.styleIdentifier();
153 function inCComment(source, state) {
155 setState(state, normal);
159 function inMathMode(source, state, endModeSeq) {
160 if (source.eatSpace()) {
163 if (source.match(endModeSeq)) {
164 setState(state, normal);
167 if (source.match(/^\\[a-zA-Z@]+/)) {
170 if (source.match(/^[a-zA-Z]+/)) {
174 if (source.match(/^\\[$&%#{}_]/)) {
177 // white space control characters
178 if (source.match(/^\\[,;!\/]/)) {
181 // special math-mode characters
182 if (source.match(/^[\^_&]/)) {
185 // non-special characters
186 if (source.match(/^[+\-<>|=,\/@!*:;'"`~#?]/)) {
189 if (source.match(/^(\d+\.\d*|\d*\.\d+|\d+)/)) {
192 var ch = source.next();
193 if (ch == "{" || ch == "}" || ch == "[" || ch == "]" || ch == "(" || ch == ")") {
197 // eat comments here, because inCComment returns us to normal state!
207 function beginParams(source, state) {
208 var ch = source.peek(), lastPlug;
209 if (ch == '{' || ch == '[') {
210 lastPlug = peekCommand(state);
211 lastPlug.openBracket(ch);
213 setState(state, normal);
216 if (/[ \t\r]/.test(ch)) {
220 setState(state, normal);
223 return normal(source, state);
227 startState: function() {
233 copyState: function(s) {
235 cmdState: s.cmdState.slice(),
239 token: function(stream, state) {
240 return state.f(stream, state);
245 CodeMirror.defineMIME("text/x-stex", "stex");
246 CodeMirror.defineMIME("text/x-latex", "stex");