Skip to content

feat: open editor in zellij pane - #240

Draft
souhoc wants to merge 1 commit into
RivoLink:mainfrom
souhoc:feat/zellij-in-place-editor
Draft

feat: open editor in zellij pane#240
souhoc wants to merge 1 commit into
RivoLink:mainfrom
souhoc:feat/zellij-in-place-editor

Conversation

@souhoc

@souhoc souhoc commented Aug 10, 2026

Copy link
Copy Markdown

What

Inside a zellij session, Ctrl+E now opens the editor in a zellij pane placed over leaf's own pane (zellij action edit --in-place --line-number <line> <file>) instead of a new tab of the host terminal.

  • is_zellij() — detects the ZELLIJ env var, alongside the existing is_wsl().
  • zellij_edit_command() — builds that command as a RunAndCheck strategy.
  • try_new_tab_command() gains line: usize and zellij: bool; when zellij is set it short-circuits ahead of the emulator match, so zellij wins over kitty / gnome-terminal / Windows Terminal / iTerm2 / Terminal.app.
  • open_in_editor() forwards the visible source line (app.source_line_at(app.scroll())).

Why

detect_terminal_emulator() reads the host terminal's environment (TERM_PROGRAM, KITTY_PID, …), and those variables are still set inside a zellij session. So leaf opened its editor tab in the host terminal — outside the multiplexer, in a window the user had deliberately left behind.

Two deliberate choices:

  • zellij is an injected bool, not a new TerminalEmulator variant. detect_terminal_emulator() also feeds selection_modifier_label(), and the host terminal still owns text selection under zellij. A new variant would have flipped the help-popup hint from option+drag to shift+drag on iTerm2. Passing the flag the way wsl already is keeps that hint correct and keeps the branch unit-testable.
  • RunAndCheck, not SpawnAndAssume. The zellij CLI exits non-zero when it cannot reach the session, so leaf falls through to NeedsSameTerminal and edits inline rather than appearing to do nothing.

Alternative: zellij run instead of zellij action edit

Happy to ship this instead if you prefer it — it is a smaller patch than the one above, and I have no strong preference.

zellij action edit delegates to zellij's own editor, which is where every trade-off below comes from. zellij run takes a command, so leaf's configured editor can be launched directly:

zellij run --in-place --close-on-exit --name "<editor tab title>" -- nano +42 /path/file.md
  • The editor setting and its {$line} / {$path} placeholders apply on this path like they do everywhere else — the string passed to try_new_tab_command() is already expanded.
  • line: usize is then no longer needed at all: it disappears from zellij_edit_command(), try_new_tab_command() and open_in_editor(), along with the second source_line_at() call in runtime/mouse.rs.
  • --name can reuse format_editor_tab_title(), so tab-title-length applies here too. The current patch silently ignores it.
  • EditorFlash::Opened becomes truthful, since the launched binary is the configured one.

The reason it is not the default here: it changes which editor opens for anyone who has set zellij's scrollback_editor. That felt like the maintainers' call rather than mine. Flags verified against zellij 0.44.0 (run -i/--in-place, -n/--name, -c/--close-on-exit).

Trade-offs of the approach in this PR

  • zellij action edit uses zellij's own editor (scrollback_editor, else $EDITOR), so the editor setting and its {$line} / {$path} placeholders do not apply on this path. Forwarding --line-number is what keeps line positioning working there without a placeholder.
  • GUI editors (code, zed, …) are untouched — they never went through the tab path.
  • No opt-out: any leaf running inside zellij gets the pane. Happy to gate it behind a config key if you would prefer that.
  • EditorFlash::Opened still reports leaf's configured editor name, which may differ from what zellij launched. With --in-place leaf's pane is suspended while editing, so the flash is effectively never seen; left alone rather than widening EditorResult for an invisible string.

How tested

  • cargo test — 417 passed, including 4 new tests: the in-place command shape, zellij winning over each host emulator, the visible line being forwarded, and line 0 clamped to 1.
  • cargo clippy --all-targets --all-features -- -D warnings — clean.
  • cargo fmt --all -- --check — clean.
  • cargo build --release — ok.
  • README updated (Open in Editor section).
  • Manually verified on a live, attached zellij 0.44.0 session: Ctrl+E opens the editor in place of leaf's pane as described.

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@RivoLink

Copy link
Copy Markdown
Owner

Hi,

Thank you for this PR and for your interest in the project!

Thanks as well for bringing up this use case. I don't think adding specific support for Zellij is the best long-term solution. I think it would be better to find a cleaner and more general way to support Open in Editor when running inside terminal multiplexers, so we don't have to add specific handling for each multiplexer in the future.

I'll look into this and work on a more general approach, and I'll keep you posted.

Thanks again for the contribution and for raising this!

@RivoLink
RivoLink marked this pull request as draft August 10, 2026 16:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants