Quantcast
Channel: User Kamil Maciorowski - Super User
Viewing all articles
Browse latest Browse all 645

Answer by Kamil Maciorowski for tmux: Chaining `send-key Enter` and `refresh -S` in key bind

$
0
0

It's a race condition.

Expanding #{pane_current_path} requires the tmux server to learn the current working directory of the process being the "main one" in the pane. It doesn't matter how the tmux server learns or already knows which process this is, the point is the process is not a part of tmux, it's a separate process.

Often, like in your case, the "main process" in a pane is an interactive shell.

Tmux server may learn the current working directory of this shell by checking /proc/<PID>/cwd, where <PID> denotes the PID of the shell; or by some equivalent method.

When you type cd whateverEnter and your send-key Enter; refresh -S works, send-key Enter makes the shell start interpreting cd whatever. The important thing is send-key just sends the key, it does not wait for any effect of this action; and the shell just starts interpreting cd whatever, interpreting and executing is going to take some time; but without any delay tmux proceeds to refresh -S which makes the tmux server start refreshing the client's status line, which involves evaluating #{pane_current_path}. The shell (triggered by send-key) and the tmux server (triggered by refresh) do their tasks in parallel and there is no guarantee the shell manages to actually change its current working directory before #{pane_current_path} gets evaluated.

If the format string is evaluated first then you will see the old working directory in the status line.

Another command or a blank line shortly after the cd makes #{pane_current_path} return the new current working directory because Enter here is late enough, the shell has managed to change its working directory.

A good way to cause cd in your interactive shell to refresh the status line without a race is to make cd itself trigger refresh -S after it actually changes the working directory:

if [ -n "$TMUX" ]; then   cd () {      command cd "$@"      (         status="$?"         tmux refresh -S         return "$status"      )   }fi

If the shell is inside tmux then the above snippet will create a shell function that overloads the cd builtin.

Notes:

  1. The shell code itself is portable. The only non-portable part is the invocation of tmux.
  2. Existence of an alias for cd will make the snippet fail or misbehave. In Bash you can solve this problem by replacing cd () with non-portable function cd.
  3. The function retains the exit status of the actual cd.
  4. Invoking tmux asynchronously (tmux refresh -S &) will make the shell not wait for it, while not breaking the solution. I'm pointing this out because in your original attempt with bind the shell also does not wait, so maybe this is what you prefer. But if the asynchronous tmux fails (very unlikely though) and prints an error message then the message may appear after the next shell prompt, which will be confusing. For this reason IMO it's more elegant to invoke tmux synchronously. It's true that invoking tmux asynchronously may save few milliseconds; most likely in an interactive shell you won't notice the difference though.

Viewing all articles
Browse latest Browse all 645

Trending Articles