Skip to content

here mode level 2: eliminate send_keys entirely — split-window/swap-pane/kill-pane #1032

@tony

Description

@tony

Parent: #1030

Approach

Eliminate all send_keys usage in --here mode by replacing the "mutate existing pane" pattern with "create fresh pane, swap into position." This is the cleanest possible approach — zero shell assumptions.

Core idea

Instead of sending cd, export, and shell commands to the existing pane (which assumes a POSIX shell is running), create a brand-new pane with the correct directory, environment, and shell, then swap it into the position of the original pane and kill the original.

Implementation

Replace builder.py:690-727:

# Before: send_keys cd, export, shell into existing pane
active_pane.send_keys(f"cd {shlex.quote(start_directory)}", enter=True)
for _ekey, _eval in environment.items():
    _here_pane.send_keys(f"export {_ekey}={shlex.quote(str(_eval))}", enter=True)
_here_pane.send_keys(window_shell, enter=True)

With:

# After: create fresh pane with correct state, swap into position
_active_pane = window.active_pane
assert _active_pane is not None

# Build split-window args
split_kwargs: dict[str, t.Any] = {}
if start_directory:
    split_kwargs["start_directory"] = start_directory
if environment:
    split_kwargs["environment"] = environment
if window_shell:
    split_kwargs["shell"] = window_shell

# Create new pane with correct dir/env/shell via tmux primitives
new_pane = window.split(**split_kwargs)

# Swap new pane into the position of the original
new_pane.cmd("swap-pane", "-t", _active_pane.pane_id)

# Kill the original pane (now in the split position)
_active_pane.kill()

How this maps to tmux commands

tmux split-window -c /path/to/dir -e KEY=VALUE [shell_command]
tmux swap-pane -t %original_pane_id
tmux kill-pane -t %original_pane_id

tmux version requirements

  • split-window -c: Available since tmux 1.9
  • split-window -e: Available since tmux 3.2 (tmuxp's minimum)
  • swap-pane: Available since tmux 0.8
  • All within tmuxp's tmux 3.2+ minimum requirement

Comparison with teamocil

teamocil uses send_keys cd for directory and split-window -c for new panes. This approach goes further than teamocil by eliminating even the cd send_keys call.

Trade-offs

Pros:

  • Zero send_keys for infrastructure — no shell assumption at all
  • Works regardless of what's running in the active pane (vim, python, fish)
  • Environment set at pane creation time via -e (tmux 3.2+)
  • Directory set via -c (tmux primitive)
  • Clean pane with fresh shell — no history pollution

Cons:

  • Destroys the user's existing shell state (history, local variables, aliases loaded via .bashrc)
  • Brief visual flash as the swap occurs
  • More complex than Level 1 — uses swap-pane + kill-pane which is unusual in libtmux
  • The existing pane is killed — if the user had unsaved work in a background process, it's gone
  • May need libtmux API additions if split() doesn't support -e kwargs yet

Behavioral change

This changes --here semantics from "modify the existing pane in-place" to "replace the existing pane with a fresh one." Users who rely on existing shell state in the first pane would lose it. This may be acceptable since --here is conceptually "rebuild this window" — but it should be documented.

Tests to add/update

  • Rewrite test_here_mode_provisions_environment to verify env via pane capture (new pane inherits env)
  • Rewrite test_here_mode_start_directory_special_chars to verify cwd without send_keys
  • Add test verifying --here works when active pane runs a non-shell program
  • Add test verifying original pane is replaced (pane count stays at 1)

libtmux prerequisites

Check whether window.split() supports:

  • environment kwarg (maps to -e KEY=VALUE)
  • shell kwarg (maps to the shell/command argument)
  • start_directory kwarg (maps to -c)

If not, these would need to be added to libtmux first.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions