module.exports = function(hljs) { var NUM_SUFFIX = '(_[uif](8|16|32|64))?'; var CRYSTAL_IDENT_RE = '[a-zA-Z_]\\w*[!?=]?'; var RE_STARTER = '!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|' + '>>|>|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~'; var CRYSTAL_METHOD_RE = '[a-zA-Z_]\\w*[!?=]?|[-+~]\\@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\][=?]?'; var CRYSTAL_KEYWORDS = { keyword: 'abstract alias 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 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: '%w?\\(', end: '\\)', contains: recursiveParen('\\(', '\\)')}, {begin: '%w?\\[', end: '\\]', contains: recursiveParen('\\[', '\\]')}, {begin: '%w?{', end: '}', contains: recursiveParen('{', '}')}, {begin: '%w?<', end: '>', contains: recursiveParen('<', '>')}, {begin: '%w?/', end: '/'}, {begin: '%w?%', end: '%'}, {begin: '%w?-', end: '-'}, {begin: '%w?\\|', 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: '%q%', end: '%'}, {begin: '%q-', end: '-'}, {begin: '%q\\|', end: '\\|'}, {begin: /<<-'\w+'$/, end: /^\s*\w+$/}, ], relevance: 0, }; var REGEXP = { begin: '(' + RE_STARTER + ')\\s*', contains: [ { className: 'regexp', contains: [hljs.BACKSLASH_ESCAPE, SUBST], variants: [ {begin: '//[a-z]*', relevance: 0}, {begin: '/', end: '/[a-z]*'}, {begin: '%r\\(', end: '\\)', contains: recursiveParen('\\(', '\\)')}, {begin: '%r\\[', end: '\\]', contains: recursiveParen('\\[', '\\]')}, {begin: '%r{', end: '}', contains: recursiveParen('{', '}')}, {begin: '%r<', end: '>', contains: recursiveParen('<', '>')}, {begin: '%r/', end: '/'}, {begin: '%r%', end: '%'}, {begin: '%r-', end: '-'}, {begin: '%r\\|', end: '\\|'}, ] } ], 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: '/'}, {begin: '%r%', end: '%'}, {begin: '%r-', end: '-'}, {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, REGEXP, REGEXP2, ATTRIBUTE, hljs.HASH_COMMENT_MODE, { className: 'class', beginKeywords: 'class module struct', end: '$|;', illegal: /=/, contains: [ hljs.HASH_COMMENT_MODE, hljs.inherit(hljs.TITLE_MODE, {begin: '[A-Za-z_]\\w*(::\\w+)*(\\?|\\!)?'}), {begin: '<'} // relevance booster for inheritance ] }, { className: 'class', beginKeywords: 'lib enum union', end: '$|;', illegal: /=/, contains: [ hljs.HASH_COMMENT_MODE, hljs.inherit(hljs.TITLE_MODE, {begin: '[A-Za-z_]\\w*(::\\w+)*(\\?|\\!)?'}), ], 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: 5 }, { 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_]*[01])' + NUM_SUFFIX }, { begin: '\\b0o([0-7_]*[0-7])' + NUM_SUFFIX }, { begin: '\\b0x([A-Fa-f0-9_]*[A-Fa-f0-9])' + NUM_SUFFIX }, { begin: '\\b(([0-9][0-9_]*[0-9]|[0-9])(\\.[0-9_]*[0-9])?([eE][+-]?[0-9_]*[0-9])?)' + NUM_SUFFIX} ], relevance: 0 } ]; SUBST.contains = CRYSTAL_DEFAULT_CONTAINS; EXPANSION.contains = CRYSTAL_DEFAULT_CONTAINS.slice(1); // without EXPANSION return { aliases: ['cr'], lexemes: CRYSTAL_IDENT_RE, keywords: CRYSTAL_KEYWORDS, contains: CRYSTAL_DEFAULT_CONTAINS }; };