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