etherpad-webcomponents 0.0.5 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/EpDropdown.d.ts +3 -0
- package/dist/EpDropdown.d.ts.map +1 -1
- package/dist/EpDropdown.js +30 -4
- package/dist/EpDropdown.js.map +1 -1
- package/dist/EpEditor.d.ts +108 -0
- package/dist/EpEditor.d.ts.map +1 -0
- package/dist/EpEditor.js +288 -0
- package/dist/EpEditor.js.map +1 -0
- package/dist/editor/AceEditor.d.ts +204 -0
- package/dist/editor/AceEditor.d.ts.map +1 -0
- package/dist/editor/AceEditor.js +2847 -0
- package/dist/editor/AceEditor.js.map +1 -0
- package/dist/editor/AttributeManager.d.ts +25 -0
- package/dist/editor/AttributeManager.d.ts.map +1 -0
- package/dist/editor/AttributeManager.js +236 -0
- package/dist/editor/AttributeManager.js.map +1 -0
- package/dist/editor/AttributeMap.d.ts +13 -0
- package/dist/editor/AttributeMap.d.ts.map +1 -0
- package/dist/editor/AttributeMap.js +37 -0
- package/dist/editor/AttributeMap.js.map +1 -0
- package/dist/editor/AttributePool.d.ts +22 -0
- package/dist/editor/AttributePool.d.ts.map +1 -0
- package/dist/editor/AttributePool.js +101 -0
- package/dist/editor/AttributePool.js.map +1 -0
- package/dist/editor/Builder.d.ts +15 -0
- package/dist/editor/Builder.d.ts.map +1 -0
- package/dist/editor/Builder.js +47 -0
- package/dist/editor/Builder.js.map +1 -0
- package/dist/editor/Changeset.d.ts +61 -0
- package/dist/editor/Changeset.d.ts.map +1 -0
- package/dist/editor/Changeset.js +942 -0
- package/dist/editor/Changeset.js.map +1 -0
- package/dist/editor/ChangesetUtils.d.ts +11 -0
- package/dist/editor/ChangesetUtils.d.ts.map +1 -0
- package/dist/editor/ChangesetUtils.js +30 -0
- package/dist/editor/ChangesetUtils.js.map +1 -0
- package/dist/editor/MergingOpAssembler.d.ts +13 -0
- package/dist/editor/MergingOpAssembler.d.ts.map +1 -0
- package/dist/editor/MergingOpAssembler.js +60 -0
- package/dist/editor/MergingOpAssembler.js.map +1 -0
- package/dist/editor/Op.d.ts +10 -0
- package/dist/editor/Op.d.ts.map +1 -0
- package/dist/editor/Op.js +18 -0
- package/dist/editor/Op.js.map +1 -0
- package/dist/editor/OpAssembler.d.ts +9 -0
- package/dist/editor/OpAssembler.d.ts.map +1 -0
- package/dist/editor/OpAssembler.js +16 -0
- package/dist/editor/OpAssembler.js.map +1 -0
- package/dist/editor/OpIter.d.ts +9 -0
- package/dist/editor/OpIter.d.ts.map +1 -0
- package/dist/editor/OpIter.js +22 -0
- package/dist/editor/OpIter.js.map +1 -0
- package/dist/editor/SmartOpAssembler.d.ts +21 -0
- package/dist/editor/SmartOpAssembler.d.ts.map +1 -0
- package/dist/editor/SmartOpAssembler.js +71 -0
- package/dist/editor/SmartOpAssembler.js.map +1 -0
- package/dist/editor/StringAssembler.d.ts +7 -0
- package/dist/editor/StringAssembler.d.ts.map +1 -0
- package/dist/editor/StringAssembler.js +15 -0
- package/dist/editor/StringAssembler.js.map +1 -0
- package/dist/editor/StringIterator.d.ts +13 -0
- package/dist/editor/StringIterator.d.ts.map +1 -0
- package/dist/editor/StringIterator.js +29 -0
- package/dist/editor/StringIterator.js.map +1 -0
- package/dist/editor/TextLinesMutator.d.ts +31 -0
- package/dist/editor/TextLinesMutator.d.ts.map +1 -0
- package/dist/editor/TextLinesMutator.js +198 -0
- package/dist/editor/TextLinesMutator.js.map +1 -0
- package/dist/editor/ace2_common.d.ts +9 -0
- package/dist/editor/ace2_common.d.ts.map +1 -0
- package/dist/editor/ace2_common.js +31 -0
- package/dist/editor/ace2_common.js.map +1 -0
- package/dist/editor/attributes.d.ts +20 -0
- package/dist/editor/attributes.d.ts.map +1 -0
- package/dist/editor/attributes.js +54 -0
- package/dist/editor/attributes.js.map +1 -0
- package/dist/editor/browser_flags.d.ts +7 -0
- package/dist/editor/browser_flags.d.ts.map +1 -0
- package/dist/editor/browser_flags.js +8 -0
- package/dist/editor/browser_flags.js.map +1 -0
- package/dist/editor/changesettracker.d.ts +54 -0
- package/dist/editor/changesettracker.d.ts.map +1 -0
- package/dist/editor/changesettracker.js +193 -0
- package/dist/editor/changesettracker.js.map +1 -0
- package/dist/editor/colorutils.d.ts +22 -0
- package/dist/editor/colorutils.d.ts.map +1 -0
- package/dist/editor/colorutils.js +81 -0
- package/dist/editor/colorutils.js.map +1 -0
- package/dist/editor/contentcollector.d.ts +52 -0
- package/dist/editor/contentcollector.d.ts.map +1 -0
- package/dist/editor/contentcollector.js +692 -0
- package/dist/editor/contentcollector.js.map +1 -0
- package/dist/editor/core/EventBus.d.ts +187 -0
- package/dist/editor/core/EventBus.d.ts.map +1 -0
- package/dist/editor/core/EventBus.js +169 -0
- package/dist/editor/core/EventBus.js.map +1 -0
- package/dist/editor/cssmanager.d.ts +6 -0
- package/dist/editor/cssmanager.d.ts.map +1 -0
- package/dist/editor/cssmanager.js +35 -0
- package/dist/editor/cssmanager.js.map +1 -0
- package/dist/editor/domline.d.ts +35 -0
- package/dist/editor/domline.d.ts.map +1 -0
- package/dist/editor/domline.js +267 -0
- package/dist/editor/domline.js.map +1 -0
- package/dist/editor/html_escape.d.ts +3 -0
- package/dist/editor/html_escape.d.ts.map +1 -0
- package/dist/editor/html_escape.js +8 -0
- package/dist/editor/html_escape.js.map +1 -0
- package/dist/editor/linestylefilter.d.ts +22 -0
- package/dist/editor/linestylefilter.d.ts.map +1 -0
- package/dist/editor/linestylefilter.js +298 -0
- package/dist/editor/linestylefilter.js.map +1 -0
- package/dist/editor/skiplist.d.ts +51 -0
- package/dist/editor/skiplist.d.ts.map +1 -0
- package/dist/editor/skiplist.js +327 -0
- package/dist/editor/skiplist.js.map +1 -0
- package/dist/editor/types/AText.d.ts +5 -0
- package/dist/editor/types/AText.d.ts.map +1 -0
- package/dist/editor/types/AText.js +2 -0
- package/dist/editor/types/AText.js.map +1 -0
- package/dist/editor/types/Attribute.d.ts +2 -0
- package/dist/editor/types/Attribute.d.ts.map +1 -0
- package/dist/editor/types/Attribute.js +2 -0
- package/dist/editor/types/Attribute.js.map +1 -0
- package/dist/editor/types/ChangeSet.d.ts +7 -0
- package/dist/editor/types/ChangeSet.d.ts.map +1 -0
- package/dist/editor/types/ChangeSet.js +2 -0
- package/dist/editor/types/ChangeSet.js.map +1 -0
- package/dist/editor/types/ChangeSetBuilder.d.ts +7 -0
- package/dist/editor/types/ChangeSetBuilder.d.ts.map +1 -0
- package/dist/editor/types/ChangeSetBuilder.js +2 -0
- package/dist/editor/types/ChangeSetBuilder.js.map +1 -0
- package/dist/editor/types/RepModel.d.ts +25 -0
- package/dist/editor/types/RepModel.d.ts.map +1 -0
- package/dist/editor/types/RepModel.js +2 -0
- package/dist/editor/types/RepModel.js.map +1 -0
- package/dist/editor/undomodule.d.ts +30 -0
- package/dist/editor/undomodule.d.ts.map +1 -0
- package/dist/editor/undomodule.js +266 -0
- package/dist/editor/undomodule.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/package.json +7 -2
|
@@ -0,0 +1,942 @@
|
|
|
1
|
+
import AttributeMap from './AttributeMap.js';
|
|
2
|
+
import AttributePool from "./AttributePool.js";
|
|
3
|
+
import Op from './Op.js';
|
|
4
|
+
import { numToString, parseNum } from './ChangesetUtils.js';
|
|
5
|
+
import { StringAssembler } from "./StringAssembler.js";
|
|
6
|
+
import { OpIter } from "./OpIter.js";
|
|
7
|
+
import { SmartOpAssembler } from "./SmartOpAssembler.js";
|
|
8
|
+
import TextLinesMutator from "./TextLinesMutator.js";
|
|
9
|
+
import { Builder } from "./Builder.js";
|
|
10
|
+
import { StringIterator } from "./StringIterator.js";
|
|
11
|
+
import { MergingOpAssembler } from "./MergingOpAssembler.js";
|
|
12
|
+
const error = (msg) => {
|
|
13
|
+
const e = new Error(msg);
|
|
14
|
+
e.easysync = true;
|
|
15
|
+
throw e;
|
|
16
|
+
};
|
|
17
|
+
export const assert = (b, msg) => {
|
|
18
|
+
if (!b)
|
|
19
|
+
error(`Failed assertion: ${msg}`);
|
|
20
|
+
};
|
|
21
|
+
export const oldLen = (cs) => unpack(cs).oldLen;
|
|
22
|
+
export const newLen = (cs) => unpack(cs).newLen;
|
|
23
|
+
export const deserializeOps = function* (ops) {
|
|
24
|
+
const regex = /((?:\*[0-9a-z]+)*)(?:\|([0-9a-z]+))?([-+=])([0-9a-z]+)|(.)/g;
|
|
25
|
+
let match;
|
|
26
|
+
while ((match = regex.exec(ops)) != null) {
|
|
27
|
+
if (match[5] === '$')
|
|
28
|
+
return;
|
|
29
|
+
if (match[5] != null)
|
|
30
|
+
error(`invalid operation: ${ops.slice(regex.lastIndex - 1)}`);
|
|
31
|
+
const opMatch = match[3];
|
|
32
|
+
const op = new Op(opMatch);
|
|
33
|
+
op.lines = parseNum(match[2] || '0');
|
|
34
|
+
op.chars = parseNum(match[4]);
|
|
35
|
+
op.attribs = match[1];
|
|
36
|
+
yield op;
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
export const opIterator = (opsStr) => new OpIter(opsStr);
|
|
40
|
+
export const clearOp = (op) => {
|
|
41
|
+
op.opcode = '';
|
|
42
|
+
op.chars = 0;
|
|
43
|
+
op.lines = 0;
|
|
44
|
+
op.attribs = '';
|
|
45
|
+
};
|
|
46
|
+
export const newOp = (optOpcode) => new Op(optOpcode);
|
|
47
|
+
export const copyOp = (op1, op2 = new Op()) => Object.assign(op2, op1);
|
|
48
|
+
export const opsFromText = function* (opcode, text, attribs = '', pool = null) {
|
|
49
|
+
const op = new Op(opcode);
|
|
50
|
+
op.attribs = typeof attribs === 'string'
|
|
51
|
+
? attribs : new AttributeMap(pool).update(attribs || [], opcode === '+').toString();
|
|
52
|
+
const lastNewlinePos = text.lastIndexOf('\n');
|
|
53
|
+
if (lastNewlinePos < 0) {
|
|
54
|
+
op.chars = text.length;
|
|
55
|
+
op.lines = 0;
|
|
56
|
+
yield op;
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
op.chars = lastNewlinePos + 1;
|
|
60
|
+
op.lines = text.match(/\n/g).length;
|
|
61
|
+
yield op;
|
|
62
|
+
const op2 = copyOp(op);
|
|
63
|
+
op2.chars = text.length - (lastNewlinePos + 1);
|
|
64
|
+
op2.lines = 0;
|
|
65
|
+
yield op2;
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
export const checkRep = (cs) => {
|
|
69
|
+
const unpacked = unpack(cs);
|
|
70
|
+
const oldLen = unpacked.oldLen;
|
|
71
|
+
const newLen = unpacked.newLen;
|
|
72
|
+
const ops = unpacked.ops;
|
|
73
|
+
let charBank = unpacked.charBank;
|
|
74
|
+
const assem = new SmartOpAssembler();
|
|
75
|
+
let oldPos = 0;
|
|
76
|
+
let calcNewLen = 0;
|
|
77
|
+
for (const o of deserializeOps(ops)) {
|
|
78
|
+
switch (o.opcode) {
|
|
79
|
+
case '=':
|
|
80
|
+
oldPos += o.chars;
|
|
81
|
+
calcNewLen += o.chars;
|
|
82
|
+
break;
|
|
83
|
+
case '-':
|
|
84
|
+
oldPos += o.chars;
|
|
85
|
+
assert(oldPos <= oldLen, `${oldPos} > ${oldLen} in ${cs}`);
|
|
86
|
+
break;
|
|
87
|
+
case '+':
|
|
88
|
+
{
|
|
89
|
+
assert(charBank.length >= o.chars, 'Invalid changeset: not enough chars in charBank');
|
|
90
|
+
const chars = charBank.slice(0, o.chars);
|
|
91
|
+
const nlines = (chars.match(/\n/g) || []).length;
|
|
92
|
+
assert(nlines === o.lines, 'Invalid changeset: number of newlines in insert op does not match the charBank');
|
|
93
|
+
assert(o.lines === 0 || chars.endsWith('\n'), 'Invalid changeset: multiline insert op does not end with a newline');
|
|
94
|
+
charBank = charBank.slice(o.chars);
|
|
95
|
+
calcNewLen += o.chars;
|
|
96
|
+
assert(calcNewLen <= newLen, `${calcNewLen} > ${newLen} in ${cs}`);
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
default:
|
|
100
|
+
assert(false, `Invalid changeset: Unknown opcode: ${JSON.stringify(o.opcode)}`);
|
|
101
|
+
}
|
|
102
|
+
assem.append(o);
|
|
103
|
+
}
|
|
104
|
+
calcNewLen += oldLen - oldPos;
|
|
105
|
+
assert(calcNewLen === newLen, 'Invalid changeset: claimed length does not match actual length');
|
|
106
|
+
assert(charBank === '', 'Invalid changeset: excess characters in the charBank');
|
|
107
|
+
assem.endDocument();
|
|
108
|
+
const normalized = pack(oldLen, calcNewLen, assem.toString(), unpacked.charBank);
|
|
109
|
+
assert(normalized === cs, 'Invalid changeset: not in canonical form');
|
|
110
|
+
return cs;
|
|
111
|
+
};
|
|
112
|
+
const applyZip = (in1, in2, func) => {
|
|
113
|
+
const ops1 = deserializeOps(in1);
|
|
114
|
+
const ops2 = deserializeOps(in2);
|
|
115
|
+
let next1 = ops1.next();
|
|
116
|
+
let next2 = ops2.next();
|
|
117
|
+
const assem = new SmartOpAssembler();
|
|
118
|
+
while (!next1.done || !next2.done) {
|
|
119
|
+
if (!next1.done && !next1.value.opcode)
|
|
120
|
+
next1 = ops1.next();
|
|
121
|
+
if (!next2.done && !next2.value.opcode)
|
|
122
|
+
next2 = ops2.next();
|
|
123
|
+
if (next1.value == null)
|
|
124
|
+
next1.value = new Op();
|
|
125
|
+
if (next2.value == null)
|
|
126
|
+
next2.value = new Op();
|
|
127
|
+
if (!next1.value.opcode && !next2.value.opcode)
|
|
128
|
+
break;
|
|
129
|
+
const opOut = func(next1.value, next2.value);
|
|
130
|
+
if (opOut && opOut.opcode)
|
|
131
|
+
assem.append(opOut);
|
|
132
|
+
}
|
|
133
|
+
assem.endDocument();
|
|
134
|
+
return assem.toString();
|
|
135
|
+
};
|
|
136
|
+
export const unpack = (cs) => {
|
|
137
|
+
const headerRegex = /Z:([0-9a-z]+)([><])([0-9a-z]+)|/;
|
|
138
|
+
const headerMatch = headerRegex.exec(cs);
|
|
139
|
+
if ((!headerMatch) || (!headerMatch[0]))
|
|
140
|
+
error(`Not a changeset: ${cs}`);
|
|
141
|
+
const oldLen = parseNum(headerMatch[1]);
|
|
142
|
+
const changeSign = (headerMatch[2] === '>') ? 1 : -1;
|
|
143
|
+
const changeMag = parseNum(headerMatch[3]);
|
|
144
|
+
const newLen = oldLen + changeSign * changeMag;
|
|
145
|
+
const opsStart = headerMatch[0].length;
|
|
146
|
+
let opsEnd = cs.indexOf('$');
|
|
147
|
+
if (opsEnd < 0)
|
|
148
|
+
opsEnd = cs.length;
|
|
149
|
+
return { oldLen, newLen, ops: cs.substring(opsStart, opsEnd), charBank: cs.substring(opsEnd + 1) };
|
|
150
|
+
};
|
|
151
|
+
export const pack = (oldLen, newLen, opsStr, bank) => {
|
|
152
|
+
const lenDiff = newLen - oldLen;
|
|
153
|
+
const lenDiffStr = (lenDiff >= 0 ? `>${numToString(lenDiff)}` : `<${numToString(-lenDiff)}`);
|
|
154
|
+
return ['Z:', numToString(oldLen), lenDiffStr, opsStr, '$', bank].join('');
|
|
155
|
+
};
|
|
156
|
+
export const applyToText = (cs, str) => {
|
|
157
|
+
const unpacked = unpack(cs);
|
|
158
|
+
assert(str.length === unpacked.oldLen, `mismatched apply: ${str.length} / ${unpacked.oldLen}`);
|
|
159
|
+
const bankIter = new StringIterator(unpacked.charBank);
|
|
160
|
+
const strIter = new StringIterator(str);
|
|
161
|
+
const assem = new StringAssembler();
|
|
162
|
+
for (const op of deserializeOps(unpacked.ops)) {
|
|
163
|
+
switch (op.opcode) {
|
|
164
|
+
case '+':
|
|
165
|
+
assem.append(bankIter.take(op.chars));
|
|
166
|
+
break;
|
|
167
|
+
case '-':
|
|
168
|
+
strIter.skip(op.chars);
|
|
169
|
+
break;
|
|
170
|
+
case '=':
|
|
171
|
+
assem.append(strIter.take(op.chars));
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
assem.append(strIter.take(strIter.remaining()));
|
|
176
|
+
return assem.toString();
|
|
177
|
+
};
|
|
178
|
+
export const mutateTextLines = (cs, lines) => {
|
|
179
|
+
const unpacked = unpack(cs);
|
|
180
|
+
const bankIter = new StringIterator(unpacked.charBank);
|
|
181
|
+
const mut = new TextLinesMutator(lines);
|
|
182
|
+
for (const op of deserializeOps(unpacked.ops)) {
|
|
183
|
+
switch (op.opcode) {
|
|
184
|
+
case '+':
|
|
185
|
+
mut.insert(bankIter.take(op.chars), op.lines);
|
|
186
|
+
break;
|
|
187
|
+
case '-':
|
|
188
|
+
mut.remove(op.chars, op.lines);
|
|
189
|
+
break;
|
|
190
|
+
case '=':
|
|
191
|
+
mut.skip(op.chars, op.lines, (!!op.attribs));
|
|
192
|
+
break;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
mut.close();
|
|
196
|
+
};
|
|
197
|
+
export const composeAttributes = (att1, att2, resultIsMutation, pool) => {
|
|
198
|
+
if ((!att1) && resultIsMutation)
|
|
199
|
+
return att2;
|
|
200
|
+
if (!att2)
|
|
201
|
+
return att1;
|
|
202
|
+
return AttributeMap.fromString(att1, pool).updateFromString(att2, !resultIsMutation).toString();
|
|
203
|
+
};
|
|
204
|
+
export const mutateAttributionLines = (cs, lines, pool) => {
|
|
205
|
+
const unpacked = unpack(cs);
|
|
206
|
+
const csOps = deserializeOps(unpacked.ops);
|
|
207
|
+
let csOpsNext = csOps.next();
|
|
208
|
+
const csBank = unpacked.charBank;
|
|
209
|
+
let csBankIndex = 0;
|
|
210
|
+
const mut = new TextLinesMutator(lines);
|
|
211
|
+
let lineOps = null;
|
|
212
|
+
let lineOpsNext = null;
|
|
213
|
+
const lineOpsHasNext = () => lineOpsNext && !lineOpsNext.done;
|
|
214
|
+
const isNextMutOp = () => lineOpsHasNext() || mut.hasMore();
|
|
215
|
+
const nextMutOp = () => {
|
|
216
|
+
if (!lineOpsHasNext() && mut.hasMore()) {
|
|
217
|
+
const line = mut.removeLines(1);
|
|
218
|
+
lineOps = deserializeOps(line);
|
|
219
|
+
lineOpsNext = lineOps.next();
|
|
220
|
+
}
|
|
221
|
+
if (!lineOpsHasNext())
|
|
222
|
+
return new Op();
|
|
223
|
+
const op = lineOpsNext.value;
|
|
224
|
+
lineOpsNext = lineOps.next();
|
|
225
|
+
return op;
|
|
226
|
+
};
|
|
227
|
+
let lineAssem = null;
|
|
228
|
+
const outputMutOp = (op) => {
|
|
229
|
+
if (!lineAssem)
|
|
230
|
+
lineAssem = new MergingOpAssembler();
|
|
231
|
+
lineAssem.append(op);
|
|
232
|
+
if (op.lines <= 0)
|
|
233
|
+
return;
|
|
234
|
+
assert(op.lines === 1, `Can't have op.lines of ${op.lines} in attribution lines`);
|
|
235
|
+
mut.insert(lineAssem.toString(), 1);
|
|
236
|
+
lineAssem = null;
|
|
237
|
+
};
|
|
238
|
+
let csOp = new Op();
|
|
239
|
+
let attOp = new Op();
|
|
240
|
+
while (csOp.opcode || !csOpsNext.done || attOp.opcode || isNextMutOp()) {
|
|
241
|
+
if (!csOp.opcode && !csOpsNext.done) {
|
|
242
|
+
csOp = csOpsNext.value;
|
|
243
|
+
csOpsNext = csOps.next();
|
|
244
|
+
}
|
|
245
|
+
if (!csOp.opcode && !attOp.opcode && !lineAssem && !lineOpsHasNext()) {
|
|
246
|
+
break;
|
|
247
|
+
}
|
|
248
|
+
else if (csOp.opcode === '=' && csOp.lines > 0 && !csOp.attribs && !attOp.opcode && !lineAssem && !lineOpsHasNext()) {
|
|
249
|
+
mut.skipLines(csOp.lines);
|
|
250
|
+
csOp.opcode = '';
|
|
251
|
+
}
|
|
252
|
+
else if (csOp.opcode === '+') {
|
|
253
|
+
const opOut = copyOp(csOp);
|
|
254
|
+
if (csOp.lines > 1) {
|
|
255
|
+
const firstLineLen = csBank.indexOf('\n', csBankIndex) + 1 - csBankIndex;
|
|
256
|
+
csOp.chars -= firstLineLen;
|
|
257
|
+
csOp.lines--;
|
|
258
|
+
opOut.lines = 1;
|
|
259
|
+
opOut.chars = firstLineLen;
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
csOp.opcode = '';
|
|
263
|
+
}
|
|
264
|
+
outputMutOp(opOut);
|
|
265
|
+
csBankIndex += opOut.chars;
|
|
266
|
+
}
|
|
267
|
+
else {
|
|
268
|
+
if (!attOp.opcode && isNextMutOp())
|
|
269
|
+
attOp = nextMutOp();
|
|
270
|
+
const opOut = slicerZipperFunc(attOp, csOp, pool);
|
|
271
|
+
if (opOut.opcode)
|
|
272
|
+
outputMutOp(opOut);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
assert(!lineAssem, `line assembler not finished:${cs}`);
|
|
276
|
+
mut.close();
|
|
277
|
+
};
|
|
278
|
+
export const slicerZipperFunc = (attOp, csOp, pool) => {
|
|
279
|
+
const opOut = new Op();
|
|
280
|
+
if (!attOp.opcode) {
|
|
281
|
+
copyOp(csOp, opOut);
|
|
282
|
+
csOp.opcode = '';
|
|
283
|
+
}
|
|
284
|
+
else if (!csOp.opcode) {
|
|
285
|
+
copyOp(attOp, opOut);
|
|
286
|
+
attOp.opcode = '';
|
|
287
|
+
}
|
|
288
|
+
else if (attOp.opcode === '-') {
|
|
289
|
+
copyOp(attOp, opOut);
|
|
290
|
+
attOp.opcode = '';
|
|
291
|
+
}
|
|
292
|
+
else if (csOp.opcode === '+') {
|
|
293
|
+
copyOp(csOp, opOut);
|
|
294
|
+
csOp.opcode = '';
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
for (const op of [attOp, csOp]) {
|
|
298
|
+
assert(op.chars >= op.lines, `op has more newlines than chars: ${op.toString()}`);
|
|
299
|
+
}
|
|
300
|
+
assert(attOp.chars < csOp.chars ? attOp.lines <= csOp.lines
|
|
301
|
+
: attOp.chars > csOp.chars ? attOp.lines >= csOp.lines
|
|
302
|
+
: attOp.lines === csOp.lines, 'line count mismatch when composing changesets');
|
|
303
|
+
assert(['+', '='].includes(attOp.opcode), `unexpected opcode in op: ${attOp.toString()}`);
|
|
304
|
+
assert(['-', '='].includes(csOp.opcode), `unexpected opcode in op: ${csOp.toString()}`);
|
|
305
|
+
opOut.opcode = {
|
|
306
|
+
'+': { '-': '', '=': '+' },
|
|
307
|
+
'=': { '-': '-', '=': '=' },
|
|
308
|
+
}[attOp.opcode][csOp.opcode];
|
|
309
|
+
const [fullyConsumedOp, partiallyConsumedOp] = [attOp, csOp].sort((a, b) => a.chars - b.chars);
|
|
310
|
+
opOut.chars = fullyConsumedOp.chars;
|
|
311
|
+
opOut.lines = fullyConsumedOp.lines;
|
|
312
|
+
opOut.attribs = csOp.opcode === '-'
|
|
313
|
+
? csOp.attribs
|
|
314
|
+
: composeAttributes(attOp.attribs, csOp.attribs, attOp.opcode === '=', pool);
|
|
315
|
+
partiallyConsumedOp.chars -= fullyConsumedOp.chars;
|
|
316
|
+
partiallyConsumedOp.lines -= fullyConsumedOp.lines;
|
|
317
|
+
if (!partiallyConsumedOp.chars)
|
|
318
|
+
partiallyConsumedOp.opcode = '';
|
|
319
|
+
fullyConsumedOp.opcode = '';
|
|
320
|
+
}
|
|
321
|
+
return opOut;
|
|
322
|
+
};
|
|
323
|
+
export const applyToAttribution = (cs, astr, pool) => {
|
|
324
|
+
const unpacked = unpack(cs);
|
|
325
|
+
return applyZip(astr, unpacked.ops, (op1, op2) => slicerZipperFunc(op1, op2, pool));
|
|
326
|
+
};
|
|
327
|
+
export const joinAttributionLines = (theAlines) => {
|
|
328
|
+
const assem = new MergingOpAssembler();
|
|
329
|
+
for (const aline of theAlines) {
|
|
330
|
+
for (const op of deserializeOps(aline))
|
|
331
|
+
assem.append(op);
|
|
332
|
+
}
|
|
333
|
+
return assem.toString();
|
|
334
|
+
};
|
|
335
|
+
export const splitAttributionLines = (attrOps, text) => {
|
|
336
|
+
const assem = new MergingOpAssembler();
|
|
337
|
+
const lines = [];
|
|
338
|
+
let pos = 0;
|
|
339
|
+
const appendOp = (op) => {
|
|
340
|
+
assem.append(op);
|
|
341
|
+
if (op.lines > 0) {
|
|
342
|
+
lines.push(assem.toString());
|
|
343
|
+
assem.clear();
|
|
344
|
+
}
|
|
345
|
+
pos += op.chars;
|
|
346
|
+
};
|
|
347
|
+
for (const op of deserializeOps(attrOps)) {
|
|
348
|
+
let numChars = op.chars;
|
|
349
|
+
let numLines = op.lines;
|
|
350
|
+
while (numLines > 1) {
|
|
351
|
+
const newlineEnd = text.indexOf('\n', pos) + 1;
|
|
352
|
+
assert(newlineEnd > 0, 'newlineEnd <= 0 in splitAttributionLines');
|
|
353
|
+
op.chars = newlineEnd - pos;
|
|
354
|
+
op.lines = 1;
|
|
355
|
+
appendOp(op);
|
|
356
|
+
numChars -= op.chars;
|
|
357
|
+
numLines -= op.lines;
|
|
358
|
+
}
|
|
359
|
+
if (numLines === 1) {
|
|
360
|
+
op.chars = numChars;
|
|
361
|
+
op.lines = 1;
|
|
362
|
+
}
|
|
363
|
+
appendOp(op);
|
|
364
|
+
}
|
|
365
|
+
return lines;
|
|
366
|
+
};
|
|
367
|
+
export const splitTextLines = (text) => text.match(/[^\n]*(?:\n|[^\n]$)/g);
|
|
368
|
+
export const compose = (cs1, cs2, pool) => {
|
|
369
|
+
const unpacked1 = unpack(cs1);
|
|
370
|
+
const unpacked2 = unpack(cs2);
|
|
371
|
+
const len1 = unpacked1.oldLen;
|
|
372
|
+
const len2 = unpacked1.newLen;
|
|
373
|
+
assert(len2 === unpacked2.oldLen, 'mismatched composition of two changesets');
|
|
374
|
+
const len3 = unpacked2.newLen;
|
|
375
|
+
const bankIter1 = new StringIterator(unpacked1.charBank);
|
|
376
|
+
const bankIter2 = new StringIterator(unpacked2.charBank);
|
|
377
|
+
const bankAssem = new StringAssembler();
|
|
378
|
+
const newOps = applyZip(unpacked1.ops, unpacked2.ops, (op1, op2) => {
|
|
379
|
+
const op1code = op1.opcode;
|
|
380
|
+
const op2code = op2.opcode;
|
|
381
|
+
if (op1code === '+' && op2code === '-') {
|
|
382
|
+
bankIter1.skip(Math.min(op1.chars, op2.chars));
|
|
383
|
+
}
|
|
384
|
+
const opOut = slicerZipperFunc(op1, op2, pool);
|
|
385
|
+
if (opOut.opcode === '+') {
|
|
386
|
+
if (op2code === '+') {
|
|
387
|
+
bankAssem.append(bankIter2.take(opOut.chars));
|
|
388
|
+
}
|
|
389
|
+
else {
|
|
390
|
+
bankAssem.append(bankIter1.take(opOut.chars));
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
return opOut;
|
|
394
|
+
});
|
|
395
|
+
return pack(len1, len3, newOps, bankAssem.toString());
|
|
396
|
+
};
|
|
397
|
+
export const attributeTester = (attribPair, pool) => {
|
|
398
|
+
const never = () => false;
|
|
399
|
+
if (!pool)
|
|
400
|
+
return never;
|
|
401
|
+
const attribNum = pool.putAttrib(attribPair, true);
|
|
402
|
+
if (attribNum < 0)
|
|
403
|
+
return never;
|
|
404
|
+
const re = new RegExp(`\\*${numToString(attribNum)}(?!\\w)`);
|
|
405
|
+
return (attribs) => re.test(attribs);
|
|
406
|
+
};
|
|
407
|
+
export const identity = (N) => pack(N, N, '', '');
|
|
408
|
+
export const makeSplice = (orig, start, ndel, ins, attribs, pool) => {
|
|
409
|
+
if (start < 0)
|
|
410
|
+
throw new RangeError(`start index must be non-negative (is ${start})`);
|
|
411
|
+
if (ndel < 0)
|
|
412
|
+
throw new RangeError(`characters to delete must be non-negative (is ${ndel})`);
|
|
413
|
+
if (start > orig.length)
|
|
414
|
+
start = orig.length;
|
|
415
|
+
if (ndel > orig.length - start)
|
|
416
|
+
ndel = orig.length - start;
|
|
417
|
+
const deleted = orig.substring(start, start + ndel);
|
|
418
|
+
const assem = new SmartOpAssembler();
|
|
419
|
+
const ops = (function* () {
|
|
420
|
+
yield* opsFromText('=', orig.substring(0, start));
|
|
421
|
+
yield* opsFromText('-', deleted);
|
|
422
|
+
yield* opsFromText('+', ins, attribs, pool);
|
|
423
|
+
})();
|
|
424
|
+
for (const op of ops)
|
|
425
|
+
assem.append(op);
|
|
426
|
+
assem.endDocument();
|
|
427
|
+
return pack(orig.length, orig.length + ins.length - ndel, assem.toString(), ins);
|
|
428
|
+
};
|
|
429
|
+
export const characterRangeFollow = (cs, startChar, endChar, insertionsAfter) => {
|
|
430
|
+
let newStartChar = startChar;
|
|
431
|
+
let newEndChar = endChar;
|
|
432
|
+
let lengthChangeSoFar = 0;
|
|
433
|
+
for (const splice of toSplices(cs)) {
|
|
434
|
+
const spliceStart = splice[0] + lengthChangeSoFar;
|
|
435
|
+
const spliceEnd = splice[1] + lengthChangeSoFar;
|
|
436
|
+
const newTextLength = splice[2].length;
|
|
437
|
+
const thisLengthChange = newTextLength - (spliceEnd - spliceStart);
|
|
438
|
+
if (spliceStart <= newStartChar && spliceEnd >= newEndChar) {
|
|
439
|
+
if (insertionsAfter) {
|
|
440
|
+
newStartChar = newEndChar = spliceStart;
|
|
441
|
+
}
|
|
442
|
+
else {
|
|
443
|
+
newStartChar = newEndChar = spliceStart + newTextLength;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
else if (spliceEnd <= newStartChar) {
|
|
447
|
+
newStartChar += thisLengthChange;
|
|
448
|
+
newEndChar += thisLengthChange;
|
|
449
|
+
}
|
|
450
|
+
else if (spliceStart >= newEndChar) {
|
|
451
|
+
// splice is after range
|
|
452
|
+
}
|
|
453
|
+
else if (spliceStart >= newStartChar && spliceEnd <= newEndChar) {
|
|
454
|
+
newEndChar += thisLengthChange;
|
|
455
|
+
}
|
|
456
|
+
else if (spliceEnd < newEndChar) {
|
|
457
|
+
newStartChar = spliceStart + newTextLength;
|
|
458
|
+
newEndChar += thisLengthChange;
|
|
459
|
+
}
|
|
460
|
+
else {
|
|
461
|
+
newEndChar = spliceStart;
|
|
462
|
+
}
|
|
463
|
+
lengthChangeSoFar += thisLengthChange;
|
|
464
|
+
}
|
|
465
|
+
return [newStartChar, newEndChar];
|
|
466
|
+
};
|
|
467
|
+
const toSplices = (cs) => {
|
|
468
|
+
const unpacked = unpack(cs);
|
|
469
|
+
const splices = [];
|
|
470
|
+
let oldPos = 0;
|
|
471
|
+
const charIter = new StringIterator(unpacked.charBank);
|
|
472
|
+
let inSplice = false;
|
|
473
|
+
for (const op of deserializeOps(unpacked.ops)) {
|
|
474
|
+
if (op.opcode === '=') {
|
|
475
|
+
oldPos += op.chars;
|
|
476
|
+
inSplice = false;
|
|
477
|
+
}
|
|
478
|
+
else {
|
|
479
|
+
if (!inSplice) {
|
|
480
|
+
splices.push([oldPos, oldPos, '']);
|
|
481
|
+
inSplice = true;
|
|
482
|
+
}
|
|
483
|
+
if (op.opcode === '-') {
|
|
484
|
+
oldPos += op.chars;
|
|
485
|
+
splices[splices.length - 1][1] += op.chars;
|
|
486
|
+
}
|
|
487
|
+
else if (op.opcode === '+') {
|
|
488
|
+
splices[splices.length - 1][2] += charIter.take(op.chars);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
return splices;
|
|
493
|
+
};
|
|
494
|
+
export const moveOpsToNewPool = (cs, oldPool, newPool) => {
|
|
495
|
+
let dollarPos = cs.indexOf('$');
|
|
496
|
+
if (dollarPos < 0)
|
|
497
|
+
dollarPos = cs.length;
|
|
498
|
+
const upToDollar = cs.substring(0, dollarPos);
|
|
499
|
+
const fromDollar = cs.substring(dollarPos);
|
|
500
|
+
return upToDollar.replace(/\*([0-9a-z]+)/g, (_, a) => {
|
|
501
|
+
const oldNum = parseNum(a);
|
|
502
|
+
const pair = oldPool.getAttrib(oldNum);
|
|
503
|
+
if (!pair)
|
|
504
|
+
return '';
|
|
505
|
+
const newNum = newPool.putAttrib(pair);
|
|
506
|
+
return `*${numToString(newNum)}`;
|
|
507
|
+
}) + fromDollar;
|
|
508
|
+
};
|
|
509
|
+
export const makeAttribution = (text) => {
|
|
510
|
+
const assem = new SmartOpAssembler();
|
|
511
|
+
for (const op of opsFromText('+', text))
|
|
512
|
+
assem.append(op);
|
|
513
|
+
return assem.toString();
|
|
514
|
+
};
|
|
515
|
+
export const eachAttribNumber = (cs, func) => {
|
|
516
|
+
let dollarPos = cs.indexOf('$');
|
|
517
|
+
if (dollarPos < 0)
|
|
518
|
+
dollarPos = cs.length;
|
|
519
|
+
const upToDollar = cs.substring(0, dollarPos);
|
|
520
|
+
upToDollar.replace(/\*([0-9a-z]+)/g, (_, a) => {
|
|
521
|
+
func(parseNum(a));
|
|
522
|
+
return '';
|
|
523
|
+
});
|
|
524
|
+
};
|
|
525
|
+
export const filterAttribNumbers = (cs, filter) => mapAttribNumbers(cs, filter);
|
|
526
|
+
export const mapAttribNumbers = (cs, func) => {
|
|
527
|
+
let dollarPos = cs.indexOf('$');
|
|
528
|
+
if (dollarPos < 0)
|
|
529
|
+
dollarPos = cs.length;
|
|
530
|
+
const upToDollar = cs.substring(0, dollarPos);
|
|
531
|
+
const newUpToDollar = upToDollar.replace(/\*([0-9a-z]+)/g, (s, a) => {
|
|
532
|
+
const n = func(parseNum(a));
|
|
533
|
+
if (n === true)
|
|
534
|
+
return s;
|
|
535
|
+
else if ((typeof n) === 'number')
|
|
536
|
+
return `*${numToString(n)}`;
|
|
537
|
+
else
|
|
538
|
+
return '';
|
|
539
|
+
});
|
|
540
|
+
return newUpToDollar + cs.substring(dollarPos);
|
|
541
|
+
};
|
|
542
|
+
export const makeAText = (text, attribs) => ({
|
|
543
|
+
text,
|
|
544
|
+
attribs: (attribs || makeAttribution(text)),
|
|
545
|
+
});
|
|
546
|
+
export const applyToAText = (cs, atext, pool) => ({
|
|
547
|
+
text: applyToText(cs, atext.text),
|
|
548
|
+
attribs: applyToAttribution(cs, atext.attribs, pool),
|
|
549
|
+
});
|
|
550
|
+
export const cloneAText = (atext) => {
|
|
551
|
+
if (!atext)
|
|
552
|
+
error('atext is null');
|
|
553
|
+
return { text: atext.text, attribs: atext.attribs };
|
|
554
|
+
};
|
|
555
|
+
export const copyAText = (atext1, atext2) => {
|
|
556
|
+
atext2.text = atext1.text;
|
|
557
|
+
atext2.attribs = atext1.attribs;
|
|
558
|
+
};
|
|
559
|
+
export const opsFromAText = function* (atext) {
|
|
560
|
+
let lastOp = null;
|
|
561
|
+
for (const op of deserializeOps(atext.attribs)) {
|
|
562
|
+
if (lastOp != null)
|
|
563
|
+
yield lastOp;
|
|
564
|
+
lastOp = op;
|
|
565
|
+
}
|
|
566
|
+
if (lastOp == null)
|
|
567
|
+
return;
|
|
568
|
+
if (lastOp.lines <= 1) {
|
|
569
|
+
lastOp.lines = 0;
|
|
570
|
+
lastOp.chars--;
|
|
571
|
+
}
|
|
572
|
+
else {
|
|
573
|
+
const nextToLastNewlineEnd = atext.text.lastIndexOf('\n', atext.text.length - 2) + 1;
|
|
574
|
+
const lastLineLength = atext.text.length - nextToLastNewlineEnd - 1;
|
|
575
|
+
lastOp.lines--;
|
|
576
|
+
lastOp.chars -= (lastLineLength + 1);
|
|
577
|
+
yield copyOp(lastOp);
|
|
578
|
+
lastOp.lines = 0;
|
|
579
|
+
lastOp.chars = lastLineLength;
|
|
580
|
+
}
|
|
581
|
+
if (lastOp.chars)
|
|
582
|
+
yield lastOp;
|
|
583
|
+
};
|
|
584
|
+
export const appendATextToAssembler = (atext, assem) => {
|
|
585
|
+
for (const op of opsFromAText(atext))
|
|
586
|
+
assem.append(op);
|
|
587
|
+
};
|
|
588
|
+
export const prepareForWire = (cs, pool) => {
|
|
589
|
+
const newPool = new AttributePool();
|
|
590
|
+
const newCs = moveOpsToNewPool(cs, pool, newPool);
|
|
591
|
+
return { translated: newCs, pool: newPool };
|
|
592
|
+
};
|
|
593
|
+
export const isIdentity = (cs) => {
|
|
594
|
+
const unpacked = unpack(cs);
|
|
595
|
+
return unpacked.ops === '' && unpacked.oldLen === unpacked.newLen;
|
|
596
|
+
};
|
|
597
|
+
export const subattribution = (astr, start, optEnd) => {
|
|
598
|
+
const attOps = deserializeOps(astr);
|
|
599
|
+
let attOpsNext = attOps.next();
|
|
600
|
+
const assem = new SmartOpAssembler();
|
|
601
|
+
let attOp = new Op();
|
|
602
|
+
const csOp = new Op();
|
|
603
|
+
const doCsOp = () => {
|
|
604
|
+
if (!csOp.chars)
|
|
605
|
+
return;
|
|
606
|
+
while (csOp.opcode && (attOp.opcode || !attOpsNext.done)) {
|
|
607
|
+
if (!attOp.opcode) {
|
|
608
|
+
attOp = attOpsNext.value;
|
|
609
|
+
attOpsNext = attOps.next();
|
|
610
|
+
}
|
|
611
|
+
if (csOp.opcode && attOp.opcode && csOp.chars >= attOp.chars && attOp.lines > 0 && csOp.lines <= 0) {
|
|
612
|
+
csOp.lines++;
|
|
613
|
+
}
|
|
614
|
+
const opOut = slicerZipperFunc(attOp, csOp, null);
|
|
615
|
+
if (opOut.opcode)
|
|
616
|
+
assem.append(opOut);
|
|
617
|
+
}
|
|
618
|
+
};
|
|
619
|
+
csOp.opcode = '-';
|
|
620
|
+
csOp.chars = start;
|
|
621
|
+
doCsOp();
|
|
622
|
+
if (optEnd === undefined) {
|
|
623
|
+
if (attOp.opcode)
|
|
624
|
+
assem.append(attOp);
|
|
625
|
+
while (!attOpsNext.done) {
|
|
626
|
+
assem.append(attOpsNext.value);
|
|
627
|
+
attOpsNext = attOps.next();
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
else {
|
|
631
|
+
csOp.opcode = '=';
|
|
632
|
+
csOp.chars = optEnd - start;
|
|
633
|
+
doCsOp();
|
|
634
|
+
}
|
|
635
|
+
return assem.toString();
|
|
636
|
+
};
|
|
637
|
+
export const inverse = (cs, lines, alines, pool) => {
|
|
638
|
+
const linesGet = (idx) => {
|
|
639
|
+
if (lines && typeof lines === 'object' && "get" in lines)
|
|
640
|
+
return lines.get(idx);
|
|
641
|
+
return lines[idx];
|
|
642
|
+
};
|
|
643
|
+
const alinesGet = (idx) => {
|
|
644
|
+
if (typeof alines === 'object' && "get" in alines)
|
|
645
|
+
return alines.get(idx);
|
|
646
|
+
return alines[idx];
|
|
647
|
+
};
|
|
648
|
+
let curLine = 0;
|
|
649
|
+
let curChar = 0;
|
|
650
|
+
let curLineOps = null;
|
|
651
|
+
let curLineOpsNext = null;
|
|
652
|
+
let curLineOpsLine;
|
|
653
|
+
let curLineNextOp = new Op('+');
|
|
654
|
+
const unpacked = unpack(cs);
|
|
655
|
+
const builder = new Builder(unpacked.newLen);
|
|
656
|
+
const consumeAttribRuns = (numChars, func) => {
|
|
657
|
+
if (!curLineOps || curLineOpsLine !== curLine) {
|
|
658
|
+
curLineOps = deserializeOps(alinesGet(curLine));
|
|
659
|
+
curLineOpsNext = curLineOps.next();
|
|
660
|
+
curLineOpsLine = curLine;
|
|
661
|
+
let indexIntoLine = 0;
|
|
662
|
+
while (!curLineOpsNext.done) {
|
|
663
|
+
curLineNextOp = curLineOpsNext.value;
|
|
664
|
+
curLineOpsNext = curLineOps.next();
|
|
665
|
+
if (indexIntoLine + curLineNextOp.chars >= curChar) {
|
|
666
|
+
curLineNextOp.chars -= (curChar - indexIntoLine);
|
|
667
|
+
break;
|
|
668
|
+
}
|
|
669
|
+
indexIntoLine += curLineNextOp.chars;
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
while (numChars > 0) {
|
|
673
|
+
if (!curLineNextOp.chars && curLineOpsNext.done) {
|
|
674
|
+
curLine++;
|
|
675
|
+
curChar = 0;
|
|
676
|
+
curLineOpsLine = curLine;
|
|
677
|
+
curLineNextOp.chars = 0;
|
|
678
|
+
curLineOps = deserializeOps(alinesGet(curLine));
|
|
679
|
+
curLineOpsNext = curLineOps.next();
|
|
680
|
+
}
|
|
681
|
+
if (!curLineNextOp.chars) {
|
|
682
|
+
if (curLineOpsNext.done) {
|
|
683
|
+
curLineNextOp = new Op();
|
|
684
|
+
}
|
|
685
|
+
else {
|
|
686
|
+
curLineNextOp = curLineOpsNext.value;
|
|
687
|
+
curLineOpsNext = curLineOps.next();
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
const charsToUse = Math.min(numChars, curLineNextOp.chars);
|
|
691
|
+
func(charsToUse, curLineNextOp.attribs, charsToUse === curLineNextOp.chars && curLineNextOp.lines > 0);
|
|
692
|
+
numChars -= charsToUse;
|
|
693
|
+
curLineNextOp.chars -= charsToUse;
|
|
694
|
+
curChar += charsToUse;
|
|
695
|
+
}
|
|
696
|
+
if (!curLineNextOp.chars && curLineOpsNext.done) {
|
|
697
|
+
curLine++;
|
|
698
|
+
curChar = 0;
|
|
699
|
+
}
|
|
700
|
+
};
|
|
701
|
+
const skip = (N, L) => {
|
|
702
|
+
if (L) {
|
|
703
|
+
curLine += L;
|
|
704
|
+
curChar = 0;
|
|
705
|
+
}
|
|
706
|
+
else if (curLineOps && curLineOpsLine === curLine) {
|
|
707
|
+
consumeAttribRuns(N, () => { });
|
|
708
|
+
}
|
|
709
|
+
else {
|
|
710
|
+
curChar += N;
|
|
711
|
+
}
|
|
712
|
+
};
|
|
713
|
+
const nextText = (numChars) => {
|
|
714
|
+
let len = 0;
|
|
715
|
+
const assem = new StringAssembler();
|
|
716
|
+
const firstString = linesGet(curLine).substring(curChar);
|
|
717
|
+
len += firstString.length;
|
|
718
|
+
assem.append(firstString);
|
|
719
|
+
let lineNum = curLine + 1;
|
|
720
|
+
while (len < numChars) {
|
|
721
|
+
const nextString = linesGet(lineNum);
|
|
722
|
+
len += nextString.length;
|
|
723
|
+
assem.append(nextString);
|
|
724
|
+
lineNum++;
|
|
725
|
+
}
|
|
726
|
+
return assem.toString().substring(0, numChars);
|
|
727
|
+
};
|
|
728
|
+
const cachedStrFunc = (func) => {
|
|
729
|
+
const cache = {};
|
|
730
|
+
return (s) => { if (!cache[s])
|
|
731
|
+
cache[s] = func(s); return cache[s]; };
|
|
732
|
+
};
|
|
733
|
+
for (const csOp of deserializeOps(unpacked.ops)) {
|
|
734
|
+
if (csOp.opcode === '=') {
|
|
735
|
+
if (csOp.attribs) {
|
|
736
|
+
const attribs = AttributeMap.fromString(csOp.attribs, pool);
|
|
737
|
+
const undoBackToAttribs = cachedStrFunc((oldAttribsStr) => {
|
|
738
|
+
const oldAttribs = AttributeMap.fromString(oldAttribsStr, pool);
|
|
739
|
+
const backAttribs = new AttributeMap(pool);
|
|
740
|
+
for (const [key, value] of attribs) {
|
|
741
|
+
const oldValue = oldAttribs.get(key) || '';
|
|
742
|
+
if (oldValue !== value)
|
|
743
|
+
backAttribs.set(key, oldValue);
|
|
744
|
+
}
|
|
745
|
+
return backAttribs.toString();
|
|
746
|
+
});
|
|
747
|
+
consumeAttribRuns(csOp.chars, (len, attribs, endsLine) => {
|
|
748
|
+
builder.keep(len, endsLine ? 1 : 0, undoBackToAttribs(attribs));
|
|
749
|
+
});
|
|
750
|
+
}
|
|
751
|
+
else {
|
|
752
|
+
skip(csOp.chars, csOp.lines);
|
|
753
|
+
builder.keep(csOp.chars, csOp.lines);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
else if (csOp.opcode === '+') {
|
|
757
|
+
builder.remove(csOp.chars, csOp.lines);
|
|
758
|
+
}
|
|
759
|
+
else if (csOp.opcode === '-') {
|
|
760
|
+
const textBank = nextText(csOp.chars);
|
|
761
|
+
let textBankIndex = 0;
|
|
762
|
+
consumeAttribRuns(csOp.chars, (len, attribs) => {
|
|
763
|
+
builder.insert(textBank.substr(textBankIndex, len), attribs);
|
|
764
|
+
textBankIndex += len;
|
|
765
|
+
});
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
return checkRep(builder.toString());
|
|
769
|
+
};
|
|
770
|
+
export const follow = (cs1, cs2, reverseInsertOrder, pool) => {
|
|
771
|
+
const unpacked1 = unpack(cs1);
|
|
772
|
+
const unpacked2 = unpack(cs2);
|
|
773
|
+
const len1 = unpacked1.oldLen;
|
|
774
|
+
const len2 = unpacked2.oldLen;
|
|
775
|
+
assert(len1 === len2, 'mismatched follow - cannot transform cs1 on top of cs2');
|
|
776
|
+
const chars1 = new StringIterator(unpacked1.charBank);
|
|
777
|
+
const chars2 = new StringIterator(unpacked2.charBank);
|
|
778
|
+
const oldLen = unpacked1.newLen;
|
|
779
|
+
let oldPos = 0;
|
|
780
|
+
let newLen = 0;
|
|
781
|
+
const hasInsertFirst = attributeTester(['insertorder', 'first'], pool);
|
|
782
|
+
const newOps = applyZip(unpacked1.ops, unpacked2.ops, (op1, op2) => {
|
|
783
|
+
const opOut = new Op();
|
|
784
|
+
if (op1.opcode === '+' || op2.opcode === '+') {
|
|
785
|
+
let whichToDo;
|
|
786
|
+
if (op2.opcode !== '+') {
|
|
787
|
+
whichToDo = 1;
|
|
788
|
+
}
|
|
789
|
+
else if (op1.opcode !== '+') {
|
|
790
|
+
whichToDo = 2;
|
|
791
|
+
}
|
|
792
|
+
else {
|
|
793
|
+
const firstChar1 = chars1.peek(1);
|
|
794
|
+
const firstChar2 = chars2.peek(1);
|
|
795
|
+
const insertFirst1 = hasInsertFirst(op1.attribs);
|
|
796
|
+
const insertFirst2 = hasInsertFirst(op2.attribs);
|
|
797
|
+
if (insertFirst1 && !insertFirst2) {
|
|
798
|
+
whichToDo = 1;
|
|
799
|
+
}
|
|
800
|
+
else if (insertFirst2 && !insertFirst1) {
|
|
801
|
+
whichToDo = 2;
|
|
802
|
+
}
|
|
803
|
+
else if (firstChar1 === '\n' && firstChar2 !== '\n') {
|
|
804
|
+
whichToDo = 2;
|
|
805
|
+
}
|
|
806
|
+
else if (firstChar1 !== '\n' && firstChar2 === '\n') {
|
|
807
|
+
whichToDo = 1;
|
|
808
|
+
}
|
|
809
|
+
else if (reverseInsertOrder) {
|
|
810
|
+
whichToDo = 2;
|
|
811
|
+
}
|
|
812
|
+
else {
|
|
813
|
+
whichToDo = 1;
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
if (whichToDo === 1) {
|
|
817
|
+
chars1.skip(op1.chars);
|
|
818
|
+
opOut.opcode = '=';
|
|
819
|
+
opOut.lines = op1.lines;
|
|
820
|
+
opOut.chars = op1.chars;
|
|
821
|
+
opOut.attribs = '';
|
|
822
|
+
op1.opcode = '';
|
|
823
|
+
}
|
|
824
|
+
else {
|
|
825
|
+
chars2.skip(op2.chars);
|
|
826
|
+
copyOp(op2, opOut);
|
|
827
|
+
op2.opcode = '';
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
else if (op1.opcode === '-') {
|
|
831
|
+
if (!op2.opcode) {
|
|
832
|
+
op1.opcode = '';
|
|
833
|
+
}
|
|
834
|
+
else if (op1.chars <= op2.chars) {
|
|
835
|
+
op2.chars -= op1.chars;
|
|
836
|
+
op2.lines -= op1.lines;
|
|
837
|
+
op1.opcode = '';
|
|
838
|
+
if (!op2.chars)
|
|
839
|
+
op2.opcode = '';
|
|
840
|
+
}
|
|
841
|
+
else {
|
|
842
|
+
op1.chars -= op2.chars;
|
|
843
|
+
op1.lines -= op2.lines;
|
|
844
|
+
op2.opcode = '';
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
else if (op2.opcode === '-') {
|
|
848
|
+
copyOp(op2, opOut);
|
|
849
|
+
if (!op1.opcode) {
|
|
850
|
+
op2.opcode = '';
|
|
851
|
+
}
|
|
852
|
+
else if (op2.chars <= op1.chars) {
|
|
853
|
+
op1.chars -= op2.chars;
|
|
854
|
+
op1.lines -= op2.lines;
|
|
855
|
+
op2.opcode = '';
|
|
856
|
+
if (!op1.chars)
|
|
857
|
+
op1.opcode = '';
|
|
858
|
+
}
|
|
859
|
+
else {
|
|
860
|
+
opOut.lines = op1.lines;
|
|
861
|
+
opOut.chars = op1.chars;
|
|
862
|
+
op2.lines -= op1.lines;
|
|
863
|
+
op2.chars -= op1.chars;
|
|
864
|
+
op1.opcode = '';
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
else if (!op1.opcode) {
|
|
868
|
+
copyOp(op2, opOut);
|
|
869
|
+
op2.opcode = '';
|
|
870
|
+
}
|
|
871
|
+
else if (!op2.opcode) {
|
|
872
|
+
op1.opcode = '';
|
|
873
|
+
}
|
|
874
|
+
else {
|
|
875
|
+
opOut.opcode = '=';
|
|
876
|
+
opOut.attribs = followAttributes(op1.attribs, op2.attribs, pool);
|
|
877
|
+
if (op1.chars <= op2.chars) {
|
|
878
|
+
opOut.chars = op1.chars;
|
|
879
|
+
opOut.lines = op1.lines;
|
|
880
|
+
op2.chars -= op1.chars;
|
|
881
|
+
op2.lines -= op1.lines;
|
|
882
|
+
op1.opcode = '';
|
|
883
|
+
if (!op2.chars)
|
|
884
|
+
op2.opcode = '';
|
|
885
|
+
}
|
|
886
|
+
else {
|
|
887
|
+
opOut.chars = op2.chars;
|
|
888
|
+
opOut.lines = op2.lines;
|
|
889
|
+
op1.chars -= op2.chars;
|
|
890
|
+
op1.lines -= op2.lines;
|
|
891
|
+
op2.opcode = '';
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
switch (opOut.opcode) {
|
|
895
|
+
case '=':
|
|
896
|
+
oldPos += opOut.chars;
|
|
897
|
+
newLen += opOut.chars;
|
|
898
|
+
break;
|
|
899
|
+
case '-':
|
|
900
|
+
oldPos += opOut.chars;
|
|
901
|
+
break;
|
|
902
|
+
case '+':
|
|
903
|
+
newLen += opOut.chars;
|
|
904
|
+
break;
|
|
905
|
+
}
|
|
906
|
+
return opOut;
|
|
907
|
+
});
|
|
908
|
+
newLen += oldLen - oldPos;
|
|
909
|
+
return pack(oldLen, newLen, newOps, unpacked2.charBank);
|
|
910
|
+
};
|
|
911
|
+
const followAttributes = (att1, att2, pool) => {
|
|
912
|
+
if ((!att2) || (!pool))
|
|
913
|
+
return '';
|
|
914
|
+
if (!att1)
|
|
915
|
+
return att2;
|
|
916
|
+
const atts = new Map();
|
|
917
|
+
att2.replace(/\*([0-9a-z]+)/g, (_, a) => {
|
|
918
|
+
const attrib = pool.getAttrib(parseNum(a));
|
|
919
|
+
if (!attrib)
|
|
920
|
+
return '';
|
|
921
|
+
const [key, val] = attrib;
|
|
922
|
+
atts.set(key, val);
|
|
923
|
+
return '';
|
|
924
|
+
});
|
|
925
|
+
att1.replace(/\*([0-9a-z]+)/g, (_, a) => {
|
|
926
|
+
const attrib = pool.getAttrib(parseNum(a));
|
|
927
|
+
if (!attrib)
|
|
928
|
+
return '';
|
|
929
|
+
const [key, val] = attrib;
|
|
930
|
+
if (atts.has(key) && val <= atts.get(key))
|
|
931
|
+
atts.delete(key);
|
|
932
|
+
return '';
|
|
933
|
+
});
|
|
934
|
+
const buf = new StringAssembler();
|
|
935
|
+
for (const att of atts) {
|
|
936
|
+
buf.append('*');
|
|
937
|
+
buf.append(numToString(pool.putAttrib(att)));
|
|
938
|
+
}
|
|
939
|
+
return buf.toString();
|
|
940
|
+
};
|
|
941
|
+
export const exportedForTestingOnly = { TextLinesMutator, followAttributes, toSplices };
|
|
942
|
+
//# sourceMappingURL=Changeset.js.map
|