Skip to content

Commit 9285131

Browse files
authored
Merge pull request docker#1313 from jeanlaurent/editor-name
Expand $EDITOR in the TUI
2 parents 9fbfbee + 885c20e commit 9285131

File tree

3 files changed

+187
-2
lines changed

3 files changed

+187
-2
lines changed

pkg/tui/page/chat/chat.go

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
package chat
22

33
import (
4+
"cmp"
45
"context"
56
"fmt"
67
"log/slog"
78
"os"
9+
"path/filepath"
10+
"runtime"
811
"strings"
912

1013
"charm.land/bubbles/v2/help"
@@ -108,8 +111,78 @@ type KeyMap struct {
108111
ToggleSplitDiff key.Binding
109112
}
110113

114+
// getEditorDisplayNameFromEnv returns a friendly display name for the configured editor.
115+
// It takes visual and editorEnv values as parameters and maps common editors to display names.
116+
// If neither is set, it returns the platform-specific fallback that will actually be used.
117+
func getEditorDisplayNameFromEnv(visual, editorEnv string) string {
118+
editorCmd := cmp.Or(visual, editorEnv)
119+
if editorCmd == "" {
120+
// Return the actual fallback editor that will be used
121+
if runtime.GOOS == "windows" {
122+
return "Notepad"
123+
}
124+
return "Vi"
125+
}
126+
127+
// Parse the command (may include arguments like "code --wait")
128+
parts := strings.Fields(editorCmd)
129+
if len(parts) == 0 {
130+
return "$EDITOR"
131+
}
132+
133+
// Get the base command name (e.g., "/usr/local/bin/code" → "code")
134+
baseName := filepath.Base(parts[0])
135+
136+
// Map common editor command prefixes to friendly display names
137+
// Using prefix matching to handle variants like "code-insiders", "nvim-qt", etc.
138+
editorPrefixes := []struct {
139+
prefix string
140+
name string
141+
}{
142+
{"code", "VSCode"},
143+
{"cursor", "Cursor"},
144+
{"nvim", "Neovim"},
145+
{"vim", "Vim"},
146+
{"vi", "Vi"},
147+
{"nano", "Nano"},
148+
{"emacs", "Emacs"},
149+
{"subl", "Sublime Text"},
150+
{"sublime", "Sublime Text"},
151+
{"atom", "Atom"},
152+
{"gedit", "gedit"},
153+
{"kate", "Kate"},
154+
{"notepad++", "Notepad++"},
155+
{"notepad", "Notepad"},
156+
{"textmate", "TextMate"},
157+
{"mate", "TextMate"},
158+
{"zed", "Zed"},
159+
}
160+
161+
for _, editor := range editorPrefixes {
162+
if strings.HasPrefix(baseName, editor.prefix) {
163+
return editor.name
164+
}
165+
}
166+
167+
// Return the base name with first letter capitalized
168+
if baseName != "" {
169+
return strings.ToUpper(baseName[:1]) + baseName[1:]
170+
}
171+
172+
return "$EDITOR"
173+
}
174+
175+
// getEditorDisplayName returns a friendly display name for the configured editor.
176+
// It reads the VISUAL or EDITOR environment variables and maps common editors to display names.
177+
// If neither is set, it returns the platform-specific fallback that will actually be used.
178+
func getEditorDisplayName() string {
179+
return getEditorDisplayNameFromEnv(os.Getenv("VISUAL"), os.Getenv("EDITOR"))
180+
}
181+
111182
// defaultKeyMap returns the default key bindings
112183
func defaultKeyMap() KeyMap {
184+
editorName := getEditorDisplayName()
185+
113186
return KeyMap{
114187
Tab: key.NewBinding(
115188
key.WithKeys("tab"),
@@ -126,7 +199,7 @@ func defaultKeyMap() KeyMap {
126199
),
127200
ExternalEditor: key.NewBinding(
128201
key.WithKeys("ctrl+g"),
129-
key.WithHelp("Ctrl+g", "edit in $EDITOR"),
202+
key.WithHelp("Ctrl+g", fmt.Sprintf("edit in %s", editorName)),
130203
),
131204
ToggleSplitDiff: key.NewBinding(
132205
key.WithKeys("ctrl+t"),

pkg/tui/page/chat/chat_test.go

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
package chat
2+
3+
import (
4+
"testing"
5+
)
6+
7+
func TestGetEditorDisplayNameFromEnv(t *testing.T) {
8+
t.Parallel()
9+
10+
tests := []struct {
11+
name string
12+
visual string
13+
editorEnv string
14+
want string
15+
}{
16+
{
17+
name: "VSCode",
18+
visual: "",
19+
editorEnv: "code",
20+
want: "VSCode",
21+
},
22+
{
23+
name: "VSCode with args",
24+
visual: "",
25+
editorEnv: "code --wait",
26+
want: "VSCode",
27+
},
28+
{
29+
name: "VSCode with full path",
30+
visual: "",
31+
editorEnv: "/usr/local/bin/code --wait",
32+
want: "VSCode",
33+
},
34+
{
35+
name: "Vim",
36+
visual: "",
37+
editorEnv: "vim",
38+
want: "Vim",
39+
},
40+
{
41+
name: "Neovim",
42+
visual: "",
43+
editorEnv: "nvim",
44+
want: "Neovim",
45+
},
46+
{
47+
name: "Cursor",
48+
visual: "",
49+
editorEnv: "cursor",
50+
want: "Cursor",
51+
},
52+
{
53+
name: "Unknown editor",
54+
visual: "",
55+
editorEnv: "myeditor",
56+
want: "Myeditor",
57+
},
58+
{
59+
name: "Unknown editor with full path",
60+
visual: "",
61+
editorEnv: "/opt/bin/myeditor",
62+
want: "Myeditor",
63+
},
64+
{
65+
name: "Empty (uses platform default)",
66+
visual: "",
67+
editorEnv: "",
68+
want: "Vi", // On non-Windows platforms, falls back to vi
69+
},
70+
{
71+
name: "VSCode Insiders",
72+
visual: "",
73+
editorEnv: "code-insiders",
74+
want: "VSCode",
75+
},
76+
{
77+
name: "Neovim Qt",
78+
visual: "",
79+
editorEnv: "nvim-qt",
80+
want: "Neovim",
81+
},
82+
{
83+
name: "Vim GTK",
84+
visual: "",
85+
editorEnv: "vim-gtk3",
86+
want: "Vim",
87+
},
88+
{
89+
name: "VISUAL takes precedence over EDITOR",
90+
visual: "code",
91+
editorEnv: "vim",
92+
want: "VSCode",
93+
},
94+
{
95+
name: "VISUAL with args takes precedence",
96+
visual: "code --wait",
97+
editorEnv: "vim",
98+
want: "VSCode",
99+
},
100+
}
101+
102+
for _, tt := range tests {
103+
t.Run(tt.name, func(t *testing.T) {
104+
t.Parallel()
105+
106+
got := getEditorDisplayNameFromEnv(tt.visual, tt.editorEnv)
107+
if got != tt.want {
108+
t.Errorf("getEditorDisplayNameFromEnv(%q, %q) = %v, want %v", tt.visual, tt.editorEnv, got, tt.want)
109+
}
110+
})
111+
}
112+
}

pkg/tui/page/chat/editor.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ func (p *chatPage) openExternalEditor() tea.Cmd {
4141
if runtime.GOOS == "windows" {
4242
editorCmd = "notepad"
4343
} else {
44-
editorCmd = "vim"
44+
editorCmd = "vi"
4545
}
4646
}
4747

0 commit comments

Comments
 (0)