elm.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. // CodeMirror, copyright (c) by Marijn Haverbeke and others
  2. // Distributed under an MIT license: https://codemirror.net/LICENSE
  3. (function(mod) {
  4. if (typeof exports == "object" && typeof module == "object") // CommonJS
  5. mod(require("../../lib/codemirror"));
  6. else if (typeof define == "function" && define.amd) // AMD
  7. define(["../../lib/codemirror"], mod);
  8. else // Plain browser env
  9. mod(CodeMirror);
  10. })(function(CodeMirror) {
  11. "use strict";
  12. CodeMirror.defineMode("elm", function() {
  13. function switchState(source, setState, f) {
  14. setState(f);
  15. return f(source, setState);
  16. }
  17. // These should all be Unicode extended, as per the Haskell 2010 report
  18. var smallRE = /[a-z_]/;
  19. var largeRE = /[A-Z]/;
  20. var digitRE = /[0-9]/;
  21. var hexitRE = /[0-9A-Fa-f]/;
  22. var octitRE = /[0-7]/;
  23. var idRE = /[a-z_A-Z0-9\']/;
  24. var symbolRE = /[-!#$%&*+.\/<=>?@\\^|~:\u03BB\u2192]/;
  25. var specialRE = /[(),;[\]`{}]/;
  26. var whiteCharRE = /[ \t\v\f]/; // newlines are handled in tokenizer
  27. function normal() {
  28. return function (source, setState) {
  29. if (source.eatWhile(whiteCharRE)) {
  30. return null;
  31. }
  32. var ch = source.next();
  33. if (specialRE.test(ch)) {
  34. if (ch == '{' && source.eat('-')) {
  35. var t = "comment";
  36. if (source.eat('#')) t = "meta";
  37. return switchState(source, setState, ncomment(t, 1));
  38. }
  39. return null;
  40. }
  41. if (ch == '\'') {
  42. if (source.eat('\\'))
  43. source.next(); // should handle other escapes here
  44. else
  45. source.next();
  46. if (source.eat('\''))
  47. return "string";
  48. return "error";
  49. }
  50. if (ch == '"') {
  51. return switchState(source, setState, stringLiteral);
  52. }
  53. if (largeRE.test(ch)) {
  54. source.eatWhile(idRE);
  55. if (source.eat('.'))
  56. return "qualifier";
  57. return "variable-2";
  58. }
  59. if (smallRE.test(ch)) {
  60. var isDef = source.pos === 1;
  61. source.eatWhile(idRE);
  62. return isDef ? "type" : "variable";
  63. }
  64. if (digitRE.test(ch)) {
  65. if (ch == '0') {
  66. if (source.eat(/[xX]/)) {
  67. source.eatWhile(hexitRE); // should require at least 1
  68. return "integer";
  69. }
  70. if (source.eat(/[oO]/)) {
  71. source.eatWhile(octitRE); // should require at least 1
  72. return "number";
  73. }
  74. }
  75. source.eatWhile(digitRE);
  76. var t = "number";
  77. if (source.eat('.')) {
  78. t = "number";
  79. source.eatWhile(digitRE); // should require at least 1
  80. }
  81. if (source.eat(/[eE]/)) {
  82. t = "number";
  83. source.eat(/[-+]/);
  84. source.eatWhile(digitRE); // should require at least 1
  85. }
  86. return t;
  87. }
  88. if (symbolRE.test(ch)) {
  89. if (ch == '-' && source.eat(/-/)) {
  90. source.eatWhile(/-/);
  91. if (!source.eat(symbolRE)) {
  92. source.skipToEnd();
  93. return "comment";
  94. }
  95. }
  96. source.eatWhile(symbolRE);
  97. return "builtin";
  98. }
  99. return "error";
  100. }
  101. }
  102. function ncomment(type, nest) {
  103. if (nest == 0) {
  104. return normal();
  105. }
  106. return function(source, setState) {
  107. var currNest = nest;
  108. while (!source.eol()) {
  109. var ch = source.next();
  110. if (ch == '{' && source.eat('-')) {
  111. ++currNest;
  112. } else if (ch == '-' && source.eat('}')) {
  113. --currNest;
  114. if (currNest == 0) {
  115. setState(normal());
  116. return type;
  117. }
  118. }
  119. }
  120. setState(ncomment(type, currNest));
  121. return type;
  122. }
  123. }
  124. function stringLiteral(source, setState) {
  125. while (!source.eol()) {
  126. var ch = source.next();
  127. if (ch == '"') {
  128. setState(normal());
  129. return "string";
  130. }
  131. if (ch == '\\') {
  132. if (source.eol() || source.eat(whiteCharRE)) {
  133. setState(stringGap);
  134. return "string";
  135. }
  136. if (!source.eat('&')) source.next(); // should handle other escapes here
  137. }
  138. }
  139. setState(normal());
  140. return "error";
  141. }
  142. function stringGap(source, setState) {
  143. if (source.eat('\\')) {
  144. return switchState(source, setState, stringLiteral);
  145. }
  146. source.next();
  147. setState(normal());
  148. return "error";
  149. }
  150. var wellKnownWords = (function() {
  151. var wkw = {};
  152. var keywords = [
  153. "case", "of", "as",
  154. "if", "then", "else",
  155. "let", "in",
  156. "infix", "infixl", "infixr",
  157. "type", "alias",
  158. "input", "output", "foreign", "loopback",
  159. "module", "where", "import", "exposing",
  160. "_", "..", "|", ":", "=", "\\", "\"", "->", "<-"
  161. ];
  162. for (var i = keywords.length; i--;)
  163. wkw[keywords[i]] = "keyword";
  164. return wkw;
  165. })();
  166. return {
  167. startState: function () { return { f: normal() }; },
  168. copyState: function (s) { return { f: s.f }; },
  169. token: function(stream, state) {
  170. var t = state.f(stream, function(s) { state.f = s; });
  171. var w = stream.current();
  172. return (wellKnownWords.hasOwnProperty(w)) ? wellKnownWords[w] : t;
  173. }
  174. };
  175. });
  176. CodeMirror.defineMIME("text/x-elm", "elm");
  177. });