fix(wrapper): follow the shell working directory in the wrapper process (#129)
Fixes #124. Since #78 the shell reports `IRIS_CWD` on `chpwd`, but the wrapper only passes it to `spec.SetCWD` for suggestions. Its own working directory never moves, so tools that read the pane's foreground process to locate the shell resolve the wrong path after a `cd`. I hit this with herdr, which stopped detecting the git repository of a pane once IRIS was in it. tmux uses the same lookup for `pane_current_path`. The absolute path check matches what `spec.SetCWD` already does, so both treat a malformed value the same way. The `os.Chdir` error is dropped because the path comes from the shell integration, the user cannot act on the failure, and keeping the last known directory is better than stopping the loop. `go test ./...` passes. I tested this end to end on macOS only: the wrapper followed the `cd`, and herdr picked the repository back up. One limitation: the outer launcher process keeps its start directory, only the wrapper running `runWrapper` follows. That was enough for herdr, which reads the wrapper.
This commit is contained in:
@@ -117,6 +117,13 @@ func restoreTerminal() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func syncProcessCWD(cwd string) {
|
||||||
|
if !filepath.IsAbs(cwd) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_ = os.Chdir(cwd)
|
||||||
|
}
|
||||||
|
|
||||||
// runWrapper sets up the pty environment, launches the shell,
|
// runWrapper sets up the pty environment, launches the shell,
|
||||||
// and manages the main input loop to provide real-time suggestions
|
// and manages the main input loop to provide real-time suggestions
|
||||||
// it handles raw terminal mode to intercept keystrokes and
|
// it handles raw terminal mode to intercept keystrokes and
|
||||||
@@ -494,6 +501,7 @@ func runWrapper() {
|
|||||||
|
|
||||||
if cwd, ok := strings.CutPrefix(query, "IRIS_CWD:"); ok {
|
if cwd, ok := strings.CutPrefix(query, "IRIS_CWD:"); ok {
|
||||||
spec.SetCWD(cwd)
|
spec.SetCWD(cwd)
|
||||||
|
syncProcessCWD(cwd)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package root
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func wantDir(t *testing.T, path string) string {
|
||||||
|
t.Helper()
|
||||||
|
resolved, err := filepath.EvalSymlinks(path)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return resolved
|
||||||
|
}
|
||||||
|
|
||||||
|
func currentDir(t *testing.T) string {
|
||||||
|
t.Helper()
|
||||||
|
cwd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return wantDir(t, cwd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSyncProcessCWDFollowsShell(t *testing.T) {
|
||||||
|
shellDir := t.TempDir()
|
||||||
|
t.Chdir(t.TempDir())
|
||||||
|
|
||||||
|
syncProcessCWD(shellDir)
|
||||||
|
|
||||||
|
if got := currentDir(t); got != wantDir(t, shellDir) {
|
||||||
|
t.Fatalf("os.Getwd() = %q, want %q", got, wantDir(t, shellDir))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSyncProcessCWDKeepsDirectoryOnBadPath(t *testing.T) {
|
||||||
|
launcherDir := t.TempDir()
|
||||||
|
t.Chdir(launcherDir)
|
||||||
|
|
||||||
|
for _, cwd := range []string{"", "relative/path", filepath.Join(launcherDir, "does-not-exist")} {
|
||||||
|
syncProcessCWD(cwd)
|
||||||
|
|
||||||
|
if got := currentDir(t); got != wantDir(t, launcherDir) {
|
||||||
|
t.Fatalf("syncProcessCWD(%q) moved the process to %q, want %q", cwd, got, wantDir(t, launcherDir))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user