File modules/editor/codemirror/src/line/highlight.min.js
Last commit: Tue May 22 22:39:54 2018 +0200 Jan Dankert Fix für PHP 7.2: 'Object' darf nun nicht mehr als Klassennamen verwendet werden. AUCH NICHT IN EINEM NAMESPACE! WTF, wozu habe ich das in einen verfickten Namespace gepackt? Wozu soll der sonst da sein??? Amateure. Daher nun notgedrungen unbenannt in 'BaseObject'.
1 import { countColumn } from "../util/misc.js" 2 import { copyState, innerMode, startState } from "../modes.js" 3 import StringStream from "../util/StringStream.js" 4 5 import { getLine, lineNo } from "./utils_line.js" 6 import { clipPos } from "./pos.js" 7 8 class SavedContext { 9 constructor(state, lookAhead) { 10 this.state = state 11 this.lookAhead = lookAhead 12 } 13 } 14 15 class Context { 16 constructor(doc, state, line, lookAhead) { 17 this.state = state 18 this.doc = doc 19 this.line = line 20 this.maxLookAhead = lookAhead || 0 21 this.baseTokens = null 22 this.baseTokenPos = 1 23 } 24 25 lookAhead(n) { 26 let line = this.doc.getLine(this.line + n) 27 if (line != null && n > this.maxLookAhead) this.maxLookAhead = n 28 return line 29 } 30 31 baseToken(n) { 32 if (!this.baseTokens) return null 33 while (this.baseTokens[this.baseTokenPos] <= n) 34 this.baseTokenPos += 2 35 let type = this.baseTokens[this.baseTokenPos + 1] 36 return {type: type && type.replace(/( |^)overlay .*/, ""), 37 size: this.baseTokens[this.baseTokenPos] - n} 38 } 39 40 nextLine() { 41 this.line++ 42 if (this.maxLookAhead > 0) this.maxLookAhead-- 43 } 44 45 static fromSaved(doc, saved, line) { 46 if (saved instanceof SavedContext) 47 return new Context(doc, copyState(doc.mode, saved.state), line, saved.lookAhead) 48 else 49 return new Context(doc, copyState(doc.mode, saved), line) 50 } 51 52 save(copy) { 53 let state = copy !== false ? copyState(this.doc.mode, this.state) : this.state 54 return this.maxLookAhead > 0 ? new SavedContext(state, this.maxLookAhead) : state 55 } 56 } 57 58 59 // Compute a style array (an array starting with a mode generation 60 // -- for invalidation -- followed by pairs of end positions and 61 // style strings), which is used to highlight the tokens on the 62 // line. 63 export function highlightLine(cm, line, context, forceToEnd) { 64 // A styles array always starts with a number identifying the 65 // mode/overlays that it is based on (for easy invalidation). 66 let st = [cm.state.modeGen], lineClasses = {} 67 // Compute the base array of styles 68 runMode(cm, line.text, cm.doc.mode, context, (end, style) => st.push(end, style), 69 lineClasses, forceToEnd) 70 let state = context.state 71 72 // Run overlays, adjust style array. 73 for (let o = 0; o < cm.state.overlays.length; ++o) { 74 context.baseTokens = st 75 let overlay = cm.state.overlays[o], i = 1, at = 0 76 context.state = true 77 runMode(cm, line.text, overlay.mode, context, (end, style) => { 78 let start = i 79 // Ensure there's a token end at the current position, and that i points at it 80 while (at < end) { 81 let i_end = st[i] 82 if (i_end > end) 83 st.splice(i, 1, end, st[i+1], i_end) 84 i += 2 85 at = Math.min(end, i_end) 86 } 87 if (!style) return 88 if (overlay.opaque) { 89 st.splice(start, i - start, end, "overlay " + style) 90 i = start + 2 91 } else { 92 for (; start < i; start += 2) { 93 let cur = st[start+1] 94 st[start+1] = (cur ? cur + " " : "") + "overlay " + style 95 } 96 } 97 }, lineClasses) 98 context.state = state 99 context.baseTokens = null 100 context.baseTokenPos = 1 101 } 102 103 return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null} 104 } 105 106 export function getLineStyles(cm, line, updateFrontier) { 107 if (!line.styles || line.styles[0] != cm.state.modeGen) { 108 let context = getContextBefore(cm, lineNo(line)) 109 let resetState = line.text.length > cm.options.maxHighlightLength && copyState(cm.doc.mode, context.state) 110 let result = highlightLine(cm, line, context) 111 if (resetState) context.state = resetState 112 line.stateAfter = context.save(!resetState) 113 line.styles = result.styles 114 if (result.classes) line.styleClasses = result.classes 115 else if (line.styleClasses) line.styleClasses = null 116 if (updateFrontier === cm.doc.highlightFrontier) 117 cm.doc.modeFrontier = Math.max(cm.doc.modeFrontier, ++cm.doc.highlightFrontier) 118 } 119 return line.styles 120 } 121 122 export function getContextBefore(cm, n, precise) { 123 let doc = cm.doc, display = cm.display 124 if (!doc.mode.startState) return new Context(doc, true, n) 125 let start = findStartLine(cm, n, precise) 126 let saved = start > doc.first && getLine(doc, start - 1).stateAfter 127 let context = saved ? Context.fromSaved(doc, saved, start) : new Context(doc, startState(doc.mode), start) 128 129 doc.iter(start, n, line => { 130 processLine(cm, line.text, context) 131 let pos = context.line 132 line.stateAfter = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo ? context.save() : null 133 context.nextLine() 134 }) 135 if (precise) doc.modeFrontier = context.line 136 return context 137 } 138 139 // Lightweight form of highlight -- proceed over this line and 140 // update state, but don't save a style array. Used for lines that 141 // aren't currently visible. 142 export function processLine(cm, text, context, startAt) { 143 let mode = cm.doc.mode 144 let stream = new StringStream(text, cm.options.tabSize, context) 145 stream.start = stream.pos = startAt || 0 146 if (text == "") callBlankLine(mode, context.state) 147 while (!stream.eol()) { 148 readToken(mode, stream, context.state) 149 stream.start = stream.pos 150 } 151 } 152 153 function callBlankLine(mode, state) { 154 if (mode.blankLine) return mode.blankLine(state) 155 if (!mode.innerMode) return 156 let inner = innerMode(mode, state) 157 if (inner.mode.blankLine) return inner.mode.blankLine(inner.state) 158 } 159 160 export function readToken(mode, stream, state, inner) { 161 for (let i = 0; i < 10; i++) { 162 if (inner) inner[0] = innerMode(mode, state).mode 163 let style = mode.token(stream, state) 164 if (stream.pos > stream.start) return style 165 } 166 throw new Error("Mode " + mode.name + " failed to advance stream.") 167 } 168 169 class Token { 170 constructor(stream, type, state) { 171 this.start = stream.start; this.end = stream.pos 172 this.string = stream.current() 173 this.type = type || null 174 this.state = state 175 } 176 } 177 178 // Utility for getTokenAt and getLineTokens 179 export function takeToken(cm, pos, precise, asArray) { 180 let doc = cm.doc, mode = doc.mode, style 181 pos = clipPos(doc, pos) 182 let line = getLine(doc, pos.line), context = getContextBefore(cm, pos.line, precise) 183 let stream = new StringStream(line.text, cm.options.tabSize, context), tokens 184 if (asArray) tokens = [] 185 while ((asArray || stream.pos < pos.ch) && !stream.eol()) { 186 stream.start = stream.pos 187 style = readToken(mode, stream, context.state) 188 if (asArray) tokens.push(new Token(stream, style, copyState(doc.mode, context.state))) 189 } 190 return asArray ? tokens : new Token(stream, style, context.state) 191 } 192 193 function extractLineClasses(type, output) { 194 if (type) for (;;) { 195 let lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/) 196 if (!lineClass) break 197 type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length) 198 let prop = lineClass[1] ? "bgClass" : "textClass" 199 if (output[prop] == null) 200 output[prop] = lineClass[2] 201 else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output[prop])) 202 output[prop] += " " + lineClass[2] 203 } 204 return type 205 } 206 207 // Run the given mode's parser over a line, calling f for each token. 208 function runMode(cm, text, mode, context, f, lineClasses, forceToEnd) { 209 let flattenSpans = mode.flattenSpans 210 if (flattenSpans == null) flattenSpans = cm.options.flattenSpans 211 let curStart = 0, curStyle = null 212 let stream = new StringStream(text, cm.options.tabSize, context), style 213 let inner = cm.options.addModeClass && [null] 214 if (text == "") extractLineClasses(callBlankLine(mode, context.state), lineClasses) 215 while (!stream.eol()) { 216 if (stream.pos > cm.options.maxHighlightLength) { 217 flattenSpans = false 218 if (forceToEnd) processLine(cm, text, context, stream.pos) 219 stream.pos = text.length 220 style = null 221 } else { 222 style = extractLineClasses(readToken(mode, stream, context.state, inner), lineClasses) 223 } 224 if (inner) { 225 let mName = inner[0].name 226 if (mName) style = "m-" + (style ? mName + " " + style : mName) 227 } 228 if (!flattenSpans || curStyle != style) { 229 while (curStart < stream.start) { 230 curStart = Math.min(stream.start, curStart + 5000) 231 f(curStart, curStyle) 232 } 233 curStyle = style 234 } 235 stream.start = stream.pos 236 } 237 while (curStart < stream.pos) { 238 // Webkit seems to refuse to render text nodes longer than 57444 239 // characters, and returns inaccurate measurements in nodes 240 // starting around 5000 chars. 241 let pos = Math.min(stream.pos, curStart + 5000) 242 f(pos, curStyle) 243 curStart = pos 244 } 245 } 246 247 // Finds the line to start with when starting a parse. Tries to 248 // find a line with a stateAfter, so that it can start with a 249 // valid state. If that fails, it returns the line with the 250 // smallest indentation, which tends to need the least context to 251 // parse correctly. 252 function findStartLine(cm, n, precise) { 253 let minindent, minline, doc = cm.doc 254 let lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100) 255 for (let search = n; search > lim; --search) { 256 if (search <= doc.first) return doc.first 257 let line = getLine(doc, search - 1), after = line.stateAfter 258 if (after && (!precise || search + (after instanceof SavedContext ? after.lookAhead : 0) <= doc.modeFrontier)) 259 return search 260 let indented = countColumn(line.text, null, cm.options.tabSize) 261 if (minline == null || minindent > indented) { 262 minline = search - 1 263 minindent = indented 264 } 265 } 266 return minline 267 } 268 269 export function retreatFrontier(doc, n) { 270 doc.modeFrontier = Math.min(doc.modeFrontier, n) 271 if (doc.highlightFrontier < n - 10) return 272 let start = doc.first 273 for (let line = n - 1; line > start; line--) { 274 let saved = getLine(doc, line).stateAfter 275 // change is on 3 276 // state on line 1 looked ahead 2 -- so saw 3 277 // test 1 + 2 < 3 should cover this 278 if (saved && (!(saved instanceof SavedContext) || line + saved.lookAhead < n)) { 279 start = line + 1 280 break 281 } 282 } 283 doc.highlightFrontier = Math.min(doc.highlightFrontier, start) 284 }
Downloadmodules/editor/codemirror/src/line/highlight.min.js
History Tue, 22 May 2018 22:39:54 +0200 Jan Dankert Fix für PHP 7.2: 'Object' darf nun nicht mehr als Klassennamen verwendet werden. AUCH NICHT IN EINEM NAMESPACE! WTF, wozu habe ich das in einen verfickten Namespace gepackt? Wozu soll der sonst da sein??? Amateure. Daher nun notgedrungen unbenannt in 'BaseObject'.