You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
387 lines
8.6 KiB
387 lines
8.6 KiB
/** |
|
* @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;
|
|
|