/* Language: Crystal Author: TSUYUSATO Kitsune Website: https://crystal-lang.org */ /** @type LanguageFn */ function crystal(hljs) { var INT_SUFFIX = '(_?[ui](8|16|32|64|128))?'; var FLOAT_SUFFIX = '(_?f(32|64))?'; var CRYSTAL_IDENT_RE = '[a-zA-Z_]\\w*[!?=]?'; var CRYSTAL_METHOD_RE = '[a-zA-Z_]\\w*[!?=]?|[-+~]@|<<|>>|[=!]~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~|]|//|//=|&[-+*]=?|&\\*\\*|\\[\\][=?]?'; var CRYSTAL_PATH_RE = '[A-Za-z_]\\w*(::\\w+)*(\\?|!)?'; var CRYSTAL_KEYWORDS = { $pattern: CRYSTAL_IDENT_RE, keyword: 'abstract alias annotation as as? asm begin break case class def do else elsif end ensure enum extend for fun if ' + 'include instance_sizeof is_a? lib macro module next nil? of out pointerof private protected rescue responds_to? ' + 'return require select self sizeof struct super then type typeof union uninitialized unless until verbatim when while with yield ' + '__DIR__ __END_LINE__ __FILE__ __LINE__', literal: 'false nil true' }; var SUBST = { className: 'subst', begin: /#\{/, end: /\}/, keywords: CRYSTAL_KEYWORDS }; var EXPANSION = { className: 'template-variable', variants: [ {begin: '\\{\\{', end: '\\}\\}'}, {begin: '\\{%', end: '%\\}'} ], keywords: CRYSTAL_KEYWORDS }; function recursiveParen(begin, end) { var contains = [{begin: begin, end: end}]; contains[0].contains = contains; return contains; } var STRING = { className: 'string', contains: [hljs.BACKSLASH_ESCAPE, SUBST], variants: [ {begin: /'/, end: /'/}, {begin: /"/, end: /"/}, {begin: /`/, end: /`/}, {begin: '%[Qwi]?\\(', end: '\\)', contains: recursiveParen('\\(', '\\)')}, {begin: '%[Qwi]?\\[', end: '\\]', contains: recursiveParen('\\[', '\\]')}, {begin: '%[Qwi]?\\{', end: /\}/, contains: recursiveParen(/\{/, /\}/)}, {begin: '%[Qwi]?<', end: '>', contains: recursiveParen('<', '>')}, {begin: '%[Qwi]?\\|', end: '\\|'}, {begin: /<<-\w+$/, end: /^\s*\w+$/}, ], relevance: 0, }; var Q_STRING = { className: 'string', variants: [ {begin: '%q\\(', end: '\\)', contains: recursiveParen('\\(', '\\)')}, {begin: '%q\\[', end: '\\]', contains: recursiveParen('\\[', '\\]')}, {begin: '%q\\{', end: /\}/, contains: recursiveParen(/\{/, /\}/)}, {begin: '%q<', end: '>', contains: recursiveParen('<', '>')}, {begin: '%q\\|', end: '\\|'}, {begin: /<<-'\w+'$/, end: /^\s*\w+$/}, ], relevance: 0, }; var REGEXP = { begin: '(?!%\\})(' + hljs.RE_STARTERS_RE + '|\\n|\\b(case|if|select|unless|until|when|while)\\b)\\s*', keywords: 'case if select unless until when while', contains: [ { className: 'regexp', contains: [hljs.BACKSLASH_ESCAPE, SUBST], variants: [ {begin: '//[a-z]*', relevance: 0}, {begin: '/(?!\\/)', end: '/[a-z]*'}, ] } ], relevance: 0 }; var REGEXP2 = { className: 'regexp', contains: [hljs.BACKSLASH_ESCAPE, SUBST], variants: [ {begin: '%r\\(', end: '\\)', contains: recursiveParen('\\(', '\\)')}, {begin: '%r\\[', end: '\\]', contains: recursiveParen('\\[', '\\]')}, {begin: '%r\\{', end: /\}/, contains: recursiveParen(/\{/, /\}/)}, {begin: '%r<', end: '>', contains: recursiveParen('<', '>')}, {begin: '%r\\|', end: '\\|'}, ], relevance: 0 }; var ATTRIBUTE = { className: 'meta', begin: '@\\[', end: '\\]', contains: [ hljs.inherit(hljs.QUOTE_STRING_MODE, {className: 'meta-string'}) ] }; var CRYSTAL_DEFAULT_CONTAINS = [ EXPANSION, STRING, Q_STRING, REGEXP2, REGEXP, ATTRIBUTE, hljs.HASH_COMMENT_MODE, { className: 'class', beginKeywords: 'class module struct', end: '$|;', illegal: /=/, contains: [ hljs.HASH_COMMENT_MODE, hljs.inherit(hljs.TITLE_MODE, {begin: CRYSTAL_PATH_RE}), {begin: '<'} // relevance booster for inheritance ] }, { className: 'class', beginKeywords: 'lib enum union', end: '$|;', illegal: /=/, contains: [ hljs.HASH_COMMENT_MODE, hljs.inherit(hljs.TITLE_MODE, {begin: CRYSTAL_PATH_RE}), ], relevance: 10 }, { beginKeywords: 'annotation', end: '$|;', illegal: /=/, contains: [ hljs.HASH_COMMENT_MODE, hljs.inherit(hljs.TITLE_MODE, {begin: CRYSTAL_PATH_RE}), ], relevance: 10 }, { className: 'function', beginKeywords: 'def', end: /\B\b/, contains: [ hljs.inherit(hljs.TITLE_MODE, { begin: CRYSTAL_METHOD_RE, endsParent: true }) ] }, { className: 'function', beginKeywords: 'fun macro', end: /\B\b/, contains: [ hljs.inherit(hljs.TITLE_MODE, { begin: CRYSTAL_METHOD_RE, endsParent: true }) ], relevance: 2 }, { className: 'symbol', begin: hljs.UNDERSCORE_IDENT_RE + '(!|\\?)?:', relevance: 0 }, { className: 'symbol', begin: ':', contains: [STRING, {begin: CRYSTAL_METHOD_RE}], relevance: 0 }, { className: 'number', variants: [ { begin: '\\b0b([01_]+)' + INT_SUFFIX }, { begin: '\\b0o([0-7_]+)' + INT_SUFFIX }, { begin: '\\b0x([A-Fa-f0-9_]+)' + INT_SUFFIX }, { begin: '\\b([1-9][0-9_]*[0-9]|[0-9])(\\.[0-9][0-9_]*)?([eE]_?[-+]?[0-9_]*)?' + FLOAT_SUFFIX + '(?!_)' }, { begin: '\\b([1-9][0-9_]*|0)' + INT_SUFFIX } ], relevance: 0 } ]; SUBST.contains = CRYSTAL_DEFAULT_CONTAINS; EXPANSION.contains = CRYSTAL_DEFAULT_CONTAINS.slice(1); // without EXPANSION return { name: 'Crystal', aliases: ['cr'], keywords: CRYSTAL_KEYWORDS, contains: CRYSTAL_DEFAULT_CONTAINS }; } module.exports = crystal;