UNPKG

5.64 kBJavaScriptView Raw
1/*
2Language: Elixir
3Author: Josh Adams <josh@isotope11.com>
4Description: language definition for Elixir source code files (.ex and .exs). Based on ruby language support.
5Category: functional
6Website: https://elixir-lang.org
7*/
8
9/** @type LanguageFn */
10function elixir(hljs) {
11 const regex = hljs.regex;
12 const ELIXIR_IDENT_RE = '[a-zA-Z_][a-zA-Z0-9_.]*(!|\\?)?';
13 const ELIXIR_METHOD_RE = '[a-zA-Z_]\\w*[!?=]?|[-+~]@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?';
14 const KEYWORDS = [
15 "after",
16 "alias",
17 "and",
18 "case",
19 "catch",
20 "cond",
21 "defstruct",
22 "do",
23 "else",
24 "end",
25 "fn",
26 "for",
27 "if",
28 "import",
29 "in",
30 "not",
31 "or",
32 "quote",
33 "raise",
34 "receive",
35 "require",
36 "reraise",
37 "rescue",
38 "try",
39 "unless",
40 "unquote",
41 "unquote_splicing",
42 "use",
43 "when",
44 "with|0"
45 ];
46 const LITERALS = [
47 "false",
48 "nil",
49 "true"
50 ];
51 const KWS = {
52 $pattern: ELIXIR_IDENT_RE,
53 keyword: KEYWORDS,
54 literal: LITERALS
55 };
56 const SUBST = {
57 className: 'subst',
58 begin: /#\{/,
59 end: /\}/,
60 keywords: KWS
61 };
62 const NUMBER = {
63 className: 'number',
64 begin: '(\\b0o[0-7_]+)|(\\b0b[01_]+)|(\\b0x[0-9a-fA-F_]+)|(-?\\b[0-9][0-9_]*(\\.[0-9_]+([eE][-+]?[0-9]+)?)?)',
65 relevance: 0
66 };
67 // TODO: could be tightened
68 // https://elixir-lang.readthedocs.io/en/latest/intro/18.html
69 // but you also need to include closing delemeters in the escape list per
70 // individual sigil mode from what I can tell,
71 // ie: \} might or might not be an escape depending on the sigil used
72 const ESCAPES_RE = /\\[\s\S]/;
73 // const ESCAPES_RE = /\\["'\\abdefnrstv0]/;
74 const BACKSLASH_ESCAPE = {
75 match: ESCAPES_RE,
76 scope: "char.escape",
77 relevance: 0
78 };
79 const SIGIL_DELIMITERS = '[/|([{<"\']';
80 const SIGIL_DELIMITER_MODES = [
81 {
82 begin: /"/,
83 end: /"/
84 },
85 {
86 begin: /'/,
87 end: /'/
88 },
89 {
90 begin: /\//,
91 end: /\//
92 },
93 {
94 begin: /\|/,
95 end: /\|/
96 },
97 {
98 begin: /\(/,
99 end: /\)/
100 },
101 {
102 begin: /\[/,
103 end: /\]/
104 },
105 {
106 begin: /\{/,
107 end: /\}/
108 },
109 {
110 begin: /</,
111 end: />/
112 }
113 ];
114 const escapeSigilEnd = (end) => {
115 return {
116 scope: "char.escape",
117 begin: regex.concat(/\\/, end),
118 relevance: 0
119 };
120 };
121 const LOWERCASE_SIGIL = {
122 className: 'string',
123 begin: '~[a-z]' + '(?=' + SIGIL_DELIMITERS + ')',
124 contains: SIGIL_DELIMITER_MODES.map(x => hljs.inherit(x,
125 {
126 contains: [
127 escapeSigilEnd(x.end),
128 BACKSLASH_ESCAPE,
129 SUBST
130 ]
131 }
132 ))
133 };
134
135 const UPCASE_SIGIL = {
136 className: 'string',
137 begin: '~[A-Z]' + '(?=' + SIGIL_DELIMITERS + ')',
138 contains: SIGIL_DELIMITER_MODES.map(x => hljs.inherit(x,
139 {
140 contains: [ escapeSigilEnd(x.end) ]
141 }
142 ))
143 };
144
145 const REGEX_SIGIL = {
146 className: 'regex',
147 variants: [
148 {
149 begin: '~r' + '(?=' + SIGIL_DELIMITERS + ')',
150 contains: SIGIL_DELIMITER_MODES.map(x => hljs.inherit(x,
151 {
152 end: regex.concat(x.end, /[uismxfU]{0,7}/),
153 contains: [
154 escapeSigilEnd(x.end),
155 BACKSLASH_ESCAPE,
156 SUBST
157 ]
158 }
159 ))
160 },
161 {
162 begin: '~R' + '(?=' + SIGIL_DELIMITERS + ')',
163 contains: SIGIL_DELIMITER_MODES.map(x => hljs.inherit(x,
164 {
165 end: regex.concat(x.end, /[uismxfU]{0,7}/),
166 contains: [ escapeSigilEnd(x.end) ]
167 })
168 )
169 }
170 ]
171 };
172
173 const STRING = {
174 className: 'string',
175 contains: [
176 hljs.BACKSLASH_ESCAPE,
177 SUBST
178 ],
179 variants: [
180 {
181 begin: /"""/,
182 end: /"""/
183 },
184 {
185 begin: /'''/,
186 end: /'''/
187 },
188 {
189 begin: /~S"""/,
190 end: /"""/,
191 contains: [] // override default
192 },
193 {
194 begin: /~S"/,
195 end: /"/,
196 contains: [] // override default
197 },
198 {
199 begin: /~S'''/,
200 end: /'''/,
201 contains: [] // override default
202 },
203 {
204 begin: /~S'/,
205 end: /'/,
206 contains: [] // override default
207 },
208 {
209 begin: /'/,
210 end: /'/
211 },
212 {
213 begin: /"/,
214 end: /"/
215 }
216 ]
217 };
218 const FUNCTION = {
219 className: 'function',
220 beginKeywords: 'def defp defmacro defmacrop',
221 end: /\B\b/, // the mode is ended by the title
222 contains: [
223 hljs.inherit(hljs.TITLE_MODE, {
224 begin: ELIXIR_IDENT_RE,
225 endsParent: true
226 })
227 ]
228 };
229 const CLASS = hljs.inherit(FUNCTION, {
230 className: 'class',
231 beginKeywords: 'defimpl defmodule defprotocol defrecord',
232 end: /\bdo\b|$|;/
233 });
234 const ELIXIR_DEFAULT_CONTAINS = [
235 STRING,
236 REGEX_SIGIL,
237 UPCASE_SIGIL,
238 LOWERCASE_SIGIL,
239 hljs.HASH_COMMENT_MODE,
240 CLASS,
241 FUNCTION,
242 {
243 begin: '::'
244 },
245 {
246 className: 'symbol',
247 begin: ':(?![\\s:])',
248 contains: [
249 STRING,
250 {
251 begin: ELIXIR_METHOD_RE
252 }
253 ],
254 relevance: 0
255 },
256 {
257 className: 'symbol',
258 begin: ELIXIR_IDENT_RE + ':(?!:)',
259 relevance: 0
260 },
261 NUMBER,
262 {
263 className: 'variable',
264 begin: '(\\$\\W)|((\\$|@@?)(\\w+))'
265 },
266 {
267 begin: '->'
268 }
269 ];
270 SUBST.contains = ELIXIR_DEFAULT_CONTAINS;
271
272 return {
273 name: 'Elixir',
274 aliases: ['ex', 'exs'],
275 keywords: KWS,
276 contains: ELIXIR_DEFAULT_CONTAINS
277 };
278}
279
280export { elixir as default };