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

Fix caret jump when backspacing into empty line at beginning of editor #11128

Merged
merged 2 commits into from
Jun 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions src/editor/caret.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,10 +104,10 @@ export function getLineAndNodePosition(
} {
const { parts } = model;
const partIndex = caretPosition.index;
const lineResult = findNodeInLineForPart(parts, partIndex);
let { offset } = caretPosition;
const lineResult = findNodeInLineForPart(parts, partIndex, offset);
const { lineIndex } = lineResult;
let { nodeIndex } = lineResult;
let { offset } = caretPosition;
// we're at an empty line between a newline part
// and another newline part or end/start of parts.
// set offset to 0 so it gets set to the <br> inside the line container
Expand All @@ -120,7 +120,11 @@ export function getLineAndNodePosition(
return { lineIndex, nodeIndex, offset };
}

function findNodeInLineForPart(parts: Part[], partIndex: number): { lineIndex: number; nodeIndex: number } {
function findNodeInLineForPart(
parts: Part[],
partIndex: number,
offset: number,
): { lineIndex: number; nodeIndex: number } {
let lineIndex = 0;
let nodeIndex = -1;

Expand All @@ -130,6 +134,10 @@ function findNodeInLineForPart(parts: Part[], partIndex: number): { lineIndex: n
for (let i = 0; i <= partIndex; ++i) {
const part = parts[i];
if (part.type === Type.Newline) {
// don't jump over the linebreak if the offset is before it
if (i == partIndex && offset === 0) {
continue;
}
lineIndex += 1;
nodeIndex = -1;
prevPart = undefined;
Expand All @@ -140,7 +148,7 @@ function findNodeInLineForPart(parts: Part[], partIndex: number): { lineIndex: n
}
// only jump over caret node if we're not at our destination node already,
// as we'll assume in moveOutOfUnselectablePart that nodeIndex
// refers to the node corresponding to the part,
// refers to the node corresponding to the part,
// and not an adjacent caret node
if (i < partIndex) {
const nextPart = parts[i + 1];
Expand Down
16 changes: 16 additions & 0 deletions test/editor/caret-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,14 @@ describe("editor/caret: DOM position for caret", function () {
});
});
describe("handling line breaks", function () {
it("at start of first line which is empty", function () {
const pc = createPartCreator();
const model = new EditorModel([pc.newline(), pc.plain("hello world")], pc);
const { offset, lineIndex, nodeIndex } = getLineAndNodePosition(model, { index: 0, offset: 0 });
expect(lineIndex).toBe(0);
expect(nodeIndex).toBe(-1);
expect(offset).toBe(0);
});
it("at end of last line", function () {
const pc = createPartCreator();
const model = new EditorModel([pc.plain("hello"), pc.newline(), pc.plain("world")], pc);
Expand All @@ -62,6 +70,14 @@ describe("editor/caret: DOM position for caret", function () {
expect(nodeIndex).toBe(0);
expect(offset).toBe(0);
});
it("before empty line", function () {
const pc = createPartCreator();
const model = new EditorModel([pc.plain("hello"), pc.newline(), pc.newline(), pc.plain("world")], pc);
const { offset, lineIndex, nodeIndex } = getLineAndNodePosition(model, { index: 0, offset: 5 });
expect(lineIndex).toBe(0);
expect(nodeIndex).toBe(0);
expect(offset).toBe(5);
});
it("in empty line", function () {
const pc = createPartCreator();
const model = new EditorModel([pc.plain("hello"), pc.newline(), pc.newline(), pc.plain("world")], pc);
Expand Down