|
1 | 1 | import * as Cursors from "../Cursors" |
2 | 2 | import * as Spans from "../Spans" |
| 3 | +import JSONClone from "lib/JSONClone" |
3 | 4 | import must from "lib/must" |
4 | 5 |
|
5 | | -// Drops up to n-bytes from an array of spans at an offset. |
6 | | -// Returns the number of bytes dropped. |
7 | | -function dropBytes({ spans, offset, nbytes }) { |
8 | | - // Compute the span and character offsets (offset): |
| 6 | +// // Returns the span and character offsets for an array of |
| 7 | +// // spans at an offset. |
| 8 | +// function computeSpanOffsets(spans, offset) { |
| 9 | +// const offsets = { |
| 10 | +// span: 0, |
| 11 | +// char: 0, |
| 12 | +// } |
| 13 | +// |
| 14 | +// let x = 0 |
| 15 | +// for (; x < spans.length; x++) { |
| 16 | +// const children = spans[x].props.children |
| 17 | +// if (offset - children.length <= 0) { |
| 18 | +// Object.assign(offsets, { |
| 19 | +// span: x, |
| 20 | +// char: offset, |
| 21 | +// }) |
| 22 | +// return offsets |
| 23 | +// } |
| 24 | +// offset -= children.length |
| 25 | +// } |
| 26 | +// return null |
| 27 | +// } |
| 28 | + |
| 29 | +// Returns the span and character offsets for an array of |
| 30 | +// spans at an offset. |
| 31 | +function computeSpanOffsets(spans, offset) { |
9 | 32 | let x = 0 |
10 | 33 | for (; x < spans.length; x++) { |
11 | | - if (offset - spans[x].props.children.length <= 0) { |
12 | | - // No-op |
13 | | - break |
| 34 | + const children = spans[x].props.children |
| 35 | + if (offset - children.length <= 0) { |
| 36 | + return { span: x, char: offset } |
14 | 37 | } |
15 | | - offset -= spans[x].props.children.length |
| 38 | + offset -= children.length |
16 | 39 | } |
17 | | - // Drop up to n-bytes: |
18 | | - nbytes = Math.min(nbytes, offset) |
19 | | - spans[x].props.children = ( |
20 | | - spans[x].props.children.slice(0, offset - nbytes) + |
21 | | - spans[x].props.children.slice(offset) |
| 40 | + return null |
| 41 | +} |
| 42 | + |
| 43 | +// Drops a number of characters from an array of spans at an |
| 44 | +// offset. Returns the number of characters dropped. |
| 45 | +function dropChars(spans, offset, nchars) { |
| 46 | + const offsets = must(computeSpanOffsets(spans, offset)) |
| 47 | + if (nchars > offsets.char) { |
| 48 | + nchars = offsets.char |
| 49 | + } |
| 50 | + spans[offsets.span].props.children = ( |
| 51 | + spans[offsets.span].props.children.slice(0, offsets.char - nchars) + |
| 52 | + spans[offsets.span].props.children.slice(offsets.char) |
22 | 53 | ) |
23 | | - if (!spans[x].props.children) { |
24 | | - spans.splice(x, 1) |
| 54 | + if (!spans[offsets.span].props.children) { |
| 55 | + spans.splice(offsets.span, 1) |
25 | 56 | } |
26 | 57 | Spans.defer(spans) |
27 | | - return nbytes |
| 58 | + return nchars |
28 | 59 | } |
29 | 60 |
|
30 | | -// Drops bytes between cursors. |
| 61 | +// Counts the number of characters between the offsets of a |
| 62 | +// set of cursors. |
| 63 | +function nchars(cursors) { |
| 64 | + const nchars = 0 |
| 65 | + if (cursors[0].key === cursors[1].key) { |
| 66 | + return cursors[1].offset - cursors[0].offset |
| 67 | + } |
| 68 | + return cursors[1].offset |
| 69 | +} |
| 70 | + |
| 71 | +// Drops the characters between a set of cursors. |
31 | 72 | function dropCursors(elements, cursors) { |
| 73 | + cursors = JSONClone(cursors) // Do not mutate references |
| 74 | + |
32 | 75 | let y = must(elements.findIndex(each => each.key === cursors[1].key)) |
33 | 76 | while (!Cursors.areEqual(cursors[0], cursors[1])) { |
34 | | - let nbytes = cursors[1].offset - (cursors[0].key === cursors[1].key && cursors[0].offset) |
35 | | - if (!nbytes && y) { |
36 | | - // Read the current span (for cursors[1].offset): |
37 | | - const textContent = Spans.textContent(elements[y - 1].props.children) |
38 | | - // Push and defer spans: |
39 | | - elements[y - 1].props.children.push(...elements[y].props.children) |
40 | | - elements.splice(y, 1) |
| 77 | + if (!cursors[1].offset && y) { |
| 78 | + // const offset = Spans.textContent(elements[y - 1].props.children).length // Precompute |
| 79 | + // elements[y - 1].props.children.push(...elements.splice(y, 1)[0].props.children) |
| 80 | + // Spans.defer(elements[y - 1].props.children) |
| 81 | + const offset = Spans.textContent(elements[y - 1].props.children).length |
| 82 | + elements.splice(y - 1, 2, { |
| 83 | + ...elements[y - 1], |
| 84 | + props: { |
| 85 | + ...elements[y - 1].props, |
| 86 | + children: [ |
| 87 | + ...elements[y - 1].props.children, |
| 88 | + ...elements[y].props.children, |
| 89 | + ], |
| 90 | + }, |
| 91 | + }) |
41 | 92 | Spans.defer(elements[y - 1].props.children) |
42 | | - // Reset cursor[1]: |
43 | 93 | Object.assign(cursors[1], { |
44 | 94 | key: elements[y - 1].key, |
45 | | - offset: textContent.length, |
| 95 | + offset, |
46 | 96 | }) |
47 | 97 | y-- |
48 | 98 | continue |
49 | 99 | } |
50 | | - const ref = { spans: elements[y].props.children, offset: cursors[1].offset, nbytes } |
51 | | - nbytes = dropBytes(ref) |
52 | | - cursors[1].offset -= nbytes |
| 100 | + const dropped = dropChars(elements[y].props.children, cursors[1].offset, nchars(cursors)) |
| 101 | + cursors[1].offset -= dropped |
53 | 102 | } |
54 | 103 | } |
55 | 104 |
|
|
0 commit comments