summaryrefslogtreecommitdiff
path: root/internal/client
diff options
context:
space:
mode:
authorKyren223 <Kyren223@proton.me>2024-12-12 19:38:13 +0200
committerKyren223 <Kyren223@proton.me>2024-12-12 19:38:13 +0200
commit310afed6846021f9b46ee783e7f2a25d41b630a4 (patch)
treee2b526b1587e1b2fa87d59408ba1245482fc22ca /internal/client
parent25844d347f1a0b2396b03f96beac9974b4e05526 (diff)
Implemented f/t/F/T motions in viminput
Diffstat (limited to 'internal/client')
-rw-r--r--internal/client/ui/viminput/helpers.go13
-rw-r--r--internal/client/ui/viminput/viminput.go98
2 files changed, 104 insertions, 7 deletions
diff --git a/internal/client/ui/viminput/helpers.go b/internal/client/ui/viminput/helpers.go
index c3dcdb1..bd12957 100644
--- a/internal/client/ui/viminput/helpers.go
+++ b/internal/client/ui/viminput/helpers.go
@@ -18,3 +18,16 @@ func IsGrouped(r1, r2 rune) bool {
}
return !IsKeyword(r2) && !unicode.IsSpace(r2)
}
+
+func SearchChar(line []rune, i, dir int, c rune) (index int, ok bool) {
+ found := false
+ for i > 0 && i < len(line) {
+ if line[i] == c {
+ found = true
+ break
+ }
+ i += dir
+ }
+
+ return i, found
+}
diff --git a/internal/client/ui/viminput/viminput.go b/internal/client/ui/viminput/viminput.go
index 7e47e7c..10d6d53 100644
--- a/internal/client/ui/viminput/viminput.go
+++ b/internal/client/ui/viminput/viminput.go
@@ -17,7 +17,11 @@ type (
LineDecoration = func(lnum int, m Model) string
)
-const Unchanged = -1
+const (
+ Unchanged = -1
+ InvalidGoal = -1
+ NullChar = 0
+)
const (
NormalMode = iota
@@ -42,6 +46,9 @@ type Model struct {
mode int
pending byte
gmod bool
+ fchar byte
+ fmod byte
+ tlast bool
focus bool
width int
@@ -58,10 +65,13 @@ func New(width, height int) Model {
lines: [][]rune{[]rune("")},
cursorLine: 0,
cursorColumn: 0,
- goalColumn: -1,
+ goalColumn: InvalidGoal,
mode: NormalMode,
- pending: 0,
+ pending: NullChar,
gmod: false,
+ fchar: NullChar,
+ fmod: NullChar,
+ tlast: false,
focus: false,
width: width,
height: height,
@@ -158,7 +168,7 @@ func (m *Model) SetCursorColumn(col int) {
} else {
m.cursorColumn = max(col, 0)
}
- m.goalColumn = -1
+ m.goalColumn = InvalidGoal
}
func (m *Model) SetCursorLine(line int) {
@@ -169,10 +179,10 @@ func (m *Model) SetCursorLine(line int) {
if m.mode == NormalMode {
toLength-- // In normal it's not (unless length == 0)
}
- if fromLength > toLength && m.goalColumn == -1 {
+ if fromLength > toLength && m.goalColumn == InvalidGoal {
m.goalColumn = fromLength
}
- if m.goalColumn != -1 {
+ if m.goalColumn != InvalidGoal {
m.cursorColumn = max(min(toLength, m.goalColumn), 0)
}
}
@@ -396,6 +406,40 @@ func (m *Model) Motion(motion string) (line, col int) {
m.gmod = false
}
+ isF := motion == "f" || motion == "t" || motion == "F" || motion == "T"
+ if isF && m.fmod == NullChar {
+ m.fmod = motion[0]
+ return Unchanged, Unchanged
+ }
+ if m.fmod != NullChar {
+ defer func(m *Model) { m.fmod = NullChar }(m)
+
+ if len(motion) != 1 {
+ return Unchanged, Unchanged
+ }
+ m.fchar = motion[0]
+
+ dir := 1
+ if m.fmod == 'F' || m.fmod == 'T' {
+ dir = -1
+ }
+ line := m.lines[m.cursorLine]
+ i := m.cursorColumn + dir
+
+ index, ok := SearchChar(line, i, dir, rune(m.fchar))
+ if !ok {
+ return Unchanged, Unchanged
+ }
+
+ if m.fmod == 't' || m.fmod == 'T' {
+ index -= dir
+ m.tlast = true
+ } else {
+ m.tlast = false
+ }
+ return Unchanged, index
+ }
+
switch motion {
case "h":
return Unchanged, m.cursorColumn - 1
@@ -445,6 +489,37 @@ func (m *Model) Motion(motion string) (line, col int) {
case "G":
return len(m.lines) - 1, Unchanged
+ case ",":
+ dir := -1
+ line := m.lines[m.cursorLine]
+ i := m.cursorColumn + dir
+ if m.tlast {
+ i += dir
+ }
+ index, ok := SearchChar(line, i, dir, rune(m.fchar))
+ if !ok {
+ return Unchanged, Unchanged
+ }
+ if m.tlast {
+ index -= dir
+ }
+ return Unchanged, index
+ case ";":
+ dir := 1
+ line := m.lines[m.cursorLine]
+ i := m.cursorColumn + dir
+ if m.tlast {
+ i += dir
+ }
+ index, ok := SearchChar(line, i, dir, rune(m.fchar))
+ if !ok {
+ return Unchanged, Unchanged
+ }
+ if m.tlast {
+ index -= dir
+ }
+ return Unchanged, index
+
// case "E":
// for {
// line := m.lines[m.cursorLine]
@@ -480,8 +555,9 @@ func (m *Model) handleOpendingModeKeys(key tea.KeyMsg) {
return
}
+ ftmod := m.fmod == 'f' || m.fmod == 't'
lnum, col := m.Motion(key.String())
- if m.gmod {
+ if m.gmod || m.fmod != NullChar {
return
}
if lnum != Unchanged || col != Unchanged {
@@ -491,6 +567,10 @@ func (m *Model) handleOpendingModeKeys(key tea.KeyMsg) {
if col == Unchanged {
col = m.cursorColumn
}
+ if ftmod {
+ col++ // Adjust due to upper bound being exclusive
+ // For F/T (backwards), being exclusive is the correct
+ }
if lnum == m.cursorLine {
line := m.lines[m.cursorLine]
@@ -507,6 +587,10 @@ func (m *Model) handleOpendingModeKeys(key tea.KeyMsg) {
}
m.SetCursorColumn(min(lower, len(line)-1))
+ if ftmod && m.pending == 'c' {
+ m.SetCursorColumn(lower) // Ignore bound
+ // Only ok because of insert mode (same as NVIM)
+ }
} else {
lower := min(lnum, m.cursorLine)