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 })();