344 lines
7.9 KiB
Go
344 lines
7.9 KiB
Go
package command
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"git.gophernest.net/azpect/TextEditor/internal/action"
|
|
"git.gophernest.net/azpect/TextEditor/internal/core"
|
|
tea "github.com/charmbracelet/bubbletea"
|
|
)
|
|
|
|
// QuitMsg: Message signaling the application should quit.
|
|
type QuitMsg struct{}
|
|
|
|
// ErrorMsg: Message signaling an error to display.
|
|
type ErrorMsg struct {
|
|
Err error
|
|
}
|
|
|
|
// cmdQuit: Handles :quit / :q command.
|
|
func cmdQuit(m action.Model, args []string) tea.Cmd {
|
|
return func() tea.Msg {
|
|
return tea.Quit()
|
|
}
|
|
}
|
|
|
|
// cmdQuitAll: Handles :qall / :qa command.
|
|
func cmdQuitAll(m action.Model, args []string) tea.Cmd {
|
|
return func() tea.Msg {
|
|
return tea.Quit()
|
|
}
|
|
}
|
|
|
|
// cmdWrite: Handles :write / :w command (TODO: implement file saving).
|
|
func cmdWrite(m action.Model, args []string) tea.Cmd {
|
|
// TODO: Implement file saving
|
|
// If args provided, save to that filename
|
|
// Otherwise save to current file
|
|
return nil
|
|
}
|
|
|
|
// cmdWriteAll: Handles :wall / :wa command (TODO: implement saving all buffers).
|
|
func cmdWriteAll(m action.Model, args []string) tea.Cmd {
|
|
// TODO: Implement saving all buffers
|
|
return nil
|
|
}
|
|
|
|
// cmdWriteQuit: Handles :wq command (TODO: save then quit).
|
|
func cmdWriteQuit(m action.Model, args []string) tea.Cmd {
|
|
// TODO: Save then quit
|
|
return func() tea.Msg {
|
|
return tea.Quit()
|
|
}
|
|
}
|
|
|
|
// cmdRegisters: Handles :register command (debug - displays register content).
|
|
func cmdRegisters(m action.Model, args []string) tea.Cmd {
|
|
// TODO: This is temporary, for debugging
|
|
if len(args) < 1 {
|
|
m.SetCommandError(fmt.Errorf("Please provide a name. Register dump not yet implemented."))
|
|
return nil
|
|
}
|
|
|
|
if len(args[0]) != 1 {
|
|
m.SetCommandError(fmt.Errorf("Name should be a single character."))
|
|
return nil
|
|
}
|
|
|
|
name := rune(args[0][0])
|
|
reg, found := m.GetRegister(name)
|
|
if !found {
|
|
m.SetCommandError(fmt.Errorf("Could not find register '%c'.", name))
|
|
return nil
|
|
}
|
|
|
|
content := strings.Join(reg.Content, "\\n")
|
|
t := reg.Type
|
|
m.SetCommandOutput(fmt.Sprintf("Type: %d Name: \"%c Content: %s", t, name, content))
|
|
return nil
|
|
}
|
|
|
|
// cmdSet: Handles :set option[=value] command for configuring editor settings.
|
|
// Examples:
|
|
//
|
|
// :set number - enable number
|
|
// :set nonumber - disable number
|
|
// :set number! - toggle number
|
|
// :set tabstop=4 - set tabstop to 4
|
|
// :set ts=4 - set tabstop to 4 (abbreviation)
|
|
func cmdSet(m action.Model, args []string) tea.Cmd {
|
|
if len(args) == 0 {
|
|
out := fmt.Sprintf("%+v", m.Settings())
|
|
m.SetCommandOutput(out)
|
|
return nil
|
|
}
|
|
|
|
for _, arg := range args {
|
|
if err := parseSetOption(m, arg); err != nil {
|
|
m.SetCommandError(err)
|
|
return nil
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Setting: Represents a configurable editor option.
|
|
type Setting struct {
|
|
Name string
|
|
ShortForm string
|
|
Type SettingType
|
|
Get func(s core.EditorSettings) any
|
|
Set func(m action.Model, val any)
|
|
}
|
|
|
|
type WindowSetting struct {
|
|
Name string
|
|
ShortForm string
|
|
Type SettingType
|
|
Get func(m action.Model) any
|
|
Set func(m action.Model, val any)
|
|
}
|
|
|
|
// SettingType: Enumeration of setting value types.
|
|
type SettingType int
|
|
|
|
const (
|
|
BoolSetting SettingType = iota
|
|
IntSetting
|
|
StringSetting
|
|
)
|
|
|
|
// settingsMap defines all available editor settings
|
|
var settingsMap = []Setting{
|
|
{
|
|
Name: "tabstop",
|
|
ShortForm: "ts",
|
|
Type: IntSetting,
|
|
Get: func(s core.EditorSettings) any { return s.TabStop },
|
|
Set: func(m action.Model, val any) {
|
|
s := m.Settings()
|
|
s.TabStop = val.(int)
|
|
m.SetSettings(s)
|
|
},
|
|
},
|
|
}
|
|
|
|
// windowSettingsMap defines all available window settings
|
|
var windowSettingsMap = []WindowSetting{
|
|
{
|
|
Name: "number",
|
|
ShortForm: "nu",
|
|
Type: BoolSetting,
|
|
Get: func(m action.Model) any { return m.ActiveWindow().Options.Number },
|
|
Set: func(m action.Model, val any) {
|
|
w := m.ActiveWindow()
|
|
o := w.Options
|
|
o.Number = val.(bool)
|
|
w.SetOptions(o)
|
|
},
|
|
},
|
|
{
|
|
Name: "relativenumber",
|
|
ShortForm: "rnu",
|
|
Type: BoolSetting,
|
|
Get: func(m action.Model) any { return m.ActiveWindow().Options.RelativeNumber },
|
|
Set: func(m action.Model, val any) {
|
|
w := m.ActiveWindow()
|
|
o := w.Options
|
|
o.RelativeNumber = val.(bool)
|
|
w.SetOptions(o)
|
|
},
|
|
},
|
|
{
|
|
Name: "scrolloff",
|
|
ShortForm: "so",
|
|
Type: IntSetting,
|
|
Get: func(m action.Model) any { return m.ActiveWindow().Options.ScrollOff },
|
|
Set: func(m action.Model, val any) {
|
|
w := m.ActiveWindow()
|
|
o := w.Options
|
|
o.ScrollOff = val.(int)
|
|
w.SetOptions(o)
|
|
},
|
|
},
|
|
{
|
|
Name: "guttersize",
|
|
ShortForm: "gu",
|
|
Type: IntSetting,
|
|
Get: func(m action.Model) any { return m.ActiveWindow().Options.GutterSize },
|
|
Set: func(m action.Model, val any) {
|
|
w := m.ActiveWindow()
|
|
o := w.Options
|
|
o.GutterSize = val.(int)
|
|
w.SetOptions(o)
|
|
},
|
|
},
|
|
}
|
|
|
|
// lookupSetting: Finds a setting by name, short form, or prefix.
|
|
func lookupSetting(name string) *Setting {
|
|
for i := range settingsMap {
|
|
s := &settingsMap[i]
|
|
if name == s.Name || name == s.ShortForm {
|
|
return s
|
|
}
|
|
// Prefix matching
|
|
if len(name) >= len(s.ShortForm) && strings.HasPrefix(s.Name, name) {
|
|
return s
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// lookupWindowSetting: Finds a window setting by name, short form, or prefix.
|
|
func lookupWindowSetting(name string) *WindowSetting {
|
|
for i := range windowSettingsMap {
|
|
s := &windowSettingsMap[i]
|
|
if name == s.Name || name == s.ShortForm {
|
|
return s
|
|
}
|
|
// Prefix matching
|
|
if len(name) >= len(s.ShortForm) && strings.HasPrefix(s.Name, name) {
|
|
return s
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// parseSetOption: Parses and applies a single :set option.
|
|
func parseSetOption(m action.Model, opt string) error {
|
|
// Handle toggle: option!
|
|
if name, ok := strings.CutSuffix(opt, "!"); ok {
|
|
setting := lookupSetting(name)
|
|
if setting != nil {
|
|
if setting.Type == BoolSetting {
|
|
// Toggle the boolean
|
|
currentVal := setting.Get(m.Settings()).(bool)
|
|
setting.Set(m, !currentVal)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
windowSetting := lookupWindowSetting(name)
|
|
if windowSetting != nil {
|
|
if windowSetting.Type == BoolSetting {
|
|
// Toggle the boolean
|
|
currentVal := windowSetting.Get(m).(bool)
|
|
windowSetting.Set(m, !currentVal)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Handle disable: nooption
|
|
if name, ok := strings.CutPrefix(opt, "no"); ok {
|
|
setting := lookupSetting(name)
|
|
if setting != nil {
|
|
if setting.Type == BoolSetting {
|
|
setting.Set(m, false)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
windowSetting := lookupWindowSetting(name)
|
|
if windowSetting != nil {
|
|
if windowSetting.Type == BoolSetting {
|
|
windowSetting.Set(m, false)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Handle assignment: option=value
|
|
if strings.Contains(opt, "=") {
|
|
parts := strings.SplitN(opt, "=", 2)
|
|
name, value := parts[0], parts[1]
|
|
|
|
setting := lookupSetting(name)
|
|
if setting != nil {
|
|
switch setting.Type {
|
|
case IntSetting:
|
|
intVal, err := strconv.Atoi(value)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
setting.Set(m, intVal)
|
|
case StringSetting:
|
|
setting.Set(m, value)
|
|
case BoolSetting:
|
|
// Handle :set option=true / :set option=false
|
|
boolVal := value == "true" || value == "1" || value == "yes"
|
|
setting.Set(m, boolVal)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
windowSetting := lookupWindowSetting(name)
|
|
if windowSetting != nil {
|
|
switch windowSetting.Type {
|
|
case IntSetting:
|
|
intVal, err := strconv.Atoi(value)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
windowSetting.Set(m, intVal)
|
|
case StringSetting:
|
|
windowSetting.Set(m, value)
|
|
case BoolSetting:
|
|
// Handle :set option=true / :set option=false
|
|
boolVal := value == "true" || value == "1" || value == "yes"
|
|
windowSetting.Set(m, boolVal)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Handle enable: option (boolean only)
|
|
setting := lookupSetting(opt)
|
|
if setting != nil {
|
|
if setting.Type == BoolSetting {
|
|
setting.Set(m, true)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
windowSetting := lookupWindowSetting(opt)
|
|
if windowSetting != nil {
|
|
if windowSetting.Type == BoolSetting {
|
|
windowSetting.Set(m, true)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
return nil
|
|
}
|