150 lines
3.2 KiB
Go
150 lines
3.2 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,
|
|
})
|
|
|
|
// Register commands
|
|
r.Register(Command{
|
|
Name: "register",
|
|
ShortForm: "reg",
|
|
Handler: cmdRegisters,
|
|
})
|
|
}
|