diff --git a/docs/reference/shell-integration.mdx b/docs/reference/shell-integration.mdx index 89a029ab..261d46e6 100644 --- a/docs/reference/shell-integration.mdx +++ b/docs/reference/shell-integration.mdx @@ -19,11 +19,13 @@ removes itself from the equation if you run the same shell elsewhere. | **bash** | An rcfile that sources your own first. | | **fish** | A `-C` init command. | | **PowerShell** | An encoded init command that wraps your existing `prompt` function and PSReadLine's line reader. | +| **nushell** | A `--config` wrapper whose `config.nu` sources yours first, then adds tty7's hooks. Local panes only. | | **WSL** *(Windows)* | The distro's shell is bootstrapped with the same scripts. | -| **Remote panes** | The same three POSIX shells, bootstrapped over the SSH connection. Toggle per profile with **Settings → SSH → Session → Shell integration**. | +| **Remote panes** | The same three POSIX shells, bootstrapped over the SSH connection. Toggle per profile with **Settings → SSH →** a profile **→ Advanced → Session → Shell integration**. | `TTY7_SHELL_INTEGRATION` is set once it is active, and guards against a second -injection when shells nest. +injection when shells nest. It does not put the integration *into* a nested +shell — [shells you start yourself](#shells-you-start-yourself) have none. A shell launched with arguments *you* wrote — `shell.args`, or a @@ -51,10 +53,49 @@ it just falls back to less precise sources: - Ghost suggestions, the completion menu, and ⌃ R's fuzzy history - "Command finished" notifications and the failure marks in history search -- Exact working-directory tracking (tty7 falls back to inspecting the process) +- Exact working-directory tracking Panes, splits, scrollback, search, SSH, and the CLI are unaffected. +The working-directory fallback is worth knowing in detail, because it is not +available everywhere. Where tty7 can see the pane's processes it reads the +foreground one's own working directory instead — a local pane on macOS or +Linux, or a pane in a remote workspace, where the `tty7-server` on that host +does the reading. It is a poll, not a report: it is sampled while the pane is +producing output, at most twice a second, so it can trail a `cd` until the +pane writes something again. A local pane on **Windows** and a pane connected +to an **SSH host** have no fallback at all — on the far end of an `ssh` there +is no process table to read, so a shell that does not report its directory +leaves the pane showing the last directory that did. + +## Shells you start yourself + +The injection happens when the pane's shell is *launched*. Type `zsh` at a +bash prompt — or `bash`, or `sh`, or `docker exec … sh` — and that second +shell is a new process, launched by your shell rather than by tty7. It emits no +prompt marks and no `OSC 7`, and the pane keeps showing the directory the +outer shell last reported until you exit back to it. + +On your own machine this is usually invisible, because prompt frameworks +report the directory themselves: oh-my-zsh does it from +`omz_termsupport_cwd` in `lib/termsupport.zsh`. **Over SSH it is not.** That +file returns early when `SSH_CLIENT` or `SSH_TTY` is set, so on a remote host +a zsh you started by hand reports nothing at all, and neither does tty7. + +To get integration into that shell, make it the shell tty7 starts: + +- **A remote host.** tty7 asks the host for `$SHELL` when it connects and + bootstraps that shell, so `chsh -s /bin/zsh` (then reconnect) is what + switches it. A login script under **Advanced → Session** that runs `zsh` + does *not* — those lines are typed into the session after the shell is + already up, so they nest like anything else. Neither does `exec zsh` from + your `.bashrc`: it replaces the process tty7 set up. +- **A local pane.** Set **Settings → Terminal → Shell → Program** (or `shell` + in [config](/reference/configuration)), and leave **Arguments** empty — + arguments you write turn the injection off. + +[If the Files panel stopped following a nested shell →](/reference/troubleshooting#the-directory-stopped-following-my-shell) + ## Per-pane history When `per_pane_history` is on, the integration is also what makes it work. It diff --git a/docs/reference/troubleshooting.mdx b/docs/reference/troubleshooting.mdx index e06c3746..ee5b40bc 100644 --- a/docs/reference/troubleshooting.mdx +++ b/docs/reference/troubleshooting.mdx @@ -81,8 +81,79 @@ shell: - **Settings → Input → Prompt → History search** If they do nothing at all in a particular pane, the shell there probably has no -[shell integration](/reference/shell-integration) — nushell, elvish, xonsh and -friends run fine but do not get the prompt layer. +[shell integration](/reference/shell-integration) — elvish, xonsh and friends +run fine but do not get the prompt layer. + +## The directory stopped following my shell + +The Files panel, new splits, and the sidebar's repo grouping all follow the +directory the pane's shell reports (`OSC 7`). If the pane is frozen on one +directory while you `cd` around, something in the pane is not reporting it. + +The usual cause is a shell you started by hand. tty7 injects its +[shell integration](/reference/shell-integration) into the shell it launches +for the pane, and nothing else: run `zsh` at a bash prompt and that nested +shell has none. Locally you rarely notice, because oh-my-zsh reports the +directory itself — but the cwd reporter sits at the end of its +`lib/termsupport.zsh`, behind + +```zsh +if [[ -n "$INSIDE_EMACS" || -n "$SSH_CLIENT" || -n "$SSH_TTY" ]]; then + return +fi +``` + +so over SSH, where sshd sets those, sourcing stops there and the reporter is +never defined — only the title hooks above that line still run. A nested zsh +on a remote host therefore reports nothing, from either side, and the pane +keeps showing wherever the outer shell was. + +**Make it the shell tty7 starts.** On the remote host: + +```sh +chsh -s /bin/zsh # then reconnect +``` + +tty7 asks the host for `$SHELL` at connect time and bootstraps *that* shell, +so this is what moves the integration into your zsh — oh-my-zsh keeps working +beside it. Starting `zsh` from a login script, or `exec zsh` from your +`.bashrc`, does not help: both run after (or instead of) the shell tty7 set +up. + +**Or report the directory yourself.** If you cannot change the login shell, +add this to the remote `~/.zshrc` — it restores directory tracking only, not +prompt marks or command status: + +```zsh +# Report the working directory to tty7 (OSC 7). +autoload -Uz add-zsh-hook +__tty7_cwd() { printf '\e]7;file://%s%s\a' "${HOST:-localhost}" "${PWD//\%/%25}" } +add-zsh-hook precmd __tty7_cwd +``` + +It is safe to leave in place afterwards: when tty7 *is* integrating that +shell, the two hooks simply report the same directory twice. The bash +equivalent is `PROMPT_COMMAND`: + +```bash +__tty7_cwd() { printf '\e]7;file://%s%s\a' "${HOSTNAME:-localhost}" "${PWD//%/%25}"; } +PROMPT_COMMAND="__tty7_cwd${PROMPT_COMMAND:+;$PROMPT_COMMAND}" +``` + +Two other reasons a pane never reports its directory: + +- **The shell is not one tty7 integrates.** It injects into zsh, bash, fish, + PowerShell and nushell locally, and into zsh, bash and fish over SSH — a + remote host whose `$SHELL` is anything else gets no bootstrap, silently. +- **You gave the shell your own arguments** in `shell` or a `custom_shells` + entry, which turns the injection off by design. + +Where tty7 can see the pane's processes it falls back to reading the +foreground process's working directory, which is why an unintegrated shell in +a **remote workspace** mostly keeps up — that fallback is a poll, though, +sampled while the pane produces output, so it can lag a `cd` until the pane +writes again. There is no such fallback for an **SSH pane** (the processes are +on the far end) or for a local pane on **Windows**. ## ⌥ B types `∫` instead of moving a word