Skip to content

Commit 0407e09

Browse files
committed
more fixes to speculative updates
1 parent edba83a commit 0407e09

File tree

2 files changed

+95
-67
lines changed

2 files changed

+95
-67
lines changed

packages/carta-md/src/lib/internal/components/Input.svelte

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,9 +135,13 @@
135135
136136
const onValueChange = (value: string) => {
137137
if (highlightElem) {
138-
highlighted = speculativeHighlightUpdate(currentlyHighlightedValue, value, highlighted);
139-
currentlyHighlightedValue = value;
140-
requestAnimationFrame(resize);
138+
try {
139+
highlighted = speculativeHighlightUpdate(currentlyHighlightedValue, value, highlighted);
140+
currentlyHighlightedValue = value;
141+
requestAnimationFrame(resize);
142+
} catch (e) {
143+
console.error(`Error executing speculative update: ${e}`);
144+
}
141145
}
142146
143147
debouncedHighlight(value);

packages/carta-md/src/lib/internal/speculative.ts

Lines changed: 88 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -6,70 +6,6 @@ type Position = {
66
char: number;
77
};
88

9-
function checkPosition(position: Position, lines: Element[]): Position {
10-
const { line, span, char } = position;
11-
const lineElement = lines[line];
12-
const spanElement = lineElement.children[span] ?? lineElement;
13-
const text = spanElement.textContent ?? '';
14-
15-
const nextPosition = { line, span, char };
16-
17-
if (char >= text.length) {
18-
nextPosition.char = 0;
19-
nextPosition.span = span + 1;
20-
}
21-
22-
if (nextPosition.span >= lineElement.children.length) {
23-
nextPosition.char = 0;
24-
nextPosition.span = 0;
25-
nextPosition.line = line + 1;
26-
}
27-
28-
return nextPosition;
29-
}
30-
31-
function nextPosition(position: Position, lines: Element[]): Position {
32-
const { line, span, char } = position;
33-
const lineElement = lines[line];
34-
const spanElement = lineElement.children[span] ?? lineElement;
35-
const text = spanElement.textContent ?? '';
36-
37-
const nextPosition = { line, span, char: char + 1 };
38-
39-
if (char + 1 >= text.length) {
40-
nextPosition.char = 0;
41-
nextPosition.span = span + 1;
42-
43-
if (nextPosition.span >= lineElement.children.length) {
44-
nextPosition.span = 0;
45-
nextPosition.line = line + 1;
46-
}
47-
}
48-
49-
return nextPosition;
50-
}
51-
52-
function isAtEndOfText(position: Position, lines: Element[]): boolean {
53-
return position.line >= lines.length - 1 && isAtEndOfLine(position, lines);
54-
}
55-
56-
function isAtEndOfLine(position: Position, lines: Element[]): boolean {
57-
const line = lines[position.line];
58-
return position.span >= line.children.length - 1 && isAtEndOfSpan(position, lines);
59-
}
60-
61-
function isAtEndOfSpan(position: Position, lines: Element[]): boolean {
62-
const line = lines[position.line];
63-
const span = line.children[position.span] ?? line;
64-
return position.char >= (span.textContent ?? '').length - 1;
65-
}
66-
67-
function getCurrentSpan(position: Position, lines: Element[]): HTMLElement {
68-
const line = lines[position.line];
69-
70-
return (line.children[position.span] as HTMLElement) ?? line;
71-
}
72-
739
/**
7410
* Temporary updates the highlight overlay to reflect the changes between two text strings,
7511
* waiting for the actual update to be applied. This way, the user can immediately see the changes,
@@ -253,3 +189,91 @@ export function speculativeHighlightUpdate(from: string, to: string, currentHTML
253189

254190
return tree.innerHTML;
255191
}
192+
193+
/**
194+
* Create a new span at the end of the line element.
195+
* @param line The line element to append the span to.
196+
* @returns A new span element.
197+
*/
198+
function createSpan(line: Element): HTMLElement {
199+
const span = document.createElement('span');
200+
line.appendChild(span);
201+
return span;
202+
}
203+
204+
/**
205+
* Check whether the current position is valid, which means that the
206+
* the character index is within the text of the span, the span index is within the line,
207+
* @param position The position to check
208+
* @param lines The lines to work on.
209+
* @returns The next valid position.
210+
*/
211+
function checkPosition(position: Position, lines: Element[]): Position {
212+
const { line, span, char } = position;
213+
const lineElement = lines[line];
214+
const spanElement = Array.from(lineElement.children).at(span);
215+
const text = spanElement?.textContent ?? '';
216+
217+
const nextPosition = { line, span, char };
218+
219+
if (char >= text.length) {
220+
nextPosition.char = 0;
221+
nextPosition.span = span + 1;
222+
}
223+
224+
if (nextPosition.span >= lineElement.children.length) {
225+
nextPosition.char = 0;
226+
nextPosition.span = 0;
227+
nextPosition.line = line + 1;
228+
}
229+
230+
return nextPosition;
231+
}
232+
233+
/**
234+
* Get the next position in the tree.
235+
* @param position Current position
236+
* @param lines The lines to work on.
237+
* @returns The next position.
238+
*/
239+
function nextPosition(position: Position, lines: Element[]): Position {
240+
const { line, span, char } = position;
241+
const lineElement = lines[line];
242+
const spanElement = lineElement.children[span] ?? createSpan(lineElement);
243+
const text = spanElement.textContent ?? '';
244+
245+
const nextPosition = { line, span, char: char + 1 };
246+
247+
if (char + 1 >= text.length) {
248+
nextPosition.char = 0;
249+
nextPosition.span = span + 1;
250+
251+
if (nextPosition.span >= lineElement.children.length) {
252+
nextPosition.span = 0;
253+
nextPosition.line = line + 1;
254+
}
255+
}
256+
257+
return nextPosition;
258+
}
259+
260+
function isAtEndOfText(position: Position, lines: Element[]): boolean {
261+
return position.line >= lines.length - 1 && isAtEndOfLine(position, lines);
262+
}
263+
264+
function isAtEndOfLine(position: Position, lines: Element[]): boolean {
265+
const line = lines[position.line];
266+
return position.span >= line.children.length - 1 && isAtEndOfSpan(position, lines);
267+
}
268+
269+
function isAtEndOfSpan(position: Position, lines: Element[]): boolean {
270+
const line = lines[position.line];
271+
const span = line.children[position.span] ?? line;
272+
return position.char >= (span.textContent ?? '').length - 1;
273+
}
274+
275+
function getCurrentSpan(position: Position, lines: Element[]): HTMLElement {
276+
const line = lines[position.line];
277+
278+
return (line.children[position.span] as HTMLElement) ?? createSpan(line);
279+
}

0 commit comments

Comments
 (0)