Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit 2117a0f

Browse files
committed
Fix caret jump when backspacing into empty line at beginning of editor
Fixes: element-hq/element-web#22335
1 parent d935da2 commit 2117a0f

File tree

2 files changed

+28
-4
lines changed

2 files changed

+28
-4
lines changed

src/editor/caret.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,10 @@ export function getLineAndNodePosition(
104104
} {
105105
const { parts } = model;
106106
const partIndex = caretPosition.index;
107-
const lineResult = findNodeInLineForPart(parts, partIndex);
107+
let { offset } = caretPosition;
108+
const lineResult = findNodeInLineForPart(parts, partIndex, offset);
108109
const { lineIndex } = lineResult;
109110
let { nodeIndex } = lineResult;
110-
let { offset } = caretPosition;
111111
// we're at an empty line between a newline part
112112
// and another newline part or end/start of parts.
113113
// set offset to 0 so it gets set to the <br> inside the line container
@@ -120,7 +120,11 @@ export function getLineAndNodePosition(
120120
return { lineIndex, nodeIndex, offset };
121121
}
122122

123-
function findNodeInLineForPart(parts: Part[], partIndex: number): { lineIndex: number; nodeIndex: number } {
123+
function findNodeInLineForPart(
124+
parts: Part[],
125+
partIndex: number,
126+
offset: number,
127+
): { lineIndex: number; nodeIndex: number } {
124128
let lineIndex = 0;
125129
let nodeIndex = -1;
126130

@@ -130,6 +134,10 @@ function findNodeInLineForPart(parts: Part[], partIndex: number): { lineIndex: n
130134
for (let i = 0; i <= partIndex; ++i) {
131135
const part = parts[i];
132136
if (part.type === Type.Newline) {
137+
// don't jump over the linebreak if the offset is before it
138+
if (i == partIndex && offset === 0) {
139+
continue;
140+
}
133141
lineIndex += 1;
134142
nodeIndex = -1;
135143
prevPart = undefined;
@@ -140,7 +148,7 @@ function findNodeInLineForPart(parts: Part[], partIndex: number): { lineIndex: n
140148
}
141149
// only jump over caret node if we're not at our destination node already,
142150
// as we'll assume in moveOutOfUnselectablePart that nodeIndex
143-
// refers to the node corresponding to the part,
151+
// refers to the node corresponding to the part,
144152
// and not an adjacent caret node
145153
if (i < partIndex) {
146154
const nextPart = parts[i + 1];

test/editor/caret-test.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,14 @@ describe("editor/caret: DOM position for caret", function () {
4646
});
4747
});
4848
describe("handling line breaks", function () {
49+
it("at start of first line which is empty", function () {
50+
const pc = createPartCreator();
51+
const model = new EditorModel([pc.newline(), pc.plain("hello world")], pc);
52+
const { offset, lineIndex, nodeIndex } = getLineAndNodePosition(model, { index: 0, offset: 0 });
53+
expect(lineIndex).toBe(0);
54+
expect(nodeIndex).toBe(-1);
55+
expect(offset).toBe(0);
56+
});
4957
it("at end of last line", function () {
5058
const pc = createPartCreator();
5159
const model = new EditorModel([pc.plain("hello"), pc.newline(), pc.plain("world")], pc);
@@ -62,6 +70,14 @@ describe("editor/caret: DOM position for caret", function () {
6270
expect(nodeIndex).toBe(0);
6371
expect(offset).toBe(0);
6472
});
73+
it("before empty line", function () {
74+
const pc = createPartCreator();
75+
const model = new EditorModel([pc.plain("hello"), pc.newline(), pc.newline(), pc.plain("world")], pc);
76+
const { offset, lineIndex, nodeIndex } = getLineAndNodePosition(model, { index: 0, offset: 5 });
77+
expect(lineIndex).toBe(0);
78+
expect(nodeIndex).toBe(0);
79+
expect(offset).toBe(5);
80+
});
6581
it("in empty line", function () {
6682
const pc = createPartCreator();
6783
const model = new EditorModel([pc.plain("hello"), pc.newline(), pc.newline(), pc.plain("world")], pc);

0 commit comments

Comments
 (0)