ruby.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553
  1. /*---------------------------------------------------------------------------------------------
  2. * Copyright (c) Microsoft Corporation. All rights reserved.
  3. * Licensed under the MIT License. See License.txt in the project root for license information.
  4. *--------------------------------------------------------------------------------------------*/
  5. define('vs/basic-languages/ruby/ruby',["require", "exports"], function (require, exports) {
  6. "use strict";
  7. Object.defineProperty(exports, "__esModule", { value: true });
  8. exports.language = exports.conf = void 0;
  9. exports.conf = {
  10. comments: {
  11. lineComment: '#',
  12. blockComment: ['=begin', '=end']
  13. },
  14. brackets: [
  15. ['(', ')'],
  16. ['{', '}'],
  17. ['[', ']']
  18. ],
  19. autoClosingPairs: [
  20. { open: '{', close: '}' },
  21. { open: '[', close: ']' },
  22. { open: '(', close: ')' },
  23. { open: '"', close: '"' },
  24. { open: "'", close: "'" }
  25. ],
  26. surroundingPairs: [
  27. { open: '{', close: '}' },
  28. { open: '[', close: ']' },
  29. { open: '(', close: ')' },
  30. { open: '"', close: '"' },
  31. { open: "'", close: "'" }
  32. ],
  33. indentationRules: {
  34. increaseIndentPattern: new RegExp('^\\s*((begin|class|(private|protected)\\s+def|def|else|elsif|ensure|for|if|module|rescue|unless|until|when|while|case)|([^#]*\\sdo\\b)|([^#]*=\\s*(case|if|unless)))\\b([^#\\{;]|("|\'|/).*\\4)*(#.*)?$'),
  35. decreaseIndentPattern: new RegExp('^\\s*([}\\]]([,)]?\\s*(#|$)|\\.[a-zA-Z_]\\w*\\b)|(end|rescue|ensure|else|elsif|when)\\b)')
  36. }
  37. };
  38. /*
  39. * Ruby language definition
  40. *
  41. * Quite a complex language due to elaborate escape sequences
  42. * and quoting of literate strings/regular expressions, and
  43. * an 'end' keyword that does not always apply to modifiers like until and while,
  44. * and a 'do' keyword that sometimes starts a block, but sometimes is part of
  45. * another statement (like 'while').
  46. *
  47. * (1) end blocks:
  48. * 'end' may end declarations like if or until, but sometimes 'if' or 'until'
  49. * are modifiers where there is no 'end'. Also, 'do' sometimes starts a block
  50. * that is ended by 'end', but sometimes it is part of a 'while', 'for', or 'until'
  51. * To do proper brace matching we do some elaborate state manipulation.
  52. * some examples:
  53. *
  54. * until bla do
  55. * work until tired
  56. * list.each do
  57. * something if test
  58. * end
  59. * end
  60. *
  61. * or
  62. *
  63. * if test
  64. * something (if test then x end)
  65. * bar if bla
  66. * end
  67. *
  68. * or, how about using class as a property..
  69. *
  70. * class Test
  71. * def endpoint
  72. * self.class.endpoint || routes
  73. * end
  74. * end
  75. *
  76. * (2) quoting:
  77. * there are many kinds of strings and escape sequences. But also, one can
  78. * start many string-like things as '%qx' where q specifies the kind of string
  79. * (like a command, escape expanded, regular expression, symbol etc.), and x is
  80. * some character and only another 'x' ends the sequence. Except for brackets
  81. * where the closing bracket ends the sequence.. and except for a nested bracket
  82. * inside the string like entity. Also, such strings can contain interpolated
  83. * ruby expressions again (and span multiple lines). Moreover, expanded
  84. * regular expression can also contain comments.
  85. */
  86. exports.language = {
  87. tokenPostfix: '.ruby',
  88. keywords: [
  89. '__LINE__',
  90. '__ENCODING__',
  91. '__FILE__',
  92. 'BEGIN',
  93. 'END',
  94. 'alias',
  95. 'and',
  96. 'begin',
  97. 'break',
  98. 'case',
  99. 'class',
  100. 'def',
  101. 'defined?',
  102. 'do',
  103. 'else',
  104. 'elsif',
  105. 'end',
  106. 'ensure',
  107. 'for',
  108. 'false',
  109. 'if',
  110. 'in',
  111. 'module',
  112. 'next',
  113. 'nil',
  114. 'not',
  115. 'or',
  116. 'redo',
  117. 'rescue',
  118. 'retry',
  119. 'return',
  120. 'self',
  121. 'super',
  122. 'then',
  123. 'true',
  124. 'undef',
  125. 'unless',
  126. 'until',
  127. 'when',
  128. 'while',
  129. 'yield'
  130. ],
  131. keywordops: ['::', '..', '...', '?', ':', '=>'],
  132. builtins: [
  133. 'require',
  134. 'public',
  135. 'private',
  136. 'include',
  137. 'extend',
  138. 'attr_reader',
  139. 'protected',
  140. 'private_class_method',
  141. 'protected_class_method',
  142. 'new'
  143. ],
  144. // these are closed by 'end' (if, while and until are handled separately)
  145. declarations: [
  146. 'module',
  147. 'class',
  148. 'def',
  149. 'case',
  150. 'do',
  151. 'begin',
  152. 'for',
  153. 'if',
  154. 'while',
  155. 'until',
  156. 'unless'
  157. ],
  158. linedecls: ['def', 'case', 'do', 'begin', 'for', 'if', 'while', 'until', 'unless'],
  159. operators: [
  160. '^',
  161. '&',
  162. '|',
  163. '<=>',
  164. '==',
  165. '===',
  166. '!~',
  167. '=~',
  168. '>',
  169. '>=',
  170. '<',
  171. '<=',
  172. '<<',
  173. '>>',
  174. '+',
  175. '-',
  176. '*',
  177. '/',
  178. '%',
  179. '**',
  180. '~',
  181. '+@',
  182. '-@',
  183. '[]',
  184. '[]=',
  185. '`',
  186. '+=',
  187. '-=',
  188. '*=',
  189. '**=',
  190. '/=',
  191. '^=',
  192. '%=',
  193. '<<=',
  194. '>>=',
  195. '&=',
  196. '&&=',
  197. '||=',
  198. '|='
  199. ],
  200. brackets: [
  201. { open: '(', close: ')', token: 'delimiter.parenthesis' },
  202. { open: '{', close: '}', token: 'delimiter.curly' },
  203. { open: '[', close: ']', token: 'delimiter.square' }
  204. ],
  205. // we include these common regular expressions
  206. symbols: /[=><!~?:&|+\-*\/\^%\.]+/,
  207. // escape sequences
  208. escape: /(?:[abefnrstv\\"'\n\r]|[0-7]{1,3}|x[0-9A-Fa-f]{1,2}|u[0-9A-Fa-f]{4})/,
  209. escapes: /\\(?:C\-(@escape|.)|c(@escape|.)|@escape)/,
  210. decpart: /\d(_?\d)*/,
  211. decimal: /0|@decpart/,
  212. delim: /[^a-zA-Z0-9\s\n\r]/,
  213. heredelim: /(?:\w+|'[^']*'|"[^"]*"|`[^`]*`)/,
  214. regexpctl: /[(){}\[\]\$\^|\-*+?\.]/,
  215. regexpesc: /\\(?:[AzZbBdDfnrstvwWn0\\\/]|@regexpctl|c[A-Z]|x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4})?/,
  216. // The main tokenizer for our languages
  217. tokenizer: {
  218. // Main entry.
  219. // root.<decl> where decl is the current opening declaration (like 'class')
  220. root: [
  221. // identifiers and keywords
  222. // most complexity here is due to matching 'end' correctly with declarations.
  223. // We distinguish a declaration that comes first on a line, versus declarations further on a line (which are most likey modifiers)
  224. [
  225. /^(\s*)([a-z_]\w*[!?=]?)/,
  226. [
  227. 'white',
  228. {
  229. cases: {
  230. 'for|until|while': {
  231. token: 'keyword.$2',
  232. next: '@dodecl.$2'
  233. },
  234. '@declarations': {
  235. token: 'keyword.$2',
  236. next: '@root.$2'
  237. },
  238. end: { token: 'keyword.$S2', next: '@pop' },
  239. '@keywords': 'keyword',
  240. '@builtins': 'predefined',
  241. '@default': 'identifier'
  242. }
  243. }
  244. ]
  245. ],
  246. [
  247. /[a-z_]\w*[!?=]?/,
  248. {
  249. cases: {
  250. 'if|unless|while|until': {
  251. token: 'keyword.$0x',
  252. next: '@modifier.$0x'
  253. },
  254. for: { token: 'keyword.$2', next: '@dodecl.$2' },
  255. '@linedecls': { token: 'keyword.$0', next: '@root.$0' },
  256. end: { token: 'keyword.$S2', next: '@pop' },
  257. '@keywords': 'keyword',
  258. '@builtins': 'predefined',
  259. '@default': 'identifier'
  260. }
  261. }
  262. ],
  263. [/[A-Z][\w]*[!?=]?/, 'constructor.identifier'],
  264. [/\$[\w]*/, 'global.constant'],
  265. [/@[\w]*/, 'namespace.instance.identifier'],
  266. [/@@@[\w]*/, 'namespace.class.identifier'],
  267. // here document
  268. [/<<[-~](@heredelim).*/, { token: 'string.heredoc.delimiter', next: '@heredoc.$1' }],
  269. [
  270. /[ \t\r\n]+<<(@heredelim).*/,
  271. { token: 'string.heredoc.delimiter', next: '@heredoc.$1' }
  272. ],
  273. [/^<<(@heredelim).*/, { token: 'string.heredoc.delimiter', next: '@heredoc.$1' }],
  274. // whitespace
  275. { include: '@whitespace' },
  276. // strings
  277. [/"/, { token: 'string.d.delim', next: '@dstring.d."' }],
  278. [/'/, { token: 'string.sq.delim', next: '@sstring.sq' }],
  279. // % literals. For efficiency, rematch in the 'pstring' state
  280. [/%([rsqxwW]|Q?)/, { token: '@rematch', next: 'pstring' }],
  281. // commands and symbols
  282. [/`/, { token: 'string.x.delim', next: '@dstring.x.`' }],
  283. [/:(\w|[$@])\w*[!?=]?/, 'string.s'],
  284. [/:"/, { token: 'string.s.delim', next: '@dstring.s."' }],
  285. [/:'/, { token: 'string.s.delim', next: '@sstring.s' }],
  286. // regular expressions. Lookahead for a (not escaped) closing forwardslash on the same line
  287. [/\/(?=(\\\/|[^\/\n])+\/)/, { token: 'regexp.delim', next: '@regexp' }],
  288. // delimiters and operators
  289. [/[{}()\[\]]/, '@brackets'],
  290. [
  291. /@symbols/,
  292. {
  293. cases: {
  294. '@keywordops': 'keyword',
  295. '@operators': 'operator',
  296. '@default': ''
  297. }
  298. }
  299. ],
  300. [/[;,]/, 'delimiter'],
  301. // numbers
  302. [/0[xX][0-9a-fA-F](_?[0-9a-fA-F])*/, 'number.hex'],
  303. [/0[_oO][0-7](_?[0-7])*/, 'number.octal'],
  304. [/0[bB][01](_?[01])*/, 'number.binary'],
  305. [/0[dD]@decpart/, 'number'],
  306. [
  307. /@decimal((\.@decpart)?([eE][\-+]?@decpart)?)/,
  308. {
  309. cases: {
  310. $1: 'number.float',
  311. '@default': 'number'
  312. }
  313. }
  314. ]
  315. ],
  316. // used to not treat a 'do' as a block opener if it occurs on the same
  317. // line as a 'do' statement: 'while|until|for'
  318. // dodecl.<decl> where decl is the declarations started, like 'while'
  319. dodecl: [
  320. [/^/, { token: '', switchTo: '@root.$S2' }],
  321. [
  322. /[a-z_]\w*[!?=]?/,
  323. {
  324. cases: {
  325. end: { token: 'keyword.$S2', next: '@pop' },
  326. do: { token: 'keyword', switchTo: '@root.$S2' },
  327. '@linedecls': {
  328. token: '@rematch',
  329. switchTo: '@root.$S2'
  330. },
  331. '@keywords': 'keyword',
  332. '@builtins': 'predefined',
  333. '@default': 'identifier'
  334. }
  335. }
  336. ],
  337. { include: '@root' }
  338. ],
  339. // used to prevent potential modifiers ('if|until|while|unless') to match
  340. // with 'end' keywords.
  341. // modifier.<decl>x where decl is the declaration starter, like 'if'
  342. modifier: [
  343. [/^/, '', '@pop'],
  344. [
  345. /[a-z_]\w*[!?=]?/,
  346. {
  347. cases: {
  348. end: { token: 'keyword.$S2', next: '@pop' },
  349. 'then|else|elsif|do': {
  350. token: 'keyword',
  351. switchTo: '@root.$S2'
  352. },
  353. '@linedecls': {
  354. token: '@rematch',
  355. switchTo: '@root.$S2'
  356. },
  357. '@keywords': 'keyword',
  358. '@builtins': 'predefined',
  359. '@default': 'identifier'
  360. }
  361. }
  362. ],
  363. { include: '@root' }
  364. ],
  365. // single quote strings (also used for symbols)
  366. // sstring.<kind> where kind is 'sq' (single quote) or 's' (symbol)
  367. sstring: [
  368. [/[^\\']+/, 'string.$S2'],
  369. [/\\\\|\\'|\\$/, 'string.$S2.escape'],
  370. [/\\./, 'string.$S2.invalid'],
  371. [/'/, { token: 'string.$S2.delim', next: '@pop' }]
  372. ],
  373. // double quoted "string".
  374. // dstring.<kind>.<delim> where kind is 'd' (double quoted), 'x' (command), or 's' (symbol)
  375. // and delim is the ending delimiter (" or `)
  376. dstring: [
  377. [/[^\\`"#]+/, 'string.$S2'],
  378. [/#/, 'string.$S2.escape', '@interpolated'],
  379. [/\\$/, 'string.$S2.escape'],
  380. [/@escapes/, 'string.$S2.escape'],
  381. [/\\./, 'string.$S2.escape.invalid'],
  382. [
  383. /[`"]/,
  384. {
  385. cases: {
  386. '$#==$S3': { token: 'string.$S2.delim', next: '@pop' },
  387. '@default': 'string.$S2'
  388. }
  389. }
  390. ]
  391. ],
  392. // literal documents
  393. // heredoc.<close> where close is the closing delimiter
  394. heredoc: [
  395. [
  396. /^(\s*)(@heredelim)$/,
  397. {
  398. cases: {
  399. '$2==$S2': [
  400. 'string.heredoc',
  401. { token: 'string.heredoc.delimiter', next: '@pop' }
  402. ],
  403. '@default': ['string.heredoc', 'string.heredoc']
  404. }
  405. }
  406. ],
  407. [/.*/, 'string.heredoc']
  408. ],
  409. // interpolated sequence
  410. interpolated: [
  411. [/\$\w*/, 'global.constant', '@pop'],
  412. [/@\w*/, 'namespace.class.identifier', '@pop'],
  413. [/@@@\w*/, 'namespace.instance.identifier', '@pop'],
  414. [
  415. /[{]/,
  416. {
  417. token: 'string.escape.curly',
  418. switchTo: '@interpolated_compound'
  419. }
  420. ],
  421. ['', '', '@pop'] // just a # is interpreted as a #
  422. ],
  423. // any code
  424. interpolated_compound: [
  425. [/[}]/, { token: 'string.escape.curly', next: '@pop' }],
  426. { include: '@root' }
  427. ],
  428. // %r quoted regexp
  429. // pregexp.<open>.<close> where open/close are the open/close delimiter
  430. pregexp: [
  431. { include: '@whitespace' },
  432. // turns out that you can quote using regex control characters, aargh!
  433. // for example; %r|kgjgaj| is ok (even though | is used for alternation)
  434. // so, we need to match those first
  435. [
  436. /[^\(\{\[\\]/,
  437. {
  438. cases: {
  439. '$#==$S3': { token: 'regexp.delim', next: '@pop' },
  440. '$#==$S2': { token: 'regexp.delim', next: '@push' },
  441. '~[)}\\]]': '@brackets.regexp.escape.control',
  442. '~@regexpctl': 'regexp.escape.control',
  443. '@default': 'regexp'
  444. }
  445. }
  446. ],
  447. { include: '@regexcontrol' }
  448. ],
  449. // We match regular expression quite precisely
  450. regexp: [
  451. { include: '@regexcontrol' },
  452. [/[^\\\/]/, 'regexp'],
  453. ['/[ixmp]*', { token: 'regexp.delim' }, '@pop']
  454. ],
  455. regexcontrol: [
  456. [
  457. /(\{)(\d+(?:,\d*)?)(\})/,
  458. [
  459. '@brackets.regexp.escape.control',
  460. 'regexp.escape.control',
  461. '@brackets.regexp.escape.control'
  462. ]
  463. ],
  464. [
  465. /(\[)(\^?)/,
  466. [
  467. '@brackets.regexp.escape.control',
  468. { token: 'regexp.escape.control', next: '@regexrange' }
  469. ]
  470. ],
  471. [/(\()(\?[:=!])/, ['@brackets.regexp.escape.control', 'regexp.escape.control']],
  472. [/\(\?#/, { token: 'regexp.escape.control', next: '@regexpcomment' }],
  473. [/[()]/, '@brackets.regexp.escape.control'],
  474. [/@regexpctl/, 'regexp.escape.control'],
  475. [/\\$/, 'regexp.escape'],
  476. [/@regexpesc/, 'regexp.escape'],
  477. [/\\\./, 'regexp.invalid'],
  478. [/#/, 'regexp.escape', '@interpolated']
  479. ],
  480. regexrange: [
  481. [/-/, 'regexp.escape.control'],
  482. [/\^/, 'regexp.invalid'],
  483. [/\\$/, 'regexp.escape'],
  484. [/@regexpesc/, 'regexp.escape'],
  485. [/[^\]]/, 'regexp'],
  486. [/\]/, '@brackets.regexp.escape.control', '@pop']
  487. ],
  488. regexpcomment: [
  489. [/[^)]+/, 'comment'],
  490. [/\)/, { token: 'regexp.escape.control', next: '@pop' }]
  491. ],
  492. // % quoted strings
  493. // A bit repetitive since we need to often special case the kind of ending delimiter
  494. pstring: [
  495. [/%([qws])\(/, { token: 'string.$1.delim', switchTo: '@qstring.$1.(.)' }],
  496. [/%([qws])\[/, { token: 'string.$1.delim', switchTo: '@qstring.$1.[.]' }],
  497. [/%([qws])\{/, { token: 'string.$1.delim', switchTo: '@qstring.$1.{.}' }],
  498. [/%([qws])</, { token: 'string.$1.delim', switchTo: '@qstring.$1.<.>' }],
  499. [/%([qws])(@delim)/, { token: 'string.$1.delim', switchTo: '@qstring.$1.$2.$2' }],
  500. [/%r\(/, { token: 'regexp.delim', switchTo: '@pregexp.(.)' }],
  501. [/%r\[/, { token: 'regexp.delim', switchTo: '@pregexp.[.]' }],
  502. [/%r\{/, { token: 'regexp.delim', switchTo: '@pregexp.{.}' }],
  503. [/%r</, { token: 'regexp.delim', switchTo: '@pregexp.<.>' }],
  504. [/%r(@delim)/, { token: 'regexp.delim', switchTo: '@pregexp.$1.$1' }],
  505. [/%(x|W|Q?)\(/, { token: 'string.$1.delim', switchTo: '@qqstring.$1.(.)' }],
  506. [/%(x|W|Q?)\[/, { token: 'string.$1.delim', switchTo: '@qqstring.$1.[.]' }],
  507. [/%(x|W|Q?)\{/, { token: 'string.$1.delim', switchTo: '@qqstring.$1.{.}' }],
  508. [/%(x|W|Q?)</, { token: 'string.$1.delim', switchTo: '@qqstring.$1.<.>' }],
  509. [/%(x|W|Q?)(@delim)/, { token: 'string.$1.delim', switchTo: '@qqstring.$1.$2.$2' }],
  510. [/%([rqwsxW]|Q?)./, { token: 'invalid', next: '@pop' }],
  511. [/./, { token: 'invalid', next: '@pop' }] // recover
  512. ],
  513. // non-expanded quoted string.
  514. // qstring.<kind>.<open>.<close>
  515. // kind = q|w|s (single quote, array, symbol)
  516. // open = open delimiter
  517. // close = close delimiter
  518. qstring: [
  519. [/\\$/, 'string.$S2.escape'],
  520. [/\\./, 'string.$S2.escape'],
  521. [
  522. /./,
  523. {
  524. cases: {
  525. '$#==$S4': { token: 'string.$S2.delim', next: '@pop' },
  526. '$#==$S3': { token: 'string.$S2.delim', next: '@push' },
  527. '@default': 'string.$S2'
  528. }
  529. }
  530. ]
  531. ],
  532. // expanded quoted string.
  533. // qqstring.<kind>.<open>.<close>
  534. // kind = Q|W|x (double quote, array, command)
  535. // open = open delimiter
  536. // close = close delimiter
  537. qqstring: [[/#/, 'string.$S2.escape', '@interpolated'], { include: '@qstring' }],
  538. // whitespace & comments
  539. whitespace: [
  540. [/[ \t\r\n]+/, ''],
  541. [/^\s*=begin\b/, 'comment', '@comment'],
  542. [/#.*$/, 'comment']
  543. ],
  544. comment: [
  545. [/[^=]+/, 'comment'],
  546. [/^\s*=begin\b/, 'comment.invalid'],
  547. [/^\s*=end\b.*/, 'comment', '@pop'],
  548. [/[=]/, 'comment']
  549. ]
  550. }
  551. };
  552. });