annotate .cms/lib/codemirror/src/model/changes.js @ 0:78edf6b517a0 draft

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