123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279 |
- /*
- Language: Elixir
- Author: Josh Adams <josh@isotope11.com>
- Description: language definition for Elixir source code files (.ex and .exs). Based on ruby language support.
- Category: functional
- Website: https://elixir-lang.org
- */
- /** @type LanguageFn */
- function elixir(hljs) {
- const regex = hljs.regex;
- const ELIXIR_IDENT_RE = '[a-zA-Z_][a-zA-Z0-9_.]*(!|\\?)?';
- const ELIXIR_METHOD_RE = '[a-zA-Z_]\\w*[!?=]?|[-+~]@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?';
- const KEYWORDS = [
- "after",
- "alias",
- "and",
- "case",
- "catch",
- "cond",
- "defstruct",
- "defguard",
- "do",
- "else",
- "end",
- "fn",
- "for",
- "if",
- "import",
- "in",
- "not",
- "or",
- "quote",
- "raise",
- "receive",
- "require",
- "reraise",
- "rescue",
- "try",
- "unless",
- "unquote",
- "unquote_splicing",
- "use",
- "when",
- "with|0"
- ];
- const LITERALS = [
- "false",
- "nil",
- "true"
- ];
- const KWS = {
- $pattern: ELIXIR_IDENT_RE,
- keyword: KEYWORDS,
- literal: LITERALS
- };
- const SUBST = {
- className: 'subst',
- begin: /#\{/,
- end: /\}/,
- keywords: KWS
- };
- const NUMBER = {
- className: 'number',
- begin: '(\\b0o[0-7_]+)|(\\b0b[01_]+)|(\\b0x[0-9a-fA-F_]+)|(-?\\b[0-9][0-9_]*(\\.[0-9_]+([eE][-+]?[0-9]+)?)?)',
- relevance: 0
- };
- // TODO: could be tightened
- // https://elixir-lang.readthedocs.io/en/latest/intro/18.html
- // but you also need to include closing delemeters in the escape list per
- // individual sigil mode from what I can tell,
- // ie: \} might or might not be an escape depending on the sigil used
- const ESCAPES_RE = /\\[\s\S]/;
- // const ESCAPES_RE = /\\["'\\abdefnrstv0]/;
- const BACKSLASH_ESCAPE = {
- match: ESCAPES_RE,
- scope: "char.escape",
- relevance: 0
- };
- const SIGIL_DELIMITERS = '[/|([{<"\']';
- const SIGIL_DELIMITER_MODES = [
- {
- begin: /"/,
- end: /"/
- },
- {
- begin: /'/,
- end: /'/
- },
- {
- begin: /\//,
- end: /\//
- },
- {
- begin: /\|/,
- end: /\|/
- },
- {
- begin: /\(/,
- end: /\)/
- },
- {
- begin: /\[/,
- end: /\]/
- },
- {
- begin: /\{/,
- end: /\}/
- },
- {
- begin: /</,
- end: />/
- }
- ];
- const escapeSigilEnd = (end) => {
- return {
- scope: "char.escape",
- begin: regex.concat(/\\/, end),
- relevance: 0
- };
- };
- const LOWERCASE_SIGIL = {
- className: 'string',
- begin: '~[a-z]' + '(?=' + SIGIL_DELIMITERS + ')',
- contains: SIGIL_DELIMITER_MODES.map(x => hljs.inherit(x,
- { contains: [
- escapeSigilEnd(x.end),
- BACKSLASH_ESCAPE,
- SUBST
- ] }
- ))
- };
- const UPCASE_SIGIL = {
- className: 'string',
- begin: '~[A-Z]' + '(?=' + SIGIL_DELIMITERS + ')',
- contains: SIGIL_DELIMITER_MODES.map(x => hljs.inherit(x,
- { contains: [ escapeSigilEnd(x.end) ] }
- ))
- };
- const REGEX_SIGIL = {
- className: 'regex',
- variants: [
- {
- begin: '~r' + '(?=' + SIGIL_DELIMITERS + ')',
- contains: SIGIL_DELIMITER_MODES.map(x => hljs.inherit(x,
- {
- end: regex.concat(x.end, /[uismxfU]{0,7}/),
- contains: [
- escapeSigilEnd(x.end),
- BACKSLASH_ESCAPE,
- SUBST
- ]
- }
- ))
- },
- {
- begin: '~R' + '(?=' + SIGIL_DELIMITERS + ')',
- contains: SIGIL_DELIMITER_MODES.map(x => hljs.inherit(x,
- {
- end: regex.concat(x.end, /[uismxfU]{0,7}/),
- contains: [ escapeSigilEnd(x.end) ]
- })
- )
- }
- ]
- };
- const STRING = {
- className: 'string',
- contains: [
- hljs.BACKSLASH_ESCAPE,
- SUBST
- ],
- variants: [
- {
- begin: /"""/,
- end: /"""/
- },
- {
- begin: /'''/,
- end: /'''/
- },
- {
- begin: /~S"""/,
- end: /"""/,
- contains: [] // override default
- },
- {
- begin: /~S"/,
- end: /"/,
- contains: [] // override default
- },
- {
- begin: /~S'''/,
- end: /'''/,
- contains: [] // override default
- },
- {
- begin: /~S'/,
- end: /'/,
- contains: [] // override default
- },
- {
- begin: /'/,
- end: /'/
- },
- {
- begin: /"/,
- end: /"/
- }
- ]
- };
- const FUNCTION = {
- className: 'function',
- beginKeywords: 'def defp defmacro defmacrop',
- end: /\B\b/, // the mode is ended by the title
- contains: [
- hljs.inherit(hljs.TITLE_MODE, {
- begin: ELIXIR_IDENT_RE,
- endsParent: true
- })
- ]
- };
- const CLASS = hljs.inherit(FUNCTION, {
- className: 'class',
- beginKeywords: 'defimpl defmodule defprotocol defrecord',
- end: /\bdo\b|$|;/
- });
- const ELIXIR_DEFAULT_CONTAINS = [
- STRING,
- REGEX_SIGIL,
- UPCASE_SIGIL,
- LOWERCASE_SIGIL,
- hljs.HASH_COMMENT_MODE,
- CLASS,
- FUNCTION,
- { begin: '::' },
- {
- className: 'symbol',
- begin: ':(?![\\s:])',
- contains: [
- STRING,
- { begin: ELIXIR_METHOD_RE }
- ],
- relevance: 0
- },
- {
- className: 'symbol',
- begin: ELIXIR_IDENT_RE + ':(?!:)',
- relevance: 0
- },
- { // Usage of a module, struct, etc.
- className: 'title.class',
- begin: /(\b[A-Z][a-zA-Z0-9_]+)/,
- relevance: 0
- },
- NUMBER,
- {
- className: 'variable',
- begin: '(\\$\\W)|((\\$|@@?)(\\w+))'
- }
- // -> has been removed, capnproto always uses this grammar construct
- ];
- SUBST.contains = ELIXIR_DEFAULT_CONTAINS;
- return {
- name: 'Elixir',
- aliases: [
- 'ex',
- 'exs'
- ],
- keywords: KWS,
- contains: ELIXIR_DEFAULT_CONTAINS
- };
- }
- export { elixir as default };
|