r.js 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. // CodeMirror, copyright (c) by Marijn Haverbeke and others
  2. // Distributed under an MIT license: https://codemirror.net/LICENSE
  3. ;(function (mod) {
  4. if (typeof exports == 'object' && typeof module == 'object')
  5. // CommonJS
  6. mod(require('../../lib/codemirror'))
  7. else if (typeof define == 'function' && define.amd)
  8. // AMD
  9. define(['../../lib/codemirror'], mod)
  10. // Plain browser env
  11. else mod(CodeMirror)
  12. })(function (CodeMirror) {
  13. 'use strict'
  14. CodeMirror.registerHelper('wordChars', 'r', /[\w.]/)
  15. CodeMirror.defineMode('r', function (config) {
  16. function wordObj(words) {
  17. var res = {}
  18. for (var i = 0; i < words.length; ++i) res[words[i]] = true
  19. return res
  20. }
  21. var commonAtoms = ['NULL', 'NA', 'Inf', 'NaN', 'NA_integer_', 'NA_real_', 'NA_complex_', 'NA_character_', 'TRUE', 'FALSE']
  22. var commonBuiltins = ['list', 'quote', 'bquote', 'eval', 'return', 'call', 'parse', 'deparse']
  23. var commonKeywords = ['if', 'else', 'repeat', 'while', 'function', 'for', 'in', 'next', 'break']
  24. var commonBlockKeywords = ['if', 'else', 'repeat', 'while', 'function', 'for']
  25. CodeMirror.registerHelper('hintWords', 'r', commonAtoms.concat(commonBuiltins, commonKeywords))
  26. var atoms = wordObj(commonAtoms)
  27. var builtins = wordObj(commonBuiltins)
  28. var keywords = wordObj(commonKeywords)
  29. var blockkeywords = wordObj(commonBlockKeywords)
  30. var opChars = /[+\-*\/^<>=!&|~$:]/
  31. var curPunc
  32. function tokenBase(stream, state) {
  33. curPunc = null
  34. var ch = stream.next()
  35. if (ch == '#') {
  36. stream.skipToEnd()
  37. return 'comment'
  38. } else if (ch == '0' && stream.eat('x')) {
  39. stream.eatWhile(/[\da-f]/i)
  40. return 'number'
  41. } else if (ch == '.' && stream.eat(/\d/)) {
  42. stream.match(/\d*(?:e[+\-]?\d+)?/)
  43. return 'number'
  44. } else if (/\d/.test(ch)) {
  45. stream.match(/\d*(?:\.\d+)?(?:e[+\-]\d+)?L?/)
  46. return 'number'
  47. } else if (ch == "'" || ch == '"') {
  48. state.tokenize = tokenString(ch)
  49. return 'string'
  50. } else if (ch == '`') {
  51. stream.match(/[^`]+`/)
  52. return 'variable-3'
  53. } else if (ch == '.' && stream.match(/.(?:[.]|\d+)/)) {
  54. return 'keyword'
  55. } else if (/[a-zA-Z\.]/.test(ch)) {
  56. stream.eatWhile(/[\w\.]/)
  57. var word = stream.current()
  58. if (atoms.propertyIsEnumerable(word)) return 'atom'
  59. if (keywords.propertyIsEnumerable(word)) {
  60. // Block keywords start new blocks, except 'else if', which only starts
  61. // one new block for the 'if', no block for the 'else'.
  62. if (blockkeywords.propertyIsEnumerable(word) && !stream.match(/\s*if(\s+|$)/, false)) curPunc = 'block'
  63. return 'keyword'
  64. }
  65. if (builtins.propertyIsEnumerable(word)) return 'builtin'
  66. return 'variable'
  67. } else if (ch == '%') {
  68. if (stream.skipTo('%')) stream.next()
  69. return 'operator variable-2'
  70. } else if ((ch == '<' && stream.eat('-')) || (ch == '<' && stream.match('<-')) || (ch == '-' && stream.match(/>>?/))) {
  71. return 'operator arrow'
  72. } else if (ch == '=' && state.ctx.argList) {
  73. return 'arg-is'
  74. } else if (opChars.test(ch)) {
  75. if (ch == '$') return 'operator dollar'
  76. stream.eatWhile(opChars)
  77. return 'operator'
  78. } else if (/[\(\){}\[\];]/.test(ch)) {
  79. curPunc = ch
  80. if (ch == ';') return 'semi'
  81. return null
  82. } else {
  83. return null
  84. }
  85. }
  86. function tokenString(quote) {
  87. return function (stream, state) {
  88. if (stream.eat('\\')) {
  89. var ch = stream.next()
  90. if (ch == 'x') stream.match(/^[a-f0-9]{2}/i)
  91. else if ((ch == 'u' || ch == 'U') && stream.eat('{') && stream.skipTo('}')) stream.next()
  92. else if (ch == 'u') stream.match(/^[a-f0-9]{4}/i)
  93. else if (ch == 'U') stream.match(/^[a-f0-9]{8}/i)
  94. else if (/[0-7]/.test(ch)) stream.match(/^[0-7]{1,2}/)
  95. return 'string-2'
  96. } else {
  97. var next
  98. while ((next = stream.next()) != null) {
  99. if (next == quote) {
  100. state.tokenize = tokenBase
  101. break
  102. }
  103. if (next == '\\') {
  104. stream.backUp(1)
  105. break
  106. }
  107. }
  108. return 'string'
  109. }
  110. }
  111. }
  112. var ALIGN_YES = 1,
  113. ALIGN_NO = 2,
  114. BRACELESS = 4
  115. function push(state, type, stream) {
  116. state.ctx = { type: type, indent: state.indent, flags: 0, column: stream.column(), prev: state.ctx }
  117. }
  118. function setFlag(state, flag) {
  119. var ctx = state.ctx
  120. state.ctx = { type: ctx.type, indent: ctx.indent, flags: ctx.flags | flag, column: ctx.column, prev: ctx.prev }
  121. }
  122. function pop(state) {
  123. state.indent = state.ctx.indent
  124. state.ctx = state.ctx.prev
  125. }
  126. return {
  127. startState: function () {
  128. return { tokenize: tokenBase, ctx: { type: 'top', indent: -config.indentUnit, flags: ALIGN_NO }, indent: 0, afterIdent: false }
  129. },
  130. token: function (stream, state) {
  131. if (stream.sol()) {
  132. if ((state.ctx.flags & 3) == 0) state.ctx.flags |= ALIGN_NO
  133. if (state.ctx.flags & BRACELESS) pop(state)
  134. state.indent = stream.indentation()
  135. }
  136. if (stream.eatSpace()) return null
  137. var style = state.tokenize(stream, state)
  138. if (style != 'comment' && (state.ctx.flags & ALIGN_NO) == 0) setFlag(state, ALIGN_YES)
  139. if ((curPunc == ';' || curPunc == '{' || curPunc == '}') && state.ctx.type == 'block') pop(state)
  140. if (curPunc == '{') push(state, '}', stream)
  141. else if (curPunc == '(') {
  142. push(state, ')', stream)
  143. if (state.afterIdent) state.ctx.argList = true
  144. } else if (curPunc == '[') push(state, ']', stream)
  145. else if (curPunc == 'block') push(state, 'block', stream)
  146. else if (curPunc == state.ctx.type) pop(state)
  147. else if (state.ctx.type == 'block' && style != 'comment') setFlag(state, BRACELESS)
  148. state.afterIdent = style == 'variable' || style == 'keyword'
  149. return style
  150. },
  151. indent: function (state, textAfter) {
  152. if (state.tokenize != tokenBase) return 0
  153. var firstChar = textAfter && textAfter.charAt(0),
  154. ctx = state.ctx,
  155. closing = firstChar == ctx.type
  156. if (ctx.flags & BRACELESS) ctx = ctx.prev
  157. if (ctx.type == 'block') return ctx.indent + (firstChar == '{' ? 0 : config.indentUnit)
  158. else if (ctx.flags & ALIGN_YES) return ctx.column + (closing ? 0 : 1)
  159. else return ctx.indent + (closing ? 0 : config.indentUnit)
  160. },
  161. lineComment: '#',
  162. }
  163. })
  164. CodeMirror.defineMIME('text/x-rsrc', 'r')
  165. })