crystal.js (5958B)
1 /* 2 Language: Crystal 3 Author: TSUYUSATO Kitsune <make.just.on@gmail.com> 4 Website: https://crystal-lang.org 5 */ 6 7 /** @type LanguageFn */ 8 function crystal(hljs) { 9 var INT_SUFFIX = '(_*[ui](8|16|32|64|128))?'; 10 var FLOAT_SUFFIX = '(_*f(32|64))?'; 11 var CRYSTAL_IDENT_RE = '[a-zA-Z_]\\w*[!?=]?'; 12 var CRYSTAL_METHOD_RE = '[a-zA-Z_]\\w*[!?=]?|[-+~]\\@|<<|>>|[=!]~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~|]|//|//=|&[-+*]=?|&\\*\\*|\\[\\][=?]?'; 13 var CRYSTAL_PATH_RE = '[A-Za-z_]\\w*(::\\w+)*(\\?|\\!)?'; 14 var 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 var SUBST = { 24 className: 'subst', 25 begin: '#{', end: '}', 26 keywords: CRYSTAL_KEYWORDS 27 }; 28 var EXPANSION = { 29 className: 'template-variable', 30 variants: [ 31 {begin: '\\{\\{', end: '\\}\\}'}, 32 {begin: '\\{%', end: '%\\}'} 33 ], 34 keywords: CRYSTAL_KEYWORDS 35 }; 36 37 function recursiveParen(begin, end) { 38 var 39 contains = [{begin: begin, end: end}]; 40 contains[0].contains = contains; 41 return contains; 42 } 43 var STRING = { 44 className: 'string', 45 contains: [hljs.BACKSLASH_ESCAPE, SUBST], 46 variants: [ 47 {begin: /'/, end: /'/}, 48 {begin: /"/, end: /"/}, 49 {begin: /`/, end: /`/}, 50 {begin: '%[Qwi]?\\(', end: '\\)', contains: recursiveParen('\\(', '\\)')}, 51 {begin: '%[Qwi]?\\[', end: '\\]', contains: recursiveParen('\\[', '\\]')}, 52 {begin: '%[Qwi]?{', end: '}', contains: recursiveParen('{', '}')}, 53 {begin: '%[Qwi]?<', end: '>', contains: recursiveParen('<', '>')}, 54 {begin: '%[Qwi]?\\|', end: '\\|'}, 55 {begin: /<<-\w+$/, end: /^\s*\w+$/}, 56 ], 57 relevance: 0, 58 }; 59 var Q_STRING = { 60 className: 'string', 61 variants: [ 62 {begin: '%q\\(', end: '\\)', contains: recursiveParen('\\(', '\\)')}, 63 {begin: '%q\\[', end: '\\]', contains: recursiveParen('\\[', '\\]')}, 64 {begin: '%q{', end: '}', contains: recursiveParen('{', '}')}, 65 {begin: '%q<', end: '>', contains: recursiveParen('<', '>')}, 66 {begin: '%q\\|', end: '\\|'}, 67 {begin: /<<-'\w+'$/, end: /^\s*\w+$/}, 68 ], 69 relevance: 0, 70 }; 71 var REGEXP = { 72 begin: '(?!%})(' + hljs.RE_STARTERS_RE + '|\\n|\\b(case|if|select|unless|until|when|while)\\b)\\s*', 73 keywords: 'case if select unless until when while', 74 contains: [ 75 { 76 className: 'regexp', 77 contains: [hljs.BACKSLASH_ESCAPE, SUBST], 78 variants: [ 79 {begin: '//[a-z]*', relevance: 0}, 80 {begin: '/(?!\\/)', end: '/[a-z]*'}, 81 ] 82 } 83 ], 84 relevance: 0 85 }; 86 var REGEXP2 = { 87 className: 'regexp', 88 contains: [hljs.BACKSLASH_ESCAPE, SUBST], 89 variants: [ 90 {begin: '%r\\(', end: '\\)', contains: recursiveParen('\\(', '\\)')}, 91 {begin: '%r\\[', end: '\\]', contains: recursiveParen('\\[', '\\]')}, 92 {begin: '%r{', end: '}', contains: recursiveParen('{', '}')}, 93 {begin: '%r<', end: '>', contains: recursiveParen('<', '>')}, 94 {begin: '%r\\|', end: '\\|'}, 95 ], 96 relevance: 0 97 }; 98 var ATTRIBUTE = { 99 className: 'meta', 100 begin: '@\\[', end: '\\]', 101 contains: [ 102 hljs.inherit(hljs.QUOTE_STRING_MODE, {className: 'meta-string'}) 103 ] 104 }; 105 var CRYSTAL_DEFAULT_CONTAINS = [ 106 EXPANSION, 107 STRING, 108 Q_STRING, 109 REGEXP2, 110 REGEXP, 111 ATTRIBUTE, 112 hljs.HASH_COMMENT_MODE, 113 { 114 className: 'class', 115 beginKeywords: 'class module struct', end: '$|;', 116 illegal: /=/, 117 contains: [ 118 hljs.HASH_COMMENT_MODE, 119 hljs.inherit(hljs.TITLE_MODE, {begin: CRYSTAL_PATH_RE}), 120 {begin: '<'} // relevance booster for inheritance 121 ] 122 }, 123 { 124 className: 'class', 125 beginKeywords: 'lib enum union', end: '$|;', 126 illegal: /=/, 127 contains: [ 128 hljs.HASH_COMMENT_MODE, 129 hljs.inherit(hljs.TITLE_MODE, {begin: CRYSTAL_PATH_RE}), 130 ], 131 relevance: 10 132 }, 133 { 134 beginKeywords: 'annotation', end: '$|;', 135 illegal: /=/, 136 contains: [ 137 hljs.HASH_COMMENT_MODE, 138 hljs.inherit(hljs.TITLE_MODE, {begin: CRYSTAL_PATH_RE}), 139 ], 140 relevance: 10 141 }, 142 { 143 className: 'function', 144 beginKeywords: 'def', end: /\B\b/, 145 contains: [ 146 hljs.inherit(hljs.TITLE_MODE, { 147 begin: CRYSTAL_METHOD_RE, 148 endsParent: true 149 }) 150 ] 151 }, 152 { 153 className: 'function', 154 beginKeywords: 'fun macro', end: /\B\b/, 155 contains: [ 156 hljs.inherit(hljs.TITLE_MODE, { 157 begin: CRYSTAL_METHOD_RE, 158 endsParent: true 159 }) 160 ], 161 relevance: 5 162 }, 163 { 164 className: 'symbol', 165 begin: hljs.UNDERSCORE_IDENT_RE + '(\\!|\\?)?:', 166 relevance: 0 167 }, 168 { 169 className: 'symbol', 170 begin: ':', 171 contains: [STRING, {begin: CRYSTAL_METHOD_RE}], 172 relevance: 0 173 }, 174 { 175 className: 'number', 176 variants: [ 177 { begin: '\\b0b([01_]+)' + INT_SUFFIX }, 178 { begin: '\\b0o([0-7_]+)' + INT_SUFFIX }, 179 { begin: '\\b0x([A-Fa-f0-9_]+)' + INT_SUFFIX }, 180 { begin: '\\b([1-9][0-9_]*[0-9]|[0-9])(\\.[0-9][0-9_]*)?([eE]_*[-+]?[0-9_]*)?' + FLOAT_SUFFIX + '(?!_)' }, 181 { begin: '\\b([1-9][0-9_]*|0)' + INT_SUFFIX } 182 ], 183 relevance: 0 184 } 185 ]; 186 SUBST.contains = CRYSTAL_DEFAULT_CONTAINS; 187 EXPANSION.contains = CRYSTAL_DEFAULT_CONTAINS.slice(1); // without EXPANSION 188 189 return { 190 name: 'Crystal', 191 aliases: ['cr'], 192 keywords: CRYSTAL_KEYWORDS, 193 contains: CRYSTAL_DEFAULT_CONTAINS 194 }; 195 } 196 197 module.exports = crystal;