Skip to content

Commit 62c4a74

Browse files
committed
feat: config editor tui
1 parent d8d4a32 commit 62c4a74

5 files changed

Lines changed: 411 additions & 13 deletions

File tree

cmd/config.go

Lines changed: 58 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import (
77
"strings"
88

99
"github.com/aiomayo/hdf/internal/config"
10+
"github.com/aiomayo/hdf/internal/ui"
11+
"github.com/charmbracelet/huh"
1012
"github.com/spf13/cobra"
1113
)
1214

@@ -99,7 +101,7 @@ func formatShow(cfg *config.Config) string {
99101
if f.Kind == config.StringMap {
100102
fmt.Fprintf(&b, "%s\n", formatted)
101103
} else {
102-
fmt.Fprintf(&b, "%-17s = %-10s # %s\n", f.Key, formatted, f.Desc)
104+
fmt.Fprintf(&b, "%-20s = %-10s # %s\n", f.DisplayName(), formatted, f.Desc)
103105
}
104106
}
105107
}
@@ -110,19 +112,35 @@ func formatShow(cfg *config.Config) string {
110112
func newConfigEditCmd() *cobra.Command {
111113
return &cobra.Command{
112114
Use: "edit",
113-
Short: "Open config in editor",
115+
Short: "Open config editor",
114116
Args: cobra.NoArgs,
115117
RunE: func(cmd *cobra.Command, args []string) error {
116-
editor := os.Getenv("VISUAL")
117-
if editor == "" {
118-
editor = os.Getenv("EDITOR")
118+
cfg, err := config.Load()
119+
if err != nil {
120+
return err
119121
}
122+
123+
editor := cfg.DefaultEditor
120124
if editor == "" {
121-
editor = "vi"
125+
editor, err = pickEditor()
126+
if err != nil {
127+
return err
128+
}
129+
cfg.DefaultEditor = editor
130+
if err := config.Save(cfg); err != nil {
131+
return err
132+
}
122133
}
123134

124-
if _, err := config.Load(); err != nil {
125-
return err
135+
if editor == "tui" {
136+
modified, err := ui.EditConfig(cfg)
137+
if err != nil {
138+
return err
139+
}
140+
if modified {
141+
return config.Save(cfg)
142+
}
143+
return nil
126144
}
127145

128146
c := exec.Command(editor, config.Path())
@@ -134,6 +152,38 @@ func newConfigEditCmd() *cobra.Command {
134152
}
135153
}
136154

155+
func pickEditor() (string, error) {
156+
f := config.LookupField("default_editor")
157+
158+
var options []huh.Option[string]
159+
for _, opt := range f.Options {
160+
if opt == "" {
161+
continue
162+
}
163+
if opt == "tui" {
164+
options = append(options, huh.NewOption("Built-in TUI", opt))
165+
continue
166+
}
167+
if _, err := exec.LookPath(opt); err == nil {
168+
options = append(options, huh.NewOption(opt, opt))
169+
}
170+
}
171+
172+
var choice string
173+
form := huh.NewForm(
174+
huh.NewGroup(
175+
huh.NewSelect[string]().
176+
Title("Choose default config editor").
177+
Options(options...).
178+
Value(&choice),
179+
),
180+
)
181+
if err := form.Run(); err != nil {
182+
return "", err
183+
}
184+
return choice, nil
185+
}
186+
137187
func newConfigResetCmd() *cobra.Command {
138188
return &cobra.Command{
139189
Use: "reset",

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ go 1.25.0
44

55
require (
66
github.com/adrg/xdg v0.5.3
7+
github.com/charmbracelet/bubbles v1.0.0
8+
github.com/charmbracelet/bubbletea v1.3.10
79
github.com/charmbracelet/huh v0.8.0
810
github.com/charmbracelet/lipgloss v1.1.0
911
github.com/charmbracelet/log v0.4.2
@@ -18,8 +20,6 @@ require (
1820
github.com/atotto/clipboard v0.1.4 // indirect
1921
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
2022
github.com/catppuccin/go v0.3.0 // indirect
21-
github.com/charmbracelet/bubbles v1.0.0 // indirect
22-
github.com/charmbracelet/bubbletea v1.3.10 // indirect
2323
github.com/charmbracelet/colorprofile v0.4.2 // indirect
2424
github.com/charmbracelet/x/ansi v0.11.6 // indirect
2525
github.com/charmbracelet/x/cellbuf v0.0.15 // indirect

internal/config/config.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ type Config struct {
1919
Aliases map[string]string `mapstructure:"aliases"`
2020
DefaultForce bool `mapstructure:"default_force"`
2121
DefaultVerbose bool `mapstructure:"default_verbose"`
22+
DefaultEditor string `mapstructure:"default_editor"`
2223
}
2324

2425
func Path() string {
@@ -143,6 +144,13 @@ func ParseValue(f *Field, raw string) (any, error) {
143144
return strconv.ParseBool(raw)
144145
case String:
145146
return raw, nil
147+
case Select:
148+
for _, opt := range f.Options {
149+
if raw == opt {
150+
return raw, nil
151+
}
152+
}
153+
return nil, fmt.Errorf("invalid option %q for %s (valid: %s)", raw, f.Key, strings.Join(f.Options, ", "))
146154
case Duration:
147155
return time.ParseDuration(raw)
148156
case StringSlice:
@@ -162,7 +170,7 @@ func FormatValue(f *Field, val any) string {
162170
switch f.Kind {
163171
case Bool:
164172
return strconv.FormatBool(val.(bool))
165-
case String:
173+
case String, Select:
166174
return fmt.Sprintf("%q", val.(string))
167175
case Duration:
168176
return fmt.Sprintf("%q", val.(time.Duration).String())

internal/config/schema.go

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,40 +7,62 @@ type Kind int
77
const (
88
Bool Kind = iota
99
String
10+
Select
1011
Duration
1112
StringSlice
1213
StringMap
1314
)
1415

1516
type Field struct {
1617
Key string
18+
Label string
1719
Group string
1820
Kind Kind
1921
Default any
22+
Options []string
2023
Desc string
2124
}
2225

26+
func (f *Field) DisplayName() string {
27+
if f.Label != "" {
28+
return f.Label
29+
}
30+
return f.Key
31+
}
32+
2333
var Schema = []Field{
2434
{
2535
Key: "graceful_timeout",
36+
Label: "Graceful timeout",
2637
Kind: Duration,
2738
Default: 5 * time.Second,
2839
Desc: "Graceful shutdown timeout before SIGKILL",
2940
},
3041
{
3142
Key: "default_force",
43+
Label: "Force kill",
3244
Kind: Bool,
3345
Default: false,
34-
Desc: "Always use SIGKILL",
46+
Desc: "Always use SIGKILL instead of SIGTERM",
3547
},
3648
{
3749
Key: "default_verbose",
50+
Label: "Verbose output",
3851
Kind: Bool,
3952
Default: false,
4053
Desc: "Enable verbose output by default",
4154
},
55+
{
56+
Key: "default_editor",
57+
Label: "Config editor",
58+
Kind: Select,
59+
Default: "",
60+
Options: []string{"", "tui", "vim", "nvim", "nano", "vi"},
61+
Desc: "Preferred editor for hdf config edit",
62+
},
4263
{
4364
Key: "protected",
65+
Label: "Protected processes",
4466
Group: "protected",
4567
Kind: StringSlice,
4668
Default: []string{
@@ -51,10 +73,11 @@ var Schema = []Field{
5173
},
5274
{
5375
Key: "aliases",
76+
Label: "Aliases",
5477
Group: "aliases",
5578
Kind: StringMap,
5679
Default: map[string]string{},
57-
Desc: "Query shortcuts",
80+
Desc: "Query shortcuts (name → target)",
5881
},
5982
}
6083

0 commit comments

Comments
 (0)