ruby.js 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. /**
  2. * @param {string} value
  3. * @returns {RegExp}
  4. * */
  5. /**
  6. * @param {RegExp | string } re
  7. * @returns {string}
  8. */
  9. function source(re) {
  10. if (!re) return null;
  11. if (typeof re === "string") return re;
  12. return re.source;
  13. }
  14. /**
  15. * @param {RegExp | string } re
  16. * @returns {string}
  17. */
  18. function lookahead(re) {
  19. return concat('(?=', re, ')');
  20. }
  21. /**
  22. * @param {...(RegExp | string) } args
  23. * @returns {string}
  24. */
  25. function concat(...args) {
  26. const joined = args.map((x) => source(x)).join("");
  27. return joined;
  28. }
  29. /*
  30. Language: Ruby
  31. Description: Ruby is a dynamic, open source programming language with a focus on simplicity and productivity.
  32. Website: https://www.ruby-lang.org/
  33. Author: Anton Kovalyov <anton@kovalyov.net>
  34. 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>
  35. Category: common
  36. */
  37. function ruby(hljs) {
  38. var RUBY_METHOD_RE = '([a-zA-Z_]\\w*[!?=]?|[-+~]@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?)';
  39. var RUBY_KEYWORDS = {
  40. keyword:
  41. 'and then defined module in return redo if BEGIN retry end for self when ' +
  42. 'next until do begin unless END rescue else break undef not super class case ' +
  43. 'require yield alias while ensure elsif or include attr_reader attr_writer attr_accessor ' +
  44. '__FILE__',
  45. built_in: 'proc lambda',
  46. literal:
  47. 'true false nil'
  48. };
  49. var YARDOCTAG = {
  50. className: 'doctag',
  51. begin: '@[A-Za-z]+'
  52. };
  53. var IRB_OBJECT = {
  54. begin: '#<', end: '>'
  55. };
  56. var COMMENT_MODES = [
  57. hljs.COMMENT(
  58. '#',
  59. '$',
  60. {
  61. contains: [YARDOCTAG]
  62. }
  63. ),
  64. hljs.COMMENT(
  65. '^=begin',
  66. '^=end',
  67. {
  68. contains: [YARDOCTAG],
  69. relevance: 10
  70. }
  71. ),
  72. hljs.COMMENT('^__END__', '\\n$')
  73. ];
  74. var SUBST = {
  75. className: 'subst',
  76. begin: /#\{/, end: /\}/,
  77. keywords: RUBY_KEYWORDS
  78. };
  79. var STRING = {
  80. className: 'string',
  81. contains: [hljs.BACKSLASH_ESCAPE, SUBST],
  82. variants: [
  83. {begin: /'/, end: /'/},
  84. {begin: /"/, end: /"/},
  85. {begin: /`/, end: /`/},
  86. {begin: /%[qQwWx]?\(/, end: /\)/},
  87. {begin: /%[qQwWx]?\[/, end: /\]/},
  88. {begin: /%[qQwWx]?\{/, end: /\}/},
  89. {begin: /%[qQwWx]?</, end: />/},
  90. {begin: /%[qQwWx]?\//, end: /\//},
  91. {begin: /%[qQwWx]?%/, end: /%/},
  92. {begin: /%[qQwWx]?-/, end: /-/},
  93. {begin: /%[qQwWx]?\|/, end: /\|/},
  94. {
  95. // \B in the beginning suppresses recognition of ?-sequences where ?
  96. // is the last character of a preceding identifier, as in: `func?4`
  97. begin: /\B\?(\\\d{1,3}|\\x[A-Fa-f0-9]{1,2}|\\u[A-Fa-f0-9]{4}|\\?\S)\b/
  98. },
  99. { // heredocs
  100. begin: /<<[-~]?'?(\w+)\n(?:[^\n]*\n)*?\s*\1\b/,
  101. returnBegin: true,
  102. contains: [
  103. { begin: /<<[-~]?'?/ },
  104. hljs.END_SAME_AS_BEGIN({
  105. begin: /(\w+)/, end: /(\w+)/,
  106. contains: [hljs.BACKSLASH_ESCAPE, SUBST],
  107. })
  108. ]
  109. }
  110. ]
  111. };
  112. // Ruby syntax is underdocumented, but this grammar seems to be accurate
  113. // as of version 2.7.2 (confirmed with (irb and `Ripper.sexp(...)`)
  114. // https://docs.ruby-lang.org/en/2.7.0/doc/syntax/literals_rdoc.html#label-Numbers
  115. var decimal = '[1-9](_?[0-9])*|0';
  116. var digits = '[0-9](_?[0-9])*';
  117. var NUMBER = {
  118. className: 'number', relevance: 0,
  119. variants: [
  120. // decimal integer/float, optionally exponential or rational, optionally imaginary
  121. { begin: `\\b(${decimal})(\\.(${digits}))?([eE][+-]?(${digits})|r)?i?\\b` },
  122. // explicit decimal/binary/octal/hexadecimal integer,
  123. // optionally rational and/or imaginary
  124. { begin: "\\b0[dD][0-9](_?[0-9])*r?i?\\b" },
  125. { begin: "\\b0[bB][0-1](_?[0-1])*r?i?\\b" },
  126. { begin: "\\b0[oO][0-7](_?[0-7])*r?i?\\b" },
  127. { begin: "\\b0[xX][0-9a-fA-F](_?[0-9a-fA-F])*r?i?\\b" },
  128. // 0-prefixed implicit octal integer, optionally rational and/or imaginary
  129. { begin: "\\b0(_?[0-7])+r?i?\\b" },
  130. ]
  131. };
  132. var PARAMS = {
  133. className: 'params',
  134. begin: '\\(', end: '\\)', endsParent: true,
  135. keywords: RUBY_KEYWORDS
  136. };
  137. var RUBY_DEFAULT_CONTAINS = [
  138. STRING,
  139. {
  140. className: 'class',
  141. beginKeywords: 'class module', end: '$|;',
  142. illegal: /=/,
  143. contains: [
  144. hljs.inherit(hljs.TITLE_MODE, {begin: '[A-Za-z_]\\w*(::\\w+)*(\\?|!)?'}),
  145. {
  146. begin: '<\\s*',
  147. contains: [{
  148. begin: '(' + hljs.IDENT_RE + '::)?' + hljs.IDENT_RE
  149. }]
  150. }
  151. ].concat(COMMENT_MODES)
  152. },
  153. {
  154. className: 'function',
  155. // def method_name(
  156. // def method_name;
  157. // def method_name (end of line)
  158. begin: concat(/def\s*/, lookahead(RUBY_METHOD_RE + "\\s*(\\(|;|$)")),
  159. keywords: "def",
  160. end: '$|;',
  161. contains: [
  162. hljs.inherit(hljs.TITLE_MODE, {begin: RUBY_METHOD_RE}),
  163. PARAMS
  164. ].concat(COMMENT_MODES)
  165. },
  166. {
  167. // swallow namespace qualifiers before symbols
  168. begin: hljs.IDENT_RE + '::'
  169. },
  170. {
  171. className: 'symbol',
  172. begin: hljs.UNDERSCORE_IDENT_RE + '(!|\\?)?:',
  173. relevance: 0
  174. },
  175. {
  176. className: 'symbol',
  177. begin: ':(?!\\s)',
  178. contains: [STRING, {begin: RUBY_METHOD_RE}],
  179. relevance: 0
  180. },
  181. NUMBER,
  182. {
  183. // negative-look forward attemps to prevent false matches like:
  184. // @ident@ or $ident$ that might indicate this is not ruby at all
  185. className: "variable",
  186. begin: '(\\$\\W)|((\\$|@@?)(\\w+))(?=[^@$?])' + `(?![A-Za-z])(?![@$?'])`
  187. },
  188. {
  189. className: 'params',
  190. begin: /\|/,
  191. end: /\|/,
  192. relevance:0, // this could be a lot of things (in other languages) other than params
  193. keywords: RUBY_KEYWORDS
  194. },
  195. { // regexp container
  196. begin: '(' + hljs.RE_STARTERS_RE + '|unless)\\s*',
  197. keywords: 'unless',
  198. contains: [
  199. {
  200. className: 'regexp',
  201. contains: [hljs.BACKSLASH_ESCAPE, SUBST],
  202. illegal: /\n/,
  203. variants: [
  204. {begin: '/', end: '/[a-z]*'},
  205. {begin: /%r\{/, end: /\}[a-z]*/},
  206. {begin: '%r\\(', end: '\\)[a-z]*'},
  207. {begin: '%r!', end: '![a-z]*'},
  208. {begin: '%r\\[', end: '\\][a-z]*'}
  209. ]
  210. }
  211. ].concat(IRB_OBJECT, COMMENT_MODES),
  212. relevance: 0
  213. }
  214. ].concat(IRB_OBJECT, COMMENT_MODES);
  215. SUBST.contains = RUBY_DEFAULT_CONTAINS;
  216. PARAMS.contains = RUBY_DEFAULT_CONTAINS;
  217. // >>
  218. // ?>
  219. var SIMPLE_PROMPT = "[>?]>";
  220. // irb(main):001:0>
  221. var DEFAULT_PROMPT = "[\\w#]+\\(\\w+\\):\\d+:\\d+>";
  222. var RVM_PROMPT = "(\\w+-)?\\d+\\.\\d+\\.\\d+(p\\d+)?[^\\d][^>]+>";
  223. var IRB_DEFAULT = [
  224. {
  225. begin: /^\s*=>/,
  226. starts: {
  227. end: '$', contains: RUBY_DEFAULT_CONTAINS
  228. }
  229. },
  230. {
  231. className: 'meta',
  232. begin: '^('+SIMPLE_PROMPT+"|"+DEFAULT_PROMPT+'|'+RVM_PROMPT+')(?=[ ])',
  233. starts: {
  234. end: '$', contains: RUBY_DEFAULT_CONTAINS
  235. }
  236. }
  237. ];
  238. COMMENT_MODES.unshift(IRB_OBJECT);
  239. return {
  240. name: 'Ruby',
  241. aliases: ['rb', 'gemspec', 'podspec', 'thor', 'irb'],
  242. keywords: RUBY_KEYWORDS,
  243. illegal: /\/\*/,
  244. contains: [
  245. hljs.SHEBANG({binary:"ruby"}),
  246. ]
  247. .concat(IRB_DEFAULT)
  248. .concat(COMMENT_MODES)
  249. .concat(RUBY_DEFAULT_CONTAINS)
  250. };
  251. }
  252. module.exports = ruby;