simple.js 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  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.defineSimpleMode = function (name, states) {
  15. CodeMirror.defineMode(name, function (config) {
  16. return CodeMirror.simpleMode(config, states)
  17. })
  18. }
  19. CodeMirror.simpleMode = function (config, states) {
  20. ensureState(states, 'start')
  21. var states_ = {},
  22. meta = states.meta || {},
  23. hasIndentation = false
  24. for (var state in states)
  25. if (state != meta && states.hasOwnProperty(state)) {
  26. var list = (states_[state] = []),
  27. orig = states[state]
  28. for (var i = 0; i < orig.length; i++) {
  29. var data = orig[i]
  30. list.push(new Rule(data, states))
  31. if (data.indent || data.dedent) hasIndentation = true
  32. }
  33. }
  34. var mode = {
  35. startState: function () {
  36. return { state: 'start', pending: null, local: null, localState: null, indent: hasIndentation ? [] : null }
  37. },
  38. copyState: function (state) {
  39. var s = { state: state.state, pending: state.pending, local: state.local, localState: null, indent: state.indent && state.indent.slice(0) }
  40. if (state.localState) s.localState = CodeMirror.copyState(state.local.mode, state.localState)
  41. if (state.stack) s.stack = state.stack.slice(0)
  42. for (var pers = state.persistentStates; pers; pers = pers.next)
  43. s.persistentStates = {
  44. mode: pers.mode,
  45. spec: pers.spec,
  46. state: pers.state == state.localState ? s.localState : CodeMirror.copyState(pers.mode, pers.state),
  47. next: s.persistentStates,
  48. }
  49. return s
  50. },
  51. token: tokenFunction(states_, config),
  52. innerMode: function (state) {
  53. return state.local && { mode: state.local.mode, state: state.localState }
  54. },
  55. indent: indentFunction(states_, meta),
  56. }
  57. if (meta) for (var prop in meta) if (meta.hasOwnProperty(prop)) mode[prop] = meta[prop]
  58. return mode
  59. }
  60. function ensureState(states, name) {
  61. if (!states.hasOwnProperty(name)) throw new Error('Undefined state ' + name + ' in simple mode')
  62. }
  63. function toRegex(val, caret) {
  64. if (!val) return /(?:)/
  65. var flags = ''
  66. if (val instanceof RegExp) {
  67. if (val.ignoreCase) flags = 'i'
  68. if (val.unicode) flags += 'u'
  69. val = val.source
  70. } else {
  71. val = String(val)
  72. }
  73. return new RegExp((caret === false ? '' : '^') + '(?:' + val + ')', flags)
  74. }
  75. function asToken(val) {
  76. if (!val) return null
  77. if (val.apply) return val
  78. if (typeof val == 'string') return val.replace(/\./g, ' ')
  79. var result = []
  80. for (var i = 0; i < val.length; i++) result.push(val[i] && val[i].replace(/\./g, ' '))
  81. return result
  82. }
  83. function Rule(data, states) {
  84. if (data.next || data.push) ensureState(states, data.next || data.push)
  85. this.regex = toRegex(data.regex)
  86. this.token = asToken(data.token)
  87. this.data = data
  88. }
  89. function tokenFunction(states, config) {
  90. return function (stream, state) {
  91. if (state.pending) {
  92. var pend = state.pending.shift()
  93. if (state.pending.length == 0) state.pending = null
  94. stream.pos += pend.text.length
  95. return pend.token
  96. }
  97. if (state.local) {
  98. if (state.local.end && stream.match(state.local.end)) {
  99. var tok = state.local.endToken || null
  100. state.local = state.localState = null
  101. return tok
  102. } else {
  103. var tok = state.local.mode.token(stream, state.localState),
  104. m
  105. if (state.local.endScan && (m = state.local.endScan.exec(stream.current()))) stream.pos = stream.start + m.index
  106. return tok
  107. }
  108. }
  109. var curState = states[state.state]
  110. for (var i = 0; i < curState.length; i++) {
  111. var rule = curState[i]
  112. var matches = (!rule.data.sol || stream.sol()) && stream.match(rule.regex)
  113. if (matches) {
  114. if (rule.data.next) {
  115. state.state = rule.data.next
  116. } else if (rule.data.push) {
  117. ;(state.stack || (state.stack = [])).push(state.state)
  118. state.state = rule.data.push
  119. } else if (rule.data.pop && state.stack && state.stack.length) {
  120. state.state = state.stack.pop()
  121. }
  122. if (rule.data.mode) enterLocalMode(config, state, rule.data.mode, rule.token)
  123. if (rule.data.indent) state.indent.push(stream.indentation() + config.indentUnit)
  124. if (rule.data.dedent) state.indent.pop()
  125. var token = rule.token
  126. if (token && token.apply) token = token(matches)
  127. if (matches.length > 2 && rule.token && typeof rule.token != 'string') {
  128. for (var j = 2; j < matches.length; j++) if (matches[j]) (state.pending || (state.pending = [])).push({ text: matches[j], token: rule.token[j - 1] })
  129. stream.backUp(matches[0].length - (matches[1] ? matches[1].length : 0))
  130. return token[0]
  131. } else if (token && token.join) {
  132. return token[0]
  133. } else {
  134. return token
  135. }
  136. }
  137. }
  138. stream.next()
  139. return null
  140. }
  141. }
  142. function cmp(a, b) {
  143. if (a === b) return true
  144. if (!a || typeof a != 'object' || !b || typeof b != 'object') return false
  145. var props = 0
  146. for (var prop in a)
  147. if (a.hasOwnProperty(prop)) {
  148. if (!b.hasOwnProperty(prop) || !cmp(a[prop], b[prop])) return false
  149. props++
  150. }
  151. for (var prop in b) if (b.hasOwnProperty(prop)) props--
  152. return props == 0
  153. }
  154. function enterLocalMode(config, state, spec, token) {
  155. var pers
  156. if (spec.persistent) for (var p = state.persistentStates; p && !pers; p = p.next) if (spec.spec ? cmp(spec.spec, p.spec) : spec.mode == p.mode) pers = p
  157. var mode = pers ? pers.mode : spec.mode || CodeMirror.getMode(config, spec.spec)
  158. var lState = pers ? pers.state : CodeMirror.startState(mode)
  159. if (spec.persistent && !pers) state.persistentStates = { mode: mode, spec: spec.spec, state: lState, next: state.persistentStates }
  160. state.localState = lState
  161. state.local = {
  162. mode: mode,
  163. end: spec.end && toRegex(spec.end),
  164. endScan: spec.end && spec.forceEnd !== false && toRegex(spec.end, false),
  165. endToken: token && token.join ? token[token.length - 1] : token,
  166. }
  167. }
  168. function indexOf(val, arr) {
  169. for (var i = 0; i < arr.length; i++) if (arr[i] === val) return true
  170. }
  171. function indentFunction(states, meta) {
  172. return function (state, textAfter, line) {
  173. if (state.local && state.local.mode.indent) return state.local.mode.indent(state.localState, textAfter, line)
  174. if (state.indent == null || state.local || (meta.dontIndentStates && indexOf(state.state, meta.dontIndentStates) > -1)) return CodeMirror.Pass
  175. var pos = state.indent.length - 1,
  176. rules = states[state.state]
  177. scan: for (;;) {
  178. for (var i = 0; i < rules.length; i++) {
  179. var rule = rules[i]
  180. if (rule.data.dedent && rule.data.dedentIfLineStart !== false) {
  181. var m = rule.regex.exec(textAfter)
  182. if (m && m[0]) {
  183. pos--
  184. if (rule.next || rule.push) rules = states[rule.next || rule.push]
  185. textAfter = textAfter.slice(m[0].length)
  186. continue scan
  187. }
  188. }
  189. }
  190. break
  191. }
  192. return pos < 0 ? 0 : state.indent[pos]
  193. }
  194. }
  195. })