File modules/editor/codemirror/src/model/history.min.js
Last commit: Tue May 22 22:39:53 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 { cmp, copyPos } from "../line/pos.js" 2 import { stretchSpansOverChange } from "../line/spans.js" 3 import { getBetween } from "../line/utils_line.js" 4 import { signal } from "../util/event.js" 5 import { indexOf, lst } from "../util/misc.js" 6 7 import { changeEnd } from "./change_measurement.js" 8 import { linkedDocs } from "./document_data.js" 9 import { Selection } from "./selection.js" 10 11 export function History(startGen) { 12 // Arrays of change events and selections. Doing something adds an 13 // event to done and clears undo. Undoing moves events from done 14 // to undone, redoing moves them in the other direction. 15 this.done = []; this.undone = [] 16 this.undoDepth = Infinity 17 // Used to track when changes can be merged into a single undo 18 // event 19 this.lastModTime = this.lastSelTime = 0 20 this.lastOp = this.lastSelOp = null 21 this.lastOrigin = this.lastSelOrigin = null 22 // Used by the isClean() method 23 this.generation = this.maxGeneration = startGen || 1 24 } 25 26 // Create a history change event from an updateDoc-style change 27 // object. 28 export function historyChangeFromChange(doc, change) { 29 let histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)} 30 attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1) 31 linkedDocs(doc, doc => attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1), true) 32 return histChange 33 } 34 35 // Pop all selection events off the end of a history array. Stop at 36 // a change event. 37 function clearSelectionEvents(array) { 38 while (array.length) { 39 let last = lst(array) 40 if (last.ranges) array.pop() 41 else break 42 } 43 } 44 45 // Find the top change event in the history. Pop off selection 46 // events that are in the way. 47 function lastChangeEvent(hist, force) { 48 if (force) { 49 clearSelectionEvents(hist.done) 50 return lst(hist.done) 51 } else if (hist.done.length && !lst(hist.done).ranges) { 52 return lst(hist.done) 53 } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) { 54 hist.done.pop() 55 return lst(hist.done) 56 } 57 } 58 59 // Register a change in the history. Merges changes that are within 60 // a single operation, or are close together with an origin that 61 // allows merging (starting with "+") into a single event. 62 export function addChangeToHistory(doc, change, selAfter, opId) { 63 let hist = doc.history 64 hist.undone.length = 0 65 let time = +new Date, cur 66 let last 67 68 if ((hist.lastOp == opId || 69 hist.lastOrigin == change.origin && change.origin && 70 ((change.origin.charAt(0) == "+" && doc.cm && hist.lastModTime > time - doc.cm.options.historyEventDelay) || 71 change.origin.charAt(0) == "*")) && 72 (cur = lastChangeEvent(hist, hist.lastOp == opId))) { 73 // Merge this change into the last event 74 last = lst(cur.changes) 75 if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) { 76 // Optimized case for simple insertion -- don't want to add 77 // new changesets for every character typed 78 last.to = changeEnd(change) 79 } else { 80 // Add new sub-event 81 cur.changes.push(historyChangeFromChange(doc, change)) 82 } 83 } else { 84 // Can not be merged, start a new event. 85 let before = lst(hist.done) 86 if (!before || !before.ranges) 87 pushSelectionToHistory(doc.sel, hist.done) 88 cur = {changes: [historyChangeFromChange(doc, change)], 89 generation: hist.generation} 90 hist.done.push(cur) 91 while (hist.done.length > hist.undoDepth) { 92 hist.done.shift() 93 if (!hist.done[0].ranges) hist.done.shift() 94 } 95 } 96 hist.done.push(selAfter) 97 hist.generation = ++hist.maxGeneration 98 hist.lastModTime = hist.lastSelTime = time 99 hist.lastOp = hist.lastSelOp = opId 100 hist.lastOrigin = hist.lastSelOrigin = change.origin 101 102 if (!last) signal(doc, "historyAdded") 103 } 104 105 function selectionEventCanBeMerged(doc, origin, prev, sel) { 106 let ch = origin.charAt(0) 107 return ch == "*" || 108 ch == "+" && 109 prev.ranges.length == sel.ranges.length && 110 prev.somethingSelected() == sel.somethingSelected() && 111 new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500) 112 } 113 114 // Called whenever the selection changes, sets the new selection as 115 // the pending selection in the history, and pushes the old pending 116 // selection into the 'done' array when it was significantly 117 // different (in number of selected ranges, emptiness, or time). 118 export function addSelectionToHistory(doc, sel, opId, options) { 119 let hist = doc.history, origin = options && options.origin 120 121 // A new event is started when the previous origin does not match 122 // the current, or the origins don't allow matching. Origins 123 // starting with * are always merged, those starting with + are 124 // merged when similar and close together in time. 125 if (opId == hist.lastSelOp || 126 (origin && hist.lastSelOrigin == origin && 127 (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin || 128 selectionEventCanBeMerged(doc, origin, lst(hist.done), sel)))) 129 hist.done[hist.done.length - 1] = sel 130 else 131 pushSelectionToHistory(sel, hist.done) 132 133 hist.lastSelTime = +new Date 134 hist.lastSelOrigin = origin 135 hist.lastSelOp = opId 136 if (options && options.clearRedo !== false) 137 clearSelectionEvents(hist.undone) 138 } 139 140 export function pushSelectionToHistory(sel, dest) { 141 let top = lst(dest) 142 if (!(top && top.ranges && top.equals(sel))) 143 dest.push(sel) 144 } 145 146 // Used to store marked span information in the history. 147 function attachLocalSpans(doc, change, from, to) { 148 let existing = change["spans_" + doc.id], n = 0 149 doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), line => { 150 if (line.markedSpans) 151 (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans 152 ++n 153 }) 154 } 155 156 // When un/re-doing restores text containing marked spans, those 157 // that have been explicitly cleared should not be restored. 158 function removeClearedSpans(spans) { 159 if (!spans) return null 160 let out 161 for (let i = 0; i < spans.length; ++i) { 162 if (spans[i].marker.explicitlyCleared) { if (!out) out = spans.slice(0, i) } 163 else if (out) out.push(spans[i]) 164 } 165 return !out ? spans : out.length ? out : null 166 } 167 168 // Retrieve and filter the old marked spans stored in a change event. 169 function getOldSpans(doc, change) { 170 let found = change["spans_" + doc.id] 171 if (!found) return null 172 let nw = [] 173 for (let i = 0; i < change.text.length; ++i) 174 nw.push(removeClearedSpans(found[i])) 175 return nw 176 } 177 178 // Used for un/re-doing changes from the history. Combines the 179 // result of computing the existing spans with the set of spans that 180 // existed in the history (so that deleting around a span and then 181 // undoing brings back the span). 182 export function mergeOldSpans(doc, change) { 183 let old = getOldSpans(doc, change) 184 let stretched = stretchSpansOverChange(doc, change) 185 if (!old) return stretched 186 if (!stretched) return old 187 188 for (let i = 0; i < old.length; ++i) { 189 let oldCur = old[i], stretchCur = stretched[i] 190 if (oldCur && stretchCur) { 191 spans: for (let j = 0; j < stretchCur.length; ++j) { 192 let span = stretchCur[j] 193 for (let k = 0; k < oldCur.length; ++k) 194 if (oldCur[k].marker == span.marker) continue spans 195 oldCur.push(span) 196 } 197 } else if (stretchCur) { 198 old[i] = stretchCur 199 } 200 } 201 return old 202 } 203 204 // Used both to provide a JSON-safe object in .getHistory, and, when 205 // detaching a document, to split the history in two 206 export function copyHistoryArray(events, newGroup, instantiateSel) { 207 let copy = [] 208 for (let i = 0; i < events.length; ++i) { 209 let event = events[i] 210 if (event.ranges) { 211 copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event) 212 continue 213 } 214 let changes = event.changes, newChanges = [] 215 copy.push({changes: newChanges}) 216 for (let j = 0; j < changes.length; ++j) { 217 let change = changes[j], m 218 newChanges.push({from: change.from, to: change.to, text: change.text}) 219 if (newGroup) for (var prop in change) if (m = prop.match(/^spans_(\d+)$/)) { 220 if (indexOf(newGroup, Number(m[1])) > -1) { 221 lst(newChanges)[prop] = change[prop] 222 delete change[prop] 223 } 224 } 225 } 226 } 227 return copy 228 }
Downloadmodules/editor/codemirror/src/model/history.min.js
History Tue, 22 May 2018 22:39:53 +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'.