Theme Inspinia
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

stex.js 7.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. // CodeMirror, copyright (c) by Marijn Haverbeke and others
  2. // Distributed under an MIT license: http://codemirror.net/LICENSE
  3. /*
  4. * Author: Constantin Jucovschi (c.jucovschi@jacobs-university.de)
  5. * Licence: MIT
  6. */
  7. (function(mod) {
  8. if (typeof exports == "object" && typeof module == "object") // CommonJS
  9. mod(require("../../lib/codemirror"));
  10. else if (typeof define == "function" && define.amd) // AMD
  11. define(["../../lib/codemirror"], mod);
  12. else // Plain browser env
  13. mod(CodeMirror);
  14. })(function(CodeMirror) {
  15. "use strict";
  16. CodeMirror.defineMode("stex", function() {
  17. "use strict";
  18. function pushCommand(state, command) {
  19. state.cmdState.push(command);
  20. }
  21. function peekCommand(state) {
  22. if (state.cmdState.length > 0) {
  23. return state.cmdState[state.cmdState.length - 1];
  24. } else {
  25. return null;
  26. }
  27. }
  28. function popCommand(state) {
  29. var plug = state.cmdState.pop();
  30. if (plug) {
  31. plug.closeBracket();
  32. }
  33. }
  34. // returns the non-default plugin closest to the end of the list
  35. function getMostPowerful(state) {
  36. var context = state.cmdState;
  37. for (var i = context.length - 1; i >= 0; i--) {
  38. var plug = context[i];
  39. if (plug.name == "DEFAULT") {
  40. continue;
  41. }
  42. return plug;
  43. }
  44. return { styleIdentifier: function() { return null; } };
  45. }
  46. function addPluginPattern(pluginName, cmdStyle, styles) {
  47. return function () {
  48. this.name = pluginName;
  49. this.bracketNo = 0;
  50. this.style = cmdStyle;
  51. this.styles = styles;
  52. this.argument = null; // \begin and \end have arguments that follow. These are stored in the plugin
  53. this.styleIdentifier = function() {
  54. return this.styles[this.bracketNo - 1] || null;
  55. };
  56. this.openBracket = function() {
  57. this.bracketNo++;
  58. return "bracket";
  59. };
  60. this.closeBracket = function() {};
  61. };
  62. }
  63. var plugins = {};
  64. plugins["importmodule"] = addPluginPattern("importmodule", "tag", ["string", "builtin"]);
  65. plugins["documentclass"] = addPluginPattern("documentclass", "tag", ["", "atom"]);
  66. plugins["usepackage"] = addPluginPattern("usepackage", "tag", ["atom"]);
  67. plugins["begin"] = addPluginPattern("begin", "tag", ["atom"]);
  68. plugins["end"] = addPluginPattern("end", "tag", ["atom"]);
  69. plugins["DEFAULT"] = function () {
  70. this.name = "DEFAULT";
  71. this.style = "tag";
  72. this.styleIdentifier = this.openBracket = this.closeBracket = function() {};
  73. };
  74. function setState(state, f) {
  75. state.f = f;
  76. }
  77. // called when in a normal (no environment) context
  78. function normal(source, state) {
  79. var plug;
  80. // Do we look like '\command' ? If so, attempt to apply the plugin 'command'
  81. if (source.match(/^\\[a-zA-Z@]+/)) {
  82. var cmdName = source.current().slice(1);
  83. plug = plugins[cmdName] || plugins["DEFAULT"];
  84. plug = new plug();
  85. pushCommand(state, plug);
  86. setState(state, beginParams);
  87. return plug.style;
  88. }
  89. // escape characters
  90. if (source.match(/^\\[$&%#{}_]/)) {
  91. return "tag";
  92. }
  93. // white space control characters
  94. if (source.match(/^\\[,;!\/\\]/)) {
  95. return "tag";
  96. }
  97. // find if we're starting various math modes
  98. if (source.match("\\[")) {
  99. setState(state, function(source, state){ return inMathMode(source, state, "\\]"); });
  100. return "keyword";
  101. }
  102. if (source.match("$$")) {
  103. setState(state, function(source, state){ return inMathMode(source, state, "$$"); });
  104. return "keyword";
  105. }
  106. if (source.match("$")) {
  107. setState(state, function(source, state){ return inMathMode(source, state, "$"); });
  108. return "keyword";
  109. }
  110. var ch = source.next();
  111. if (ch == "%") {
  112. // special case: % at end of its own line; stay in same state
  113. if (!source.eol()) {
  114. setState(state, inCComment);
  115. }
  116. return "comment";
  117. }
  118. else if (ch == '}' || ch == ']') {
  119. plug = peekCommand(state);
  120. if (plug) {
  121. plug.closeBracket(ch);
  122. setState(state, beginParams);
  123. } else {
  124. return "error";
  125. }
  126. return "bracket";
  127. } else if (ch == '{' || ch == '[') {
  128. plug = plugins["DEFAULT"];
  129. plug = new plug();
  130. pushCommand(state, plug);
  131. return "bracket";
  132. }
  133. else if (/\d/.test(ch)) {
  134. source.eatWhile(/[\w.%]/);
  135. return "atom";
  136. }
  137. else {
  138. source.eatWhile(/[\w\-_]/);
  139. plug = getMostPowerful(state);
  140. if (plug.name == 'begin') {
  141. plug.argument = source.current();
  142. }
  143. return plug.styleIdentifier();
  144. }
  145. }
  146. function inCComment(source, state) {
  147. source.skipToEnd();
  148. setState(state, normal);
  149. return "comment";
  150. }
  151. function inMathMode(source, state, endModeSeq) {
  152. if (source.eatSpace()) {
  153. return null;
  154. }
  155. if (source.match(endModeSeq)) {
  156. setState(state, normal);
  157. return "keyword";
  158. }
  159. if (source.match(/^\\[a-zA-Z@]+/)) {
  160. return "tag";
  161. }
  162. if (source.match(/^[a-zA-Z]+/)) {
  163. return "variable-2";
  164. }
  165. // escape characters
  166. if (source.match(/^\\[$&%#{}_]/)) {
  167. return "tag";
  168. }
  169. // white space control characters
  170. if (source.match(/^\\[,;!\/]/)) {
  171. return "tag";
  172. }
  173. // special math-mode characters
  174. if (source.match(/^[\^_&]/)) {
  175. return "tag";
  176. }
  177. // non-special characters
  178. if (source.match(/^[+\-<>|=,\/@!*:;'"`~#?]/)) {
  179. return null;
  180. }
  181. if (source.match(/^(\d+\.\d*|\d*\.\d+|\d+)/)) {
  182. return "number";
  183. }
  184. var ch = source.next();
  185. if (ch == "{" || ch == "}" || ch == "[" || ch == "]" || ch == "(" || ch == ")") {
  186. return "bracket";
  187. }
  188. // eat comments here, because inCComment returns us to normal state!
  189. if (ch == "%") {
  190. if (!source.eol()) {
  191. source.skipToEnd();
  192. }
  193. return "comment";
  194. }
  195. return "error";
  196. }
  197. function beginParams(source, state) {
  198. var ch = source.peek(), lastPlug;
  199. if (ch == '{' || ch == '[') {
  200. lastPlug = peekCommand(state);
  201. lastPlug.openBracket(ch);
  202. source.eat(ch);
  203. setState(state, normal);
  204. return "bracket";
  205. }
  206. if (/[ \t\r]/.test(ch)) {
  207. source.eat(ch);
  208. return null;
  209. }
  210. setState(state, normal);
  211. popCommand(state);
  212. return normal(source, state);
  213. }
  214. return {
  215. startState: function() {
  216. return {
  217. cmdState: [],
  218. f: normal
  219. };
  220. },
  221. copyState: function(s) {
  222. return {
  223. cmdState: s.cmdState.slice(),
  224. f: s.f
  225. };
  226. },
  227. token: function(stream, state) {
  228. return state.f(stream, state);
  229. },
  230. lineComment: "%"
  231. };
  232. });
  233. CodeMirror.defineMIME("text/x-stex", "stex");
  234. CodeMirror.defineMIME("text/x-latex", "stex");
  235. });