crystal.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. /*
  2. Language: Crystal
  3. Author: TSUYUSATO Kitsune <make.just.on@gmail.com>
  4. Website: https://crystal-lang.org
  5. */
  6. /** @type LanguageFn */
  7. function crystal(hljs) {
  8. const INT_SUFFIX = '(_?[ui](8|16|32|64|128))?';
  9. const FLOAT_SUFFIX = '(_?f(32|64))?';
  10. const CRYSTAL_IDENT_RE = '[a-zA-Z_]\\w*[!?=]?';
  11. const CRYSTAL_METHOD_RE = '[a-zA-Z_]\\w*[!?=]?|[-+~]@|<<|>>|[=!]~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~|]|//|//=|&[-+*]=?|&\\*\\*|\\[\\][=?]?';
  12. const CRYSTAL_PATH_RE = '[A-Za-z_]\\w*(::\\w+)*(\\?|!)?';
  13. const CRYSTAL_KEYWORDS = {
  14. $pattern: CRYSTAL_IDENT_RE,
  15. keyword:
  16. 'abstract alias annotation as as? asm begin break case class def do else elsif end ensure enum extend for fun if '
  17. + 'include instance_sizeof is_a? lib macro module next nil? of out pointerof private protected rescue responds_to? '
  18. + 'return require select self sizeof struct super then type typeof union uninitialized unless until verbatim when while with yield '
  19. + '__DIR__ __END_LINE__ __FILE__ __LINE__',
  20. literal: 'false nil true'
  21. };
  22. const SUBST = {
  23. className: 'subst',
  24. begin: /#\{/,
  25. end: /\}/,
  26. keywords: CRYSTAL_KEYWORDS
  27. };
  28. // borrowed from Ruby
  29. const VARIABLE = {
  30. // negative-look forward attemps to prevent false matches like:
  31. // @ident@ or $ident$ that might indicate this is not ruby at all
  32. className: "variable",
  33. begin: '(\\$\\W)|((\\$|@@?)(\\w+))(?=[^@$?])' + `(?![A-Za-z])(?![@$?'])`
  34. };
  35. const EXPANSION = {
  36. className: 'template-variable',
  37. variants: [
  38. {
  39. begin: '\\{\\{',
  40. end: '\\}\\}'
  41. },
  42. {
  43. begin: '\\{%',
  44. end: '%\\}'
  45. }
  46. ],
  47. keywords: CRYSTAL_KEYWORDS
  48. };
  49. function recursiveParen(begin, end) {
  50. const
  51. contains = [
  52. {
  53. begin: begin,
  54. end: end
  55. }
  56. ];
  57. contains[0].contains = contains;
  58. return contains;
  59. }
  60. const STRING = {
  61. className: 'string',
  62. contains: [
  63. hljs.BACKSLASH_ESCAPE,
  64. SUBST
  65. ],
  66. variants: [
  67. {
  68. begin: /'/,
  69. end: /'/
  70. },
  71. {
  72. begin: /"/,
  73. end: /"/
  74. },
  75. {
  76. begin: /`/,
  77. end: /`/
  78. },
  79. {
  80. begin: '%[Qwi]?\\(',
  81. end: '\\)',
  82. contains: recursiveParen('\\(', '\\)')
  83. },
  84. {
  85. begin: '%[Qwi]?\\[',
  86. end: '\\]',
  87. contains: recursiveParen('\\[', '\\]')
  88. },
  89. {
  90. begin: '%[Qwi]?\\{',
  91. end: /\}/,
  92. contains: recursiveParen(/\{/, /\}/)
  93. },
  94. {
  95. begin: '%[Qwi]?<',
  96. end: '>',
  97. contains: recursiveParen('<', '>')
  98. },
  99. {
  100. begin: '%[Qwi]?\\|',
  101. end: '\\|'
  102. },
  103. {
  104. begin: /<<-\w+$/,
  105. end: /^\s*\w+$/
  106. }
  107. ],
  108. relevance: 0
  109. };
  110. const Q_STRING = {
  111. className: 'string',
  112. variants: [
  113. {
  114. begin: '%q\\(',
  115. end: '\\)',
  116. contains: recursiveParen('\\(', '\\)')
  117. },
  118. {
  119. begin: '%q\\[',
  120. end: '\\]',
  121. contains: recursiveParen('\\[', '\\]')
  122. },
  123. {
  124. begin: '%q\\{',
  125. end: /\}/,
  126. contains: recursiveParen(/\{/, /\}/)
  127. },
  128. {
  129. begin: '%q<',
  130. end: '>',
  131. contains: recursiveParen('<', '>')
  132. },
  133. {
  134. begin: '%q\\|',
  135. end: '\\|'
  136. },
  137. {
  138. begin: /<<-'\w+'$/,
  139. end: /^\s*\w+$/
  140. }
  141. ],
  142. relevance: 0
  143. };
  144. const REGEXP = {
  145. begin: '(?!%\\})(' + hljs.RE_STARTERS_RE + '|\\n|\\b(case|if|select|unless|until|when|while)\\b)\\s*',
  146. keywords: 'case if select unless until when while',
  147. contains: [
  148. {
  149. className: 'regexp',
  150. contains: [
  151. hljs.BACKSLASH_ESCAPE,
  152. SUBST
  153. ],
  154. variants: [
  155. {
  156. begin: '//[a-z]*',
  157. relevance: 0
  158. },
  159. {
  160. begin: '/(?!\\/)',
  161. end: '/[a-z]*'
  162. }
  163. ]
  164. }
  165. ],
  166. relevance: 0
  167. };
  168. const REGEXP2 = {
  169. className: 'regexp',
  170. contains: [
  171. hljs.BACKSLASH_ESCAPE,
  172. SUBST
  173. ],
  174. variants: [
  175. {
  176. begin: '%r\\(',
  177. end: '\\)',
  178. contains: recursiveParen('\\(', '\\)')
  179. },
  180. {
  181. begin: '%r\\[',
  182. end: '\\]',
  183. contains: recursiveParen('\\[', '\\]')
  184. },
  185. {
  186. begin: '%r\\{',
  187. end: /\}/,
  188. contains: recursiveParen(/\{/, /\}/)
  189. },
  190. {
  191. begin: '%r<',
  192. end: '>',
  193. contains: recursiveParen('<', '>')
  194. },
  195. {
  196. begin: '%r\\|',
  197. end: '\\|'
  198. }
  199. ],
  200. relevance: 0
  201. };
  202. const ATTRIBUTE = {
  203. className: 'meta',
  204. begin: '@\\[',
  205. end: '\\]',
  206. contains: [ hljs.inherit(hljs.QUOTE_STRING_MODE, { className: 'string' }) ]
  207. };
  208. const CRYSTAL_DEFAULT_CONTAINS = [
  209. EXPANSION,
  210. STRING,
  211. Q_STRING,
  212. REGEXP2,
  213. REGEXP,
  214. ATTRIBUTE,
  215. VARIABLE,
  216. hljs.HASH_COMMENT_MODE,
  217. {
  218. className: 'class',
  219. beginKeywords: 'class module struct',
  220. end: '$|;',
  221. illegal: /=/,
  222. contains: [
  223. hljs.HASH_COMMENT_MODE,
  224. hljs.inherit(hljs.TITLE_MODE, { begin: CRYSTAL_PATH_RE }),
  225. { // relevance booster for inheritance
  226. begin: '<' }
  227. ]
  228. },
  229. {
  230. className: 'class',
  231. beginKeywords: 'lib enum union',
  232. end: '$|;',
  233. illegal: /=/,
  234. contains: [
  235. hljs.HASH_COMMENT_MODE,
  236. hljs.inherit(hljs.TITLE_MODE, { begin: CRYSTAL_PATH_RE })
  237. ]
  238. },
  239. {
  240. beginKeywords: 'annotation',
  241. end: '$|;',
  242. illegal: /=/,
  243. contains: [
  244. hljs.HASH_COMMENT_MODE,
  245. hljs.inherit(hljs.TITLE_MODE, { begin: CRYSTAL_PATH_RE })
  246. ],
  247. relevance: 2
  248. },
  249. {
  250. className: 'function',
  251. beginKeywords: 'def',
  252. end: /\B\b/,
  253. contains: [
  254. hljs.inherit(hljs.TITLE_MODE, {
  255. begin: CRYSTAL_METHOD_RE,
  256. endsParent: true
  257. })
  258. ]
  259. },
  260. {
  261. className: 'function',
  262. beginKeywords: 'fun macro',
  263. end: /\B\b/,
  264. contains: [
  265. hljs.inherit(hljs.TITLE_MODE, {
  266. begin: CRYSTAL_METHOD_RE,
  267. endsParent: true
  268. })
  269. ],
  270. relevance: 2
  271. },
  272. {
  273. className: 'symbol',
  274. begin: hljs.UNDERSCORE_IDENT_RE + '(!|\\?)?:',
  275. relevance: 0
  276. },
  277. {
  278. className: 'symbol',
  279. begin: ':',
  280. contains: [
  281. STRING,
  282. { begin: CRYSTAL_METHOD_RE }
  283. ],
  284. relevance: 0
  285. },
  286. {
  287. className: 'number',
  288. variants: [
  289. { begin: '\\b0b([01_]+)' + INT_SUFFIX },
  290. { begin: '\\b0o([0-7_]+)' + INT_SUFFIX },
  291. { begin: '\\b0x([A-Fa-f0-9_]+)' + INT_SUFFIX },
  292. { begin: '\\b([1-9][0-9_]*[0-9]|[0-9])(\\.[0-9][0-9_]*)?([eE]_?[-+]?[0-9_]*)?' + FLOAT_SUFFIX + '(?!_)' },
  293. { begin: '\\b([1-9][0-9_]*|0)' + INT_SUFFIX }
  294. ],
  295. relevance: 0
  296. }
  297. ];
  298. SUBST.contains = CRYSTAL_DEFAULT_CONTAINS;
  299. EXPANSION.contains = CRYSTAL_DEFAULT_CONTAINS.slice(1); // without EXPANSION
  300. return {
  301. name: 'Crystal',
  302. aliases: [ 'cr' ],
  303. keywords: CRYSTAL_KEYWORDS,
  304. contains: CRYSTAL_DEFAULT_CONTAINS
  305. };
  306. }
  307. export { crystal as default };