Skip to content
ttak0422Public

About

Tmux, reimagined for text editor lovers

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

pterm built with nix

Persistent terminal sessions for Neovim.

Processes survive Neovim restarts. Terminal rendering is delegated to Neovim's native terminal (jobstart(..., { term = true })) while pterm keeps PTY processes alive.

CLI

# Create a new persistent session (forks into background)
pterm new mysession
pterm new mysession -- /bin/zsh        # custom command

# Attach bridge mode (for terminal clients)
pterm attach mysession

# Attach if exists, otherwise create and attach
pterm open mysession
pterm open mysession -- /bin/zsh

# List active sessions (optionally filter by prefix)
pterm list
pterm list myprefix

# Get socket path for a session
pterm socket mysession

# Redraw terminal (resend snapshot to all clients)
pterm redraw mysession

# Print diagnostic state dump as JSON
pterm dump mysession

# Print the visible screen as plain text
pterm snapshot-text mysession

# Print scrollback + visible screen with ANSI colors/attributes preserved
pterm full-text mysession --ansi

# Print the visible screen with ANSI colors/attributes preserved
pterm snapshot-ansi mysession

# Kill a session
pterm kill mysession

Session names may contain / for hierarchical sessions. Killing a parent session also kills all children. Names must be relative, with no empty, ., .., socket, or cwd components. Session directories below the socket root must not be symbolic links.

pterm new parent
pterm new parent/child
pterm kill parent          # kills parent and parent/child

Neovim Usage

:Pterm new dev      " opens/creates named session
:Pterm new dev zsh  " opens/creates session with custom command
:Pterm list         " list sessions
:Pterm redraw dev   " redraw a session
:Pterm dump dev     " open diagnostic state dump as JSON
:Pterm kill dev     " kill a session

pterm opens a terminal buffer backed by jobstart({ "pterm", "attach", <name> }, { term = true }).

The Lua module also exports functions for programmatic use: open, attach, detach, list, kill, redraw, dump.

Shared-session window size

The most recently focused pterm window chooses the shared session's terminal width and height. Switching between differently sized splits resizes the shell or TUI to the newly active window's text area. Background mirrors do not acquire resize authority merely because their layout changes. Switching back to another Neovim instance reclaims authority when that editor reports FocusGained.

Other windows show the same canonical terminal screen: larger windows have padding, and smaller windows clip the view instead of rewrapping the application. The plugin sends explicit size-control requests and uses a --no-resize bridge so Neovim's automatic largest-window terminal sizing, including delayed bridge startup, cannot overwrite the active window's dimensions. Standalone CLI attachments retain their normal SIGWINCH behavior. Managed Neovim attachments require protocol version 2; restart an older daemon session before using this plugin version. Extremely large windows are capped at the daemon's 512-column, 256-row and 65,536-cell limits.

When resized history is replayed, a scrollback-reset handshake retains one blank sentinel line before the refreshed history. This also supports Neovim 0.11 and older, which cannot clear terminal history with CSI 3 J. Previous history content is replaced rather than duplicated, and the user's scrollback setting is restored.

TUI redraws

Managed Neovim attachments keep the last complete view while an application uses synchronized output (DEC mode 2026), rather than displaying each partial PTY read. A missing end marker is released after a one-second safety timeout (on the next daemon poll), and child exit releases the final view immediately. Each generated view is also wrapped in synchronized output so native terminals that support it, including Neovim 0.12, can paint a whole frame together. Older Neovim versions still receive complete application updates, but do not provide atomic rendering of a view delivered across multiple terminal reads.

Requirements

  • Neovim 0.10+
  • Nix (with flakes enabled)
  • Linux (x86_64-linux, aarch64-linux) or macOS on Apple Silicon (aarch64-darwin)

Intel macOS (x86_64-darwin) is no longer supported. The Nix flake and macOS CI target Apple Silicon only.

Install

This plugin is designed to be installed via Nix flakes. Add pterm as a flake input and include it in your Neovim plugin list.

Flake input

{
  inputs = {
    pterm.url = "github:ttak0422/pterm";
  };
}

Neovim plugin

Add inputs.pterm.packages.${system}.pterm to your Neovim plugin list and call setup().

require("pterm").setup()


-- Default configuration:
require("pterm").setup({
  -- Default shell command
  shell = vim.env.SHELL or "/bin/sh",
  -- Socket directory (nil = let daemon decide)
  socket_dir = nil,
  -- Automatically redraw on BufEnter / TermEnter to recover from rendering
  -- corruption that can occur during mode or window focus switches.
  auto_redraw = true,
  -- Cooldown window in milliseconds for automatic redraws. The first
  -- BufEnter / TermEnter redraw fires immediately; repeated events are
  -- suppressed until the cooldown expires.
  auto_redraw_delay_ms = 1000,
})

Telescope Extension

pterm provides an optional Telescope extension for fuzzy-finding sessions.

-- Call after telescope.setup()
require("telescope").load_extension("pterm")
:Telescope pterm sessions

Each session is shown as <name> (<directory>), where the directory is the basename of the session shell's current working directory. The daemon tracks cd inside the session and refreshes it periodically. Connected sessions are marked with a ✔ prefix. The preview pane shows the tail of each session's current terminal screen, trimmed to the preview width, with colors and attributes preserved as highlighting. If no existing session matches the current Telescope query, pressing Enter creates or opens a session using the prompt text as the session name.

For full-text search across every session's contents — scrollback history included — use:

:Telescope pterm grep

Each non-empty line is shown as <session>: <line>; pressing Enter opens the session containing the selected line. Lines that wrapped in the terminal are re-joined before matching, so text spanning a wrap point stays searchable. Sessions currently showing a full-screen TUI (the alternate screen, e.g. less or htop) contribute only their visible screen, not their shell scrollback. Matching words are highlighted in the result list and preview. The preview also preserves the original terminal colors and attributes, including scrollback and re-joined wrapped lines. The selected line is marked underneath these styles, and matching words are highlighted on top. Existing sessions started by an older daemon remain searchable in plain text; restart them with the updated daemon to enable colored grep previews.

Development

Run the Lua linter from the repository root with the pinned Nix toolchain:

nix develop -c selene lua tests/neovim_regressions.lua tests/neovim_resize_regressions.lua nix/apps/nvim
nix flake check

vim.yml defines the Neovim APIs used here, including argument checks. Extend it when adding API calls. Selene also runs in the pre-commit check; nix flake check includes that check and the mocked headless Neovim plugin regressions. The Rust checks include session/server and CLI integration tests using Unix sockets. The real Neovim/PTY and tig fixtures in tests/neovim_resize_regressions.lua and tests/neovim_tig_regressions.lua are opt-in development tests and are not run by nix flake check.

License

MIT

About

Tmux, reimagined for text editor lovers

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages