diff --git a/CHANGES b/CHANGES index 25cc587db1..3c03028899 100644 --- a/CHANGES +++ b/CHANGES @@ -54,6 +54,16 @@ site's light/dark palette and code styling, replacing the old static ASCII art. with a concept-first overview, and {ref}`examples` shows each pane example as a faithful tmux window layout. +#### Concept-first documentation pass (#1072) + +The docs now open with what each feature *is* before its configuration, in a +consistent second-person voice, with advanced and Python-only material marked +opt-in. New diagrams trace the plugin hook lifecycle, the CLI request flow, the +workspace hierarchy, and what `tmuxp load` does inside an existing session, and +more internal API cross-references resolve to their pages. The {ref}`quickstart` +and {ref}`about-tmux` primer also pick up corrected version requirements, +tooling, and commands. + ## tmuxp 1.73.0 (2026-06-28) tmuxp 1.73.0 makes the workspace build step pluggable and tunable. A workspace can now build through a third-party builder selected by registered entry-point name or Python import path, and a new `workspace_builder_options` catalog controls the pane-readiness wait per workspace. The built-in builder stays the default, so existing workspaces keep working — though the new `pane_readiness: auto` default skips the prompt wait on non-zsh shells. See {ref}`custom-workspace-builders` for the guide. diff --git a/README.md b/README.md index 359ae17202..74befa3c7a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # tmuxp -Session manager for tmux, which allows users to save and load tmux sessions through simple configuration files. Powered by [libtmux](https://github.com/tmux-python/libtmux). +Session manager for tmux. Save and load your tmux sessions through simple configuration files. Powered by [libtmux](https://github.com/tmux-python/libtmux). [![Python Package](https://img.shields.io/pypi/v/tmuxp.svg)](https://pypi.org/project/tmuxp/) [![Docs](https://github.com/tmux-python/tmuxp/workflows/docs/badge.svg)](https://tmuxp.git-pull.com/) @@ -13,7 +13,7 @@ is available on Leanpub and [Amazon Kindle](http://amzn.to/2gPfRhC). Read and browse the book for free [on the web](https://leanpub.com/the-tao-of-tmux/read). -**Have some spare time?** Help us triage and code review and the tracker. See [issue +**Have some spare time?** Help us triage and code-review on the tracker. See [issue #290](https://github.com/tmux-python/tmuxp/discussions/290)! # Installation @@ -21,38 +21,38 @@ web](https://leanpub.com/the-tao-of-tmux/read). pip: ```console -$ pip install --user tmuxp +pip install --user tmuxp ``` If you're managing the project with [uv](https://docs.astral.sh/uv/), add tmuxp as a dependency instead: ```console -$ uv add tmuxp +uv add tmuxp ``` To run tmuxp without installing it globally, similar to `pipx`, invoke it with [uvx](https://docs.astral.sh/uv/guides/tools/): ```console -$ uvx tmuxp +uvx tmuxp ``` Homebrew: ```console -$ brew install tmuxp +brew install tmuxp ``` Debian / ubuntu: ```console -$ sudo apt install tmuxp +sudo apt install tmuxp ``` Nix: ```console -$ [[ -z $(which tmux) ]] && (nix-env -i tmux && nix-env -i tmuxp) || nix-env -i tmuxp +[[ -z $(which tmux) ]] && (nix-env -i tmux && nix-env -i tmuxp) || nix-env -i tmuxp ``` Find the package for your distro on repology: @@ -149,7 +149,7 @@ Load your tmuxp config from anywhere by using the filename, assuming _\~/.config/tmuxp/mysession.yaml_ (or _.json_): ```console -$ tmuxp load mysession +tmuxp load mysession ``` See [author's tmuxp configs](https://github.com/tony/tmuxp-config) and @@ -165,7 +165,7 @@ server, session, and window in [libtmux](https://github.com/tmux-python/libtmux) objects. ```console -$ tmuxp shell +tmuxp shell (Pdb) server @@ -190,10 +190,10 @@ Supports [PEP (including `PYTHONBREAKPOINT`). Also supports direct commands via `-c`: ```console -$ tmuxp shell -c 'print(window.name)' +tmuxp shell -c 'print(window.name)' my_window -$ tmuxp shell -c 'print(window.name.upper())' +tmuxp shell -c 'print(window.name.upper())' MY_WINDOW ``` @@ -220,7 +220,7 @@ You can also load sessions in the background by passing `-d` flag Snapshot your tmux layout, pane paths, and window/session names. ```console -$ tmuxp freeze session-name +tmuxp freeze session-name ``` See more about [freezing @@ -231,7 +231,7 @@ tmux](https://tmuxp.git-pull.com/cli/freeze/) sessions. Convert a session file from yaml to json and vice versa. ```console -$ tmuxp convert filename +tmuxp convert filename ``` This will prompt you for confirmation and shows you the new file that is @@ -240,8 +240,8 @@ going to be written. You can auto confirm the prompt. In this case no preview will be shown. ```console -$ tmuxp convert -y filename -$ tmuxp convert --yes filename +tmuxp convert -y filename +tmuxp convert --yes filename ``` # Plugin System @@ -255,13 +255,13 @@ The `load` command provides a way to log output to a log file for debugging purposes. ```console -$ tmuxp load --log-file . +tmuxp load --log-file . ``` Collect system info to submit with a Github issue: ```console -$ tmuxp debug-info +tmuxp debug-info ------------------ environment: system: Linux diff --git a/docs/AGENTS.md b/docs/AGENTS.md index 16961d0df4..a84054248b 100644 --- a/docs/AGENTS.md +++ b/docs/AGENTS.md @@ -68,6 +68,25 @@ by shrinking audience, an honest trade-off on the prompt wait, and precise reference tables left precise. Read it before reshaping another page. +## Diagrams and reference pages + +Two mechanical conventions, separate from voice: + +- **Mermaid diagrams** render to inline SVG at build time (see + `docs/_ext`). Tag any node whose label is a command, code identifier, + config key, or other symbol with `:::cmd` so it renders monospace — + the way that text reads as code inline; leave prose and concept nodes + unstyled. Prefer top-to-bottom (`flowchart TD`); wide left-to-right + charts don't scale on narrow viewports. `docs/configuration/workspace-builders.md` + is the reference. +- **Internal API pages** document a module with an `{eval-rst}` block + wrapping `.. automodule:: ` (with `:members:`), the way the + existing `docs/internals/api/**` pages do. A bare `.. py:module::` + registers a cross-reference target but renders an empty page — reach + for it only to add a *package* target to an index page that already + carries its own content (grids, prose), where `automodule` would + duplicate members documented on the leaf pages. + ## Before you commit - Does the page open with what the feature *is*, or with how to @@ -78,3 +97,5 @@ page. concept instead? - Are the advanced and Python-only parts clearly marked opt-in? - Did you leave every table, error string, and cross-reference exact? +- Are diagram command/symbol nodes tagged `:::cmd`, and is the chart + vertical unless it has a reason to be wide? diff --git a/docs/about_tmux.md b/docs/about_tmux.md index 2db7b3c4d7..7f06817921 100644 --- a/docs/about_tmux.md +++ b/docs/about_tmux.md @@ -38,29 +38,23 @@ inside the text dimension. Inside tmux you can: | ----------- | ------------------------------- | ------------------------------------- | | Multiplexer | Multi-tasking | Multiple applications simultaneously. | | Session | Desktop | Applications are visible here | -| Window | Virtual Desktop or applications | A desktop that stores it own screen | +| Window | Virtual Desktop or applications | A desktop that stores its own screen | | Pane | Application | Performs operations | -```{eval-rst} -.. aafig:: - :textual: - - +----------------------------------------------------------------+ - | +--------+--------+ +-----------------+ +-----------------+ | - | | pane | pane | | pane | | pane | | - | | | | | | | | | - | | | | | | | | | - | +--------+--------+ | | +-----------------+ | - | | pane | pane | | | | pane | | - | | | | | | | | | - | | | | | | | | | - | +--------+--------+ +-----------------+ +-----------------+ | - | | window | | window | | window | | - | \--------+--------/ \-----------------/ \-----------------/ | - +----------------------------------------------------------------+ - | session | - \----------------------------------------------------------------/ -``` +:::{mermaid} +:caption: A session holds windows; each window holds one or more panes. + +flowchart TD + session["session"] --> w1["window"] + session --> w2["window"] + session --> w3["window"] + w1 --> p1["pane"] + w1 --> p2["pane"] + w1 --> p3["pane"] + w1 --> p4["pane"] + w3 --> p5["pane"] + w3 --> p6["pane"] +::: - 1 {term}`Server`. @@ -84,66 +78,36 @@ splitting up 1 terminal into multiple. One screen can be used to edit a file, and another may be used to `$ tail -F` a logfile. -```{eval-rst} -.. aafig:: - - +--------+--------+ - | $ bash | $ bash | - | | | - | | | - | | | - | | | - | | | - | | | - +--------+--------+ -``` +:::{tmux-layout} +:size: 40x10 +:layout: even-horizontal -```{eval-rst} -.. aafig:: - - +--------+--------+ - | $ bash | $ bash | - | | | - | | | - +--------+--------+ - | $ vim | $ bash | - | | | - | | | - +--------+--------+ -``` +bash +--- +bash +::: + +:::{tmux-layout} +:size: 40x12 +:layout: tiled + +bash +--- +bash +--- +vim +--- +bash +::: tmux supports as many terminals as you want. -```{eval-rst} -.. aafig:: - :textual: - - +---------+---------+ - | $ bash | $ bash | - | | | - | | | /-----------------\ - +---------+---------+ --> |'switch-window 2'| - | $ bash | $ bash | \-----------------/ - | | | | - | | | | - +---------+---------+ | - | '1:sys* 2:vim' | | - +-------------------+ | - /------------------------/ - | - v - +---------+---------+ - | $ vim | - | | - | | - +-------------------+ - | $ bash | $ bash | - | | | - | | | - +-------------------+ - | '1:sys 2:vim*' | - +-------------------+ -``` +:::{mermaid} +:caption: Switch between the windows in a session. + +flowchart TD + w1["window 1 · sys (active)"] -->|"switch-window 2"| w2["window 2 · vim (active)"] +::: You can switch between the windows you create. @@ -152,57 +116,13 @@ You can switch between the windows you create. You can leave tmux and all applications running (detach), log out, make a sandwich, and re-(attach), all applications are still running! -```{eval-rst} -.. aafig:: - :textual: - - +--------+--------+ - | $ bash | $ bash | - | | | - | | | /------------\ - +--------+--------+ --> | detach | - | $ vim | $ bash | | 'Ctrl-b b' | - | | | \------------/ - | | | | - +--------+--------+ | - /------------------/ - | - v - +-----------------------+ - | $ [screen detached] | - | | - | | - | | - | | - | | - | | - +-----------------------+ - v - | - v - +-----------------------+ - | $ [screen detached] | - | $ tmux attach | - | | /------------\ - | | --> | attaching | - | | \------------/ - | | | - | | | - +-----------------------+ | - | - /---------------------------/ - | - v - +--------+--------+ - | $ bash | $ bash | - | | | - | | | - +--------+--------+ - | $ vim | $ bash | - | | | - | | | - +--------+--------+ -``` +:::{mermaid} +:caption: Detach, leave everything running, and reattach later. + +flowchart TD + running["session running"] -->|"detach · Prefix d"| detached["screen detached — apps keep running"] + detached -->|"tmux attach"| running +::: ### Manage workflow diff --git a/docs/cli/completion.md b/docs/cli/completion.md index 410aed4ee3..756f4921b8 100644 --- a/docs/cli/completion.md +++ b/docs/cli/completion.md @@ -6,6 +6,11 @@ # Completions +Shell completion teaches your shell to finish `tmuxp` subcommands and flags when +you press Tab. It's optional, and how you set it up depends on your tmuxp +version — 1.17 and newer use shtab, while 1.1 through 1.16 used click. Pick the +section that matches yours. + ## tmuxp 1.17+ (experimental) ```{note} diff --git a/docs/cli/exit-codes.md b/docs/cli/exit-codes.md index e8a80e892d..42486e21cf 100644 --- a/docs/cli/exit-codes.md +++ b/docs/cli/exit-codes.md @@ -10,7 +10,10 @@ tmuxp uses standard exit codes for scripting and automation. | `1` | General error (config validation, tmux command failure) | | `2` | Usage error (invalid arguments, missing required options) | -## Usage in Scripts +## Usage in scripts + +Because the exit code is meaningful, a script can check it and react. Test it +explicitly with `$?`: ```bash #!/bin/bash @@ -21,6 +24,8 @@ if [ $? -ne 0 ]; then fi ``` +Or short-circuit with `||` to handle the failure inline: + ```bash #!/bin/bash tmuxp load -d my-workspace.yaml || { diff --git a/docs/cli/freeze.md b/docs/cli/freeze.md index 0490dd5abf..6f3f8c55ee 100644 --- a/docs/cli/freeze.md +++ b/docs/cli/freeze.md @@ -38,7 +38,7 @@ $ tmuxp freeze --force [session_name] ## Output format -Tmuxp will offer to save your session state to `.json` or `.yaml`. +tmuxp will offer to save your session state to `.json` or `.yaml`. If no session is specified, it will default to the attached session. diff --git a/docs/cli/load.md b/docs/cli/load.md index 8be9178f29..c0cbfdb8ce 100644 --- a/docs/cli/load.md +++ b/docs/cli/load.md @@ -6,7 +6,10 @@ # tmuxp load -Load tmux sessions from workspace configuration files. This is the primary command for starting sessions from YAML or JSON configurations. +Load a tmux session from a workspace file — this is the command you reach for +most. Point it at a project directory or a YAML/JSON file and tmuxp builds the +session and attaches you to it; the flags below are for the cases where you want +something other than that default. ## Command @@ -88,9 +91,9 @@ $ tmuxp load [filename] ## Inside sessions -If you try to load a workspace file from within a tmux session, it will ask you -if you want to load and attach to the new session, or just load detached. -You can also load a workspace file and append the windows to the current active session. +When you load a workspace from _inside_ an existing tmux client, tmuxp asks what +you want rather than guessing — switch to the freshly built session, append its +windows to the session you're in, or build it detached and stay put: ``` Already inside TMUX, switch to session? yes/no @@ -98,6 +101,16 @@ Or (a)ppend windows in the current active session? [y/n/a]: ``` +:::{mermaid} +:caption: Loading from inside an existing tmux client. + +flowchart TD + load["tmuxp load"]:::cmd --> q{"y / n / a ?"} + q -->|yes| switch["build the new session, switch the client to it"] + q -->|a| append["append its windows to the current session"] + q -->|no| detached["build it detached, stay where you are"] +::: + ## Options All of these options can be preselected to skip the prompt: diff --git a/docs/cli/recipes.md b/docs/cli/recipes.md index fdbdd7dad9..61836e87a7 100644 --- a/docs/cli/recipes.md +++ b/docs/cli/recipes.md @@ -2,7 +2,8 @@ # Recipes -Copy-pasteable command invocations for common tasks. +Copy-pasteable command invocations for common tasks. Each command has a full +reference — with every flag — under {ref}`cli`. ## Load a workspace diff --git a/docs/cli/shell.md b/docs/cli/shell.md index 586c305e54..ceff183a82 100644 --- a/docs/cli/shell.md +++ b/docs/cli/shell.md @@ -4,7 +4,10 @@ # tmuxp shell -Launch an interactive Python shell with [libtmux] objects pre-loaded. Similar to Django's shell command, this provides quick access to your tmux server, sessions, windows, and panes for scripting and debugging. +Launch an interactive Python shell with [libtmux] objects pre-loaded. Like +Django's `shell` command, it hands you the current tmux server, sessions, +windows, and panes already wired up, so you can poke at a live session or +prototype a script without writing boilerplate. ## Command @@ -16,26 +19,12 @@ Launch an interactive Python shell with [libtmux] objects pre-loaded. Similar to :path: shell ``` -## Directly enter commands - -```console -$ tmuxp shell -c 'python code' -``` - -```{image} ../_static/tmuxp-shell.gif -:width: 878 -:height: 109 -:loading: lazy -``` - ## Interactive usage -Launch into a Python console with [libtmux] objects. Compare to django's shell. - -Automatically preloads current tmux {class}`server `, -{class}`session `, {class}`window ` -{class}`pane `. Pass additional arguments to select a -specific one of your choice: +Run `tmuxp shell` to drop into a Python console with the current tmux +{class}`server `, {class}`session `, +{class}`window `, and {class}`pane ` already bound. +Pass arguments to select a specific one: ```console (Pdb) server @@ -56,80 +45,81 @@ Window(@3 1:your_window, Session($1 your_project)) Pane(%6 Window(@3 1:your_window, Session($1 your_project))) ``` -## Debugger integration +## Running code directly -Supports [PEP 553][pep 553]'s `PYTHONBREAKPOINT` and -compatible debuggers, for instance [ipdb][ipdb]: +Pass `-c` to run a snippet and exit, much like `python -c`: ```console -$ pip install --user ipdb +$ tmuxp shell -c 'python code' ``` -Inside a [uv](https://docs.astral.sh/uv/getting-started/features/#python-versions)-managed project you can add `ipdb` as a development dependency: - -```console -$ uv add --dev ipdb +```{image} ../_static/tmuxp-shell.gif +:width: 878 +:height: 109 +:loading: lazy ``` -For a pipx-style ad hoc install, run it through [uvx](https://docs.astral.sh/uv/guides/tools/): +The same objects are in scope. Name a server, then a window, to narrow what the +snippet sees: ```console -$ uvx --from ipdb ipdb3 --help +$ tmuxp shell -c 'print(session.name); print(window.name)' +my_server +my_window ``` ```console -$ env PYTHONBREAKPOINT=ipdb.set_trace tmuxp shell +$ tmuxp shell my_server my_window -c 'print(window.name.upper())' +MY_WINDOW ``` -## Code execution - -You can also pass in python code directly, similar to `python -c`, do -this via `tmuxp -c`: +Inside a tmux pane — or attached to the default server — the pane is in scope +too: ```console -$ tmuxp shell -c 'print(session.name); print(window.name)' -my_server +$ tmuxp shell -c 'print(pane.id); print(pane.window.name)' +%2 my_window ``` -```console -$ tmuxp shell my_server -c 'print(session.name); print(window.name)' -my_server -my_window -``` +## Debugger integration + +`tmuxp shell` supports [PEP 553][pep 553]'s `PYTHONBREAKPOINT` and compatible +debuggers, such as [ipdb][ipdb]: ```console -$ tmuxp shell my_server my_window -c 'print(session.name); print(window.name)' -my_server -my_window +$ pip install --user ipdb ``` +Inside a [uv](https://docs.astral.sh/uv/getting-started/features/#python-versions)-managed +project, add `ipdb` as a development dependency: + ```console -$ tmuxp shell my_server my_window -c 'print(window.name.upper())' -MY_WINDOW +$ uv add --dev ipdb ``` -Assuming inside a tmux pane or one is attached on default server: +For a pipx-style ad hoc install, run it through [uvx](https://docs.astral.sh/uv/guides/tools/): ```console -$ tmuxp shell -c 'print(pane.id); print(pane.window.name)' -%2 -my_window +$ uvx --from ipdb ipdb3 --help ``` -[pep 553]: https://www.python.org/dev/peps/pep-0553/ -[ipdb]: https://pypi.org/project/ipdb/ -[libtmux]: https://libtmux.git-pull.com +```console +$ env PYTHONBREAKPOINT=ipdb.set_trace tmuxp shell +``` ## Shell detection -`tmuxp shell` detects the richest shell available in your _site packages_, you can also pick your shell via args: +`tmuxp shell` drops into the richest shell available in your _site packages_. Pick +one yourself with a flag: + +- `--pdb`: plain `breakpoint()` (python 3.7+) or `pdb.set_trace` +- `--code`: drop into `code.interact`, accepts `--use-pythonrc` +- `--bpython`: drop into bpython +- `--ipython`: drop into ipython +- `--ptpython`: drop into ptpython, accepts `--use-vi-mode` +- `--ptipython`: drop into ipython + ptpython, accepts `--use-vi-mode` -- `--pdb`: Use plain old `breakpoint()` (python 3.7+) or - `pdb.set_trace` -- `--code`: Drop into `code.interact`, accepts `--use-pythonrc` -- `--bpython`: Drop into bpython -- `--ipython`: Drop into ipython -- `--ptpython`: Drop into ptpython, accepts `--use-vi-mode` -- `--ptipython`: Drop into ipython + ptpython, accepts - `--use-vi-mode` +[pep 553]: https://www.python.org/dev/peps/pep-0553/ +[ipdb]: https://pypi.org/project/ipdb/ +[libtmux]: https://libtmux.git-pull.com diff --git a/docs/configuration/environmental-variables.md b/docs/configuration/environmental-variables.md index 73c0d1e373..86e54aa1ab 100644 --- a/docs/configuration/environmental-variables.md +++ b/docs/configuration/environmental-variables.md @@ -2,6 +2,13 @@ # Environmental variables +These environment variables tune how tmuxp finds your workspaces and how much it +shows you while loading — set from your shell, outside any workspace file. You +rarely need them: tmuxp works out of the box. Reach for one when you want to +point tmuxp at a different config directory, quiet or reshape the load progress +display, or work around a rare tmux quirk. The progress variables each mirror a +`tmuxp load` flag, noted alongside the variable. + (TMUXP_CONFIGDIR)= ## `TMUXP_CONFIGDIR` diff --git a/docs/configuration/examples.md b/docs/configuration/examples.md index 9ab5777308..98b917f4f4 100644 --- a/docs/configuration/examples.md +++ b/docs/configuration/examples.md @@ -2,10 +2,16 @@ # Examples +This is a gallery of working workspace files — each a small, complete YAML (with +its JSON twin) you can copy, load, and adapt. They run from the simplest +two-pane split up to a full development environment, and many preview the panes +they build. Skim for the feature you need — inline shorthand, blank panes, +environment variables, pane shells, focus, history, timed pauses — and lift the +snippet. + ## Short hand / inline style -tmuxp has a short-hand syntax for those who wish to keep their workspace -punctual. +tmuxp has a short-hand syntax for those who like to keep a workspace concise. :::{tmux-layout} :size: 36x12 @@ -628,7 +634,7 @@ A successful script will exit with a status of `0`. Important: the script file must be chmod executable `+x` or `755`. -Run a python script (and check for it's return code), the script is +Run a python script (and check for its return code), the script is _relative to the `.tmuxp.yaml`'s root_ (Windows and panes omitted in this example): diff --git a/docs/configuration/index.md b/docs/configuration/index.md index a3e82e39c8..8cee308410 100644 --- a/docs/configuration/index.md +++ b/docs/configuration/index.md @@ -37,7 +37,7 @@ Select a builder and tune pane readiness. tmuxp loads your terminal workspace into tmux using workspace files. -The workspace file can be JSON or YAML. It's declarative style resembles tmux's object hierarchy: session, window and panes. +The workspace file can be JSON or YAML. Its declarative style resembles tmux's object hierarchy: session, window, and panes. ## Launching your session @@ -62,6 +62,18 @@ tmuxp will offer to assist when: 3. A list of _panes_ for each window 4. A list of _commands_ for each pane +:::{mermaid} +:caption: A workspace file mirrors tmux's own hierarchy. + +flowchart TD + session["session_name"]:::cmd --> w1["window"] + session --> w2["window"] + w1 --> p1["pane"] + w1 --> p2["pane"] + p1 --> c1["shell_command"]:::cmd + p1 --> c2["shell_command"]:::cmd +::: + ````{tab} Basics ```yaml diff --git a/docs/configuration/top-level.md b/docs/configuration/top-level.md index b8e480522e..845ee79b9c 100644 --- a/docs/configuration/top-level.md +++ b/docs/configuration/top-level.md @@ -3,48 +3,48 @@ # Top-level configuration -## `session_name` - -Used for: - -- tmux session name -- checking for existing sessions - -Notes: - -- Session names may differ from workspace filename. +Top-level keys describe the session as a whole — its name, where it starts, +the tmux options it sets — and sit above the `windows` and `panes` that fill +it. Only `session_name` is required; leave the rest out and a workspace with +just a name and a list of windows loads fine. This page covers `session_name` +and the keys for choosing a workspace builder. For the full set of session, +window, and pane keys, work through {ref}`examples`. - e.g. _apple.yaml_: +## `session_name` - ```yaml - session_name: banana - windows: - - panes: - - - ``` +The name tmux gives the session — and the name tmuxp checks against when it +decides whether that session is already running. It need not match the +workspace filename. - Load detached: +For example, _apple.yaml_: - ```console - $ tmuxp load ./apple.yaml -d - ``` +```yaml +session_name: banana +windows: + - panes: + - +``` - Above: +Load it detached: - - tmuxp loads a file named _apple.yaml_ from the current directory. - - tmuxp built a tmux session called _banana_. - - `-d` means _detached_, loading in background. +```console +$ tmuxp load ./apple.yaml -d +``` - ```console - $ tmux attach -t banana - ``` +tmuxp reads _apple.yaml_ from the current directory and builds a tmux session +called _banana_ in the background — `-d` is detached. Attach to it with tmux +directly: - Above: Use `tmux` directly to attach _banana_. +```console +$ tmux attach -t banana +``` ## Workspace builder keys -A workspace file can also select a custom builder and tune builder behavior with +A workspace file can also choose a custom builder and tune its behavior with `workspace_builder`, `workspace_builder_paths`, and `workspace_builder_options`. +Most workspaces never set these — leave them out and you get tmuxp's built-in +classic builder. ```{seealso} {ref}`workspace-builders` diff --git a/docs/glossary.md b/docs/glossary.md index 02375f742d..4ad1f3a56e 100644 --- a/docs/glossary.md +++ b/docs/glossary.md @@ -19,17 +19,14 @@ ConfigReader dataclasses) Server - Tmux runs in the background of your system as a process. + tmux runs in the background of your system as a process. - The server holds multiple {term}`Session`. By default, tmux - automatically starts the server the first time ``$ tmux`` is ran. + A server holds one or more {term}`Session`. tmux starts the server + automatically the first time ``$ tmux`` runs, if it isn't already + running. - A server contains {term}`session`'s. - - tmux starts the server automatically if it's not running. - - Advanced cases: multiple can be run by specifying - ``[-L socket-name]`` and ``[-S socket-path]``. + Advanced: you can run more than one by specifying + ``[-L socket-name]`` or ``[-S socket-path]``. Client Attaches to a tmux {term}`server`. When you use tmux through CLI, @@ -51,7 +48,7 @@ Window Can have 1 or more {term}`pane`. - Panes can be organized with a layouts. + Panes can be organized with layouts. Windows can have names. diff --git a/docs/internals/api/cli/formatter.md b/docs/internals/api/cli/formatter.md new file mode 100644 index 0000000000..7aa37596c5 --- /dev/null +++ b/docs/internals/api/cli/formatter.md @@ -0,0 +1,8 @@ +# tmuxp formatter - `tmuxp.cli._formatter` + +```{eval-rst} +.. automodule:: tmuxp.cli._formatter + :members: + :show-inheritance: + :undoc-members: +``` diff --git a/docs/internals/api/cli/index.md b/docs/internals/api/cli/index.md index 1381fbc90f..851f620586 100644 --- a/docs/internals/api/cli/index.md +++ b/docs/internals/api/cli/index.md @@ -13,6 +13,7 @@ convert debug_info edit freeze +formatter import_config load ls diff --git a/docs/internals/api/index.md b/docs/internals/api/index.md index 2debbe3f26..314e57e9e9 100644 --- a/docs/internals/api/index.md +++ b/docs/internals/api/index.md @@ -2,12 +2,77 @@ # API Reference +This is the internal Python API — the modules tmuxp's CLI is built from, +documented for contributors and plugin authors. Everyday use goes through the +{ref}`CLI `; to drive tmux from Python directly, reach for libtmux instead. + :::{seealso} See {ref}`libtmux's API ` and {ref}`Quickstart ` to see how you can control tmux via python API calls. ::: +::::{grid} 1 2 3 3 +:gutter: 2 2 3 3 + +:::{grid-item-card} Workspace +:link: workspace/index +:link-type: doc +Finding, loading, building, and freezing sessions. +::: + +:::{grid-item-card} CLI modules +:link: cli/index +:link-type: doc +The argparse commands behind each subcommand. +::: + +:::{grid-item-card} Plugin API +:link: plugin +:link-type: doc +The TmuxpPlugin base class and its hooks. +::: + +:::{grid-item-card} Internal helpers +:link: _internal/index +:link-type: doc +Config reader, colors, and private path helpers. +::: + +:::{grid-item-card} Exceptions +:link: exc +:link-type: doc +The TmuxpException hierarchy. +::: + +:::{grid-item-card} Logging +:link: log +:link-type: doc +Loggers and user-facing echo helpers. +::: + +:::{grid-item-card} Shell +:link: shell +:link-type: doc +Internals of the interactive shell launcher. +::: + +:::{grid-item-card} Utilities +:link: util +:link-type: doc +Assorted helpers used across tmuxp. +::: + +:::{grid-item-card} Types +:link: types +:link-type: doc +Shared type definitions. +::: + +:::: + ```{toctree} +:hidden: + _internal/index cli/index workspace/index diff --git a/docs/internals/api/workspace/builder/index.md b/docs/internals/api/workspace/builder/index.md index 0b3ddbfabe..723e2abf33 100644 --- a/docs/internals/api/workspace/builder/index.md +++ b/docs/internals/api/workspace/builder/index.md @@ -1,5 +1,9 @@ # Builder - `tmuxp.workspace.builder` +```{eval-rst} +.. py:module:: tmuxp.workspace.builder +``` + `tmuxp.workspace.builder` is a package. The classic, default builder lives in {mod}`tmuxp.workspace.builder.classic`; the public contract and the selection machinery live alongside it. diff --git a/docs/internals/architecture.md b/docs/internals/architecture.md index 7a104dbfea..90406a33be 100644 --- a/docs/internals/architecture.md +++ b/docs/internals/architecture.md @@ -1,27 +1,32 @@ # Architecture -How the tmuxp CLI dispatches commands to the underlying library. - -## Request Flow - -``` -tmuxp CLI (argparse) - │ - ├── tmuxp load ──→ workspace.loader ──→ workspace.builder ──→ libtmux - ├── tmuxp freeze ──→ workspace.freezer ──→ libtmux - ├── tmuxp convert ──→ _internal.config_reader - ├── tmuxp shell ──→ libtmux (interactive) - └── tmuxp ls/search ──→ workspace.finders -``` +This page traces how a tmuxp command travels from the CLI down to libtmux, the +library that does the actual tmux work. Each subcommand takes its own short path +through one or two workspace modules: + +:::{mermaid} +:caption: How each tmuxp subcommand reaches libtmux. + +flowchart LR + cli["tmuxp CLI (argparse)"] + cli -->|load| loader["workspace.loader"]:::cmd + loader --> builder["workspace.builder"]:::cmd + builder --> libtmux["libtmux"]:::cmd + cli -->|freeze| freezer["workspace.freezer"]:::cmd + freezer --> libtmux + cli -->|convert| reader["_internal.config_reader"]:::cmd + cli -->|shell| interactive["libtmux (interactive)"] + cli -->|"ls / search"| finders["workspace.finders"]:::cmd +::: ## Key Components ### CLI Layer (`tmuxp.cli`) The CLI uses Python's `argparse` with a custom formatter ({mod}`tmuxp.cli._formatter`). -Each subcommand lives in its own module under `tmuxp.cli`. +Each subcommand lives in its own module under {mod}`tmuxp.cli`. -The entry point is `tmuxp.cli.cli()`, registered as a console script in `pyproject.toml`. +The entry point is {func}`tmuxp.cli.cli`, registered as a console script in `pyproject.toml`. ### Workspace Layer (`tmuxp.workspace`) @@ -35,5 +40,6 @@ The workspace layer handles configuration lifecycle: ### Library Layer (libtmux) tmuxp delegates all tmux operations to [libtmux](https://libtmux.git-pull.com/). -The `WorkspaceBuilder` creates libtmux `Server`, `Session`, `Window`, and `Pane` -objects to construct the requested workspace. +The {class}`~tmuxp.workspace.builder.classic.ClassicWorkspaceBuilder` creates +libtmux {class}`~libtmux.Server`, {class}`~libtmux.Session`, {class}`~libtmux.Window`, +and {class}`~libtmux.Pane` objects to construct the requested workspace. diff --git a/docs/project/contributing.md b/docs/project/contributing.md index cceb9eb61e..446340079b 100644 --- a/docs/project/contributing.md +++ b/docs/project/contributing.md @@ -2,16 +2,9 @@ # Developing and Testing -```{eval-rst} -.. todo:: - link to sliderepl or ipython notebook slides -``` - -Our tests are inside `tests/`. Tests are implemented using -[pytest]. - -`make test` will create a tmux server on a separate `socket_name` -using `$ tmux -L test_case`. +The tests live in `tests/`, written with [pytest]. They run against a real tmux +server on a separate socket (`$ tmux -L test_case`), so they never disturb your +own sessions. [pytest]: http://pytest.org/ @@ -21,7 +14,7 @@ using `$ tmux -L test_case`. ### Get the source -To begin developing, check out the code from github: +Check out the code from GitHub: ```console $ git clone git@github.com:tmux-python/tmuxp.git @@ -33,201 +26,152 @@ $ cd tmuxp ### Bootstrap -The easiest way to configure a dev environment is through [uv]. This -automatically will manage virtualenv and python dependencies for tmuxp. -For information on installing uv visit the [uv's documentation]. - -To begin developing, check out the code from github: +The easiest way to set up a dev environment is with [uv], which manages the +virtualenv and Python dependencies for you. (See [uv's documentation] to install +uv itself.) -```console -$ git clone git@github.com:tmux-python/tmuxp.git -``` - -```console -$ cd tmuxp -``` - -You can create a virtualenv, and install all of the locked -packages as listed in uv.lock: +Create the virtualenv and install everything locked in `uv.lock`: ```console $ uv sync --all-extras --dev ``` -If you ever need to update packages during your development session, the -following command can be used to update all packages as per uv settings: +To refresh those packages later: ```console $ uv sync --all-extras --dev --upgrade ``` -Then before any python command in tty / terminal session, run with: +Then prefix any Python command with `uv run`: ```console $ uv run [command] ``` -That is it! You are now ready to code! +That's it — you're ready to code. [uv]: https://github.com/astral-sh/uv [uv's documentation]: https://docs.astral.sh/uv -### Advanced: Manual virtualenv +### Advanced: manual virtualenv -Now create a virtualenv, if you don't know how to, you can create a -virtualenv with: +Prefer to manage the virtualenv yourself? Create one: ```console $ virtualenv .venv ``` -Then activate it to your current tty / terminal session with: +Activate it in your current shell: ```console $ source .venv/bin/activate ``` -Good! Now let's run this: +Install tmuxp in editable mode, so your edits take effect immediately: ```console $ pip install -e . ``` -This has `pip`, a python package manager install the python package -in the current directory. `-e` means `--editable`, which means you can -adjust the code and the installed software will reflect the changes. - -When you manage dependencies with -[uv](https://docs.astral.sh/uv/getting-started/features/#python-versions), -add the checkout as an editable development dependency instead: +With a uv-managed project, add the checkout as an editable dev dependency +instead: ```console $ uv add --dev --editable . ``` -Prefer a one-off, pipx-style execution while you hack? Call tmuxp via -[uvx](https://docs.astral.sh/uv/guides/tools/): +Prefer a one-off, pipx-style run while you hack? Call tmuxp through [uvx]: ```console $ uvx tmuxp ``` -```console -$ tmuxp -``` +[uvx]: https://docs.astral.sh/uv/guides/tools/ -## Test Runner +## Test runner -[pytest] is used for tests. - -As you've seen above, the `tmuxp` command will now be available to you, -since you are in the virtual environment, your `PATH` environment was -updated to include a special version of `python` inside your `.venv` -folder with its own packages. +[pytest] runs the tests. Inside the virtualenv, the `tmuxp` command and a +project-local `python` are already on your `PATH`. ### Rerun on file change -via [pytest-watcher] (works out of the box): - -```console -$ make start -``` - -via [`entr(1)`] (requires installation): +Watch files and re-run tests on every save, via [pytest-watcher]: ```console -$ make watch_test +$ just start ``` [pytest-watcher]: https://github.com/olzhasar/pytest-watcher -### Manual (just the command, please) +### Manual ```console $ uv run py.test ``` -or: +Or: ```console -$ make test +$ just test ``` ### pytest options -`PYTEST_ADDOPTS` can be set in the commands below. For more -information read [docs.pytest.com] for the latest documentation. +Pass extra arguments through `PYTEST_ADDOPTS`. See the [pytest usage docs] for +everything it accepts. -[docs.pytest.com]: https://docs.pytest.org/ +[pytest usage docs]: https://docs.pytest.org/ Verbose: ```console -$ env PYTEST_ADDOPTS="-verbose" make start +$ env PYTEST_ADDOPTS="--verbose" just start ``` Pick a file: ```console -$ env PYTEST_ADDOPTS="tests/workspace/test_builder.py" uv run make start +$ env PYTEST_ADDOPTS="tests/workspace/test_builder.py" just start ``` -Drop into `test_automatic_rename_option()` in `tests/workspace/test_builder.py`: - -```console -$ env PYTEST_ADDOPTS="-s -x -vv tests/workspace/test_builder.py" \ - uv run make start -``` - -Drop into `test_automatic_rename_option()` in `tests/workspace/test_builder.py` and stop on first error: +Drop into a single test and stop on the first error: ```console $ env PYTEST_ADDOPTS="-s -x -vv tests/workspace/test_builder.py::test_automatic_rename_option" \ - uv run make start + just start ``` -Drop into `pdb` on first error: +Drop into `pdb` on the first error: ```console -$ env PYTEST_ADDOPTS="-x -s --pdb" make start +$ env PYTEST_ADDOPTS="-x -s --pdb" just start ``` -If you have [ipython] installed: +With [ipython] installed: ```console -$ env PYTEST_ADDOPTS="--pdbcls=IPython.terminal.debugger:TerminalPdb" make start +$ env PYTEST_ADDOPTS="--pdbcls=IPython.terminal.debugger:TerminalPdb" just start ``` [ipython]: https://ipython.org/ -```console -$ make test -``` - -You probably didn't see anything but tests scroll by. - -If you found a problem or are trying to write a test, you can file an -[issue on github]. - (test-specific-tests)= ### Manual invocation -Test only a file: +Test a single file: ```console $ py.test tests/test_config.py ``` -will test the `tests/test_config.py` tests. +A single test inside it: ```console $ py.test tests/test_config.py::test_export_json ``` -tests `test_export_json` inside of `tests/test_config.py`. - -Multiple can be separated by spaces: +Several at once, space-separated: ```console $ py.test tests/test_{window,pane}.py tests/test_config.py::test_export_json @@ -237,249 +181,117 @@ $ py.test tests/test_{window,pane}.py tests/test_config.py::test_export_json ### Visual testing -You can watch tmux testsuite build sessions visually by keeping a client -open in a separate terminal. - -Create two terminals: - -- Terminal 1: `$ tmux -L test_case` - -- Terminal 2: `$ cd` into the tmuxp project and into the - `virtualenv` if you are using one, see details on installing the dev - version of tmuxp above. Then: - - ```console - $ py.test tests/workspace/test_builder.py - ``` - -Terminal 1 should have flickered and built the session before your eyes. -tmuxp hides this building from normal users. - -### Run tests on save (old method) - -You can re-run tests automatically on file edit. - -:::{note} - -This requires [`entr(1)`]. +You can watch the suite build sessions in real time by keeping a client open in +a second terminal. -::: - -Install [entr]. Packages are available on most Linux and BSD -variants, including Debian, Ubuntu, FreeBSD, OS X. - -To run all tests upon editing any `.py` file: +Terminal 1 — start a server on the test socket: ```console -$ make watch_test +$ tmux -L test_case ``` -You can also re-run a specific test file or any other [py.test usage -argument]: +Terminal 2 — from the tmuxp checkout (and your virtualenv, if you use one), run +the builder tests: ```console -$ make watch_test test=tests/test_config.py +$ py.test tests/workspace/test_builder.py ``` -```console -$ make watch_test test='-x tests/test_config.py tests/test_util.py' -``` +Terminal 1 flickers as sessions build before your eyes — the building tmuxp +normally hides from users. ### Testing options -`RETRY_TIMEOUT_SECONDS` can be toggled if certain workspace builder -tests are being stubborn. +Set `RETRY_TIMEOUT_SECONDS` if certain workspace-builder tests are stubborn on +your machine, e.g. `RETRY_TIMEOUT_SECONDS=10 py.test`. CI runs the same suite: -e.g. `RETRY_TIMEOUT_SECONDS=10 py.test` - -```{literalinclude} ../.github/workflows/tests.yml +```{literalinclude} ../../.github/workflows/tests.yml :language: yaml - ``` ## Documentation -### Rebuild on save - -Rebuild the documentation when an `.md` file is edited: - -```console -$ cd doc -``` - -```console -$ make watch -``` +Rebuild the docs whenever a source file changes: ```console -$ make SPHINXBUILD='uv run sphinx-build' watch +$ just watch-docs ``` (tmuxp-developer-config)= ## tmuxp developer config -```{image} _static/tmuxp-dev-screenshot.png +```{image} /_static/tmuxp-dev-screenshot.png :width: 1030 :height: 605 :align: center :loading: lazy ``` -After you {ref}`install-dev-env`, when inside the tmuxp checkout: +After you {ref}`install-dev-env`, load the project's own workspace from the +checkout root: ```console $ tmuxp load . ``` -this will load the `.tmuxp.yaml` in the root of the project. +This loads the `.tmuxp.yaml` at the project root: -```{literalinclude} ../.tmuxp.yaml +```{literalinclude} ../../.tmuxp.yaml :language: yaml - ``` ## Formatting ### ruff -The project uses [ruff] to handle formatting, sorting imports and linting. - -````{tab} Command - -uv: - -```console -$ uv run ruff -``` - -If you setup manually: - -```console -$ ruff check . -``` - -```` - -````{tab} make - -```console -$ make ruff -``` +The project uses [ruff] for linting, import sorting, and formatting. -```` - -````{tab} Watch +Lint: ```console -$ make watch_ruff +$ just ruff ``` -requires [`entr(1)`]. - -```` - -````{tab} Fix files - -uv: +Autofix what ruff can: ```console -$ uv run ruff check . --fix +$ uv run ruff check . --fix --show-fixes ``` -If you setup manually: - -```console -$ ruff check . --fix -``` - -```` - #### ruff format -[ruff format] is used for formatting. - -````{tab} Command - -uv: +[ruff format] handles formatting: ```console -$ uv run ruff format . +$ just ruff-format ``` -If you setup manually: - -```console -$ ruff format . -``` - -```` - -````{tab} make - -```console -$ make ruff_format -``` - -```` - ### mypy -[mypy] is used for static type checking. - -````{tab} Command - -uv: +[mypy] does static type checking: ```console -$ uv run mypy . +$ just mypy ``` -If you setup manually: +Re-check on change: ```console -$ mypy . +$ just watch-mypy ``` -```` - -````{tab} make - -```console -$ make mypy -``` - -```` - -````{tab} Watch - -```console -$ make watch_mypy -``` - -requires [`entr(1)`]. -```` - (gh-actions)= ## Continuous integration -### Github Actions - -tmuxp uses [github actions] for continuous integration / automatic unit -testing. - -To view the tmux and python versions tested see the [.github/workflows/tests.yml]. -Builds are done on `master` and pull requests and can be viewed on -the [gh build site]. +tmuxp uses [GitHub Actions] for continuous integration. To see the tmux and +Python versions under test, read [.github/workflows/tests.yml]. Builds run on +`master` and on pull requests, and are visible on the [build site]. -[py.test usage argument]: https://pytest.org/latest/usage.html -[entr]: http://entrproject.org/ -[`entr(1)`]: http://entrproject.org/ [ruff]: https://ruff.rs [ruff format]: https://docs.astral.sh/ruff/formatter/ [mypy]: http://mypy-lang.org/ -[github actions]: https://github.com/features/actions -[gh build site]: https://github.com/tmux-python/tmuxp/actions?query=workflow%3Atests +[GitHub Actions]: https://github.com/features/actions +[build site]: https://github.com/tmux-python/tmuxp/actions?query=workflow%3Atests [.github/workflows/tests.yml]: https://github.com/tmux-python/tmuxp/blob/master/.github/workflows/tests.yml -[issue on github]: https://github.com/tmux-python/tmuxp/issues diff --git a/docs/project/releasing.md b/docs/project/releasing.md index 37b31aa3a7..456a0cd90b 100644 --- a/docs/project/releasing.md +++ b/docs/project/releasing.md @@ -1,50 +1,54 @@ # Releasing -## Release Process +## Release process -Releases are triggered by git tags and published to PyPI via OIDC trusted publishing. +You release tmuxp by tagging a version: pushing a `v` tag triggers a CI +workflow that builds the package and publishes it to PyPI via OIDC trusted +publishing. -1. Update `CHANGES` with the release notes - -2. Bump version in `src/tmuxp/__about__.py` - -3. Commit: +1. Update `CHANGES` with the release notes. +2. Bump the version in `src/tmuxp/__about__.py`. +3. Commit the bump: ```console - $ git commit -m "tmuxp " + $ git commit -m "Tag v" ``` -4. Tag: +4. Tag it: ```console $ git tag v ``` -5. Push: +5. Push the commit and the tag: ```console $ git push && git push --tags ``` -6. CI builds and publishes to PyPI automatically via trusted publishing +6. CI builds and publishes to PyPI automatically. -## Changelog Format +## Changelog format -The `CHANGES` file uses this format: +`CHANGES` is rendered as the changelog page. Each release is a Markdown section +headed by its version and date: ```text -tmuxp () ------------------------- +## tmuxp () -### What's new +### Breaking changes -- Description of feature (#issue) +- Description of the break, with a migration path (#issue) -### Bug fixes +### What's new -- Description of fix (#issue) +- Description of the feature (#issue) -### Breaking changes +### Fixes -- Description of break, migration path (#issue) +- Description of the fix (#issue) ``` + +Subheadings appear in a fixed order when present: `### Breaking changes`, +`### Dependencies`, `### What's new`, `### Fixes`, `### Documentation`, +`### Development`. diff --git a/docs/quickstart.md b/docs/quickstart.md index 6b52b4e1f1..e561b5f735 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -2,45 +2,52 @@ # Quickstart +tmuxp launches a whole tmux workspace — its windows, panes, and the commands +inside them — from a single YAML or JSON file. Install it, write one file, and +`tmuxp load` builds the session and drops you into it. This page takes you from +nothing to a running session. + ## Installation -Ensure you have at least tmux **>= 3.2** and python **>= 3.9**. +Ensure you have at least tmux **>= 3.2** and python **>= 3.10**. ```console $ pip install --user tmuxp ``` -If you're managing dependencies with [uv]_ inside a project environment, -add tmuxp directly to your lockfile: +If you manage dependencies with [uv] inside a project environment, add tmuxp to +your lockfile instead: ```console $ uv add tmuxp ``` -To run tmuxp without installing it globally—similar to what you'd do with -`pipx`—invoke it via [uvx]_: +To run tmuxp without installing it globally — the way you'd use [pipx] — invoke +it through [uvx]: ```console $ uvx tmuxp ``` -You can upgrade to the latest release with: +Upgrade to the latest release with: ```console $ pip install --user --upgrade tmuxp ``` -Within a uv-managed project you can upgrade by refreshing the lockfile and -syncing the environment: +Within a uv-managed project, upgrade by refreshing the lockfile and syncing: ```console $ uv lock --upgrade-package tmuxp +``` + +```console $ uv sync ``` Then install {ref}`completion`. -If you are a Homebrew user you can install it with: +Homebrew users can install it with: ```console $ brew install tmuxp @@ -50,61 +57,47 @@ $ brew install tmuxp ### Developmental releases -New versions of tmuxp are published to PyPI as alpha, beta, or release candidates. -In their versions you will see notification like `a1`, `b1`, and `rc1`, respectively. -`1.10.0b4` would mean the 4th beta release of `1.10.0` before general availability. - -- [pip]\: - - ```console - $ pip install --user --upgrade --pre tmuxp - ``` - -- [uv]_: - - ```console - $ uv add tmuxp --prerelease allow - ``` - -- [uvx]_: - - ```console - $ uvx --from 'tmuxp' --prerelease allow tmuxp - ``` +New versions of tmuxp are published to PyPI as alpha, beta, or release +candidates. Their version carries an `a1`, `b1`, or `rc1` suffix — `1.10.0b4` is +the fourth beta of `1.10.0`, before general availability. -- [pipx]\: +Install the latest pre-release with the tool you use: - ```console - $ pipx install --suffix=@next 'tmuxp' --pip-args '\--pre' --force - ``` - - Then use `tmuxp@next load [session]`. +```console +$ pip install --user --upgrade --pre tmuxp +``` -via trunk (can break easily): +```console +$ uv add tmuxp --prerelease allow +``` -- [pip]\: +```console +$ uvx --from 'tmuxp' --prerelease allow tmuxp +``` - ```console - $ pip install --user -e git+https://github.com/tmux-python/tmuxp.git#egg=tmuxp - ``` +```console +$ pipx install --suffix=@next 'tmuxp' --pip-args '\--pre' --force +``` -- [uv]_: +After the pipx install, load with `tmuxp@next load [session]`. - ```console - $ uv add "tmuxp @ git+https://github.com/tmux-python/tmuxp.git@master" - ``` +Or track trunk directly (it can break): -- [uvx]_: +```console +$ pip install --user -e git+https://github.com/tmux-python/tmuxp.git#egg=tmuxp +``` - ```console - $ uvx --from "tmuxp @ git+https://github.com/tmux-python/tmuxp.git@master" tmuxp - ``` +```console +$ uv add "tmuxp @ git+https://github.com/tmux-python/tmuxp.git@master" +``` -- [pipx]\: +```console +$ uvx --from "tmuxp @ git+https://github.com/tmux-python/tmuxp.git@master" tmuxp +``` - ```console - $ pipx install --suffix=@master 'tmuxp @ git+https://github.com/tmux-python/tmuxp.git@master' --force - ``` +```console +$ pipx install --suffix=@master 'tmuxp @ git+https://github.com/tmux-python/tmuxp.git@master' --force +``` [pip]: https://pip.pypa.io/en/stable/ [pipx]: https://pypa.github.io/pipx/docs/ @@ -121,60 +114,48 @@ via trunk (can break easily): tmuxp launches workspaces / sessions from JSON and YAML files. -Workspace files can be stored in `$HOME/.tmuxp` or in project -directories as `.tmuxp.py`, `.tmuxp.json` or `.tmuxp.yaml`. - -Every workspace file is required to have: +Workspace files live in `$HOME/.tmuxp`, or in a project directory as +`.tmuxp.yaml`, `.tmuxp.yml`, or `.tmuxp.json`. Every workspace file needs: -1. `session_name` -2. list of `windows` -3. list of `panes` for every window in `windows` +1. a `session_name` +2. a list of `windows` +3. a list of `panes` for every window Create a file, `~/.tmuxp/example.yaml`: ```{literalinclude} ../examples/2-pane-vertical.yaml :language: yaml - ``` ```console - $ tmuxp load example.yaml - ``` -This creates your tmuxp session. +This builds and attaches your session. -Load multiple tmux sessions at once: +Load several at once: ```console - $ tmuxp load example.yaml anothersession.yaml - ``` -tmuxp will offer to `switch-client` for you if you're already in a -session. You can also load a workspace and append the windows to -the current active session. +If you're already inside a session, tmuxp offers to `switch-client` for you, or +to append the new windows to the session you're in. -You can also have a custom tmuxp config directory by setting the -`TMUXP_CONFIGDIR` in your environment variables. +You can point tmuxp at a different config directory with the `TMUXP_CONFIGDIR` +environment variable: ```console - $ TMUXP_CONFIGDIR=$HOME/.tmuxpmoo tmuxp load cpython - ``` -Or in your `~/.bashrc` / `~/.zshrc` you can set: +Or set it in your `~/.bashrc` / `~/.zshrc`: ```console - -export TMUXP_CONFIGDIR=$HOME/.yourconfigdir/tmuxp - +$ export TMUXP_CONFIGDIR=$HOME/.yourconfigdir/tmuxp ``` -You can also [Import][import] configs [teamocil] and [tmuxinator]. +You can also [import][import] configs from [teamocil] and [tmuxinator]. ## Pythonics @@ -184,11 +165,11 @@ You can also [Import][import] configs [teamocil] and [tmuxinator]. ::: -ORM - [Object Relational Mapper][object relational mapper] - -AL - [Abstraction Layer][abstraction layer] - -### python abstraction layer +Under the hood, tmuxp drives tmux through +[libtmux](https://libtmux.git-pull.com/) — an +[object-relational mapper][object relational mapper] and +[abstraction layer] over `tmux(1)`'s commands. Each config concept maps to a +libtmux call: | {ref}`tmuxp python api ` | {term}`tmux(1)` equivalent | | ------------------------------------- | -------------------------- | diff --git a/docs/topics/custom-workspace-builders.md b/docs/topics/custom-workspace-builders.md index 26f6f38ff7..18a231d013 100644 --- a/docs/topics/custom-workspace-builders.md +++ b/docs/topics/custom-workspace-builders.md @@ -5,8 +5,8 @@ A *workspace builder* turns an expanded workspace ``dict`` into a live tmux session. tmuxp ships one builder, {class}`~tmuxp.workspace.builder.classic.ClassicWorkspaceBuilder`, and uses it -by default. Advanced users can point tmuxp at a different builder, and packagers -can distribute builders that users select by name. +by default. You can point tmuxp at a different builder, and packagers can +distribute builders that you select by name. This is an advanced, opt-in feature. Existing workspace files keep using the classic builder with no changes. diff --git a/docs/topics/library-vs-cli.md b/docs/topics/library-vs-cli.md index 8c431caa3d..2481a91f76 100644 --- a/docs/topics/library-vs-cli.md +++ b/docs/topics/library-vs-cli.md @@ -15,6 +15,9 @@ Use `tmuxp` when: $ tmuxp load my-workspace.yaml ``` +New to the format? Start with {ref}`quickstart`, then browse ready-to-load files +in {ref}`examples`. + ## When to Use libtmux Use [libtmux](https://libtmux.git-pull.com/) directly when: diff --git a/docs/topics/plugins.md b/docs/topics/plugins.md index 0276d66e64..cc964a0d94 100644 --- a/docs/topics/plugins.md +++ b/docs/topics/plugins.md @@ -2,15 +2,17 @@ # Plugins -The plugin system allows users to customize and extend different aspects of -tmuxp without the need to change tmuxp itself. +Plugins let you customize and extend how tmuxp builds a session — renaming it, +reacting when you reattach, running setup at specific moments — without forking +tmuxp or hand-editing your workspace files. This is an advanced, opt-in feature: +most users never write or install one, and a workspace loads exactly the same +with no plugins at all. Reach for a plugin when you want behavior the workspace +format can't express and you're comfortable writing a little Python. -## Using a Plugin +## Using a plugin -To use a plugin, install it in your local python environment and add it to -your tmuxp workspace file. - -### Example Workspace files +Install the plugin into the same Python environment as tmuxp, then name it in +your workspace file under `plugins`: ````{tab} YAML @@ -30,42 +32,63 @@ your tmuxp workspace file. ```` -## Developing a Plugin +## When your hooks fire -tmuxp expects all plugins to be a class within a python submodule named -`plugin` that is within a python module that is installed in the local -python environment. A plugin interface is provided by tmuxp to inherit. +A plugin is a class whose methods tmuxp calls at set points while it builds and +attaches the session. You override only the hooks you care about; the rest do +nothing. The order is fixed: -[uv] is the chosen python package manager for tmuxp. It is highly -suggested to use it when developing plugins; however, `pip` will work -just as well. Only one of the configuration files is needed for the packaging -tool that the package developer decides to use. +:::{mermaid} +:caption: When each plugin hook fires during `tmuxp load`. -```console +flowchart TD + load["tmuxp load"]:::cmd --> bwb["before_workspace_builder"]:::cmd + bwb --> oc["on_window_create"]:::cmd + oc --> panes["create panes, run commands"] + panes --> awf["after_window_finished"]:::cmd + awf -->|more windows| oc + awf -->|all windows built| bs["before_script"]:::cmd + bs --> reattach["reattach"]:::cmd +::: + +`before_workspace_builder` runs first, once the session exists but before any +windows. `on_window_create` and `after_window_finished` bracket each window's +panes. Two of the names can mislead: `before_script` runs _after_ the whole +session is built — it augments, rather than replaces, the workspace's own +`before_script` — and `reattach` fires only when tmuxp re-attaches you to a +session that already exists. + +## Developing a plugin +tmuxp expects a plugin to be a class in a Python submodule named `plugin`, inside +a module installed in the same environment as tmuxp. You inherit from the +interface tmuxp provides, {class}`~tmuxp.plugin.TmuxpPlugin`. + +[uv] is tmuxp's package manager of choice, and what these examples use; `pip` +works just as well. You need only one project file, for whichever packaging tool +you choose. + +```console python_module ├── tmuxp_plugin_my_plugin_module -│   ├── __init__.py -│   └── plugin.py +│ ├── __init__.py +│ └── plugin.py └── pyproject.toml # Python project configuration file - ``` -When publishing plugins to pypi, tmuxp advocates for standardized naming: -`tmuxp-plugin-{your-plugin-name}` to allow for easier searching. To create a -module configuration file with uv, run `uv virtualenv` in the module -directory. The resulting file looks something like this: +When publishing to PyPI, tmuxp suggests the naming convention +`tmuxp-plugin-{your-plugin-name}` so others can find it. A minimal +`pyproject.toml` looks like this: ```toml - [project] name = "tmuxp-plugin-my-tmuxp-plugin" version = "0.0.2" description = "An example tmuxp plugin." -authors = ["Author Name .com>"] -requires-python = ">=3.8,<4.0" +authors = [{ name = "Author Name", email = "author.name@example.com" }] +requires-python = ">=3.10" dependencies = [ - "tmuxp^=1.7.0" + "tmuxp>=1.7.0", ] [build-system] @@ -73,28 +96,27 @@ requires = ["hatchling"] build-backend = "hatchling.build" ``` -The `plugin.py` file could contain something like the following: +The `plugin.py` file holds the class: ```python +import datetime from tmuxp.plugin import TmuxpPlugin -import datetime + class MyTmuxpPlugin(TmuxpPlugin): def __init__(self): - """ - Initialize my custom plugin. - """ - # Optional version dependency configuration. See Plugin API docs - # for all supported config parameters + """Initialize my custom plugin.""" + # Optional version-dependency configuration. See the Plugin API + # docs for every supported parameter. config = { - 'tmuxp_min_version' = '1.6.2' + 'tmuxp_min_version': '1.6.2', } TmuxpPlugin.__init__( self, plugin_name='tmuxp-plugin-my-tmuxp-plugin', - **config + **config, ) def before_workspace_builder(self, session): @@ -103,17 +125,15 @@ class MyTmuxpPlugin(TmuxpPlugin): def reattach(self, session): now = datetime.datetime.now().strftime('%Y-%m-%d') session.rename_session('session_{}'.format(now)) - ``` -Once this plugin is installed in the local python environment, it can be used -in a configuration file like the following: +Once it's installed in the same environment, name it in a workspace file: ```yaml session_name: plugin example plugins: - my_plugin_module.plugin.MyTmuxpPlugin -# ... the rest of your config +# ... the rest of your workspace ``` ## Plugin API diff --git a/docs/topics/troubleshooting.md b/docs/topics/troubleshooting.md index bfacad2c34..7843cde76c 100644 --- a/docs/topics/troubleshooting.md +++ b/docs/topics/troubleshooting.md @@ -19,7 +19,7 @@ Ensure tmux is installed: $ tmux -V ``` -Minimum required version: tmux 3.2a. +Minimum required version: tmux 3.2. ## Configuration errors @@ -36,5 +36,4 @@ Use `tmuxp load -d` to load in detached mode alongside existing sessions. ## Shell completion not working -See [Shell Completion](../cli/completion.md) for setup instructions -for bash, zsh, and fish. +See {ref}`completion` for setup instructions for bash, zsh, and tcsh. diff --git a/docs/topics/workflows.md b/docs/topics/workflows.md index 5965653c0d..d47530a842 100644 --- a/docs/topics/workflows.md +++ b/docs/topics/workflows.md @@ -1,32 +1,54 @@ # Workflows -## CI Integration +tmuxp is small enough to drop into a script, a CI job, or your daily startup +routine. This page collects a few patterns — running headless, branching on exit +codes, and turning a session you arranged by hand into a reusable file. None of +it is special machinery; it's the same `tmuxp load`, `freeze`, and exit codes you +already have. -tmuxp can set up tmux sessions in CI pipelines for integration testing: +## CI integration + +You can build a tmux session inside a CI pipeline for integration testing. Load +it detached so nothing waits on a terminal: ```console $ tmuxp load -d my-workspace.yaml ``` -The `-d` flag loads the session in detached mode, useful for headless environments. +The `-d` flag loads the session in the background, which is what you want in a +headless environment. ## Scripting -tmuxp's exit codes enable scripting and error handling. See -[Exit Codes](../cli/exit-codes.md) for the complete list. +tmuxp returns meaningful exit codes, so a script can tell success from failure +and branch on it. See {ref}`cli-exit-codes` for the full list. + +## Automating development environments + +You don't have to write a workspace file from scratch. Arrange a session the way +you like it, freeze it to capture the layout, then edit and replay it anywhere: + +:::{mermaid} +:caption: Capture a session once, replay it anywhere. -## Automating Development Environments +flowchart TD + arrange["arrange tmux by hand"] --> freeze["tmuxp freeze"]:::cmd + freeze --> yaml["workspace.yaml"]:::cmd + yaml --> edit["edit + commit"] + edit --> load["tmuxp load"]:::cmd + load --> arrange +::: -Use tmuxp to codify your development environment: +1. Arrange your ideal tmux layout by hand. +2. Freeze it: `tmuxp freeze my-session`. +3. Edit the generated YAML to add commands. +4. Load it on any machine: `tmuxp load my-workspace.yaml`. -1. Set up your ideal tmux layout manually -2. Freeze it: `tmuxp freeze my-session` -3. Edit the generated YAML to add commands -4. Load it on any machine: `tmuxp load my-workspace.yaml` +## User-level configuration -## User-Level Configuration +You can store workspace files in any of these, then load them by name from +anywhere: -Workspace configs can be stored in: - `~/.tmuxp/` (legacy) - `~/.config/tmuxp/` (XDG default) -- Project-local `.tmuxp.yaml` or `.tmuxp/` directory +- a project-local `.tmuxp.yaml` or `.tmuxp/` directory