From 3fd0a7a02dc391f7353e48fabe93de3b6d5399ae Mon Sep 17 00:00:00 2001 From: simota Date: Thu, 16 Jul 2026 20:40:15 +0900 Subject: [PATCH] perf(shell-integration): emit prompt marks in one write via kitty-shell-cwd:// MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The zsh precmd hook percent-encoded $PWD one character at a time, forking $(printf) for every non-ASCII character (~60µs/prompt on an ASCII path, ~3.7ms in a non-ASCII path), and each hook issued four separate printf writes. The fish hook forked (hostname) on every prompt. Switch OSC 7 to the kitty-shell-cwd:// scheme (raw path, no encoding — the parser already supports it per REQ-OSC-3) and emit D/7/A/B as a single builtin printf in zsh/bash/fish: ~7µs/prompt regardless of path content. Also stop leaking noa-only bookkeeping into the session environment: ZDOTDIR is now unset after startup when the user never had one (Ghostty parity; previously ZDOTDIR=$HOME stayed exported), and USER_ZDOTDIR is a plain unexported variable. --- crates/noa-pty/src/pty.rs | 16 ++++--- shell-integration/bash/noa.bash | 19 ++++----- .../fish/vendor_conf.d/noa-integration.fish | 18 +++----- shell-integration/zsh/.zshenv | 7 +++- shell-integration/zsh/.zshrc | 42 +++++++------------ 5 files changed, 44 insertions(+), 58 deletions(-) diff --git a/crates/noa-pty/src/pty.rs b/crates/noa-pty/src/pty.rs index 7a4040eb..34b47d71 100644 --- a/crates/noa-pty/src/pty.rs +++ b/crates/noa-pty/src/pty.rs @@ -827,8 +827,8 @@ mod tests { collected.extend_from_slice(&chunk); if collected.windows(7).any(|w| w == b"\x1b]133;A") && collected - .windows(11) - .any(|w| w == b"\x1b]7;file://".as_ref()) + .windows(22) + .any(|w| w == b"\x1b]7;kitty-shell-cwd://".as_ref()) { break; } @@ -844,7 +844,9 @@ mod tests { "expected an OSC 133;A prompt mark in zsh output" ); assert!( - collected.windows(11).any(|w| w == b"\x1b]7;file://"), + collected + .windows(22) + .any(|w| w == b"\x1b]7;kitty-shell-cwd://"), "expected an OSC 7 cwd report in zsh output" ); } @@ -879,8 +881,8 @@ mod tests { collected.extend_from_slice(&chunk); if collected.windows(7).any(|w| w == b"\x1b]133;A") && collected - .windows(11) - .any(|w| w == b"\x1b]7;file://".as_ref()) + .windows(22) + .any(|w| w == b"\x1b]7;kitty-shell-cwd://".as_ref()) { break; } @@ -896,7 +898,9 @@ mod tests { "expected an OSC 133;A prompt mark in bash output" ); assert!( - collected.windows(11).any(|w| w == b"\x1b]7;file://"), + collected + .windows(22) + .any(|w| w == b"\x1b]7;kitty-shell-cwd://"), "expected an OSC 7 cwd report in bash output" ); } diff --git a/shell-integration/bash/noa.bash b/shell-integration/bash/noa.bash index f73716bf..a8c5beba 100644 --- a/shell-integration/bash/noa.bash +++ b/shell-integration/bash/noa.bash @@ -21,19 +21,14 @@ unset NOA_BASH_LOGIN NOA_BASH_INJECT NOA_BASH_RCFILE # Only wire up integration for a real interactive terminal. case "$-" in *i*) - _noa_report_cwd() { - printf '\e]7;file://%s%s\a' "${HOSTNAME}" "$PWD" - } - - # OSC 133 D (last command's exit status) + A (prompt start) + B (input - # start), plus OSC 7, emitted before each prompt. Runs first in - # PROMPT_COMMAND so `$?` is still the finished command's status. + # OSC 133 D (last command's exit status) + OSC 7 (cwd, raw path via the + # kitty-shell-cwd:// scheme — no encoding needed) + A (prompt start) + + # B (input start), emitted before each prompt in one builtin printf. + # Runs first in PROMPT_COMMAND so `$?` is still the finished command's + # status when it's expanded. _noa_prompt() { - local ret="$?" - printf '\e]133;D;%s\a' "$ret" - _noa_report_cwd - printf '\e]133;A\a' - printf '\e]133;B\a' + builtin printf '\e]133;D;%s\a\e]7;kitty-shell-cwd://%s%s\a\e]133;A\a\e]133;B\a' \ + "$?" "$HOSTNAME" "$PWD" } PROMPT_COMMAND="_noa_prompt${PROMPT_COMMAND:+; $PROMPT_COMMAND}" diff --git a/shell-integration/fish/vendor_conf.d/noa-integration.fish b/shell-integration/fish/vendor_conf.d/noa-integration.fish index 2a6c6b58..c52e528c 100644 --- a/shell-integration/fish/vendor_conf.d/noa-integration.fish +++ b/shell-integration/fish/vendor_conf.d/noa-integration.fish @@ -6,22 +6,16 @@ status is-interactive; or exit 0 -function _noa_report_cwd - # OSC 7: report cwd as a file:// URL. - printf '\e]7;file://%s%s\a' (hostname) "$PWD" -end - function _noa_preexec --on-event fish_preexec # OSC 133 C: a command is about to run. printf '\e]133;C\a' end function _noa_prompt --on-event fish_prompt - # OSC 133 D (last command's exit status) + A (prompt start) + B (input - # start), plus OSC 7, emitted before each prompt. - set -l ret $status - printf '\e]133;D;%s\a' $ret - _noa_report_cwd - printf '\e]133;A\a' - printf '\e]133;B\a' + # OSC 133 D (last command's exit status) + OSC 7 (cwd, raw path via the + # kitty-shell-cwd:// scheme) + A (prompt start) + B (input start), emitted + # before each prompt. One builtin printf and the $hostname variable — no + # command substitution, since this runs on every prompt. + printf '\e]133;D;%s\a\e]7;kitty-shell-cwd://%s%s\a\e]133;A\a\e]133;B\a' \ + $status $hostname $PWD end diff --git a/shell-integration/zsh/.zshenv b/shell-integration/zsh/.zshenv index a6395c42..840e163b 100644 --- a/shell-integration/zsh/.zshenv +++ b/shell-integration/zsh/.zshenv @@ -8,10 +8,13 @@ # # The user's real ZDOTDIR (or $HOME) is carried in NOA_ZDOTDIR, set by noa. +# Plain (unexported) shell variables: later startup files in this same shell +# see them, but child processes don't inherit noa-only bookkeeping. if [[ -n "$NOA_ZDOTDIR" ]]; then - export USER_ZDOTDIR="$NOA_ZDOTDIR" + USER_ZDOTDIR="$NOA_ZDOTDIR" + NOA_USER_HAD_ZDOTDIR=1 else - export USER_ZDOTDIR="$HOME" + USER_ZDOTDIR="$HOME" fi unset NOA_ZDOTDIR diff --git a/shell-integration/zsh/.zshrc b/shell-integration/zsh/.zshrc index 3067e1cd..31edd4ab 100644 --- a/shell-integration/zsh/.zshrc +++ b/shell-integration/zsh/.zshrc @@ -1,42 +1,32 @@ # noa shell integration (zsh) — interactive setup. # # Source the user's real .zshrc first so noa's hooks wrap their prompt, then -# restore ZDOTDIR so any zsh spawned later reads the user's config directly. +# restore ZDOTDIR (or unset it if the user never had one — Ghostty parity) so +# any zsh spawned later reads the user's config directly. [[ -f "$USER_ZDOTDIR/.zshrc" ]] && source "$USER_ZDOTDIR/.zshrc" -ZDOTDIR="$USER_ZDOTDIR" +if [[ -n "$NOA_USER_HAD_ZDOTDIR" ]]; then + ZDOTDIR="$USER_ZDOTDIR" + unset NOA_USER_HAD_ZDOTDIR +else + unset ZDOTDIR +fi # Only wire up integration for a real interactive terminal. if [[ -o interactive ]]; then - # OSC 7: report the working directory as a file:// URL (percent-encoding the - # few characters that would otherwise break the URL). - _noa_report_cwd() { - local encoded="" c - local i=1 - while (( i <= ${#PWD} )); do - c="${PWD[i]}" - case "$c" in - [a-zA-Z0-9/._~-]) encoded+="$c" ;; - *) encoded+=$(printf '%%%02X' "'$c") ;; - esac - (( i++ )) - done - printf '\e]7;file://%s%s\a' "${HOST}" "$encoded" - } - - # OSC 133 D (previous command's exit status) + A (prompt start) + B (prompt - # end / input start), plus OSC 7, emitted just before each prompt. + # OSC 133 D (previous command's exit status) + OSC 7 (cwd) + OSC 133 A + # (prompt start) + B (prompt end / input start), emitted just before each + # prompt. This runs on every prompt, so it must stay cheap: one builtin + # printf, no subshells. The kitty-shell-cwd:// scheme carries the path raw + # (no percent-encoding), same as Ghostty/kitty. _noa_precmd() { - local ret="$?" - printf '\e]133;D;%s\a' "$ret" - _noa_report_cwd - printf '\e]133;A\a' - printf '\e]133;B\a' + builtin printf '\e]133;D;%s\a\e]7;kitty-shell-cwd://%s%s\a\e]133;A\a\e]133;B\a' \ + "$?" "$HOST" "$PWD" } # OSC 133 C: a command is about to run. _noa_preexec() { - printf '\e]133;C\a' + builtin printf '\e]133;C\a' } autoload -Uz add-zsh-hook