327 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			327 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/*
 | 
						|
Language: Crystal
 | 
						|
Author: 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;
 |