Compare commits

..

No commits in common. "a5eb8983ca2abe5b0ed9f60a1dce9cb8d6c0f243" and "f12ce37bebe7f7a1d29aaff36714e8a83e5f700f" have entirely different histories.

5 changed files with 11 additions and 1977 deletions

View File

@ -1,83 +0,0 @@
package action
import (
"git.gophernest.net/azpect/TextEditor/internal/core"
tea "github.com/charmbracelet/bubbletea"
)
type FindChar struct {
Char string
Forward bool
Inclusive bool
Count int
}
func (m FindChar) WithChar(char string) Motion {
m.Char = char
return m
}
func (m FindChar) Type() core.MotionType {
if m.Inclusive {
return core.CharwiseInclusive
}
return core.CharwiseExclusive
}
// WithCount sets the count (required by Repeatable interface)
func (f FindChar) WithCount(n int) Action {
f.Count = n
return f
}
func (a FindChar) Execute(m Model) tea.Cmd {
// Get the line
// Get the current position, moved based on inputs
win := m.ActiveWindow()
buf := win.Buffer
line := buf.Line(win.Cursor.Line)
col := win.Cursor.Col
if len(line) <= 0 {
return nil
}
if a.Forward {
for x := col; x < len(line); x++ {
if string(line[x]) == a.Char {
if a.Count == 1 {
if a.Inclusive {
win.SetCursorCol(x)
} else {
win.SetCursorCol(x - 1)
}
break
} else {
a.Count--
}
}
}
}
if !a.Forward {
for x := col; x >= 0; x-- {
if string(line[x]) == a.Char {
if a.Count == 1 {
if a.Inclusive {
win.SetCursorCol(x)
} else {
win.SetCursorCol(x + 1)
}
break
} else {
a.Count--
}
}
}
}
return nil
}

File diff suppressed because it is too large Load Diff

View File

@ -83,10 +83,3 @@ type DoublePresser interface {
type Repeatable interface {
WithCount(n int) Action
}
// CharMotion is a motion that requires a character argument (f/t/F/T)
// The state machine will call WithChar to set the character before executing
type CharMotion interface {
Motion
WithChar(char string) Motion
}

View File

@ -14,20 +14,18 @@ const (
StateCount
StateOperatorPending
StateMotionCount
StateWaitingForChar // Waiting for character argument (f/t/F/T)
)
// Handler: Manages input processing with a state machine for vim-style commands.
// Handles counts, operators, motions, and multi-key sequences.
type Handler struct {
state InputState
count1 int
count2 int
operator action.Operator
operatorKey string // track which key started operator (for dd, yy, cc)
buffer string // for display (what user has typed)
pending string // partial key sequence (e.g., "g" waiting for second key)
charMotionType string // which char motion is waiting: "f", "t", "F", or "T"
state InputState
count1 int
count2 int
operator action.Operator
operatorKey string // track which key started operator (for dd, yy, cc)
buffer string // for display (what user has typed)
pending string // partial key sequence (e.g., "g" waiting for second key)
// Keymaps
normalKeymap *Keymap
@ -72,11 +70,6 @@ func (h *Handler) Handle(m action.Model, key string) tea.Cmd {
return h.handleCommandKey(m, key)
}
// If waiting for character argument (f/t/F/T), capture it
if h.state == StateWaitingForChar {
return h.handleCharMotion(m, key)
}
// Try to accumulate count (only if no pending sequence)
if h.pending == "" && h.tryAccumulateCount(key) {
return nil
@ -127,13 +120,6 @@ func (h *Handler) Handle(m action.Model, key string) tea.Cmd {
// Handler.dispatch: Routes to the appropriate handler based on current state.
func (h *Handler) dispatch(m action.Model, kind string, binding any, key string) tea.Cmd {
// Handle character motions (f/t/F/T) - transition to waiting state
if kind == "char_motion" {
h.charMotionType = key
h.state = StateWaitingForChar
return nil
}
switch h.state {
case StateReady, StateCount:
return h.handleInitial(m, kind, binding, key)
@ -234,83 +220,6 @@ func (h *Handler) handleAfterOperator(m action.Model, kind string, binding any,
return nil
}
// Handler.handleCharMotion: Handles input when waiting for a character argument
// for f/t/F/T motions. Captures the character and creates the appropriate motion.
//
// USAGE FOR IMPLEMENTING f/t/F/T MOTIONS:
//
// You need to create a CharMotion interface in internal/action/interface.go:
//
// type CharMotion interface {
// Motion
// WithChar(char string) Motion
// }
//
// Then implement it for your FindChar motion (example):
//
// type FindChar struct {
// Char string
// Forward bool // true = f/t, false = F/T
// To bool // true = f/F (to char), false = t/T (till before/after char)
// Count int
// }
//
// func (f FindChar) WithChar(char string) action.Motion {
// f.Char = char
// return f
// }
//
// The state machine will:
// 1. Call WithChar(key) to set the character
// 2. Apply count if the motion is Repeatable
// 3. If operator pending (df{char}), execute motion and operate on range
// 4. Otherwise just execute the motion
func (h *Handler) handleCharMotion(m action.Model, key string) tea.Cmd {
count := h.effectiveCount()
// Get the char motion from the keymap
// The keymap should have registered f/t/F/T as "char_motion" type
// and stored the motion template (without character set yet)
motion := h.currentKeymap.LookupCharMotion(h.charMotionType)
if motion == nil {
// Motion not found - shouldn't happen if keymap configured correctly
h.Reset()
return nil
}
// Type assert to CharMotion interface and set the character
charMot, ok := motion.(action.CharMotion)
if !ok {
// Motion doesn't implement CharMotion interface
h.Reset()
return nil
}
// Set the character that was pressed
mot := charMot.WithChar(key)
// Apply count if supported
if r, ok := mot.(action.Repeatable); ok {
mot = r.WithCount(count).(action.Motion)
}
// If operator pending (e.g., "df{char}"), get range and operate
if h.operator != nil {
win := m.ActiveWindow()
start := win.Cursor
mot.Execute(m)
end := win.Cursor
cmd := h.operator.Operate(m, start, end, mot.Type())
h.Reset()
return cmd
}
// Otherwise just execute the motion
cmd := mot.Execute(m)
h.Reset()
return cmd
}
// Handler.tryAccumulateCount: Attempts to add a digit to the count. Returns
// true if successful, false if the key is not a digit or is an invalid count.
func (h *Handler) tryAccumulateCount(key string) bool {
@ -368,7 +277,6 @@ func (h *Handler) Reset() {
h.operatorKey = ""
h.buffer = ""
h.pending = ""
h.charMotionType = ""
}
// Handler.Pending: Returns the accumulated input buffer for display.

View File

@ -9,10 +9,9 @@ import (
// Keymap: Maps key sequences to motions, operators, and actions.
type Keymap struct {
motions map[string]action.Motion
operators map[string]action.Operator
actions map[string]action.Action // standalone actions: i.e., 'i', 'a'
charMotions map[string]action.Motion // motions that need character argument: f/t/F/T
motions map[string]action.Motion
operators map[string]action.Operator
actions map[string]action.Action // standalone actions: i.e., 'i', 'a'
}
// NewNormalKeymap: Creates a keymap for normal mode with all standard vim bindings.
@ -64,12 +63,6 @@ func NewNormalKeymap() *Keymap {
"p": action.Paste{Count: 1},
"P": action.PasteBefore{Count: 1},
},
charMotions: map[string]action.Motion{
"f": action.FindChar{Forward: true, Inclusive: true},
"F": action.FindChar{Forward: false, Inclusive: true},
"t": action.FindChar{Forward: true, Inclusive: false},
"T": action.FindChar{Forward: false, Inclusive: false},
},
}
}
@ -104,12 +97,6 @@ func NewVisualKeymap() *Keymap {
"p": action.VisualPaste{Count: 1},
// ":": action.EnterComandMode{}, // Different OP
},
charMotions: map[string]action.Motion{
"f": action.FindChar{Forward: true, Inclusive: true},
"F": action.FindChar{Forward: false, Inclusive: true},
"t": action.FindChar{Forward: true, Inclusive: false},
"T": action.FindChar{Forward: false, Inclusive: false},
},
}
}
@ -153,7 +140,7 @@ func NewCommandKeymap() *Keymap {
}
// Keymap.Lookup: Returns the type and value of a key binding (motion, operator, action, or char_motion).
// Keymap.Lookup: Returns the type and value of a key binding (motion, operator, or action).
func (km *Keymap) Lookup(key string) (kind string, value any) {
if m, ok := km.motions[key]; ok {
return "motion", m
@ -164,9 +151,6 @@ func (km *Keymap) Lookup(key string) (kind string, value any) {
if a, ok := km.actions[key]; ok {
return "action", a
}
if cm, ok := km.charMotions[key]; ok {
return "char_motion", cm
}
return "", nil
}
@ -187,19 +171,5 @@ func (km *Keymap) HasPrefix(prefix string) bool {
return true
}
}
for key := range km.charMotions {
if len(key) > len(prefix) && key[:len(prefix)] == prefix {
return true
}
}
return false
}
// Keymap.LookupCharMotion: Returns the motion template for character motions (f/t/F/T).
// The returned motion should implement the CharMotion interface.
func (km *Keymap) LookupCharMotion(key string) action.Motion {
if cm, ok := km.charMotions[key]; ok {
return cm
}
return nil
}