muggle-string 0.4.1 → 1.0.0-alpha.1

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.
@@ -0,0 +1,387 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createSourceRewrite = createSourceRewrite;
4
+ function createSourceRewrite(source, text) {
5
+ return new Rewrite(source, text);
6
+ }
7
+ const coreRank = 0x7fffffff;
8
+ // Before: outermost (highest rank) first; Slot and After: innermost first
9
+ function compareEdits(a, b) {
10
+ if (a.at !== b.at) {
11
+ return a.at - b.at;
12
+ }
13
+ if (a.side !== b.side) {
14
+ return a.side - b.side;
15
+ }
16
+ if (a.rank !== b.rank) {
17
+ return a.side === 0 /* Side.Before */ ? b.rank - a.rank : a.rank - b.rank;
18
+ }
19
+ if (a.order !== b.order) {
20
+ return a.order < b.order ? -1 : 1;
21
+ }
22
+ return a.seq - b.seq;
23
+ }
24
+ class Edits {
25
+ constructor() {
26
+ this.records = [];
27
+ // the sorted records by column, filled when frozen
28
+ this.at = new Int32Array(0);
29
+ this.side = new Uint8Array(0);
30
+ this.code = [];
31
+ }
32
+ push(at, side, rank, order, code) {
33
+ this.records.push({ at, side, rank, order, seq: this.records.length, code });
34
+ }
35
+ freeze() {
36
+ const records = this.records;
37
+ // every writer's edits usually arrive in order; the sort is linear on such runs
38
+ for (let i = 1; i < records.length; i++) {
39
+ if (compareEdits(records[i - 1], records[i]) > 0) {
40
+ records.sort(compareEdits);
41
+ break;
42
+ }
43
+ }
44
+ const size = records.length;
45
+ this.at = new Int32Array(size);
46
+ this.side = new Uint8Array(size);
47
+ this.code = new Array(size);
48
+ for (let i = 0; i < size; i++) {
49
+ const record = records[i];
50
+ this.at[i] = record.at;
51
+ this.side[i] = record.side;
52
+ this.code[i] = record.code;
53
+ }
54
+ this.records = [];
55
+ }
56
+ /** The first index from `low` whose anchor is at or after `offset`. */
57
+ lowerBound(offset, low) {
58
+ const at = this.at;
59
+ let high = at.length;
60
+ while (low < high) {
61
+ const mid = (low + high) >>> 1;
62
+ if (at[mid] < offset) {
63
+ low = mid + 1;
64
+ }
65
+ else {
66
+ high = mid;
67
+ }
68
+ }
69
+ return low;
70
+ }
71
+ }
72
+ class Rewrite {
73
+ constructor(source, text) {
74
+ this.source = source;
75
+ this.text = text;
76
+ this.edits = new Edits();
77
+ /** `insertAfter(0)`: before all text. */
78
+ this.head = new Edits();
79
+ /** `insertBefore(length)`: after all text. */
80
+ this.tail = new Edits();
81
+ /** Removed ranges as flat start, end pairs. */
82
+ this.removalPairs = [];
83
+ /** The removed ranges, sorted and merged, filled when frozen. */
84
+ this.removals = new Int32Array(0);
85
+ /** The top-level cuts, sorted and disjoint. */
86
+ this.roots = [];
87
+ this.frozen = false;
88
+ this.core = this.createWriter(coreRank);
89
+ }
90
+ writer(rank) {
91
+ if (!Number.isInteger(rank) || rank < 0 || rank >= coreRank) {
92
+ throw new RangeError(`The rank ${rank} is not a non-negative integer below the rank of core.`);
93
+ }
94
+ return this.createWriter(rank);
95
+ }
96
+ createWriter(rank) {
97
+ return {
98
+ insertBefore: (offset, code, options) => {
99
+ this.check(offset, offset);
100
+ const order = getOrder(options);
101
+ if (offset === this.text.length) {
102
+ this.tail.push(offset, 2 /* Side.After */, rank, order, code);
103
+ }
104
+ else {
105
+ this.edits.push(offset, 0 /* Side.Before */, rank, order, code);
106
+ }
107
+ },
108
+ insertAfter: (offset, code, options) => {
109
+ this.check(offset, offset);
110
+ const order = getOrder(options);
111
+ if (offset === 0) {
112
+ this.head.push(0, 0 /* Side.Before */, rank, order, code);
113
+ }
114
+ else {
115
+ this.edits.push(offset - 1, 2 /* Side.After */, rank, order, code);
116
+ }
117
+ },
118
+ replace: (start, end, code, options) => {
119
+ this.check(start, end);
120
+ if (start === end) {
121
+ throw new RangeError(`The replace at ${start}..${end} removes no char.`);
122
+ }
123
+ this.edits.push(start, 1 /* Side.Slot */, rank, getOrder(options), code);
124
+ this.removalPairs.push(start, end);
125
+ },
126
+ cut: (start, end, { data, placeholder }) => {
127
+ this.check(start, end);
128
+ const cut = new Cut(this, start, end, data, placeholder);
129
+ if (start === end) {
130
+ if (placeholder !== undefined) {
131
+ throw new RangeError(`The empty cut ${start}..${end} cannot have a placeholder.`);
132
+ }
133
+ if (!this.text.length) {
134
+ this.firstEmpty ??= cut;
135
+ }
136
+ }
137
+ else {
138
+ this.insertCut(cut);
139
+ }
140
+ return cut;
141
+ },
142
+ };
143
+ }
144
+ check(start, end) {
145
+ if (this.frozen) {
146
+ throw new Error('The rewrite is already rendered.');
147
+ }
148
+ if (!Number.isInteger(start) || !Number.isInteger(end) || start < 0 || start > end || end > this.text.length) {
149
+ throw new RangeError(`${start}..${end} is out of 0..${this.text.length}.`);
150
+ }
151
+ }
152
+ // A cut that crosses `cut` intersects it, so it lies inside every cut that contains `cut`: the descent only checks
153
+ // the siblings that `cut` intersects, of the innermost cut that contains it.
154
+ insertCut(cut) {
155
+ const { start, end } = cut;
156
+ const last = this.lastCut;
157
+ let siblings = last && last.start <= start && end <= last.end ? last.children : this.roots;
158
+ while (true) {
159
+ let first = 0;
160
+ let after = siblings.length;
161
+ for (let high = after; first < high;) {
162
+ const mid = (first + high) >>> 1;
163
+ if (siblings[mid].end <= start) {
164
+ first = mid + 1;
165
+ }
166
+ else {
167
+ high = mid;
168
+ }
169
+ }
170
+ for (let low = first; low < after;) {
171
+ const mid = (low + after) >>> 1;
172
+ if (siblings[mid].start < end) {
173
+ low = mid + 1;
174
+ }
175
+ else {
176
+ after = mid;
177
+ }
178
+ }
179
+ if (after - first === 1 && siblings[first].start <= start && end <= siblings[first].end) {
180
+ siblings = siblings[first].children;
181
+ continue;
182
+ }
183
+ if (after > first) {
184
+ // the siblings in between lie strictly inside `cut`
185
+ for (const sibling of [siblings[first], siblings[after - 1]]) {
186
+ if (sibling.start < start || sibling.end > end) {
187
+ throw new RangeError(`The cut ${start}..${end} crosses the cut ${sibling.start}..${sibling.end}; cuts are nested or disjoint.`);
188
+ }
189
+ }
190
+ cut.children = siblings.splice(first, after - first, cut);
191
+ }
192
+ else {
193
+ siblings.splice(first, 0, cut);
194
+ }
195
+ this.lastCut = cut;
196
+ return;
197
+ }
198
+ }
199
+ freeze() {
200
+ if (this.frozen) {
201
+ return;
202
+ }
203
+ this.frozen = true;
204
+ this.edits.freeze();
205
+ this.head.freeze();
206
+ this.tail.freeze();
207
+ const removals = this.removalPairs;
208
+ const pairs = removals.length >> 1;
209
+ for (let pair = 1; pair < pairs; pair++) {
210
+ if (removals[2 * pair - 2] > removals[2 * pair]) {
211
+ const sorted = Array.from({ length: pairs }, (_, index) => [removals[2 * index], removals[2 * index + 1]])
212
+ .sort((a, b) => a[0] - b[0]);
213
+ for (let index = 0; index < pairs; index++) {
214
+ removals[2 * index] = sorted[index][0];
215
+ removals[2 * index + 1] = sorted[index][1];
216
+ }
217
+ break;
218
+ }
219
+ }
220
+ let merged = 0;
221
+ for (let pair = 0; pair < pairs; pair++) {
222
+ const start = removals[2 * pair];
223
+ const end = removals[2 * pair + 1];
224
+ if (merged && start <= removals[merged - 1]) {
225
+ if (end > removals[merged - 1]) {
226
+ removals[merged - 1] = end;
227
+ }
228
+ }
229
+ else {
230
+ removals[merged++] = start;
231
+ removals[merged++] = end;
232
+ }
233
+ }
234
+ removals.length = merged;
235
+ this.removals = Int32Array.from(removals);
236
+ this.removalPairs = [];
237
+ for (const root of this.roots) {
238
+ root.isRoot = true;
239
+ }
240
+ }
241
+ }
242
+ function getOrder(options) {
243
+ const order = options?.order ?? 0;
244
+ if (typeof order !== 'number' || Number.isNaN(order)) {
245
+ throw new TypeError(`The order ${order} is not a number.`);
246
+ }
247
+ return order;
248
+ }
249
+ class Cut {
250
+ constructor(rewrite, start, end, data, placeholder) {
251
+ this.rewrite = rewrite;
252
+ this.start = start;
253
+ this.end = end;
254
+ this.data = data;
255
+ this.placeholder = placeholder;
256
+ /** The cuts directly inside, sorted and disjoint. */
257
+ this.children = [];
258
+ this.isRoot = false;
259
+ this.placed = false;
260
+ }
261
+ pieces(out = []) {
262
+ if (this.placed) {
263
+ throw new Error(`The cut ${this.start}..${this.end} is placed twice.`);
264
+ }
265
+ this.placed = true;
266
+ const rewrite = this.rewrite;
267
+ rewrite.freeze();
268
+ const { edits, head, tail } = rewrite;
269
+ const { start, end, children } = this;
270
+ if (start === end) {
271
+ if (this === rewrite.firstEmpty) {
272
+ pushAll(head, out);
273
+ pushAll(tail, out);
274
+ }
275
+ return out;
276
+ }
277
+ if (this.isRoot && start === 0) {
278
+ pushAll(head, out);
279
+ }
280
+ const walker = new Walker(rewrite.removals, rewrite.source, this.data, out, start);
281
+ const { at, side, code } = edits;
282
+ const size = at.length;
283
+ let edit = edits.lowerBound(start, 0);
284
+ for (let index = 0;; index++) {
285
+ const child = children[index];
286
+ const until = child ? child.start : end;
287
+ for (; edit < size && at[edit] < until; edit++) {
288
+ walker.keep(side[edit] === 2 /* Side.After */ ? at[edit] + 1 : at[edit]);
289
+ pushCode(code[edit], out);
290
+ }
291
+ if (!child) {
292
+ break;
293
+ }
294
+ walker.keep(child.start);
295
+ if (child.placeholder !== undefined) {
296
+ pushCode(child.placeholder, out);
297
+ }
298
+ walker.skip(child.end);
299
+ edit = edits.lowerBound(child.end, edit);
300
+ }
301
+ walker.keep(end);
302
+ if (this.isRoot && end === rewrite.text.length) {
303
+ pushAll(tail, out);
304
+ }
305
+ return out;
306
+ }
307
+ }
308
+ /** Pushes the kept chars of one cut, as pieces between the removals. */
309
+ class Walker {
310
+ constructor(removals, source, data, out, pos) {
311
+ this.removals = removals;
312
+ this.source = source;
313
+ this.data = data;
314
+ this.out = out;
315
+ this.pos = pos;
316
+ /** The first removal that ends after `pos`. */
317
+ this.removal = 0;
318
+ this.seek();
319
+ }
320
+ /** The kept chars from `pos` to `to`. */
321
+ keep(to) {
322
+ const { removals, out } = this;
323
+ const size = removals.length;
324
+ let { pos, removal } = this;
325
+ while (pos < to) {
326
+ while (removal < size && removals[removal + 1] <= pos) {
327
+ removal += 2;
328
+ }
329
+ if (removal < size && removals[removal] <= pos) {
330
+ pos = removals[removal + 1] < to ? removals[removal + 1] : to;
331
+ continue;
332
+ }
333
+ const stop = removal < size && removals[removal] < to ? removals[removal] : to;
334
+ out.push({ source: this.source, start: pos, end: stop, data: this.data });
335
+ pos = stop;
336
+ }
337
+ this.pos = pos;
338
+ this.removal = removal;
339
+ }
340
+ /** Moves past the chars up to `to`, which a child cut owns. */
341
+ skip(to) {
342
+ this.pos = to;
343
+ this.seek();
344
+ }
345
+ seek() {
346
+ const removals = this.removals;
347
+ let low = this.removal >> 1;
348
+ let high = removals.length >> 1;
349
+ while (low < high) {
350
+ const mid = (low + high) >>> 1;
351
+ if (removals[2 * mid + 1] <= this.pos) {
352
+ low = mid + 1;
353
+ }
354
+ else {
355
+ high = mid;
356
+ }
357
+ }
358
+ this.removal = low * 2;
359
+ }
360
+ }
361
+ function pushAll(edits, out) {
362
+ for (const code of edits.code) {
363
+ pushCode(code, out);
364
+ }
365
+ }
366
+ function pushCode(code, out) {
367
+ if (typeof code === 'string') {
368
+ if (code) {
369
+ out.push(code);
370
+ }
371
+ }
372
+ else if (code instanceof Cut) {
373
+ code.pieces(out);
374
+ }
375
+ else if (isCodeArray(code)) {
376
+ for (let index = 0; index < code.length; index++) {
377
+ pushCode(code[index], out);
378
+ }
379
+ }
380
+ else {
381
+ out.push(code);
382
+ }
383
+ }
384
+ function isCodeArray(code) {
385
+ return Array.isArray(code);
386
+ }
387
+ //# sourceMappingURL=sourceRewrite.js.map
package/out/types.d.ts CHANGED
@@ -1,16 +1,12 @@
1
- declare const NO_DATA_SYMBOL: unique symbol;
2
- export type Segment<T = typeof NO_DATA_SYMBOL> = string | (T extends typeof NO_DATA_SYMBOL ? [
3
- text: string,
4
- source: string | undefined,
5
- sourceOffset: number
6
- ] : [
7
- text: string,
8
- source: string | undefined,
9
- sourceOffset: number,
10
- data: T
11
- ]);
12
- export interface StackNode {
13
- length: number;
14
- stack: string;
15
- }
16
- export {};
1
+ /**
2
+ * Generated code: new text, which maps nowhere, or the range `start`..`end` of the text of `source`. A range without
3
+ * `text` is a copy of the text there; `text` is code that stands for the range, mapped as its consumer decides. An
4
+ * empty range is an anchor.
5
+ */
6
+ export type Piece<Source, Data> = string | {
7
+ source: Source;
8
+ start: number;
9
+ end: number;
10
+ data: Data;
11
+ text?: string;
12
+ };
package/package.json CHANGED
@@ -1,7 +1,9 @@
1
1
  {
2
2
  "name": "muggle-string",
3
- "version": "0.4.1",
3
+ "version": "1.0.0-alpha.1",
4
+ "description": "Ranked, conflict-free rewrites of a source text, rendered as format-neutral pieces",
4
5
  "main": "out/index.js",
6
+ "types": "out/index.d.ts",
5
7
  "license": "MIT",
6
8
  "files": [
7
9
  "out/**/*.js",
@@ -12,11 +14,12 @@
12
14
  "url": "https://github.com/johnsoncodehk/muggle-string.git"
13
15
  },
14
16
  "scripts": {
17
+ "build": "tsc",
15
18
  "test": "vitest run"
16
19
  },
17
20
  "devDependencies": {
18
- "typescript": "latest",
19
- "vitest": "latest",
20
- "vite": "latest"
21
+ "@types/node": "^26.0.1",
22
+ "typescript": "7.1.0-beta",
23
+ "vitest": "latest"
21
24
  }
22
25
  }
package/out/base.d.ts DELETED
@@ -1,6 +0,0 @@
1
- import { Range, Segment } from './types';
2
- export declare function getLength(segments: Segment<any>[]): number;
3
- export declare function toString<T extends Segment<any>>(segments: T[]): string;
4
- export declare function replace<T extends Segment<any>>(segments: T[], pattern: string | RegExp, ...replacers: (T | ((match: string) => T))[]): void;
5
- export declare function replaceAll<T extends Segment<any>>(segments: T[], pattern: RegExp, ...replacers: (T | ((match: string) => T))[]): void;
6
- export declare function overwrite<T extends Segment<any>>(segments: T[], range: number | Range, ...inserts: T[]): T[];
package/out/base.js DELETED
@@ -1,119 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.overwrite = exports.replaceAll = exports.replace = exports.toString = exports.getLength = void 0;
4
- const binarySearch_1 = require("./binarySearch");
5
- const track_1 = require("./track");
6
- function getLength(segments) {
7
- let length = 0;
8
- for (const segment of segments) {
9
- length += typeof segment == 'string' ? segment.length : segment[0].length;
10
- }
11
- return length;
12
- }
13
- exports.getLength = getLength;
14
- function toString(segments) {
15
- return segments.map(s => typeof s === 'string' ? s : s[0]).join('');
16
- }
17
- exports.toString = toString;
18
- function replace(segments, pattern, ...replacers) {
19
- const str = toString(segments);
20
- const match = str.match(pattern);
21
- if (match && match.index !== undefined) {
22
- const start = match.index;
23
- const end = start + match[0].length;
24
- (0, track_1.offsetStack)();
25
- overwrite(segments, [start, end], ...replacers.map(replacer => typeof replacer === 'function' ? replacer(match[0]) : replacer));
26
- (0, track_1.resetOffsetStack)();
27
- }
28
- }
29
- exports.replace = replace;
30
- function replaceAll(segments, pattern, ...replacers) {
31
- const str = toString(segments);
32
- const allMatch = str.matchAll(pattern);
33
- let length = str.length;
34
- let lengthDiff = 0;
35
- for (const match of allMatch) {
36
- if (match.index !== undefined) {
37
- const start = match.index + lengthDiff;
38
- const end = start + match[0].length;
39
- (0, track_1.offsetStack)();
40
- overwrite(segments, [start, end], ...replacers.map(replacer => typeof replacer === 'function' ? replacer(match[0]) : replacer));
41
- (0, track_1.resetOffsetStack)();
42
- const newLength = getLength(segments);
43
- lengthDiff += newLength - length;
44
- length = newLength;
45
- }
46
- }
47
- }
48
- exports.replaceAll = replaceAll;
49
- function overwrite(segments, range, ...inserts) {
50
- const offsets = toOffsets(segments);
51
- const [start, end] = typeof range === 'number' ? [range, range] : range;
52
- const startIndex = (0, binarySearch_1.binarySearch)(offsets, start);
53
- const endIndex = (0, binarySearch_1.binarySearch)(offsets, end);
54
- const startSegment = segments[startIndex];
55
- const endSegment = segments[endIndex];
56
- const startSegmentStart = offsets[startIndex];
57
- const endSegmentStart = offsets[endIndex];
58
- const endSegmentEnd = offsets[endIndex] + (typeof endSegment === 'string' ? endSegment.length : endSegment[0].length);
59
- if (start > startSegmentStart) {
60
- inserts.unshift(trimSegmentEnd(startSegment, start - startSegmentStart));
61
- }
62
- if (end < endSegmentEnd) {
63
- inserts.push(trimSegmentStart(endSegment, end - endSegmentStart));
64
- }
65
- (0, track_1.offsetStack)();
66
- const deleted = segments.splice(startIndex, endIndex - startIndex + 1, ...inserts);
67
- (0, track_1.resetOffsetStack)();
68
- if (end < endSegmentEnd) {
69
- deleted[deleted.length - 1] = trimSegmentEnd(deleted[deleted.length - 1], end - endSegmentStart);
70
- }
71
- if (start > startSegmentStart) {
72
- deleted[0] = trimSegmentStart(deleted[0], start - startSegmentStart);
73
- }
74
- return deleted;
75
- }
76
- exports.overwrite = overwrite;
77
- function trimSegmentEnd(segment, trimEnd) {
78
- if (typeof segment === 'string') {
79
- return segment.slice(0, trimEnd);
80
- }
81
- const originalString = segment[0];
82
- const originalRange = segment[2];
83
- const newString = originalString.slice(0, trimEnd);
84
- const newRange = typeof originalRange === 'number' ? originalRange : [originalRange[0], originalRange[1] - (originalString.length - newString.length)];
85
- return [
86
- newString,
87
- segment[1],
88
- newRange,
89
- ...segment.slice(3),
90
- ];
91
- }
92
- function trimSegmentStart(segment, trimStart) {
93
- if (typeof segment === 'string') {
94
- return segment.slice(trimStart);
95
- }
96
- const originalString = segment[0];
97
- const originalRange = segment[2];
98
- const newString = originalString.slice(trimStart);
99
- if (trimStart < 0) {
100
- trimStart += originalString.length;
101
- }
102
- const newRange = typeof originalRange === 'number' ? originalRange + trimStart : [originalRange[0] + trimStart, originalRange[1]];
103
- return [
104
- newString,
105
- segment[1],
106
- newRange,
107
- ...segment.slice(3),
108
- ];
109
- }
110
- function toOffsets(segments) {
111
- const offsets = [];
112
- let offset = 0;
113
- for (const segment of segments) {
114
- offsets.push(offset);
115
- offset += typeof segment == 'string' ? segment.length : segment[0].length;
116
- }
117
- return offsets;
118
- }
119
- //# sourceMappingURL=base.js.map
package/out/basic.d.ts DELETED
@@ -1,6 +0,0 @@
1
- import { Range, Segment } from './types';
2
- export declare function getLength(segments: Segment<any>[]): number;
3
- export declare function toString<T extends Segment<any>>(segments: T[]): string;
4
- export declare function replace<T extends Segment<any>>(segments: T[], pattern: string | RegExp, ...replacers: (T | ((match: string) => T))[]): void;
5
- export declare function replaceAll<T extends Segment<any>>(segments: T[], pattern: RegExp, ...replacers: (T | ((match: string) => T))[]): void;
6
- export declare function overwrite<T extends Segment<any>>(segments: T[], range: number | Range, ...inserts: T[]): T[];
package/out/basic.js DELETED
@@ -1,119 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.overwrite = exports.replaceAll = exports.replace = exports.toString = exports.getLength = void 0;
4
- const binarySearch_1 = require("./binarySearch");
5
- const track_1 = require("./track");
6
- function getLength(segments) {
7
- let length = 0;
8
- for (const segment of segments) {
9
- length += typeof segment == 'string' ? segment.length : segment[0].length;
10
- }
11
- return length;
12
- }
13
- exports.getLength = getLength;
14
- function toString(segments) {
15
- return segments.map(s => typeof s === 'string' ? s : s[0]).join('');
16
- }
17
- exports.toString = toString;
18
- function replace(segments, pattern, ...replacers) {
19
- const str = toString(segments);
20
- const match = str.match(pattern);
21
- if (match && match.index !== undefined) {
22
- const start = match.index;
23
- const end = start + match[0].length;
24
- (0, track_1.offsetStack)();
25
- overwrite(segments, [start, end], ...replacers.map(replacer => typeof replacer === 'function' ? replacer(match[0]) : replacer));
26
- (0, track_1.resetOffsetStack)();
27
- }
28
- }
29
- exports.replace = replace;
30
- function replaceAll(segments, pattern, ...replacers) {
31
- const str = toString(segments);
32
- const allMatch = str.matchAll(pattern);
33
- let length = str.length;
34
- let lengthDiff = 0;
35
- for (const match of allMatch) {
36
- if (match.index !== undefined) {
37
- const start = match.index + lengthDiff;
38
- const end = start + match[0].length;
39
- (0, track_1.offsetStack)();
40
- overwrite(segments, [start, end], ...replacers.map(replacer => typeof replacer === 'function' ? replacer(match[0]) : replacer));
41
- (0, track_1.resetOffsetStack)();
42
- const newLength = getLength(segments);
43
- lengthDiff += newLength - length;
44
- length = newLength;
45
- }
46
- }
47
- }
48
- exports.replaceAll = replaceAll;
49
- function overwrite(segments, range, ...inserts) {
50
- const offsets = toOffsets(segments);
51
- const [start, end] = typeof range === 'number' ? [range, range] : range;
52
- const startIndex = (0, binarySearch_1.binarySearch)(offsets, start);
53
- const endIndex = (0, binarySearch_1.binarySearch)(offsets, end);
54
- const startSegment = segments[startIndex];
55
- const endSegment = segments[endIndex];
56
- const startSegmentStart = offsets[startIndex];
57
- const endSegmentStart = offsets[endIndex];
58
- const endSegmentEnd = offsets[endIndex] + (typeof endSegment === 'string' ? endSegment.length : endSegment[0].length);
59
- if (start > startSegmentStart) {
60
- inserts.unshift(trimSegmentEnd(startSegment, start - startSegmentStart));
61
- }
62
- if (end < endSegmentEnd) {
63
- inserts.push(trimSegmentStart(endSegment, end - endSegmentStart));
64
- }
65
- (0, track_1.offsetStack)();
66
- const deleted = segments.splice(startIndex, endIndex - startIndex + 1, ...inserts);
67
- (0, track_1.resetOffsetStack)();
68
- if (end < endSegmentEnd) {
69
- deleted[deleted.length - 1] = trimSegmentEnd(deleted[deleted.length - 1], end - endSegmentStart);
70
- }
71
- if (start > startSegmentStart) {
72
- deleted[0] = trimSegmentStart(deleted[0], start - startSegmentStart);
73
- }
74
- return deleted;
75
- }
76
- exports.overwrite = overwrite;
77
- function trimSegmentEnd(segment, trimEnd) {
78
- if (typeof segment === 'string') {
79
- return segment.slice(0, trimEnd);
80
- }
81
- const originalString = segment[0];
82
- const originalRange = segment[2];
83
- const newString = originalString.slice(0, trimEnd);
84
- const newRange = typeof originalRange === 'number' ? originalRange : [originalRange[0], originalRange[1] - (originalString.length - newString.length)];
85
- return [
86
- newString,
87
- segment[1],
88
- newRange,
89
- ...segment.slice(3),
90
- ];
91
- }
92
- function trimSegmentStart(segment, trimStart) {
93
- if (typeof segment === 'string') {
94
- return segment.slice(trimStart);
95
- }
96
- const originalString = segment[0];
97
- const originalRange = segment[2];
98
- const newString = originalString.slice(trimStart);
99
- if (trimStart < 0) {
100
- trimStart += originalString.length;
101
- }
102
- const newRange = typeof originalRange === 'number' ? originalRange + trimStart : [originalRange[0] + trimStart, originalRange[1]];
103
- return [
104
- newString,
105
- segment[1],
106
- newRange,
107
- ...segment.slice(3),
108
- ];
109
- }
110
- function toOffsets(segments) {
111
- const offsets = [];
112
- let offset = 0;
113
- for (const segment of segments) {
114
- offsets.push(offset);
115
- offset += typeof segment == 'string' ? segment.length : segment[0].length;
116
- }
117
- return offsets;
118
- }
119
- //# sourceMappingURL=basic.js.map