| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326 | /*Language: CrystalAuthor: TSUYUSATO Kitsune <make.just.on@gmail.com>Website: https://crystal-lang.org*//** @type LanguageFn */function crystal(hljs) {  const INT_SUFFIX = '(_?[ui](8|16|32|64|128))?';  const FLOAT_SUFFIX = '(_?f(32|64))?';  const CRYSTAL_IDENT_RE = '[a-zA-Z_]\\w*[!?=]?';  const CRYSTAL_METHOD_RE = '[a-zA-Z_]\\w*[!?=]?|[-+~]@|<<|>>|[=!]~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~|]|//|//=|&[-+*]=?|&\\*\\*|\\[\\][=?]?';  const CRYSTAL_PATH_RE = '[A-Za-z_]\\w*(::\\w+)*(\\?|!)?';  const 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'  };  const SUBST = {    className: 'subst',    begin: /#\{/,    end: /\}/,    keywords: CRYSTAL_KEYWORDS  };  const EXPANSION = {    className: 'template-variable',    variants: [      {        begin: '\\{\\{',        end: '\\}\\}'      },      {        begin: '\\{%',        end: '%\\}'      }    ],    keywords: CRYSTAL_KEYWORDS  };  function recursiveParen(begin, end) {    const        contains = [          {            begin: begin,            end: end          }        ];    contains[0].contains = contains;    return contains;  }  const 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  };  const 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  };  const 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  };  const 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  };  const ATTRIBUTE = {    className: 'meta',    begin: '@\\[',    end: '\\]',    contains: [      hljs.inherit(hljs.QUOTE_STRING_MODE, {        className: 'meta-string'      })    ]  };  const 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        }),        { // relevance booster for inheritance          begin: '<'        }      ]    },    {      className: 'class',      beginKeywords: 'lib enum union',      end: '$|;',      illegal: /=/,      contains: [        hljs.HASH_COMMENT_MODE,        hljs.inherit(hljs.TITLE_MODE, {          begin: CRYSTAL_PATH_RE        })      ]    },    {      beginKeywords: 'annotation',      end: '$|;',      illegal: /=/,      contains: [        hljs.HASH_COMMENT_MODE,        hljs.inherit(hljs.TITLE_MODE, {          begin: CRYSTAL_PATH_RE        })      ],      relevance: 2    },    {      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;
 |