Skip to content

Conversation

@felixzsh
Copy link

Fix cursor getting stuck when moving down over virtual extmarks at the beginning of lines.When moving vertically over a virtual extmark at the start of a line, the cursor would be forced backwards instead of allowing forward movement. This fix detects when the adjusted position would cause backward movement during down navigation and positions the cursor at the end of the extmark with proper spacing.

the decision on the reposition at the end + 2, was made to be consistent with the current behavior when moving up over virtual extmarks (skiping the line)

Fix cursor getting stuck when moving down over virtual extmarks at the
beginning of lines.When moving vertically over a virtual extmark at the
start of a line, the cursor would be forced backwards instead of
allowing forward movement. This fix detects when the adjusted position
would cause backward movement during down navigation and positions the
cursor at the end of the extmark with proper spacing.
@felixzsh
Copy link
Author

felixzsh commented Jan 20, 2026

to see what i mean, in open code do something like this:

-----------

text example

[any extmark]

text example
-------------

when using the the arrow keys to move the cursor in such a way it preserves its column position so it raises the repositioning condition "extmark start + 1" when moving down over the extmark, that reposition means the previous line of the extmark, which means the down arrow keys doesnt work to move downwords.

@simonklee
Copy link
Contributor

These fail on main and pass in the branch.

diff --git i/packages/core/src/lib/extmarks.test.ts w/packages/core/src/lib/extmarks.test.ts
index c38f74c..c9cf232 100644
--- i/packages/core/src/lib/extmarks.test.ts
+++ w/packages/core/src/lib/extmarks.test.ts
@@ -1975,6 +1975,63 @@ Press ESC to return to main menu.`
       const isInsideExtmark = cursorAfterDown >= 6 && cursorAfterDown < 25
       expect(isInsideExtmark).toBe(false)
     })
+
+    it("should not get stuck when moving down into virtual extmark at start of line", async () => {
+      // Regression test for cursor getting stuck when moving down over
+      // virtual extmarks at the beginning of lines.
+      // Setup:
+      //   Line 0: "a"
+      //   Line 1: "" (empty)
+      //   Line 2: "[EXT]" (virtual extmark starting at column 0)
+      //   Line 3: "b"
+      await setup("a\n\n[EXT]\nb")
+
+      textarea.focus()
+      textarea.cursorOffset = 2
+
+      extmarks.create({
+        start: 3,
+        end: 8,
+        virtual: true,
+      })
+
+      const initialOffset = textarea.cursorOffset
+      expect(initialOffset).toBe(2)
+
+      currentMockInput.pressArrow("down")
+      const cursorAfterDown = textarea.cursorOffset
+
+      // Cursor should move forward (not backward to previous line)
+      expect(cursorAfterDown).toBeGreaterThan(initialOffset)
+
+      const isInsideExtmark = cursorAfterDown >= 3 && cursorAfterDown < 8
+      expect(isInsideExtmark).toBe(false)
+    })
+
+    it("should navigate past virtual extmark at line start with repeated down presses", async () => {
+      await setup("abc\n\n[EXTMARK]\n\nxyz")
+
+      textarea.focus()
+      textarea.cursorOffset = 1
+
+      extmarks.create({
+        start: 5,
+        end: 14,
+        virtual: true,
+      })
+
+      currentMockInput.pressArrow("down")
+      currentMockInput.pressArrow("down")
+      currentMockInput.pressArrow("down")
+
+      const finalOffset = textarea.cursorOffset
+
+      // Should have navigated past the extmark to "xyz" line or beyond
+      expect(finalOffset).toBeGreaterThanOrEqual(15)
+
+      const isInsideExtmark = finalOffset >= 5 && finalOffset < 14
+      expect(isInsideExtmark).toBe(false)
+    })
   })
 
   describe("TypeId Operations", () => {

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants