Skip to content

Commit 668655d

Browse files
authored
fix: preserve copy-mode visual anchor (#88)
fix(tui): preserve copy-mode visual anchor
1 parent 6bc51ac commit 668655d

2 files changed

Lines changed: 43 additions & 3 deletions

File tree

packages/opencode/src/cli/cmd/tui/routes/session/copy-mode.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -450,7 +450,7 @@ export function createCopyMode(input: {
450450
setState((prev) => ({
451451
...prev,
452452
visual: mode,
453-
anchor: { idx: prev.idx, col: prev.col },
453+
anchor: prev.anchor ?? { idx: prev.idx, col: prev.col },
454454
}))
455455
}
456456

packages/opencode/test/cli/tui/vim-motions.test.ts

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { describe, expect, test } from "bun:test"
2-
import type { TextareaRenderable } from "@opentui/core"
3-
import { createSignal } from "solid-js"
2+
import type { ScrollBoxRenderable, TextareaRenderable } from "@opentui/core"
3+
import type { Part } from "@opencode-ai/sdk/v2"
4+
import { createRoot, createSignal } from "solid-js"
5+
import { createCopyMode } from "../../../src/cli/cmd/tui/routes/session/copy-mode"
46
import { createVimHandler } from "../../../src/cli/cmd/tui/component/vim/vim-handler"
57
import { createVimState } from "../../../src/cli/cmd/tui/component/vim/vim-state"
68
import type { VimScroll } from "../../../src/cli/cmd/tui/component/vim/vim-scroll"
@@ -4634,6 +4636,44 @@ describe("copy mode", () => {
46344636
expect(ctx.copyVisualCalls).not.toContain("char")
46354637
})
46364638

4639+
test("V after characterwise visual preserves copy anchor", () => {
4640+
createRoot((dispose) => {
4641+
const children = [
4642+
{ id: "text-part", y: 0, height: 1 },
4643+
{ id: "text-part", y: 1, height: 1 },
4644+
{ id: "text-part", y: 2, height: 1 },
4645+
]
4646+
const scroll = {
4647+
y: 0,
4648+
height: 3,
4649+
width: 80,
4650+
getChildren: () => children,
4651+
scrollBy(delta: number) {
4652+
scroll.y += delta
4653+
},
4654+
} as unknown as ScrollBoxRenderable
4655+
const cm = createCopyMode({
4656+
scroll: () => scroll,
4657+
messages: () => [{ id: "msg", role: "assistant" }],
4658+
parts: () => [{ type: "text", id: "part" } as Part],
4659+
thinking: () => false,
4660+
details: () => false,
4661+
session: () => "session",
4662+
toBottom() {},
4663+
})
4664+
4665+
cm.prompt.enter()
4666+
cm.prompt.visual("char")
4667+
cm.prompt.move("up")
4668+
cm.prompt.visual("line")
4669+
4670+
expect(cm.state().visual).toBe("line")
4671+
expect(cm.state().anchor).toEqual({ idx: 2, col: 3 })
4672+
expect(cm.state().idx).toBe(1)
4673+
dispose()
4674+
})
4675+
})
4676+
46374677
test("y yanks copy selection and exits copy mode", () => {
46384678
const ctx = createHandler("abc", { mode: "copy", copy: { text: "picked text", isVisual: true } })
46394679

0 commit comments

Comments
 (0)