report the working directory to the terminal with OSC 7

Before each prompt gosh sends ESC ] 7 ; file://host/path BEL (host and
path percent-encoded), so terminals like GNOME Terminal open new tabs
and windows in the directory of the shell instead of the one gosh was
started in. Nothing is sent if stdout is not a terminal or TERM=dumb.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Florian WaltherandClaude Opus 5.5 committed 2026-10-05 19:22:59 +02:00
1 parent 1f507f6562
commit af5281eed2
4 files changed
+80

No files matched your search

+46
View File
@@ -795,6 +795,49 @@ func useColor() bool {
return readline.IsTerminal(int(os.Stdout.Fd()))
}
// reportCwd tells the terminal the current working directory with the OSC 7
// escape sequence, so that new tabs or windows open in the same directory
// (VTE terminals like GNOME Terminal, kitty, WezTerm, foot, iTerm2, ...).
// Terminals without support ignore it. Nothing is written if stdout is not a
// terminal or TERM is "dumb".
func reportCwd() {
if os.Getenv("TERM") == "dumb" || !readline.IsTerminal(int(os.Stdout.Fd())) {
return
}
wd, err := os.Getwd()
if err != nil {
return
}
hostname, _ := os.Hostname()
fmt.Fprint(os.Stdout, osc7Sequence(hostname, wd))
}
// osc7Sequence returns ESC ] 7 ; file://host/path BEL. Host and path are
// percent-encoded, so control characters in a directory name cannot end the
// sequence early or inject other escape sequences.
func osc7Sequence(hostname, dir string) string {
return "\x1b]7;file://" + percentEncode(hostname, false) + percentEncode(dir, true) + "\a"
}
// percentEncode encodes every byte except unreserved URI characters (and '/'
// if keepSlash) as %XX.
func percentEncode(s string, keepSlash bool) string {
const hex = "0123456789ABCDEF"
var b strings.Builder
for i := 0; i < len(s); i++ {
c := s[i]
if c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9' ||
c == '-' || c == '.' || c == '_' || c == '~' || c == '/' && keepSlash {
b.WriteByte(c)
} else {
b.WriteByte('%')
b.WriteByte(hex[c>>4])
b.WriteByte(hex[c&15])
}
}
return b.String()
}
// shellCompleter implements readline.AutoCompleter for contextual completion
type shellCompleter struct {
commands []string
@@ -1334,6 +1377,7 @@ func interactive() {
// Report finished and stopped background jobs
reapJobs(true)
// Update prompt dynamically (in case directory changed)
reportCwd()
l.SetPrompt(getPrompt())
} else {
l.SetPrompt("> ")
@@ -2137,6 +2181,8 @@ Features:
GOSH_COLORS='user=1;33:path=36' (keys user, root, path, error; values
are ANSI SGR codes, empty = no color); disabled with $NO_COLOR,
TERM=dumb or when output is not a terminal
- Reports the working directory to the terminal (OSC 7) before each
prompt, so new tabs/windows open in the same directory
- Variables: name=value sets a variable (like set), name=value command
sets it only for that command; the value is not split or expanded as
a pattern