elixir.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. /*
  2. Language: Elixir
  3. Author: Josh Adams <josh@isotope11.com>
  4. Description: language definition for Elixir source code files (.ex and .exs). Based on ruby language support.
  5. Category: functional
  6. Website: https://elixir-lang.org
  7. */
  8. /** @type LanguageFn */
  9. function elixir(hljs) {
  10. const ELIXIR_IDENT_RE = '[a-zA-Z_][a-zA-Z0-9_.]*(!|\\?)?';
  11. const ELIXIR_METHOD_RE = '[a-zA-Z_]\\w*[!?=]?|[-+~]@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?';
  12. const ELIXIR_KEYWORDS = {
  13. $pattern: ELIXIR_IDENT_RE,
  14. keyword: 'and false then defined module in return redo retry end for true self when ' +
  15. 'next until do begin unless nil break not case cond alias while ensure or ' +
  16. 'include use alias fn quote require import with|0'
  17. };
  18. const SUBST = {
  19. className: 'subst',
  20. begin: /#\{/,
  21. end: /\}/,
  22. keywords: ELIXIR_KEYWORDS
  23. };
  24. const NUMBER = {
  25. className: 'number',
  26. begin: '(\\b0o[0-7_]+)|(\\b0b[01_]+)|(\\b0x[0-9a-fA-F_]+)|(-?\\b[1-9][0-9_]*(\\.[0-9_]+([eE][-+]?[0-9]+)?)?)',
  27. relevance: 0
  28. };
  29. const SIGIL_DELIMITERS = '[/|([{<"\']';
  30. const LOWERCASE_SIGIL = {
  31. className: 'string',
  32. begin: '~[a-z]' + '(?=' + SIGIL_DELIMITERS + ')',
  33. contains: [
  34. {
  35. endsParent: true,
  36. contains: [
  37. {
  38. contains: [
  39. hljs.BACKSLASH_ESCAPE,
  40. SUBST
  41. ],
  42. variants: [
  43. {
  44. begin: /"/,
  45. end: /"/
  46. },
  47. {
  48. begin: /'/,
  49. end: /'/
  50. },
  51. {
  52. begin: /\//,
  53. end: /\//
  54. },
  55. {
  56. begin: /\|/,
  57. end: /\|/
  58. },
  59. {
  60. begin: /\(/,
  61. end: /\)/
  62. },
  63. {
  64. begin: /\[/,
  65. end: /\]/
  66. },
  67. {
  68. begin: /\{/,
  69. end: /\}/
  70. },
  71. {
  72. begin: /</,
  73. end: />/
  74. }
  75. ]
  76. }
  77. ]
  78. }
  79. ]
  80. };
  81. const UPCASE_SIGIL = {
  82. className: 'string',
  83. begin: '~[A-Z]' + '(?=' + SIGIL_DELIMITERS + ')',
  84. contains: [
  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. begin: /</,
  115. end: />/
  116. }
  117. ]
  118. };
  119. const STRING = {
  120. className: 'string',
  121. contains: [
  122. hljs.BACKSLASH_ESCAPE,
  123. SUBST
  124. ],
  125. variants: [
  126. {
  127. begin: /"""/,
  128. end: /"""/
  129. },
  130. {
  131. begin: /'''/,
  132. end: /'''/
  133. },
  134. {
  135. begin: /~S"""/,
  136. end: /"""/,
  137. contains: [] // override default
  138. },
  139. {
  140. begin: /~S"/,
  141. end: /"/,
  142. contains: [] // override default
  143. },
  144. {
  145. begin: /~S'''/,
  146. end: /'''/,
  147. contains: [] // override default
  148. },
  149. {
  150. begin: /~S'/,
  151. end: /'/,
  152. contains: [] // override default
  153. },
  154. {
  155. begin: /'/,
  156. end: /'/
  157. },
  158. {
  159. begin: /"/,
  160. end: /"/
  161. }
  162. ]
  163. };
  164. const FUNCTION = {
  165. className: 'function',
  166. beginKeywords: 'def defp defmacro',
  167. end: /\B\b/, // the mode is ended by the title
  168. contains: [
  169. hljs.inherit(hljs.TITLE_MODE, {
  170. begin: ELIXIR_IDENT_RE,
  171. endsParent: true
  172. })
  173. ]
  174. };
  175. const CLASS = hljs.inherit(FUNCTION, {
  176. className: 'class',
  177. beginKeywords: 'defimpl defmodule defprotocol defrecord',
  178. end: /\bdo\b|$|;/
  179. });
  180. const ELIXIR_DEFAULT_CONTAINS = [
  181. STRING,
  182. UPCASE_SIGIL,
  183. LOWERCASE_SIGIL,
  184. hljs.HASH_COMMENT_MODE,
  185. CLASS,
  186. FUNCTION,
  187. {
  188. begin: '::'
  189. },
  190. {
  191. className: 'symbol',
  192. begin: ':(?![\\s:])',
  193. contains: [
  194. STRING,
  195. {
  196. begin: ELIXIR_METHOD_RE
  197. }
  198. ],
  199. relevance: 0
  200. },
  201. {
  202. className: 'symbol',
  203. begin: ELIXIR_IDENT_RE + ':(?!:)',
  204. relevance: 0
  205. },
  206. NUMBER,
  207. {
  208. className: 'variable',
  209. begin: '(\\$\\W)|((\\$|@@?)(\\w+))'
  210. },
  211. {
  212. begin: '->'
  213. },
  214. { // regexp container
  215. begin: '(' + hljs.RE_STARTERS_RE + ')\\s*',
  216. contains: [
  217. hljs.HASH_COMMENT_MODE,
  218. {
  219. // to prevent false regex triggers for the division function:
  220. // /:
  221. begin: /\/: (?=\d+\s*[,\]])/,
  222. relevance: 0,
  223. contains: [NUMBER]
  224. },
  225. {
  226. className: 'regexp',
  227. illegal: '\\n',
  228. contains: [
  229. hljs.BACKSLASH_ESCAPE,
  230. SUBST
  231. ],
  232. variants: [
  233. {
  234. begin: '/',
  235. end: '/[a-z]*'
  236. },
  237. {
  238. begin: '%r\\[',
  239. end: '\\][a-z]*'
  240. }
  241. ]
  242. }
  243. ],
  244. relevance: 0
  245. }
  246. ];
  247. SUBST.contains = ELIXIR_DEFAULT_CONTAINS;
  248. return {
  249. name: 'Elixir',
  250. keywords: ELIXIR_KEYWORDS,
  251. contains: ELIXIR_DEFAULT_CONTAINS
  252. };
  253. }
  254. module.exports = elixir;