crystal.js 7.0 KB

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