File modules/editor/codemirror/src/model/changes.min.js
Last commit: Tue May 22 22:39:49 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 { retreatFrontier } from "../line/highlight.js" 2 import { startWorker } from "../display/highlight_worker.js" 3 import { operation } from "../display/operations.js" 4 import { regChange, regLineChange } from "../display/view_tracking.js" 5 import { clipLine, clipPos, cmp, Pos } from "../line/pos.js" 6 import { sawReadOnlySpans } from "../line/saw_special_spans.js" 7 import { lineLength, removeReadOnlyRanges, stretchSpansOverChange, visualLine } from "../line/spans.js" 8 import { getBetween, getLine, lineNo } from "../line/utils_line.js" 9 import { estimateHeight } from "../measurement/position_measurement.js" 10 import { hasHandler, signal, signalCursorActivity } from "../util/event.js" 11 import { indexOf, lst, map, sel_dontScroll } from "../util/misc.js" 12 import { signalLater } from "../util/operation_group.js" 13 14 import { changeEnd, computeSelAfterChange } from "./change_measurement.js" 15 import { isWholeLineUpdate, linkedDocs, updateDoc } from "./document_data.js" 16 import { addChangeToHistory, historyChangeFromChange, mergeOldSpans, pushSelectionToHistory } from "./history.js" 17 import { Range, Selection } from "./selection.js" 18 import { setSelection, setSelectionNoUndo } from "./selection_updates.js" 19 20 // UPDATING 21 22 // Allow "beforeChange" event handlers to influence a change 23 function filterChange(doc, change, update) { 24 let obj = { 25 canceled: false, 26 from: change.from, 27 to: change.to, 28 text: change.text, 29 origin: change.origin, 30 cancel: () => obj.canceled = true 31 } 32 if (update) obj.update = (from, to, text, origin) => { 33 if (from) obj.from = clipPos(doc, from) 34 if (to) obj.to = clipPos(doc, to) 35 if (text) obj.text = text 36 if (origin !== undefined) obj.origin = origin 37 } 38 signal(doc, "beforeChange", doc, obj) 39 if (doc.cm) signal(doc.cm, "beforeChange", doc.cm, obj) 40 41 if (obj.canceled) return null 42 return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin} 43 } 44 45 // Apply a change to a document, and add it to the document's 46 // history, and propagating it to all linked documents. 47 export function makeChange(doc, change, ignoreReadOnly) { 48 if (doc.cm) { 49 if (!doc.cm.curOp) return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly) 50 if (doc.cm.state.suppressEdits) return 51 } 52 53 if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) { 54 change = filterChange(doc, change, true) 55 if (!change) return 56 } 57 58 // Possibly split or suppress the update based on the presence 59 // of read-only spans in its range. 60 let split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to) 61 if (split) { 62 for (let i = split.length - 1; i >= 0; --i) 63 makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text, origin: change.origin}) 64 } else { 65 makeChangeInner(doc, change) 66 } 67 } 68 69 function makeChangeInner(doc, change) { 70 if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) return 71 let selAfter = computeSelAfterChange(doc, change) 72 addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN) 73 74 makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change)) 75 let rebased = [] 76 77 linkedDocs(doc, (doc, sharedHist) => { 78 if (!sharedHist && indexOf(rebased, doc.history) == -1) { 79 rebaseHist(doc.history, change) 80 rebased.push(doc.history) 81 } 82 makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change)) 83 }) 84 } 85 86 // Revert a change stored in a document's history. 87 export function makeChangeFromHistory(doc, type, allowSelectionOnly) { 88 if (doc.cm && doc.cm.state.suppressEdits && !allowSelectionOnly) return 89 90 let hist = doc.history, event, selAfter = doc.sel 91 let source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done 92 93 // Verify that there is a useable event (so that ctrl-z won't 94 // needlessly clear selection events) 95 let i = 0 96 for (; i < source.length; i++) { 97 event = source[i] 98 if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges) 99 break 100 } 101 if (i == source.length) return 102 hist.lastOrigin = hist.lastSelOrigin = null 103 104 for (;;) { 105 event = source.pop() 106 if (event.ranges) { 107 pushSelectionToHistory(event, dest) 108 if (allowSelectionOnly && !event.equals(doc.sel)) { 109 setSelection(doc, event, {clearRedo: false}) 110 return 111 } 112 selAfter = event 113 } 114 else break 115 } 116 117 // Build up a reverse change object to add to the opposite history 118 // stack (redo when undoing, and vice versa). 119 let antiChanges = [] 120 pushSelectionToHistory(selAfter, dest) 121 dest.push({changes: antiChanges, generation: hist.generation}) 122 hist.generation = event.generation || ++hist.maxGeneration 123 124 let filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange") 125 126 for (let i = event.changes.length - 1; i >= 0; --i) { 127 let change = event.changes[i] 128 change.origin = type 129 if (filter && !filterChange(doc, change, false)) { 130 source.length = 0 131 return 132 } 133 134 antiChanges.push(historyChangeFromChange(doc, change)) 135 136 let after = i ? computeSelAfterChange(doc, change) : lst(source) 137 makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change)) 138 if (!i && doc.cm) doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}) 139 let rebased = [] 140 141 // Propagate to the linked documents 142 linkedDocs(doc, (doc, sharedHist) => { 143 if (!sharedHist && indexOf(rebased, doc.history) == -1) { 144 rebaseHist(doc.history, change) 145 rebased.push(doc.history) 146 } 147 makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change)) 148 }) 149 } 150 } 151 152 // Sub-views need their line numbers shifted when text is added 153 // above or below them in the parent document. 154 function shiftDoc(doc, distance) { 155 if (distance == 0) return 156 doc.first += distance 157 doc.sel = new Selection(map(doc.sel.ranges, range => new Range( 158 Pos(range.anchor.line + distance, range.anchor.ch), 159 Pos(range.head.line + distance, range.head.ch) 160 )), doc.sel.primIndex) 161 if (doc.cm) { 162 regChange(doc.cm, doc.first, doc.first - distance, distance) 163 for (let d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++) 164 regLineChange(doc.cm, l, "gutter") 165 } 166 } 167 168 // More lower-level change function, handling only a single document 169 // (not linked ones). 170 function makeChangeSingleDoc(doc, change, selAfter, spans) { 171 if (doc.cm && !doc.cm.curOp) 172 return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans) 173 174 if (change.to.line < doc.first) { 175 shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line)) 176 return 177 } 178 if (change.from.line > doc.lastLine()) return 179 180 // Clip the change to the size of this doc 181 if (change.from.line < doc.first) { 182 let shift = change.text.length - 1 - (doc.first - change.from.line) 183 shiftDoc(doc, shift) 184 change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch), 185 text: [lst(change.text)], origin: change.origin} 186 } 187 let last = doc.lastLine() 188 if (change.to.line > last) { 189 change = {from: change.from, to: Pos(last, getLine(doc, last).text.length), 190 text: [change.text[0]], origin: change.origin} 191 } 192 193 change.removed = getBetween(doc, change.from, change.to) 194 195 if (!selAfter) selAfter = computeSelAfterChange(doc, change) 196 if (doc.cm) makeChangeSingleDocInEditor(doc.cm, change, spans) 197 else updateDoc(doc, change, spans) 198 setSelectionNoUndo(doc, selAfter, sel_dontScroll) 199 } 200 201 // Handle the interaction of a change to a document with the editor 202 // that this document is part of. 203 function makeChangeSingleDocInEditor(cm, change, spans) { 204 let doc = cm.doc, display = cm.display, from = change.from, to = change.to 205 206 let recomputeMaxLength = false, checkWidthStart = from.line 207 if (!cm.options.lineWrapping) { 208 checkWidthStart = lineNo(visualLine(getLine(doc, from.line))) 209 doc.iter(checkWidthStart, to.line + 1, line => { 210 if (line == display.maxLine) { 211 recomputeMaxLength = true 212 return true 213 } 214 }) 215 } 216 217 if (doc.sel.contains(change.from, change.to) > -1) 218 signalCursorActivity(cm) 219 220 updateDoc(doc, change, spans, estimateHeight(cm)) 221 222 if (!cm.options.lineWrapping) { 223 doc.iter(checkWidthStart, from.line + change.text.length, line => { 224 let len = lineLength(line) 225 if (len > display.maxLineLength) { 226 display.maxLine = line 227 display.maxLineLength = len 228 display.maxLineChanged = true 229 recomputeMaxLength = false 230 } 231 }) 232 if (recomputeMaxLength) cm.curOp.updateMaxLine = true 233 } 234 235 retreatFrontier(doc, from.line) 236 startWorker(cm, 400) 237 238 let lendiff = change.text.length - (to.line - from.line) - 1 239 // Remember that these lines changed, for updating the display 240 if (change.full) 241 regChange(cm) 242 else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change)) 243 regLineChange(cm, from.line, "text") 244 else 245 regChange(cm, from.line, to.line + 1, lendiff) 246 247 let changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change") 248 if (changeHandler || changesHandler) { 249 let obj = { 250 from: from, to: to, 251 text: change.text, 252 removed: change.removed, 253 origin: change.origin 254 } 255 if (changeHandler) signalLater(cm, "change", cm, obj) 256 if (changesHandler) (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj) 257 } 258 cm.display.selForContextMenu = null 259 } 260 261 export function replaceRange(doc, code, from, to, origin) { 262 if (!to) to = from 263 if (cmp(to, from) < 0) [from, to] = [to, from] 264 if (typeof code == "string") code = doc.splitLines(code) 265 makeChange(doc, {from, to, text: code, origin}) 266 } 267 268 // Rebasing/resetting history to deal with externally-sourced changes 269 270 function rebaseHistSelSingle(pos, from, to, diff) { 271 if (to < pos.line) { 272 pos.line += diff 273 } else if (from < pos.line) { 274 pos.line = from 275 pos.ch = 0 276 } 277 } 278 279 // Tries to rebase an array of history events given a change in the 280 // document. If the change touches the same lines as the event, the 281 // event, and everything 'behind' it, is discarded. If the change is 282 // before the event, the event's positions are updated. Uses a 283 // copy-on-write scheme for the positions, to avoid having to 284 // reallocate them all on every rebase, but also avoid problems with 285 // shared position objects being unsafely updated. 286 function rebaseHistArray(array, from, to, diff) { 287 for (let i = 0; i < array.length; ++i) { 288 let sub = array[i], ok = true 289 if (sub.ranges) { 290 if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true } 291 for (let j = 0; j < sub.ranges.length; j++) { 292 rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff) 293 rebaseHistSelSingle(sub.ranges[j].head, from, to, diff) 294 } 295 continue 296 } 297 for (let j = 0; j < sub.changes.length; ++j) { 298 let cur = sub.changes[j] 299 if (to < cur.from.line) { 300 cur.from = Pos(cur.from.line + diff, cur.from.ch) 301 cur.to = Pos(cur.to.line + diff, cur.to.ch) 302 } else if (from <= cur.to.line) { 303 ok = false 304 break 305 } 306 } 307 if (!ok) { 308 array.splice(0, i + 1) 309 i = 0 310 } 311 } 312 } 313 314 function rebaseHist(hist, change) { 315 let from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1 316 rebaseHistArray(hist.done, from, to, diff) 317 rebaseHistArray(hist.undone, from, to, diff) 318 } 319 320 // Utility for applying a change to a line by handle or number, 321 // returning the number and optionally registering the line as 322 // changed. 323 export function changeLine(doc, handle, changeType, op) { 324 let no = handle, line = handle 325 if (typeof handle == "number") line = getLine(doc, clipLine(doc, handle)) 326 else no = lineNo(handle) 327 if (no == null) return null 328 if (op(line, no) && doc.cm) regLineChange(doc.cm, no, changeType) 329 return line 330 }
Downloadmodules/editor/codemirror/src/model/changes.min.js
History Tue, 22 May 2018 22:39:49 +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'.