Gim/internal/command/registry.go
Hayden Hargreaves ee7bf9354b feat: rough command mode implementation
I am starting to develop so fast, testing is such a life saver, oh my
god.
2026-02-13 23:16:47 -07:00

143 lines
3.1 KiB
Go

package command
import (
"fmt"
"strings"
"git.gophernest.net/azpect/TextEditor/internal/action"
tea "github.com/charmbracelet/bubbletea"
)
// Command represents a command that can be executed from command mode
type Command struct {
Name string // Full name: "quit"
ShortForm string // Minimum abbreviation: "q"
Handler func(m action.Model, args []string) tea.Cmd // Handler function
}
// Registry holds all registered commands
type Registry struct {
commands []Command
}
// NewRegistry creates a new command registry with default commands
func NewRegistry() *Registry {
r := &Registry{}
r.registerDefaults()
return r
}
// Register adds a command to the registry
func (r *Registry) Register(cmd Command) {
r.commands = append(r.commands, cmd)
}
// Lookup finds a command by name or abbreviation
// Returns the command and any error (unknown or ambiguous)
func (r *Registry) Lookup(input string) (*Command, error) {
if input == "" {
return nil, fmt.Errorf("no command given")
}
var matches []*Command
for i := range r.commands {
cmd := &r.commands[i]
// Exact match on short form
if input == cmd.ShortForm {
return cmd, nil
}
// Exact match on full name
if input == cmd.Name {
return cmd, nil
}
// Prefix match: input must be at least as long as short form
// and must be a prefix of the full name
if len(input) >= len(cmd.ShortForm) && strings.HasPrefix(cmd.Name, input) {
matches = append(matches, cmd)
}
}
if len(matches) == 0 {
return nil, fmt.Errorf("unknown command: %s", input)
}
if len(matches) > 1 {
names := make([]string, len(matches))
for i, m := range matches {
names[i] = m.Name
}
return nil, fmt.Errorf("ambiguous command: %s (could be: %s)", input, strings.Join(names, ", "))
}
return matches[0], nil
}
// Parse splits a command line into command name and arguments
func Parse(cmdLine string) (name string, args []string) {
parts := strings.Fields(cmdLine)
if len(parts) == 0 {
return "", nil
}
return parts[0], parts[1:]
}
// Execute parses and executes a command line
func (r *Registry) Execute(m action.Model, cmdLine string) (tea.Cmd, error) {
name, args := Parse(cmdLine)
cmd, err := r.Lookup(name)
if err != nil {
return nil, err
}
return cmd.Handler(m, args), nil
}
// DefaultRegistry is the global command registry
var DefaultRegistry = NewRegistry()
// registerDefaults registers the built-in commands
func (r *Registry) registerDefaults() {
// Quit commands
r.Register(Command{
Name: "quit",
ShortForm: "q",
Handler: cmdQuit,
})
r.Register(Command{
Name: "qall",
ShortForm: "qa",
Handler: cmdQuitAll,
})
// Write commands
r.Register(Command{
Name: "write",
ShortForm: "w",
Handler: cmdWrite,
})
r.Register(Command{
Name: "wall",
ShortForm: "wa",
Handler: cmdWriteAll,
})
r.Register(Command{
Name: "wq",
ShortForm: "wq",
Handler: cmdWriteQuit,
})
// Set command
r.Register(Command{
Name: "set",
ShortForm: "se",
Handler: cmdSet,
})
}