Skip to content

Commit 3a76ccf

Browse files
committed
Port the fish setup function to Go
1 parent 092e0fb commit 3a76ccf

File tree

3 files changed

+101
-11
lines changed

3 files changed

+101
-11
lines changed

tools/tui/run.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ func RunShell(shell_cmd []string, shell_integration_env_var_val string) (err err
125125
env[k] = v
126126
}
127127
}
128-
argv, env, err := shell_integration.Setup(shell_name, shell_cmd, env)
128+
argv, env, err := shell_integration.Setup(shell_name, shell_integration_env_var_val, shell_cmd, env)
129129
if err != nil {
130130
return err
131131
}
@@ -134,18 +134,20 @@ func RunShell(shell_cmd []string, shell_integration_env_var_val string) (err err
134134
}
135135
exe := shell_cmd[0]
136136
if runtime.GOOS == "darwin" {
137-
// ensure shell runs in login mode
137+
// ensure shell runs in login mode. On macOS lots of people use ~/.bash_profile instead of ~/.bashrc
138+
// which means they expect the shell to run in login mode always. Le Sigh.
138139
shell_cmd[0] = "-" + filepath.Base(shell_cmd[0])
139140
}
140141
var env []string
141142
if shell_env != nil {
142-
env := make([]string, 0, len(shell_env))
143+
env = make([]string, 0, len(shell_env))
143144
for k, v := range shell_env {
144145
env = append(env, fmt.Sprintf("%s=%s", k, v))
145146
}
146147
} else {
147148
env = os.Environ()
148149
}
150+
// fmt.Println(fmt.Sprintf("%s %v\n%#v", utils.FindExe(exe), shell_cmd, env))
149151
return unix.Exec(utils.FindExe(exe), shell_cmd, env)
150152
}
151153

tools/tui/shell_integration/api.go

Lines changed: 54 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,20 @@ package shell_integration
44

55
import (
66
"archive/tar"
7+
"bytes"
78
"fmt"
9+
"kitty/tools/utils"
810
"os"
911
"path/filepath"
12+
"strings"
13+
14+
"golang.org/x/exp/maps"
15+
"golang.org/x/exp/slices"
1016
)
1117

1218
var _ = fmt.Print
1319

14-
type integration_setup_func = func(argv []string, env map[string]string) ([]string, map[string]string, error)
20+
type integration_setup_func = func(shell_integration_dir string, argv []string, env map[string]string) ([]string, map[string]string, error)
1521

1622
func extract_shell_integration_for(shell_name string, dest_dir string) (err error) {
1723
d := Data()
@@ -32,23 +38,55 @@ func extract_shell_integration_for(shell_name string, dest_dir string) (err erro
3238
return
3339
}
3440
case tar.TypeReg:
35-
if err = os.WriteFile(dest, entry.Data, 0o644); err != nil {
41+
if existing, rerr := os.ReadFile(dest); rerr == nil && bytes.Equal(existing, entry.Data) {
42+
continue
43+
}
44+
if err = utils.AtomicWriteFile(dest, entry.Data, 0o644); err != nil {
3645
return
3746
}
3847
}
3948
}
4049
return
4150
}
4251

43-
func zsh_setup_func(argv []string, env map[string]string) (final_argv []string, final_env map[string]string, err error) {
44-
return
52+
func EnsureShellIntegrationFilesFor(shell_name string) (shell_integration_dir string, err error) {
53+
if kid := os.Getenv("KITTY_INSTALLATION_DIR"); kid != "" {
54+
if s, e := os.Stat(kid); e == nil && s.IsDir() {
55+
q := filepath.Join(kid, "shell-integration", shell_name)
56+
if s, e := os.Stat(q); e == nil && s.IsDir() {
57+
return q, nil
58+
}
59+
}
60+
}
61+
base := filepath.Join(utils.CacheDir(), "extracted-ksi")
62+
if err = os.MkdirAll(base, 0o755); err != nil {
63+
return "", err
64+
}
65+
if err = extract_shell_integration_for(shell_name, base); err != nil {
66+
return "", err
67+
}
68+
return filepath.Join(base, "shell-integration"), nil
4569
}
4670

47-
func fish_setup_func(argv []string, env map[string]string) (final_argv []string, final_env map[string]string, err error) {
71+
func zsh_setup_func(shell_integration_dir string, argv []string, env map[string]string) (final_argv []string, final_env map[string]string, err error) {
4872
return
4973
}
5074

51-
func bash_setup_func(argv []string, env map[string]string) (final_argv []string, final_env map[string]string, err error) {
75+
func fish_setup_func(shell_integration_dir string, argv []string, env map[string]string) (final_argv []string, final_env map[string]string, err error) {
76+
shell_integration_dir = filepath.Dir(shell_integration_dir)
77+
val := env[`XDG_DATA_DIRS`]
78+
env[`KITTY_FISH_XDG_DATA_DIR`] = shell_integration_dir
79+
if val == "" {
80+
env[`XDG_DATA_DIRS`] = shell_integration_dir
81+
} else {
82+
dirs := utils.Filter(strings.Split(val, string(filepath.ListSeparator)), func(x string) bool { return x != "" })
83+
dirs = append([]string{shell_integration_dir}, dirs...)
84+
env[`XDG_DATA_DIRS`] = strings.Join(dirs, string(filepath.ListSeparator))
85+
}
86+
return argv, env, nil
87+
}
88+
89+
func bash_setup_func(shell_integration_dir string, argv []string, env map[string]string) (final_argv []string, final_env map[string]string, err error) {
5290
return
5391
}
5492

@@ -66,6 +104,14 @@ func setup_func_for_shell(shell_name string) integration_setup_func {
66104

67105
func IsSupportedShell(shell_name string) bool { return setup_func_for_shell(shell_name) != nil }
68106

69-
func Setup(shell_name string, argv []string, env map[string]string) ([]string, map[string]string, error) {
70-
return setup_func_for_shell(shell_name)(argv, env)
107+
func Setup(shell_name string, ksi_var string, argv []string, env map[string]string) ([]string, map[string]string, error) {
108+
ksi_dir, err := EnsureShellIntegrationFilesFor(shell_name)
109+
if err != nil {
110+
return nil, nil, err
111+
}
112+
argv, env, err = setup_func_for_shell(shell_name)(ksi_dir, slices.Clone(argv), maps.Clone(env))
113+
if err == nil {
114+
env[`KITTY_SHELL_INTEGRATION`] = ksi_var
115+
}
116+
return argv, env, err
71117
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>
2+
3+
package shell_integration
4+
5+
import (
6+
"bytes"
7+
"fmt"
8+
"os"
9+
"path/filepath"
10+
"testing"
11+
)
12+
13+
var _ = fmt.Print
14+
15+
func TestExtractShellIntegration(t *testing.T) {
16+
tdir := t.TempDir()
17+
if err := extract_shell_integration_for("zsh", tdir); err != nil {
18+
t.Fatal(err)
19+
}
20+
kzsh := filepath.Join(tdir, "shell-integration", "zsh", "kitty.zsh")
21+
if _, err := os.Stat(kzsh); err != nil {
22+
t.Fatal(err)
23+
}
24+
if _, err := os.Stat(filepath.Join(tdir, "shell-integration", "zsh", "completions", "_kitty")); err != nil {
25+
t.Fatal(err)
26+
}
27+
orig, err := os.ReadFile(kzsh)
28+
if err != nil {
29+
t.Fatal(err)
30+
}
31+
os.WriteFile(kzsh, []byte("changed"), 0o644)
32+
if err := extract_shell_integration_for("zsh", tdir); err != nil {
33+
t.Fatal(err)
34+
}
35+
changed, err := os.ReadFile(kzsh)
36+
if err != nil {
37+
t.Fatal(err)
38+
}
39+
if !bytes.Equal(changed, orig) {
40+
t.Fatalf("Failed to update shell integration file")
41+
}
42+
}

0 commit comments

Comments
 (0)