Skip to content
Closed
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ Toggle via command palette (`Ctrl+p` -> `Toggle vim mode`).

**Editing**

`i` `I` `a` `A` `o` `O` `R` `r` `x` `~` `dd` `dw` `db` `d%` `d}` `d{` `cc` `cw` `cb` `C` `c%` `c}` `c{` `S` `J`
`i` `I` `a` `A` `o` `O` `R` `r` `x` `~` `dd` `dw` `db` `dt` `dT` `df` `dF` `d%` `d}` `d{` `cc` `cw` `cb` `ct` `cT` `cf` `cF` `C` `c%` `c}` `c{` `S` `J`

**yank / put / undo / repeat**

Expand Down
115 changes: 113 additions & 2 deletions packages/opencode/src/cli/cmd/tui/component/vim/vim-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,68 @@ export function createVimHandler(input: {
return true
}

const deleteFind = input.state.pending()
if (deleteFind === "df" || deleteFind === "dF" || deleteFind === "dt" || deleteFind === "dT") {
if (isPrintable(event) && !hasModifier(event)) {
const forward = deleteFind === "df" || deleteFind === "dt"
const till = deleteFind === "dt" || deleteFind === "dT"
const textarea = input.textarea()
const start = textarea.cursorOffset
const char = value(event)
findChar(textarea, char, forward, till)
if (textarea.cursorOffset !== start) {
const span = forward
? { start, end: textarea.cursorOffset + 1 }
: { start: textarea.cursorOffset, end: start + 1 }
const text = textarea.plainText.slice(span.start, span.end)
edit(() => {
deleteSpan(textarea, span)
if (text) setRegister({ text, linewise: false })
textarea.cursorOffset = Math.min(start, textarea.cursorOffset)
})
}
input.state.setLastFind({ char, forward, till })
input.state.clearPending()
event.preventDefault()
return true
}
input.state.clearPending()
event.preventDefault()
return true
}

const changeFind = input.state.pending()
if (changeFind === "cf" || changeFind === "cF" || changeFind === "ct" || changeFind === "cT") {
if (isPrintable(event) && !hasModifier(event)) {
const forward = changeFind === "cf" || changeFind === "ct"
const till = changeFind === "ct" || changeFind === "cT"
const textarea = input.textarea()
const start = textarea.cursorOffset
const char = value(event)
findChar(textarea, char, forward, till)
if (textarea.cursorOffset !== start) {
const span = forward
? { start, end: textarea.cursorOffset + 1 }
: { start: textarea.cursorOffset, end: start + 1 }
const text = textarea.plainText.slice(span.start, span.end)
edit(() => {
deleteSpan(textarea, span)
if (text) setRegister({ text, linewise: false })
textarea.cursorOffset = Math.min(start, textarea.cursorOffset)
})
}
input.state.setLastFind({ char, forward, till })
input.state.setMode("insert")
input.state.clearPending()
event.preventDefault()
return true
}

input.state.clearPending()
event.preventDefault()
return true
}

const scroll = vimScroll(event)
if (scroll) {
input.state.clearPending()
Expand Down Expand Up @@ -612,6 +674,30 @@ export function createVimHandler(input: {
return true
}

if (key === "t" && !event.shift && !hasModifier(event)) {
input.state.setPending("ct")
event.preventDefault()
return true
}

if (isShifted(event, "t") && !hasModifier(event)) {
input.state.setPending("cT")
event.preventDefault()
return true
}

if (key === "f" && !event.shift && !hasModifier(event)) {
input.state.setPending("cf")
event.preventDefault()
return true
}

if (isShifted(event, "f") && !hasModifier(event)) {
input.state.setPending("cF")
event.preventDefault()
return true
}

input.state.clearPending()
}

Expand Down Expand Up @@ -682,6 +768,30 @@ export function createVimHandler(input: {
return true
}

if (key === "t" && !event.shift && !hasModifier(event)) {
input.state.setPending("dt")
event.preventDefault()
return true
}

if (isShifted(event, "t") && !hasModifier(event)) {
input.state.setPending("dT")
event.preventDefault()
return true
}

if (key === "f" && !event.shift && !hasModifier(event)) {
input.state.setPending("df")
event.preventDefault()
return true
}

if (isShifted(event, "f") && !hasModifier(event)) {
input.state.setPending("dF")
event.preventDefault()
return true
}

input.state.clearPending()
}

Expand Down Expand Up @@ -750,8 +860,9 @@ export function createVimHandler(input: {
if (isPrintable(event) && !hasModifier(event)) {
const forward = find === "f" || find === "t"
const till = find === "t" || find === "T"
findChar(input.textarea(), key, forward, till)
input.state.setLastFind({ char: key, forward, till })
const char = value(event)
findChar(input.textarea(), char, forward, till)
input.state.setLastFind({ char, forward, till })
input.state.clearPending()
event.preventDefault()
return true
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { createEffect, createMemo, createSignal, type Accessor } from "solid-js"

export type VimMode = "normal" | "insert" | "replace" | "visual" | "visual-line" | "copy"
export type VimPending = "" | "c" | "d" | "g" | "z" | "f" | "F" | "t" | "T" | "y" | "w" | "r" | "vr"
export type VimPending = "" | "c" | "d" | "g" | "z" | "f" | "F" | "t" | "T" | "y" | "w" | "r" | "vr" | "df" | "dF" | "dt" | "dT" | "cf" | "cF" | "ct" | "cT"
export type VimFind = { char: string; forward: boolean; till: boolean } | null
export type VimRegister = { text: string; linewise: boolean } | null
export type VimSnapshot = { text: string; cursor: number; data?: unknown }
Expand Down
Loading
Loading