388 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			388 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /**
 | |
|  * @param {string} value
 | |
|  * @returns {RegExp}
 | |
|  * */
 | |
| 
 | |
| /**
 | |
|  * @param {RegExp | string } re
 | |
|  * @returns {string}
 | |
|  */
 | |
| function source(re) {
 | |
|   if (!re) return null;
 | |
|   if (typeof re === "string") return re;
 | |
| 
 | |
|   return re.source;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * @param {RegExp | string } re
 | |
|  * @returns {string}
 | |
|  */
 | |
| function lookahead(re) {
 | |
|   return concat('(?=', re, ')');
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * @param {...(RegExp | string) } args
 | |
|  * @returns {string}
 | |
|  */
 | |
| function concat(...args) {
 | |
|   const joined = args.map((x) => source(x)).join("");
 | |
|   return joined;
 | |
| }
 | |
| 
 | |
| /*
 | |
| Language: Ruby
 | |
| Description: Ruby is a dynamic, open source programming language with a focus on simplicity and productivity.
 | |
| Website: https://www.ruby-lang.org/
 | |
| Author: Anton Kovalyov <anton@kovalyov.net>
 | |
| Contributors: Peter Leonov <gojpeg@yandex.ru>, Vasily Polovnyov <vast@whiteants.net>, Loren Segal <lsegal@soen.ca>, Pascal Hurni <phi@ruby-reactive.org>, Cedric Sohrauer <sohrauer@googlemail.com>
 | |
| Category: common
 | |
| */
 | |
| 
 | |
| function ruby(hljs) {
 | |
|   const RUBY_METHOD_RE = '([a-zA-Z_]\\w*[!?=]?|[-+~]@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?)';
 | |
|   const RUBY_KEYWORDS = {
 | |
|     keyword:
 | |
|       'and then defined module in return redo if BEGIN retry end for self when ' +
 | |
|       'next until do begin unless END rescue else break undef not super class case ' +
 | |
|       'require yield alias while ensure elsif or include attr_reader attr_writer attr_accessor ' +
 | |
|       '__FILE__',
 | |
|     built_in: 'proc lambda',
 | |
|     literal:
 | |
|       'true false nil'
 | |
|   };
 | |
|   const YARDOCTAG = {
 | |
|     className: 'doctag',
 | |
|     begin: '@[A-Za-z]+'
 | |
|   };
 | |
|   const IRB_OBJECT = {
 | |
|     begin: '#<',
 | |
|     end: '>'
 | |
|   };
 | |
|   const COMMENT_MODES = [
 | |
|     hljs.COMMENT(
 | |
|       '#',
 | |
|       '$',
 | |
|       {
 | |
|         contains: [ YARDOCTAG ]
 | |
|       }
 | |
|     ),
 | |
|     hljs.COMMENT(
 | |
|       '^=begin',
 | |
|       '^=end',
 | |
|       {
 | |
|         contains: [ YARDOCTAG ],
 | |
|         relevance: 10
 | |
|       }
 | |
|     ),
 | |
|     hljs.COMMENT('^__END__', '\\n$')
 | |
|   ];
 | |
|   const SUBST = {
 | |
|     className: 'subst',
 | |
|     begin: /#\{/,
 | |
|     end: /\}/,
 | |
|     keywords: RUBY_KEYWORDS
 | |
|   };
 | |
|   const STRING = {
 | |
|     className: 'string',
 | |
|     contains: [
 | |
|       hljs.BACKSLASH_ESCAPE,
 | |
|       SUBST
 | |
|     ],
 | |
|     variants: [
 | |
|       {
 | |
|         begin: /'/,
 | |
|         end: /'/
 | |
|       },
 | |
|       {
 | |
|         begin: /"/,
 | |
|         end: /"/
 | |
|       },
 | |
|       {
 | |
|         begin: /`/,
 | |
|         end: /`/
 | |
|       },
 | |
|       {
 | |
|         begin: /%[qQwWx]?\(/,
 | |
|         end: /\)/
 | |
|       },
 | |
|       {
 | |
|         begin: /%[qQwWx]?\[/,
 | |
|         end: /\]/
 | |
|       },
 | |
|       {
 | |
|         begin: /%[qQwWx]?\{/,
 | |
|         end: /\}/
 | |
|       },
 | |
|       {
 | |
|         begin: /%[qQwWx]?</,
 | |
|         end: />/
 | |
|       },
 | |
|       {
 | |
|         begin: /%[qQwWx]?\//,
 | |
|         end: /\//
 | |
|       },
 | |
|       {
 | |
|         begin: /%[qQwWx]?%/,
 | |
|         end: /%/
 | |
|       },
 | |
|       {
 | |
|         begin: /%[qQwWx]?-/,
 | |
|         end: /-/
 | |
|       },
 | |
|       {
 | |
|         begin: /%[qQwWx]?\|/,
 | |
|         end: /\|/
 | |
|       },
 | |
|       // in the following expressions, \B in the beginning suppresses recognition of ?-sequences
 | |
|       // where ? is the last character of a preceding identifier, as in: `func?4`
 | |
|       {
 | |
|         begin: /\B\?(\\\d{1,3})/
 | |
|       },
 | |
|       {
 | |
|         begin: /\B\?(\\x[A-Fa-f0-9]{1,2})/
 | |
|       },
 | |
|       {
 | |
|         begin: /\B\?(\\u\{?[A-Fa-f0-9]{1,6}\}?)/
 | |
|       },
 | |
|       {
 | |
|         begin: /\B\?(\\M-\\C-|\\M-\\c|\\c\\M-|\\M-|\\C-\\M-)[\x20-\x7e]/
 | |
|       },
 | |
|       {
 | |
|         begin: /\B\?\\(c|C-)[\x20-\x7e]/
 | |
|       },
 | |
|       {
 | |
|         begin: /\B\?\\?\S/
 | |
|       },
 | |
|       { // heredocs
 | |
|         begin: /<<[-~]?'?(\w+)\n(?:[^\n]*\n)*?\s*\1\b/,
 | |
|         returnBegin: true,
 | |
|         contains: [
 | |
|           {
 | |
|             begin: /<<[-~]?'?/
 | |
|           },
 | |
|           hljs.END_SAME_AS_BEGIN({
 | |
|             begin: /(\w+)/,
 | |
|             end: /(\w+)/,
 | |
|             contains: [
 | |
|               hljs.BACKSLASH_ESCAPE,
 | |
|               SUBST
 | |
|             ]
 | |
|           })
 | |
|         ]
 | |
|       }
 | |
|     ]
 | |
|   };
 | |
| 
 | |
|   // Ruby syntax is underdocumented, but this grammar seems to be accurate
 | |
|   // as of version 2.7.2 (confirmed with (irb and `Ripper.sexp(...)`)
 | |
|   // https://docs.ruby-lang.org/en/2.7.0/doc/syntax/literals_rdoc.html#label-Numbers
 | |
|   const decimal = '[1-9](_?[0-9])*|0';
 | |
|   const digits = '[0-9](_?[0-9])*';
 | |
|   const NUMBER = {
 | |
|     className: 'number',
 | |
|     relevance: 0,
 | |
|     variants: [
 | |
|       // decimal integer/float, optionally exponential or rational, optionally imaginary
 | |
|       {
 | |
|         begin: `\\b(${decimal})(\\.(${digits}))?([eE][+-]?(${digits})|r)?i?\\b`
 | |
|       },
 | |
| 
 | |
|       // explicit decimal/binary/octal/hexadecimal integer,
 | |
|       // optionally rational and/or imaginary
 | |
|       {
 | |
|         begin: "\\b0[dD][0-9](_?[0-9])*r?i?\\b"
 | |
|       },
 | |
|       {
 | |
|         begin: "\\b0[bB][0-1](_?[0-1])*r?i?\\b"
 | |
|       },
 | |
|       {
 | |
|         begin: "\\b0[oO][0-7](_?[0-7])*r?i?\\b"
 | |
|       },
 | |
|       {
 | |
|         begin: "\\b0[xX][0-9a-fA-F](_?[0-9a-fA-F])*r?i?\\b"
 | |
|       },
 | |
| 
 | |
|       // 0-prefixed implicit octal integer, optionally rational and/or imaginary
 | |
|       {
 | |
|         begin: "\\b0(_?[0-7])+r?i?\\b"
 | |
|       }
 | |
|     ]
 | |
|   };
 | |
| 
 | |
|   const PARAMS = {
 | |
|     className: 'params',
 | |
|     begin: '\\(',
 | |
|     end: '\\)',
 | |
|     endsParent: true,
 | |
|     keywords: RUBY_KEYWORDS
 | |
|   };
 | |
| 
 | |
|   const RUBY_DEFAULT_CONTAINS = [
 | |
|     STRING,
 | |
|     {
 | |
|       className: 'class',
 | |
|       beginKeywords: 'class module',
 | |
|       end: '$|;',
 | |
|       illegal: /=/,
 | |
|       contains: [
 | |
|         hljs.inherit(hljs.TITLE_MODE, {
 | |
|           begin: '[A-Za-z_]\\w*(::\\w+)*(\\?|!)?'
 | |
|         }),
 | |
|         {
 | |
|           begin: '<\\s*',
 | |
|           contains: [
 | |
|             {
 | |
|               begin: '(' + hljs.IDENT_RE + '::)?' + hljs.IDENT_RE,
 | |
|               // we already get points for <, we don't need poitns
 | |
|               // for the name also
 | |
|               relevance: 0
 | |
|             }
 | |
|           ]
 | |
|         }
 | |
|       ].concat(COMMENT_MODES)
 | |
|     },
 | |
|     {
 | |
|       className: 'function',
 | |
|       // def method_name(
 | |
|       // def method_name;
 | |
|       // def method_name (end of line)
 | |
|       begin: concat(/def\s+/, lookahead(RUBY_METHOD_RE + "\\s*(\\(|;|$)")),
 | |
|       relevance: 0, // relevance comes from kewords
 | |
|       keywords: "def",
 | |
|       end: '$|;',
 | |
|       contains: [
 | |
|         hljs.inherit(hljs.TITLE_MODE, {
 | |
|           begin: RUBY_METHOD_RE
 | |
|         }),
 | |
|         PARAMS
 | |
|       ].concat(COMMENT_MODES)
 | |
|     },
 | |
|     {
 | |
|       // swallow namespace qualifiers before symbols
 | |
|       begin: hljs.IDENT_RE + '::'
 | |
|     },
 | |
|     {
 | |
|       className: 'symbol',
 | |
|       begin: hljs.UNDERSCORE_IDENT_RE + '(!|\\?)?:',
 | |
|       relevance: 0
 | |
|     },
 | |
|     {
 | |
|       className: 'symbol',
 | |
|       begin: ':(?!\\s)',
 | |
|       contains: [
 | |
|         STRING,
 | |
|         {
 | |
|           begin: RUBY_METHOD_RE
 | |
|         }
 | |
|       ],
 | |
|       relevance: 0
 | |
|     },
 | |
|     NUMBER,
 | |
|     {
 | |
|       // negative-look forward attemps to prevent false matches like:
 | |
|       // @ident@ or $ident$ that might indicate this is not ruby at all
 | |
|       className: "variable",
 | |
|       begin: '(\\$\\W)|((\\$|@@?)(\\w+))(?=[^@$?])' + `(?![A-Za-z])(?![@$?'])`
 | |
|     },
 | |
|     {
 | |
|       className: 'params',
 | |
|       begin: /\|/,
 | |
|       end: /\|/,
 | |
|       relevance: 0, // this could be a lot of things (in other languages) other than params
 | |
|       keywords: RUBY_KEYWORDS
 | |
|     },
 | |
|     { // regexp container
 | |
|       begin: '(' + hljs.RE_STARTERS_RE + '|unless)\\s*',
 | |
|       keywords: 'unless',
 | |
|       contains: [
 | |
|         {
 | |
|           className: 'regexp',
 | |
|           contains: [
 | |
|             hljs.BACKSLASH_ESCAPE,
 | |
|             SUBST
 | |
|           ],
 | |
|           illegal: /\n/,
 | |
|           variants: [
 | |
|             {
 | |
|               begin: '/',
 | |
|               end: '/[a-z]*'
 | |
|             },
 | |
|             {
 | |
|               begin: /%r\{/,
 | |
|               end: /\}[a-z]*/
 | |
|             },
 | |
|             {
 | |
|               begin: '%r\\(',
 | |
|               end: '\\)[a-z]*'
 | |
|             },
 | |
|             {
 | |
|               begin: '%r!',
 | |
|               end: '![a-z]*'
 | |
|             },
 | |
|             {
 | |
|               begin: '%r\\[',
 | |
|               end: '\\][a-z]*'
 | |
|             }
 | |
|           ]
 | |
|         }
 | |
|       ].concat(IRB_OBJECT, COMMENT_MODES),
 | |
|       relevance: 0
 | |
|     }
 | |
|   ].concat(IRB_OBJECT, COMMENT_MODES);
 | |
| 
 | |
|   SUBST.contains = RUBY_DEFAULT_CONTAINS;
 | |
|   PARAMS.contains = RUBY_DEFAULT_CONTAINS;
 | |
| 
 | |
|   // >>
 | |
|   // ?>
 | |
|   const SIMPLE_PROMPT = "[>?]>";
 | |
|   // irb(main):001:0>
 | |
|   const DEFAULT_PROMPT = "[\\w#]+\\(\\w+\\):\\d+:\\d+>";
 | |
|   const RVM_PROMPT = "(\\w+-)?\\d+\\.\\d+\\.\\d+(p\\d+)?[^\\d][^>]+>";
 | |
| 
 | |
|   const IRB_DEFAULT = [
 | |
|     {
 | |
|       begin: /^\s*=>/,
 | |
|       starts: {
 | |
|         end: '$',
 | |
|         contains: RUBY_DEFAULT_CONTAINS
 | |
|       }
 | |
|     },
 | |
|     {
 | |
|       className: 'meta',
 | |
|       begin: '^(' + SIMPLE_PROMPT + "|" + DEFAULT_PROMPT + '|' + RVM_PROMPT + ')(?=[ ])',
 | |
|       starts: {
 | |
|         end: '$',
 | |
|         contains: RUBY_DEFAULT_CONTAINS
 | |
|       }
 | |
|     }
 | |
|   ];
 | |
| 
 | |
|   COMMENT_MODES.unshift(IRB_OBJECT);
 | |
| 
 | |
|   return {
 | |
|     name: 'Ruby',
 | |
|     aliases: [
 | |
|       'rb',
 | |
|       'gemspec',
 | |
|       'podspec',
 | |
|       'thor',
 | |
|       'irb'
 | |
|     ],
 | |
|     keywords: RUBY_KEYWORDS,
 | |
|     illegal: /\/\*/,
 | |
|     contains: [
 | |
|       hljs.SHEBANG({
 | |
|         binary: "ruby"
 | |
|       })
 | |
|     ]
 | |
|       .concat(IRB_DEFAULT)
 | |
|       .concat(COMMENT_MODES)
 | |
|       .concat(RUBY_DEFAULT_CONTAINS)
 | |
|   };
 | |
| }
 | |
| 
 | |
| module.exports = ruby;
 |