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