0
|
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
|
|
2 // Distributed under an MIT license: https://codemirror.net/5/LICENSE
|
|
3
|
|
4 (function(mod) {
|
|
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
|
|
6 mod(require("../../lib/codemirror"));
|
|
7 else if (typeof define == "function" && define.amd) // AMD
|
|
8 define(["../../lib/codemirror"], mod);
|
|
9 else // Plain browser env
|
|
10 mod(CodeMirror);
|
|
11 })(function(CodeMirror) {
|
|
12 "use strict";
|
|
13
|
|
14 CodeMirror.defineMode("haskell", function(_config, modeConfig) {
|
|
15
|
|
16 function switchState(source, setState, f) {
|
|
17 setState(f);
|
|
18 return f(source, setState);
|
|
19 }
|
|
20
|
|
21 // These should all be Unicode extended, as per the Haskell 2010 report
|
|
22 var smallRE = /[a-z_]/;
|
|
23 var largeRE = /[A-Z]/;
|
|
24 var digitRE = /\d/;
|
|
25 var hexitRE = /[0-9A-Fa-f]/;
|
|
26 var octitRE = /[0-7]/;
|
|
27 var idRE = /[a-z_A-Z0-9'\xa1-\uffff]/;
|
|
28 var symbolRE = /[-!#$%&*+.\/<=>?@\\^|~:]/;
|
|
29 var specialRE = /[(),;[\]`{}]/;
|
|
30 var whiteCharRE = /[ \t\v\f]/; // newlines are handled in tokenizer
|
|
31
|
|
32 function normal(source, setState) {
|
|
33 if (source.eatWhile(whiteCharRE)) {
|
|
34 return null;
|
|
35 }
|
|
36
|
|
37 var ch = source.next();
|
|
38 if (specialRE.test(ch)) {
|
|
39 if (ch == '{' && source.eat('-')) {
|
|
40 var t = "comment";
|
|
41 if (source.eat('#')) {
|
|
42 t = "meta";
|
|
43 }
|
|
44 return switchState(source, setState, ncomment(t, 1));
|
|
45 }
|
|
46 return null;
|
|
47 }
|
|
48
|
|
49 if (ch == '\'') {
|
|
50 if (source.eat('\\')) {
|
|
51 source.next(); // should handle other escapes here
|
|
52 }
|
|
53 else {
|
|
54 source.next();
|
|
55 }
|
|
56 if (source.eat('\'')) {
|
|
57 return "string";
|
|
58 }
|
|
59 return "string error";
|
|
60 }
|
|
61
|
|
62 if (ch == '"') {
|
|
63 return switchState(source, setState, stringLiteral);
|
|
64 }
|
|
65
|
|
66 if (largeRE.test(ch)) {
|
|
67 source.eatWhile(idRE);
|
|
68 if (source.eat('.')) {
|
|
69 return "qualifier";
|
|
70 }
|
|
71 return "variable-2";
|
|
72 }
|
|
73
|
|
74 if (smallRE.test(ch)) {
|
|
75 source.eatWhile(idRE);
|
|
76 return "variable";
|
|
77 }
|
|
78
|
|
79 if (digitRE.test(ch)) {
|
|
80 if (ch == '0') {
|
|
81 if (source.eat(/[xX]/)) {
|
|
82 source.eatWhile(hexitRE); // should require at least 1
|
|
83 return "integer";
|
|
84 }
|
|
85 if (source.eat(/[oO]/)) {
|
|
86 source.eatWhile(octitRE); // should require at least 1
|
|
87 return "number";
|
|
88 }
|
|
89 }
|
|
90 source.eatWhile(digitRE);
|
|
91 var t = "number";
|
|
92 if (source.match(/^\.\d+/)) {
|
|
93 t = "number";
|
|
94 }
|
|
95 if (source.eat(/[eE]/)) {
|
|
96 t = "number";
|
|
97 source.eat(/[-+]/);
|
|
98 source.eatWhile(digitRE); // should require at least 1
|
|
99 }
|
|
100 return t;
|
|
101 }
|
|
102
|
|
103 if (ch == "." && source.eat("."))
|
|
104 return "keyword";
|
|
105
|
|
106 if (symbolRE.test(ch)) {
|
|
107 if (ch == '-' && source.eat(/-/)) {
|
|
108 source.eatWhile(/-/);
|
|
109 if (!source.eat(symbolRE)) {
|
|
110 source.skipToEnd();
|
|
111 return "comment";
|
|
112 }
|
|
113 }
|
|
114 var t = "variable";
|
|
115 if (ch == ':') {
|
|
116 t = "variable-2";
|
|
117 }
|
|
118 source.eatWhile(symbolRE);
|
|
119 return t;
|
|
120 }
|
|
121
|
|
122 return "error";
|
|
123 }
|
|
124
|
|
125 function ncomment(type, nest) {
|
|
126 if (nest == 0) {
|
|
127 return normal;
|
|
128 }
|
|
129 return function(source, setState) {
|
|
130 var currNest = nest;
|
|
131 while (!source.eol()) {
|
|
132 var ch = source.next();
|
|
133 if (ch == '{' && source.eat('-')) {
|
|
134 ++currNest;
|
|
135 }
|
|
136 else if (ch == '-' && source.eat('}')) {
|
|
137 --currNest;
|
|
138 if (currNest == 0) {
|
|
139 setState(normal);
|
|
140 return type;
|
|
141 }
|
|
142 }
|
|
143 }
|
|
144 setState(ncomment(type, currNest));
|
|
145 return type;
|
|
146 };
|
|
147 }
|
|
148
|
|
149 function stringLiteral(source, setState) {
|
|
150 while (!source.eol()) {
|
|
151 var ch = source.next();
|
|
152 if (ch == '"') {
|
|
153 setState(normal);
|
|
154 return "string";
|
|
155 }
|
|
156 if (ch == '\\') {
|
|
157 if (source.eol() || source.eat(whiteCharRE)) {
|
|
158 setState(stringGap);
|
|
159 return "string";
|
|
160 }
|
|
161 if (source.eat('&')) {
|
|
162 }
|
|
163 else {
|
|
164 source.next(); // should handle other escapes here
|
|
165 }
|
|
166 }
|
|
167 }
|
|
168 setState(normal);
|
|
169 return "string error";
|
|
170 }
|
|
171
|
|
172 function stringGap(source, setState) {
|
|
173 if (source.eat('\\')) {
|
|
174 return switchState(source, setState, stringLiteral);
|
|
175 }
|
|
176 source.next();
|
|
177 setState(normal);
|
|
178 return "error";
|
|
179 }
|
|
180
|
|
181
|
|
182 var wellKnownWords = (function() {
|
|
183 var wkw = {};
|
|
184 function setType(t) {
|
|
185 return function () {
|
|
186 for (var i = 0; i < arguments.length; i++)
|
|
187 wkw[arguments[i]] = t;
|
|
188 };
|
|
189 }
|
|
190
|
|
191 setType("keyword")(
|
|
192 "case", "class", "data", "default", "deriving", "do", "else", "foreign",
|
|
193 "if", "import", "in", "infix", "infixl", "infixr", "instance", "let",
|
|
194 "module", "newtype", "of", "then", "type", "where", "_");
|
|
195
|
|
196 setType("keyword")(
|
|
197 "\.\.", ":", "::", "=", "\\", "<-", "->", "@", "~", "=>");
|
|
198
|
|
199 setType("builtin")(
|
|
200 "!!", "$!", "$", "&&", "+", "++", "-", ".", "/", "/=", "<", "<*", "<=",
|
|
201 "<$>", "<*>", "=<<", "==", ">", ">=", ">>", ">>=", "^", "^^", "||", "*",
|
|
202 "*>", "**");
|
|
203
|
|
204 setType("builtin")(
|
|
205 "Applicative", "Bool", "Bounded", "Char", "Double", "EQ", "Either", "Enum",
|
|
206 "Eq", "False", "FilePath", "Float", "Floating", "Fractional", "Functor",
|
|
207 "GT", "IO", "IOError", "Int", "Integer", "Integral", "Just", "LT", "Left",
|
|
208 "Maybe", "Monad", "Nothing", "Num", "Ord", "Ordering", "Rational", "Read",
|
|
209 "ReadS", "Real", "RealFloat", "RealFrac", "Right", "Show", "ShowS",
|
|
210 "String", "True");
|
|
211
|
|
212 setType("builtin")(
|
|
213 "abs", "acos", "acosh", "all", "and", "any", "appendFile", "asTypeOf",
|
|
214 "asin", "asinh", "atan", "atan2", "atanh", "break", "catch", "ceiling",
|
|
215 "compare", "concat", "concatMap", "const", "cos", "cosh", "curry",
|
|
216 "cycle", "decodeFloat", "div", "divMod", "drop", "dropWhile", "either",
|
|
217 "elem", "encodeFloat", "enumFrom", "enumFromThen", "enumFromThenTo",
|
|
218 "enumFromTo", "error", "even", "exp", "exponent", "fail", "filter",
|
|
219 "flip", "floatDigits", "floatRadix", "floatRange", "floor", "fmap",
|
|
220 "foldl", "foldl1", "foldr", "foldr1", "fromEnum", "fromInteger",
|
|
221 "fromIntegral", "fromRational", "fst", "gcd", "getChar", "getContents",
|
|
222 "getLine", "head", "id", "init", "interact", "ioError", "isDenormalized",
|
|
223 "isIEEE", "isInfinite", "isNaN", "isNegativeZero", "iterate", "last",
|
|
224 "lcm", "length", "lex", "lines", "log", "logBase", "lookup", "map",
|
|
225 "mapM", "mapM_", "max", "maxBound", "maximum", "maybe", "min", "minBound",
|
|
226 "minimum", "mod", "negate", "not", "notElem", "null", "odd", "or",
|
|
227 "otherwise", "pi", "pred", "print", "product", "properFraction", "pure",
|
|
228 "putChar", "putStr", "putStrLn", "quot", "quotRem", "read", "readFile",
|
|
229 "readIO", "readList", "readLn", "readParen", "reads", "readsPrec",
|
|
230 "realToFrac", "recip", "rem", "repeat", "replicate", "return", "reverse",
|
|
231 "round", "scaleFloat", "scanl", "scanl1", "scanr", "scanr1", "seq",
|
|
232 "sequence", "sequence_", "show", "showChar", "showList", "showParen",
|
|
233 "showString", "shows", "showsPrec", "significand", "signum", "sin",
|
|
234 "sinh", "snd", "span", "splitAt", "sqrt", "subtract", "succ", "sum",
|
|
235 "tail", "take", "takeWhile", "tan", "tanh", "toEnum", "toInteger",
|
|
236 "toRational", "truncate", "uncurry", "undefined", "unlines", "until",
|
|
237 "unwords", "unzip", "unzip3", "userError", "words", "writeFile", "zip",
|
|
238 "zip3", "zipWith", "zipWith3");
|
|
239
|
|
240 var override = modeConfig.overrideKeywords;
|
|
241 if (override) for (var word in override) if (override.hasOwnProperty(word))
|
|
242 wkw[word] = override[word];
|
|
243
|
|
244 return wkw;
|
|
245 })();
|
|
246
|
|
247
|
|
248
|
|
249 return {
|
|
250 startState: function () { return { f: normal }; },
|
|
251 copyState: function (s) { return { f: s.f }; },
|
|
252
|
|
253 token: function(stream, state) {
|
|
254 var t = state.f(stream, function(s) { state.f = s; });
|
|
255 var w = stream.current();
|
|
256 return wellKnownWords.hasOwnProperty(w) ? wellKnownWords[w] : t;
|
|
257 },
|
|
258
|
|
259 blockCommentStart: "{-",
|
|
260 blockCommentEnd: "-}",
|
|
261 lineComment: "--"
|
|
262 };
|
|
263
|
|
264 });
|
|
265
|
|
266 CodeMirror.defineMIME("text/x-haskell", "haskell");
|
|
267
|
|
268 });
|