mode-ruby.js 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003
  1. ace.define("ace/mode/ruby_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"], function(require, exports, module) {
  2. "use strict";
  3. var oop = require("../lib/oop");
  4. var TextHighlightRules = require("./text_highlight_rules").TextHighlightRules;
  5. var constantOtherSymbol = exports.constantOtherSymbol = {
  6. token : "constant.other.symbol.ruby", // symbol
  7. regex : "[:](?:[A-Za-z_]|[@$](?=[a-zA-Z0-9_]))[a-zA-Z0-9_]*[!=?]?"
  8. };
  9. exports.qString = {
  10. token : "string", // single line
  11. regex : "['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"
  12. };
  13. exports.qqString = {
  14. token : "string", // single line
  15. regex : '["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'
  16. };
  17. exports.tString = {
  18. token : "string", // backtick string
  19. regex : "[`](?:(?:\\\\.)|(?:[^'\\\\]))*?[`]"
  20. };
  21. var constantNumericHex = exports.constantNumericHex = {
  22. token : "constant.numeric", // hex
  23. regex : "0[xX][0-9a-fA-F](?:[0-9a-fA-F]|_(?=[0-9a-fA-F]))*\\b"
  24. };
  25. var constantNumericBinary = exports.constantNumericBinary = {
  26. token: "constant.numeric",
  27. regex: /\b(0[bB][01](?:[01]|_(?=[01]))*)\b/
  28. };
  29. var constantNumericDecimal = exports.constantNumericDecimal = {
  30. token: "constant.numeric",
  31. regex: /\b(0[dD](?:[1-9](?:[\d]|_(?=[\d]))*|0))\b/
  32. };
  33. var constantNumericOctal = exports.constantNumericDecimal = {
  34. token: "constant.numeric",
  35. regex: /\b(0[oO]?(?:[1-7](?:[0-7]|_(?=[0-7]))*|0))\b/
  36. };
  37. var constantNumericRational = exports.constantNumericRational = {
  38. token: "constant.numeric", //rational + complex
  39. regex: /\b([\d]+(?:[./][\d]+)?ri?)\b/
  40. };
  41. var constantNumericComplex = exports.constantNumericComplex = {
  42. token: "constant.numeric", //simple complex numbers
  43. regex: /\b([\d]i)\b/
  44. };
  45. var constantNumericFloat = exports.constantNumericFloat = {
  46. token : "constant.numeric", // float + complex
  47. regex : "[+-]?\\d(?:\\d|_(?=\\d))*(?:(?:\\.\\d(?:\\d|_(?=\\d))*)?(?:[eE][+-]?\\d+)?)?i?\\b"
  48. };
  49. var instanceVariable = exports.instanceVariable = {
  50. token : "variable.instance", // instance variable
  51. regex : "@{1,2}[a-zA-Z_\\d]+"
  52. };
  53. var RubyHighlightRules = function() {
  54. var builtinFunctions = (
  55. "abort|Array|assert|assert_equal|assert_not_equal|assert_same|assert_not_same|" +
  56. "assert_nil|assert_not_nil|assert_match|assert_no_match|assert_in_delta|assert_throws|" +
  57. "assert_raise|assert_nothing_raised|assert_instance_of|assert_kind_of|assert_respond_to|" +
  58. "assert_operator|assert_send|assert_difference|assert_no_difference|assert_recognizes|" +
  59. "assert_generates|assert_response|assert_redirected_to|assert_template|assert_select|" +
  60. "assert_select_email|assert_select_rjs|assert_select_encoded|css_select|at_exit|" +
  61. "attr|attr_writer|attr_reader|attr_accessor|attr_accessible|autoload|binding|block_given?|callcc|" +
  62. "caller|catch|chomp|chomp!|chop|chop!|defined?|delete_via_redirect|eval|exec|exit|" +
  63. "exit!|fail|Float|flunk|follow_redirect!|fork|form_for|form_tag|format|gets|global_variables|gsub|" +
  64. "gsub!|get_via_redirect|host!|https?|https!|include|Integer|lambda|link_to|" +
  65. "link_to_unless_current|link_to_function|link_to_remote|load|local_variables|loop|open|open_session|" +
  66. "p|print|printf|proc|putc|puts|post_via_redirect|put_via_redirect|raise|rand|" +
  67. "raw|readline|readlines|redirect?|request_via_redirect|require|scan|select|" +
  68. "set_trace_func|sleep|split|sprintf|srand|String|stylesheet_link_tag|syscall|system|sub|sub!|test|" +
  69. "throw|trace_var|trap|untrace_var|atan2|cos|exp|frexp|ldexp|log|log10|sin|sqrt|tan|" +
  70. "render|javascript_include_tag|csrf_meta_tag|label_tag|text_field_tag|submit_tag|check_box_tag|" +
  71. "content_tag|radio_button_tag|text_area_tag|password_field_tag|hidden_field_tag|" +
  72. "fields_for|select_tag|options_for_select|options_from_collection_for_select|collection_select|" +
  73. "time_zone_select|select_date|select_time|select_datetime|date_select|time_select|datetime_select|" +
  74. "select_year|select_month|select_day|select_hour|select_minute|select_second|file_field_tag|" +
  75. "file_field|respond_to|skip_before_filter|around_filter|after_filter|verify|" +
  76. "protect_from_forgery|rescue_from|helper_method|redirect_to|before_filter|" +
  77. "send_data|send_file|validates_presence_of|validates_uniqueness_of|validates_length_of|" +
  78. "validates_format_of|validates_acceptance_of|validates_associated|validates_exclusion_of|" +
  79. "validates_inclusion_of|validates_numericality_of|validates_with|validates_each|" +
  80. "authenticate_or_request_with_http_basic|authenticate_or_request_with_http_digest|" +
  81. "filter_parameter_logging|match|get|post|resources|redirect|scope|assert_routing|" +
  82. "translate|localize|extract_locale_from_tld|caches_page|expire_page|caches_action|expire_action|" +
  83. "cache|expire_fragment|expire_cache_for|observe|cache_sweeper|" +
  84. "has_many|has_one|belongs_to|has_and_belongs_to_many|p|warn|refine|using|module_function|extend|alias_method|" +
  85. "private_class_method|remove_method|undef_method"
  86. );
  87. var keywords = (
  88. "alias|and|BEGIN|begin|break|case|class|def|defined|do|else|elsif|END|end|ensure|" +
  89. "__FILE__|finally|for|gem|if|in|__LINE__|module|next|not|or|private|protected|public|" +
  90. "redo|rescue|retry|return|super|then|undef|unless|until|when|while|yield|__ENCODING__|prepend"
  91. );
  92. var buildinConstants = (
  93. "true|TRUE|false|FALSE|nil|NIL|ARGF|ARGV|DATA|ENV|RUBY_PLATFORM|RUBY_RELEASE_DATE|" +
  94. "RUBY_VERSION|STDERR|STDIN|STDOUT|TOPLEVEL_BINDING|RUBY_PATCHLEVEL|RUBY_REVISION|RUBY_COPYRIGHT|RUBY_ENGINE|RUBY_ENGINE_VERSION|RUBY_DESCRIPTION"
  95. );
  96. var builtinVariables = (
  97. "$DEBUG|$defout|$FILENAME|$LOAD_PATH|$SAFE|$stdin|$stdout|$stderr|$VERBOSE|" +
  98. "$!|root_url|flash|session|cookies|params|request|response|logger|self"
  99. );
  100. var keywordMapper = this.$keywords = this.createKeywordMapper({
  101. "keyword": keywords,
  102. "constant.language": buildinConstants,
  103. "variable.language": builtinVariables,
  104. "support.function": builtinFunctions,
  105. "invalid.deprecated": "debugger" // TODO is this a remnant from js mode?
  106. }, "identifier");
  107. var escapedChars = "\\\\(?:n(?:[1-7][0-7]{0,2}|0)|[nsrtvfbae'\"\\\\]|c(?:\\\\M-)?.|M-(?:\\\\C-|\\\\c)?.|C-(?:\\\\M-)?.|[0-7]{3}|x[\\da-fA-F]{2}|u[\\da-fA-F]{4}|u{[\\da-fA-F]{1,6}(?:\\s[\\da-fA-F]{1,6})*})";
  108. var closeParen = {
  109. "(": ")",
  110. "[": "]",
  111. "{": "}",
  112. "<": ">",
  113. "^": "^",
  114. "|": "|",
  115. "%": "%"
  116. };
  117. this.$rules = {
  118. "start": [
  119. {
  120. token: "comment",
  121. regex: "#.*$"
  122. }, {
  123. token: "comment.multiline", // multi line comment
  124. regex: "^=begin(?=$|\\s.*$)",
  125. next: "comment"
  126. }, {
  127. token: "string.regexp",
  128. regex: /[/](?=.*\/)/,
  129. next: "regex"
  130. },
  131. [{
  132. token: ["constant.other.symbol.ruby", "string.start"],
  133. regex: /(:)?(")/,
  134. push: [{
  135. token: "constant.language.escape",
  136. regex: escapedChars
  137. }, {
  138. token: "paren.start",
  139. regex: /#{/,
  140. push: "start"
  141. }, {
  142. token: "string.end",
  143. regex: /"/,
  144. next: "pop"
  145. }, {
  146. defaultToken: "string"
  147. }]
  148. }, {
  149. token: "string.start",
  150. regex: /`/,
  151. push: [{
  152. token: "constant.language.escape",
  153. regex: escapedChars
  154. }, {
  155. token: "paren.start",
  156. regex: /#{/,
  157. push: "start"
  158. }, {
  159. token: "string.end",
  160. regex: /`/,
  161. next: "pop"
  162. }, {
  163. defaultToken: "string"
  164. }]
  165. }, {
  166. token: ["constant.other.symbol.ruby", "string.start"],
  167. regex: /(:)?(')/,
  168. push: [{
  169. token: "constant.language.escape",
  170. regex: /\\['\\]/
  171. }, {
  172. token: "string.end",
  173. regex: /'/,
  174. next: "pop"
  175. }, {
  176. defaultToken: "string"
  177. }]
  178. }, {
  179. token: "string.start",//doesn't see any differences between strings and array of strings in highlighting
  180. regex: /%[qwx]([(\[<{^|%])/, onMatch: function (val, state, stack) {
  181. if (stack.length)
  182. stack = [];
  183. var paren = val[val.length - 1];
  184. stack.unshift(paren, state);
  185. this.next = "qStateWithoutInterpolation";
  186. return this.token;
  187. }
  188. }, {
  189. token: "string.start", //doesn't see any differences between strings and array of strings in highlighting
  190. regex: /%[QWX]?([(\[<{^|%])/, onMatch: function (val, state, stack) {
  191. if (stack.length)
  192. stack = [];
  193. var paren = val[val.length - 1];
  194. stack.unshift(paren, state);
  195. this.next = "qStateWithInterpolation";
  196. return this.token;
  197. }
  198. }, {
  199. token: "constant.other.symbol.ruby", //doesn't see any differences between symbols and array of symbols in highlighting
  200. regex: /%[si]([(\[<{^|%])/, onMatch: function (val, state, stack) {
  201. if (stack.length)
  202. stack = [];
  203. var paren = val[val.length - 1];
  204. stack.unshift(paren, state);
  205. this.next = "sStateWithoutInterpolation";
  206. return this.token;
  207. }
  208. }, {
  209. token: "constant.other.symbol.ruby", //doesn't see any differences between symbols and array of symbols in highlighting
  210. regex: /%[SI]([(\[<{^|%])/, onMatch: function (val, state, stack) {
  211. if (stack.length)
  212. stack = [];
  213. var paren = val[val.length - 1];
  214. stack.unshift(paren, state);
  215. this.next = "sStateWithInterpolation";
  216. return this.token;
  217. }
  218. }, {
  219. token: "string.regexp",
  220. regex: /%[r]([(\[<{^|%])/, onMatch: function (val, state, stack) {
  221. if (stack.length)
  222. stack = [];
  223. var paren = val[val.length - 1];
  224. stack.unshift(paren, state);
  225. this.next = "rState";
  226. return this.token;
  227. }
  228. }],
  229. {
  230. token: "punctuation", // namespaces aren't symbols
  231. regex: "::"
  232. },
  233. instanceVariable,
  234. {
  235. token: "variable.global", // global variable
  236. regex: "[$][a-zA-Z_\\d]+"
  237. }, {
  238. token: "support.class", // class name
  239. regex: "[A-Z][a-zA-Z_\\d]*"
  240. }, {
  241. token: ["punctuation.operator", "support.function"],
  242. regex: /(\.)([a-zA-Z_\d]+)(?=\()/
  243. }, {
  244. token: ["punctuation.operator", "identifier"],
  245. regex: /(\.)([a-zA-Z_][a-zA-Z_\d]*)/
  246. }, {
  247. token: "string.character",
  248. regex: "\\B\\?(?:" + escapedChars + "|\\S)"
  249. }, {
  250. token: "punctuation.operator",
  251. regex: /\?(?=.+:)/
  252. },
  253. constantNumericRational,
  254. constantNumericComplex,
  255. constantOtherSymbol,
  256. constantNumericHex,
  257. constantNumericFloat,
  258. constantNumericBinary,
  259. constantNumericDecimal,
  260. constantNumericOctal,
  261. {
  262. token: "constant.language.boolean",
  263. regex: "(?:true|false)\\b"
  264. }, {
  265. token: keywordMapper,
  266. regex: "[a-zA-Z_$][a-zA-Z0-9_$]*\\b"
  267. }, {
  268. token: "punctuation.separator.key-value",
  269. regex: "=>"
  270. }, {
  271. stateName: "heredoc",
  272. onMatch: function (value, currentState, stack) {
  273. var next = (value[2] == '-' || value[2] == '~') ? "indentedHeredoc" : "heredoc";
  274. var tokens = value.split(this.splitRegex);
  275. stack.push(next, tokens[3]);
  276. return [
  277. {type: "constant", value: tokens[1]},
  278. {type: "string", value: tokens[2]},
  279. {type: "support.class", value: tokens[3]},
  280. {type: "string", value: tokens[4]}
  281. ];
  282. },
  283. regex: "(<<[-~]?)(['\"`]?)([\\w]+)(['\"`]?)",
  284. rules: {
  285. heredoc: [{
  286. onMatch: function(value, currentState, stack) {
  287. if (value === stack[1]) {
  288. stack.shift();
  289. stack.shift();
  290. this.next = stack[0] || "start";
  291. return "support.class";
  292. }
  293. this.next = "";
  294. return "string";
  295. },
  296. regex: ".*$",
  297. next: "start"
  298. }],
  299. indentedHeredoc: [{
  300. token: "string",
  301. regex: "^ +"
  302. }, {
  303. onMatch: function(value, currentState, stack) {
  304. if (value === stack[1]) {
  305. stack.shift();
  306. stack.shift();
  307. this.next = stack[0] || "start";
  308. return "support.class";
  309. }
  310. this.next = "";
  311. return "string";
  312. },
  313. regex: ".*$",
  314. next: "start"
  315. }]
  316. }
  317. }, {
  318. regex: "$",
  319. token: "empty",
  320. next: function(currentState, stack) {
  321. if (stack[0] === "heredoc" || stack[0] === "indentedHeredoc")
  322. return stack[0];
  323. return currentState;
  324. }
  325. }, {
  326. token: "keyword.operator",
  327. regex: "!|\\$|%|&|\\*|/|\\-\\-|\\-|\\+\\+|\\+|~|===|==|=|!=|!==|<=|>=|<<=|>>=|>>>=|<>|<|>|!|&&|\\|\\||\\?\\:|\\*=|%=|\\+=|\\-=|&=|\\^=|\\||\\b(?:in|instanceof|new|delete|typeof|void)"
  328. }, {
  329. token: "paren.lparen",
  330. regex: "[[({]"
  331. }, {
  332. token: "paren.rparen",
  333. regex: "[\\])}]",
  334. onMatch: function(value, currentState, stack) {
  335. this.next = '';
  336. if (value == "}" && stack.length > 1 && stack[1] != "start") {
  337. stack.shift();
  338. this.next = stack.shift();
  339. }
  340. return this.token;
  341. }
  342. }, {
  343. token: "text",
  344. regex: "\\s+"
  345. }, {
  346. token: "punctuation.operator",
  347. regex: /[?:,;.]/
  348. }
  349. ],
  350. "comment": [
  351. {
  352. token: "comment.multiline", // closing comment
  353. regex: "^=end(?=$|\\s.*$)",
  354. next: "start"
  355. }, {
  356. token: "comment", // comment spanning whole line
  357. regex: ".+"
  358. }
  359. ],
  360. "qStateWithInterpolation": [{
  361. token: "string.start",// excluded nested |^% due to difficulty in realization
  362. regex: /[(\[<{]/, onMatch: function (val, state, stack) {
  363. if (stack.length && val === stack[0]) {
  364. stack.unshift(val, state);
  365. return this.token;
  366. }
  367. return "string";
  368. }
  369. }, {
  370. token: "constant.language.escape",
  371. regex: escapedChars
  372. }, {
  373. token: "constant.language.escape",
  374. regex: /\\./
  375. }, {
  376. token: "paren.start",
  377. regex: /#{/,
  378. push: "start"
  379. }, {
  380. token: "string.end",
  381. regex: /[)\]>}^|%]/, onMatch: function (val, state, stack) {
  382. if (stack.length && val === closeParen[stack[0]]) {
  383. stack.shift();
  384. this.next = stack.shift();
  385. return this.token;
  386. }
  387. this.next = '';
  388. return "string";
  389. }
  390. }, {
  391. defaultToken: "string"
  392. }],
  393. "qStateWithoutInterpolation": [{
  394. token: "string.start",// excluded nested |^% due to difficulty in realization
  395. regex: /[(\[<{]/, onMatch: function (val, state, stack) {
  396. if (stack.length && val === stack[0]) {
  397. stack.unshift(val, state);
  398. return this.token;
  399. }
  400. return "string";
  401. }
  402. }, {
  403. token: "constant.language.escape",
  404. regex: /\\['\\]/
  405. }, {
  406. token: "constant.language.escape",
  407. regex: /\\./
  408. }, {
  409. token: "string.end",
  410. regex: /[)\]>}^|%]/, onMatch: function (val, state, stack) {
  411. if (stack.length && val === closeParen[stack[0]]) {
  412. stack.shift();
  413. this.next = stack.shift();
  414. return this.token;
  415. }
  416. this.next = '';
  417. return "string";
  418. }
  419. }, {
  420. defaultToken: "string"
  421. }],
  422. "sStateWithoutInterpolation": [{
  423. token: "constant.other.symbol.ruby",// excluded nested |^% due to difficulty in realization
  424. regex: /[(\[<{]/, onMatch: function (val, state, stack) {
  425. if (stack.length && val === stack[0]) {
  426. stack.unshift(val, state);
  427. return this.token;
  428. }
  429. return "constant.other.symbol.ruby";
  430. }
  431. }, {
  432. token: "constant.other.symbol.ruby",
  433. regex: /[)\]>}^|%]/, onMatch: function (val, state, stack) {
  434. if (stack.length && val === closeParen[stack[0]]) {
  435. stack.shift();
  436. this.next = stack.shift();
  437. return this.token;
  438. }
  439. this.next = '';
  440. return "constant.other.symbol.ruby";
  441. }
  442. }, {
  443. defaultToken: "constant.other.symbol.ruby"
  444. }],
  445. "sStateWithInterpolation": [{
  446. token: "constant.other.symbol.ruby",// excluded nested |^% due to difficulty in realization
  447. regex: /[(\[<{]/, onMatch: function (val, state, stack) {
  448. if (stack.length && val === stack[0]) {
  449. stack.unshift(val, state);
  450. return this.token;
  451. }
  452. return "constant.other.symbol.ruby";
  453. }
  454. }, {
  455. token: "constant.language.escape",
  456. regex: escapedChars
  457. }, {
  458. token: "constant.language.escape",
  459. regex: /\\./
  460. }, {
  461. token: "paren.start",
  462. regex: /#{/,
  463. push: "start"
  464. }, {
  465. token: "constant.other.symbol.ruby",
  466. regex: /[)\]>}^|%]/, onMatch: function (val, state, stack) {
  467. if (stack.length && val === closeParen[stack[0]]) {
  468. stack.shift();
  469. this.next = stack.shift();
  470. return this.token;
  471. }
  472. this.next = '';
  473. return "constant.other.symbol.ruby";
  474. }
  475. }, {
  476. defaultToken: "constant.other.symbol.ruby"
  477. }],
  478. "rState": [{
  479. token: "string.regexp",// excluded nested |^% due to difficulty in realization
  480. regex: /[(\[<{]/, onMatch: function (val, state, stack) {
  481. if (stack.length && val === stack[0]) {
  482. stack.unshift(val, state);
  483. return this.token;
  484. }
  485. return "constant.language.escape";
  486. }
  487. }, {
  488. token: "paren.start",
  489. regex: /#{/,
  490. push: "start"
  491. }, {
  492. token: "string.regexp",
  493. regex: /\//
  494. }, {
  495. token: "string.regexp",
  496. regex: /[)\]>}^|%][imxouesn]*/, onMatch: function (val, state, stack) {
  497. if (stack.length && val[0] === closeParen[stack[0]]) {
  498. stack.shift();
  499. this.next = stack.shift();
  500. return this.token;
  501. }
  502. this.next = '';
  503. return "constant.language.escape";
  504. }
  505. },
  506. {include: "regex"},
  507. {
  508. defaultToken: "string.regexp"
  509. }],
  510. "regex": [
  511. {// character classes
  512. token: "regexp.keyword",
  513. regex: /\\[wWdDhHsS]/
  514. }, {
  515. token: "constant.language.escape",
  516. regex: /\\[AGbBzZ]/
  517. }, {
  518. token: "constant.language.escape",
  519. regex: /\\g<[a-zA-Z0-9]*>/
  520. }, {
  521. token: ["constant.language.escape", "regexp.keyword", "constant.language.escape"],
  522. regex: /(\\p{\^?)(Alnum|Alpha|Blank|Cntrl|Digit|Graph|Lower|Print|Punct|Space|Upper|XDigit|Word|ASCII|Any|Assigned|Arabic|Armenian|Balinese|Bengali|Bopomofo|Braille|Buginese|Buhid|Canadian_Aboriginal|Carian|Cham|Cherokee|Common|Coptic|Cuneiform|Cypriot|Cyrillic|Deseret|Devanagari|Ethiopic|Georgian|Glagolitic|Gothic|Greek|Gujarati|Gurmukhi|Han|Hangul|Hanunoo|Hebrew|Hiragana|Inherited|Kannada|Katakana|Kayah_Li|Kharoshthi|Khmer|Lao|Latin|Lepcha|Limbu|Linear_B|Lycian|Lydian|Malayalam|Mongolian|Myanmar|New_Tai_Lue|Nko|Ogham|Ol_Chiki|Old_Italic|Old_Persian|Oriya|Osmanya|Phags_Pa|Phoenician|Rejang|Runic|Saurashtra|Shavian|Sinhala|Sundanese|Syloti_Nagri|Syriac|Tagalog|Tagbanwa|Tai_Le|Tamil|Telugu|Thaana|Thai|Tibetan|Tifinagh|Ugaritic|Vai|Yi|Ll|Lm|Lt|Lu|Lo|Mn|Mc|Me|Nd|Nl|Pc|Pd|Ps|Pe|Pi|Pf|Po|No|Sm|Sc|Sk|So|Zs|Zl|Zp|Cc|Cf|Cn|Co|Cs|N|L|M|P|S|Z|C)(})/
  523. }, {
  524. token: ["constant.language.escape", "invalid", "constant.language.escape"],
  525. regex: /(\\p{\^?)([^/]*)(})/
  526. }, {// escapes
  527. token: "regexp.keyword.operator",
  528. regex: "\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"
  529. }, {// flag
  530. token: "string.regexp",
  531. regex: /[/][imxouesn]*/,
  532. next: "start"
  533. }, {// invalid operators
  534. token: "invalid",
  535. regex: /\{\d+\b,?\d*\}[+*]|[+*$^?][+*]|[$^][?]|\?{3,}/
  536. }, {// operators
  537. token: "constant.language.escape",
  538. regex: /\(\?(?:[:=!>]|<'?[a-zA-Z]*'?>|<[=!])|\)|\{\d+\b,?\d*\}|[+*]\?|[()$^+*?.]/
  539. }, {
  540. token: "constant.language.delimiter",
  541. regex: /\|/
  542. }, {
  543. token: "regexp.keyword",
  544. regex: /\[\[:(?:alnum|alpha|blank|cntrl|digit|graph|lower|print|punct|space|upper|xdigit|word|ascii):\]\]/
  545. }, {
  546. token: "constant.language.escape",
  547. regex: /\[\^?/,
  548. push: "regex_character_class"
  549. }, {
  550. defaultToken: "string.regexp"
  551. }
  552. ],
  553. "regex_character_class": [
  554. {
  555. token: "regexp.keyword",
  556. regex: /\\[wWdDhHsS]/
  557. }, {
  558. token: "regexp.charclass.keyword.operator",
  559. regex: "\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"
  560. }, {
  561. token: "constant.language.escape",
  562. regex: /&?&?\[\^?/,
  563. push: "regex_character_class"
  564. }, {
  565. token: "constant.language.escape",
  566. regex: "]",
  567. next: "pop"
  568. }, {
  569. token: "constant.language.escape",
  570. regex: "-"
  571. }, {
  572. defaultToken: "string.regexp.characterclass"
  573. }
  574. ]
  575. };
  576. this.normalizeRules();
  577. };
  578. oop.inherits(RubyHighlightRules, TextHighlightRules);
  579. exports.RubyHighlightRules = RubyHighlightRules;
  580. });
  581. ace.define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"], function(require, exports, module) {
  582. "use strict";
  583. var Range = require("../range").Range;
  584. var MatchingBraceOutdent = function() {};
  585. (function() {
  586. this.checkOutdent = function(line, input) {
  587. if (! /^\s+$/.test(line))
  588. return false;
  589. return /^\s*\}/.test(input);
  590. };
  591. this.autoOutdent = function(doc, row) {
  592. var line = doc.getLine(row);
  593. var match = line.match(/^(\s*\})/);
  594. if (!match) return 0;
  595. var column = match[1].length;
  596. var openBracePos = doc.findMatchingBracket({row: row, column: column});
  597. if (!openBracePos || openBracePos.row == row) return 0;
  598. var indent = this.$getIndent(doc.getLine(openBracePos.row));
  599. doc.replace(new Range(row, 0, row, column-1), indent);
  600. };
  601. this.$getIndent = function(line) {
  602. return line.match(/^\s*/)[0];
  603. };
  604. }).call(MatchingBraceOutdent.prototype);
  605. exports.MatchingBraceOutdent = MatchingBraceOutdent;
  606. });
  607. ace.define("ace/mode/folding/ruby",["require","exports","module","ace/lib/oop","ace/mode/folding/fold_mode","ace/range","ace/token_iterator"], function (require, exports, module) {
  608. "use strict";
  609. var oop = require("../../lib/oop");
  610. var BaseFoldMode = require("./fold_mode").FoldMode;
  611. var Range = require("../../range").Range;
  612. var TokenIterator = require("../../token_iterator").TokenIterator;
  613. var FoldMode = exports.FoldMode = function () {
  614. };
  615. oop.inherits(FoldMode, BaseFoldMode);
  616. (function () {
  617. this.indentKeywords = {
  618. "class": 1,
  619. "def": 1,
  620. "module": 1,
  621. "do": 1,
  622. "unless": 1,
  623. "if": 1,
  624. "while": 1,
  625. "for": 1,
  626. "until": 1,
  627. "begin": 1,
  628. "else": 0,
  629. "elsif": 0,
  630. "rescue": 0,
  631. "ensure": 0,
  632. "when": 0,
  633. "end": -1,
  634. "case": 1,
  635. "=begin": 1,
  636. "=end": -1
  637. };
  638. this.foldingStartMarker = /(?:\s|^)(def|do|while|class|unless|module|if|for|until|begin|else|elsif|case|rescue|ensure|when)\b|({\s*$)|(=begin)/;
  639. this.foldingStopMarker = /(=end(?=$|\s.*$))|(^\s*})|\b(end)\b/;
  640. this.getFoldWidget = function (session, foldStyle, row) {
  641. var line = session.getLine(row);
  642. var isStart = this.foldingStartMarker.test(line);
  643. var isEnd = this.foldingStopMarker.test(line);
  644. if (isStart && !isEnd) {
  645. var match = line.match(this.foldingStartMarker);
  646. if (match[1]) {
  647. if (match[1] == "if" || match[1] == "else" || match[1] == "while" || match[1] == "until" || match[1] == "unless") {
  648. if (match[1] == "else" && /^\s*else\s*$/.test(line) === false) {
  649. return;
  650. }
  651. if (/^\s*(?:if|else|while|until|unless)\s*/.test(line) === false) {
  652. return;
  653. }
  654. }
  655. if (match[1] == "when") {
  656. if (/\sthen\s/.test(line) === true) {
  657. return;
  658. }
  659. }
  660. if (session.getTokenAt(row, match.index + 2).type === "keyword")
  661. return "start";
  662. } else if (match[3]) {
  663. if (session.getTokenAt(row, match.index + 1).type === "comment.multiline")
  664. return "start";
  665. } else {
  666. return "start";
  667. }
  668. }
  669. if (foldStyle != "markbeginend" || !isEnd || isStart && isEnd)
  670. return "";
  671. var match = line.match(this.foldingStopMarker);
  672. if (match[3] === "end") {
  673. if (session.getTokenAt(row, match.index + 1).type === "keyword")
  674. return "end";
  675. } else if (match[1]) {
  676. if (session.getTokenAt(row, match.index + 1).type === "comment.multiline")
  677. return "end";
  678. } else
  679. return "end";
  680. };
  681. this.getFoldWidgetRange = function (session, foldStyle, row) {
  682. var line = session.doc.getLine(row);
  683. var match = this.foldingStartMarker.exec(line);
  684. if (match) {
  685. if (match[1] || match[3])
  686. return this.rubyBlock(session, row, match.index + 2);
  687. return this.openingBracketBlock(session, "{", row, match.index);
  688. }
  689. var match = this.foldingStopMarker.exec(line);
  690. if (match) {
  691. if (match[3] === "end") {
  692. if (session.getTokenAt(row, match.index + 1).type === "keyword")
  693. return this.rubyBlock(session, row, match.index + 1);
  694. }
  695. if (match[1] === "=end") {
  696. if (session.getTokenAt(row, match.index + 1).type === "comment.multiline")
  697. return this.rubyBlock(session, row, match.index + 1);
  698. }
  699. return this.closingBracketBlock(session, "}", row, match.index + match[0].length);
  700. }
  701. };
  702. this.rubyBlock = function (session, row, column, tokenRange) {
  703. var stream = new TokenIterator(session, row, column);
  704. var token = stream.getCurrentToken();
  705. if (!token || (token.type != "keyword" && token.type != "comment.multiline"))
  706. return;
  707. var val = token.value;
  708. var line = session.getLine(row);
  709. switch (token.value) {
  710. case "if":
  711. case "unless":
  712. case "while":
  713. case "until":
  714. var checkToken = new RegExp("^\\s*" + token.value);
  715. if (!checkToken.test(line)) {
  716. return;
  717. }
  718. var dir = this.indentKeywords[val];
  719. break;
  720. case "when":
  721. if (/\sthen\s/.test(line)) {
  722. return;
  723. }
  724. case "elsif":
  725. case "rescue":
  726. case "ensure":
  727. var dir = 1;
  728. break;
  729. case "else":
  730. var checkToken = new RegExp("^\\s*" + token.value + "\\s*$");
  731. if (!checkToken.test(line)) {
  732. return;
  733. }
  734. var dir = 1;
  735. break;
  736. default:
  737. var dir = this.indentKeywords[val];
  738. break;
  739. }
  740. var stack = [val];
  741. if (!dir)
  742. return;
  743. var startColumn = dir === -1 ? session.getLine(row - 1).length : session.getLine(row).length;
  744. var startRow = row;
  745. var ranges = [];
  746. ranges.push(stream.getCurrentTokenRange());
  747. stream.step = dir === -1 ? stream.stepBackward : stream.stepForward;
  748. if (token.type == "comment.multiline") {
  749. while (token = stream.step()) {
  750. if (token.type !== "comment.multiline")
  751. continue;
  752. if (dir == 1) {
  753. startColumn = 6;
  754. if (token.value == "=end") {
  755. break;
  756. }
  757. } else {
  758. if (token.value == "=begin") {
  759. break;
  760. }
  761. }
  762. }
  763. } else {
  764. while (token = stream.step()) {
  765. var ignore = false;
  766. if (token.type !== "keyword")
  767. continue;
  768. var level = dir * this.indentKeywords[token.value];
  769. line = session.getLine(stream.getCurrentTokenRow());
  770. switch (token.value) {
  771. case "do":
  772. for (var i = stream.$tokenIndex - 1; i >= 0; i--) {
  773. var prevToken = stream.$rowTokens[i];
  774. if (prevToken && (prevToken.value == "while" || prevToken.value == "until" || prevToken.value == "for")) {
  775. level = 0;
  776. break;
  777. }
  778. }
  779. break;
  780. case "else":
  781. var checkToken = new RegExp("^\\s*" + token.value + "\\s*$");
  782. if (!checkToken.test(line) || val == "case") {
  783. level = 0;
  784. ignore = true;
  785. }
  786. break;
  787. case "if":
  788. case "unless":
  789. case "while":
  790. case "until":
  791. var checkToken = new RegExp("^\\s*" + token.value);
  792. if (!checkToken.test(line)) {
  793. level = 0;
  794. ignore = true;
  795. }
  796. break;
  797. case "when":
  798. if (/\sthen\s/.test(line) || val == "case") {
  799. level = 0;
  800. ignore = true;
  801. }
  802. break;
  803. }
  804. if (level > 0) {
  805. stack.unshift(token.value);
  806. } else if (level <= 0 && ignore === false) {
  807. stack.shift();
  808. if (!stack.length) {
  809. if ((val == "while" || val == "until" || val == "for") && token.value != "do") {
  810. break;
  811. }
  812. if (token.value == "do" && dir == -1 && level != 0)
  813. break;
  814. if (token.value != "do")
  815. break;
  816. }
  817. if (level === 0) {
  818. stack.unshift(token.value);
  819. }
  820. }
  821. }
  822. }
  823. if (!token)
  824. return null;
  825. if (tokenRange) {
  826. ranges.push(stream.getCurrentTokenRange());
  827. return ranges;
  828. }
  829. var row = stream.getCurrentTokenRow();
  830. if (dir === -1) {
  831. if (token.type === "comment.multiline") {
  832. var endColumn = 6;
  833. } else {
  834. var endColumn = session.getLine(row).length;
  835. }
  836. return new Range(row, endColumn, startRow - 1, startColumn);
  837. } else
  838. return new Range(startRow, startColumn, row - 1, session.getLine(row - 1).length);
  839. };
  840. }).call(FoldMode.prototype);
  841. });
  842. ace.define("ace/mode/ruby",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/ruby_highlight_rules","ace/mode/matching_brace_outdent","ace/range","ace/mode/behaviour/cstyle","ace/mode/folding/ruby"], function(require, exports, module) {
  843. "use strict";
  844. var oop = require("../lib/oop");
  845. var TextMode = require("./text").Mode;
  846. var RubyHighlightRules = require("./ruby_highlight_rules").RubyHighlightRules;
  847. var MatchingBraceOutdent = require("./matching_brace_outdent").MatchingBraceOutdent;
  848. var Range = require("../range").Range;
  849. var CstyleBehaviour = require("./behaviour/cstyle").CstyleBehaviour;
  850. var FoldMode = require("./folding/ruby").FoldMode;
  851. var Mode = function() {
  852. this.HighlightRules = RubyHighlightRules;
  853. this.$outdent = new MatchingBraceOutdent();
  854. this.$behaviour = new CstyleBehaviour();
  855. this.foldingRules = new FoldMode();
  856. this.indentKeywords = this.foldingRules.indentKeywords;
  857. };
  858. oop.inherits(Mode, TextMode);
  859. (function() {
  860. this.lineCommentStart = "#";
  861. this.getNextLineIndent = function(state, line, tab) {
  862. var indent = this.$getIndent(line);
  863. var tokenizedLine = this.getTokenizer().getLineTokens(line, state);
  864. var tokens = tokenizedLine.tokens;
  865. if (tokens.length && tokens[tokens.length - 1].type == "comment") {
  866. return indent;
  867. }
  868. if (state == "start") {
  869. var match = line.match(/^.*[\{\(\[]\s*$/);
  870. var startingClassOrMethod = line.match(/^\s*(class|def|module)\s.*$/);
  871. var startingDoBlock = line.match(/.*do(\s*|\s+\|.*\|\s*)$/);
  872. var startingConditional = line.match(/^\s*(if|else|when|elsif|unless|while|for|begin|rescue|ensure)\s*/);
  873. if (match || startingClassOrMethod || startingDoBlock || startingConditional) {
  874. indent += tab;
  875. }
  876. }
  877. return indent;
  878. };
  879. this.checkOutdent = function(state, line, input) {
  880. return /^\s+(end|else|rescue|ensure)$/.test(line + input) || this.$outdent.checkOutdent(line, input);
  881. };
  882. this.autoOutdent = function(state, session, row) {
  883. var line = session.getLine(row);
  884. if (/}/.test(line))
  885. return this.$outdent.autoOutdent(session, row);
  886. var indent = this.$getIndent(line);
  887. var prevLine = session.getLine(row - 1);
  888. var prevIndent = this.$getIndent(prevLine);
  889. var tab = session.getTabString();
  890. if (prevIndent.length <= indent.length) {
  891. if (indent.slice(-tab.length) == tab)
  892. session.remove(new Range(row, indent.length - tab.length, row, indent.length));
  893. }
  894. };
  895. this.getMatching = function(session, row, column) {
  896. if (row == undefined) {
  897. var pos = session.selection.lead;
  898. column = pos.column;
  899. row = pos.row;
  900. }
  901. var startToken = session.getTokenAt(row, column);
  902. if (startToken && startToken.value in this.indentKeywords)
  903. return this.foldingRules.rubyBlock(session, row, column, true);
  904. };
  905. this.$id = "ace/mode/ruby";
  906. this.snippetFileId = "ace/snippets/ruby";
  907. }).call(Mode.prototype);
  908. exports.Mode = Mode;
  909. }); (function() {
  910. ace.require(["ace/mode/ruby"], function(m) {
  911. if (typeof module == "object" && typeof exports == "object" && module) {
  912. module.exports = m;
  913. }
  914. });
  915. })();