Skip to content

Commit 4bae799

Browse files
author
Zaydek Michels-Gualtieri
committed
Almost finished extracting backspace handlers
1 parent 3b70aaa commit 4bae799

File tree

4 files changed

+98
-50
lines changed

4 files changed

+98
-50
lines changed

src/Editor/backspace/backspace.js

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@ import * as Spans from "../Spans"
44
import dropCursors from "./dropCursors"
55
import must from "lib/must"
66

7-
// Returns the next right-to-left (RTL) cursor for a
8-
// boundary.
7+
// Returns the next right-to-left cursor for a boundary.
98
function nextRTLCursor(elements, { ...rtl }, boundary) {
109
const y = must(elements.findIndex(each => each.key === rtl.key))
1110
const substr = Spans.textContent(elements[y].props.children).slice(0, rtl.offset)
@@ -16,12 +15,13 @@ function nextRTLCursor(elements, { ...rtl }, boundary) {
1615
})
1716
return rtl
1817
}
19-
const runes = Iterators.RTL[boundary](substr)
20-
rtl.offset -= runes.length
18+
// const runes = Iterators.RTL[boundary](substr) // DEBUG
19+
// rtl.offset -= runes.length // DEBUG
20+
rtl.offset -= Iterators.RTL[boundary](substr).length
2121
return rtl
2222
}
2323

24-
// Returns the next left-to-right (LTR) cursor.
24+
// Returns the next left-to-right cursor for a boundary.
2525
function nextLTRCursor(elements, { ...ltr }, boundary) {
2626
const y = must(elements.findIndex(each => each.key === ltr.key))
2727
const substr = Spans.textContent(elements[y].props.children).slice(ltr.offset)
@@ -32,13 +32,13 @@ function nextLTRCursor(elements, { ...ltr }, boundary) {
3232
})
3333
return ltr
3434
}
35-
const runes = Iterators.LTR[boundary](substr)
36-
ltr.offset += runes.length
35+
// const runes = Iterators.LTR[boundary](substr) // DEBUG
36+
// ltr.offset += runes.length // DEBUG
37+
ltr.offset += Iterators.LTR[boundary](substr).length
3738
return ltr
3839
}
3940

40-
// Returns the next set of cursors for a direction and a
41-
// boundary.
41+
// Returns the next set of cursors.
4242
function nextCursors(elements, cursors, dir, boundary) {
4343
if (!cursors.collapsed) {
4444
return cursors
@@ -60,13 +60,12 @@ function nextCursors(elements, cursors, dir, boundary) {
6060
return next
6161
}
6262

63-
// Backspace handler. Returns a collapsed set of cursors.
64-
// dir maps to "rtl" or "ltr" and boundary maps to "rune",
65-
// "word", or "line".
66-
function backspace(elements, cursors, dir, boundary) {
63+
// Backspace handler for both directions and all boundaries.
64+
// Returns the next set of collapsed cursors.
65+
function backspaceHandler(elements, cursors, dir, boundary) {
6766
const next = nextCursors(elements, cursors, dir, boundary)
6867
dropCursors(elements, next)
6968
return Cursors.collapse(next)
7069
}
7170

72-
export default backspace
71+
export default backspaceHandler

src/Editor/backspace/dropCursors.js

Lines changed: 78 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,104 @@
11
import * as Cursors from "../Cursors"
22
import * as Spans from "../Spans"
3+
import JSONClone from "lib/JSONClone"
34
import must from "lib/must"
45

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) {
932
let x = 0
1033
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 }
1437
}
15-
offset -= spans[x].props.children.length
38+
offset -= children.length
1639
}
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)
2253
)
23-
if (!spans[x].props.children) {
24-
spans.splice(x, 1)
54+
if (!spans[offsets.span].props.children) {
55+
spans.splice(offsets.span, 1)
2556
}
2657
Spans.defer(spans)
27-
return nbytes
58+
return nchars
2859
}
2960

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.
3172
function dropCursors(elements, cursors) {
73+
cursors = JSONClone(cursors) // Do not mutate references
74+
3275
let y = must(elements.findIndex(each => each.key === cursors[1].key))
3376
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+
})
4192
Spans.defer(elements[y - 1].props.children)
42-
// Reset cursor[1]:
4393
Object.assign(cursors[1], {
4494
key: elements[y - 1].key,
45-
offset: textContent.length,
95+
offset,
4696
})
4797
y--
4898
continue
4999
}
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
53102
}
54103
}
55104

src/Editor/useEditor.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as Cursors from "./Cursors"
22
import * as Elements from "./Elements"
3-
import backspace from "./backspace"
3+
import backspaceHandler from "./backspace"
44
import must from "lib/must"
55
import newHashID from "lib/newHashID"
66
import React from "react"
@@ -107,31 +107,31 @@ const methods = state => ({
107107

108108
backspaceRTLRune() {
109109
const { elements, cursors } = state
110-
const collapsed = backspace(elements, cursors, "rtl", "rune")
110+
const collapsed = backspaceHandler(elements, cursors, "rtl", "rune")
111111
this.select(collapsed)
112112
this.render()
113113
},
114114
backspaceRTLWord() {
115115
const { elements, cursors } = state
116-
const collapsed = backspace(elements, cursors, "rtl", "word")
116+
const collapsed = backspaceHandler(elements, cursors, "rtl", "word")
117117
this.select(collapsed)
118118
this.render()
119119
},
120120
backspaceRTLLine() {
121121
const { elements, cursors } = state
122-
const collapsed = backspace(elements, cursors, "rtl", "line")
122+
const collapsed = backspaceHandler(elements, cursors, "rtl", "line")
123123
this.select(collapsed)
124124
this.render()
125125
},
126126
backspaceLTRRune() {
127127
const { elements, cursors } = state
128-
const collapsed = backspace(elements, cursors, "ltr", "rune")
128+
const collapsed = backspaceHandler(elements, cursors, "ltr", "rune")
129129
this.select(collapsed)
130130
this.render()
131131
},
132132
backspaceLTRWord() {
133133
const { elements, cursors } = state
134-
const collapsed = backspace(elements, cursors, "ltr", "word")
134+
const collapsed = backspaceHandler(elements, cursors, "ltr", "word")
135135
this.select(collapsed)
136136
this.render()
137137
},

src/lib/must/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
//
99
function must(value) {
1010
if (value === undefined || value === false || value === -1 || value === "" || value === null) {
11-
throw new Error(`must: value=${JSON.stringify(value)} must not be undefined, false, -1, "", or null`)
11+
throw new Error(`FIXME: value=${JSON.stringify(value)}`)
1212
}
1313
return value
1414
}

0 commit comments

Comments
 (0)