diff --git a/.codecov.yml b/.codecov.yml new file mode 100644 index 00000000000..bf9b9abef60 --- /dev/null +++ b/.codecov.yml @@ -0,0 +1,15 @@ +codecov: + notify: + require_ci_to_pass: no + +coverage: + precision: 2 + round: down + range: "70...100" + status: + project: + default: + target: auto + threshold: 1% + base: auto + patch: off diff --git a/.cursor/rules/avoid-debug-loops.mdc b/.cursor/rules/avoid-debug-loops.mdc new file mode 100644 index 00000000000..8a241ec9900 --- /dev/null +++ b/.cursor/rules/avoid-debug-loops.mdc @@ -0,0 +1,57 @@ +--- +description: When stuck in debugging loops, break the cycle by minimizing to an MVP, removing debugging cruft, and documenting the issue completely for a fresh approach +globs: *.py +alwaysApply: true +--- +# Avoid Debug Loops + +When debugging becomes circular and unproductive, follow these steps: + +## Detection +- You have made multiple unsuccessful attempts to fix the same issue +- You are adding increasingly complex code to address errors +- Each fix creates new errors in a cascading pattern +- You are uncertain about the root cause after 2-3 iterations + +## Action Plan + +1. **Pause and acknowledge the loop** + - Explicitly state that you are in a potential debug loop + - Review what approaches have been tried and failed + +2. **Minimize to MVP** + - Remove all debugging cruft and experimental code + - Revert to the simplest version that demonstrates the issue + - Focus on isolating the core problem without added complexity + +3. **Comprehensive Documentation** + - Provide a clear summary of the issue + - Include minimal but complete code examples that reproduce the problem + - Document exact error messages and unexpected behaviors + - Explain your current understanding of potential causes + +4. **Format for Portability** + - Present the problem in quadruple backticks for easy copying: + +```` +# Problem Summary +[Concise explanation of the issue] + +## Minimal Reproduction Code +```python +# Minimal code example that reproduces the issue +``` + +## Error/Unexpected Output +``` +[Exact error messages or unexpected output] +``` + +## Failed Approaches +[Brief summary of approaches already tried] + +## Suspected Cause +[Your current hypothesis about what might be causing the issue] +```` + +This format enables the user to easily copy the entire problem statement into a fresh conversation for a clean-slate approach. diff --git a/.cursor/rules/dev-loop.mdc b/.cursor/rules/dev-loop.mdc new file mode 100644 index 00000000000..d60a5210953 --- /dev/null +++ b/.cursor/rules/dev-loop.mdc @@ -0,0 +1,187 @@ +--- +description: QA every edit +globs: *.py +alwaysApply: true +--- + +# Development Process + +## Project Stack + +The project uses the following tools and technologies: + +- **uv** - Python package management and virtual environments +- **ruff** - Fast Python linter and formatter +- **py.test** - Testing framework + - **pytest-watcher** - Continuous test runner +- **mypy** - Static type checking +- **doctest** - Testing code examples in documentation + +## 1. Start with Formatting + +Format your code first: + +``` +uv run ruff format . +``` + +## 2. Run Tests + +Verify that your changes pass the tests: + +``` +uv run py.test +``` + +For continuous testing during development, use pytest-watcher: + +``` +# Watch all tests +uv run ptw . + +# Watch and run tests immediately, including doctests +uv run ptw . --now --doctest-modules + +# Watch specific files or directories +uv run ptw . --now --doctest-modules src/libtmux/_internal/ +``` + +## 3. Commit Initial Changes + +Make an atomic commit for your changes using conventional commits. +Use `@git-commits.mdc` for assistance with commit message standards. + +## 4. Run Linting and Type Checking + +Check and fix linting issues: + +``` +uv run ruff check . --fix --show-fixes +``` + +Check typings: + +``` +uv run mypy +``` + +## 5. Verify Tests Again + +Ensure tests still pass after linting and type fixes: + +``` +uv run py.test +``` + +## 6. Final Commit + +Make a final commit with any linting/typing fixes. +Use `@git-commits.mdc` for assistance with commit message standards. + +## Development Loop Guidelines + +If there are any failures at any step due to your edits, fix them before proceeding to the next step. + +## Python Code Standards + +### Docstring Guidelines + +For `src/**/*.py` files, follow these docstring guidelines: + +1. **Use reStructuredText format** for all docstrings. + ```python + """Short description of the function or class. + + Detailed description using reStructuredText format. + + Parameters + ---------- + param1 : type + Description of param1 + param2 : type + Description of param2 + + Returns + ------- + type + Description of return value + """ + ``` + +2. **Keep the main description on the first line** after the opening `"""`. + +3. **Use NumPy docstyle** for parameter and return value documentation. + +### Doctest Guidelines + +For doctests in `src/**/*.py` files: + +1. **Use narrative descriptions** for test sections rather than inline comments: + ```python + """Example function. + + Examples + -------- + Create an instance: + + >>> obj = ExampleClass() + + Verify a property: + + >>> obj.property + 'expected value' + """ + ``` + +2. **Move complex examples** to dedicated test files at `tests/examples//test_.py` if they require elaborate setup or multiple steps. + +3. **Utilize pytest fixtures** via `doctest_namespace` for more complex test scenarios: + ```python + """Example with fixture. + + Examples + -------- + >>> # doctest_namespace contains all pytest fixtures from conftest.py + >>> example_fixture = getfixture('example_fixture') + >>> example_fixture.method() + 'expected result' + """ + ``` + +4. **Keep doctests simple and focused** on demonstrating usage rather than comprehensive testing. + +5. **Add blank lines between test sections** for improved readability. + +6. **Test your doctests continuously** using pytest-watcher during development: + ``` + # Watch specific modules for doctest changes + uv run ptw . --now --doctest-modules src/path/to/module.py + ``` + +### Pytest Testing Guidelines + +1. **Use existing fixtures over mocks**: + - Use fixtures from conftest.py instead of `monkeypatch` and `MagicMock` when available + - For instance, if using libtmux, use provided fixtures: `server`, `session`, `window`, and `pane` + - Document in test docstrings why standard fixtures weren't used for exceptional cases + +2. **Preferred pytest patterns**: + - Use `tmp_path` (pathlib.Path) fixture over Python's `tempfile` + - Use `monkeypatch` fixture over `unittest.mock` + +### Import Guidelines + +1. **Prefer namespace imports**: + - Import modules and access attributes through the namespace instead of importing specific symbols + - Example: Use `import enum` and access `enum.Enum` instead of `from enum import Enum` + - This applies to standard library modules like `pathlib`, `os`, and similar cases + +2. **Standard aliases**: + - For `typing` module, use `import typing as t` + - Access typing elements via the namespace: `t.NamedTuple`, `t.TypedDict`, etc. + - Note primitive types like unions can be done via `|` pipes and primitive types like list and dict can be done via `list` and `dict` directly. + +3. **Benefits of namespace imports**: + - Improves code readability by making the source of symbols clear + - Reduces potential naming conflicts + - Makes import statements more maintainable diff --git a/.cursor/rules/git-commits.mdc b/.cursor/rules/git-commits.mdc new file mode 100644 index 00000000000..f9c0980db76 --- /dev/null +++ b/.cursor/rules/git-commits.mdc @@ -0,0 +1,95 @@ +--- +description: git-commits: Git commit message standards and AI assistance +globs: git-commits: Git commit message standards and AI assistance | *.git/* .gitignore .github/* CHANGELOG.md CHANGES.md +alwaysApply: true +--- +# Optimized Git Commit Standards + +## Commit Message Format +``` +Component/File(commit-type[Subcomponent/method]): Concise description + +why: Explanation of necessity or impact. +what: +- Specific technical changes made +- Focused on a single topic + +refs: #issue-number, breaking changes, or relevant links +``` + +## Component Patterns +### General Code Changes +``` +Component/File(feat[method]): Add feature +Component/File(fix[method]): Fix bug +Component/File(refactor[method]): Code restructure +``` + +### Packages and Dependencies +| Language | Standard Packages | Dev Packages | Extras / Sub-packages | +|------------|------------------------------------|-------------------------------|-----------------------------------------------| +| General | `lang(deps):` | `lang(deps[dev]):` | | +| Python | `py(deps):` | `py(deps[dev]):` | `py(deps[extra]):` | +| JavaScript | `js(deps):` | `js(deps[dev]):` | `js(deps[subpackage]):`, `js(deps[dev{subpackage}]):` | + +#### Examples +- `py(deps[dev]): Update pytest to v8.1` +- `js(deps[ui-components]): Upgrade Button component package` +- `js(deps[dev{linting}]): Add ESLint plugin` + +### Documentation Changes +Prefix with `docs:` +``` +docs(Component/File[Subcomponent/method]): Update API usage guide +``` + +### Test Changes +Prefix with `tests:` +``` +tests(Component/File[Subcomponent/method]): Add edge case tests +``` + +## Commit Types Summary +- **feat**: New features or enhancements +- **fix**: Bug fixes +- **refactor**: Code restructuring without functional change +- **docs**: Documentation updates +- **chore**: Maintenance (dependencies, tooling, config) +- **test**: Test-related updates +- **style**: Code style and formatting + +## General Guidelines +- Subject line: Maximum 50 characters +- Body lines: Maximum 72 characters +- Use imperative mood (e.g., "Add", "Fix", not "Added", "Fixed") +- Limit to one topic per commit +- Separate subject from body with a blank line +- Mark breaking changes clearly: `BREAKING:` +- Use `See also:` to provide external references + +## AI Assistance Workflow in Cursor +- Stage changes with `git add` +- Use `@commit` to generate initial commit message +- Review and refine generated message +- Ensure adherence to these standards + +## Good Commit Example +``` +Pane(feat[capture_pane]): Add screenshot capture support + +why: Provide visual debugging capability +what: +- Implement capturePane method with image export +- Integrate with existing Pane component logic +- Document usage in Pane README + +refs: #485 +See also: https://example.com/docs/pane-capture +``` + +## Bad Commit Example +``` +fixed stuff and improved some functions +``` + +These guidelines ensure clear, consistent commit histories, facilitating easier code review and maintenance. \ No newline at end of file diff --git a/.cursor/rules/notes-llms-txt.mdc b/.cursor/rules/notes-llms-txt.mdc new file mode 100644 index 00000000000..ac17097737c --- /dev/null +++ b/.cursor/rules/notes-llms-txt.mdc @@ -0,0 +1,42 @@ +--- +description: LLM-friendly markdown format for notes directories +globs: notes/**/*.md,**/notes/**/*.md +alwaysApply: true +--- + +# Instructions for Generating LLM-Optimized Markdown Content + +When creating or editing markdown files within the specified directories, adhere to the following guidelines to ensure the content is optimized for LLM understanding and efficient token usage: + +1. **Conciseness and Clarity**: + - **Be Brief**: Present information succinctly, avoiding unnecessary elaboration. + - **Use Clear Language**: Employ straightforward language to convey ideas effectively. + +2. **Structured Formatting**: + - **Headings**: Utilize markdown headings (`#`, `##`, `###`, etc.) to organize content hierarchically. + - **Lists**: Use bullet points (`-`) or numbered lists (`1.`, `2.`, etc.) to enumerate items clearly. + - **Code Blocks**: Enclose code snippets within triple backticks (```) to distinguish them from regular text. + +3. **Semantic Elements**: + - **Emphasis**: Use asterisks (`*`) or underscores (`_`) for italicizing text to denote emphasis. + - **Strong Emphasis**: Use double asterisks (`**`) or double underscores (`__`) for bold text to highlight critical points. + - **Inline Code**: Use single backticks (`) for inline code references. + +4. **Linking and References**: + - **Hyperlinks**: Format links using `[Link Text](mdc:URL)` to provide direct access to external resources. + - **References**: When citing sources, use footnotes or inline citations to maintain readability. + +5. **Avoid Redundancy**: + - **Eliminate Repetition**: Ensure that information is not unnecessarily repeated within the document. + - **Use Summaries**: Provide brief summaries where detailed explanations are not essential. + +6. **Standard Compliance**: + - **llms.txt Conformance**: Structure the document in alignment with the `llms.txt` standard, which includes: + - An H1 heading with the project or site name. + - A blockquote summarizing the project's purpose. + - Additional markdown sections providing detailed information. + - H2-delimited sections containing lists of URLs for further details. + +By following these guidelines, the markdown files will be tailored for optimal LLM processing, ensuring that the content is both accessible and efficiently tokenized for AI applications. + +For more information on the `llms.txt` standard, refer to the official documentation: https://llmstxt.org/ diff --git a/.cursor/rules/tmuxp-pytest.mdc b/.cursor/rules/tmuxp-pytest.mdc new file mode 100644 index 00000000000..579809cc64f --- /dev/null +++ b/.cursor/rules/tmuxp-pytest.mdc @@ -0,0 +1,77 @@ +--- +description: Guidelines for using pytest with tmuxp, including libtmux fixtures +globs: tests/**/test_*.py +alwaysApply: true +--- + +# tmuxp Pytest Guidelines + +## Leveraging libtmux fixtures + +tmuxp tests can utilize libtmux's pytest fixtures for fast, efficient tmux server, session, window, and pane setup/teardown. These fixtures automatically manage the lifecycle of tmux resources during tests. + +### Available fixtures + +The following fixtures are available through libtmux's pytest plugin: + +- `server`: Creates a temporary tmux server with isolated socket +- `session`: Creates a temporary tmux session in the server +- `window`: Creates a temporary tmux window in the session +- `pane`: Creates a temporary tmux pane in the pane +- `TestServer`: Factory for creating multiple independent servers with unique socket names + +### Usage in tests + +```python +def test_something_with_server(server): + # server is already running with proper configuration + my_session = server.new_session("test-session") + assert server.is_alive() + assert my_session in server.sessions + +def test_something_with_session(session): + # session is already created and configured + new_window = session.new_window("test-window") + assert new_window in session.windows + +def test_with_multiple_servers(TestServer): + # Create independent servers for comparison tests + Server1 = TestServer() + Server2 = TestServer() + + server1 = Server1() + server2 = Server2() + + session1 = server1.new_session() + session2 = server2.new_session() + + assert server1.socket_path != server2.socket_path +``` + +### Customizing session parameters + +You can override the `session_params` fixture to customize session creation: + +```python +import pytest + +@pytest.fixture +def session_params(): + return { + 'x': 800, + 'y': 600, + 'window_name': 'custom-window' + } +``` + +### Benefits + +- No need to manually set up and tear down tmux infrastructure +- Tests run in isolated tmux environments +- Faster test execution +- Reliable test environment with predictable configuration +- Multiple tests can run in parallel without tmux session conflicts + +For more details, see: +- [libtmux pytest plugin documentation](https://libtmux.git-pull.com/pytest-plugin/index.html) +- [libtmux pytest plugin code](https://github.com/tmux-python/libtmux/blob/master/src/libtmux/pytest_plugin.py) diff --git a/.github/contributing.md b/.github/contributing.md new file mode 100644 index 00000000000..5712dcf03a6 --- /dev/null +++ b/.github/contributing.md @@ -0,0 +1,35 @@ +# Contributing + +When contributing to this repository, please first discuss the change you wish to make via issue, +email, or any other method with the maintainers of this repository before making a change. + +Please note we have a code of conduct, please follow it in all your interactions with the project. + +## Pull Request Process + +1. Ensure any install or build dependencies are removed before the end of the layer when doing a + build. +2. This project uses flake8 to conform with common Python standards. Make sure + to run your code through linter using latest version of flake8, before pull request. +3. Bad documnentation is a Bug. If your change demands documentation update, please do so. If you + find an issue with documentation, take the time to improve or fix it. +4. pytest is used for automated testing. Please make sure to update tests that are needed, and to run + `make test` before submitting your pull request. This should prevent issues with TravisCI and + make the review and merging process easier and faster. +5. Update the README.md with details of changes to the interface, this includes new environment + variables, exposed ports, useful file locations and container parameters. +6. Increase the version numbers in any examples files and the README.md to the new version that this + Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/). +7. You may merge the Pull Request in once you have the sign-off of one other developer. If you + do not have permission to do that, you may request reviewer to merge it for you. + +## Decorum + +- Participants will be tolerant of opposing views. +- Participants must ensure that their language and actions are free of personal + attacks and disparaging personal remarks. +- When interpreting the words and actions of others, participants should always + assume good intentions. +- Behaviour which can be reasonably considered harassment will not be tolerated. + +Based on [Ruby's Community Conduct Guideline](https://www.ruby-lang.org/en/conduct/) diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000000..d202a332d29 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,7 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + # Check for updates to GitHub Actions every week + interval: "weekly" diff --git a/.github/issue_template.md b/.github/issue_template.md new file mode 100644 index 00000000000..980833e8763 --- /dev/null +++ b/.github/issue_template.md @@ -0,0 +1,34 @@ +Are you using the latest version of tmuxp? Check `tmuxp -V` against +https://pypi.org/project/tmuxp/. If it's not the latest, consider updating, e.g. +`pip install --user tmuxp`. + +### Step 1: Provide a summary of your problem + +- For general technical questions, problems or feature requests related to the code **in this repository** file an issue. + +### Step 2: Provide tmuxp details + +- Include output of `tmuxp debug-info` +- Include any other pertinant system info not captured + +### Step 3: Describe the problem: + +#### Steps to reproduce: + +1. ... +2. ... +3. ... + +#### Observed Results: + +- What happened? This could be a description, log output, etc. + +#### Expected Results: + +- What did you expect to happen? + +#### Relevant Code: + +``` +// TODO(you): paste here tmuxp configuration (YAML / JSON) you are having issues with. +``` diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 00000000000..6246268b7a2 --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,30 @@ +# Number of days of inactivity before an issue becomes stale +daysUntilStale: 60 +# Number of days of inactivity before a stale issue is closed +daysUntilClose: 7 +# Issues with these labels will never be considered stale +exemptLabels: + - pinned + - security + - enhancement +# Label to use when marking an issue as stale +staleLabel: stale +# Comment to post when marking an issue as stale. Set to `false` to disable +markComment: | + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. Thank you + for your contributions. + + This bot is used to handle issues where the issue hasn't been discussed or + has gone out of date. If an issue isn't resolved and handled in a certain + period of time, it may be closed. If you would like your issue re-opened, + please create a fresh issue with the latest, up to date information and + mention this issue in it. +unmarkComment: | + Thank you for updating this issue. It is no longer marked as stale. + +# Comment to post when closing a stale issue. Set to `false` to disable +closeComment: false + +# limit to only 'issues' or 'pulls' +only: issues diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 00000000000..9b41f046c81 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,78 @@ +name: docs + +on: + push: + branches: + - master + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ['3.13'] + steps: + - uses: actions/checkout@v4 + - name: Filter changed file paths to outputs + uses: dorny/paths-filter@v3.0.2 + id: changes + with: + filters: | + root_docs: + - CHANGES + - README.* + docs: + - 'docs/**' + - 'examples/**' + python_files: + - 'src/tmuxp/**' + - pyproject.toml + - uv.lock + + - name: Should publish + if: steps.changes.outputs.docs == 'true' || steps.changes.outputs.root_docs == 'true' || steps.changes.outputs.python_files == 'true' + run: echo "PUBLISH=$(echo true)" >> $GITHUB_ENV + + - name: Install uv + uses: astral-sh/setup-uv@v5 + if: env.PUBLISH == 'true' + with: + enable-cache: true + + - name: Set up Python ${{ matrix.python-version }} + if: env.PUBLISH == 'true' + run: uv python install ${{ matrix.python-version }} + + - name: Install dependencies + if: env.PUBLISH == 'true' + run: uv sync --all-extras --dev + + - name: Print python versions + if: env.PUBLISH == 'true' + run: | + python -V + uv run python -V + + - name: Build documentation + if: env.PUBLISH == 'true' + run: | + pushd docs; make SPHINXBUILD='uv run sphinx-build' html; popd + + - name: Push documentation to S3 + if: env.PUBLISH == 'true' + uses: jakejarvis/s3-sync-action@master + with: + args: --acl public-read --follow-symlinks --delete + env: + AWS_S3_BUCKET: ${{ secrets.AWS_S3_BUCKET }} + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_REGION: 'us-west-1' # optional: defaults to us-east-1 + SOURCE_DIR: 'docs/_build/html' # optional: defaults to entire repository + + - name: Purge cache on Cloudflare + if: env.PUBLISH == 'true' + uses: jakejarvis/cloudflare-purge-action@v0.3.0 + env: + CLOUDFLARE_TOKEN: ${{ secrets.CLOUDFLARE_TOKEN }} + CLOUDFLARE_ZONE: ${{ secrets.CLOUDFLARE_ZONE }} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 00000000000..17597d80f4b --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,127 @@ +name: tests + +on: [push, pull_request] + +jobs: + build: + # Don't run twice for internal PRs from our own repo + if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository + + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ['3.13'] + tmux-version: ['2.6', '2.7', '2.8', '3.0a', '3.1b', '3.2a', '3.3a', '3.4', '3.5', 'master'] + # balance ci coverage across supported python/tmux versions with CI speed + include: + - python-version: '3.9' + tmux-version: '2.6' + - python-version: '3.9' + tmux-version: 'master' + steps: + - uses: actions/checkout@v4 + + - name: Install uv + uses: astral-sh/setup-uv@v5 + with: + enable-cache: true + + - name: Set up Python ${{ matrix.python-version }} + run: uv python install ${{ matrix.python-version }} + + - name: Test runtime dependencies + run: | + uv run --no-dev -p python${{ matrix.python-version }} -- python -c ' + from tmuxp import _internal, cli, workspace, exc, log, plugin, shell, types, util, __version__ + from tmuxp._internal import config_reader, types + from tmuxp.cli import convert, debug_info, edit, freeze, import_config, load, ls, shell, utils + from tmuxp.workspace import builder, constants, finders, freezer, importers, loader, validation + from libtmux import __version__ as __libtmux_version__ + print("tmuxp version:", __version__) + print("libtmux version:", __libtmux_version__) + ' + + - name: Install dependencies + run: uv sync --all-extras --dev + + - name: Setup tmux build cache for tmux ${{ matrix.tmux-version }} + id: tmux-build-cache + uses: actions/cache@v4 + with: + path: ~/tmux-builds/tmux-${{ matrix.tmux-version }} + key: tmux-${{ matrix.tmux-version }} + + - name: Build tmux ${{ matrix.tmux-version }} + if: steps.tmux-build-cache.outputs.cache-hit != 'true' + run: | + sudo apt install libevent-dev libncurses5-dev libtinfo-dev libutempter-dev bison + mkdir ~/tmux-builds + mkdir ~/tmux-src + git clone https://github.com/tmux/tmux.git ~/tmux-src/tmux-${{ matrix.tmux-version }} + cd ~/tmux-src/tmux-${{ matrix.tmux-version }} + git checkout ${{ matrix.tmux-version }} + sh autogen.sh + ./configure --prefix=$HOME/tmux-builds/tmux-${{ matrix.tmux-version }} && make && make install + export PATH=$HOME/tmux-builds/tmux-${{ matrix.tmux-version }}/bin:$PATH + cd ~ + tmux -V + + - name: Lint with ruff check . + run: uv run ruff check . + + - name: Format with ruff + run: uv run ruff format . --check + + - name: Lint with mypy + run: uv run mypy . + + - name: Print python versions + run: | + python -V + uv run python -V + + - name: Test with pytest + continue-on-error: ${{ matrix.tmux-version == 'master' }} + run: | + sudo apt install libevent-2.1-7 + export PATH=$HOME/tmux-builds/tmux-${{ matrix.tmux-version }}/bin:$PATH + ls $HOME/tmux-builds/tmux-${{ matrix.tmux-version }}/bin + tmux -V + uv run py.test --cov=./ --cov-report=xml --verbose + + - uses: codecov/codecov-action@v5 + with: + token: ${{ secrets.CODECOV_TOKEN }} + + release: + runs-on: ubuntu-latest + needs: build + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') + + strategy: + matrix: + python-version: ['3.13'] + + steps: + - uses: actions/checkout@v4 + + - name: Install uv + uses: astral-sh/setup-uv@v5 + with: + enable-cache: true + + - name: Set up Python ${{ matrix.python-version }} + run: uv python install ${{ matrix.python-version }} + + - name: Install dependencies + run: uv sync --all-extras --dev + + - name: Build package + run: uv build + + - name: Publish package + uses: pypa/gh-action-pypi-publish@release/v1 + with: + user: __token__ + password: ${{ secrets.PYPI_API_TOKEN }} + skip_existing: true diff --git a/.gitignore b/.gitignore index 3de12b60cef..63f65afd3ce 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,87 @@ -*.pyc -*.pyo -.env -dist +# macOS +.DS_Store + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +*env*/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg *.egg -*.egg-info -doc/_build -tmuxp/testsuite/.tmuxp + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Ipython Notebook +.ipynb_checkpoints + +# mypy +.mypy_cache/ + +# editors +.idea +.ropeproject +*.swp +.vscode + +# docs +doc/_build/ + +# MonkeyType +monkeytype.sqlite3 + +# Claude code +**/CLAUDE.md +**/CLAUDE.local.md +**/CLAUDE.*.md +**/.claude/settings.local.json diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 0e25ae48349..00000000000 --- a/.gitmodules +++ /dev/null @@ -1,4 +0,0 @@ - -[submodule "doc/_themes"] - path = doc/_themes - url = https://github.com/ericholscher/readthedocs-sphinx.git diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000000..de753c537d2 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,3 @@ +{ + "printWidth": 100 +} diff --git a/.python-version b/.python-version new file mode 100644 index 00000000000..4eba2a62eb7 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.13.0 diff --git a/.tmuxp.json b/.tmuxp.json index 1cf06c82eed..694c2a92623 100644 --- a/.tmuxp.json +++ b/.tmuxp.json @@ -1,73 +1,41 @@ { + "session_name": "tmuxp", + "start_directory": "./", + "shell_command_before": [ + "uv virtualenv --quiet > /dev/null 2>&1 && clear" + ], "windows": [ { + "window_name": "tmuxp", + "focus": true, + "layout": "main-horizontal", + "options": { + "main-pane-height": "67%" + }, "panes": [ { - "shell_command": [ - "reset", - "vim", - ":Ex" - ], "focus": true - }, - { - "shell_command": [ - "echo hi" - ] - }, - { - "shell_command": [ - "command -v .env/bin/tmuxp >/dev/null 2>&1 || { pip install -e .; }", - "command -v watching_testrunner >/dev/null 2>&1 || { pip install watching_testrunner; }", - "watching_testrunner --basepath ./ --pattern=\"*.py\" 'python run_tests.py'" - ] - } - ], - "shell_command_before": [ - "command -v virtualenv >/dev/null 2>&1 || { pip install virtualenv; }", - "[ -d .env -a -f .env/bin/activate ] && source .env/bin/activate || virtualenv .env", - "[ ! -d .env/build ] || rm -rf .env/build" - ], - "layout": "main-horizontal", - "window_name": "tmuxp", - "options": { - "main-pane-height": 50 - } - }, + }, + "pane", + "make watch_mypy", + "make watch_test" + ] + }, { + "window_name": "docs", + "layout": "main-horizontal", + "options": { + "main-pane-height": "67%" + }, + "start_directory": "docs/", "panes": [ { - "shell_command": [ - "reset", - "vim", - ":Ex" - ], "focus": true - }, - "pwd", - "echo 'docs built to '; python -m SimpleHTTPServer", - { - "shell_command": [ - "command -v sphinx-quickstart >/dev/null 2>&1 || { pip install -r requirements.pip; }", - "command -v watching_testrunner >/dev/null 2>&1 || { pip install watching_testrunner; }", - "watching_testrunner --basepath ./ --pattern=\"*.rst\" 'make html'", - "python -m SimpleHTTPServer" - ] - } - ], - "shell_command_before": [ - "command -v virtualenv >/dev/null 2>&1 || { pip install virtualenv; }", - "[ -d .env -a -f .env/bin/activate ] && source .env/bin/activate || virtualenv .env", - "[ ! -d .env/build ] || rm -rf .env/build", - "command -v .env/bin/tmuxp >/dev/null 2>&1 || { pip install -e .; }", - "cd ./doc" - ], - "layout": "main-horizontal", - "window_name": "docs", - "options": { - "main-pane-height": 50 - } + }, + "pane", + "pane", + "make start" + ] } - ], - "session_name": "tmuxp" -} \ No newline at end of file + ] +} diff --git a/.tmuxp.yaml b/.tmuxp.yaml index b6f51c3e1c5..16d7044be3a 100644 --- a/.tmuxp.yaml +++ b/.tmuxp.yaml @@ -1,45 +1,25 @@ session_name: tmuxp +start_directory: ./ # load session relative to config location (project root). +shell_command_before: +- uv virtualenv --quiet > /dev/null 2>&1 && clear windows: - window_name: tmuxp + focus: True layout: main-horizontal options: - main-pane-height: 50 - shell_command_before: - - command -v virtualenv >/dev/null 2>&1 || { pip install virtualenv; } - - '[ -d .env -a -f .env/bin/activate ] && source .env/bin/activate || virtualenv .env' - - '[ ! -d .env/build ] || rm -rf .env/build' + main-pane-height: 67% panes: - - shell_command: - - reset - - vim - - :Ex - focus: true - - shell_command: - - echo hi - - shell_command: - - command -v .env/bin/tmuxp >/dev/null 2>&1 || { pip install -e .; } - - command -v watching_testrunner >/dev/null 2>&1 || { pip install watching_testrunner; } - - watching_testrunner --basepath ./ --pattern="*.py" 'python run_tests.py' + - focus: true + - pane + - make watch_mypy + - make watch_test - window_name: docs layout: main-horizontal options: - main-pane-height: 50 - shell_command_before: - - command -v virtualenv >/dev/null 2>&1 || { pip install virtualenv; } - - '[ -d .env -a -f .env/bin/activate ] && source .env/bin/activate || virtualenv .env' - - '[ ! -d .env/build ] || rm -rf .env/build' - - command -v .env/bin/tmuxp >/dev/null 2>&1 || { pip install -e .; } - - cd ./doc + main-pane-height: 67% + start_directory: docs/ panes: - - shell_command: - - reset - - vim - - :Ex - focus: true - - pwd - - echo 'docs built to '; python -m SimpleHTTPServer - - shell_command: - - command -v sphinx-quickstart >/dev/null 2>&1 || { pip install -r requirements.pip; } - - command -v watching_testrunner >/dev/null 2>&1 || { pip install watching_testrunner; } - - watching_testrunner --basepath ./ --pattern="*.rst" 'make html' - - python -m SimpleHTTPServer + - focus: true + - pane + - pane + - make start diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 00000000000..c17a23a75f0 --- /dev/null +++ b/.tool-versions @@ -0,0 +1,2 @@ +uv 0.7.13 +python 3.13.5 3.12.11 3.11.13 3.10.18 3.9.23 diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 1932929253c..00000000000 --- a/.travis.yml +++ /dev/null @@ -1,22 +0,0 @@ -language: python - -python: - - "2.7" - - "3.3" -env: - - TMUX_VERSION=master - - TMUX_VERSION=1.8 -before_install: - - sudo apt-get update -qq -install: - - "pip install -e ." -before_script: - - sudo apt-get install -qq libevent-dev libncurses-dev - - git clone git://git.code.sf.net/p/tmux/tmux-code tmux - - cd tmux - - git checkout $TMUX_VERSION - - sh autogen.sh - - ./configure && make && sudo make install - - cd .. - - tmux -V -script: python run_tests.py diff --git a/.vim/coc-settings.json b/.vim/coc-settings.json new file mode 100644 index 00000000000..d542a726297 --- /dev/null +++ b/.vim/coc-settings.json @@ -0,0 +1,19 @@ +{ + "[markdown][python]": { + "coc.preferences.formatOnSave": true + }, + "python.analysis.autoSearchPaths": true, + "python.analysis.typeCheckingMode": "basic", + "python.analysis.useLibraryCodeForTypes": true, + "python.formatting.provider": "ruff", + "python.linting.ruffEnabled": true, + "python.linting.mypyEnabled": true, + "python.linting.flake8Enabled": false, + "python.linting.pyflakesEnabled": false, + "python.linting.pycodestyleEnabled": false, + "python.linting.banditEnabled": false, + "python.linting.pylamaEnabled": false, + "python.linting.pylintEnabled": false, + "pyright.organizeimports.provider": "ruff", + "pyright.testing.provider": "pytest", +} diff --git a/.windsurfrules b/.windsurfrules new file mode 100644 index 00000000000..80360fcb75e --- /dev/null +++ b/.windsurfrules @@ -0,0 +1,170 @@ +# libtmux Python Project Rules + + +- uv - Python package management and virtual environments +- ruff - Fast Python linter and formatter +- py.test - Testing framework + - pytest-watcher - Continuous test runner +- mypy - Static type checking +- doctest - Testing code examples in documentation + + + +- Use a consistent coding style throughout the project +- Format code with ruff before committing +- Run linting and type checking before finalizing changes +- Verify tests pass after each significant change + + + +- Use reStructuredText format for all docstrings in src/**/*.py files +- Keep the main description on the first line after the opening `"""` +- Use NumPy docstyle for parameter and return value documentation +- Format docstrings as follows: + ```python + """Short description of the function or class. + + Detailed description using reStructuredText format. + + Parameters + ---------- + param1 : type + Description of param1 + param2 : type + Description of param2 + + Returns + ------- + type + Description of return value + """ + ``` + + + +- Use narrative descriptions for test sections rather than inline comments +- Format doctests as follows: + ```python + """ + Examples + -------- + Create an instance: + + >>> obj = ExampleClass() + + Verify a property: + + >>> obj.property + 'expected value' + """ + ``` +- Add blank lines between test sections for improved readability +- Keep doctests simple and focused on demonstrating usage +- Move complex examples to dedicated test files at tests/examples//test_.py +- Utilize pytest fixtures via doctest_namespace for complex scenarios + + + +- Run tests with `uv run py.test` before committing changes +- Use pytest-watcher for continuous testing: `uv run ptw . --now --doctest-modules` +- Fix any test failures before proceeding with additional changes + + + +- Make atomic commits with conventional commit messages +- Start with an initial commit of functional changes +- Follow with separate commits for formatting, linting, and type checking fixes + + + +- Use the following commit message format: + ``` + Component/File(commit-type[Subcomponent/method]): Concise description + + why: Explanation of necessity or impact. + what: + - Specific technical changes made + - Focused on a single topic + + refs: #issue-number, breaking changes, or relevant links + ``` + +- Common commit types: + - **feat**: New features or enhancements + - **fix**: Bug fixes + - **refactor**: Code restructuring without functional change + - **docs**: Documentation updates + - **chore**: Maintenance (dependencies, tooling, config) + - **test**: Test-related updates + - **style**: Code style and formatting + +- Prefix Python package changes with: + - `py(deps):` for standard packages + - `py(deps[dev]):` for development packages + - `py(deps[extra]):` for extras/sub-packages + +- General guidelines: + - Subject line: Maximum 50 characters + - Body lines: Maximum 72 characters + - Use imperative mood (e.g., "Add", "Fix", not "Added", "Fixed") + - Limit to one topic per commit + - Separate subject from body with a blank line + - Mark breaking changes clearly: `BREAKING:` + + + +- Use fixtures from conftest.py instead of monkeypatch and MagicMock when available +- For libtmux tests, use these provided fixtures for fast, efficient tmux resource management: + - `server`: Creates a temporary tmux server with isolated socket + - `session`: Creates a temporary tmux session in the server + - `window`: Creates a temporary tmux window in the session + - `pane`: Creates a temporary tmux pane in the pane + - `TestServer`: Factory for creating multiple independent servers with unique socket names +- Example usage with server fixture: + ```python + def test_something_with_server(server): + # server is already running with proper configuration + my_session = server.new_session("test-session") + assert server.is_alive() + ``` +- Example usage with session fixture: + ```python + def test_something_with_session(session): + # session is already created and configured + new_window = session.new_window("test-window") + assert new_window in session.windows + ``` +- Customize session parameters by overriding the session_params fixture: + ```python + @pytest.fixture + def session_params(): + return { + 'x': 800, + 'y': 600, + 'window_name': 'custom-window' + } + ``` +- Benefits of using libtmux fixtures: + - No need to manually set up and tear down tmux infrastructure + - Tests run in isolated tmux environments + - Faster test execution + - Reliable test environment with predictable configuration +- Document in test docstrings why standard fixtures weren't used for exceptional cases +- Use tmp_path (pathlib.Path) fixture over Python's tempfile +- Use monkeypatch fixture over unittest.mock + + + +- Prefer namespace imports over importing specific symbols +- Import modules and access attributes through the namespace: + - Use `import enum` and access `enum.Enum` instead of `from enum import Enum` + - This applies to standard library modules like pathlib, os, and similar cases +- For typing, use `import typing as t` and access via the namespace: + - Access typing elements as `t.NamedTuple`, `t.TypedDict`, etc. + - Note primitive types like unions can be done via `|` pipes + - Primitive types like list and dict can be done via `list` and `dict` directly +- Benefits of namespace imports: + - Improves code readability by making the source of symbols clear + - Reduces potential naming conflicts + - Makes import statements more maintainable + diff --git a/CHANGES b/CHANGES index 09f0714ea62..125c30fdce3 100644 --- a/CHANGES +++ b/CHANGES @@ -1,276 +1,2457 @@ -tmuxp Changelog -=============== +# Changelog -Here you can find the recent changes to tmuxp. +To install the unreleased tmuxp version, see [developmental releases](https://tmuxp.git-pull.com/quickstart.html#developmental-releases). -2013-10-29 ----------- +[pip](https://pip.pypa.io/en/stable/): -- [cli] enhancements to prompts -- [cli] ``tmuxp import`` for teamocil and tmuxinator now has a wizard and offers +```console +$ pip install --user --upgrade --pre tmuxp +``` + +[pipx](https://pypa.github.io/pipx/docs/): + +```console +$ pipx install --suffix=@next 'tmuxp' --pip-args '\--pre' --force +// Usage: tmuxp@next load yoursession +``` + +## tmuxp 1.56.0 (Yet to be released) + + + +- _Future release notes will be placed here_ + +## tmuxp 1.55.0 (2025-02-26) + +_Maintenance only, no bug fixes or new features_ + +### Breaking changes + +- libtmux: Bump minimum version from 0.45.0 -> 0.46.0 (#969) + + 0.46.0+ needs this release of tmuxp to stay up-to-date with test helpers. + +## tmuxp 1.54.0 (2025-02-23) + +### Breaking changes + +- libtmux: Bump minimum version from 0.44.2 -> 0.45.0 (#968) + + 0.45.0+ needs this release of tmuxp to stay up-to-date with test helpers. + +### Development + +- CI: Check CLI modules runtime dependencies (#967) + + An extra set of checks on top of #965. + +## tmuxp 1.53.0 (2025-02-19) + +### Bug fixes + +- Fix import type unavailable at runtime (#965) + +### Development + +- CI: Check for runtime dependencies (#965) +- libtmux: Bump minimum version from 0.42.0 -> 0.44.2 (#962) +- Tests: Improve parametrized test suite (#964) + + Convert remaining `pytest.mark.parametrize()` tests to `NamedTuple` fixtures: + + - Improved test maintainability and readability + - Added descriptive test IDs for better failure reporting + - Added docstrings to test fixtures + - Files updated: + - `test_cli.py`: Added `HelpTestFixture` + - `test_convert.py`: Added `ConvertTestFixture` and `ConvertJsonTestFixture` + - `test_freeze.py`: Added `FreezeTestFixture` and `FreezeOverwriteTestFixture` + - `test_import.py`: Added `ImportTestFixture`, `ImportTeamocilTestFixture`, and `ImportTmuxinatorTestFixture` + - `test_load.py`: Added `ZshAutotitleTestFixture`, `LogFileTestFixture`, `PluginVersionTestFixture`, and `PluginMissingTestFixture` + +## tmuxp 1.52.2 (2025-02-02) + +### Bug fixes + +- `run_before_script()`: Additional output capturing improvements (#960) + +## tmuxp 1.52.1 (2025-02-02) + +### Bug fixes + +- `run_before_script()`: Fix output issue (#959) + +## tmuxp 1.52.0 (2025-02-02) + +_Maintenance only, no bug fixes or new features_ + +### Development + +- libtmux: Bump minimum version from 0.40.1 -> 0.42.0 (#958) + + - `run_before_script()`: Remove reliance on `console_to_str()` + +## tmuxp 1.51.0 (2025-02-02) + +_Maintenance only, no bug fixes or new features_ + +### Development + +#### chore: Implement PEP 563 deferred annotation resolution (#957) + +- Add `from __future__ import annotations` to defer annotation resolution and reduce unnecessary runtime computations during type checking. +- Enable Ruff checks for PEP-compliant annotations: + - [non-pep585-annotation (UP006)](https://docs.astral.sh/ruff/rules/non-pep585-annotation/) + - [non-pep604-annotation (UP007)](https://docs.astral.sh/ruff/rules/non-pep604-annotation/) + +For more details on PEP 563, see: https://peps.python.org/pep-0563/ + +## tmuxp 1.50.1 (2024-12-24) + +### Development + +- libtmux: Bump minimum version 0.40.0 -> 0.40.1 (#956) + + Bug fix for server environmental variables from https://github.com/tmux-python/libtmux/pull/553. + Thank you @ppentchev! + +## tmuxp 1.50.0 (2024-12-20) + +_Maintenance only, no bug fixes or new features_ + +### Development + +- libtmux: Bump minimum version 0.39.0 -> 0.40.0 (#954) + + Adopts Python 3.9 syntax features + +- Aggressive automated lint fixes via `ruff` (#953) + + via ruff v0.8.4, all automated lint fixes, including unsafe and previews were applied for Python 3.9: + + ```sh + ruff check --select ALL . --fix --unsafe-fixes --preview --show-fixes; ruff format . + ``` + +## tmuxp 1.49.0 (2024-11-26) + +_Maintenance only, no bug fixes or new features_ + +### Breaking changes + +- Drop Python 3.8. end of life was October 7th, 2024 (#951) + + tmuxp 1.48.0 was the last release for Python 3.8. + + The minimum python for tmuxp as of 1.49.0 is Python 3.9 +- libtmux 0.38.1 -> 0.39.0 (Minimum Python version of 3.9) + +## tmuxp 1.48.0 (2024-11-26) + +_Maintenance only, no bug fixes or new features_ + +### Breaking changes + +#### Project and package management: poetry to uv (#949) + +[uv] is the new package and project manager for the project, replacing Poetry. + +[uv]: https://github.com/astral-sh/uv + +#### Build system: poetry to hatchling (#949) + +[Build system] moved from [poetry] to [hatchling]. + +[Build system]: https://packaging.python.org/en/latest/tutorials/packaging-projects/#choosing-a-build-backend +[poetry]: https://github.com/python-poetry/poetry +[hatchling]: https://hatch.pypa.io/latest/ + +#### Minimum libtmux version 0.37.0 -> 0.38.1 (#950) + +Built with uv. + +### Development + +- Code quality: Use f-strings in more places (#931) + + via [ruff 0.4.2](https://github.com/astral-sh/ruff/blob/v0.4.2/CHANGELOG.md). + +[uv]: https://github.com/astral-sh/uv + +## tmuxp 1.47.0 (2024-04-21) + +_Maintenance only, no bug fixes or new features_ + +### Developmental + +- libtmux: 0.36.0 -> 0.37.0 (#929) + + Internal improvements to test suite (pytest-xdist and relaxing `retry_until()` tests) + +## tmuxp 1.46.0 (2024-04-12) + +### Breaking change + +#### Workspace builder now detects terminal size (#926) + +Dimensions used by workspace builder now use {py:func}`shutil.get_terminal_size()`. + +In conjunction with `main-pane-height: 67%`, for instance, this will render a +proportional layout: + +```yaml +session_name: my session +windows: +- window_name: example with percentage + focus: True + layout: main-horizontal + options: + main-pane-height: 67% + panes: + - focus: true + - pane +``` + +To use old behavior, set `TMUXP_DETECT_TERMINAL_SIZE=0` in your terminal +environment and file an issue on the tracker. + +### Documentation + +- Automatically linkify links that were previously only text. + +### Development + +- Another `ruff` linting pass, this time with ruff 0.3.7 (#928) +- poetry: 1.8.1 -> 1.8.2 + + See also: https://github.com/python-poetry/poetry/blob/1.8.2/CHANGELOG.md + +## tmuxp 1.45.0 (2024-03-24) + +_Maintenance only, no bug fixes or new features_ + +### Development + +- Aggressive automated lint fixes via `ruff` (#922) + + via ruff v0.3.4, all automated lint fixes, including unsafe and previews were applied: + + ```sh + ruff check --select ALL . --fix --unsafe-fixes --preview --show-fixes; ruff format . + ``` + + Branches were treated with: + + ```sh + git rebase \ + --strategy-option=theirs \ + --exec 'poetry run ruff check --select ALL . --fix --unsafe-fixes --preview --show-fixes; poetry run ruff format .; git add src tests; git commit --amend --no-edit' \ + origin/master + ``` + +## tmuxp 1.44.0 (2024-03-24) + +### Breaking changes + +- libtmux: 0.35.1 -> 0.36.0 (#923) + + Internal refactorings and maintenance. + +## tmuxp 1.43.1 (2024-03-24) + +### Breaking changes + +- libtmux: 0.35.0 -> 0.35.1 + + Improved support in libtmux when multiple clients attached to a session in a server. + +## tmuxp 1.43.0 (2024-03-17) + +### Breaking changes + +- libtmux: 0.34.0 -> 0.35.0 (#920) + + Simplify redundant `target` passing and `window-index` usages (#920) + +## tmuxp 1.42.0 (2024-03-17) + +### Breaking changes + +- libtmux: 0.33.0 -> 0.34.0 (#919) + + Explicit targets in `cmd()` + +## tmuxp 1.41.1 (2024-03-17) + +_Maintenance only, no bug fixes or new features_ + +- WorkspaceBuilder: Use `Pane.split` instead of `Window.split_window` + +## tmuxp 1.41.0 (2024-03-17) + +_Maintenance only, no bug fixes or new features_ + +### Breaking changes + +- libtmux: 0.32.0 -> 0.33.0 (#918) + + Move `split_window()` to `split()`. + +### Development + +- poetry: 1.7.1 -> 1.8.1 + + See also: https://github.com/python-poetry/poetry/blob/1.8.1/CHANGELOG.md + +## tmuxp 1.40.0 (2024-03-32) + +_Maintenance only, no bug fixes or new features_ + +### Breaking changes + +- libtmux: 0.31.0.post0 -> 0.32.0 (#914) + + Export fix, ruff 0.3.0 updates. + +### Development + +- ruff 0.2.2 -> 0.3.0 (#913) + + Related formattings. Update CI to use `ruff check .` instead of `ruff .`. + +## tmuxp 1.39.0 (2024-02-17) + +_Maintenance only, no bug fixes or new features_ + +#### Breaking changes + +- libtmux: 0.30.2 -> 0.31.0 (#912) + +- Renamings of libtmux 0.31.0's streamlining of `cmd()`, renaming of `attached_{window,pane}s` to + `active_{window,pane}s`. + +## tmuxp 1.38.0 (2024-02-16) + +_Maintenance only, no bug fixes or new features_ + +#### Breaking changes + +- libtmux: 0.28.1 -> 0.30.1 (#911) + + Updated method names + +- Rename methods to libtmux v0.30.0+-style (#911). + +## tmuxp 1.37.1 (2024-02-15) + +#### Development + +- libtmux: 0.28.0 -> 0.28.1 + + Maintenance release (docs and CI bumps) + +#### Testing + +- CI: Bump actions to node 20+ versions + +## tmuxp 1.37.0 (2024-02-14) + +### Breaking changes + +- libtmux: 0.27.0 -> 0.28.0 (#910) + + Refresh and resize improvements + +### Tests + +- CI: Add tmux 3.4 to test matrix (#909) + +## tmuxp 1.36.0 (2024-02-07) + +_Maintenance only, no bug fixes or new features_ + +### Breaking changes + +- libtmux: 0.26.0 -> 0.27.0 (#908) + + QueryList generic typing improvements. + +## tmuxp 1.35.0 (2024-02-07) + +_Maintenance only, no bug fixes or new features_ + +### Breaking changes + +- libtmux: 0.25.0 -> 0.26.0, maintenance release (#906) + + Doc string + linting stringency updates. + +### Development + +- Strengthen linting (#907) + + - Add flake8-commas (COM) + + - https://docs.astral.sh/ruff/rules/#flake8-commas-com + - https://pypi.org/project/flake8-commas/ + + - Add flake8-builtins (A) + + - https://docs.astral.sh/ruff/rules/#flake8-builtins-a + - https://pypi.org/project/flake8-builtins/ + + - Add flake8-errmsg (EM) + + - https://docs.astral.sh/ruff/rules/#flake8-errmsg-em + - https://pypi.org/project/flake8-errmsg/ + +## tmuxp 1.34.0 (2023-12-21) + +_Maintenance only, no bug fixes or new features_ + +### Breaking changes + +- libtmux: 0.24.1 -> 0.25.0, maintenance release (#896) + + Improve styling via pydocstyle. + +- `config_reader`: Move to `tmuxp._internal` (#897) +- `_types`: Move to `tmuxp._internal` (#900) + +### Documentation + +- Refactor API docs to split across multiple pages (#898) +- Remove unused reStructuredText section headers from some modules (#898) + +## tmuxp 1.33.0 (2023-12-21) + +_Maintenance only, no bug fixes or new features_ + +### CI + +- Move CodeQL from advanced configuration file to GitHub's default +- Add pydocstyle rule to ruff (#891) + +### Documentation + +- Add docstrings to functions, methods, classes, and packages (#891) + +## tmuxp 1.32.1 (2023-11-23) + +### Packaging + +- pypoetry: Add `gp-lib` to `test` dependency group + +### Development + +- libtmux: 0.24.0 -> 0.24.1 (maintenance release) + +### Tests + +- Shell tests: Use named, typed test fixture (#893) + +## tmuxp 1.32.0 (2023-11-19) + +_Maintenance only, no bug fixes or new features_ + +### Packaging + +- Move pytest configuration to `pyproject.toml` (#886) +- Poetry: 1.6.1 -> 1.7.0 + + See also: https://github.com/python-poetry/poetry/blob/1.7.0/CHANGELOG.md + +- Add Python 3.12 to trove classifiers +- Packaging (poetry): Fix development dependencies + + Per [Poetry's docs on managing dependencies] and `poetry check`, we had it wrong: Instead of using extras, we should create these: + + ```toml + [tool.poetry.group.group-name.dependencies] + dev-dependency = "1.0.0" + ``` + + Which we now do. + + [Poetry's docs on managing dependencies]: https://python-poetry.org/docs/master/managing-dependencies/ + +### Development + +- libtmux: 0.23.0 -> 0.24.0 (maintenance release) +- Move formatting from `black` to [`ruff format`] (#890) + + This retains the same formatting style of `black` while eliminating a + dev dependency by using our existing rust-based `ruff` linter. + + [`ruff format`]: https://docs.astral.sh/ruff/formatter/ + +- CI: Update action packages to fix warnings + + - [dorny/paths-filter]: 2.7.0 -> 2.11.1 + + [dorny/paths-filter]: https://github.com/dorny/paths-filter + +## tmuxp 1.31.0 (2023-09-23) + +### Breaking changes + +- Python 3.7 Dropped (#885) + +### Development + +- **Improved typings** + + Now [`mypy --strict`] compliant (#859) + + [`mypy --strict`]: https://mypy.readthedocs.io/en/stable/command_line.html#cmdoption-mypy-strict + +- Poetry 1.5.1 -> 1.6.1 (#885) + +## tmuxp 1.30.1 (2023-09-09) + +_Maintenance only, no bug fixes or new features_ + +### Breaking changes + +- Cut last python 3.7 release (EOL was June 27th, 2023) + + For security updates, a 1.30.x branch can be maintained for a limited time, + if necessary. + +## tmuxp 1.30.0 (2023-09-04) + +_Maintenance only, no bug fixes or new features_ + +### Development + +- Code quality improved via [ruff] rules (#879) + + This includes fixes made by hand alongside ruff's automated fixes. The more + stringent rules include import sorting, and still runs almost instantly + against the whole codebase. + +- CI: `black . --check` now runs on pushes and pull requests + +### Packaging + +- libtmux: v0.23.1 -> v0.23.2 + + Final Python 3.7 release of libtmux + +### Documentation + +- README Example for Nix (#883), thank you @ChristopherHarwell! + +## tmuxp 1.29.1 (2023-09-02) + +_Maintenance only, no bug fixes or new features_ + +### Development + +- libtmux: v0.23.0 -> v0.23.1 + + Typo fixes inside libtmux + +### Docs + +- Typo fixes (#884), thanks @kianmeng + +## tmuxp 1.29.0 (2023-08-20) + +_Maintenance only, no bug fixes or new features_ + +### Development + +- libtmux v0.22.2 -> v0.23.0 (#882) + + Code quality improvements from https://github.com/tmux-python/libtmux/pull/488 + +### Post-release: v1.29.0post0 (2023-09-02) + +- libtmux post-release bumps + + Re-add comments that went missing from `ruff` formatter. + +## tmuxp 1.28.2 (2023-08-20) + +_Maintenance only, no bug fixes or new features_ + +### Packaging + +- libtmux v0.22.1 -> v0.22.2 + + Removes `setuptools` from `build-system` requirements + +## tmuxp 1.28.1 (2023-05-28) + +_Maintenance only, no bug fixes or new features_ + +### Development + +- Add back `black` for formatting + + This is still necessary to accompany `ruff`, until it replaces black. + +## tmuxp 1.28.0 (2023-05-27) + +_Maintenance only, no bug fixes or new features_ + +### Internal improvements + +- Move formatting, import sorting, and linting to [ruff]. + + This rust-based checker has dramatically improved performance. Linting and + formatting can be done almost instantly. + + This change replaces black, isort, flake8 and flake8 plugins. + +- libtmux: 0.21.1 -> 0.22.0 (Moved to ruff as well) +- poetry: 1.4.0 -> 1.5.0 + + See also: https://github.com/python-poetry/poetry/releases/tag/1.5.0 + +[ruff]: https://ruff.rs + +## tmuxp 1.27.1 (2022-04-07) + +_Maintenance only, no bug fixes or new features_ + +### Development + +- Update mypy to 1.2.0 +- libtmux 0.21.0 -> 0.21.1 (mypy / typing updates only) + +## tmuxp 1.27.0 (2022-01-29) + +_Maintenance only, no bug fixes or new features_ + +### Internal improvements + +- libtmux 0.20.0 -> 0.21.0 (#865) + + This updates the separator uses from libtmux to be a rarer character. See + [libtmux#475](https://github.com/tmux-python/libtmux/pull/475). + +## tmuxp 1.26.0 (2023-01-15) + +_Maintenance only, no bug fixes or new features_ + +### Internal improvements + +- libtmux 0.19.1 -> 0.20.0 (#863) + + Improves support around `Server.new_session` and its params, e.g. `-x` and + `-y`. + +## tmuxp 1.25.0 (2023-01-07) + +### Internal improvements + +- libtmux 0.18.3 -> 0.19.1 (#862) + + 0.19.1 has a fix for `Window.set_window_option()` + +## tmuxp 1.24.1 (2023-01-07) + +### Internal improvements + +- libtmux: 0.18.2 -> 0.18.3 (#861) + + via [libtmux#466](https://github.com/tmux-python/libtmux/pull/466) + +- tests(test_pane_order): Improve reliability of `pane_current_path` test. + +## tmuxp 1.24.0 (2022-12-30) + +_Maintenance only, no bug fixes or new features_ + +### Internal improvements + +- libtmux: 0.18.1 -> 0.18.2 + + Fix for starting new sessions at default socket / temporary directory + ([libtmux#464](https://github.com/tmux-python/libtmux/pull/464)) + +- CLI Tests: Refactor `tests/cli` (#858) + + - Fix resolution of directories + +- WorkspaceBuilder: Require `Server` in constructor (#857) + +## tmuxp 1.23.0 (_yanked_, 2022-12-28) + +_Yanked release: `tmuxp load` issues, see #856_ + +### Internal improvements + +_Maintenance only, no bug fixes or new features_ + +- Type annotations: More mypy typings (#796) + +## tmuxp 1.22.1 (2022-12-27) + +_Maintenance only, no bug fixes or new features_ + +- Update libtmux 0.18.0 -> 0.18.1 + + Only code documentation fixes + +## tmuxp 1.22.0 (2022-12-27) + +### Improvement + +- `tmuxp shell`: now detects current `server` via `TMUX` (#854) + +## tmuxp 1.21.0 (2022-12-27) + +_Maintenance only, no bug fixes or new features_ + +- libtmux 0.17.2 -> 0.18.0 + + Server: Improved `__repr__` + +## tmuxp 1.20.3 (2022-12-27) + +- Fix warnings for `_update_panes()` in builder + +## tmuxp 1.20.2 (2022-12-27) + +_Internal update only_ + +- libtmux 0.17.1 -> 0.17.2 + + More deprecation warnings + +## tmuxp 1.20.1 (2022-12-27) + +_Internal update only_ + +- libtmux 0.17.0 -> ~0.17.1 + + Deprecation warning updates, doc fixes + +### Development + +- Poetry 1.2.2 -> 1.3.1 + +## tmuxp 1.20.0 (2022-12-26) + +### Breaking change + +- libtmux 0.16 -> 0.17 (#850) + + This includes the API overhaul from + [libtmux#426](https://github.com/tmux-python/libtmux/pull/426). + +### Development + +- Tests: Stabilization fix for automatic rename test (#853) + +## tmuxp 1.19.1 (2022-12-12) + +### Fixes + +- Update libtmux 0.16.0 -> 0.16.1 + + This removes the underlying dependency on `packaging` + +## tmuxp 1.19.0 (2022-12-10) + +### What's new + +- Environment variables for windows / panes (#845) + + _Requires tmux 3.0_ + + Allow to configure window and pane specific environment variables + + Having a setup like: + + ```yaml + session_name: env-demo + environment: + DATABASE_URL: "sqlite3:///default.db" + windows: + - window_name: dev + environment: + DATABASE_URL: "sqlite3:///dev-1.db" + panes: + - pane + - environment: + DATABASE_URL: "sqlite3:///dev-2.db" + ``` + + will result in a window with two panes. In the first pane `$DATABASE_URL` is + `sqlite3:///dev-1.db`, while in the second pane it is `sqlite3://dev-2.db`. + Any freshly created window gets `sqlite3:///default.db` as this is what was + defined for the session. + + Credit: @zappolowski + +### Internal + +- Update libtmux 0.15.9 -> 0.16.0 + + - Support for environmental variables + - Remove reliance on `distutils.version.LooseVersion` for + `libtmux._compat.LegacyVersion` + +- Fix distutil warnings by using libtmux 0.16.0's `LegacyVersion` (#727) + +## tmuxp 1.18.2 (2022-11-06) + +**Maintenance release, no features or fixes** + +### Development + +- Bump libtmux from 0.15.9 -> 0.15.10 (only test tweaks) +- Poetry no longer forces `in-project: true` + +## tmuxp 1.18.1 (2022-10-31) + +### Bug fix + +- cli: `tmuxp load`: Fix pass-through of config files (#843) + +## tmuxp 1.18.0 (2022-10-30) + +We now refer to configs as workspaces. Other than just, just maintenance. + +### Internal refactoring (#840) + +#### Rename config to workspace + +Reference to "config" are now "workspace" + +#### Organize files + +- `cli/utils.py` functions moved to `workspace/finders.py` +- `config.py` split between: + + - `workspace/finders.py` + - `workspace/freezer.py` + - `workspace/importers.py` + - `workspace/validation.py` + +- `workspacebuilder.py` split into: + + - `workspace/builder.py` + - `workspace/freezer.py` + + `config.inline` moved to freezer + +#### Tests + +- `tests/fixtures/{workspacebuilder,workspacefreezer}` -> `tests/fixtures/workspace/{builder,freezer}` +- `tests/test_import_{teamocil,tmuxinator}.py` -> `tests/workspace/test_import_{teamocil,tmuxinator}.py` + +## tmuxp 1.17.3 (2022-10-30) + +**Maintenance release, no features or fixes** + +### Development + +- Add python 3.11 to test grid, pyenv / asdf files (#842) + +### Packaging + +- Add python 3.11 to classifiers + +## tmuxp 1.17.2 (2022-10-29) + +### Bug fix + +- CLI: Fix loading of multiple workspaces in `tmuxp load` (#838 fixes #837) + +## tmuxp 1.17.1 (2022-10-15) + +### Minor completion improvements + +- Improved shtab completions for files with `tmuxp load [tab]` (#834) + +### Internal + +- Remove unused completion code leftover from click (#834) + +## tmuxp 1.17.0 (2022-10-09) + +### Breaking changes + +- **Completions have changed** (#830) + + Completions now use a different tool: [shtab]. See the [completions page] for more information. + + If you were using earlier versions of tmuxp (earlier than 1.17.0), you may need to uninstall the old completions, first. + + [completions page]: https://tmuxp.git-pull.com/cli/completion.html + [shtab]: https://docs.iterative.ai/shtab/ + +- Deprecate `click` in favor of {mod}`argparse` (#830) + +### Packages + +- Remove `click` dependency + +## tmuxp 1.16.2 (2022-10-08) + +### Packaging + +- Add `yaml` to required dependencies (#833, credit: @heindsight) + +## tmuxp 1.16.1 (2022-10-02) + +### Bug fix + +- Update libtmux 0.15.7 -> 0.15.8 + + Includes an improvement for blank window names, e.g. `window_name: ''` + + See also: https://github.com/tmux-python/libtmux/pull/444 + +## tmuxp 1.16.0 (2022-10-01) + +**Maintenance release, no features or fixes** + +### Internal + +- Add `ConfigReader`: Our clean, typed parser for raw strings and files (#828) + + This is our shiny, new, 200-line, doctested and typed parser that replaces `kaptan`. + +### Packaging + +- Drop kaptan dependency (#828) + +## tmuxp 1.15.3 (2022-10-01) + +### Bug fixes + +- WorkspaceBuilder: Fix bug in `start_directory` (#829, credit: @heindsight) + +## tmuxp 1.15.2 (2022-09-24) + +**Maintenance release, no features or fixes** + +### Packaging + +- Move conftest.py to root, to avoid packaging in wheel (#826 vs #825) + +## tmuxp 1.15.1 (2022-09-23) + +**Maintenance release, no features or fixes** + +### Infrastructure + +- CI speedups (#819) + + - Split out release to separate job so the PyPI Upload docker image isn't pulled on normal runs + - Clean up CodeQL + +- Bump poetry 1.1.x -> 1.2.x +- Bump libtmux 0.15.1 -> 0.15.7 + - 0.15.7 (move `.coveragerc` -> `pyproject.toml`) + - 0.15.6 (#823, pytest fixes, packaging improvements) + - 0.15.3 (#821, pytest plugin improvements, root-level + conftest.py) +- Move `.coveragerc` -> `pyproject.toml` (#824) + +### Packaging + +- Remove `MANIFEST.in` + + This is handled by poetry's `include` in pyproject.toml. + +- Remove `.tmuxp-before-script.sh` from `.tmuxp.yaml` + +## tmuxp 1.15.0 (2022-09-11) + +**Maintenance release, no features or fixes** + +### Development + +- Project moved to `src/` layout (#814) + +## tmuxp 1.14.0 (2022-09-11) + +**Maintenance release, no features or fixes** + +There will be several of these releases as infrastructure and APIs are upgraded +to facilitate fixes for layout issues and general contributions. + +### Development + +- libtmux bumped to v0.15.1 + + This includes a major retooling underneath, including `src/` layout and + testing of `doctest` in documentation. + +[v0.15.1]: https://github.com/tmux-python/libtmux/blob/v0.15.1/CHANGES#libtmux-0151-2022-09-11 + +### Documentation + +- Render changelog in [`linkify_issues`] (#812) +- Fix Table of contents rendering with sphinx autodoc with [`sphinx_toctree_autodoc_fix`] (#812) +- Test doctests in our docs via [`pytest_doctest_docutils`] (built on [`doctest_docutils`]) (#812) + +[`linkify_issues`]: https://gp-libs.git-pull.com/linkify_issues/ +[`sphinx_toctree_autodoc_fix`]: https://gp-libs.git-pull.com/sphinx_toctree_autodoc_fix/ +[`pytest_doctest_docutils`]: https://gp-libs.git-pull.com/doctest/pytest.html +[`doctest_docutils`]: https://gp-libs.git-pull.com/doctest + +## tmuxp 1.13.3 (2022-09-10) + +- Revert v1.13.1's #793 change for now, the behavior behind launching workspace + layouts need to be understood clearly. Future releases will decisively solve + this issue (#811). + +## tmuxp 1.13.2 (2022-09-10) + +### Bug fixes + +- Layout size has been bumped for those experiencing layout spacing issues + (#809, fixes #800) + + If you encounter issues with pane spacing, consider passing an `option` like + so: + + ```yaml + session_name: main-pane-height + start_directory: "~" + options: + default-size: 999x999 + windows: + - window_name: my window name + layout: main-horizontal + options: + main-pane-height: 30 + panes: + - shell_command: top + - shell_command: top + - shell_command: top + - shell_command: echo "hey" + - shell_command: echo "moo" + ``` + +### Development + +- Add [flake8-bugbear](https://github.com/PyCQA/flake8-bugbear) (#807) +- Add [flake8-comprehensions](https://github.com/adamchainz/flake8-comprehensions) (#808) + +## tmuxp 1.13.1 (2022-08-21) + +### Bug fixes + +- Fix layout related issues from #667, #704, #737, via + #793. Thank you @nvasilas. + +## tmuxp 1.13.0 (2022-08-14) + +### Internal + +- libtmux updated from v0.12 to v0.14 #790 +- Add [doctest](https://docs.python.org/3/library/doctest.html) w/ + [pytest + doctest](https://docs.pytest.org/en/7.1.x/how-to/doctest.html) via #791. +- Added basic [mypy](http://mypy-lang.org/) type annotations via #786 + +## tmuxp 1.12.1 (2022-08-04) + +### Bug fix + +- #787 Fix #724 Fix `start_directory` issue with first pane, + credit: nvasilas. + +## tmuxp 1.12.0 (2022-07-31) + +_Mostly internal cleanups, no features_ + +### Tests + +- #774 Fix #620 tests not finishing +- #777 Fix #778 to move the old, broken `retry()` to the new + `retry_until()`, credit: @categulario. +- #783 Fix #620 Symlink edge for for pane order tests, credit: @categulario. +- #781 Testing with ZSH will mock `~/.zshrc` startup file for cleaner + pane outputs. + +## tmuxp 1.11.1 (2022-05-02) + +### Bug fix + +- #775: Assure click 8+ + + tmuxp 1.10 supports click 7.x. + +## tmuxp 1.11.0 (2022-04-24) + +### Compatibility + +- #773: Allow click 8.1.x +- #770: Fix shell completion for `tmuxp load` {kbd}`tab` and `tmuxp freeze` + {kbd}`tab` +- Note: Requires click 8+ (#770) + +### Maintenance + +- #762: CLI: Break up into modules in _cli.py_ -> _cli/{command}_ +- #761: Refactor _tests/_ constants and fixtures +- Show libtmux version with `-V` / `--version` + +### Development + +- Remove tox and tox-poetry-installer + + This created issues with running poetry while inside the virtualenv. + +- Publish packages to PyPI via github action by setting git tag. + +## tmuxp 1.10.1 (2022-04-17) + +### Compatibility + +- #773 (backport): Allow click 8.1.x + +## tmuxp 1.10.0 (2022-03-19) + +### Compatibility + +- Final python 3.7 and 3.8 release + + Bug fixes and security updates will go to + [`v1.10.x`](https://github.com/tmux-python/tmuxp/tree/v1.10.x) + +### What's new + +- #747: Skip execution via `enter: false` + + See {ref}`enter`. + + :::{note} + + _Experimental setting_: behavior and api is subject to change until stable. + + ::: + + ```yaml + session_name: Should not execute + windows: + - panes: + - shell_command: + - echo "this sends" + - cmd: echo "___$((1 + 3))___" + enter: false + # pane-wide skip + - shell_command: + - echo "___$((1 + 3))___" + enter: false + ``` + +- #750: Pause execution via `sleep_before: [int]` and `sleep_after: [int]` + + See {ref}`sleep`. + + :::{note} + + _Experimental setting_: behavior and api is subject to change until stable. + + ::: + + ```yaml + session_name: Pause / skip command execution (command-level) + windows: + - panes: + - shell_command: + # Executes immediately + - echo "___$((11 + 1))___" + # Delays before sending 2 seconds + - cmd: echo "___$((1 + 3))___" + sleep_before: 2 + # Executes immediately + - cmd: echo "___$((1 + 3))___" + # Pauses 2 seconds after + - cmd: echo "Stuff rendering here!" + sleep_after: 2 + # Executes after earlier commands (after 2 sec) + - cmd: echo "2 seconds later" + ``` + +- #701: `tmuxp freeze` now accepts `--quiet` and `--yes` along with the + `--config-format` and filename (`--save-to`). This means you can do it all in + one command: + + `tmuxp freeze -yqo .tmuxp.yaml` + + Or bind it to `.tmux.conf` itself: `bind -T root C-s run-shell "tmuxp freeze -yqo .tmuxp.yaml"` + + Credit: [@davidatbu](https://github.com/davidatbu) + +- #672: Panes now accept `shell` for their initial command. + + Equivalent to `tmux split-windows`'s `[shell-command]` + + ```yaml + session_name: Pane shell example + windows: + - window_name: first + window_shell: /usr/bin/python2 + layout: even-vertical + suppress_history: false + options: + remain-on-exit: true + panes: + - shell: /usr/bin/python3 + shell_command: + - print('This is python 3') + - shell: /usr/bin/vim -u none + shell_command: + - iAll panes have the `remain-on-exit` setting on. + - When you exit out of the shell or application, the panes will remain. + - Use tmux command `:kill-pane` to remove the pane. + - Use tmux command `:respawn-pane` to restart the shell in the pane. + - Use and then `:q!` to get out of this vim window. :-) + - shell_command: + - print('Hello World 2') + - shell: /usr/bin/top + ``` + + Credit: @jerri + +### Improvements + +- Improve `tmuxp freeze` UX flow, credit @joseph-flinn (#657, in re: #627) +- `tmuxp freeze` will now detect the attached session if no session name + is specified. Credit: @will-ockmore. (#660) + +### Bugs + +- Fix loading of `.yml` files with `tmuxp convert`, thank you @kalixi! (#725) + +### Internal API + +- #752: Command structure (internal API) + + To pave the way for per-command options such as `enter: false` (#53), commands are now a different format: + + Before, [`str`](str): + + ```python + "echo hello" + ``` + + After, {py:class}`dict`: + + ```python + { + "cmd": "echo hello" + } + ``` + + This is purely internal. Normal usage should be the same since the + configuration emits the equivalent result. + +- #752: Configuration parsing refactorings + +### Development + +- Run through black + isort with string normalization (#738). This way we can + use black without any configuration. (One-time, big diff) +- Run codebase through pyupgrade (#745) +- Add `codeql-analysis.yml` to try it out + +### Documentation + +- Move to `furo` sphinx theme +- Reorganize documentation into sections +- Added examples for `enter: false` and `sleep: [second]` + +## tmuxp 1.9.4 (2022-01-10) + +### Packaging + +- `poetry build` used to package in place of `python setup.py build` (#729) + + Package maintainers: If you run into any issues check in at #625 and file an issue. + + Additionally, `libtmux` has been pinned to a similar release at + [0.10.3](https://pypi.org/project/libtmux/0.10.3/) which has used the new + build process. + +- `poetry publish` instead of `twine upload dist/*` (#729) + + Similar to the above, reach out to the #625 issue if you bump into problems. + +### What's new + +- `tmuxp edit` for configuration changes (#707, @GlebPoljakov) + + Inside of configuration directory: `tmuxp edit yourconfig` + + Inside a project: `tmuxp edit .` + +### Removed support + +- Python 3.6 support has been removed (#726) + +### Development + +- We are trying `.pre-commit-config.yaml` in pull requests to automate + black, isort and flake8 for those who forget (#726) +- Poetry update 1.1.7 -> 1.1.12 and use new installer URL (#726) +- Black updated 21.9b0 -> 21.12b0 (#726) + +## tmuxp 1.9.3 (2021-10-30) + +- #700: Add `-h` / `--help` option, thanks @GHPS +- #689: Update poetry to 1.1 + - CI: Use poetry 1.1.7 and `install-poetry.py` installer + - Relock poetry.lock at 1.1 (w/ 1.1.7's fix) +- #696: Typo fix, thanks @inkch + +## tmuxp 1.9.2 (2021-06-17) + +- #686: Allow click 8.0.x +- Remove `manual/`, move to https://github.com/tmux-python/tmux-manuals + +## tmuxp 1.9.1 (2021-06-16) + +- libtmux: Update to 0.10.1+ to include `Window.select_window()` fix + + https://github.com/tmux-python/libtmux/pull/271 + +## tmuxp 1.9.0 (2021-06-16) + +- libtmux: Update to 0.10.x + +## tmuxp 1.8.2 (2021-06-15) + +- #474 Re-release with `python setup.py sdist bdist_wheel` to + fix missing test files + +## tmuxp 1.8.1 (2021-06-14) + +- #681 Bump version to make homebrew release easier + +## tmuxp 1.8.0.post0 (2021-06-14) + +- #681 tmuxp is now available on homebrew! Thank you @jvcarli! + +## tmuxp 1.8.0 (2021-06-14) + +- #662 CI: Only update docs when changed +- #666 CI: Move test plugin packages from pyproject.toml to + pytest fixtures. Fixes #658 +- #661 + + - Bump minimum version 3.5 -> 3.6 + - Drop python 2.7 support + - Modernize syntax (remove `__future__` and modesets) + - Update black to 21.6b0 + +## tmuxp 1.7.2 (2021-02-03) + +- #666 CI: Move test plugin packages from pyproject.toml to + pytest fixtures. Fixes #658 + +## tmuxp 1.7.1 (2021-02-03) + +- #665 Support for passing tmux config file (`-f`), thanks + @jfindlay! Fixes #654 + +## tmuxp 1.6.5 (2021-02-03) + +- #665 Support for passing tmux config file (`-f`), thanks @jfindlay! Fixes + #654 + +## tmuxp 1.7.0 (2021-01-09) + +This will be the last Python 2.7 release of tmuxp. Bug fixes for python +2.7 will live in the [1.7.x branch]. + +- #530 New feature: Plugin system + + - Add plugin system for user customization of tmuxp + - Add tests for the plugin system + - Update existing tests for the plugin system + - Add the plugin interface to the tmuxp package + - Add in depth documentation for the plugin system + + Thank you @joseph-flinn! + +- #656 New feature: Ability to append windows to a session + + `tmuxp load -a configfile` will append a configuration to your current + tmux session. + + Thank you @will-ockmore! + +- #647 Improvement: Logging to file: + + `tmuxp load --log-level outputfile.log` + +- #643 New command: Debug information + + Port `tmuxp debug-info` from via v1.6.2 + +[1.7.x branch]: https://github.com/tmux-python/tmuxp/tree/v1.7.x + +## tmuxp 1.7.0a4 (2021-01-06) + +- Port click package fix from 1.6.4 + +## tmuxp 1.7.0a3 (2020-11-22) + +- Port `tmuxp load --log-level outputfile.log` from + 1.6.3 + +## tmuxp 1.7.0a2 (2020-11-08) + +- Port `tmuxp debug-info` from #643 from via v1.6.2 + +## tmuxp 1.7.0a1 (2020-11-07) + +- #530 Plugin system + + - Add plugin system for user customization of tmuxp + - Add tests for the plugin system + - Update existing tests for the plugin system + - Add the plugin interface to the tmuxp package + - Add in depth documentation for the plugin system + + Thank you @joseph-flinn! + +## tmuxp 1.6.4 (2021-01-06) + +- #651 Fix packaging issue with click, thanks @dougharris! Fixes + #649 + +## tmuxp 1.6.3 (2020-11-22) + +- #647 Adding option to dump `load` output to log file, thank you + @joseph-flinn! + + `tmuxp load file.yaml --log-file yourfile.txt` + + Adjust log levels: + + `tmuxp --log-level DEBUG load file.yaml --log-file yourfile.txt` + +## tmuxp 1.6.2 (2020-11-08) + +- #643 New command `tmuxp debug-info` for creating github + issues, fixes #352. Thank you @joseph-flinn! + +(v1-6-1)= + +## tmuxp 1.6.1 (2020-11-07) + +- #641 Improvements to `shell` + + Thanks [django-extensions] (licensed MIT) for the shell detection abstraction. + + - Deprecate `shell_plus` + - `tmuxp shell` now detects the best shell available by default + - Python 3.7+ with `PYTHONBREAKPOINT` set in env will drop into `pdb` by + default + - Drop into `code.interact` by default instead of `pdb` if no third + party shells found + - New options, override: + + - `--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], accepts + `--use-vi-mode` + +[django-extensions]: https://github.com/django-extensions/django-extensions + +## tmuxp 1.6.0 (2020-11-06) + +- #636 + #638 New command: `tmuxp shell` + + Automatically preloads session, window, and pane via [libtmux] + {ref}`API objects ` and makes them available in a python + console. + + ```{image} _static/tmuxp-shell.gif + :width: 100% + + ``` + + As of {ref}`1.6.1 (above) `, `tmuxp shell` will find the most + feature-rich shell available. If you have [ipython], or + [bpython] available, it will be selected automatically. Pass `--pdb` + to use standard library pdb, or `--code` to use `code.interact`. + + In python 3.7+, supports `PYTHONBREAKPOINT`: + + ```{code-block} sh + + $ pip install --user ipdb + $ env PYTHONBREAKPOINT=ipdb.set_trace tmuxp shell + + ``` + + You can execute python directly via `-c`: + + ```{code-block} sh + + $ tmuxp shell -c 'print(session.name); print(window.name)' + my_server + my_window + + $ tmuxp shell my_server -c 'print(session.name); print(window.name)' + my_server + my_window + + $ tmuxp shell my_server my_window -c 'print(session.name); print(window.name)' + my_server + my_window + + $ tmuxp shell my_server my_window -c 'print(window.name.upper())' + MY_WINDOW + + ``` + +[bpython]: https://bpython-interpreter.org/ +[ipython]: https://ipython.org/ +[ptpython]: https://github.com/prompt-toolkit/ptpython + +## tmuxp 1.5.8 (2020-10-31) + +- #639 Passes start_directory through to new tmux session + Fixes #631, thank you @joseph-flinn! + +## tmuxp 1.5.7 (2020-10-31) + +- #637 Support for loading directories with periods in it + + `tmuxp load ~/work/your.project` will now work + + Earlier workaround was to do `tmuxp load ~/work/your.project/.tmuxp.yaml` + + Fixes #212 and #201 + +## tmuxp 1.5.6 (2020-10-12) + +- #618: allow passing `--overwrite` to `tmuxp freeze`. Thank you + @betoSolares! +- #589 added option for the the confirm command to auto-confirm the prompt. + Thank you @aRkedos! +- #626 Add new session name option to cli. Thank you @joseph-flinn! +- #626 Add test for new session name option +- #626 Update docs for new session name option +- #623 Move docs from RTD to self-serve site +- #623 Modernize Makefiles +- #623 New development docs +- #623 Move doc -> docs +- #623 Move tests to GitHub Actions +- #623 Update pyproject.toml to experiment with poetry packaging +- #619 isort 5 +- #629 Update black from 19.10b0 to 20.08b1 + +## tmuxp 1.5.5 (2020-07-26) + +- #616 (via: #599) New command: `tmuxp ls` + + List commands available via config directory. If the config is printed, + it's loadable via `tmuxp load configfilename` without needing to type the + full filepath. Thank you @pythops! + +- #480 Fix typo, thanks @jstoja +- #578 Fix typo, thanks @mauroporras +- #519 Fix typo, thanks @timgates42 +- #506 Fix Makefile typo, thanks @wolfgangpfnuer +- #619 Update isort to 5.x +- Travis: Only run on master and PRs one time +- Travis: Add caching for tmux builds +- Travis: Test 2.9 and 3.0a +- #613: Move from Pipenv to Poetry + +## tmuxp 1.5.4 (2019-11-06) + +- #500: Fix window focus +- Fix travis CI builds for python 3.7 + +## tmuxp 1.5.3 (2019-06-06) + +- #377: Include examples in source distribution package + +## tmuxp 1.5.2 (2019-06-02) + +- Loosen libtmux version constraint to >0.8 and <0.9 (libtmux 0.8.2 + released today) +- #484 CHANGES converted to plain reStructuredText +- #491 `tmuxp freeze` will now infer active session with freezing +- #490 Fix XDG's `$XDG_CONFIG_HOME` behavior +- #483, #482, #480 Doc fixes +- #487 Simplifying handling of configs with no panes (Fixes + #470) + +## tmuxp 1.5.1 (2019-02-18) + +- Add tests/\*.sh scripts to MANIFEST.in to include them in Pypi package. +- Include twine to dev packages on requirements and Pipfile files. + +## tmuxp 1.5.0 (2018-10-02) + +- Support Click 7.0 +- Remove unused `__future__` imports +- #471 Update libtmux 0.8.0 -> 0.8.1 +- #404 from @anddam, support XDG base directory +- Sort imports +- Add configuration and make command for isort. +- Add sphinxcontrib-napoleon. +- Assure _requirements/dev.txt_ dependencies are in _Pipfile_ +- Update sphinx, releases to latest version +- Sync _requirements/_.txt* dependencies with *Pipfile\*. +- Update docstring style to use numpy-style documentation. + This enhances readability and plays nicely with sphinx documentation. +- Documentation overhaul. + + - Areas like {meth}`tmuxp.cli.load_workspace` are now documented verbosely. + This is so contributors helping on the project can more quickly gain + situational awareness in this tricky area of code. + +## tmuxp 1.4.2 (2018-09-30) + +- #431 Include tests in source distribution + +## tmuxp 1.4.1 (2018-09-26) + +- Loosen click restraint to <7 + +## tmuxp 1.4.0 (2018-03-11) + +- Bump libtmux to 0.8.0 +- #264 Update license from BSD to MIT +- #348 Continuous integration updates and fixes for Travis CI + + - Update builds to use trusty + - Remove older python 3 versions (before 3.6) + - Update pypy versions + +- #349 flake8 via continuous integration +- Improve reliability of time-sensitive tests by + using `while True` with a timeout. +- Update sphinx to 1.7.1 +- Update alagitpull (sphinx theme) to 0.0.19. + External websites open in new window. +- Update pytest to 3.4.1 + +## tmuxp 1.3.5 (2017-11-10) + +- #312 Support for tmux 2.6 layout setting (via hooks) in the + following scenarios: + + - loading outside tmux + - loading inside tmux, via switch-client + - loading inside tmux, with session in background (with -d), and + reattaching/switching to after + - loading session outside tmux in background with -d, and + reattaching/switching after + +- #308 Fix bug where layouts don't correctly set on tmux 2.6 +- Upgrade libtmux to 0.7.7 + +## tmuxp 1.3.4 (2017-10-12) + +- `before_script` now respects `start_directory` + in the session root. This makes it easier to run things like `pipenv install` as a `before_script`. + +## tmuxp 1.3.3 (2017-10-07) + +- Update libtmux to 0.7.5 for tmux 2.6 hotfix + +## tmuxp 1.3.2 (2017-08-20) + +- #184 - update libtmux to fix environmental variables in the + session scope +- Update libtmux to 0.7.4 +- Updates to pytest and pytest-rerunfailures + +## tmuxp 1.3.1 (2017-05-29) + +- #252 Fix bug where loading a session with a name matching a subset + of current session causes undesired behavior. +- Update libtmux to 0.7.3 +- Switch theme to alagitpull (alabaster subtheme) +- Remove unneeded doc dependency packages + +## tmuxp 1.3.0 (2017-04-27) + +- #239 Improve support for formatted options when freezing and + using configs with them. +- #236 Support for symlinked directories, thanks @rafi. +- #235 Support for `options_after`, for setting options like + `synchronize-panes`. Thanks @sebastianst. +- #248 Drop python 2.6 support +- #248 Upgrade libtmux to 0.7.1 +- Upgrade colorama from 0.3.7 to 0.3.9 + +## tmuxp 1.2.8 (2017-04-02) + +- #229 More helpful error message on systems missing + tmux. +- Update libtmux from 0.6.4 to 0.6.5. + +## tmuxp 1.2.7 (2017-03-25) + +- Support for OpenBSD. + +## tmuxp 1.2.6 (2017-02-24) + +- #218 Fix pane ordering by running `select-layout` before + splits. + +## tmuxp 1.2.5 (2017-02-08) + +- #207 add custom tmuxp config directory via + `TMUXP_CONFIGDIR` variable. +- #199 support for running tmuxp on tmux `master`. +- update libtmux from 0.6.2 to 0.6.3. + +## tmuxp 1.2.4 (2017-01-13) + +- #198 bump click from 6.6 to 6.7 +- #195 pin packages for colorama and doc requirements + +## tmuxp 1.2.3 (2016-12-21) + +- bump libtmux 0.6.0 to 0.6.1 +- #193 improve suppress history test, courtesy of @abeyer. +- #191 documentation typo from @modille +- #186 documentation typo from @joelwallis + +## tmuxp 1.2.2 (2016-09-16) + +- #181 Support tmux 2.3 + +## tmuxp 1.2.1 (2016-09-16) + +- #132 Handle cases with invalid session names +- update libtmux from 0.5.0 to 0.6.0 + +## tmuxp 1.2.0 (2016-06-16) + +- #65 Ability to specify `options` and `global_options` via + configuration. Also you can specify environment variables via that. + + Include tests and add example. + +## tmuxp 1.1.1 (2016-06-02) + +- #167 fix attaching multiple sessions +- #165 fix typo in error output, thanks @fpietka +- #166 add new docs on zsh/bash completion +- Add back `tmuxp -V` for version info + +## tmuxp 1.1.0 (2016-06-01) + +- #160 load tmuxp configs by name +- #134 Use `click` for command-line completion, Rewrite command + line functionality for importing, config finding, conversion and prompts. +- Remove `-l` from `tmuxp import tmuxinator|teamocil` +- #158 argparse bug overcome by switch to click + +## tmuxp 1.0.2 (2016-05-25) + +- #163 fix issue re-attaching sessions that are already loaded +- #159 improved support for tmuxinator imports, from @fpietka. +- #161 readme link fixes from @Omeryl. + +## tmuxp 1.0.1 (2016-05-25) + +- switch to readthedocs.io for docs +- #157 bump libtmux to 0.4.1 + +## tmuxp 1.0.0-rc1 (2016-05-25) + +- version jump 0.11.1 to 1.0 +- tests moved to py.test framework +- [libtmux] core split into its own project +- #145 Add new-window command functionality, @ikirudennis +- #146 Optionally disable shell history suppression, @kmactavish +- #147 Patching unittest timing for shell history suppression +- move doc building, tests and watcher to Makefile +- update .tmuxp.yaml and .tmuxp.json for Makefile change +- overhaul README + +## tmuxp 0.11.0 (2016-02-29) + +- #137 Support for environment settings in configs, thanks + `@tasdomas` +- Spelling correction, thanks [@sehe]. + +## tmuxp 0.10.0 (2016-01-30) + +- #135 Load multiple tmux sessions at once, thanks [@madprog]. +- #131 #133 README and Documentation fixes + +## tmuxp 0.9.3 (2016-01-06) + +- switch to `.venv` for virtualenv directory to not conflict + with `.env` (used by [autoenv]). +- #130 move to [entr(1)] for file watching in tests. update docs. +- [compatibility] Support [Anaconda Python] 2 and 3 + +## tmuxp 0.9.2 (2015-10-21) + +- #122 Update to support tmux 2.1, thank you [@estin]. +- use travis container infrastructure for faster tests +- change test in workspace builder test to use `top(1)` instead of + `man(1)`. `man(1)` caused errors on some systems where `PAGER` is set. + +## tmuxp 0.9.1 (2015-08-23) + +- #119 Add fix python 3 for [sysutils/pytmuxp] on FreeBSD ports. + See GH issue 119 and [#201564] @ FreeBSD Bugzilla. Thanks Ruslan + Makhmatkhanov. + +## tmuxp 0.9.0 (2015-07-08) + +- Renamed `config.expandpath` to `config.expandshell`. +- compat 2.7/3.3 wrapper for `EnvironmentVarGuard` for + testing. +- You can now use environment variables inside of + `start_directory`, `before_script`, `shell_command_before`, + `session_name` and `window_name`. +- [examples]: add example for environmental variables, + `examples/env-variables.json` and `examples/env-variables.yaml`. +- #110 de-vendorize [colorama]. +- #109 fix failure of test_pane_order on fedora machines from + [@marbu] +- #105 append `.txt` extension to manuals (repo only) + from [@yegortimoshenko]. +- #107 Fix Server.attached_sessions return type by + [@thomasballinger]. +- update travis to use new tmux git repository. + +## tmuxp 0.8.1 (2015-05-09) + +- [testing]: fix sniffer test runner in python 3 +- new animated image demo for RTD and README + +## tmuxp 0.8.0 (2015-05-07) + +- version bump 0.1.13 -> 0.8.0 +- tmux 2.0 support +- Fix documentation for :meth:`Session.switch_client()`. +- Add `--log-level` argument. +- Refactor `{Server,Session,Window,Pane}.tmux` into: + + - {meth}`Server.cmd()` + - {meth}`Session.cmd()` + - {meth}`Window.cmd()` + - {meth}`Pane.cmd()` + + (See conversation at ) + +- Refactor `util.tmux` into {meth}`util.tmux_cmd`. + +## tmuxp 0.1.13 (2015-03-25) + +- Remove `package_metadata.py` in favor of `__about__.py`. +- `scent.py` for building docs +- docutils from 0.11 to 0.12 +- `bootstrap_env.py` will check for linux, darwin (OS X) and + windows and install the correct [sniffer] file watcher plugin. +- testsuite for cli uses {py:func}`tempfile.mkdtemp()` instead + `TMP_DIR` (which resolved to `.tmuxp` in the testsuite directory. +- replace [watchingtestrunner] in examples. + `.tmuxp.conf` and `.tmux.json` updated +- updates to doc links +- `make checkbuild` for verifying internal / intersphinx doc + references. +- Add Warning tmux versions less than 1.4 from [@techtonik]. +- Add documentation on leading space in `send_keys` + from [@thomasballinger]. +- Update about page from teamocil and erb support from [@raine]. + +## tmuxp 0.1.12 (2014-08-06) + +- [config] {meth}`config.expand` now resolves directories in configuration + via {py:func}`os.path.expanduser` and {py:func}`os.path.expandvars`. +- [config] {meth}`config.expandpath` for helping resolve paths. +- improved support for loading tmuxp project files from + outside current working directory. e.g. + + ``` + $ tmuxp load /path/to/my/project/.tmuxp.yaml + ``` + + Will behave better with relative directories. + +## tmuxp 0.1.11 (2014-04-06) + +- `before_script` now loads relative to project directory with + `./`. +- Use `bootstrap_env.py` in tmuxp's `.tmuxp.yaml` and + `.tmuxp.json` project files. +- Improvements to {meth}`util.run_before_script()`, + {class}`exc.BeforeLoadScriptFailed` behavior to print `stdout` and + return `stderr` is a non-zero exit is returned. +- `run_script_before` has moved to `util`. +- `BeforeLoadScriptFailed` and `BeforeLoadScriptNotExists` + has moved to the `exc` module. +- Tests for `run_script_before` refactored. + +## tmuxp 0.1.10 (2014-04-02) + +- 2 bug fixes and allow panes with no shell commands to accept options, + thanks for these 3 patches, [@ThiefMaster]: +- #73 Fix an error caused by spaces in + `start_directory`. +- #77 Fix bug where having a `-` in a + `shell_command` would cauesd a build error. +- #76 Don't require `shell_command` to + pass options to panes (like `focus: true`). + +## tmuxp 0.1.9 (2014-04-01) + +- The `--force` was not with us. + +## tmuxp 0.1.8 (2014-03-30) + +- #72 Create destination directory if it doesn't exist. Thanks + [@ThiefMaster]. +- New context manager for tests, `temp_session`. +- New testsuite, `testsuite.test_utils` for testing testsuite + tools. +- New command, `before_script`, which is a file to + be executed with a return code. It can be a bash, perl, python etc. + script. +- #56 {ref}`python_api_quickstart ` + +## tmuxp 0.1.7 (2014-02-25) + +- #55 where tmuxp would crash with letter numbers in version. + Write tests. + +## tmuxp 0.1.6 (2014-02-08) + +- {meth}`Window.split_window()` now allows `-c start_directory`. +- #35 Builder will now use `-c start_directory` to + create new windows and panes. + + This removes a hack where `default-path` would be set for new pane and + window creations. This would bleed into tmux user sessions after + creations. + +## tmuxp 0.1.5-1 (2014-02-05) + +- #49 bug where `package_manifest.py` missing + from `MANIFEST.in` would cause error installing. + +## tmuxp 0.1.5 (2014-02-05) + +- section heading normalization. +- tao of tmux section now treated as a chatper. tao of tmux may be + split off into its own project. +- use conventions from [tony/cookiecutter-pypackage]. + +## tmuxp 0.1.4 (2014-02-02) + +- Fix `$ tmuxp freeze` CLI output. +- Update `_compat` support module. +- Fix extra space in [PEP 263]. + +## tmuxp 0.1.3 (2014-01-29) + +- #48 Fix Python 3 CLI issue. +- #48 `$ tmuxp` without option raises an error. +- Add space before send-keys to not populate bash and zsh + history. + +## tmuxp 0.1.2 (2014-01-08) + +- now using werkzeug / flask style testsuites. +- #43 Merge `tmuxp -d` for loading in detached mode. + Thanks [roxit]. + +## tmuxp 0.1.1 (2013-12-25) + +- #32 Fix bug where special characters caused unicode caused + unexpected outcomes loading and freezing sessions. + +## tmuxp 0.1.0 (2013-12-18) + +- fix duplicate print out of filename with using `tmuxp load .`. +- version to 0.1. No `--pre` needed. Future versions will not use rc. + +## tmuxp 0.1-rc8 (2013-12-17) + +- `unicode_literals` +- Move py2/py3 compliance code to `_compat`. + +## tmuxp 0.1-rc7 (2013-12-07) + +- #33 Partial rewrite of {meth}`config.expand`. +- tmuxp will exit silently with `Ctrl-c`. + +## tmuxp 0.1-rc6 (2013-12-06) + +- #31 [examples] from stratoukos add `window_index` option, + and example. + +## tmuxp 0.1-rc5 (2013-12-04) + +- #28 shell_command_before in session scope of config causing + duplication. New test. +- #26 #29 for OS X tests. Thanks stratoukos. +- #27 `$ tmuxp freeze` raises unhelpful message if session doesn't + exist. + +## tmuxp 0.1-rc4 (2013-12-03) + +- fix bug were `focus: true` would not launch sessions when using + `$ tmuxp load` in a tmux session. + +## tmuxp 0.1-rc3 (2013-12-03) + +- #25 `focus: true` not working in panes. Add + tests for focusing panes in config. +- {meth}`Pane.select_pane()`. +- add new example for `focus: true`. + +## tmuxp 0.1-rc2 (2013-11-23) + +- #23 fix bug where workspace would not build with pane-base-index + set to 1. Update tests to fix if `pane-base-index` is not 0. +- removed `$ tmuxp load --list` functionality. Update + {ref}`quickstart` accordingly. + +## tmuxp 0.1-rc1 (2013-11-23) + +- [pep8] in unit tests. +- Changelog will now be updated on a version basis, use [pep440] + versioning. + +## tmuxp 0.1-dev (2013-11-21) + +- {meth}`Session.show_options`, {meth}`Session.show_option` now + accept `g` to pass in `-g`. + +## tmuxp 0.1-dev (2013-11-20) + +- {meth}`Window.show_window_options`, + {meth}`Window.show_window_option` now accept `g` to pass in `-g`. +- #15 Behavioral changes in the WorkspaceBuilder to fix pane + ordering. +- #21 Error with unit testing python 2.6 python configuration tests. + Use {py:mod}`tempfile` instead. +- WorkspaceBuilder tests have been improved to use async better. + +## tmuxp 0.1-dev (2013-11-17) + +- fix a bug where missing tmux didn't show correct warning. + +## tmuxp 0.1-dev (2013-11-15) + +- Travis now tests python 2.6 as requirement and not allowed to + fail. + +## tmuxp 0.1-dev (2013-11-13) + +- #19 accept `-y` argument to answer yes to questions. +- {meth}`cli.SessionCompleter` no longer allows a duplicate session + after one is added. +- ongoing work on {ref}`about-tmux`. + +## tmuxp 0.1-dev (2013-11-09) + +- [translation] [documentation in Chinese]. +- More work done on the {ref}`about-tmux` page. +- {meth}`Pane.split_window()` for splitting {class}`Window` at + `target-pane` location. + +## tmuxp 0.1-dev (2013-11-08) + +- [freeze] - `$ tmuxp freeze` will now freeze a window with a + `start_directory` when all panes in a window are inside the same + directory. +- [config] {meth}`config.inline` will now turn panes with no + other attributes and 1 command into a single item value. + + ``` + - panes: + - shell_command: top + + # will now inline to: + + - panes + - top + + This will improve ``$ tmuxp freeze`` + ``` + +## tmuxp 0.1-dev (2013-11-07) + +- Remove old logger (based on [tornado's log.py]), replace + with new, simpler one. +- fix [teamocil] import. +- support import teamocil `root` to + `start_directory`. + +## tmuxp 0.1-dev (2013-11-06) + +- tagged v0.0.37. Many fixes. Python 2.6 support. Will + switch to per-version changelog after 0.1 release. +- support for blank panes (null, `pane`, `blank`) and panes + with empty strings. +- tmuxp freeze supports exporting to blank panes. +- tmuxp freeze will now return a blank pane for panes that would + previously return a duplicate shell command, or generic python, node + interpreter. + +## tmuxp 0.1-dev (2013-11-05) + +- Support for `[-L socket-name]` and `[-S socket-path]` in + autocompletion and when loading. Note, switching client into another + socket may cause an error. +- Documentation tweaking to {ref}`API`, {ref}`about-tmux`. +- [pep257]. + +## tmuxp 0.1-dev (2013-11-04) + +- [pep257]. +- tagged version `v0.0.36`. + +## tmuxp 0.1-dev (2013-11-02) + +- Many documentation, [pep257] fixes +- move old {class}`Server` methods `__list_panes()`, + `__list_windows` and `__list_sessions` into the single underscore. +- #12 fix for `$ tmuxp freeze` by @finder. +- Support for spaces in `$ tmuxp attach-session` and + `$ tmuxp kill-session`, and `$ tmuxp freeze`. +- [config] support for relative paths of `start_directory`. Add an + update config in _Start Directory_ on {ref}`examples`. + +## tmuxp 0.1-dev (2013-11-01) + +- New servers for {class}`Server` arguments `socket_name`, + `socket_path`, `config_file`. +- {class}`Server` support for `-2` with `colors=256` and + `colors=8`. +- `$ tmuxp -2` for forcing 256 colors and `tmuxp -8` for forcing 88. +- [config] Concatenation with `start_directory` via + {meth}`config.trickle()` if window `start_directory` is alphanumeric / + relative (doesn't start with `/`). See {ref}`Examples` in _start directory_. +- Fix bug with import teamocil and tmuxinator +- Improve quality of tmuxinator imports. Especially `session_name` + and `start_directory`. +- Allow saving with `~` in file destination. + +## tmuxp 0.1-dev (2013-10-31) + +- {meth}`util.is_version()` +- correctly {meth}`config.trickle()` the `start_directory`. +- get `start_directory` working for configs +- fix :meth:`Window.kill_window()` target to + `session_id:window_index` for compatibility and pass tests. +- [examples]: Example for `start_directory`. +- fix bug where first and second window would load in mixed order +- {class}`Window.move_window()` for moving window. +- doc overhaul. front page, renamed orm_al.rst to internals.rst. + +## tmuxp 0.1-dev (2013-10-30) + +- fix bug where if inside tmux, loading a workspace via switch_client + wouldn't work. +- fix bug where `tmuxp load .` would return an error instead of a + notice. +- `tmuxp freeze ` experimental +- tmuxp now has experimental support for freezing live + sessions. +- {meth}`Window.kill_window()` +- support for `start_directory` (work in progress) + +## tmuxp 0.1-dev (2013-10-29) + +- {meth}`Window.select_pane` now accepts `-l`, `-U`, `-D`, + `-L`, `-R`. +- support for `automatic-rename` option. +- 3 new {ref}`examples`, 'main-pane-height', 'automatic-rename', and + 'shorthands'. +- enhancements to prompts +- `tmuxp import` for teamocil and tmuxinator now has a wizard and offers to save in JSON or YAML format. +- [b6c2e84] Fix bug where tmuxp load w/ session already loaded would + switch/attach even if no was entered +- when workspace loader crashes, give option to kill session, attach it or + detach it. +- tmux 1.8 `set-option` / `set-window-options` command + `target-window` fix. +- {class}`WorkspaceBuilder` now has `.session` attribute accessible + publicly. +- tmux will now use {meth}`Session.switch_client` and + {meth}`Session.attach_session` to open new sessions instead of `os.exec`. +- [config] tmuxp now allows a new shorter form for panes. Panes can just be a + string. See the shorthand form in the {ref}`examples` section. +- [config] support loading `.yml`. -2013-10-28 ----------- +## tmuxp 0.1-dev (2013-10-28) -- [cli] fix ``tmuxp load .`` fixed -- [cli] fix ``tmuxp convert `` fixed. -- [internal] `pep257`_ fixes. -- [internal] [tests] - :class:`Pane` now has :meth:`Pane.set_width` and - :meth:`Pane.set_height`. -- [tests] ``./run_tests.py --tests`` now automatically prepends - ``tmuxp.testsuite`` to names. -- [internal] :meth:`Window.tmux` and :meth:`Pane.tmux` will automatically add - their ``{window/pane}_id`` if one isn't specific. +- fix `tmuxp load .` fixed +- fix `tmuxp convert ` fixed. +- [pep257] fixes. +- {class}`Pane` now has {meth}`Pane.set_width` and + {meth}`Pane.set_height`. +- `./run_tests.py --tests` now automatically prepends + `tmuxp.testsuite` to names. +- {meth}`Window.tmux` and {meth}`Pane.tmux` will automatically add + their `{window/pane}_id` if one isn't specific. -2013-10-27 ----------- +## tmuxp 0.1-dev (2013-10-27) -- [cli] `argcomplete`_ overhaul for CLI bash completion. -- [cli] ``tmuxp load``, ``tmuxp convert`` and ``tmuxp import`` now support +- [argcomplete] overhaul for CLI bash completion. +- `tmuxp load`, `tmuxp convert` and `tmuxp import` now support relative and full filenames in addition to searching the config directory. -2013-10-26 ----------- +## tmuxp 0.1-dev (2013-10-26) -- [import] [tests] initial version of `tmuxinator`_ and `teamocil`_ +- initial version of [tmuxinator] config importer. it does not support all options and it not guaranteed to fully convert window/pane size and state. -- [internal] :meth:`config.in_dir` supports a list of ``extensions`` for - filetypes to search, i.e. ``['.yaml', '.json']``. -- [internal] :meth:`config.is_config_file` now supports ``extensions`` +- {meth}`config.in_dir` supports a list of `extensions` for + filetypes to search, i.e. `['.yaml', '.json']`. +- {meth}`config.is_config_file` now supports `extensions` argument as a string also. -- [cli] fix ``$ tmuxp load -l`` to work correctly alongside - ``$ tmuxp load filename``. +- fix `$ tmuxp load -l` to work correctly alongside + `$ tmuxp load filename`. -2013-10-25 ----------- +## tmuxp 0.1-dev (2013-10-25) -- [cli] fix bug where ``-v`` and ``--version`` wouldn't print version. -- [cli] property handle case where no tmux server exists when - ``attach-session`` or ``kill-session`` is used. -- [import] [tests] test fixtures and inital work for importing - `tmuxinator`_ and `teamocil`_ configs +- fix bug where `-v` and `--version` wouldn't print version. +- property handle case where no tmux server exists when + `attach-session` or `kill-session` is used. +- test fixtures and initial work for importing + [tmuxinator] configs -2013-10-24 ----------- +## tmuxp 0.1-dev (2013-10-24) -- [internal] clean out old code for ``automatic-rename`` option. it will +- clean out old code for `automatic-rename` option. it will be reimplemented fresh. -- [cli] check for ``oh-my-zsh`` when using ``$SHELL`` ``zsh``. Prompt if - ``DISABLE_AUTO_TITLE`` is unset or set to ``true``. -- [cli] tmuxp can now ``$ tmuxp convert `` from JSON <=> YAML, back +- check for `oh-my-zsh` when using `$SHELL` `zsh`. Prompt if + `DISABLE_AUTO_TITLE` is unset or set to `true`. +- tmuxp can now `$ tmuxp convert ` from JSON <=> YAML, back and forth. -- [docs] New examples in JSON. Update the :ref:`examples` page in the +- New examples in JSON. Update the {ref}`examples` page in the docs. -- [dev] ``.tmuxp.json`` now exists as a config for tmuxp development and +- [dev] `.tmuxp.json` now exists as a config for tmuxp development and as an example. -- [cli] Fix bug where ``tmuxp kill-session`` would give bad output -- [cli] Fix bug in tab completion for listing sessions with no tmux server +- Fix bug where `tmuxp kill-session` would give bad output +- Fix bug in tab completion for listing sessions with no tmux server is active. +## tmuxp 0.1-dev (2013-10-23) -2013-10-23 ----------- - -- [cli] zsh/bash/tcsh completion improvements for tab-completion options -- [cli] tmuxp ``kill-session`` with tab-completion. -- [cli] tmuxp ``attach-session`` with tab-completion. Attach session will - ``switch-client`` for you if you are inside of of a tmux client. -- [cli] tmuxp ``load`` for loading configs. -- [tests] unit test fixes. +- zsh/bash/tcsh completion improvements for tab-completion options +- tmuxp `kill-session` with tab-completion. +- tmuxp `attach-session` with tab-completion. Attach session will + `switch-client` for you if you are inside of of a tmux client. +- tmuxp `load` for loading configs. +- unit test fixes. -2013-10-21 ----------- +## tmuxp 0.1-dev (2013-10-21) -- [cli] Make 1.8 the official minimym version, give warning notice to +- Make 1.8 the official minimym version, give warning notice to upgrade tmux if out of date - Fix regression causing unexpected build behavior due to unremoved code supporting old tmux versions. -- [docs] Added 2 new examples to the :ref:`examples` page. -- [docs] Examples now have graphics -- [cli] [internal] ``$ tmuxp -v`` will print the version info. - -2013-10-19 ----------- - -- [internal] tmuxp will now give warning and sys.exit() with a message if - ``tmux`` not found in system PATH -- [internal] major internal overhaul of :class:`Server`, :class:`Session` - , :class:`Window`, and :class:`Pane` continues. - - - :class:`Server` has @property :meth:`Server.sessions`, which is forward - to :meth:`Server.list_sessions()` (kept to keep tmux commands in - serendipty with api), :meth:`Server._list_sessions()` returns dict - object from :meth:`Server.__list_sessions()` tmux command. - :meth:`Server.__list_sessions()` exists to keep the command layered so +- Added 2 new examples to the {ref}`examples` page. +- Examples now have graphics +- `$ tmuxp -v` will print the version info. + +## tmuxp 0.1-dev (2013-10-19) + +- tmuxp will now give warning and sys.exit() with a message if + `tmux` not found in system PATH +- internal overhaul of {class}`Server`, {class}`Session` + , {class}`Window`, and {class}`Pane` continues. + + - {class}`Server` has @property {meth}`Server.sessions`, which is forward + to {meth}`Server.list_sessions()` (kept to keep tmux commands in + serendipty with api), {meth}`Server._list_sessions()` returns dict + object from {meth}`Server.__list_sessions()` tmux command. + {meth}`Server.__list_sessions()` exists to keep the command layered so it can be tested against in a matrix with travis and compatibility methods can be made. - - - :class:`Session` now has @proprety :meth:`Session.windows` returning a - list of :class:`Window` objects via :meth:`Session.list_windows()`. - @property :meth:`Session._windows` to :meth:`Session._list_windows()` + - {class}`Session` now has @property {meth}`Session.windows` returning a + list of {class}`Window` objects via {meth}`Session.list_windows()`. + @property {meth}`Session._windows` to {meth}`Session._list_windows()` to return a list of dicts without making objects. - - - :class:`Window` now has @proprety :meth:`Window.panes` returning a - list of :class:`Pane` objects via :meth:`Window.list_panes()`. - @property :meth:`Window._panes` to :meth:`Window._list_panes()` + - {class}`Window` now has @property {meth}`Window.panes` returning a + list of {class}`Pane` objects via {meth}`Window.list_panes()`. + @property {meth}`Window._panes` to {meth}`Window._list_panes()` to return a list of dicts without making objects. -2013-10-18 ----------- - -- [internal] major internal overhaul of :class:`Server`, :class:`Session`, - :class:`Window`, and :class:`Pane`. +## tmuxp 0.1-dev (2013-10-18) - - ``Session``, ``Window`` and ``Pane`` now refer to a data object - in :class:`Server` internally and always pull the freshest data. +- internal overhaul of {class}`Server`, {class}`Session`, + {class}`Window`, and {class}`Pane`. + - `Session`, `Window` and `Pane` now refer to a data object + in {class}`Server` internally and always pull the freshest data. - A lot of code and complexity regarding setting new data for objects has been reduced since objects use their unique key identifier to - filter their objects through the windows and panes in ``Server`` + filter their objects through the windows and panes in `Server` object. + - `Server` object is what does the updating now. - - ``Server`` object is what does the updating now. - [project] some research into supporting legacy tmux versions. tmux 1.6 and 1.7 support seem likely eventually if there is enough demand. -- [internal] python 3 support +- python 3 support -2013-10-17 ----------- +## tmuxp 0.1-dev (2013-10-17) -- [docs] updated README docs with new project details, screenshots -- [dev] [docs] - new example ``.tmuxp.yaml`` file updated to include +- updated README docs with new project details, screenshots +- new example `.tmuxp.yaml` file updated to include development workflow. Removed nodemon as the tool for checking files for now. -- [cli] Support for switching sessions from within tmux. In both cases +- Support for switching sessions from within tmux. In both cases after the the session is built and if session already exists. -2013-10-16 ----------- +## tmuxp 0.1-dev (2013-10-16) -- [cli] use :meth:`util.which()` from salt.util to find tmux binary. -- [pypi / packaging] add MANIFEST.in, fix issue where package would not +- use {meth}`util.which()` from salt.util to find tmux binary. +- add MANIFEST.in, fix issue where package would not install because missing file -- [cli] bash / zsh completion. -- [docs] New page on :ref:`orm_al`. -- [docs] Updates to :ref:`about_tmux` page. -- [docs] add vim modeline for rst to bottom of this page -- [internal] [tests] Server is now a subclass of :class:`util.TmuxObject`. -- [internal] [tests] subclasses of :class:`util.TmuxRelationalObject`, - :class:`Server`, :class:`Session`, :class:`Window`, :class:`Pane` now - have :meth:`util.TmuxRelationalObject.getById` (similar to `.get()`_ in - `backbone.js`_ collection), :meth:`util.TmuxRelationalObject.where` and - :meth:`util.TmuxRelationalObject.findWhere` (`.where()`_ and - `.findWhere()`_ in `underscore.js`_), to easily find child objects. -- [internal] tmux object mapping has been split into - :class:`util.TmuxMappingObject`. The mapping and the relational has been - decoupled to allow :class:`Server` to have children while not being a +- bash / zsh completion. +- New page on {ref}`internals`. +- Updates to {ref}`about-tmux` page. +- add vim modeline for rst to bottom of this page +- Server is now a subclass of `util.TmuxObject`. +- subclasses of {class}`util.TmuxRelationalObject`, + {class}`Server`, {class}`Session`, {class}`Window`, {class}`Pane` now + have {meth}`util.TmuxRelationalObject.getById` (similar to [.get()] in + [backbone.js] collection), {meth}`util.TmuxRelationalObject.where` and + {meth}`util.TmuxRelationalObject.findWhere` ([.where()] and + [.findWhere()]), to easily find child objects. +- tmux object mapping has been split into + {class}`util.TmuxMappingObject`. The mapping and the relational has been + decoupled to allow {class}`Server` to have children while not being a dict-like object. -- [internal] :class:`Server`, :class:`Session`, :class:`Window`, - :class:`Pane` now explicitly mixin subclasses. +- {class}`Server`, {class}`Session`, {class}`Window`, + {class}`Pane` now explicitly mixin subclasses. -.. _underscore.js: http://underscorejs.org/ -.. _backbone.js: http://backbonejs.org/ -.. _.get(): http://backbonejs.org/#Collection-get -.. _.where(): http://underscorejs.org/#where -.. _.findWhere(): http://underscorejs.org/#findWhere +## tmuxp 0.1-dev (2013-10-15) -2013-10-15 ----------- - -- [docs] new theme -- [docs] initial examples, misc. updates, front page update. -- [cli] support for ``$ tmux .`` to load ``.tmuxp.{yaml/json/py}`` in current +- new theme +- initial examples, misc. updates, front page update. +- support for `$ tmux .` to load `.tmuxp.{yaml/json/py}` in current working directory. -- [cli] support for ``socket-name`` (``-L``) and ``socket-path`` - (``socket-path``) -- [config] [builder] Support for 1-command pane items. - - .. code-block:: yaml - - session_name: my session - windows: - - window_name: hi - panes: - - bash - - htop -- [cli] If session name is already exists, prompt to attach. - -2013-10-14 ----------- - -- [cli] can now -l to list configs in current directory and $HOME/.tmuxp -- [cli] tmuxp can now launch configs and build sessions -- [internal] new exceptions -- [config] [tests] :meth:`config.check_consistency()` to verify and diagnose +- support for `socket-name` (`-L`) and `socket-path` + (`socket-path`) +- [config] Support for 1-command pane items. + + ``` + session_name: my session + windows: + - window_name: hi + panes: + - bash + - htop + ``` + +- If session name is already exists, prompt to attach. + +## tmuxp 0.1-dev (2013-10-14) + +- can now -l to list configs in current directory and $HOME/.tmuxp +- tmuxp can now launch configs and build sessions +- new exceptions +- {meth}`config.check_consistency()` to verify and diagnose issues with config files. -- [cli] [tests] :meth:`cli.startup()` -- [config] [tests] :meth:`config.is_config_file()` -- [config] [tests] :meth:`config.in_dir()` -- [config] [tests] :meth:`config.in_cwd()` +- {meth}`cli.startup()` +- {meth}`config.is_config_file()` +- {meth}`config.in_dir()` +- {meth}`config.in_cwd()` -2013-10-13 ----------- +## tmuxp 0.1-dev (2013-10-13) -- [cli] [tests] :meth:`config.inline()` to produce far far better looking +- {meth}`config.inline()` to produce far far better looking config exports and tests. -- :meth:`Pane.resize_pane()` and tests +- {meth}`Pane.resize_pane()` and tests - documentation fixes and updates -- [internal] :meth:`Session.refresh()`, :meth:`Window.refresh()`, - :meth:`Pane.refresh()`. -- [internal] :meth:`Server.find()`, :meth:`Session.find()`, - :meth:`Window.find()`. +- {meth}`Session.refresh()`, {meth}`Window.refresh()`, + {meth}`Pane.refresh()`. +- {meth}`Server.find()`, {meth}`Session.find()`, + {meth}`Window.find()`. -2013-10-12 ----------- +## tmuxp 0.1-dev (2013-10-12) -- [tests] Test documentation updates -- [internal] [tests] Builder is now :class:`WorkspaceBuilder` + tests. +- Test documentation updates +- Builder is now {class}`WorkspaceBuilder` + tests. - WorkspaceBuilder can build panes - WorkspaceBuilder can build windows and set options -- [internal] [tests] :meth:`Window.show_window_options()`, - :meth:`Window.show_window_option()`, :meth:`Window.set_window_option()` -- [internal] [tests] :meth:`Session.show_options()`, - :meth:`Session.show_option()`, :meth:`Session.set_option()` - -2013-10-11 ----------- - -- [builder] More preparation for builder / session maker utility. -- [tests] Major test runner and test suite overhaul. -- [docs] Documentation for development environment and test runner updated. -- [tests] Travis now tests against tmux 1.8 and latest source. Door open for +- {meth}`Window.show_window_options()`, + {meth}`Window.show_window_option()`, {meth}`Window.set_window_option()` +- {meth}`Session.show_options()`, + {meth}`Session.show_option()`, {meth}`Session.set_option()` + +## tmuxp 0.1-dev (2013-10-11) + +- More preparation for builder / session maker utility. +- test runner and test suite overhaul. +- Documentation for development environment and test runner updated. +- Travis now tests against tmux 1.8 and latest source. Door open for future testing against python 3 and earlier tmux versions in the future. -- [internal] Quiet logger down in some areas -- [internal] __future__ imports for future python 3 compatibility -- [internal] setup.py import __version__ via regex from tmuxp package -- [cli] move beginnings of cli to tmuxp.cli +- Quiet logger down in some areas +- **future** imports for future python 3 compatibility +- setup.py import **version** via regex from tmuxp package +- move beginnings of cli to `tmuxp.cli` -2013-10-09 ----------- +## tmuxp 0.1-dev (2013-10-09) - New logging module - Removed dependency logutils - Removed dependency sh -2013-10-08 ----------- +## tmuxp 0.1-dev (2013-10-08) - switch to semver -initial -------- - -(initial release) - -- changedlog added -- sphinx documentation - -# vim: set filetype=rst: +[tmuxinator]: https://github.com/aziz/tmuxinator +[teamocil]: https://github.com/remiprev/teamocil +[argcomplete]: https://github.com/kislyuk/argcomplete +[pep257]: http://www.python.org/dev/peps/pep-0257/ +[pep8]: http://www.python.org/dev/peps/pep-0008/ +[pep440]: http://www.python.org/dev/peps/pep-0440/ +[tony/cookiecutter-pypackage]: https://github.com/tony/cookiecutter-pypackage +[@tasdomas]: https://github.com/tasdomas +[@sehe]: https://github.com/sehe +[@madprog]: https://github.com/madprog +[autoenv]: https://github.com/kennethreitz/autoenv +[entr(1)]: http://entrproject.org/ +[anaconda python]: http://docs.continuum.io/anaconda/index +[@estin]: https://github.com/estin +[sysutils/pytmuxp]: http://www.freshports.org/sysutils/py-tmuxp/ +[#201564]: https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=201564 +[colorama]: https://pypi.python.org/pypi/colorama +[@marbu]: https://github.com/marbu +[@yegortimoshenko]: https://github.com/yegortimoshenko +[@thomasballinger]: https://github.com/thomasballinger +[sniffer]: https://github.com/jeffh/sniffer +[watchingtestrunner]: https://pypi.python.org/pypi/watching_testrunner/1.0 +[@raine]: https://github.com/raine +[@techtonik]: https://github.com/techtonik +[@thiefmaster]: https://github.com/ThiefMaster +[pep 263]: http://www.python.org/dev/peps/pep-0263/ +[roxit]: https://github.com/roxit +[documentation in chinese]: http://tmuxp-zh.readthedocs.io +[wrongwaycn]: https://github.com/wrongwaycn +[tornado's log.py]: https://github.com/facebook/tornado/blob/master/tornado/log.py +[underscore.js]: http://underscorejs.org/ +[backbone.js]: http://backbonejs.org/ +[.get()]: http://backbonejs.org/#Collection-get +[.where()]: http://underscorejs.org/#where +[.findwhere()]: http://underscorejs.org/#findWhere +[libtmux]: https://github.com/tmux-python/libtmux -.. _tmuxinator: https://github.com/aziz/tmuxinator -.. _teamocil: https://github.com/remiprev/teamocil -.. _argcomplete: https://github.com/kislyuk/argcomplete -.. _pep257: http://www.python.org/dev/peps/pep-0257/ + diff --git a/CITATION.cff b/CITATION.cff new file mode 100644 index 00000000000..8f79f960edc --- /dev/null +++ b/CITATION.cff @@ -0,0 +1,13 @@ +cff-version: 1.2.0 +message: >- + If you use this software, please cite it as below. + NOTE: Change "x.y" by the version you use. If you are unsure about which version + you are using run: `pip show tmuxp`." +authors: +- family-names: "Narlock" + given-names: "Tony" + orcid: "https://orcid.org/0000-0002-2568-415X" +title: "tmuxp" +type: software +version: x.y +url: "https://tmuxp.git-pull.com" diff --git a/LICENSE b/LICENSE index 9596f9b2e73..203c5d283f4 100644 --- a/LICENSE +++ b/LICENSE @@ -1,29 +1,21 @@ -Copyright (c) 2013 by the respective authors (see AUTHORS file). -All rights reserved. +MIT License -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: +Copyright (c) 2013- tmuxp contributors -* Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: -* Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. -* The names of the contributors may not be used to endorse or promote - products derived from this software without specific prior written - permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index ea2d73a1770..00000000000 --- a/MANIFEST.in +++ /dev/null @@ -1,3 +0,0 @@ -include README.rst LICENSE CHANGES run_tests.py .tmuxp.yaml -include requirements.pip -recursive-include doc *.rst diff --git a/MIGRATION b/MIGRATION new file mode 100644 index 00000000000..0d31b4e3776 --- /dev/null +++ b/MIGRATION @@ -0,0 +1,69 @@ +# Migration notes + +Migration and deprecation notes for tmuxp are here, see {ref}`changelog` as +well. + +```{admonition} Welcome on board! 👋 +1. 📌 For safety, **always** pin the package +2. 📖 Check the migration notes _(You are here)_ +3. 📣 If you feel something got deprecated and it interrupted you - past, present, or future - voice your opinion on the [tracker]. + + We want to make tmuxp fun, reliable, and useful for users. + + API changes can be painful. + + If we can do something to draw the sting, we'll do it. We're taking a balanced approach. That's why these notes are here! + + (Please pin the package. 🙏) + + [tracker]: https://github.com/tmux-python/tmuxp/discussions +``` + +## Next release + +_Notes on the upcoming release will be added here_ + + + +## tmuxp 1.18.0 (2022-10-30) + +**Restructuring** (#840) + +"Config files" and "configs" are now referred to as workspace files. + +Additionally, there's been a major file structure update: + +- `cli/utils.py` functions moved to `workspace/finders.py` +- `config.py` split between: + + - `workspace/finders.py` + - `workspace/freezer.py` + - `workspace/importers.py` + - `workspace/validation.py` + +- `workspacebuilder.py` split into: + + - `workspace/builder.py` + - `workspace/freezer.py` + + `config.inline` moved to freezer + +Tests: + +- `tests/fixtures/{workspacebuilder,workspacefreezer}` -> `tests/fixtures/workspace/{builder,freezer}` +- `tests/test_import_{teamocil,tmuxinator}.py` -> `tests/workspace/test_import_{teamocil,tmuxinator}.py` + +## tmuxp 1.17.0 (2022-10-09) + +**Completions have changed** (#830) + +Completions now use a different tool: [shtab]. See the [completions page] for more information. + +If you were using earlier versions of tmuxp (earlier than 1.17.0), you may need to uninstall the old completions, first. + +[completions page]: https://tmuxp.git-pull.com/cli/completion.html +[shtab]: https://docs.iterative.ai/shtab/ + + diff --git a/Makefile b/Makefile new file mode 100644 index 00000000000..97493204403 --- /dev/null +++ b/Makefile @@ -0,0 +1,63 @@ +PY_FILES= find . -type f -not -path '*/\.*' | grep -i '.*[.]py$$' 2> /dev/null +TEST_FILES= find . -type f -not -path '*/\.*' | grep -i '.*[.]\(yaml\|py\)$$' 2> /dev/null +DOC_FILES= find . -type f -not -path '*/\.*' | grep -i '.*[.]rst\$\|.*[.]md\$\|.*[.]css\$\|.*[.]py\$\|mkdocs\.yml\|CHANGES\|TODO\|.*conf\.py' 2> /dev/null +SHELL := /bin/bash + +entr_warn: + @echo "----------------------------------------------------------" + @echo " ! File watching functionality non-operational ! " + @echo " " + @echo "Install entr(1) to automatically run tasks on file change." + @echo "See https://eradman.com/entrproject/ " + @echo "----------------------------------------------------------" + +test: + uv run py.test $(test) + +start: + $(MAKE) test; uv run ptw . + +watch_test: + if command -v entr > /dev/null; then ${TEST_FILES} | entr -c $(MAKE) test; else $(MAKE) test entr_warn; fi + +build_docs: + $(MAKE) -C docs html + +watch_docs: + if command -v entr > /dev/null; then ${DOC_FILES} | entr -c $(MAKE) build_docs; else $(MAKE) build_docs entr_warn; fi + +serve_docs: + $(MAKE) -C docs serve + +dev_docs: + $(MAKE) -j watch_docs serve_docs + +start_docs: + $(MAKE) -C docs start + +design_docs: + $(MAKE) -C docs design + +ruff_format: + uv run ruff format . + +ruff: + uv run ruff check . + +watch_ruff: + if command -v entr > /dev/null; then ${PY_FILES} | entr -c $(MAKE) ruff; else $(MAKE) ruff entr_warn; fi + +mypy: + uv run mypy `${PY_FILES}` + +watch_mypy: + if command -v entr > /dev/null; then ${PY_FILES} | entr -c $(MAKE) mypy; else $(MAKE) mypy entr_warn; fi + +format_markdown: + npx prettier --parser=markdown -w *.md docs/*.md docs/**/*.md CHANGES + +monkeytype_create: + uv run monkeytype run `uv run which py.test` + +monkeytype_apply: + uv run monkeytype list-modules | xargs -n1 -I{} sh -c 'uv run monkeytype apply {}' diff --git a/README.md b/README.md new file mode 100644 index 00000000000..fd84fc34686 --- /dev/null +++ b/README.md @@ -0,0 +1,281 @@ +# 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). + +[![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/) +[![Build status](https://github.com/tmux-python/tmuxp/workflows/tests/badge.svg)](https://github.com/tmux-python/tmuxp/actions?query=workflow%3A%22tests%22) +[![Code Coverage](https://codecov.io/gh/tmux-python/tmuxp/branch/master/graph/badge.svg)](https://codecov.io/gh/tmux-python/tmuxp) +[![License](https://img.shields.io/github/license/tmux-python/tmuxp.svg)](https://github.com/tmux-python/tmuxp/blob/master/LICENSE) + +**New to tmux?** [The Tao of tmux](https://leanpub.com/the-tao-of-tmux) +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 +#290](https://github.com/tmux-python/tmuxp/discussions/290)! + +# Installation + +pip: + +```console +$ pip install --user tmuxp +``` + +Homebrew: + +```console +$ brew install tmuxp +``` + +Debian / ubuntu: + +```console +$ sudo apt install tmuxp +``` + +Nix: + +```console +$ [[ -z $(which tmux) ]] && (nix-env -i tmux && nix-env -i tmuxp) || nix-env -i tmuxp +``` + +Find the package for your distro on repology: + +Developmental releases: + +- [pip](https://pip.pypa.io/en/stable/): + + ```console + pip install --user --upgrade --pre tmuxp + ``` + +- [pipx](https://pypa.github.io/pipx/docs/): + + ```console + pipx install --suffix=@next 'tmuxp' --pip-args '\--pre' --force + ``` + + Then use `tmuxp@next load [session]`. + +# Load a tmux session + +Load tmux sessions via json and YAML, +[tmuxinator](https://github.com/aziz/tmuxinator) and +[teamocil](https://github.com/remiprev/teamocil) style. + +```yaml +session_name: 4-pane-split +windows: + - window_name: dev window + layout: tiled + shell_command_before: + - cd ~/ # run as a first command in all panes + panes: + - shell_command: # pane no. 1 + - cd /var/log # run multiple commands in this pane + - ls -al | grep \.log + - echo second pane # pane no. 2 + - echo third pane # pane no. 3 + - echo fourth pane # pane no. 4 +``` + +Save as _mysession.yaml_, and load: + +```console +tmuxp load ./mysession.yaml +``` + +Projects with _.tmuxp.yaml_ or _.tmuxp.json_ load via directory: + +```console +tmuxp load path/to/my/project/ +``` + +Load multiple at once (in bg, offer to attach last): + +```console +tmuxp load mysession ./another/project/ +``` + +Name a session: + +```console +tmuxp load -s session_name ./mysession.yaml +``` + +[simple](http://tmuxp.git-pull.com/examples.html#short-hand-inline) and +[very +elaborate](http://tmuxp.git-pull.com/examples.html#super-advanced-dev-environment) +config examples + +# User-level configurations + +tmuxp checks for configs in user directories: + +- `$TMUXP_CONFIGDIR`, if set +- `$XDG_CONFIG_HOME`, usually _$HOME/.config/tmuxp/_ +- `$HOME/.tmuxp/` + +Load your tmuxp config from anywhere by using the filename, assuming +_\~/.config/tmuxp/mysession.yaml_ (or _.json_): + +```console +$ tmuxp load mysession +``` + +See [author's tmuxp configs](https://github.com/tony/tmuxp-config) and +the projects' +[tmuxp.yaml](https://github.com/tmux-python/tmuxp/blob/master/.tmuxp.yaml). + +# Shell + +_New in 1.6.0_: + +`tmuxp shell` launches into a python console preloaded with the attached +server, session, and window in +[libtmux](https://github.com/tmux-python/libtmux) objects. + +```console +$ tmuxp shell + +(Pdb) server + +(Pdb) server.sessions +[Session($1 your_project)] +(Pdb) session +Session($1 your_project) +(Pdb) session.name +'your_project' +(Pdb) window +Window(@3 1:your_window, Session($1 your_project)) +(Pdb) window.name +'your_window' +(Pdb) window.panes +[Pane(%6 Window(@3 1:your_window, Session($1 your_project))) +(Pdb) pane +Pane(%6 Window(@3 1:your_window, Session($1 your_project)) +``` + +Supports [PEP +553](https://www.python.org/dev/peps/pep-0553/) `breakpoint()` +(including `PYTHONBREAKPOINT`). Also supports direct commands via `-c`: + +```console +$ tmuxp shell -c 'print(window.name)' +my_window + +$ tmuxp shell -c 'print(window.name.upper())' +MY_WINDOW +``` + +Read more on [tmuxp shell](https://tmuxp.git-pull.com/cli/shell.html) in +the CLI docs. + +# Pre-load hook + +Run custom startup scripts (such as installing project dependencies +before loading tmux. See the +[bootstrap_env.py](https://github.com/tmux-python/tmuxp/blob/master/bootstrap_env.py) +and +[before_script](http://tmuxp.git-pull.com/examples.html#bootstrap-project-before-launch) +example + +# Load in detached state + +You can also load sessions in the background by passing `-d` flag + +# Screenshot + +image + +# Freeze a tmux session + +Snapshot your tmux layout, pane paths, and window/session names. + +```console +$ tmuxp freeze session-name +``` + +See more about [freezing +tmux](https://tmuxp.git-pull.com/cli/freeze.html) sessions. + +# Convert a session file + +Convert a session file from yaml to json and vice versa. + +```console +$ tmuxp convert filename +``` + +This will prompt you for confirmation and shows you the new file that is +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 +``` + +# Plugin System + +tmuxp has a plugin system to allow for custom behavior. See more about +the [Plugin System](http://tmuxp.git-pull.com/plugin_system.html). + +# Debugging Helpers + +The `load` command provides a way to log output to a log file for +debugging purposes. + +```console +$ tmuxp load --log-file . +``` + +Collect system info to submit with a Github issue: + +```console +$ tmuxp debug-info +------------------ +environment: + system: Linux + arch: x86_64 + +# ... so on +``` + +# Docs / Reading material + +See the [Quickstart](http://tmuxp.git-pull.com/quickstart.html). + +[Documentation](http://tmuxp.git-pull.com) homepage (also in +[中文](http://tmuxp-zh.rtfd.org/)) + +Want to learn more about tmux itself? [Read The Tao of Tmux +online](http://tmuxp.git-pull.com/about_tmux.html). + +# Donations + +Your donations fund development of new features, testing and support. +Your money will go directly to maintenance and development of the +project. If you are an individual, feel free to give whatever feels +right for the value you get out of the project. + +See donation options at . + +# Project details + +- tmux support: 1.8+ +- python support: >= 3.9, pypy, pypy3 +- Source: +- Docs: +- API: +- Changelog: +- Issues: +- Test Coverage: +- pypi: +- Open Hub: +- Repology: +- License: [MIT](http://opensource.org/licenses/MIT). diff --git a/README.rst b/README.rst deleted file mode 100644 index 486c9fc8636..00000000000 --- a/README.rst +++ /dev/null @@ -1,174 +0,0 @@ -`tmuxp` solves the panes / pains of managing workspaces. - -.. image:: https://travis-ci.org/tony/tmuxp.png?branch=master - :target: https://travis-ci.org/tony/tmuxp - -.. image:: https://badge.fury.io/py/tmuxp.png - :target: http://badge.fury.io/py/tmuxp - -.. figure:: https://raw.github.com/tony/tmuxp/master/doc/_static/tmuxp-dev-screenshot.png - :scale: 100% - :width: 65% - :align: center - -Open to testers ---------------- - -tmuxp is still **alpha** code and needs a few more weeks until stable. -See the `Issues tracker`_ to see known issues and for any other concerns. - -Install -""""""" - -- install ``tmux``, at least version **1.8** -- libyaml is installed for your distribution. - -Install ``tmuxp``: - -.. code-block:: bash - - $ pip install tmuxp - - -See: `Quickstart`_ - -CLI Commands -"""""""""""" - -tmuxp uses ``switch-client`` for you if already in a TMUX client. - -.. code-block:: bash - - $ tmuxp attach-session # current sessions - -Kill session - -.. code-block:: bash - - $ tmuxp kill-session # current sessions - -Load a session configuration from a YAML or JSON file. - -.. code-block:: bash - - $ tmuxp load # configs in config dir, current directory - -Convert a session config JSON <=> YAML: - -.. code-block:: bash - - $ tmuxp convert # configs in config dir, current directory - -Experimental: Import configuration from `teamocil`_ or `tmuxinator`_: - -.. code-block:: bash - - $ tmuxp import teamocil # configs in ~/.teamocil dir - $ tmuxp import tmuxinator # configs in ~/.tmuxinator dir - -See `installing bash completion`_ to get bash, zsh and tcsh completion -working on your machine. - -load tmux sessions from yaml and json -""""""""""""""""""""""""""""""""""""" - -Load from ``~/.tmuxp.yaml`` or ``~/.tmuxp.json`` in current directory. - -.. code-block:: bash - - $ tmuxp load . - -Load ``myconfig.yaml`` from ``~/.tmuxp`` - -.. code-block:: bash - - $ tmuxp load myconfig.yaml - -Load a relative or full config file (bash complete supports this too) - -.. code-block:: bash - - $ tmuxp load ./myconfig.yaml - $ tmuxp load ../myconfig.yaml - $ tmuxp load /var/www/mywebproject/myconfig.yaml - -``$ mkdir ~/.tmuxp`` and make a file ``~/.tmuxp/test.yaml``. - -.. code-block:: yaml - - session_name: 2-pane-vertical - windows: - - window_name: my test window - panes: - - pwd - - pwd - -.. code-block:: bash - - $ tmuxp load test.yaml - -or ``~/.tmuxp/test.json``: - -.. code-block:: json - - { - "windows": [ - { - "panes": [ - "pwd", - "pwd" - ], - "window_name": "my test window" - } - ], - "session_name": "2-pane-vertical" - } - -.. code-block:: bash - - $ tmuxp load test.json - -See: `Examples`_ - -============== ========================================================== -tmux support 1.8, 1.9-dev -config support yaml, json, python dict -Travis http://travis-ci.org/tony/tmuxp -Docs http://tmuxp.rtfd.org -API http://tmuxp.readthedocs.org/en/latest/api.html -Changelog http://tmuxp.readthedocs.org/en/latest/changes.html -Issues https://github.com/tony/tmuxp/issues -Source https://github.com/tony/tmuxp -pypi https://pypi.python.org/pypi/tmuxp -License `BSD`_. -git repo .. code-block:: bash - - $ git clone https://github.com/tony/tmuxp.git -install dev .. code-block:: bash - - $ git clone https://github.com/tony/tmuxp.git tmuxp - $ cd ./tmuxp - $ virtualenv .env - $ source .env/bin/activate - $ pip install -e . - - See the `developing and testing`_ page in the docs for - more. -tests .. code-block:: bash - - $ python ./run_tests.py -============== ========================================================== - -.. _BSD: http://opensource.org/licenses/BSD-3-Clause -.. _developing and testing: http://tmuxp.readthedocs.org/en/latest/developing.html -.. _Examples: http://tmuxp.readthedocs.org/en/latest/examples.html -.. _Quickstart: http://tmuxp.readthedocs.org/en/latest/quickstart.html -.. _installing bash completion: http://tmuxp.readthedocs.org/en/latest/quickstart.html#bash-completion -.. _Developing and Testing: http://tmuxp.readthedocs.org/en/latest/developing.html -.. _tmuxinator: https://github.com/aziz/tmuxinator -.. _teamocil: https://github.com/remiprev/teamocil -.. _abstraction layer: http://en.wikipedia.org/wiki/Abstraction_layer -.. _ORM: http://tmuxp.readthedocs.org/en/latest/quickstart.html#tmux-orm -.. _tmux(1): http://tmux.sourceforge.net/ -.. _Issues tracker: https://github.com/tony/tmuxp/issues -.. _python dict: http://docs.python.org/2/library/stdtypes.html#dict diff --git a/TODO b/TODO deleted file mode 100644 index c3faba1c0e9..00000000000 --- a/TODO +++ /dev/null @@ -1,316 +0,0 @@ -Roadmap -------- - -Immediate -""""""""" - -- auto-rename -- prompt filepath/filename choice before save -- confirm and loop -- fix import of blank panes teamocil tmuxinator -- give a real session name if none exists or support building without - a session name - -- make prompt_bool Y/n by default -- shorts for load - -========================= ====================================================== -tmuxp . search cwd for .tmuxp.yaml, .tmuxp.json, load -tmuxp / list sessions -tmuxp / load session name -tmuxp load search ~/.tmuxp for .yaml, .json, - .py -tmuxp load fullpath load from path - (starts with . or /) -========================= ====================================================== - - -Now -""" - -- tmuxp load/import/convert should also support any file on file system, if it - begins with ./ or /, it should be allowed. -- panes .set_width() and .set_height() as shortcut for .resize_pane() -- from tmuxinator: add a "pre" command that runs once at start of session -- from teamocil: add 'clear': True -- from teamocil: add 'target' bottom-right, bottom-left, top-right, top-left - to split. -- in order to incorporate interoperability with tmux versions 1.6, Session, - Window and Pane classes will be index'd on a hierachy of keys -- new-session inside $TMUX that switches' client - - Session - - session_id ( tmux >= 1.8 ) - - session_name ( tmux < 1.7) - - Window - - window_id (tmux >= 1.8) - - window_index (tmux < 1.7, if a window is moved, it will return a new - object instance for the same window. tmuxp should sound a - warning that this version that objects is bound to - window_index, it won't be the same window returned.) - for code cleanness, tmuxp ORM only works for TMUX >= 1.8. - The build system can support TMUX >= 1.6 and maybe lower. - - window_name ? - - Pane - - pane_id - - .getById should be refactored to filter by the idAttribute of the object. - - if child[child.idAttribute]a == id - -Detect version on startup, Detect if binary exists. - -use self.idAttribute for individual classes so it can declare it does not have a -window_id. - -Notes -""""" - -- 1.6 introduces formatters -F. tmux 1.7 may not support -P for returning -- it may be still possible to get basic tmux state with ``-a`` without custom - formatting for older versions. actions can be done, and list-sessions, - list-panes, list-windows can be indexed the new state -- http://sourceforge.net/projects/tmux/files/tmux/tmux-1.4/ - - tmux 1.4 adds that pane is active. - - adds more window for list-windows and list-panes -- 1.3 - - http://sourceforge.net/projects/tmux/files/tmux/tmux-1.3/tmux-1.3-notes/download - - adds tiled layout. - - select pane UDLR -- 1.2 - - http://sourceforge.net/projects/tmux/files/tmux/tmux-1.2/tmux-1.2-notes/download - - libevent. - - panes can be reffered to as top, bottom, top-left, etc. - - server-wide options, set-options -s, show-options -s. - - join-pane (alias joinp) to split, and move an existing pane into the space - (the opposite of break-pane), tus simpolifying calls to split-window, - followed by move-window. - - window tarets supports -t! last window, -t+ next, -t- previous. - - reads socket path from $TMUX env if present and -L -S are not given - - vi style keys b, w, e copy mode - - copy mode may be prefixed by repeat count, [1-9] in vi mode, M-[1-9] in - emacs mode. - - attach session -r for read only -- 1.1 - - 2009/11/05 - - http://sourceforge.net/projects/tmux/files/tmux/tmux-1.1/tmux-1.1-notes/download - - split list-panes from list-windows - - grouped sessions with -t - - -big note. since 1.2, display-message -p can print to pane. - -To do -""""" - -- load / save sessions -- tmuxp to load current sessions, windows. present them like - unix-type files, session_name:window_index.pane_index -- https://github.com/zolrath/wemux wemux features -- redo unit tests to test for proper response from tmux commands for tmux - versions. this can be done by making tmux shell commands kept inside of _cmd - to see in travis how different versions react to this. -- rm nodemon dependency for .tmuxp.yaml, try a python solution -- create .focus or .select_pane that works on Window and Pane objects directly. -- find, findWhere, get, a la backbone for TmuxObject's. -- implement ``focus: true`` for panes and windows -- importer for teamocil / tmuxinator -- export live sessions -- autocomplete - - freeze live sessions - - configs in directory, in config folder - - tmuxp attach live sessions - - tmuxp attach-window live windows -- examples in py, json, yaml - - -- adjust builder tests to count windows + panes in conf then assert against - the ._panes, ._windows, .sessions count in built objects -- config_consistency_check() to check the structure, if windows have panes, - diagnose config issues. -- cli: allow shorthand for 'filename' without json/yaml/py/ini, but cli will - respond with more than one match if multiple file exists. -- window.resize_pane() -- window.select_layout() for custom layouts, test -- freeze session `tmuxp freeze` from inside current session - - to /tmp/ then launch $EDITOR? - - to .tmuxp/freezed/session_name-year-mm-dd.yaml / json / py? - - have a .py export that can launch tmux via python .tmux.py -- create example of an smart-session. -- add :rtype: for methods -- exporting config inlining. tests. -- self.refresh() - - self.refresh(), self.refresh_options(), self.refresh_panes() - - self.refresh(), self.refresh_* returns self to be chainable, so set_options - can self.refresh_options() can refresh_options after update (it's also - possible to just update one entry, and read stderr, decide on this - !performance) -- decouple the updating logic: - - self.list_windows will self.refresh and return self.windows, this - allows a self.list_windows which returns the latest, but a self.refresh() -- show-options, show-window-options -- add options to config syntax -- set-option, set-window-options -- use mktmpdir for config tests -- remove redundant from . import log from files -- look at removing sh dependency -- socket-name, socket-path support for .server instance and tmux commands -- tmux command should be from server, since it will rely on the contexxt of the - socket-name, socket-path -- move tests to use socket-name and socket-path -- allow run_tests.py and main.py to accept a CLI arg of a PID, or pass it in via - adding an os.environ['TMUXP_CLIENT_PID'], which can be used to SIGINT the - process http://stackoverflow.com/a/1112350 -- or detach see if detaching works (on tests, new server is created) - -- To a degree, be able to pull running tmux sessions, windows and panes - into Session, Window, and Pane objects and therefore be exportable - into configs. A la, many attempts before, a ``pip freeze``. -- The biggest difficulty is keeping the abstraction of tmux pure and - pythonic. -- A workflow to test a configuration file, launch tmux session/windows/panes - with a ctrl-a ``tbd`` to ``kill-session`` and monitor config file for changes, - lint it, create a new session, switch to it, and ``kill-session`` the old - one. -- Check for ``.hg`` and ``.git`` in ``$HOME/.tmuxp``, set a - notification if it is out of date. -- Have ``freeze`` check for ``virtualenv``, ``rvm``, ``perlbrew`` and add - it to the ``before_cmd``. -- grab pane when new_session created -- session.new_window -- create session.[windowindex] = Window or session.w.[window index] = Window ? -- session.to_yaml() export config to yaml -- session.to_yaml().save('filename') -- session.from_yaml().load('filename') -- cli: allow loading session tmw filename.{yaml, json, ..} to load, - - catch has-session, prompt to rename - - possibility: open to ``-t`` group session to target? -- cli: and definitely bashcomplete json/yaml/ini files + commands -- cli: replicate tmux commands too -- window.split_pane to split-pane -- experiment: have windows with 1 pane have access to pane objects? -- have session inherit window methods of the current active window - such as session.next_layout is now available -- have window inherit some pane methods of current pane? only if just 1? -- experiment: using .send-keys can be done on session, window and pane - level for power? -- experiment: Server object for managing / orchestrating across sessions? -- pane.send_keys for send-keys -- tmux session config generator - - log god: scan /var/log /opt/etc/var/log/ for apache2, nginx, Xorg and - create a session with windows reading logs of common 90% cases. - - web warrior: check for apache2/nginx/php/supervisor/upstart etc dirs - and make a session for that. - - dot config: check for .vim .config/awesome .tmux.conf and make a - session with windows for those config files -- feature like `z` to attach search session name / windows name / pane - directory, pane apps, and finally buffers to attach directly to that - session. note `find-window` does this. -- docs in this style? - http://docs.python-guide.org/en/latest/notes/styleguide/ -- should ._TMUX metadata make passing Session and Window objects into new - Window and Pane objects obsolete? look at thread locals / global -- contextmanager's and with to iterate over a server, session, window -- contextmanager iterate for all panes that have an attribute (directory, - window_name, etc) -- global for server, contains sessions, attribute _session and - _window object references global / thread local -- ipython notebook try using fbcat + imagemagick convert to see results - of tmux changes. fbgrab + tty works well for demonstration -- also look into scrot, x11 solutions and - https://github.com/KittyKatt/screenFetch -- control mode, for longer tmuxp sessions where references to - objects are needed to be updated and shown they've gone stale (a pane - object that has been closed needs to be changed to being stale, a window - object that has been renamed needs to have its window_name updated) -- and one more thing -- vim: may be used inside of a pane object with a filename (relative to - the pane dir, also accepts /) and vim windows may be split and opened -- support for importing teamocil and tmuxinator configs -- creating a pane / window should return the new object, then refresh the - parent (list_sessions for server, list_windows for session, list_panes - for window). -- renaming or moving a pane should always return the object session, - window or pane object and flush/refresh the contents of the tmux server - objects (sessions, windows, panes). -- if an object is removed from the list, any reference to it should be - changed. since python doesn't use pointers/references like other - languages, a pubsub like blinker http://pythonhosted.org/blinker/ or ee - https://github.com/jesusabdullah/pyee. -- remove ._TMUX, use collections.MutableMapping. check for {session, - window,pane}_id to see if its a live tmux object. use kwargs.pop() for - session, window, pane. -- create and test a compact / inline config format. -- a C-type of binding to pull server/session/window/pane information - directly from tmux. -- support for show-options and setting options via ``.options`` on session - and window. -- automatically handle rename-window when the value of the window-name is - set. this gives an abstraction of tmux that is then 'model-driven' like - backbone js, but also a pythonic abstraction. -- unit test roadmap. - - test schema, types of objects - - parsing of config types. export of config types - - config expand - - config inliner script - - config passthru / hierarchy - - export a current tmux session to tmux objects, then config - - data driven tmux, handle options, renames - - swapping windows using objects, swapping panes using Pane objects, - linking or moving windows via Session. -- remember that new-window without ``shell-command`` with run option - ``default-command`` if used. -- remove unnecessary kwargs, use optional kwargs in params to keep it - clean. -- sphinx docs -- before_cmd, after_cmd: - tbd, but commands will be able to be go before/after commands on any - level also. for instance, session may run before_cmd: and all windows - and panes within will run accordingly -- make session, window, pane chainable by returning self -- make Server.sessions, Session.windows, Windows.panes return a list, let - Server.refresh().sessions be chainable to return sessions object. -- Create a list of chainable items. .rename_window, .rename_session are - chainable. -- config precedence - - :: - - session(s) - 1. cmds (str like 'htop' or list ['pwd', 'htop']) - 2. root (str dir path, like '/var/www') - 3. window(s) - 1. cmd(s) - 2. root - 3. panes(s) - 1. dimensions - 2. cmd(s) - 3. root - - cmd, cwd can be added at the session, window and pane level. - - the deepest will have precedence. a command or cwd at the session level - will apply to all windows, panes within it. a command or cwd at window - level applies to all panes. a pane may specify its own cmd. -- create sliderepl quickstart -- bug in tmux as of 09/10/2013 - control mode will resize an - 'attach-session' and switching `aggressive_resize` and using - `force_width`, `force_height` doesn't seem to work. -- if no tmux session is live for tests, perhaps context managers can - async wrap tmux -C. -- use pane_id, window_id, session_id in targets. -- need to fix two use cases: - 1. no client but, list-sessions has results - but no client. raise - exception? use tmux -C wrapper for unit tests? prompt user to make a - tmux session? - 2. no client, no sessions -- check for list_clients -- check for list_sessions -- Check if tmux 1.8 (has control mode) -- Unit tests for tmux < 1.7, unit tests require a tmux open in another - terminal. -- tmux -C commands pass-thru if version is > 1.8 -- Server check if 'connect' exists. -- Server socket option diff --git a/conftest.py b/conftest.py new file mode 100644 index 00000000000..1c6c63e6b62 --- /dev/null +++ b/conftest.py @@ -0,0 +1,115 @@ +"""Conftest.py (root-level). + +We keep this in root pytest fixtures in pytest's doctest plugin to be available, as well +as avoiding conftest.py from being included in the wheel, in addition to pytest_plugin +for pytester only being available via the root directory. + +See "pytest_plugins in non-top-level conftest files" in +https://docs.pytest.org/en/stable/deprecations.html +""" + +from __future__ import annotations + +import logging +import os +import pathlib +import shutil +import typing as t + +import pytest +from _pytest.doctest import DoctestItem +from libtmux.test.random import namer + +from tests.fixtures import utils as test_utils +from tmuxp.workspace.finders import get_workspace_dir + +if t.TYPE_CHECKING: + from libtmux.session import Session + +logger = logging.getLogger(__name__) +USING_ZSH = "zsh" in os.getenv("SHELL", "") + + +@pytest.fixture(autouse=USING_ZSH, scope="session") +def zshrc(user_path: pathlib.Path) -> pathlib.Path | None: + """Quiets ZSH default message. + + Needs a startup file .zshenv, .zprofile, .zshrc, .zlogin. + """ + if not USING_ZSH: + return None + p = user_path / ".zshrc" + p.touch() + return p + + +@pytest.fixture(autouse=True) +def home_path_default(monkeypatch: pytest.MonkeyPatch, user_path: pathlib.Path) -> None: + """Set HOME to user_path (random, temporary directory).""" + monkeypatch.setenv("HOME", str(user_path)) + + +@pytest.fixture +def tmuxp_configdir(user_path: pathlib.Path) -> pathlib.Path: + """Ensure and return tmuxp config directory.""" + xdg_config_dir = user_path / ".config" + xdg_config_dir.mkdir(exist_ok=True) + + tmuxp_configdir = xdg_config_dir / "tmuxp" + tmuxp_configdir.mkdir(exist_ok=True) + return tmuxp_configdir + + +@pytest.fixture +def tmuxp_configdir_default( + monkeypatch: pytest.MonkeyPatch, + tmuxp_configdir: pathlib.Path, +) -> None: + """Set tmuxp configuration directory for ``TMUXP_CONFIGDIR``.""" + monkeypatch.setenv("TMUXP_CONFIGDIR", str(tmuxp_configdir)) + assert get_workspace_dir() == str(tmuxp_configdir) + + +@pytest.fixture +def monkeypatch_plugin_test_packages(monkeypatch: pytest.MonkeyPatch) -> None: + """Monkeypatch tmuxp plugin fixtures to python path.""" + paths = [ + "tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_bwb/", + "tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_bs/", + "tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_r/", + "tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_owc/", + "tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_awf/", + "tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_fail/", + ] + for path in paths: + monkeypatch.syspath_prepend(str(pathlib.Path(path).resolve())) + + +@pytest.fixture +def session_params(session_params: dict[str, t.Any]) -> dict[str, t.Any]: + """Terminal-friendly tmuxp session_params for dimensions.""" + session_params.update({"x": 800, "y": 600}) + return session_params + + +@pytest.fixture +def socket_name(request: pytest.FixtureRequest) -> str: + """Random socket name for tmuxp.""" + return f"tmuxp_test{next(namer)}" + + +@pytest.fixture(autouse=True) +def add_doctest_fixtures( + request: pytest.FixtureRequest, + doctest_namespace: dict[str, t.Any], + tmp_path: pathlib.Path, +) -> None: + """Harness pytest fixtures to doctests namespace.""" + if isinstance(request._pyfuncitem, DoctestItem) and shutil.which("tmux"): + doctest_namespace["server"] = request.getfixturevalue("server") + session: Session = request.getfixturevalue("session") + doctest_namespace["session"] = session + doctest_namespace["window"] = session.active_window + doctest_namespace["pane"] = session.active_pane + doctest_namespace["test_utils"] = test_utils + doctest_namespace["tmp_path"] = tmp_path diff --git a/doc/_static/tmuxp.css b/doc/_static/tmuxp.css deleted file mode 100644 index bdea2017fc8..00000000000 --- a/doc/_static/tmuxp.css +++ /dev/null @@ -1,14 +0,0 @@ -div.sidebar { - margin: 0px; - border: 0px; - padding: 0px; - background-color: inherit; -} - -div.sidebar .sidebar-title { - display: none; -} - -form.navbar-form { - padding: 0px 10px; -} diff --git a/doc/_templates/layout.html b/doc/_templates/layout.html deleted file mode 100644 index 022f011cab9..00000000000 --- a/doc/_templates/layout.html +++ /dev/null @@ -1,5 +0,0 @@ -{# Import the theme's layout. #} -{% extends "!layout.html" %} - -{# Include our new CSS file into existing ones. #} -{% set css_files = css_files + ['_static/tmuxp.css']%} diff --git a/doc/_themes b/doc/_themes deleted file mode 160000 index 8844c82c716..00000000000 --- a/doc/_themes +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8844c82c716916cf082cf3adb43f4224c8cc12f8 diff --git a/doc/about.rst b/doc/about.rst deleted file mode 100644 index b8906a5af2f..00000000000 --- a/doc/about.rst +++ /dev/null @@ -1,26 +0,0 @@ -.. _about: - -===== -About -===== - -tmuxp helps you manage your text-based workspaces. Its BSD licensed. - -Internally, tmuxp is an object relational mapper on top of tmux. -End-users may use YAML, JSON and :py:obj:`dict` configurations to launch -workspaces like `tmuxinator`_ and `teamocil`_. - -To jump right in, see :ref:`quickstart` and :ref:`examples`. - -Interested in some kung-fu or joining the effort? :ref:`api` and -:ref:`developing` - -License is `BSD-licensed`_. Code can be found at github at -http://github.com/tony/tmuxp. - -.. _attempt at 1.7 test: https://travis-ci.org/tony/tmuxp/jobs/12348263 -.. _kaptan: https://github.com/emre/kaptan -.. _unittest: http://docs.python.org/2/library/unittest.html -.. _BSD-licensed: http://opensource.org/licenses/BSD-2-Clause -.. _tmuxinator: https://github.com/aziz/tmuxinator -.. _teamocil: https://github.com/remiprev/teamocil diff --git a/doc/about_tmux.rst b/doc/about_tmux.rst deleted file mode 100644 index b9000b9835f..00000000000 --- a/doc/about_tmux.rst +++ /dev/null @@ -1,274 +0,0 @@ -.. _about_tmux: - -=============== -The Tao of tmux -=============== - -.. figure:: _static/tao-tmux-screenshot.png - :scale: 100% - :width: 100% - :align: center - - BSD-licensed terminal multiplexer. - -tmux is geared for technical users who make heavy use of text-based -interfaces. If you are interested in starting to learn Linux / OS X better -as a future programmer or system administrator - welcome. - -In the world of computers, there are many dimensions: - -1. The text dimension -2. The graphical dimension - -This is the text dimension. This is about fixed-width fonts and that old -fashioned black terminal. - -tmux is to the console what a desktop is to gui apps. It's a world inside -the text dimension. Inside tmux you can: - -- multitask inside of a terminal, run multiple applications -- have multiple command lines in the same window -- have multiple windows in the workspace -- switch between workspaces, like virtual desktops - -Features -======== - -For terminals only. No graphics. --------------------------------- - -uses: - -- window-manager for text-based applications -- keep applications in a background process - -A text-based window manager ---------------------------- - -=================== ====================== =============================== -**tmux** **"Desktop"-Speak** **Plain English** -------------------- ---------------------- ------------------------------- -Multiplexer Multitasking Do more than one thing at once -Session Desktop Where stuff gets done -Window Virtual Desktop or Has windows inside - screen -Pane Application Performs operations -=================== ====================== =============================== - -Multiple terminals to one screen --------------------------------- -It allows multiple applications or terminals to run at once. - -Being able to run 2 or more terminals on one screen is convenient. This -way one screen can be used to edit a file, and another may be used to -``$ tail -F`` a logfile. - -.. aafig:: - - +--------+--------+ - | $ bash | $ bash | - | | | - | | | - | | | - | | | - | | | - | | | - +--------+--------+ - -.. aafig:: - - +--------+--------+ - | $ bash | $ bash | - | | | - | | | - +--------+--------+ - | $ vim | $ bash | - | | | - | | | - +--------+--------+ - -You can create and remove as many terminal as you want. - -It allows multiple layouts to view the apps -------------------------------------------- - -Different applications are viewable better in different layouts. - -It allows switching between layouts such as... - -Organize apps based on your needs ---------------------------------- -You can categorize and keep many terminals / applications separated into -multiple windows - -In addition to being able to split the terminal into multiple panes, you -can create new windows as much as you want. - -.. aafig:: - :textual: - - +---------+---------+ +--------------------+ - | $ bash | $ bash | | $ vim | - | | | | | - | | | /-----------------\ | | - +---------+---------+ -> |'switch-window 2'| -> | | - | $ vim | $ bash | \-----------------/ | | - | | | | | - | | | | | - +---------+---------+ +--------------------+ - | '1:sys* 2:vim' | | '1:sys 2:vim*' | - +-------------------+ +--------------------+ - -You can switch between the windows you create. - -Resume everything later ------------------------ - -You can leave tmux and all applications running (detach), log out, make a -sandwich, and re-(attach), all applications are still running! - -.. aafig:: - :textual: - - +--------+--------+ +-----------------------+ - | $ bash | $ bash | | $ [screen detached] | - | | | | | - | | | /------------\ | | - +--------+--------+ --> | detach | --> | | - | $ vim | $ bash | | 'Ctrl-b b' | | | - | | | \------------/ | | - | | | | | - +--------+--------+ +-----------------------+ - | - +-------------------------------------------+ - | - v - +-----------------------+ +--------+--------+ - | $ [screen detached] | | $ bash | $ bash | - | $ tmux attach | | | | - | | /------------\ | | | - | | --> | attaching | --> +--------+--------+ - | | \------------/ | $ vim | $ bash | - | | | | | - | | | | | - +-----------------------+ +--------+--------+ - - -Core Concepts -============= - -Your workflow -------------- -You can keep tmux on a server with your latest work, come back and resume -your `"train of thought"`_ and work. - -Multitasking. More important than any technical jargon - it's preserving -the thinking you have, whether you were in the midst of a one-off task, or -a common task. - -If you do a task commonly, it may help to use an application which manages -tmux workspaces. - -.. _"train of thought": http://en.wikipedia.org/wiki/Train_of_thought - -.. _server: - -Server ------- - -A server contains :ref:`session`'s. - -tmux starts the server automatically if it's not running. - -In advanced cases, multiple can be run by specifying ``[-L socket-name]`` -and ``[-S socket-path]``. - -.. _client: - -Client ------- - -Attaches to a tmux :ref:`server`. - -.. _session: - -Session -------- - -Inside a tmux :ref:`server`. - -The session holds :ref:`window`. The bottom bar in tmux shows a list of -windows. Normally they can be navigated with ``Ctrl-a [0-9]``, -``Ctrl-a n`` and ``Ctrl-a p``. - - -.. aafig:: - :textual: - - +----------------------------------------------------------------+ - | +--------+--------+ +-----------------+ +-----------------+ | - | | pane | pane | | pane | | pane | | - | | | | | | | | | - | | | | | | | | | - | +--------+--------+ | | +-----------------+ | - | | pane | pane | | | | pane | | - | | | | | | | | | - | | | | | | | | | - | +--------+--------+ +-----------------+ +-----------------+ | - | | window | | window | | window | | - | \--------+--------/ \-----------------/ \-----------------/ | - +----------------------------------------------------------------+ - | session | - \----------------------------------------------------------------/ - -sessions can have a name. - -uniquely identified by: - -important attributes: - -========================= ================================================ -session_name -session_id -========================= ================================================ - -other attributes: - -========================= ================================================ -session_windows -session_width -session_height -session_created -session_created_string -session_attached -session_grouped -session_group -========================= ================================================ - -.. _window: - -Window ------- -inside a :ref:`session`. - -holds panes. - -panes can be organized with a layouts. - -windows can have names. - -.. _pane: - -Pane ----- -inside / Linked to a :ref:`window`. - -a pty (pseudoterminal). - -.. _target: - -Target ------- - -a target, cited in the manual as ``[-t target]`` can be a session, window -or pane. diff --git a/doc/api.rst b/doc/api.rst deleted file mode 100644 index b043b67aa72..00000000000 --- a/doc/api.rst +++ /dev/null @@ -1,137 +0,0 @@ -.. _api: - -==================== -Python API Reference -==================== - -.. module:: tmuxp - -Server Object -------------- - -.. autoclass:: Server - :members: - :inherited-members: - :show-inheritance: - - .. attribute:: _sessions - - A :py:obj:`list` of the server's :class:`Session` objects. - -Session Object --------------- - -.. autoclass:: Session - :members: - :inherited-members: - :show-inheritance: - - .. attribute:: _windows - - A :py:obj:`list` of session's :class:`Window` objects. - -Window Object -------------- - -.. autoclass:: Window - :members: - :inherited-members: - :private-members: - :show-inheritance: - - .. attribute:: _session - - The :class:`Session` of the window. - - .. attribute:: _panes - - A :py:obj:`list` of the window's :class:`Pane` objects. - -Pane Object ------------ - -.. autoclass:: Pane - :members: - :inherited-members: - :show-inheritance: - - .. attribute:: _session - - The :class:`Session` of the pane. - - .. attribute:: _window - - The :class:`Window` of the pane. - -Internals ---------- - -.. autoclass:: tmuxp.util.TmuxRelationalObject - :members: - -.. autoclass:: tmuxp.util.TmuxMappingObject - :members: - -.. autoclass:: tmuxp.util.tmux - -.. automethod:: tmuxp.util.version - -.. automethod:: tmuxp.util.oh_my_zsh_auto_title - -.. automethod:: tmuxp.util.which - -Command Line ------------- - -.. automethod:: tmuxp.cli.startup - -.. automethod:: tmuxp.cli.prompt -.. automethod:: tmuxp.cli.prompt_bool -.. automethod:: tmuxp.cli.prompt_choices - -.. automethod:: tmuxp.cli.setup_logger -.. automethod:: tmuxp.cli.get_parser - -Configuration -------------- - -Finding -""""""" - -.. automethod:: tmuxp.config.is_config_file - -.. automethod:: tmuxp.config.in_dir - -.. automethod:: tmuxp.config.in_cwd - -Import and export -""""""""""""""""" - -.. automethod:: tmuxp.config.check_consistency - -.. automethod:: tmuxp.config.expand - -.. automethod:: tmuxp.config.inline - -.. automethod:: tmuxp.config.trickle - -.. automethod:: tmuxp.config.import_teamocil - -.. automethod:: tmuxp.config.import_tmuxinator - -Workspace Builder ------------------ - -.. autoclass:: tmuxp.WorkspaceBuilder - :members: - -Exceptions ----------- - -.. autoexception:: tmuxp.exc.TmuxSessionExists - -.. autoexception:: tmuxp.exc.EmptyConfigException - -.. autoexception:: tmuxp.exc.ConfigError - - diff --git a/doc/changes.rst b/doc/changes.rst deleted file mode 100644 index 0ff94394ba3..00000000000 --- a/doc/changes.rst +++ /dev/null @@ -1,10 +0,0 @@ -.. _changes: - -========= -Changelog -========= - -.. module:: tmuxp - -.. include:: ../CHANGES - :start-line: 3 diff --git a/doc/conf.py b/doc/conf.py deleted file mode 100644 index c34e6a43900..00000000000 --- a/doc/conf.py +++ /dev/null @@ -1,274 +0,0 @@ -# -*- coding: utf-8 -*- -# -# tmuxp documentation build configuration file, created by -# sphinx-quickstart on Sun Sep 8 17:59:29 2013. -# -# This file is execfile()d with the current directory set to its containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - -import sys, os, cloud_sptheme - -sys.path.append(os.path.abspath('.')) -sys.path.append(os.path.abspath('_themes')) - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -#sys.path.insert(0, os.path.abspath('.')) - -# -- General configuration ----------------------------------------------------- - - -# If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be extensions -# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['sphinx.ext.autodoc', - 'sphinx.ext.intersphinx', - 'sphinx.ext.todo', - 'sphinxcontrib.aafig', - ] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix of source filenames. -source_suffix = '.rst' - -# The encoding of source files. -#source_encoding = 'utf-8-sig' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = u'tmuxp' -copyright = u'2013, Tony Narlock' - -rst_prolog = """ -.. note:: - - tmuxp is still **alpha** code and needs a few more weeks until stable. - See the `Issues tracker`_ to see known issues and for any other concerns. - -.. _Issues tracker: https://github.com/tony/tmuxp/issues -""" - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. -version = '0.1' -# The full version, including alpha/beta/rc tags. -release = '0.1-dev' - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -#language = None - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -#today = '' -# Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -exclude_patterns = ['_build'] - -# The reST default role (used for this markup: `text`) to use for all documents. -#default_role = None - -# If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -#add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -#show_authors = False - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] - - -# -- Options for HTML output --------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -on_rtd = os.environ.get('READTHEDOCS', None) == 'True' -if on_rtd: - html_theme = 'default' -else: - html_theme = 'bootstrap' - html_theme = 'rtd' - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -#html_theme_options = {} - -# Add any paths that contain custom themes here, relative to this directory. -html_theme_path = ['_themes'] - -# from pyramid_sphinx_themes import get_html_themes_path -# html_theme_path = get_html_themes_path() - -# The name for this set of Sphinx documents. If None, it defaults to -# " v documentation". -#html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -#html_logo = None - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -#html_favicon = None - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] - -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -#html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -#html_sidebars = {} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -#html_additional_pages = {} - -# If false, no module index is generated. -#html_domain_indices = True - -# If false, no index is generated. -#html_use_index = True - -# If true, the index is split into individual pages for each letter. -#html_split_index = False - -# If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True - -# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True - -# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -#html_use_opensearch = '' - -# This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None - -# Output file base name for HTML help builder. -htmlhelp_basename = 'tmuxpdoc' - - -# -- Options for LaTeX output -------------------------------------------------- - -latex_elements = { -# The paper size ('letterpaper' or 'a4paper'). -#'papersize': 'letterpaper', - -# The font size ('10pt', '11pt' or '12pt'). -#'pointsize': '10pt', - -# Additional stuff for the LaTeX preamble. -#'preamble': '', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, author, documentclass [howto/manual]). -latex_documents = [ - ('index', 'tmuxp.tex', u'tmuxp Documentation', - u'Tony Narlock', 'manual'), -] - -# The name of an image file (relative to this directory) to place at the top of -# the title page. -#latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -#latex_use_parts = False - -# If true, show page references after internal links. -#latex_show_pagerefs = False - -# If true, show URL addresses after external links. -#latex_show_urls = False - -# Documents to append as an appendix to all manuals. -#latex_appendices = [] - -# If false, no module index is generated. -#latex_domain_indices = True - - -# -- Options for manual page output -------------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - ('index', 'tmuxp', u'tmuxp Documentation', - [u'Tony Narlock'], 1) -] - -# If true, show URL addresses after external links. -#man_show_urls = False - - -# -- Options for Texinfo output ------------------------------------------------ - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - ('index', 'tmuxp', u'tmuxp Documentation', - u'Tony Narlock', 'tmuxp', 'tmuxp', - 'Miscellaneous'), -] - -# Documents to append as an appendix to all manuals. -#texinfo_appendices = [] - -# If false, no module index is generated. -#texinfo_domain_indices = True - -# How to display URL addresses: 'footnote', 'no', or 'inline'. -#texinfo_show_urls = 'footnote' - - -# Example configuration for intersphinx: refer to the Python standard library. -intersphinx_mapping = {'http://docs.python.org/': None} - -# aafig format, try to get working with pdf -aafig_format = dict(latex='pdf', html='svg') diff --git a/doc/developing.rst b/doc/developing.rst deleted file mode 100644 index a0639eb0906..00000000000 --- a/doc/developing.rst +++ /dev/null @@ -1,301 +0,0 @@ -.. _developing: - -====================== -Developing and Testing -====================== - -.. todo:: - link to sliderepl or ipython notebook slides - -Our tests are inside ``./tmuxp/testsuite``. Tests are implemented using -:py:mod:`unittest`. - -``./run_tests.py`` will create a tmux server on a separate ``socket_name`` -using ``$ tmux -L test_case``. - -.. _install_dev_env: - -Install the latest code from git --------------------------------- - -To begin developing, check out the code from github: - -.. code-block:: bash - - $ git clone git@github.com:tony/tmuxp.git - $ cd tmuxp - -Now create a virtualenv, if you don't know how to, you can create a -virtualenv with: - -.. code-block:: bash - - $ virtualenv .env - -Then activate it to your current tty / terminal session with: - -.. code-block:: bash - - $ source .env/bin/activate - -Good! Now let's run this: - -.. code-block:: bash - - $ 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. - -.. code-block:: bash - - $ tmuxp - -Test Runner ------------ - -As you 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 ``.env`` -folder with its own packages. - -.. code-block:: bash - - $ ./run_tests.py - -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: - -Choose tests to run -""""""""""""""""""" - -.. note:: - - As of v0.0.20, ``--tests`` automatically assume the namespace of - ``tmuxp.testsuite``. - - .. code-block:: bash - - $ ./run_tests.py --tests test_config.ImportExportTest - - Is now equivalent to: - - .. code-block:: bash - - $ ./run_tests.py --tests tmuxp.testsuite.test_config.ImportExportTest - -Testing specific TestSuites, TestCase and tests - -.. code-block:: bash - - $ ./run_tests.py --help - -Will give you an output of ways you can choose to run tests. Example for -``test_config`` TestSuite: - -By :py:class:`unittest.TestSuite` / module: - -.. code-block:: bash - - $ ./run_tests.py test_config - -by :py:class:`unittest.TestCase`: - -.. code-block:: bash - - $ ./run_tests.py --tests test_config.ImportExportTest - -individual tests: - -.. code-block:: bash - - $ ./run_tests.py --tests test_config.ImportExportTest.test_export_json - -Multiple can be separated by spaces: - -.. code-block:: bash - - $ ./run_tests.py --tests ImportExportTest.test_export_json \ - ImportExportTest.test_window - -.. _test_builder_visually: - -Visual testing -"""""""""""""" - -You can watch tmux testsuite build sessions visually also. - -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: - - .. code-block:: bash - - $ python ./run_tests.py --visual - -Terminal 1 should have flickered and built the session before your eyes. -tmuxp hides this building from normal users. :) - -Verbosity and logging -""""""""""""""""""""" - -``./run_tests.py`` supports two options, these are *optional* flags that -may be added to for :ref:`test_specific_tests` and -:ref:`test_builder_visually`. - -1. log level: ``-l`` aka ``--log-level``, with the options of ``debug``, - ``info``, ``warn``, ``error``, ``fatal``. Default is ``INFO``. - - .. code-block:: bash - - $ ./run_tests.py --log-level debug - - short form: - - .. code-block:: bash - - $ ./run_tests.py -l debug - -2. unit test verbosity: - - ``--verbosity`` may be set to ``0``, ``1`` and ``2``. Default: ``2``. - - .. code-block:: bash - - $ ./run_tests.py --verbosity 0 - -Watch files and test --------------------- - -You can re-run tests automatically on file edit. - -.. note:: - This requires and installation of ``watching_testrunner`` from pypi. - -Install `watching_testrunner`_ from `pypi`_: - -.. code-block:: bash - - $ pip install watching_testrunner - -To run all tests upon editing any ``.py`` file: - -.. code-block:: bash - - $ watching_testrunner --basepath ./ --pattern="*.py" python run_tests.py - -To run test where :ref:`test_builder_visually` you may: - -.. code-block:: bash - - $ watching_testrunner --basepath ./ --pattern="*.py" python run_tests.py --visual - -.. _watching_testrunner: https://pypi.python.org/pypi/watching_testrunner/1.0 -.. _pypi: https://pypi.python.org/pypi - -.. _tmuxp developer config: - -tmuxp developer config -"""""""""""""""""""""" - -.. image:: _static/tmuxp-dev-screenshot.png - :scale: 100% - :width: 60% - :align: center - -After you :ref:`install_dev_env`, when inside the tmuxp checkout: - -.. code-block:: bash - - $ tmuxp load . - -this will load the ``.tmuxp.yaml`` in the root of the project. - -.. literalinclude:: ../.tmuxp.yaml - :language: yaml - -Travis CI -""""""""" - -tmuxp uses `travis-ci`_ for continuous integration / automatic unit -testing. - -travis allows for testing against multiple scenarios. Currently tmuxp -is tested against 1.8 and latest in addition to python 2.7. The -`travis build site`_ uses this `.travis.yml`_ configuration: - -.. literalinclude:: ../.travis.yml - :language: yaml - -Internals ---------- - -Similarities to Tmux and Pythonics ----------------------------------- - -tmuxp is was built in the spirit of understanding how tmux operates -and how python objects and tools can abstract the API's in a pleasant way. - -tmuxp uses the identify ``FORMATTERS`` used by tmux, you can see -them inside of http://sourceforge.net/p/tmux/tmux-code/ci/master/tree/format.c. - -In this, I will also begin documenting the API. - -the use of: - -Session -:meth:`Session.new_window()` - returns a new Window object bound to the session, -also uses ``tmux new-window``. -:meth:`Session.new_session()` - class method - returns a new Session object. - -Differences from tmux ---------------------- - -Because this is a python abstraction and flags like ``start-directory`` -have dashes (-) replaced with underscores (_). - -interesting observations ------------------------- - -How is tmuxp able to keep references to panes, windows and sessions? - - Tmux has unique ID's for sessions, windows and panes. - - panes use ``%``, such as ``%1234`` - - windows use ``@``, such as ``@2345`` - - sessions use ``$``, for money, such as ``$`` - -How is tmuxp able to handle windows with no names? - - Tmux provides ``window_id`` as a unique identifier. - -What is a {pane,window}_index vs a {pane,window,session}_id? - - Pane index refers to the order of a pane on the screen. - - Window index refers to the # of the pane in the session. - -To assert pane, window and session data, tmuxp will use -:meth:`tmuxp.Server.list_sessions`, :meth:`tmuxp.Session.list_windows`, -:meth:`tmuxp.Window.list_panes` to update objects. - -Reference ---------- - -- tmux docs http://www.openbsd.org/cgi-bin/man.cgi?query=tmux&sektion=1 -- tmux source code http://sourceforge.net/p/tmux/tmux-code/ci/master/tree/ - -.. _travis-ci: http://www.travis-ci.org -.. _travis build site: http://www.travis-ci.org/tony/tmuxp -.. _.travis.yml: https://github.com/tony/tmuxp/blob/master/.travis.yml -.. _issue on github: https://github.com/tony/tmuxp/issues diff --git a/doc/examples.rst b/doc/examples.rst deleted file mode 100644 index 6cc567a85e6..00000000000 --- a/doc/examples.rst +++ /dev/null @@ -1,138 +0,0 @@ -.. _examples: - -======== -Examples -======== - -2 split panes -------------- - -.. sidebar:: 2 pane - - .. aafig:: - - +-----------------+ - | $ pwd | - | | - | | - +-----------------+ - | $ pwd | - | | - | | - +-----------------+ - -YAML - Short form -""""""""""""""""" - -.. literalinclude:: ../examples/2-pane-vertical.yaml - :language: yaml - -JSON - Short form -""""""""""""""""" - -.. literalinclude:: ../examples/2-pane-vertical.json - :language: json - -YAML - Christmas Tree -""""""""""""""""""""" - -.. literalinclude:: ../examples/2-pane-vertical-long.yaml - :language: yaml - -JSON - Christmas Tree -""""""""""""""""""""" - -.. literalinclude:: ../examples/2-pane-vertical-long.json - :language: json - -3 panes -------- - -.. sidebar:: 3 panes - - .. aafig:: - - +-----------------+ - | $ pwd | - | | - | | - +--------+--------+ - | $ pwd | $ pwd | - | | | - | | | - +--------+--------+ - -YAML -"""" - -.. literalinclude:: ../examples/3-pane.yaml - :language: yaml - -JSON -"""" - -.. literalinclude:: ../examples/3-pane.json - :language: json - -4 panes -------- - -.. sidebar:: 4 panes - - .. aafig:: - - +--------+--------+ - | $ pwd | $ pwd | - | | | - | | | - +--------+--------+ - | $ pwd | $ pwd | - | | | - | | | - +--------+--------+ - -YAML -"""" - -.. literalinclude:: ../examples/4-pane.yaml - :language: yaml - -JSON -"""" - -.. literalinclude:: ../examples/4-pane.json - :language: json - - -Super-advanced dev environment ------------------------------- - -.. seealso:: - :ref:`tmuxp developer config` in the :ref:`developing` section. - -YAML -"""" - -.. literalinclude:: ../.tmuxp.yaml - :language: yaml - -JSON -"""" - -.. literalinclude:: ../.tmuxp.json - :language: json - - -Kung fu -------- - -.. note:: - - tmuxp sessions can be scripted in python. The first way is to use the - ORM in the :ref:`API`. The second is to pass a :py:obj:`dict` into - :class:`tmuxp.WorkspaceBuilder` with a correct schema. See: - :meth:`tmuxp.config.check_consistency`. - -Add yours? Submit a pull request to the `github`_ site! - -.. _github: https://github.com/tony/tmuxp diff --git a/doc/glossary.rst b/doc/glossary.rst deleted file mode 100644 index fac3a86ab60..00000000000 --- a/doc/glossary.rst +++ /dev/null @@ -1,19 +0,0 @@ -============== -tmuxp Glossary -============== - - -.. glossary:: - - tmuxp - A tool to manage workspaces with tmux. A pythonic abstraction of - tmux. - - tmux(1) - The tmux binary. Used internally to distinguish tmuxp is only a - layer on top of tmux. - - kaptan - configuration management library, see `kaptan on github`_. - -.. _kaptan on github: https://github.com/emre/kaptan diff --git a/doc/index.rst b/doc/index.rst deleted file mode 100644 index f42cde082ff..00000000000 --- a/doc/index.rst +++ /dev/null @@ -1,114 +0,0 @@ -tmuxp -===== - -.. image:: _static/tmuxp-dev-screenshot.png - :scale: 35% - :width: 100% - :align: right - -tmuxp, a novel approach to managing `tmux(1)`_ workspaces through -python objects. Features: - -- Load + switch to new session from inside tmux. -- bash / zsh / tcsh completion -- JSON, YAML and `python dict`_ configuration. -- Support for pre-commands with ``shell_command_before`` to load - virtualenv / rvm / any other commands. -- Session resuming from config file if already running. -- Per-project tmux sessions -- uses tmux 1.8's ``pane_id``, ``window_id`` and ``session_id`` to build - create python objects to build workspaces with the freshest data. - -tmuxp works in 3 ways: - -- a pythonic `abstraction layer`_ on top of tmux' CLI commands -- an `ORM`_ that internally orchestrates relations between servers, - sessions, windows and panes for good and evil purposes. -- CLI tmux session manager, similar to `teamocil`_ and `tmuxinator`_, with - support for loading YAML, JSON and python dicts. - -Get started ------------ - -Get the prerequisites: - -1. installed ``tmux``, at least version **1.8** -2. libyaml is installed for your distribution. - -To install ``tmuxp``: - -.. code-block:: bash - - $ pip install tmuxp - -``$ mkdir ~/.tmuxp`` and make a file ``~/.tmuxp/test.yaml``. - -.. code-block:: yaml - - session_name: 2-pane-vertical - windows: - - window_name: my test window - panes: - - pwd - - pwd - -.. code-block:: bash - - $ tmuxp load test.yaml - -or ``~/.tmuxp/test.json``: - -.. code-block:: json - - { - "windows": [ - { - "panes": [ - "pwd", - "pwd" - ], - "window_name": "my test window" - } - ], - "session_name": "2-pane-vertical" - } - -.. code-block:: bash - - $ tmuxp load test.json - -Jump right in: See `Examples`_, `Quickstart`_ and `bash completion`_ -support. - -Explore: - -.. toctree:: - :maxdepth: 2 - - about - about_tmux - quickstart - examples - orm_al - developing - api - glossary - changes - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` - -.. _tmuxinator: https://github.com/aziz/tmuxinator -.. _teamocil: https://github.com/remiprev/teamocil -.. _abstraction layer: http://en.wikipedia.org/wiki/Abstraction_layer -.. _ORM: http://en.wikipedia.org/wiki/Object-relational_mapping -.. _Examples: http://tmuxp.readthedocs.org/en/latest/examples.html -.. _Quickstart: http://tmuxp.readthedocs.org/en/latest/quickstart.html -.. _bash completion: http://tmuxp.readthedocs.org/en/latest/quickstart.html#bash-completion -.. _tmux(1): http://tmux.sourceforge.net/ -.. _Issues tracker: https://github.com/tony/tmuxp/issues -.. _python dict: http://docs.python.org/2/library/stdtypes.html#dict diff --git a/doc/orm_al.rst b/doc/orm_al.rst deleted file mode 100644 index 926b164b5bf..00000000000 --- a/doc/orm_al.rst +++ /dev/null @@ -1,45 +0,0 @@ -.. _orm_al: - -========================= -ORM and Abstraction Layer -========================= - -.. module:: tmuxp - -tmuxp is an `abstraction layer` against tmux' command line arguments. - -tmuxp is an `ORM` in the sense bases of :class:`util.TmuxObject`, such as -:class:`Server`, :class:`Session`, :class:`Window` and :class:`Pane` -are stateful objects and related to their parent or child. - -======================== ======================= ========================= -Object Child Parent -======================== ======================= ========================= -:class:`Server` :class:`Session` None -:class:`Session` :class:`Window` :class:`Server` -:class:`Window` :class:`Pane` :class:`Session` -:class:`Pane` None :class:`Window` -======================== ======================= ========================= - -Internally, tmux allows multiple servers to be ran on a system. Each one -uses a socket. Most users worry since tmux will communicate to a default -server automatically. If one doesn't exist, tmux does it for you. - -A server can have multiple sessions. ``Ctrl-a s`` can be used to switch -between sessions running on the server. - -Sessions, Windows and Panes all have their own unique identifier for -internal purposes. - -======================== ======================= ========================= -Object Prefix Example -======================== ======================= ========================= -:class:`Server` N/A N/A, uses ``socket-name`` - and ``socket-path`` -:class:`Session` ``$`` ``$13`` -:class:`Window` ``@`` ``@3243`` -:class:`Pane` ``%`` ``%5433`` -======================== ======================= ========================= - -.. _abstraction layer: http://en.wikipedia.org/wiki/Abstraction_layer -.. _ORM: http://en.wikipedia.org/wiki/Object-relational_mapping diff --git a/doc/quickstart.rst b/doc/quickstart.rst deleted file mode 100644 index 3f9b35bef3b..00000000000 --- a/doc/quickstart.rst +++ /dev/null @@ -1,111 +0,0 @@ -.. _quickstart: - -========== -Quickstart -========== - -Tmux Session Manager --------------------- - -tmuxp launches sessions from a configuration file. - -Configuration files can be stored in ``$HOME/.tmuxp`` or in project -directories as ``.tmuxp.py``, ``.tmuxp.json`` or ``.tmuxp.yaml``. - -Every configuratio is required to have: - -1. ``session_name`` -2. list of ``windows`` -3. list of ``panes`` for every window in ``windows`` - -Create a file, ``~/.tmuxp/example.yaml``: - -.. literalinclude:: ../examples/2-pane-vertical.yaml - :language: yaml - -with tmuxp: - -.. code-block:: bash - - $ tmuxp load -l - -It will list configs available in the current directory and -``$HOME/.tmuxp``. ``example.yaml`` is detected by tmuxp. - -.. code-block:: bash - - $ tmuxp load example.yaml - -This creates your tmuxp session. - -.. seealso:: :ref:`examples` - -Bash completion -""""""""""""""" - -For bash, ``.bashrc``: - -.. code-block:: bash - - $ source tmuxp.bash - -For tcsh, ``.tcshrc``: - -.. code-block:: bash - - $ complete tmuxp 'p/*/`tmuxp.tcsh`/' - -For zsh, ``.zshrc``: - -.. code-block:: bash - - $ source tmuxp.zsh - -Python ORM + AL ---------------- - -ORM - `Object Relational Mapper`_ - -AL - `Abstraction Layer`_ - -.. _Abstraction Layer: http://en.wikipedia.org/wiki/Abstraction_layer -.. _Object Relational Mapper: http://en.wikipedia.org/wiki/Object-relational_mapping - -python abstraction layer -"""""""""""""""""""""""" - -.. module:: tmuxp - -.. seealso:: - :ref:`tmuxp python API documentation ` and :ref:`developing`. - -======================================== ================================= -:ref:`tmuxp python api ` :term:`tmux(1)` equivalent -======================================== ================================= -:class:`Server.list_sessions()` ``$ tmux list-sessions`` -:class:`Session.list_windows()` ``$ tmux list-windows`` -:class:`Window.list_panes()` ``$ tmux list-panes`` -:class:`Server.new_session()` ``$ tmux new-session`` -:class:`Session.new_window()` ``$ tmux new-window`` -:class:`Window.split_window()` ``$ tmux split-window`` -:class:`Pane.send_keys()` ``$ tmux send-keys`` -======================================== ================================= - -tmux ORM -"""""""" - -tmuxp's core internal feature is the object relation and orchestration of -the tmux server (think an `engine`_ in `SQLAlchemy`_) and the server's -sessions, so on... - -- :class:`Server` holds :class:`Session` objects. -- :class:`Session` holds :class:`Window` objects. -- :class:`Window` holds :class:`Pane` objects. - -instances of tmux objects use tmux `1.8`_'s ``pane_id``, ``window_id`` and -``session_id`` to build create python objects to build workspaces with the -freshest data. - -.. _engine: http://docs.sqlalchemy.org/en/rel_0_8/core/engines.html -.. _sqlalchemy: http://www.sqlalchemy.org/ -.. _1.8: http://sourceforge.net/projects/tmux/files/tmux/tmux-1.8/ diff --git a/doc/requirements.pip b/doc/requirements.pip deleted file mode 100644 index 418f231be68..00000000000 --- a/doc/requirements.pip +++ /dev/null @@ -1,5 +0,0 @@ --r ../requirements.pip -docutils==0.11 -sphinx==dev -sphinxcontrib-aafig -reportlab diff --git a/doc/Makefile b/docs/Makefile similarity index 81% rename from doc/Makefile rename to docs/Makefile index 2a3ca6ec833..43f55d20d01 100644 --- a/doc/Makefile +++ b/docs/Makefile @@ -1,9 +1,12 @@ # Makefile for Sphinx documentation # +SHELL := /bin/bash +HTTP_PORT = 8031 +WATCH_FILES= find .. -type f -not -path '*/\.*' | grep -i '.*[.]\(rst\|md\)\$\|.*[.]py\$\|CHANGES\|TODO\|.*conf\.py' 2> /dev/null # You can set these variables from the command line. SPHINXOPTS = -SPHINXBUILD = sphinx-build +SPHINXBUILD = uv run sphinx-build PAPER = BUILDDIR = _build @@ -151,3 +154,36 @@ doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." + +redirects: + $(SPHINXBUILD) -b rediraffewritediff $(ALLSPHINXOPTS) $(BUILDDIR)/redirects + @echo + @echo "Build finished. The redirects are in rediraffe_redirects." + +checkbuild: + rm -rf $(BUILDDIR) + $(SPHINXBUILD) -n -q ./ $(BUILDDIR) + +watch: + if command -v entr > /dev/null; then ${WATCH_FILES} | entr -c $(MAKE) html; else $(MAKE) html; fi + +serve: + @echo '==============================================================' + @echo + @echo 'docs server running at http://localhost:${HTTP_PORT}/' + @echo + @echo '==============================================================' + @$(MAKE) serve_py3 + +serve_py3: + python -m http.server ${HTTP_PORT} --directory _build/html + +dev: + $(MAKE) -j watch serve + +start: + uv run sphinx-autobuild "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) --port ${HTTP_PORT} $(O) + +design: + # This adds additional watch directories (for _static file changes) and disable incremental builds + uv run sphinx-autobuild "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) --port ${HTTP_PORT} --watch "." -a $(O) diff --git a/docs/_ext/__init__.py b/docs/_ext/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/docs/_ext/aafig.py b/docs/_ext/aafig.py new file mode 100644 index 00000000000..d40cb40dfd9 --- /dev/null +++ b/docs/_ext/aafig.py @@ -0,0 +1,241 @@ +"""aafig plugin for sphinx. + +sphinxcontrib.aafig +~~~~~~~~~~~~~~~~~~~ + +Allow embedded ASCII art to be rendered as nice looking images +using the aafigure reStructuredText extension. + +See the README file for details. + +:author: Leandro Lucarella +:license: BOLA, see LICENSE for details +""" + +from __future__ import annotations + +import locale +import logging +import posixpath +import typing as t +from hashlib import sha1 as sha +from os import path + +from docutils import nodes +from docutils.parsers.rst.directives import flag, images, nonnegative_int +from sphinx.errors import SphinxError +from sphinx.util.osutil import ensuredir, relative_uri + +if t.TYPE_CHECKING: + from sphinx.application import Sphinx + + +try: + import aafigure +except ImportError: + aafigure = None + + +logger = logging.getLogger(__name__) + +DEFAULT_FORMATS = {"html": "svg", "latex": "pdf", "text": None} + + +def merge_dict( + dst: dict[str, str | None], + src: dict[str, str | None], +) -> dict[str, str | None]: + for k, v in src.items(): + if k not in dst: + dst[k] = v + return dst + + +def get_basename( + text: str, + options: dict[str, str], + prefix: str | None = "aafig", +) -> str: + options = options.copy() + if "format" in options: + del options["format"] + hashkey = text + str(options) + id_ = sha(hashkey.encode("utf-8")).hexdigest() + return f"{prefix}-{id_}" + + +class AafigError(SphinxError): + category = "aafig error" + + +class AafigDirective(images.Image): + """Directive to insert an ASCII art figure to be rendered by aafigure.""" + + has_content = True + required_arguments = 0 + own_option_spec: t.ClassVar = { + "line_width": float, + "background": str, + "foreground": str, + "fill": str, + "aspect": nonnegative_int, + "textual": flag, + "proportional": flag, + } + option_spec = ( + images.Image.option_spec.copy() if images.Image.option_spec is not None else {} + ) + option_spec.update(own_option_spec) + + def run(self) -> list[nodes.Node]: + aafig_options = {} + own_options_keys = [self.own_option_spec.keys(), "scale"] + for k, v in self.options.items(): + if k in own_options_keys: + # convert flags to booleans + if v is None: + v = True + # convert percentage to float + if k in {"scale", "aspect"}: + v = float(v) / 100.0 + aafig_options[k] = v + del self.options[k] + self.arguments = [""] + (image_node,) = images.Image.run(self) + if isinstance(image_node, nodes.system_message): + return [image_node] + text = "\n".join(self.content) + image_node.aafig = {"options": aafig_options, "text": text} # type: ignore[attr-defined] + return [image_node] + + +def render_aafig_images(app: Sphinx, doctree: nodes.Node) -> None: + format_map = app.builder.config.aafig_format + merge_dict(format_map, DEFAULT_FORMATS) + if aafigure is None: + logger.warning( + "aafigure module not installed, ASCII art images " + "will be rendered as literal text", + ) + for img in doctree.traverse(nodes.image): + if not hasattr(img, "aafig"): + continue + if aafigure is None: + continue + options = img.aafig["options"] + text = img.aafig["text"] + format_ = app.builder.format + merge_dict(options, app.builder.config.aafig_default_options) + if format_ in format_map: + options["format"] = format_map[format_] + else: + logger.warning( + f'unsupported builder format "{format_}", please ' + "add a custom entry in aafig_format config " + "option for this builder", + ) + img.replace_self(nodes.literal_block(text, text)) + continue + if options["format"] is None: + img.replace_self(nodes.literal_block(text, text)) + continue + try: + fname, _outfn, _id, extra = render_aafigure(app, text, options) + except AafigError as exc: + logger.warning("aafigure error: " + str(exc)) + img.replace_self(nodes.literal_block(text, text)) + continue + img["uri"] = fname + # FIXME: find some way to avoid this hack in aafigure + if extra: + (width, height) = (x.split('"')[1] for x in extra.split()) + if "width" not in img: + img["width"] = width + if "height" not in img: + img["height"] = height + + +class AafigureNotInstalled(AafigError): + def __init__(self, *args: object, **kwargs: object) -> None: + return super().__init__("aafigure module not installed", *args, **kwargs) + + +def render_aafigure( + app: Sphinx, + text: str, + options: dict[str, str], +) -> tuple[str, str, str | None, str | None]: + """Render an ASCII art figure into the requested format output file.""" + if aafigure is None: + raise AafigureNotInstalled + + fname = get_basename(text, options) + fname = "{}.{}".format(get_basename(text, options), options["format"]) + if app.builder.format == "html": + # HTML + imgpath = relative_uri(app.builder.env.docname, "_images") + relfn = posixpath.join(imgpath, fname) + outfn = path.join(app.builder.outdir, "_images", fname) + else: + # Non-HTML + if app.builder.format != "latex": + logger.warning( + f"aafig: the builder format {app.builder.format} is not officially " + "supported, aafigure images could not work. " + "Please report problems and working builder to " + "avoid this warning in the future", + ) + relfn = fname + outfn = path.join(app.builder.outdir, fname) + metadata_fname = f"{outfn}.aafig" + + try: + if path.isfile(outfn): + extra = None + if options["format"].lower() == "svg": + f = None + try: + try: + with open( + metadata_fname, + encoding=locale.getpreferredencoding(False), + ) as f: + extra = f.read() + except Exception as e: + raise AafigError from e + finally: + if f is not None: + f.close() + return relfn, outfn, None, extra + except AafigError: + pass + + ensuredir(path.dirname(outfn)) + + try: + (visitor, output) = aafigure.render(text, outfn, options) + output.close() + except aafigure.UnsupportedFormatError as e: + raise AafigError(str(e)) from e + + extra = None + if options["format"].lower() == "svg": + extra = visitor.get_size_attrs() + with open( + metadata_fname, + "w", + encoding=locale.getpreferredencoding(False), + ) as f: + f.write(extra) + + return relfn, outfn, None, extra + + +def setup(app: Sphinx) -> None: + app.add_directive("aafig", AafigDirective) + app.connect("doctree-read", render_aafig_images) + app.add_config_value("aafig_format", DEFAULT_FORMATS, "html") + app.add_config_value("aafig_default_options", {}, "html") + + +# vim: set expandtab shiftwidth=4 softtabstop=4 : diff --git a/docs/_static/css/custom.css b/docs/_static/css/custom.css new file mode 100644 index 00000000000..b420cee9447 --- /dev/null +++ b/docs/_static/css/custom.css @@ -0,0 +1,20 @@ +.sidebar-tree p.indented-block { + padding: var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal) 0 + var(--sidebar-item-spacing-horizontal); + margin-bottom: 0; +} + +.sidebar-tree p.indented-block span.indent { + margin-left: var(--sidebar-item-spacing-horizontal); + display: block; +} + +.sidebar-tree p.indented-block .project-name { + font-size: var(--sidebar-item-font-size); + font-weight: bold; + margin-right: calc(var(--sidebar-item-spacing-horizontal) / 2.5); +} + +.sidebar-tree .active { + font-weight: bold; +} diff --git a/docs/_static/favicon.ico b/docs/_static/favicon.ico new file mode 100644 index 00000000000..a9f8b7195b2 Binary files /dev/null and b/docs/_static/favicon.ico differ diff --git a/docs/_static/img/books/amazon-logo.png b/docs/_static/img/books/amazon-logo.png new file mode 100644 index 00000000000..4f8bc02a82a Binary files /dev/null and b/docs/_static/img/books/amazon-logo.png differ diff --git a/docs/_static/img/books/ibooks-logo.png b/docs/_static/img/books/ibooks-logo.png new file mode 100644 index 00000000000..8d00c53827f Binary files /dev/null and b/docs/_static/img/books/ibooks-logo.png differ diff --git a/docs/_static/img/icons/icon-128x128.png b/docs/_static/img/icons/icon-128x128.png new file mode 100644 index 00000000000..9b2a2ada23d Binary files /dev/null and b/docs/_static/img/icons/icon-128x128.png differ diff --git a/docs/_static/img/icons/icon-144x144.png b/docs/_static/img/icons/icon-144x144.png new file mode 100644 index 00000000000..f1012985eff Binary files /dev/null and b/docs/_static/img/icons/icon-144x144.png differ diff --git a/docs/_static/img/icons/icon-152x152.png b/docs/_static/img/icons/icon-152x152.png new file mode 100644 index 00000000000..be9154a3293 Binary files /dev/null and b/docs/_static/img/icons/icon-152x152.png differ diff --git a/docs/_static/img/icons/icon-192x192.png b/docs/_static/img/icons/icon-192x192.png new file mode 100644 index 00000000000..9f0ed6e500a Binary files /dev/null and b/docs/_static/img/icons/icon-192x192.png differ diff --git a/docs/_static/img/icons/icon-384x384.png b/docs/_static/img/icons/icon-384x384.png new file mode 100644 index 00000000000..b901c6922ee Binary files /dev/null and b/docs/_static/img/icons/icon-384x384.png differ diff --git a/docs/_static/img/icons/icon-512x512.png b/docs/_static/img/icons/icon-512x512.png new file mode 100644 index 00000000000..567e71d5fb4 Binary files /dev/null and b/docs/_static/img/icons/icon-512x512.png differ diff --git a/docs/_static/img/icons/icon-72x72.png b/docs/_static/img/icons/icon-72x72.png new file mode 100644 index 00000000000..df99774366e Binary files /dev/null and b/docs/_static/img/icons/icon-72x72.png differ diff --git a/docs/_static/img/icons/icon-96x96.png b/docs/_static/img/icons/icon-96x96.png new file mode 100644 index 00000000000..f421a709af2 Binary files /dev/null and b/docs/_static/img/icons/icon-96x96.png differ diff --git a/docs/_static/img/tmuxp.svg b/docs/_static/img/tmuxp.svg new file mode 100644 index 00000000000..12bf640f7f2 --- /dev/null +++ b/docs/_static/img/tmuxp.svg @@ -0,0 +1,216 @@ + + + + + tmuxp + + + + + + + + + + + + + + + + + + + Bottom bar shadow object (Shape) + + + Blue Square Group object (Shape) + + Blue Square shadow object (Shape) + + + Blue Square object (Shape) + + + + Yellow Square Group object (Shape) + + Yellow Square Shadow object (Shape) + + + Yellow Square object (Shape) + + + + Bottom bar object (Shape) + + + Light Blue Square Group object (Shape) + + Light Blue Square Shadow 1 object (Shape) + + + Light Blue Square object (Shape) + + + Light Blue Square Shadow 2 object (Shape) + + + + Gear object (Group) + + + + + + tmuxp + + + + diff --git a/doc/_static/tao-tmux-screenshot.png b/docs/_static/tao-tmux-screenshot.png similarity index 100% rename from doc/_static/tao-tmux-screenshot.png rename to docs/_static/tao-tmux-screenshot.png diff --git a/docs/_static/tmuxp-demo.gif b/docs/_static/tmuxp-demo.gif new file mode 100644 index 00000000000..e4fff092585 Binary files /dev/null and b/docs/_static/tmuxp-demo.gif differ diff --git a/doc/_static/tmuxp-dev-screenshot.png b/docs/_static/tmuxp-dev-screenshot.png similarity index 100% rename from doc/_static/tmuxp-dev-screenshot.png rename to docs/_static/tmuxp-dev-screenshot.png diff --git a/docs/_static/tmuxp-shell.gif b/docs/_static/tmuxp-shell.gif new file mode 100644 index 00000000000..26f0d36c034 Binary files /dev/null and b/docs/_static/tmuxp-shell.gif differ diff --git a/docs/_templates/book.html b/docs/_templates/book.html new file mode 100644 index 00000000000..08e43b88b99 --- /dev/null +++ b/docs/_templates/book.html @@ -0,0 +1,7 @@ +

The book!

+ +

+ +

The Tao of tmux is available on Leanpub and Kindle (Amazon).

+

Read and browse the book for free on the web.

+Amazon Kindle diff --git a/docs/_templates/layout.html b/docs/_templates/layout.html new file mode 100644 index 00000000000..2943238cf7b --- /dev/null +++ b/docs/_templates/layout.html @@ -0,0 +1,45 @@ +{% extends "!layout.html" %} +{%- block extrahead %} + {{ super() }} + {%- if theme_show_meta_manifest_tag == true %} + + {% endif -%} + {%- if theme_show_meta_og_tags == true %} + + + + + + + + + + + + + + + + {% endif -%} + {%- if theme_show_meta_app_icon_tags == true %} + + + + + + + + + + + + + + + + + + + + {% endif -%} +{% endblock %} diff --git a/docs/_templates/sidebar/projects.html b/docs/_templates/sidebar/projects.html new file mode 100644 index 00000000000..7b46e0bcea9 --- /dev/null +++ b/docs/_templates/sidebar/projects.html @@ -0,0 +1,69 @@ + + diff --git a/docs/about.md b/docs/about.md new file mode 100644 index 00000000000..71007d950c5 --- /dev/null +++ b/docs/about.md @@ -0,0 +1,104 @@ +```{module} tmuxp + +``` + +(about)= + +# About + +tmuxp helps you manage tmux workspaces. + +Built on an object relational mapper for tmux. tmux users can reload common +workspaces from YAML, JSON and {py:obj}`dict` workspace files like +[tmuxinator] and [teamocil]. + +tmuxp is used by developers for tmux automation at great companies like +[Bugsnag], [Pragmatic Coders] and many others. + +To jump right in, see {ref}`quickstart` and {ref}`examples`. + +Interested in some kung-fu or joining the effort? {ref}`api` and +{ref}`developing`. + +[MIT-licensed]. Code on [github](http://github.com/tmux-python/tmuxp). + +[bugsnag]: https://blog.bugsnag.com/benefits-of-using-tmux/ +[pragmatic coders]: http://pragmaticcoders.com/blog/tmuxp-preconfigured-sessions/ + +## Compared to tmuxinator / teamocil + +### Similarities + +**Load sessions** Loads tmux sessions from config + +**YAML** Supports YAML format + +**Inlining / shorthand configuration** All three support short-hand and +simplified markup for panes that have one command. + +**Maturity and stability** As of 2016, all three are considered stable, +well tested and adopted. + +### Missing + +**Version support** tmuxp only supports `tmux >= 1.8`. Teamocil and +tmuxinator may have support for earlier versions. + +### Differences + +**Programming Language** python. teamocil and tmuxinator use ruby. + +**Workspace building process** teamocil and tmuxinator process configs +directly shell commands. tmuxp processes configuration via ORM layer. + +## Additional Features + +**CLI** tmuxp's CLI can attach and kill sessions with tab-completion +support. See {ref}`commands`. + +**Import config** import configs from Teamocil / Tmuxinator [^id4]. See +{ref}`cli-import`. + +**Session freezing** Supports session freezing into YAML and JSON +format [^id4]. See {ref}`cli-freeze`. + +**JSON config** JSON config support. See {ref}`Examples`. + +**ORM-based API** via [libtmux] - Utilizes tmux >= 1.8's unique ID's for +panes, windows and sessions to create an object relational view of the tmux +{class}`~libtmux.Server`, its {class}`~libtmux.Session`, +{class}`~libtmux.Window`, and {class}`~libtmux.Pane`. +See {ref}`libtmux's internals `. + +**Conversion** `$ tmuxp convert ` can convert files to and +from JSON and YAML. + +[^id4]: On freezing + + While freezing and importing sessions is a great way to save time, + tweaking will probably be required - There is no substitute to a + config made with love. + +[libtmux]: https://libtmux.git-pull.com + +## Minor tweaks + +- Unit tests against live tmux version to test statefulness of tmux + sessions, windows and panes. See {ref}`gh-actions`. +- Load + switch to new session from inside tmux. +- Resume session if config loaded. +- Pre-commands virtualenv / rvm / any other commands. +- Load config from anywhere `$ tmuxp load /full/file/path.json`. +- Load config `.tmuxp.yaml` or `.tmuxp.json` from current working + directory with `$ tmuxp load .`. +- `$ tmuxp -2`, `$ tmuxp -8` for forcing term colors a la + {term}`tmux(1)`. +- `$ tmuxp -L`, `$ tmuxp -S` for sockets and + `$ tmuxp -f ` for config file. + +[attempt at 1.7 test]: https://travis-ci.org/tmux-python/tmuxp/jobs/12348263 +[mit-licensed]: http://opensource.org/licenses/MIT +[tmuxinator]: https://github.com/aziz/tmuxinator +[teamocil]: https://github.com/remiprev/teamocil +[erb]: http://ruby-doc.org/stdlib-2.0.0/libdoc/erb/rdoc/ERB.html +[edit this page]: https://github.com/tmux-python/tmuxp/edit/master/doc/about.rst diff --git a/docs/about_tmux.md b/docs/about_tmux.md new file mode 100644 index 00000000000..e69c2268f98 --- /dev/null +++ b/docs/about_tmux.md @@ -0,0 +1,631 @@ +(about-tmux)= + +# The Tao of tmux + +:::{figure} /\_static/tao-tmux-screenshot.png +:scale: 60% +:align: center + +ISC-licensed terminal multiplexer. + +::: + +tmux is geared for developers and admins who interact regularly with CLI +(text-only interfaces) + +In the world of computers, there are 2 realms: + +1. The text realm +2. The graphical realm + +tmux resides in the text realm. This is about fixed-width fonts and that +old fashioned black terminal. + +tmux is to the console what a desktop is to gui apps. It's a world +inside the text dimension. Inside tmux you can: + +- multitask inside the terminal, run multiple applications. +- have multiple command lines (pane) in the same window +- have multiple windows (window) in the workspace (session) +- switch between multiple workspaces, like virtual desktops + +## Thinking tmux + +### Text-based window manager + +| **tmux** | **"Desktop"-Speak** | **Plain English** | +| ----------- | ------------------------------- | ------------------------------------- | +| Multiplexer | Multi-tasking | Multiple applications simultaneously. | +| Session | Desktop | Applications are visible here | +| Window | Virtual Desktop or applications | A desktop that stores it own screen | +| Pane | Application | Performs operations | + +```{eval-rst} +.. aafig:: + :textual: + + +----------------------------------------------------------------+ + | +--------+--------+ +-----------------+ +-----------------+ | + | | pane | pane | | pane | | pane | | + | | | | | | | | | + | | | | | | | | | + | +--------+--------+ | | +-----------------+ | + | | pane | pane | | | | pane | | + | | | | | | | | | + | | | | | | | | | + | +--------+--------+ +-----------------+ +-----------------+ | + | | window | | window | | window | | + | \--------+--------/ \-----------------/ \-----------------/ | + +----------------------------------------------------------------+ + | session | + \----------------------------------------------------------------/ +``` + +- 1 {term}`Server`. + + - has 1 or more {term}`Session`. + + - has 1 or more {term}`Window`. + + - has 1 or more {term}`Pane`. + +:::{seealso} + +{ref}`glossary` has a dictionary of tmux words. + +::: + +### CLI Power Tool + +Multiple applications or terminals to run on the same screen by +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 | + | | | + | | | + | | | + | | | + | | | + | | | + +--------+--------+ +``` + +```{eval-rst} +.. aafig:: + + +--------+--------+ + | $ 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*' | + +-------------------+ +``` + +You can switch between the windows you create. + +### Resume everything later + +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 | + | | | + | | | + +--------+--------+ +``` + +### Manage workflow + +- System administrators monitor logs and services. +- Programmers like to have an editor open with a CLI nearby. + +Applications running on a remote server can be launched inside of a tmux +session, detached, and reattached next time your ["train of +thought"](http://en.wikipedia.org/wiki/Train_of_thought) and work. + +Multitasking. Preserving the thinking you have. + +## Installing tmux + +tmux is packaged on most Linux and BSD systems. + +For the freshest results on how to get tmux installed on your system, +"How to install tmux on \" will do, as directions change and +are slightly different between distributions. + +This documentation is written for version **1.8**. It's important that +you have the latest stable release of tmux. The latest stable version is +viewable on the [tmux homepage](http://tmux.sourceforge.net/). + +**Mac OS X** users may install the latest stable version of tmux +through [MacPorts](http://www.macports.org/), +[fink](http://fink.thetis.ig42.org/) or [Homebrew](http://www.brew.sh) +(aka brew). + +If **compiling from source**, the dependencies are +[libevent](http://www.monkey.org/~provos/libevent/) and +[ncurses](http://invisible-island.net/ncurses/). + +## Using tmux + +### Start a new session + +```console +$ tmux +``` + +That's all it takes to launch yourself into a tmux session. + +:::{admonition} Common pitfall +:class: note + +Running `$ tmux list-sessions` or any other command for listing tmux +entities (such as `$ tmux list-windows` or `$ tmux list-panes`). +This can generate the error "failed to connect to server". + +This could be because: + +- tmux server has killed its last session, killing the server. +- tmux server has encountered a crash. (tmux is highly stable, + this will rarely happen) +- tmux has not been launched yet at all. + +::: + +(prefix-key)= + +### The prefix key + +Tmux hot keys have to be pressed in a special way. **Read this +carefully**, then try it yourself. + +First, you press the _prefix_ key. This is `C-b` by default. + +Release. Then pause. For less than a second. Then type what's next. + +`C-b o` means: Press `Ctrl` and `b` at the same time. Release, Then +press `o`. + +**Remember, prefix + short cut!** `C` is `Ctrl` key. + +### Session Name + +Sessions can be _named upon creation_. + +```console +$ tmux new-session [-s session-name] +``` + +Sessions can be _renamed after creation_. + +```{eval-rst} +=============== ========================================================= +Command .. code-block:: bash + + $ tmux rename-session + +Short cut ``Prefix`` + ``$`` +=============== ========================================================= +``` + +### Window Name + +Windows can be _named upon creation_. + +```console +$ tmux new-window [-n window-name] +``` + +Windows can be _renamed after creation_. + +```{eval-rst} +=============== ========================================================== +Command .. code-block:: bash + + $ tmux rename-window + +Short cut ``Prefix`` + ``,`` +=============== ========================================================== +``` + +### Creating new windows + +```{eval-rst} +=============== ========================================================= +Command .. code-block:: bash + + $ tmux new-window [-n window-name] + +Short cut ``Prefix`` + ``c`` + + You may then rename window. +=============== ========================================================= +``` + +### Traverse windows + +By number + +```console +$ tmux select-window +``` + +Next + +```console +$ tmux next-window +``` + +Previous + +```console +$ tmux previous-window +``` + +Last-window + +```console +$ tmux last-window +``` + +| Short cut | Action | +| --------- | ----------------------------------------------------------- | +| `n` | Change to the next window. | +| `p` | Change to the previous window. | +| `w` | Choose the current window interactively. | +| `0 to 9` | Select windows 0 to 9. | +| `M-n` | Move to the next window with a bell or activity marker. | +| `M-p` | Move to the previous window with a bell or activity marker. | + +### Move windows + +Move window + +```console +$ tmux move-window [-t dst-window] +``` + +Swap the window + +```console +$ tmux swap-window [-t dst-window] +``` + +| Short cut | Action | +| --------- | ----------------------------------------------- | +| `.` | Prompt for an index to move the current window. | + +### Move panes + +```console +$ tmux move-pane [-t dst-pane] +``` + +| Short cut | Action | +| --------- | ------------------------------------------------ | +| `C-o` | Rotate the panes in the current window forwards. | +| `{` | Swap the current pane with the previous pane. | +| `}` | Swap the current pane with the next pane. | + +### Traverse panes + +Shortcut to move between panes. + +```console +$ tmux last-window +``` + +```console +$ tmux next-window +``` + +| Short cut | Action | +| ------------- | --------------------------------------------------- | +| `Up, Down` | Change to the pane above, below, to the left, or to | +| `Left, Right` | the right of the current pane. | + +Recipe: tmux conf to `hjkl` commands, add this to your `~/.tmux.conf`: + + # hjkl pane traversal + bind h select-pane -L + bind j select-pane -D + bind k select-pane -U + bind l select-pane -R + +### Kill window + +```console +$ tmux kill-window [-t target-window] +``` + +| Short cut | Action | +| --------- | ------------------------ | +| `&` | Kill the current window. | + +### Kill pane + +```console +$ tmux kill-pane [-t target-pane] +``` + +| Short cut | Action | +| --------- | ---------------------- | +| `x` | Kill the current pane. | + +### Splitting windows into panes + +```console +$ tmux split-window [-c start-directory] +``` + +Tmux windows can be split into multiple panes. + +| Short cut | Action | +| --------- | ------------------------------------------------ | +| `%` | Split the current pane into two, left and right. | +| `"` | Split the current pane into two, top and bottom. | + +## Configuring tmux + +Tmux can be configured via a `tmux(1)` configuration at `~/.tmux.conf`. + +Depending on your tmux version, there is different options available. + +### Vi-style copy and paste + +```console +# Vi copypaste mode +set-window-option -g mode-keys vi +bind-key -t vi-copy 'v' begin-selection +bind-key -t vi-copy 'y' copy-selection +``` + +### Aggressive resizing for clients + +```console +setw -g aggressive-resize on +``` + +### Reload config + +`` + `r`. + +```console +bind r source-file ~/.tmux.conf \; display-message "Config reloaded." +``` + +### Status lines + +Tmux allows configuring a status line that displays system information, +window list, and even pipe in the `stdout` of an application. + +You can use [tmux-mem-cpu-load][tmux-mem-cpu-load] to get stats (requires compilation) and +[basic-cpu-and-memory.tmux][basic-cpu-and-memory.tmux]. You can pipe in a bash command to a tmux +status line like: + +```console +$(shell-command) +``` + +So if `/usr/local/bin/tmux-mem-cpu-load` outputs stats to `stdout`, then +`$(tmux-mem-cpu-load)` is going to output the first line to the status +line. The interval is determined by the `status-interval`: + + set -g status-interval 1 + +[tmux-mem-cpu-load]: https://github.com/thewtex/tmux-mem-cpu-load +[basic-cpu-and-memory.tmux]: https://github.com/zaiste/tmuxified/blob/master/scripts/basic-cpu-and-memory.tmux + +### Examples + +- - works with tmux 1.5+. + Supports screen's `ctrl-a` `Prefix key`. Support for system cpu, + memory, uptime stats. +- Add yours, edit this page on github. + +## Reference + +### Short cuts + +:::{tip} + +{ref}`prefix-key` is pressed before a short cut! + +::: + +| Short cut | Action | +| -------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | +| `C-b` | Send the prefix key (C-b) through to the application. | +| `C-o` | Rotate the panes in the current window forwards. | +| `C-z` | Suspend the tmux client. | +| `!` | Break the current pane out of the window. | +| `"` | Split the current pane into two, top and bottom. | +| `#` | List all paste buffers. | +| `$` | Rename the current session. | +| `%` | Split the current pane into two, left and right. | +| `&` | Kill the current window. | +| `'` | Prompt for a window index to select. | +| `,` | Rename the current window. | +| `-` | Delete the most recently copied buffer of text. | +| `.` | Prompt for an index to move the current window. | +| `0 to 9` | Select windows 0 to 9. | +| `:` | Enter the tmux command prompt. | +| `;` | Move to the previously active pane. | +| `=` | Choose which buffer to paste interactively from a list. | +| `?` | List all key bindings. | +| `D` | Choose a client to detach. | +| `[` | Enter copy mode to copy text or view the history. | +| `]` | Paste the most recently copied buffer of text. | +| `c` | Create a new window. | +| `d` | Detach the current client. | +| `f` | Prompt to search for text in open windows. | +| `i` | Display some information about the current window. | +| `l` | Move to the previously selected window. | +| `n` | Change to the next window. | +| `o` | Select the next pane in the current window. | +| `p` | Change to the previous window. | +| `q` | Briefly display pane indexes. | +| `r` | Force redraw of the attached client. | +| `s` | Select a new session for the attached client interactively. | +| `L` | Switch the attached client back to the last session. | +| `t` | Show the time. | +| `w` | Choose the current window interactively. | +| `x` | Kill the current pane. | +| `{` | Swap the current pane with the previous pane. | +| `}` | Swap the current pane with the next pane. | +| `~` | Show previous messages from tmux, if any. | +| `Page Up` | Enter copy mode and scroll one page up. | +| `Up, Down` | Change to the pane above, below, to the left, or to | +| `Left, Right` | the right of the current pane. | +| `M-1 to M-5` | Arrange panes in one of the five preset layouts: even-horizontal, even-vertical, main-horizontal, main-vertical, or tiled. | +| `M-n` | Move to the next window with a bell or activity marker. | +| `M-o` | Rotate the panes in the current window backwards. | +| `M-p` | Move to the previous window with a bell or activity marker. | +| `C-Up, C-Down` `C-Left, C-Right` | Resize the current pane in steps of one cell. | +| `M-Up, M-Down` `M-Left, M-Right` | Resize the current pane in steps of five cells. | + +Source: tmux manpage[1]. + +To get the text documentation of a `.1` manual file: + +```console +$ nroff -mdoc tmux.1|less +``` + +For more information on how to export and differentiate tmux between versions, see https://github.com/tmux-python/tmux-manuals. + +[^id2]: + +[creative commons by-nc-nd 3.0 us]: http://creativecommons.org/licenses/by-nc-nd/3.0/us/ + +### The Book + +:::::::{container} book-container + +::::{container} leftside-book + +:::{figure} https://s3.amazonaws.com/titlepages.leanpub.com/the-tao-of-tmux/large +:scale: 100% +:width: 301 +:height: 390 +:align: left +:target: https://leanpub.com/the-tao-of-tmux +:alt: The Tao of tmux + +::: + +:::: + +::::{container} rightside-book + +_The Tao of tmux_ is available on [Leanpub][leanpub] and [Kindle][kindle] (Amazon). + +:::{figure} \_static/img/books/amazon-logo.png +:scale: 19% +:target: http://amzn.to/2gPfRhC +:alt: Amazon Kindle + +::: + +Read and browse the book for [free on the web][free on the web]. + +:::: + +[free on the web]: https://leanpub.com/the-tao-of-tmux/read +[leanpub]: https://leanpub.com/the-tao-of-tmux +[kindle]: http://amzn.to/2gPfRhC + +::::::: + +### License + +This page is licensed [Creative Commons BY-NC-ND 3.0 US][creative commons by-nc-nd 3.0 us]. diff --git a/docs/api/cli/convert.md b/docs/api/cli/convert.md new file mode 100644 index 00000000000..4c9b87f34f6 --- /dev/null +++ b/docs/api/cli/convert.md @@ -0,0 +1,8 @@ +# tmuxp convert - `tmuxp.cli.convert` + +```{eval-rst} +.. automodule:: tmuxp.cli.convert + :members: + :show-inheritance: + :undoc-members: +``` diff --git a/docs/api/cli/debug_info.md b/docs/api/cli/debug_info.md new file mode 100644 index 00000000000..56a50052e66 --- /dev/null +++ b/docs/api/cli/debug_info.md @@ -0,0 +1,8 @@ +# tmuxp debug-info - `tmuxp.cli.debug_info` + +```{eval-rst} +.. automodule:: tmuxp.cli.debug_info + :members: + :show-inheritance: + :undoc-members: +``` diff --git a/docs/api/cli/edit.md b/docs/api/cli/edit.md new file mode 100644 index 00000000000..704e8144c3b --- /dev/null +++ b/docs/api/cli/edit.md @@ -0,0 +1,8 @@ +# tmuxp edit - `tmuxp.cli.edit` + +```{eval-rst} +.. automodule:: tmuxp.cli.edit + :members: + :show-inheritance: + :undoc-members: +``` diff --git a/docs/api/cli/freeze.md b/docs/api/cli/freeze.md new file mode 100644 index 00000000000..70c4d068454 --- /dev/null +++ b/docs/api/cli/freeze.md @@ -0,0 +1,8 @@ +# tmuxp freeze - `tmuxp.cli.freeze` + +```{eval-rst} +.. automodule:: tmuxp.cli.freeze + :members: + :show-inheritance: + :undoc-members: +``` diff --git a/docs/api/cli/import_config.md b/docs/api/cli/import_config.md new file mode 100644 index 00000000000..3c75e88eadf --- /dev/null +++ b/docs/api/cli/import_config.md @@ -0,0 +1,8 @@ +# tmuxp import - `tmuxp.cli.import_config` + +```{eval-rst} +.. automodule:: tmuxp.cli.import_config + :members: + :show-inheritance: + :undoc-members: +``` diff --git a/docs/api/cli/index.md b/docs/api/cli/index.md new file mode 100644 index 00000000000..08f49a2131e --- /dev/null +++ b/docs/api/cli/index.md @@ -0,0 +1,30 @@ +(api_cli)= + +# CLI + +:::{warning} +Be careful with these! Internal APIs are **not** covered by version policies. They can break or be removed between minor versions! + +If you need an internal API stabilized please [file an issue](https://github.com/tmux-python/tmuxp/issues). +::: + +```{toctree} +convert +debug_info +edit +freeze +import_config +load +ls +shell +utils +``` + +## `tmuxp.cli` + +```{eval-rst} +.. automodule:: tmuxp.cli + :members: + :show-inheritance: + :undoc-members: +``` diff --git a/docs/api/cli/load.md b/docs/api/cli/load.md new file mode 100644 index 00000000000..bae0f6a899f --- /dev/null +++ b/docs/api/cli/load.md @@ -0,0 +1,8 @@ +# tmuxp load - `tmuxp.cli.load` + +```{eval-rst} +.. automodule:: tmuxp.cli.load + :members: + :show-inheritance: + :undoc-members: +``` diff --git a/docs/api/cli/ls.md b/docs/api/cli/ls.md new file mode 100644 index 00000000000..34d27718a64 --- /dev/null +++ b/docs/api/cli/ls.md @@ -0,0 +1,8 @@ +# tmuxp ls - `tmuxp.cli.ls` + +```{eval-rst} +.. automodule:: tmuxp.cli.ls + :members: + :show-inheritance: + :undoc-members: +``` diff --git a/docs/api/cli/shell.md b/docs/api/cli/shell.md new file mode 100644 index 00000000000..c6f05d08749 --- /dev/null +++ b/docs/api/cli/shell.md @@ -0,0 +1,8 @@ +# tmuxp shell - `tmuxp.cli.shell` + +```{eval-rst} +.. automodule:: tmuxp.cli.shell + :members: + :show-inheritance: + :undoc-members: +``` diff --git a/docs/api/cli/utils.md b/docs/api/cli/utils.md new file mode 100644 index 00000000000..4556dcf5342 --- /dev/null +++ b/docs/api/cli/utils.md @@ -0,0 +1,8 @@ +# CLI utilities - `tmuxp.cli.utils` + +```{eval-rst} +.. automodule:: tmuxp.cli.utils + :members: + :show-inheritance: + :undoc-members: +``` diff --git a/docs/api/exc.md b/docs/api/exc.md new file mode 100644 index 00000000000..70b68a24666 --- /dev/null +++ b/docs/api/exc.md @@ -0,0 +1,8 @@ +# Exceptions - `tmuxp.exc` + +```{eval-rst} +.. automodule:: tmuxp.exc + :members: + :show-inheritance: + :undoc-members: +``` diff --git a/docs/api/index.md b/docs/api/index.md new file mode 100644 index 00000000000..89ec3d508b7 --- /dev/null +++ b/docs/api/index.md @@ -0,0 +1,20 @@ +(api)= + +# API Reference + +:::{seealso} +See {ref}`libtmux's API ` and {ref}`Quickstart ` to see how you can control +tmux via python API calls. +::: + +```{toctree} +internals/index +cli/index +workspace/index +exc +log +plugin +shell +util +types +``` diff --git a/docs/api/internals/config_reader.md b/docs/api/internals/config_reader.md new file mode 100644 index 00000000000..fa932f8643a --- /dev/null +++ b/docs/api/internals/config_reader.md @@ -0,0 +1,14 @@ +# Config reader - `tmuxp._internal.config_reader` + +:::{warning} +Be careful with these! Internal APIs are **not** covered by version policies. They can break or be removed between minor versions! + +If you need an internal API stabilized please [file an issue](https://github.com/tmux-python/tmuxp/issues). +::: + +```{eval-rst} +.. automodule:: tmuxp._internal.config_reader + :members: + :show-inheritance: + :undoc-members: +``` diff --git a/docs/api/internals/index.md b/docs/api/internals/index.md new file mode 100644 index 00000000000..74b5fa04816 --- /dev/null +++ b/docs/api/internals/index.md @@ -0,0 +1,14 @@ +(internals)= + +# Internals + +:::{warning} +Be careful with these! Internal APIs are **not** covered by version policies. They can break or be removed between minor versions! + +If you need an internal API stabilized please [file an issue](https://github.com/tmux-python/tmuxp/issues). +::: + +```{toctree} +config_reader +types +``` diff --git a/docs/api/internals/types.md b/docs/api/internals/types.md new file mode 100644 index 00000000000..f41f12b6d10 --- /dev/null +++ b/docs/api/internals/types.md @@ -0,0 +1,8 @@ +# Typings - `tmuxp._internal.types` + +```{eval-rst} +.. automodule:: tmuxp._internal.types + :members: + :show-inheritance: + :undoc-members: +``` diff --git a/docs/api/log.md b/docs/api/log.md new file mode 100644 index 00000000000..2e000328c4d --- /dev/null +++ b/docs/api/log.md @@ -0,0 +1,8 @@ +# Logging - `tmuxp.log` + +```{eval-rst} +.. automodule:: tmuxp.log + :members: + :show-inheritance: + :undoc-members: +``` diff --git a/docs/api/plugin.md b/docs/api/plugin.md new file mode 100644 index 00000000000..fd7ce9781c7 --- /dev/null +++ b/docs/api/plugin.md @@ -0,0 +1,8 @@ +# Plugin - `tmuxp.plugin` + +```{eval-rst} +.. automodule:: tmuxp.plugin + :members: + :show-inheritance: + :undoc-members: +``` diff --git a/docs/api/shell.md b/docs/api/shell.md new file mode 100644 index 00000000000..286974a67e0 --- /dev/null +++ b/docs/api/shell.md @@ -0,0 +1,8 @@ +# Shell - `tmuxp.shell` + +```{eval-rst} +.. automodule:: tmuxp.shell + :members: + :show-inheritance: + :undoc-members: +``` diff --git a/docs/api/types.md b/docs/api/types.md new file mode 100644 index 00000000000..adac7ee4d92 --- /dev/null +++ b/docs/api/types.md @@ -0,0 +1,8 @@ +# Typings - `tmuxp.types` + +```{eval-rst} +.. automodule:: tmuxp.types + :members: + :show-inheritance: + :undoc-members: +``` diff --git a/docs/api/util.md b/docs/api/util.md new file mode 100644 index 00000000000..80e067e169d --- /dev/null +++ b/docs/api/util.md @@ -0,0 +1,8 @@ +# Utilities - `tmuxp.util` + +```{eval-rst} +.. automodule:: tmuxp.util + :members: + :show-inheritance: + :undoc-members: +``` diff --git a/docs/api/workspace/builder.md b/docs/api/workspace/builder.md new file mode 100644 index 00000000000..24525e1efbc --- /dev/null +++ b/docs/api/workspace/builder.md @@ -0,0 +1,8 @@ +# Builder - `tmuxp.workspace.builder` + +```{eval-rst} +.. automodule:: tmuxp.workspace.builder + :members: + :show-inheritance: + :undoc-members: +``` diff --git a/docs/api/workspace/constants.md b/docs/api/workspace/constants.md new file mode 100644 index 00000000000..df8cf583f21 --- /dev/null +++ b/docs/api/workspace/constants.md @@ -0,0 +1,8 @@ +# Constants - `tmuxp.workspace.constants` + +```{eval-rst} +.. automodule:: tmuxp.workspace.constants + :members: + :show-inheritance: + :undoc-members: +``` diff --git a/docs/api/workspace/finders.md b/docs/api/workspace/finders.md new file mode 100644 index 00000000000..d48318c361e --- /dev/null +++ b/docs/api/workspace/finders.md @@ -0,0 +1,8 @@ +# Finders - `tmuxp.workspace.finders` + +```{eval-rst} +.. automodule:: tmuxp.workspace.finders + :members: + :show-inheritance: + :undoc-members: +``` diff --git a/docs/api/workspace/freezer.md b/docs/api/workspace/freezer.md new file mode 100644 index 00000000000..ec9afd873ac --- /dev/null +++ b/docs/api/workspace/freezer.md @@ -0,0 +1,8 @@ +# Freezer - `tmuxp.workspace.freezer` + +```{eval-rst} +.. automodule:: tmuxp.workspace.freezer + :members: + :show-inheritance: + :undoc-members: +``` diff --git a/docs/api/workspace/importers.md b/docs/api/workspace/importers.md new file mode 100644 index 00000000000..b3b9cd00db2 --- /dev/null +++ b/docs/api/workspace/importers.md @@ -0,0 +1,8 @@ +# Importers - `tmuxp.workspace.importers` + +```{eval-rst} +.. automodule:: tmuxp.workspace.importers + :members: + :show-inheritance: + :undoc-members: +``` diff --git a/docs/api/workspace/index.md b/docs/api/workspace/index.md new file mode 100644 index 00000000000..4c2d6241add --- /dev/null +++ b/docs/api/workspace/index.md @@ -0,0 +1,19 @@ +(workspace)= + +# Workspace + +:::{warning} +Be careful with these! Internal APIs are **not** covered by version policies. They can break or be removed between minor versions! + +If you need an internal API stabilized please [file an issue](https://github.com/tmux-python/tmuxp/issues). +::: + +```{toctree} +builder +constants +finders +freezer +importers +loader +validation +``` diff --git a/docs/api/workspace/loader.md b/docs/api/workspace/loader.md new file mode 100644 index 00000000000..f422644ab04 --- /dev/null +++ b/docs/api/workspace/loader.md @@ -0,0 +1,8 @@ +# Loader - `tmuxp.workspace.loader` + +```{eval-rst} +.. automodule:: tmuxp.workspace.loader + :members: + :show-inheritance: + :undoc-members: +``` diff --git a/docs/api/workspace/validation.md b/docs/api/workspace/validation.md new file mode 100644 index 00000000000..77f7655e801 --- /dev/null +++ b/docs/api/workspace/validation.md @@ -0,0 +1,8 @@ +# Validation - `tmuxp.workspace.validation` + +```{eval-rst} +.. automodule:: tmuxp.workspace.validation + :members: + :show-inheritance: + :undoc-members: +``` diff --git a/docs/cli/completion.md b/docs/cli/completion.md new file mode 100644 index 00000000000..1de10869a86 --- /dev/null +++ b/docs/cli/completion.md @@ -0,0 +1,78 @@ +(completion)= + +(completions)= + +(cli-completions)= + +# Completions + +## tmuxp 1.17+ (experimental) + +```{note} +See the [shtab library's documentation on shell completion](https://docs.iterative.ai/shtab/use/#cli-usage) for the most up to date way of connecting completion for tmuxp. +``` + +Provisional support for completions in tmuxp 1.17+ are powered by [shtab](https://docs.iterative.ai/shtab/). This must be **installed separately**, as it's **not currently bundled with tmuxp**. + +```console +$ pip install shtab --user +``` + +:::{tab} bash + +```bash +shtab --shell=bash -u tmuxp.cli.create_parser \ + | sudo tee "$BASH_COMPLETION_COMPAT_DIR"/TMUXP +``` + +::: + +:::{tab} zsh + +```zsh +shtab --shell=zsh -u tmuxp.cli.create_parser \ + | sudo tee /usr/local/share/zsh/site-functions/_TMUXP +``` + +::: + +:::{tab} tcsh + +```zsh +shtab --shell=tcsh -u tmuxp.cli.create_parser \ + | sudo tee /etc/profile.d/TMUXP.completion.csh +``` + +::: + +## tmuxp 1.1 to 1.16 + +```{note} +See the [click library's documentation on shell completion](https://click.palletsprojects.com/en/8.0.x/shell-completion/) for the most up to date way of connecting completion for tmuxp. +``` + +tmuxp 1.1 to 1.16 use [click](https://click.palletsprojects.com)'s completion: + +:::{tab} Bash + +_~/.bashrc_: + +```bash + +eval "$(_TMUXP_COMPLETE=bash_source tmuxp)" + +``` + +::: + +:::{tab} Zsh + +_~/.zshrc_: + +```zsh + +eval "$(_TMUXP_COMPLETE=zsh_source tmuxp)" + +``` + +::: diff --git a/docs/cli/convert.md b/docs/cli/convert.md new file mode 100644 index 00000000000..082f82abd65 --- /dev/null +++ b/docs/cli/convert.md @@ -0,0 +1,34 @@ +(cli-convert)= + +# tmuxp convert + +Convert between YAML and JSON + +```{eval-rst} +.. argparse:: + :module: tmuxp.cli + :func: create_parser + :prog: tmuxp + :path: convert +``` + +## Usage + +````{tab} YAML -> JSON + +```console +$ tmuxp convert /path/to/file.yaml +``` + +```` + +````{tab} JSON -> YAML + +```console +$ tmuxp convert /path/to/file.json +``` + +```` + +tmuxp automatically will prompt to convert `.yaml` to `.json` and +`.json` to `.yaml`. diff --git a/docs/cli/debug-info.md b/docs/cli/debug-info.md new file mode 100644 index 00000000000..5bb4fd4b62b --- /dev/null +++ b/docs/cli/debug-info.md @@ -0,0 +1,29 @@ +(cli-debug-info)= + +(tmuxp-debug-info)= + +# tmuxp debug-info + +Use to collect all relevant information for submitting an issue to +the project. + +```{eval-rst} +.. argparse:: + :module: tmuxp.cli + :func: create_parser + :prog: tmuxp + :path: debug-info +``` + +## Usage + +```console + +$ tmuxp debug-info +-------------------------- +environment: + system: Linux + arch: x86_64 +... + +``` diff --git a/docs/cli/edit.md b/docs/cli/edit.md new file mode 100644 index 00000000000..323426931b8 --- /dev/null +++ b/docs/cli/edit.md @@ -0,0 +1,13 @@ +(edit-config)= + +(cli-edit)= + +# tmuxp edit + +```{eval-rst} +.. argparse:: + :module: tmuxp.cli + :func: create_parser + :prog: tmuxp + :path: edit +``` diff --git a/docs/cli/freeze.md b/docs/cli/freeze.md new file mode 100644 index 00000000000..fa4e2ad7593 --- /dev/null +++ b/docs/cli/freeze.md @@ -0,0 +1,37 @@ +(cli-freeze)= + +(cli-freeze-reference)= + +# tmuxp freeze + +```{eval-rst} +.. argparse:: + :module: tmuxp.cli + :func: create_parser + :prog: tmuxp + :path: freeze +``` + +## Usage + +Freeze sessions + +```console +$ tmuxp freeze +``` + +```console +$ tmuxp freeze [session_name] +``` + +```console +$ tmuxp freeze --force [session_name] +``` + +You can save the state of your tmux session by freezing it. + +Tmuxp will offer to save your session state to `.json` or `.yaml`. + +If no session is specified, it will default to the attached session. + +If the `--force` argument is passed, it will overwrite any existing workspace file with the same name. diff --git a/docs/cli/import.md b/docs/cli/import.md new file mode 100644 index 00000000000..c90ad4cd4e7 --- /dev/null +++ b/docs/cli/import.md @@ -0,0 +1,59 @@ +(cli-import)= + +# tmuxp import + +(import-teamocil)= + +## From teamocil + +```{eval-rst} +.. argparse:: + :module: tmuxp.cli + :func: create_parser + :prog: tmuxp + :path: import teamocil +``` + +````{tab} YAML + +```console +$ tmuxp import teamocil /path/to/file.yaml +``` + +```` + +````{tab} JSON + +```console +$ tmuxp import teamocil /path/to/file.json +``` + +```` + +(import-tmuxinator)= + +## From tmuxinator + +```{eval-rst} +.. argparse:: + :module: tmuxp.cli + :func: create_parser + :prog: tmuxp + :path: import tmuxinator +``` + +````{tab} YAML + +```console +$ tmuxp import tmuxinator /path/to/file.yaml +``` + +```` + +````{tab} JSON + +```console +$ tmuxp import tmuxinator /path/to/file.json +``` + +```` diff --git a/docs/cli/index.md b/docs/cli/index.md new file mode 100644 index 00000000000..3205cccc3b3 --- /dev/null +++ b/docs/cli/index.md @@ -0,0 +1,55 @@ +(cli)= + +(commands)= + +# Commands + +```{toctree} +:caption: General commands +:maxdepth: 1 + +load +shell +ls +``` + +```{toctree} +:caption: Configuration +:maxdepth: 1 + +edit +import +convert +freeze +``` + +```{toctree} +:caption: Diagnostic +:maxdepth: 1 + +debug-info +``` + +```{toctree} +:caption: Completion +:maxdepth: 1 + +completion +``` + +(cli-main)= + +(tmuxp-main)= + +## Command: `tmuxp` + +```{eval-rst} +.. argparse:: + :module: tmuxp.cli + :func: create_parser + :prog: tmuxp + :nosubcommands: + + subparser_name : @replace + See :ref:`cli-ls` +``` diff --git a/docs/cli/load.md b/docs/cli/load.md new file mode 100644 index 00000000000..d1d515ab8c7 --- /dev/null +++ b/docs/cli/load.md @@ -0,0 +1,150 @@ +(cli-load)= + +(tmuxp-load)= + +(tmuxp-load-reference)= + +# tmuxp load + +```{eval-rst} +.. argparse:: + :module: tmuxp.cli + :func: create_parser + :prog: tmuxp + :path: load +``` + +## Usage + +You can load your tmuxp file and attach the tmux session via a few +shorthands: + +1. The directory with a `.tmuxp.{yaml,yml,json}` file in it +2. The name of the project file in your `$HOME/.tmuxp` folder +3. The direct path of the tmuxp file you want to load + +Path to folder with `.tmuxp.yaml`, `.tmuxp.yml`, `.tmuxp.json`: + +````{tab} Project based + +Projects with a file named `.tmuxp.yaml` or `.tmuxp.json` can be loaded: + +```console +// current directory +$ tmuxp load . +``` + +```console +$ tmuxp load ../ +``` + +```console +$ tmuxp load path/to/folder/ +``` + +```console +$ tmuxp load /path/to/folder/ +``` + +```` + +````{tab} User based + +Name of the config, assume `$HOME/.tmuxp/myconfig.yaml`: + +```console +$ tmuxp load myconfig +``` + +Direct path to json/yaml file: + +```console +$ tmuxp load ./myfile.yaml +``` + +```console +$ tmuxp load /abs/path/to/myfile.yaml +``` + +```console +$ tmuxp load ~/myfile.yaml +``` + +```` + +````{tab} Direct + +Absolute and relative directory paths are supported. + +```console +$ 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. + +``` +Already inside TMUX, switch to session? yes/no +Or (a)ppend windows in the current active session? +[y/n/a]: +``` + +## Options + +All of these options can be preselected to skip the prompt: + +- Attach / open the client after load: + + ```console + $ tmuxp load -y config + ``` + +- Detached / open in background: + + ```console + $ tmuxp load -d config + ``` + +- Append windows to existing session + + ```console + $ tmuxp load -a config + ``` + +## Loading multiple sessions + +Multiple sessions can be loaded at once. The first ones will be created +without being attached. The last one will be attached if there is no +`-d` flag on the command line. + +```console +$ tmuxp load [filename1] [filename2] ... +``` + +## Custom session name + +A session name can be provided at the terminal. If multiple sessions +are created, the last session is named from the terminal. + +```console +$ tmuxp load -s [new_session_name] [filename1] ... +``` + +## Logging + +The output of the `load` command can be logged to a file for +debugging purposes. the log level can be controlled with the global +`--log-level` option (defaults to INFO). + +```console +$ tmuxp load [filename] --log-file [log_filename] +``` + +```console +$ tmuxp --log-level [LEVEL] load [filename] --log-file [log_filename] +``` diff --git a/docs/cli/ls.md b/docs/cli/ls.md new file mode 100644 index 00000000000..4e5ec75fee3 --- /dev/null +++ b/docs/cli/ls.md @@ -0,0 +1,15 @@ +(cli-ls)= + +(ls-config)= + +# tmuxp ls + +List sessions. + +```{eval-rst} +.. argparse:: + :module: tmuxp.cli + :func: create_parser + :prog: tmuxp + :path: ls +``` diff --git a/docs/cli/shell.md b/docs/cli/shell.md new file mode 100644 index 00000000000..58c2d7ebb09 --- /dev/null +++ b/docs/cli/shell.md @@ -0,0 +1,113 @@ +(cli-shell)= + +(tmuxp-shell)= + +# tmuxp shell + +```{eval-rst} +.. argparse:: + :module: tmuxp.cli + :func: create_parser + :prog: tmuxp + :path: shell +``` + +## Directly enter commands + +```console +$ tmuxp shell -c 'python code' +``` + +```{image} ../_static/tmuxp-shell.gif +:width: 100% +``` + +## Guide + +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: + +```console +(Pdb) server + +(Pdb) server.sessions +[Session($1 your_project)] +(Pdb) session +Session($1 your_project) +(Pdb) session.name +'your_project' +(Pdb) window +Window(@3 1:your_window, Session($1 your_project)) +(Pdb) window.name +'your_window' +(Pdb) window.panes +[Pane(%6 Window(@3 1:your_window, Session($1 your_project))) +(Pdb) pane +Pane(%6 Window(@3 1:your_window, Session($1 your_project))) +``` + +Supports [PEP 553][pep 553]'s `PYTHONBREAKPOINT` and +compatible debuggers, for instance [ipdb][ipdb]: + +```console +$ pip install --user ipdb +``` + +```console +$ env PYTHONBREAKPOINT=ipdb.set_trace tmuxp shell +``` + +You can also pass in python code directly, similar to `python -c`, do +this via `tmuxp -c`: + +```console +$ tmuxp shell -c 'print(session.name); print(window.name)' +my_server +my_window +``` + +```console +$ tmuxp shell my_server -c 'print(session.name); print(window.name)' +my_server +my_window +``` + +```console +$ tmuxp shell my_server my_window -c 'print(session.name); print(window.name)' +my_server +my_window +``` + +```console +$ tmuxp shell my_server my_window -c 'print(window.name.upper())' +MY_WINDOW +``` + +Assuming inside a tmux pane or one is attached on default server: + +```console +$ tmuxp shell -c 'print(pane.id); print(pane.window.name)' +%2 +my_window +``` + +[pep 553]: https://www.python.org/dev/peps/pep-0553/ +[ipdb]: https://pypi.org/project/ipdb/ +[libtmux]: https://libtmux.git-pull.com + +## Shell detection + +`tmuxp shell` detects the richest shell available in your _site packages_, you can also pick your shell via args: + +- `--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` diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 00000000000..81c72be3457 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,228 @@ +# flake8: NOQA: E501 +"""Sphinx documentation configuration for tmuxp.""" + +from __future__ import annotations + +import contextlib +import inspect +import pathlib +import sys +import typing as t +from os.path import relpath + +import tmuxp + +if t.TYPE_CHECKING: + from sphinx.application import Sphinx + +# Get the project root dir, which is the parent dir of this +cwd = pathlib.Path(__file__).parent +project_root = cwd.parent +src_root = project_root / "src" + +sys.path.insert(0, str(src_root)) +sys.path.insert(0, str(cwd / "_ext")) + +# package data +about: dict[str, str] = {} +with (src_root / "tmuxp" / "__about__.py").open() as fp: + exec(fp.read(), about) + +extensions = [ + "sphinx.ext.autodoc", + "sphinx.ext.intersphinx", + "sphinx_autodoc_typehints", + "sphinx.ext.todo", + "sphinx.ext.napoleon", + "sphinx.ext.linkcode", + "aafig", + "sphinxarg.ext", # sphinx-argparse + "sphinx_inline_tabs", + "sphinx_copybutton", + "sphinxext.opengraph", + "sphinxext.rediraffe", + "myst_parser", + "linkify_issues", +] + +myst_enable_extensions = [ + "colon_fence", + "substitution", + "replacements", + "strikethrough", + "linkify", +] + +templates_path = ["_templates"] + +source_suffix = {".rst": "restructuredtext", ".md": "markdown"} + +master_doc = "index" + +project = about["__title__"] +project_copyright = about["__copyright__"] + +version = "{}".format(".".join(about["__version__"].split("."))[:2]) +release = "{}".format(about["__version__"]) + +exclude_patterns = ["_build"] + +pygments_style = "monokai" +pygments_dark_style = "monokai" + +html_css_files = ["css/custom.css"] +html_extra_path = ["manifest.json"] +html_static_path = ["_static"] +html_favicon = "_static/favicon.ico" +html_theme = "furo" +html_theme_path: list[str] = [] +html_theme_options: dict[str, str | list[dict[str, str]]] = { + "light_logo": "img/tmuxp.svg", + "dark_logo": "img/tmuxp.svg", + "footer_icons": [ + { + "name": "GitHub", + "url": about["__github__"], + "html": """ + + + + """, + "class": "", + }, + ], + "source_repository": f"{about['__github__']}/", + "source_branch": "master", + "source_directory": "docs/", +} +html_sidebars = { + "**": [ + "sidebar/scroll-start.html", + "sidebar/brand.html", + "sidebar/search.html", + "sidebar/navigation.html", + "sidebar/projects.html", + "sidebar/scroll-end.html", + ], +} + +# linkify_issues +issue_url_tpl = about["__github__"] + "/issues/{issue_id}" + +# sphinx.ext.autodoc +toc_object_entries_show_parents = "hide" +autodoc_default_options = { + "undoc-members": True, + "members": True, + "private-members": True, + "show-inheritance": True, + "member-order": "bysource", +} + +# sphinxext.opengraph +ogp_site_url = about["__docs__"] +ogp_image = "_static/img/icons/icon-192x192.png" +ogp_site_name = about["__title__"] + +# sphinx-copybutton +copybutton_prompt_text = ( + r">>> |\.\.\. |> |\$ |\# | In \[\d*\]: | {2,5}\.\.\.: | {5,8}: " +) +copybutton_prompt_is_regexp = True +copybutton_remove_prompts = True + +# sphinxext-rediraffe +rediraffe_redirects = "redirects.txt" +rediraffe_branch = "master~1" + +# aafig format, try to get working with pdf +aafig_format = {"latex": "pdf", "html": "gif"} +aafig_default_options = {"scale": 0.75, "aspect": 0.5, "proportional": True} + +intersphinx_mapping = { + "python": ("https://docs.python.org/", None), + "libtmux": ("https://libtmux.git-pull.com/", None), +} + + +def linkcode_resolve(domain: str, info: dict[str, str]) -> None | str: + """ + Determine the URL corresponding to Python object. + + Notes + ----- + From https://github.com/numpy/numpy/blob/v1.15.1/doc/source/conf.py, 7c49cfa + on Jul 31. License BSD-3. https://github.com/numpy/numpy/blob/v1.15.1/LICENSE.txt + """ + if domain != "py": + return None + + modname = info["module"] + fullname = info["fullname"] + + submod = sys.modules.get(modname) + if submod is None: + return None + + obj = submod + for part in fullname.split("."): + try: + obj = getattr(obj, part) + except Exception: # NOQA: PERF203 + return None + + # strip decorators, which would resolve to the source of the decorator + # possibly an upstream bug in getsourcefile, bpo-1764286 + try: + unwrap = inspect.unwrap + except AttributeError: + pass + else: + if callable(obj): + obj = unwrap(obj) + + try: + fn = inspect.getsourcefile(obj) + except Exception: + fn = None + if not fn: + return None + + try: + source, lineno = inspect.getsourcelines(obj) + except Exception: + lineno = None + + linespec = f"#L{lineno}-L{lineno + len(source) - 1}" if lineno else "" + + fn = relpath(fn, start=pathlib.Path(tmuxp.__file__).parent) + + if "dev" in about["__version__"]: + return "{}/blob/master/{}/{}/{}{}".format( + about["__github__"], + "src", + about["__package_name__"], + fn, + linespec, + ) + return "{}/blob/v{}/{}/{}/{}{}".format( + about["__github__"], + about["__version__"], + "src", + about["__package_name__"], + fn, + linespec, + ) + + +def remove_tabs_js(app: Sphinx, exc: Exception) -> None: + """Fix for sphinx-inline-tabs#18.""" + if app.builder.format == "html" and not exc: + tabs_js = pathlib.Path(app.builder.outdir) / "_static" / "tabs.js" + with contextlib.suppress(FileNotFoundError): + tabs_js.unlink() # When python 3.7 deprecated, use missing_ok=True + + +def setup(app: Sphinx) -> None: + """Sphinx setup hook.""" + app.connect("build-finished", remove_tabs_js) diff --git a/docs/configuration/environmental-variables.md b/docs/configuration/environmental-variables.md new file mode 100644 index 00000000000..d485da45744 --- /dev/null +++ b/docs/configuration/environmental-variables.md @@ -0,0 +1,26 @@ +(environmental-variables)= + +# Environmental variables + +(TMUXP_CONFIGDIR)= + +## `TMUXP_CONFIGDIR` + +Example: `TMUXP_CONFIGDIR=$HOME/.mytmuxpconfigdir tmuxp load cpython` + +(LIBTMUX_TMUX_FORMAT_SEPARATOR)= + +## `LIBTMUX_TMUX_FORMAT_SEPARATOR` + +:::{seealso} + +[`LIBTMUX_TMUX_FORMAT_SEPARATOR`](https://libtmux.git-pull.com/api.html#tmux-format-separator) in libtmux API. + +::: + +In rare circumstances the `tmux -F` separator under the hood may cause issues +building sessions. For this case you can override it here. + +```console +$ env LIBTMUX_TMUX_FORMAT_SEPARATOR='__SEP__' tmuxp load [session] +``` diff --git a/docs/configuration/examples.md b/docs/configuration/examples.md new file mode 100644 index 00000000000..97b2a369a16 --- /dev/null +++ b/docs/configuration/examples.md @@ -0,0 +1,802 @@ +(examples)= + +# Examples + +## Short hand / inline style + +tmuxp has a short-hand syntax for those who wish to keep their workspace +punctual. + +::::{sidebar} short hand + +```{eval-rst} +.. aafig:: + :textual: + + +-------------------+ + | 'did you know' | + | 'you can inline' | + +-------------------+ + | 'single commands' | + | | + +-------------------+ + | 'for panes' | + | | + +-------------------+ +``` + +:::: + +````{tab} YAML + +```{literalinclude} ../../examples/shorthands.yaml +:language: yaml + +``` + + +```` + +````{tab} JSON + +```{literalinclude} ../../examples/shorthands.json +:language: json + +``` + +```` + +## Blank panes + +No need to repeat `pwd` or a dummy command. A `null`, `'blank'`, +`'pane'` are valid. + +Note `''` counts as an empty carriage return. + +````{tab} YAML + +```{literalinclude} ../../examples/blank-panes.yaml +:language: yaml + +``` + +```` + +````{tab} JSON + +```{literalinclude} ../../examples/blank-panes.json +:language: json + +``` + +```` + +## 2 panes + +::::{sidebar} 2 pane + +```{eval-rst} +.. aafig:: + + +-----------------+ + | $ pwd | + | | + | | + +-----------------+ + | $ pwd | + | | + | | + +-----------------+ +``` + +:::: + +````{tab} YAML + +```{literalinclude} ../../examples/2-pane-vertical.yaml +:language: yaml + +``` + +```` + +````{tab} JSON + +```{literalinclude} ../../examples/2-pane-vertical.json +:language: json + +``` + +```` + +## 3 panes + +::::{sidebar} 3 panes + +```{eval-rst} +.. aafig:: + + +-----------------+ + | $ pwd | + | | + | | + +--------+--------+ + | $ pwd | $ pwd | + | | | + | | | + +--------+--------+ +``` + +:::: + +````{tab} YAML + +```{literalinclude} ../../examples/3-pane.yaml +:language: yaml + +``` + +```` + +````{tab} JSON + +```{literalinclude} ../../examples/3-pane.json +:language: json + +``` + +```` + +## 4 panes + +::::{sidebar} 4 panes + +```{eval-rst} +.. aafig:: + + +--------+--------+ + | $ pwd | $ pwd | + | | | + | | | + +--------+--------+ + | $ pwd | $ pwd | + | | | + | | | + +--------+--------+ +``` + +:::: + +````{tab} YAML + +```{literalinclude} ../../examples/4-pane.yaml +:language: yaml + +``` + +```` + +````{tab} JSON + +```{literalinclude} ../../examples/4-pane.json +:language: json + +``` + +```` + +## Start Directory + +Equivalent to `tmux new-window -c `. + +````{tab} YAML + +```{literalinclude} ../../examples/start-directory.yaml +:language: yaml + +``` + +```` + +````{tab} JSON + +```{literalinclude} ../../examples/start-directory.json +:language: json + +``` + +```` + +## Environment variable replacing + +tmuxp will replace environment variables wrapped in curly brackets +for values of these settings: + +- `start_directory` +- `before_script` +- `session_name` +- `window_name` +- `shell_command_before` +- `global_options` +- `options` in session scope and window scope + +tmuxp replaces these variables before-hand with variables in the +terminal `tmuxp` invokes in. + +In this case of this example, assuming the username "user": + +```console +$ MY_ENV_VAR=foo tmuxp load examples/env-variables.yaml +``` + +and your session name will be `session - user (foo)`. + +Shell variables in `shell_command` do not support this type of +concatenation. `shell_command` and `shell_command_before` both +support normal shell variables, since they are sent into panes +automatically via `send-key` in `tmux(1)`. See `ls $PWD` in +example. + +If you have a special case and would like to see behavior changed, +please make a ticket on the [issue tracker][issue tracker]. + +[issue tracker]: https://github.com/tmux-python/tmuxp/issues + +````{tab} YAML + +```{literalinclude} ../../examples/env-variables.yaml +:language: yaml + +``` + +```` + +````{tab} JSON + +```{literalinclude} ../../examples/env-variables.json +:language: json + +``` + +```` + +## Environment variables + +tmuxp will set session, window and pane environment variables. + +```{note} + +Setting environment variables for windows and panes requires tmuxp 1.19 or +newer and tmux 3.0 or newer. +``` + +````{tab} YAML + +```{literalinclude} ../../examples/session-environment.yaml +:language: yaml + +``` +```` + +````{tab} JSON + +```{literalinclude} ../../examples/session-environment.json +:language: json + +``` + +```` + +## Focusing + +tmuxp allows `focus: true` for assuring windows and panes are attached / +selected upon loading. + +````{tab} YAML + +```{literalinclude} ../../examples/focus-window-and-panes.yaml +:language: yaml + +``` + +```` + +````{tab} JSON + +```{literalinclude} ../../examples/focus-window-and-panes.json +:language: json + +``` + +```` + +## Terminal History + +tmuxp allows `suppress_history: false` to override the default command / +suppression when building the workspace. +This will add the `shell_command` to the shell history in the pane. +The suppression of the `shell_command` commands from the shell's history +occurs by prefixing the commands with a space when `suppress_history: true`. +Accordingly, this functionality depends on the shell being appropriately +configured: bash requires the shell variable `HISTCONTROL` to be set and +include either of the values `ignorespace` or `ignoreboth` (to also ignore +sequential duplicate commands), and zsh requires `setopt HIST_IGNORE_SPACE`. + +````{tab} YAML + +```{literalinclude} ../../examples/suppress-history.yaml +:language: yaml + +``` + +```` + +````{tab} JSON + +```{literalinclude} ../../examples/suppress-history.json +:language: json + +``` + +```` + +(enter)= + +## Skip command execution + +See more at {ref}`enter`. + +:::{note} + +_Experimental setting_: behavior and api is subject to change until stable. + +::: + +```{versionadded} 1.10.0 +`enter: false` option. Pane-level support. +``` + +Omit sending {kbd}`enter` to key commands. Equivalent to +[`send_keys(enter=False)`](libtmux.Pane.send_keys). + +````{tab} YAML + +```{literalinclude} ../../examples/skip-send.yaml +:language: yaml + +``` + +```` + +````{tab} JSON + +```{literalinclude} ../../examples/skip-send.json +:language: json + +``` + +```` + +````{tab} YAML (pane-level) + +```{literalinclude} ../../examples/skip-send-pane-level.yaml +:language: yaml + +``` + +```` + +````{tab} JSON (pane-level) + +```{literalinclude} ../../examples/skip-send-pane-level.json +:language: json + +``` + +```` + +(sleep)= + +## Pausing commands + +:::{note} + +_Experimental setting_: behavior and api is subject to change until stable. + +::: + +```{versionadded} 1.10.0 +`sleep_before` and `sleep_after` options added. Pane and command-level support. +``` + +```{warning} +**Blocking.** This will delay loading as it runs synchronously for each pane as [`asyncio`](asyncio)) is not implemented yet. +``` + +Omit sending {kbd}`enter` to key commands. Equivalent to having +a [`time.sleep`](time.sleep) before and after [`send_keys`](libtmux.Pane.send_keys). + +This is especially useful for expensive commands where the terminal needs some breathing room (virtualenv, poetry, pipenv, uv, sourcing a configuration, launching a tui app, etc). + +````{tab} Virtualenv + +```{literalinclude} ../../examples/sleep-virtualenv.yaml +:language: yaml + +``` +```` + +````{tab} YAML + +```{literalinclude} ../../examples/sleep.yaml +:language: yaml + +``` + +```` + +````{tab} JSON + +```{literalinclude} ../../examples/sleep.json +:language: json + +``` + +```` + +````{tab} YAML (pane-level) + +```{literalinclude} ../../examples/sleep-pane-level.yaml +:language: yaml + +``` + +```` + +````{tab} JSON (pane-level) + +```{literalinclude} ../../examples/sleep-pane-level.json +:language: json + +``` + +```` + +## Window Index + +You can specify a window's index using the `window_index` property. Windows +without `window_index` will use the lowest available window index. + +````{tab} YAML + +```{literalinclude} ../../examples/window-index.yaml +:language: yaml + +``` + +```` + +````{tab} JSON + +```{literalinclude} ../../examples/window-index.json +:language: json + +``` + +```` + +## Shell per pane + +Every pane can have its own shell or application started. This allows for usage +of the `remain-on-exit` setting to be used properly, but also to have +different shells on different panes. + +````{tab} YAML + +```{literalinclude} ../../examples/pane-shell.yaml +:language: yaml + +``` + +```` + +````{tab} JSON + +```{literalinclude} ../../examples/pane-shell.json +:language: json + +``` + +```` + +## Set tmux options + +Works with global (server-wide) options, session options +and window options. + +Including `automatic-rename`, `default-shell`, +`default-command`, etc. + +````{tab} YAML + +```{literalinclude} ../../examples/options.yaml +:language: yaml + +``` +```` + +````{tab} JSON +```{literalinclude} ../../examples/options.json +:language: json + +``` +```` + +## Set window options after pane creation + +Apply window options after panes have been created. Useful for +`synchronize-panes` option after executing individual commands in each +pane during creation. + +````{tab} YAML +```{literalinclude} ../../examples/2-pane-synchronized.yaml +:language: yaml + +``` +```` + +````{tab} JSON +```{literalinclude} ../../examples/2-pane-synchronized.json +:language: json + +``` +```` + +## Main pane height + +### Percentage + +:::{versionadded} 1.46.0 + +Before this, tmuxp layouts would not detect the terminal's size. + +::: + +````{tab} YAML +```{literalinclude} ../../examples/main-pane-height-percentage.yaml +:language: yaml + +``` +```` + +````{tab} JSON +```{literalinclude} ../../examples/main-pane-height-percentage.json +:language: json + +``` +```` + +### Rows + +````{tab} YAML +```{literalinclude} ../../examples/main-pane-height.yaml +:language: yaml + +``` +```` + +````{tab} JSON +```{literalinclude} ../../examples/main-pane-height.json +:language: json + +``` +```` + +## Super-advanced dev environment + +:::{seealso} + +{ref}`tmuxp-developer-config` in the {ref}`developing` section. + +::: + +````{tab} YAML +```{literalinclude} ../../.tmuxp.yaml +:language: yaml + +``` +```` + +````{tab} JSON +```{literalinclude} ../../.tmuxp.json +:language: json + +``` +```` + +## Multi-line commands + +You can use YAML's multiline syntax to easily split multiple +commands into the same shell command: https://stackoverflow.com/a/21699210 + +````{tab} YAML +```yaml +session_name: my project +shell_command_before: +- > + [ -d `.venv/bin/activate` ] && + source .venv/bin/activate && + reset +- sleep 1 +windows: +- window_name: first window + layout: main-horizontal + focus: true + panes: + - focus: True + - blank + - > + uv run ./manage.py migrate && + npm -C js run start + - uv run ./manage.py runserver + options: + main-pane-height: 35 +``` +```` + +## Bootstrap project before launch + +You can use `before_script` to run a script before the tmux session +starts building. This can be used to start a script to create a virtualenv +or download a virtualenv/rbenv/package.json's dependency files before +tmuxp even begins building the session. + +It works by using the [Exit Status][exit status] code returned by a script. Your +script can be any type, including bash, python, ruby, etc. + +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 +_relative to the `.tmuxp.yaml`'s root_ (Windows and panes omitted in +this example): + +````{tab} YAML +```yaml +session_name: my session +before_script: ./bootstrap.py +# ... the rest of your workspace + +``` +```` + +````{tab} JSON +```json +{ + "session_name": "my session", + "before_script": "./bootstrap.py" +} + +``` +```` + +Run a shell script + check for return code on an absolute path. (Windows +and panes omitted in this example) + +````{tab} YAML + +```yaml +session_name: another example +before_script: /absolute/path/this.sh # abs path to shell script +# ... the rest of your workspace + +``` +```` + +````{tab} JSON + +```json +{ + "session_name": "my session", + "before_script": "/absolute/path/this.sh" +} + +``` +```` + +[exit status]: http://tldp.org/LDP/abs/html/exit-status.html + +## Per-project tmuxp workspaces + +You can load your software project in tmux by placing a `.tmuxp.yaml` or +`.tmuxp.json` in the project's workspace and loading it. + +tmuxp supports loading workspace via absolute filename with `tmuxp load` +and via `$ tmuxp load .` if workspace is in the directory. + +```console + +$ tmuxp load ~/workspaces/myproject.yaml + +``` + +See examples of `tmuxp` in the wild. Have a project workspace to show off? +Edit this page. + +- +- +- + +You can use `start_directory: ./` to make the directories relative to +the workspace file / project root. + +## Bonus: pipenv auto-bootstrapping + +:::{versionadded} 1.3.4 + +`before_script` CWD's into the root (session)-level +`start_directory`. + +::: + +If you use [pipenv] / [poetry] / [uv], you can use a script like this to ensure +your packages are installed: + +````{tab} YAML + +```yaml +# assuming your .tmuxp.yaml is in your project root directory +session_name: my pipenv project +start_directory: ./ +before_script: pipenv install --dev --skip-lock # ensure dev deps install +windows: +- window_name: django project + focus: true + panes: + - blank + - pipenv run ./manage.py runserver + +``` +```` + +You can also source yourself into the virtual environment using +`shell_command_before`: + +````{tab} YAML + +```yaml +# assuming your .tmuxp.yaml is in your project root directory +session_name: my pipenv project +start_directory: ./ +before_script: pipenv install --dev --skip-lock # ensure dev deps install +shell_command_before: +- '[ -d `pipenv --venv` ] && source `pipenv --venv`/bin/activate && reset' +windows: +- window_name: django project + focus: true + panes: + - blank + - ./manage.py runserver + +``` +```` + +[pipenv]: https://docs.pipenv.org/ +[poetry]: https://python-poetry.org/ +[uv]: https://github.com/astral-sh/uv + +## Kung fu + +:::{note} + +tmuxp sessions can be scripted in python. The first way is to use the +ORM in the {ref}`API`. The second is to pass a {py:obj}`dict` into +{class}`~tmuxp.workspace.builder.WorkspaceBuilder` with a correct schema. +See: {meth}`tmuxp.validation.validate_schema`. + +::: + +Add yours? Submit a pull request to the [github][github] site! + +[github]: https://github.com/tmux-python/tmuxp diff --git a/docs/configuration/index.md b/docs/configuration/index.md new file mode 100644 index 00000000000..aa59b017ee0 --- /dev/null +++ b/docs/configuration/index.md @@ -0,0 +1,176 @@ +(config)= + +(configuration)= + +(workspace)= + +# Workspace files + +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. + +## Launching your session + +Once you have `tmuxp` installed alongside tmux, you can load a workspace with: + +```console +$ tmuxp load ./path/to/file +``` + +tmuxp will offer to assist when: + +- _Session already exists_: tmuxp will prompt you to re-attach. It does this + by checking if the workspace's `session_name` matches a session already + running on the same server. +- _When inside a tmux client_, `tmuxp` will let you create a new session and switch to it, or append the windows to your existing + session. + +## What's in a workspace file? + +1. A session name +2. A list of _windows_ +3. A list of _panes_ for each window +4. A list of _commands_ for each pane + +````{tab} Basics + +```yaml +session_name: My session +windows: +- window_name: Window 1 + panes: + - shell_command: + - cmd: echo "pane 1" + - shell_command: + - cmd: echo "pane 2" +``` + +```` + +````{tab} Smallest possible + +```{literalinclude} ../../examples/minimal.yaml +:language: yaml + +``` + +As of 1.11.x. + +```` + +Breaking down the basic workspace into sections: + +1. A session name + + ```yaml + session_name: My session + ``` + +2. A list of _windows_ + + ```yaml + windows: + - window_name: Window 1 + panes: ... + # window settings + - window_name: Window 2 + panes: ... + # window settings + ``` + +3. A list of _panes_ for each window + + ```yaml + windows: + panes: + - # pane settings + - # pane settings + ``` + +4. A list of _commands_ for each pane + + ```yaml + windows: + panes: + - shell_command: + - cmd: echo "pane 1 - cmd 1" + # command options + - cmd: echo "pane 1 - cmd 2" + # command options + ``` + +## Where do I store workspace files? + +### Direct + +You can create a workspace and load it from anywhere in your file system. + +```console +$ tmuxp load [workspace-file] +``` + +````{tab} Relative +```console +$ tmuxp load ./favorites.yaml +``` +```` + +````{tab} Absolute +```console +$ tmuxp load /opt/myapp/favorites.yaml +``` +```` + +### User-based workspaces + +tmuxp uses the [XDG Base Directory] specification. + +Often on POSIX machines, you will store them in `~/.config/tmuxp`. + +Assume you store `apple.yaml` in `$XDG_CONFIG_HOME/tmuxp/apple.yaml`, you can +then use: + +```console +$ tmuxp load apple +``` + +:::{seealso} + +This path can be overridden by {ref}`TMUXP_CONFIGDIR` + +::: + +[xdg base directory]: https://specifications.freedesktop.org/basedir-spec/latest/ + +### Project-specific + +You can store a workspace in your project's root directory as `.tmuxp.yaml` or `.tmuxp.json`, then: + +Assume `.tmuxp.yaml` inside `/opt/myapp` + +```console +$ tmuxp load [workspace-file] +``` + +````{tab} In project root +```console +$ tmuxp load ./ +``` +```` + +````{tab} Absolute +```console +$ tmuxp load /opt/myapp +``` +```` + +## Reference and usage + +```{toctree} + +top-level +environmental-variables +examples + +``` diff --git a/docs/configuration/top-level.md b/docs/configuration/top-level.md new file mode 100644 index 00000000000..72eb24f32fb --- /dev/null +++ b/docs/configuration/top-level.md @@ -0,0 +1,42 @@ +(top-level)= +(top-level-config)= + +# Top-level configuration + +## `session_name` + +Used for: + +- tmux session name +- checking for existing sessions + +Notes: + +- Session names may differ from workspace filename. + + e.g. _apple.yaml_: + + ```yaml + session_name: banana + windows: + - panes: + - + ``` + + Load detached: + + ```console + $ tmuxp load ./apple.yaml -d + ``` + + Above: + + - 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 + $ tmux attach -t banana + ``` + + Above: Use `tmux` directly to attach _banana_. diff --git a/docs/developing.md b/docs/developing.md new file mode 100644 index 00000000000..95f4dd52f8b --- /dev/null +++ b/docs/developing.md @@ -0,0 +1,466 @@ +(developing)= + +# 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`. + +[pytest]: http://pytest.org/ + +(install-dev-env)= + +## Install the latest code from git + +### Get the source + +To begin developing, check out the code from github: + +```console +$ git clone git@github.com:tmux-python/tmuxp.git +``` + +```console +$ 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: + +```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: + +```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: + +```console +$ uv sync --all-extras --dev --upgrade +``` + +Then before any python command in tty / terminal session, run with: + +```console +$ uv run [command] +``` + +That is it! You are now ready to code! + +[uv]: https://github.com/astral-sh/uv +[uv's documentation]: https://docs.astral.sh/uv + +### Advanced: Manual virtualenv + +Now create a virtualenv, if you don't know how to, you can create a +virtualenv with: + +```console +$ virtualenv .venv +``` + +Then activate it to your current tty / terminal session with: + +```console +$ source .venv/bin/activate +``` + +Good! Now let's run this: + +```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. + +```console +$ tmuxp +``` + +## 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. + +### Rerun on file change + +via [pytest-watcher] (works out of the box): + +```console +$ make start +``` + +via [`entr(1)`] (requires installation): + +```console +$ make watch_test +``` + +[pytest-watcher]: https://github.com/olzhasar/pytest-watcher + +### Manual (just the command, please) + +```console +$ uv run py.test +``` + +or: + +```console +$ make test +``` + +### pytest options + +`PYTEST_ADDOPTS` can be set in the commands below. For more +information read [docs.pytest.com] for the latest documentation. + +[docs.pytest.com]: https://docs.pytest.org/ + +Verbose: + +```console +$ env PYTEST_ADDOPTS="-verbose" make start +``` + +Pick a file: + +```console +$ env PYTEST_ADDOPTS="tests/workspace/test_builder.py" uv run make 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: + +```console +$ env PYTEST_ADDOPTS="-s -x -vv tests/workspace/test_builder.py::test_automatic_rename_option" uv run make start +``` + +Drop into `pdb` on first error: + +```console +$ env PYTEST_ADDOPTS="-x -s --pdb" make start +``` + +If you have [ipython] installed: + +```console +$ env PYTEST_ADDOPTS="--pdbcls=IPython.terminal.debugger:TerminalPdb" make 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: + +```console +$ py.test tests/test_config.py +``` + +will test the `tests/test_config.py` tests. + +```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: + +```console +$ py.test tests/test_{window,pane}.py tests/test_config.py::test_export_json +``` + +(test-builder-visually)= + +### 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)`]. + +::: + +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: + +```console +$ make watch_test +``` + +You can also re-run a specific test file or any other [py.test usage +argument]: + +```console +$ make watch_test test=tests/test_config.py +``` + +```console +$ make watch_test test='-x tests/test_config.py tests/test_util.py' +``` + +### Testing options + +`RETRY_TIMEOUT_SECONDS` can be toggled if certain workspace builder +tests are being stubborn. + +e.g. `RETRY_TIMEOUT_SECONDS=10 py.test` + +```{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 +``` + +```console +$ make SPHINXBUILD='uv run sphinx-build' watch +``` + +(tmuxp-developer-config)= + +## tmuxp developer config + +```{image} _static/tmuxp-dev-screenshot.png +:align: center + +``` + +After you {ref}`install-dev-env`, when inside the tmuxp checkout: + +```console +$ tmuxp load . +``` + +this will load the `.tmuxp.yaml` in the root of the project. + +```{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 +``` + +```` + +````{tab} Watch + +```console +$ make watch_ruff +``` + +requires [`entr(1)`]. + +```` + +````{tab} Fix files + +uv: + +```console +$ uv run ruff check . --fix +``` + +If you setup manually: + +```console +$ ruff check . --fix +``` + +```` + +#### ruff format + +[ruff format] is used for formatting. + +````{tab} Command + +uv: + +```console +$ uv run 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: + +```console +$ uv run mypy . +``` + +If you setup manually: + +```console +$ 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]. + +[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/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/glossary.md b/docs/glossary.md new file mode 100644 index 00000000000..02375f742d4 --- /dev/null +++ b/docs/glossary.md @@ -0,0 +1,66 @@ +(glossary)= + +# Glossary + +```{glossary} + +tmuxp + A tool to manage workspaces with tmux. A pythonic abstraction of + tmux. + +tmux +tmux(1) + The tmux binary. Used internally to distinguish tmuxp is only a + layer on top of tmux. + +ConfigReader + configuration management class, for parsing YAML / JSON / etc. files + to and from python data (dictionaries, in the future, potentially + dataclasses) + +Server + 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 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]``. + +Client + Attaches to a tmux {term}`server`. When you use tmux through CLI, + you are using tmux as a client. + +Session + Inside a tmux {term}`server`. + + The session has 1 or more {term}`Window`. The bottom bar in tmux + show a list of windows. Normally they can be navigated with + ``Ctrl-a [0-9]``, ``Ctrl-a n`` and ``Ctrl-a p``. + + Sessions can have a ``session_name``. + + Uniquely identified by ``session_id``. + +Window + Entity of a {term}`session`. + + Can have 1 or more {term}`pane`. + + Panes can be organized with a layouts. + + Windows can have names. + +Pane + Linked to a {term}`Window`. + + a pseudoterminal. + +Target + A target, cited in the manual as ``[-t target]`` can be a session, + window or pane. +``` diff --git a/docs/history.md b/docs/history.md new file mode 100644 index 00000000000..55bbcbc445b --- /dev/null +++ b/docs/history.md @@ -0,0 +1,7 @@ +(changelog)= + +(history)= + +```{include} ../CHANGES + +``` diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 00000000000..14b69dedb04 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,41 @@ +(index)= + +```{include} ../README.md +:end-before: +``` diff --git a/docs/manifest.json b/docs/manifest.json new file mode 100644 index 00000000000..d691c43cb3a --- /dev/null +++ b/docs/manifest.json @@ -0,0 +1,53 @@ +{ + "name": "tmuxp", + "short_name": "tmuxp", + "description": "tmux session manager", + "theme_color": "#2196f3", + "background_color": "#fff", + "display": "browser", + "Scope": "https://tmuxp.git-pull.com/", + "start_url": "https://tmuxp.git-pull.com/", + "icons": [ + { + "src": "_static/img/icons/icon-72x72.png", + "sizes": "72x72", + "type": "image/png" + }, + { + "src": "_static/img/icons/icon-96x96.png", + "sizes": "96x96", + "type": "image/png" + }, + { + "src": "_static/img/icons/icon-128x128.png", + "sizes": "128x128", + "type": "image/png" + }, + { + "src": "_static/img/icons/icon-144x144.png", + "sizes": "144x144", + "type": "image/png" + }, + { + "src": "_static/img/icons/icon-152x152.png", + "sizes": "152x152", + "type": "image/png" + }, + { + "src": "_static/img/icons/icon-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "_static/img/icons/icon-384x384.png", + "sizes": "384x384", + "type": "image/png" + }, + { + "src": "_static/img/icons/icon-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "splash_pages": null +} diff --git a/docs/migration.md b/docs/migration.md new file mode 100644 index 00000000000..7bd3f4664f7 --- /dev/null +++ b/docs/migration.md @@ -0,0 +1,9 @@ +(migration)= + +```{currentmodule} libtmux + +``` + +```{include} ../MIGRATION + +``` diff --git a/docs/plugins/index.md b/docs/plugins/index.md new file mode 100644 index 00000000000..0276d66e643 --- /dev/null +++ b/docs/plugins/index.md @@ -0,0 +1,145 @@ +(plugins)= + +# Plugins + +The plugin system allows users to customize and extend different aspects of +tmuxp without the need to change tmuxp itself. + +## 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 + +````{tab} YAML + +```{literalinclude} ../../examples/plugin-system.yaml +:language: yaml + +``` + +```` + +````{tab} JSON + +```{literalinclude} ../../examples/plugin-system.json +:language: json + +``` + +```` + +## Developing a Plugin + +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. + +[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. + +```console + +python_module +├── tmuxp_plugin_my_plugin_module +│   ├── __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: + +```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" +dependencies = [ + "tmuxp^=1.7.0" +] + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" +``` + +The `plugin.py` file could contain something like the following: + +```python + +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 + config = { + 'tmuxp_min_version' = '1.6.2' + } + + TmuxpPlugin.__init__( + self, + plugin_name='tmuxp-plugin-my-tmuxp-plugin', + **config + ) + + def before_workspace_builder(self, session): + session.rename_session('my-new-session-name') + + 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: + +```yaml +session_name: plugin example +plugins: + - my_plugin_module.plugin.MyTmuxpPlugin +# ... the rest of your config +``` + +## Plugin API + +```{eval-rst} +.. automethod:: tmuxp.plugin.TmuxpPlugin.__init__ +``` + +```{eval-rst} +.. automethod:: tmuxp.plugin.TmuxpPlugin.before_workspace_builder +``` + +```{eval-rst} +.. automethod:: tmuxp.plugin.TmuxpPlugin.on_window_create +``` + +```{eval-rst} +.. automethod:: tmuxp.plugin.TmuxpPlugin.after_window_finished +``` + +```{eval-rst} +.. automethod:: tmuxp.plugin.TmuxpPlugin.before_script +``` + +```{eval-rst} +.. automethod:: tmuxp.plugin.TmuxpPlugin.reattach +``` + +[uv]: https://github.com/astral-sh/uv diff --git a/docs/quickstart.md b/docs/quickstart.md new file mode 100644 index 00000000000..239fd22252a --- /dev/null +++ b/docs/quickstart.md @@ -0,0 +1,159 @@ +(quickstart)= + +# Quickstart + +## Installation + +Ensure you have at least tmux **>= 1.8** and python **>= 3.7**. + +```console +$ pip install --user tmuxp +``` + +You can upgrade to the latest release with: + +```console +$ pip install --user --upgrade tmuxp +``` + +Then install {ref}`completion`. + +If you are a Homebrew user you can install it with: + +```console +$ brew install tmuxp +``` + +(developmental-releases)= + +### 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 + ``` + +- [pipx]\: + + ```console + $ pipx install --suffix=@next 'tmuxp' --pip-args '\--pre' --force + ``` + + Then use `tmuxp@next load [session]`. + +via trunk (can break easily): + +- [pip]\: + + ```console + $ pip install --user -e git+https://github.com/tmux-python/tmuxp.git#egg=tmuxp + ``` + +- [pipx]\: + + ```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/ + +## Commands + +:::{seealso} + +{ref}`examples`, {ref}`commands`, {ref}`completion`. + +::: + +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: + +1. `session_name` +2. list of `windows` +3. list of `panes` for every window in `windows` + +Create a file, `~/.tmuxp/example.yaml`: + +```{literalinclude} ../examples/2-pane-vertical.yaml +:language: yaml + +``` + +```console + +$ tmuxp load example.yaml + +``` + +This creates your tmuxp session. + +Load multiple tmux sessions 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. + +You can also have a custom tmuxp config directory by setting the +`TMUXP_CONFIGDIR` in your environment variables. + +```console + +$ TMUXP_CONFIGDIR=$HOME/.tmuxpmoo tmuxp load cpython + +``` + +Or in your `~/.bashrc` / `~/.zshrc` you can set: + +```console + +export TMUXP_CONFIGDIR=$HOME/.yourconfigdir/tmuxp + +``` + +You can also [Import][import] configs [teamocil] and [tmuxinator]. + +## Pythonics + +:::{seealso} + +{ref}`libtmux python API documentation ` and {ref}`developing`. + +::: + +ORM - [Object Relational Mapper][object relational mapper] + +AL - [Abstraction Layer][abstraction layer] + +### python abstraction layer + +| {ref}`tmuxp python api ` | {term}`tmux(1)` equivalent | +| ------------------------------------- | -------------------------- | +| {meth}`libtmux.Server.new_session` | `$ tmux new-session` | +| {meth}`libtmux.Server.sessions` | `$ tmux list-sessions` | +| {meth}`libtmux.Session.windows` | `$ tmux list-windows` | +| {meth}`libtmux.Session.new_window` | `$ tmux new-window` | +| {meth}`libtmux.Window.panes` | `$ tmux list-panes` | +| {meth}`libtmux.Window.split` | `$ tmux split-window` | +| {meth}`libtmux.Pane.send_keys` | `$ tmux send-keys` | + +[import]: http://tmuxp.git-pull.com/commands/#import +[tmuxinator]: https://github.com/aziz/tmuxinator +[teamocil]: https://github.com/remiprev/teamocil +[abstraction layer]: http://en.wikipedia.org/wiki/Abstraction_layer +[object relational mapper]: http://en.wikipedia.org/wiki/Object-relational_mapping diff --git a/docs/redirects.txt b/docs/redirects.txt new file mode 100644 index 00000000000..66d2cb860b4 --- /dev/null +++ b/docs/redirects.txt @@ -0,0 +1,5 @@ +"cli.md" "commands/index.md" +"api.md" "api/index.md" +"examples.md" "configuration/examples.md" +"plugin_system.md" "plugins/index.md" +"commands/index.md" "cli/index.md" diff --git a/examples/2-pane-synchronized.json b/examples/2-pane-synchronized.json new file mode 100644 index 00000000000..6cd349ebbf7 --- /dev/null +++ b/examples/2-pane-synchronized.json @@ -0,0 +1,15 @@ +{ + "session_name": "2-pane-synchronized", + "windows": [ + { + "window_name": "Two synchronized panes", + "panes": [ + "ssh server1", + "ssh server2" + ], + "options_after": { + "synchronize-panes": true + } + } + ] +} \ No newline at end of file diff --git a/examples/2-pane-synchronized.yaml b/examples/2-pane-synchronized.yaml new file mode 100644 index 00000000000..50f77473d6c --- /dev/null +++ b/examples/2-pane-synchronized.yaml @@ -0,0 +1,8 @@ +session_name: 2-pane-synchronized +windows: + - window_name: Two synchronized panes + panes: + - ssh server1 + - ssh server2 + options_after: + synchronize-panes: on diff --git a/examples/2-pane-vertical-long.json b/examples/2-pane-vertical-long.json deleted file mode 100644 index 887664403da..00000000000 --- a/examples/2-pane-vertical-long.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "windows": [ - { - "panes": [ - { - "shell_command": [ - "cd ~", - "pwd", - "top" - ] - }, - { - "shell_command": [ - "cd /var/www", - "pwd" - ] - } - ], - "window_name": "test" - }, - { - "panes": [ - { - "shell_command": "pwd" - }, - { - "shell_command": [ - "pwd" - ] - } - ], - "shell_command_before": "cd /var/www", - "window_name": "second window" - } - ], - "session_name": "2-pane-vertical-long" -} \ No newline at end of file diff --git a/examples/2-pane-vertical-long.yaml b/examples/2-pane-vertical-long.yaml deleted file mode 100644 index f1003a54255..00000000000 --- a/examples/2-pane-vertical-long.yaml +++ /dev/null @@ -1,17 +0,0 @@ -session_name: 2-pane-vertical-long -windows: - - window_name: test - panes: - - shell_command: - - cd ~ - - pwd - - top - - shell_command: - - cd /var/www - - pwd - - window_name: second window - shell_command_before: cd /var/www - panes: - - shell_command: pwd - - shell_command: - - pwd diff --git a/examples/2-pane-vertical.json b/examples/2-pane-vertical.json index d05cfce5efa..19e335dbfbb 100644 --- a/examples/2-pane-vertical.json +++ b/examples/2-pane-vertical.json @@ -2,11 +2,11 @@ "windows": [ { "panes": [ - "pwd", - "pwd" + "echo hello", + "echo hello" ], "window_name": "my test window" } ], "session_name": "2-pane-vertical" -} \ No newline at end of file +} diff --git a/examples/2-pane-vertical.yaml b/examples/2-pane-vertical.yaml index 86b04553976..a40e05680da 100644 --- a/examples/2-pane-vertical.yaml +++ b/examples/2-pane-vertical.yaml @@ -2,5 +2,5 @@ session_name: 2-pane-vertical windows: - window_name: my test window panes: - - pwd - - pwd + - echo hello + - echo hello diff --git a/examples/3-pane.json b/examples/3-pane.json index 5a0bb2f74f1..1ad2d839ac4 100644 --- a/examples/3-pane.json +++ b/examples/3-pane.json @@ -7,9 +7,9 @@ "cd /var/log", "ls -al | grep \\.log" ] - }, - "pwd", - "pwd" + }, + "echo hello", + "echo hello" ], "shell_command_before": [ "cd ~/" @@ -19,4 +19,4 @@ } ], "session_name": "3-panes" -} \ No newline at end of file +} diff --git a/examples/3-pane.yaml b/examples/3-pane.yaml index 9bf1a842b6c..76e61103444 100644 --- a/examples/3-pane.yaml +++ b/examples/3-pane.yaml @@ -1,12 +1,12 @@ session_name: 3-panes windows: -- window_name: dev window - layout: main-vertical - shell_command_before: - - cd ~/ - panes: - - shell_command: - - cd /var/log - - ls -al | grep \.log - - pwd - - pwd + - window_name: dev window + layout: main-vertical + shell_command_before: + - cd ~/ + panes: + - shell_command: + - cd /var/log + - ls -al | grep \.log + - echo hello + - echo hello diff --git a/examples/4-pane.json b/examples/4-pane.json index 1af94fc135e..9cf8b19e7b7 100644 --- a/examples/4-pane.json +++ b/examples/4-pane.json @@ -8,9 +8,9 @@ "ls -al | grep \\.log" ] }, - "pwd", - "pwd", - "pwd" + "echo hello", + "echo hello", + "echo hello" ], "shell_command_before": [ "cd ~/" @@ -20,4 +20,4 @@ } ], "session_name": "4-pane-split" -} \ No newline at end of file +} diff --git a/examples/4-pane.yaml b/examples/4-pane.yaml index e0c5c5611d5..d42b89955c4 100644 --- a/examples/4-pane.yaml +++ b/examples/4-pane.yaml @@ -1,13 +1,13 @@ session_name: 4-pane-split windows: -- window_name: dev window - layout: tiled - shell_command_before: - - cd ~/ - panes: - - shell_command: - - cd /var/log - - ls -al | grep \.log - - pwd - - pwd - - pwd + - window_name: dev window + layout: tiled + shell_command_before: + - cd ~/ + panes: + - shell_command: + - cd /var/log + - ls -al | grep \.log + - echo hello + - echo hello + - echo hello diff --git a/examples/blank-panes.json b/examples/blank-panes.json new file mode 100644 index 00000000000..482a2caac62 --- /dev/null +++ b/examples/blank-panes.json @@ -0,0 +1,52 @@ +{ + "windows": [ + { + "panes": [ + null, + "pane", + "blank" + ], + "window_name": "Blank pane test" + }, + { + "panes": [ + null, + { + "shell_command": null + }, + { + "shell_command": [ + null + ] + } + ], + "window_name": "More blank panes" + }, + { + "panes": [ + "", + { + "shell_command": "" + }, + { + "shell_command": [ + "" + ] + } + ], + "window_name": "Empty string (return)" + }, + { + "panes": [ + { + "focus": true + }, + { + "start_directory": "/tmp" + } + ], + "window_name": "Blank with options" + } + ], + "session_name": "Blank pane test" +} \ No newline at end of file diff --git a/examples/blank-panes.yaml b/examples/blank-panes.yaml new file mode 100644 index 00000000000..9828ac1874a --- /dev/null +++ b/examples/blank-panes.yaml @@ -0,0 +1,27 @@ +session_name: Blank pane test +windows: + # Emptiness will simply open a blank pane, if no shell_command_before. + # All these are equivalent + - window_name: Blank pane test + panes: + - + - pane + - blank + - window_name: More blank panes + panes: + - null + - shell_command: + - shell_command: + - + # an empty string will be treated as a carriage return + - window_name: Empty string (return) + panes: + - "" + - shell_command: "" + - shell_command: + - "" + # a pane can have other options but still be blank + - window_name: Blank with options + panes: + - focus: true + - start_directory: /tmp diff --git a/examples/env-variables.json b/examples/env-variables.json new file mode 100644 index 00000000000..d3294eb551c --- /dev/null +++ b/examples/env-variables.json @@ -0,0 +1,33 @@ +{ + "before_script": "${MY_ENV_VAR}/test3.sh", + "windows": [ + { + "panes": [ + { + "shell_command": [ + "tail -F /var/log/syslog" + ] + } + ], + "start_directory": "/var/log", + "window_name": "editor" + }, + { + "panes": [ + { + "shell_command": [ + "htop", + "ls $PWD" + ] + } + ], + "window_name": "logging for ${USER}", + "options": { + "automatic-rename": true + } + } + ], + "shell_command_before": "echo ${PWD}", + "start_directory": "${PWD}/test", + "session_name": "session - ${USER} (${MY_ENV_VAR})" +} \ No newline at end of file diff --git a/examples/env-variables.yaml b/examples/env-variables.yaml new file mode 100644 index 00000000000..e448f5a0cc9 --- /dev/null +++ b/examples/env-variables.yaml @@ -0,0 +1,17 @@ +start_directory: "${PWD}/test" +shell_command_before: "echo ${PWD}" +before_script: "${MY_ENV_VAR}/test3.sh" +session_name: session - ${USER} (${MY_ENV_VAR}) +windows: + - window_name: editor + panes: + - shell_command: + - tail -F /var/log/syslog + start_directory: /var/log + - window_name: logging for ${USER} + options: + automatic-rename: true + panes: + - shell_command: + - htop + - ls $PWD diff --git a/examples/focus-window-and-panes.json b/examples/focus-window-and-panes.json new file mode 100644 index 00000000000..f87516cc3c8 --- /dev/null +++ b/examples/focus-window-and-panes.json @@ -0,0 +1,38 @@ +{ + "windows": [ + { + "panes": [ + { + "shell_command": [ + "echo hello", + "echo 'this pane should be selected on load'" + ], + "focus": true + }, + { + "shell_command": [ + "cd /var/log", + "echo hello" + ] + } + ], + "window_name": "attached window on load", + "focus": true + }, + { + "panes": [ + "pane", + { + "shell_command": [ + "echo 'this pane should be focused, when window switched to first time'" + ], + "focus": true + }, + "pane" + ], + "shell_command_before": "cd /var/www", + "window_name": "second window" + } + ], + "session_name": "focus window and pane when loading sessions" +} diff --git a/examples/focus-window-and-panes.yaml b/examples/focus-window-and-panes.yaml new file mode 100644 index 00000000000..de4805660df --- /dev/null +++ b/examples/focus-window-and-panes.yaml @@ -0,0 +1,20 @@ +session_name: focus +windows: + - window_name: attached window + focus: true + panes: + - shell_command: + - echo hello + - echo 'this pane should be selected on load' + focus: true + - shell_command: + - cd /var/log + - echo hello + - window_name: second window + shell_command_before: cd /var/log + panes: + - pane + - shell_command: + - echo 'this pane should be focused, when window switched to first time' + focus: true + - pane diff --git a/examples/main-pane-height-percentage.json b/examples/main-pane-height-percentage.json new file mode 100644 index 00000000000..32b593d0ef3 --- /dev/null +++ b/examples/main-pane-height-percentage.json @@ -0,0 +1,31 @@ +{ + "windows": [ + { + "panes": [ + { + "shell_command": [ + "top" + ], + "start_directory": "~" + }, + { + "shell_command": [ + "echo \"hey\"" + ] + }, + { + "shell_command": [ + "echo \"moo\"" + ] + } + ], + "layout": "main-horizontal", + "options": { + "main-pane-height": "67%" + }, + "window_name": "editor" + } + ], + "session_name": "main pane height", + "start_directory": "~" +} diff --git a/examples/main-pane-height-percentage.yaml b/examples/main-pane-height-percentage.yaml new file mode 100644 index 00000000000..061aad4ab6e --- /dev/null +++ b/examples/main-pane-height-percentage.yaml @@ -0,0 +1,15 @@ +session_name: main-pane-height +start_directory: "~" +windows: + - layout: main-horizontal + options: + main-pane-height: 67% + panes: + - shell_command: + - top + start_directory: "~" + - shell_command: + - echo "hey" + - shell_command: + - echo "moo" + window_name: my window name diff --git a/examples/main-pane-height.json b/examples/main-pane-height.json new file mode 100644 index 00000000000..8d90ee30225 --- /dev/null +++ b/examples/main-pane-height.json @@ -0,0 +1,31 @@ +{ + "windows": [ + { + "panes": [ + { + "shell_command": [ + "top" + ], + "start_directory": "~" + }, + { + "shell_command": [ + "echo \"hey\"" + ] + }, + { + "shell_command": [ + "echo \"moo\"" + ] + } + ], + "layout": "main-horizontal", + "options": { + "main-pane-height": 30 + }, + "window_name": "editor" + } + ], + "session_name": "main pane height", + "start_directory": "~" +} diff --git a/examples/main-pane-height.yaml b/examples/main-pane-height.yaml new file mode 100644 index 00000000000..fe9a23f0e42 --- /dev/null +++ b/examples/main-pane-height.yaml @@ -0,0 +1,15 @@ +session_name: main-pane-height +start_directory: "~" +windows: + - layout: main-horizontal + options: + main-pane-height: 30 + panes: + - shell_command: + - top + start_directory: "~" + - shell_command: + - echo "hey" + - shell_command: + - echo "moo" + window_name: my window name diff --git a/examples/minimal.yaml b/examples/minimal.yaml new file mode 100644 index 00000000000..e4f86e47a16 --- /dev/null +++ b/examples/minimal.yaml @@ -0,0 +1,4 @@ +session_name: My tmux session +windows: + - panes: + - diff --git a/examples/options.json b/examples/options.json new file mode 100644 index 00000000000..c7df08b61cf --- /dev/null +++ b/examples/options.json @@ -0,0 +1,37 @@ +{ + "windows": [ + { + "panes": [ + { + "shell_command": [ + "man echo" + ], + "start_directory": "~" + }, + { + "shell_command": [ + "echo \"hey\"" + ] + }, + { + "shell_command": [ + "echo \"moo\"" + ] + } + ], + "layout": "main-horizontal", + "options": { + "automatic-rename": true + } + } + ], + "session_name": "test window options", + "start_directory": "~", + "global_options": { + "default-shell": "/bin/sh", + "default-command": "/bin/sh" + }, + "options": { + "main-pane-height": "${MAIN_PANE_HEIGHT}" + } +} \ No newline at end of file diff --git a/examples/options.yaml b/examples/options.yaml new file mode 100644 index 00000000000..b66189820df --- /dev/null +++ b/examples/options.yaml @@ -0,0 +1,19 @@ +session_name: test window options +start_directory: "~" +global_options: + default-shell: /bin/sh + default-command: /bin/sh +options: + main-pane-height: ${MAIN_PANE_HEIGHT} # works with env variables +windows: + - layout: main-horizontal + options: + automatic-rename: on + panes: + - shell_command: + - man echo + start_directory: "~" + - shell_command: + - echo "hey" + - shell_command: + - echo "moo" diff --git a/examples/pane-shell.json b/examples/pane-shell.json new file mode 100644 index 00000000000..1a4b6af487c --- /dev/null +++ b/examples/pane-shell.json @@ -0,0 +1,40 @@ +{ + "session_name": "Pane shell example", + "windows": [ + { + "window_name": "first", + "window_shell": "/usr/bin/python2", + "layout": "even-vertical", + "suppress_history": false, + "options": { + "remain-on-exit": true + }, + "panes": [ + { + "shell": "/usr/bin/python3", + "shell_command": [ + "print('This is python 3')" + ] + }, + { + "shell": "/usr/bin/vim -u none", + "shell_command": [ + "iAll panes have the `remain-on-exit` setting on.", + "When you exit out of the shell or application, the panes will remain.", + "Use tmux command `:kill-pane` to remove the pane.", + "Use tmux command `:respawn-pane` to restart the shell in the pane.", + "Use and then `:q!` to get out of this vim window. :-)" + ] + }, + { + "shell_command": [ + "print('Hello World 2')" + ] + }, + { + "shell": "/usr/bin/top" + } + ] + } + ] +} \ No newline at end of file diff --git a/examples/pane-shell.yaml b/examples/pane-shell.yaml new file mode 100644 index 00000000000..0fee4fdea62 --- /dev/null +++ b/examples/pane-shell.yaml @@ -0,0 +1,22 @@ +session_name: Pane shell example +windows: + - window_name: first + window_shell: /usr/bin/python2 + layout: even-vertical + suppress_history: false + options: + remain-on-exit: true + panes: + - shell: /usr/bin/python3 + shell_command: + - print('This is python 3') + - shell: /usr/bin/vim -u none + shell_command: + - iAll panes have the `remain-on-exit` setting on. + - When you exit out of the shell or application, the panes will remain. + - Use tmux command `:kill-pane` to remove the pane. + - Use tmux command `:respawn-pane` to restart the shell in the pane. + - Use and then `:q!` to get out of this vim window. :-) + - shell_command: + - print('Hello World 2') + - shell: /usr/bin/top diff --git a/examples/plugin-system.json b/examples/plugin-system.json new file mode 100644 index 00000000000..4309676b309 --- /dev/null +++ b/examples/plugin-system.json @@ -0,0 +1,24 @@ +{ + "session_name": "plugin-system", + "plugins": [ + "tmuxp_plugin_extended_build.plugin.PluginExtendedBuild" + ], + "windows": [ + { + "window_name": "editor", + "layout": "tiled", + "shell_command_before": [ + "cd ~/" + ], + "panes": [ + { + "shell_command": [ + "cd /var/log", + "ls -al | grep *.log" + ] + }, + "echo \"hello world\"" + ] + } + ] +} \ No newline at end of file diff --git a/examples/plugin-system.yaml b/examples/plugin-system.yaml new file mode 100644 index 00000000000..15581de7150 --- /dev/null +++ b/examples/plugin-system.yaml @@ -0,0 +1,13 @@ +session_name: plugin-system +plugins: + - "tmuxp_plugin_extended_build.plugin.PluginExtendedBuild" +windows: + - window_name: editor + layout: tiled + shell_command_before: + - cd ~/ + panes: + - shell_command: + - cd /var/log + - ls -al | grep *.log + - echo "hello world" diff --git a/examples/session-environment.json b/examples/session-environment.json new file mode 100644 index 00000000000..3c31e12ec32 --- /dev/null +++ b/examples/session-environment.json @@ -0,0 +1,33 @@ +{ + "environment": { + "EDITOR": "/usr/bin/vim", + "DJANGO_SETTINGS_MODULE": "my_app.settings.local", + "SERVER_PORT": "8009" + }, + "windows": [ + { + "panes": [ + "./manage.py runserver 0.0.0.0:${SERVER_PORT}" + ], + "window_name": "Django project" + }, + { + "environment": { + "DJANGO_SETTINGS_MODULE": "my_app.settings.local", + "SERVER_PORT": "8010" + }, + "panes": [ + "./manage.py runserver 0.0.0.0:${SERVER_PORT}", + { + "environment": { + "DJANGO_SETTINGS_MODULE": "my_app.settings.local-testing", + "SERVER_PORT": "8011" + }, + "shell_command": "./manage.py runserver 0.0.0.0:${SERVER_PORT}" + } + ], + "window_name": "Another Django project" + } + ], + "session_name": "Environment variables test" +} \ No newline at end of file diff --git a/examples/session-environment.yaml b/examples/session-environment.yaml new file mode 100644 index 00000000000..a1091e7a181 --- /dev/null +++ b/examples/session-environment.yaml @@ -0,0 +1,19 @@ +session_name: Environment variables test +environment: + EDITOR: /usr/bin/vim + DJANGO_SETTINGS_MODULE: my_app.settings.local + SERVER_PORT: "8009" +windows: + - window_name: Django project + panes: + - ./manage.py runserver 0.0.0.0:${SERVER_PORT} + - window_name: Another Django project + environment: + DJANGO_SETTINGS_MODULE: my_app.settings.local + SERVER_PORT: "8010" + panes: + - ./manage.py runserver 0.0.0.0:${SERVER_PORT} + - environment: + DJANGO_SETTINGS_MODULE: my_app.settings.local-testing + SERVER_PORT: "8011" + shell_command: ./manage.py runserver 0.0.0.0:${SERVER_PORT} diff --git a/examples/shorthands.json b/examples/shorthands.json new file mode 100644 index 00000000000..977592cf298 --- /dev/null +++ b/examples/shorthands.json @@ -0,0 +1,20 @@ +{ + "windows": [ + { + "panes": [ + { + "shell_command": [ + "echo 'did you know'", + "echo 'you can inline'" + ] + }, + { + "shell_command": "echo 'single commands'" + }, + "echo 'for panes'" + ], + "window_name": "long form" + } + ], + "session_name": "shorthands" +} \ No newline at end of file diff --git a/examples/shorthands.yaml b/examples/shorthands.yaml new file mode 100644 index 00000000000..cd4afaf39a0 --- /dev/null +++ b/examples/shorthands.yaml @@ -0,0 +1,9 @@ +session_name: shorthands +windows: + - window_name: long form + panes: + - shell_command: + - echo 'did you know' + - echo 'you can inline' + - shell_command: echo 'single commands' + - echo 'for panes' diff --git a/examples/skip-send-pane-level.json b/examples/skip-send-pane-level.json new file mode 100644 index 00000000000..2fa8a2535ce --- /dev/null +++ b/examples/skip-send-pane-level.json @@ -0,0 +1,20 @@ +{ + "session_name": "Skip command execution (pane-level)", + "windows": [ + { + "panes": [ + { + "shell_command": "echo \"___$((1 + 3))___\"", + "enter": false + }, + { + "shell_command": [ + "echo \"___$((1 + 3))___\"\\;", + "echo \"___$((1 + 3))___\"" + ], + "enter": false + } + ] + } + ] +} diff --git a/examples/skip-send-pane-level.yaml b/examples/skip-send-pane-level.yaml new file mode 100644 index 00000000000..2e988e89247 --- /dev/null +++ b/examples/skip-send-pane-level.yaml @@ -0,0 +1,9 @@ +session_name: Skip command execution (pane-level) +windows: + - panes: + - shell_command: echo "___$((1 + 3))___" + enter: false + - shell_command: + - echo "___$((1 + 3))___"\; + - echo "___$((1 + 3))___" + enter: false diff --git a/examples/skip-send.json b/examples/skip-send.json new file mode 100644 index 00000000000..db97609d7a6 --- /dev/null +++ b/examples/skip-send.json @@ -0,0 +1,18 @@ +{ + "session_name": "Skip command execution (command-level)", + "windows": [ + { + "panes": [ + { + "shell_command": [ + "echo \"___$((11 + 1))___\"", + { + "cmd": "echo \"___$((1 + 3))___\"", + "enter": false + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/examples/skip-send.yaml b/examples/skip-send.yaml new file mode 100644 index 00000000000..150aa64f0ef --- /dev/null +++ b/examples/skip-send.yaml @@ -0,0 +1,9 @@ +session_name: Skip command execution (command-level) +windows: + - panes: + - shell_command: + # You can see this + - echo "___$((11 + 1))___" + # This is skipped + - cmd: echo "___$((1 + 3))___" + enter: false diff --git a/examples/sleep-pane-level.json b/examples/sleep-pane-level.json new file mode 100644 index 00000000000..92daeeab77c --- /dev/null +++ b/examples/sleep-pane-level.json @@ -0,0 +1,27 @@ +{ + "session_name": "Pause / skip command execution (pane-level)", + "windows": [ + { + "panes": [ + { + "sleep_before": 2, + "shell_command": [ + "echo \"___$((11 + 1))___\"", + { + "cmd": "echo \"___$((1 + 3))___\"" + }, + { + "cmd": "echo \"___$((1 + 3))___\"" + }, + { + "cmd": "echo \"Stuff rendering here!\"" + }, + { + "cmd": "echo \"2 seconds later\"" + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/examples/sleep-pane-level.yaml b/examples/sleep-pane-level.yaml new file mode 100644 index 00000000000..00a1ef21510 --- /dev/null +++ b/examples/sleep-pane-level.yaml @@ -0,0 +1,11 @@ +session_name: Pause / skip command execution (pane-level) +windows: + - panes: + - # Wait 2 seconds before sending all commands in this pane + sleep_before: 2 + shell_command: + - echo "___$((11 + 1))___" + - cmd: echo "___$((1 + 3))___" + - cmd: echo "___$((1 + 3))___" + - cmd: echo "Stuff rendering here!" + - cmd: echo "2 seconds later" diff --git a/examples/sleep-virtualenv.yaml b/examples/sleep-virtualenv.yaml new file mode 100644 index 00000000000..e226b6e3f29 --- /dev/null +++ b/examples/sleep-virtualenv.yaml @@ -0,0 +1,11 @@ +session_name: virtualenv +shell_command_before: + # - cmd: source $(poetry env info --path)/bin/activate + # - cmd: source `pipenv --venv`/bin/activate + - cmd: source .venv/bin/activate + sleep_before: 1 + sleep_after: 1 +windows: + - panes: + - shell_command: + - ./manage.py runserver diff --git a/examples/sleep.json b/examples/sleep.json new file mode 100644 index 00000000000..2136cab086f --- /dev/null +++ b/examples/sleep.json @@ -0,0 +1,28 @@ +{ + "session_name": "Pause / skip command execution (command-level)", + "windows": [ + { + "panes": [ + { + "shell_command": [ + "echo \"___$((11 + 1))___\"", + { + "cmd": "echo \"___$((1 + 3))___\"", + "sleep_before": 2 + }, + { + "cmd": "echo \"___$((1 + 3))___\"" + }, + { + "cmd": "echo \"Stuff rendering here!\"", + "sleep_after": 2 + }, + { + "cmd": "echo \"2 seconds later\"" + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/examples/sleep.yaml b/examples/sleep.yaml new file mode 100644 index 00000000000..cf1e7f7ebe9 --- /dev/null +++ b/examples/sleep.yaml @@ -0,0 +1,16 @@ +session_name: Pause / skip command execution (command-level) +windows: + - panes: + - shell_command: + # Executes immediately + - echo "___$((11 + 1))___" + # Delays before sending 2 seconds + - cmd: echo "___$((1 + 3))___" + sleep_before: 2 + # Executes immediately + - cmd: echo "___$((1 + 3))___" + # Pauses 2 seconds after + - cmd: echo "Stuff rendering here!" + sleep_after: 2 + # Executes after earlier commands (after 2 sec) + - cmd: echo "2 seconds later" diff --git a/examples/start-directory.json b/examples/start-directory.json new file mode 100644 index 00000000000..2bc00033d51 --- /dev/null +++ b/examples/start-directory.json @@ -0,0 +1,73 @@ +{ + "windows": [ + { + "panes": [ + { + "shell_command": [ + "echo \"\\033c", + "it trickles down from session-level\"" + ] + }, + "echo hello" + ], + "window_name": "should be /var/" + }, + { + "panes": [ + { + "shell_command": [ + "echo '\\033c", + "window start_directory concatenates to session start_directory", + "if it is not absolute'" + ] + }, + "echo hello" + ], + "start_directory": "log", + "window_name": "should be /var/log" + }, + { + "panes": [ + { + "shell_command": [ + "echo \\\\033c ~ has precedence. note: remember to quote ~ in YAML" + ] + }, + "echo hello" + ], + "start_directory": "~", + "window_name": "should be ~" + }, + { + "panes": [ + "echo '\\033c absolute paths also have precedence.'", + "echo hello" + ], + "start_directory": "/bin", + "window_name": "should be /bin" + }, + { + "panes": [ + { + "shell_command": [ + "echo '\\033c", + "./ is relative to workspace file location", + "../ will be parent of workspace file", + "./test will be \\\"test\\\" dir inside dir of workspace file'" + ] + }, + { + "shell_command": [ + "echo '\\033c", + "This way you can load up workspaces from projects and maintain", + "relative paths.'" + ] + } + ], + "start_directory": "./", + "window_name": "should be config's dir" + } + ], + "session_name": "start directory", + "start_directory": "/var/" +} diff --git a/examples/start-directory.yaml b/examples/start-directory.yaml new file mode 100644 index 00000000000..dcecf67b879 --- /dev/null +++ b/examples/start-directory.yaml @@ -0,0 +1,41 @@ +session_name: start directory +start_directory: /var/ +windows: + - window_name: should be /var/ + panes: + - shell_command: + - echo "\033c + - it trickles down from session-level" + - echo hello + - window_name: should be /var/log + start_directory: log + panes: + - shell_command: + - echo '\033c + - window start_directory concatenates to session start_directory + - if it is not absolute' + - echo hello + - window_name: should be ~ + start_directory: "~" + panes: + - shell_command: + - 'echo \\033c ~ has precedence. note: remember to quote ~ in YAML' + - echo hello + - window_name: should be /bin + start_directory: /bin + panes: + - echo '\033c absolute paths also have precedence.' + - echo hello + - window_name: should be workspace file's dir + + start_directory: ./ + panes: + - shell_command: + - echo '\033c + - ./ is relative to workspace file location + - ../ will be parent of workspace file + - ./test will be \"test\" dir inside dir of workspace file' + - shell_command: + - echo '\033c + - This way you can load up workspaces from projects and maintain + - relative paths.' diff --git a/examples/suppress-history.json b/examples/suppress-history.json new file mode 100644 index 00000000000..e2ea393b2a5 --- /dev/null +++ b/examples/suppress-history.json @@ -0,0 +1,44 @@ +{ + "windows": [ + { + "panes": [ + "echo 'window in the history!'" + ], + "focus": true, + "suppress_history": false, + "window_name": "appended" + }, + { + "panes": [ + "echo 'window not in the history!'" + ], + "suppress_history": true, + "window_name": "suppressed" + }, + { + "panes": [ + "echo 'session in the history!'" + ], + "window_name": "default" + }, + { + "panes": [ + { + "shell_command": "echo 'command in the history!'", + "suppress_history": false + }, + { + "shell_command": "echo 'command not in the history!'", + "suppress_history": true + }, + { + "shell_command": "echo 'window not in the history!'" + } + ], + "suppress_history": true, + "window_name": "mixed" + } + ], + "suppress_history": false, + "session_name": "suppress" +} diff --git a/examples/suppress-history.yaml b/examples/suppress-history.yaml new file mode 100644 index 00000000000..6e12823f7cc --- /dev/null +++ b/examples/suppress-history.yaml @@ -0,0 +1,29 @@ +session_name: suppress +suppress_history: false +windows: + - window_name: appended + focus: true + suppress_history: false + panes: + - echo "window in the history!" + + - window_name: suppressed + suppress_history: true + panes: + - echo "window not in the history!" + + - window_name: default + panes: + - echo "session in the history!" + + - window_name: mixed + suppress_history: false + panes: + - shell_command: + - echo "command in the history!" + suppress_history: false + - shell_command: + - echo "command not in the history!" + suppress_history: true + - shell_command: + - echo "window in the history!" diff --git a/examples/window-index.json b/examples/window-index.json new file mode 100644 index 00000000000..63df0377f72 --- /dev/null +++ b/examples/window-index.json @@ -0,0 +1,24 @@ +{ + "windows": [ + { + "panes": [ + "echo \"this window's index will be zero\"" + ], + "window_name": "zero" + }, + { + "panes": [ + "echo \"this window's index will be five\"" + ], + "window_index": 5, + "window_name": "five" + }, + { + "panes": [ + "echo \"this window's index will be one\"" + ], + "window_name": "one" + } + ], + "session_name": "Window index example" +} diff --git a/examples/window-index.yaml b/examples/window-index.yaml new file mode 100644 index 00000000000..36da4f07266 --- /dev/null +++ b/examples/window-index.yaml @@ -0,0 +1,12 @@ +session_name: Window index example +windows: + - window_name: zero + panes: + - echo "this window's index will be zero" + - window_name: five + panes: + - echo "this window's index will be five" + window_index: 5 + - window_name: one + panes: + - echo "this window's index will be one" diff --git a/manual/0.8 b/manual/0.8 deleted file mode 100644 index 0550ae7d33d..00000000000 --- a/manual/0.8 +++ /dev/null @@ -1,808 +0,0 @@ -TMUX(1) BSD General Commands Manual TMUX(1) - -NAME - tmux -- terminal multiplexer - -SYNOPSIS - tmux [-28dqUuVv] [-f file] [-L socket-name] [-S socket-path] - [command [flags]] - -DESCRIPTION - tmux is a terminal multiplexer; it enables a number of terminals to be - accessed and controlled from a single terminal. - - tmux runs as a server-client system. A server is created automatically - when necessary and holds a number of sessions, each of which may have a - number of windows linked to it. A window may be split on screen into one - or more panes, each of which is a separate terminal. Any number of - clients may connect to a session, or the server may be controlled by - issuing commands with tmux. Communication takes place through a socket, - by default placed in /tmp. - - The options are as follows: - - -2 Force tmux to assume the terminal supports 256 colours. - - -8 Like -2, indicates the terminal supports 88 colours. - - -d Force tmux to assume the terminal supports default colours. - - -f file Specify an alternative configuration file. By default, - tmux will look for a config file at ~/.tmux.conf. The con- - figuration file is a set of tmux commands which are exe- - cuted in sequence when the server is first started. - - -q Prevent the server sending various information messages, - for example when window flags are altered. - - -L socket-name - tmux stores the server socket in a directory under /tmp; - the default socket is named default. This option allows a - different socket name to be specified, allowing several - independent tmux servers to be run. Unlike -S a full path - is not necessary: the sockets are all created in the same - directory. - - -S socket-path - Specify a full alternative path to the server socket. If - -S is specified, the default socket directory is not used - and any -L flag is ignored. - - -U Unlock the server. - - -u Instruct tmux that the terminal support UTF-8. - - -V Print program version. - - -v Request verbose logging. This option may be specified mul- - tiple times for increasing verbosity. Log messages will be - saved into tmux-client-PID.log and tmux-server-PID.log - files in the current directory, where PID is the pid of the - server or client process. - - command [flags] - This specifies one of a set of commands used to control - tmux, and described in the following sections. If no com- - mand and flags is specified, the new-session command is - assumed. - -QUICK START - To create a new tmux session running vi(1): - - $ tmux new-session vi - - Most commands have a shorter form, known as an alias. For new-session, - this is new: - - $ tmux new vi - - Alternatively, the shortest unambiguous form of a command is accepted. - If there are several options, they are listed: - - $ tmux n - ambiguous command: n, could be: new-session, new-window, next-window - $ - - Within an active session, a new window may be created by typing 'C-b' - (ctrl-b, known as the prefix key) followed by the 'c' key. - - Windows may be navigated with: 'C-b 0' (to select window 0), 'C-b 1' (to - select window 1), and so on; 'C-b n' to select the next window; and 'C-b - p' to select the previous window. - - A session may be detached using 'C-b d' and reattached with: - - $ tmux attach-session - - Typing 'C-b ?' lists the current key bindings in the current window; up - and down may be used to navigate the list or 'Q' to exit from it. - -KEY BINDINGS - tmux may be controlled from an attached client by using a key combination - of a prefix key, 'C-b' (ctrl-b) by default, followed by a command key. - - Some of the default key bindings include: - - 'd' Detach current client. - 'c' Create new window. - 'n' Change to next window in the current session. - 'p' Change to previous window in the current session. - 'l' Move to last (previously selected) window in the current session. - 't' Display a large clock. - '?' List current key bindings. - - A complete list may be obtained with the list-keys command (bound to '?' - by default). Key bindings may be changed with the bind-key and - unbind-key commands. - -HISTORY - tmux maintains a configurable history buffer for each window. By - default, up to 2000 lines are kept, this can be altered with the - history-limit option (see the set-option command below). - -MODES - A tmux window may be in one of several modes. The default permits direct - access to the terminal attached to the window. The others are: - - output mode - This is entered when a command which produces output, such as - list-keys, is executed from a key binding. - - scroll mode - This is entered with the scroll-mode command (bound to '=' by - default) and permits the window history buffer to be inspected. - - copy mode - This permits a section of a window or its history to be copied to - a paste buffer for later insertion into another window. This - mode is entered with the copy-mode command, bound to ['' by - default. - - The keys available depend on whether emacs(1) or vi(1) mode is selected - (see the mode-keys option). The following keys are supported as appro- - priate for the mode: - - Function vi emacs - Start of line 0 or ^ C-a - Clear selection Escape C-g - Copy selection Enter M-w - Cursor down j Down - End of line $ C-e - Cursor left h Left - Next page C-f Page down - Next word w M-f - Previous page C-u Page up - Previous word b M-b - Quit mode q Escape - Cursor right l Right - Start selection Space C-Space - Cursor up k Up - -BUFFERS - tmux maintains a stack of paste buffers for each session. Up to the - value of the buffer-limit option are kept; when a new buffer is added, - the buffer at the bottom of the stack is removed. Buffers may be added - using copy-mode or the set-buffer command, and pasted into a window using - the paste-buffer command. - -PANES AND LAYOUTS - Each window displayed by tmux may be split into one or more panes; each - pane takes up a certain area of the display and is a separate terminal. - A window may be split into panes using the split-window command. - - Panes are numbered beginning from zero; in horizontal layouts zero is the - leftmost pane and in vertical the topmost. - - Panes may be arranged using several layouts. The layout may be cycled - with the next-layout command (bound to 'C-space' by default), the current - pane may be changed with the up-pane and down-pane commands and the - rotate-window and swap-pane commands may be used to swap panes without - changing the window layout. - - The following layouts are supported: - - manual Manual layout splits windows vertically (running across); only - with this layout may panes be resized using the resize-pane-up - and resize-pane-down commands. - - active-only - Only the active pane is shown - all other panes are hidden. - - even-horizontal - Panes are spread out evenly from left to right across the window. - - even-vertical - Panes are spread evenly from top to bottom. - - left-vertical - A large (81 column) pane is shown on the left of the window and - the remaining panes are spread from top to bottom in the leftover - space to the right. - -COMMANDS - This section contains a list of the commands supported by tmux. Most - commands accept the optional -t argument with one of target-client, - target-session or target-window. These specify the client, session or - window which a command should affect. target-client is the name of the - pty(4) file to which the client is connected, for example /dev/ttyp1. - Clients may be listed with the list-clients command. - - target-session is either the name of a session (as listed by the - list-sessions command); or the name of a client as for target-client, in - this case, the session attached to the client is used. An fnmatch(3) - pattern may be used to match the session name. If a session is omitted - when required, tmux attempts to use the current session; if no current - session is available, the most recently created is chosen. If no client - is specified, the current client is chosen, if possible, or an error is - reported. - - target-window specifies a window in the form session:index, for example - mysession:1. The session is in the same form as for target-session. - session, index or both may be omitted. If session is omitted, the same - rules as for target-session are followed; if index is not present, the - current window for the given session is used. When the argument does not - contain a colon (:), tmux first attempts to parse it as window index; if - that fails, an attempt is made to match a session or client name. - - Multiple commands may be specified together as part of a command - sequence. Each command should be separated by spaces and a semicolon (' - ; '); commands are executed sequentially from left to right. A literal - semicolon may be included by escaping it with a backslash (for example, - when specifying a command sequence to bind-key). - - Examples include: - - refresh-client -t/dev/ttyp2 - - rename-session -tfirst newname - - set-window-option -t:0 monitor-activity on - - new-window ; split-window -d - - bind-key D detach-client \; lock-server - - The following commands are available: - - attach-session [-d] [-t target-session] - (alias: attach) - Create a new client in the current terminal and attach it to a - session. If -d is specified, any other clients attached to the - session are detached. - - If no server is started, attach-session will attempt to start it; - this will fail unless sessions are created in the configuration - file. - - bind-key [-r] key command [arguments] - (alias: bind) - Bind key key to command. Keys may be specified prefixed with - 'C-' or '^' for ctrl keys, or 'M-' for alt (meta) keys. The -r - flag indicates this key may repeat, see the repeat-time option. - - break-pane [-d] [-p pane-index] [-t target-window] - Break the current pane off from its containing window to make it - the only pane in a new window. If -d is given, the new window - does not become the current window. - - choose-session [-t target-window] - Put a window into session choice mode, where the session for the - current client may be selected interactively from a list. This - command works only from inside tmux. - - choose-window [-t target-window] - Put a window into window choice mode, where the window for the - session attached to the current client may be selected interac- - tively from a list. This command works only from inside tmux. - - clock-mode [-t target-window] - Display a large clock. - - command-prompt [-t target-client] [template] - Open the command prompt in a client. This may be used from - inside tmux to execute commands interactively. If template is - specified, it is used as the command; any %% in the template will - be replaced by what is entered at the prompt. - - copy-buffer [-a src-index] [-b dst-index] [-s src-session] [-t - dst-session] - (alias: copyb) - Copy a session paste buffer to another session. If no sessions - are specified, the current one is used instead. - - copy-mode [-u] [-t target-window] - Enter copy mode. The -u option scrolls one page up. - - delete-buffer [-b buffer-index] [-t target-session] - (alias: deleteb) - Delete the buffer at buffer-index, or the top buffer if not spec- - ified. - - detach-client [-t target-client] - (alias: detach) - Detach the current client if bound to a key, or the specified - client with -t. - - down-pane [-p pane-index] [-t target-window] - (alias: downp) - Move down a pane. - - find-window [-t target-window] match-string - (alias: findw) - Search for match-string in window names, titles, and visible con- - tent (but not history). If only one window is matched, it'll be - automatically selected, otherwise a choice list is shown. This - command only works from inside tmux. - - has-session [-t target-session] - (alias: has) - Report an error and exit with 1 if the specified session does not - exist. If it does exist, exit with 0. - - kill-pane [-p pane-index] [-t target-window] - (alias: killp) - Destroy the given pane. - - kill-server - Kill the tmux server and clients and destroy all sessions. - - kill-session [-t target-session] - Destroy the given session, closing any windows linked to it and - no other sessions, and detaching all clients attached to it. - - kill-window [-t target-window] - (alias: killw) - Kill the current window or the window at target-window, removing - it from any sessions to which it is linked. - - last-window [-t target-session] - (alias: last) - Select the last (previously selected) window. If no - target-session is specified, select the last window of the cur- - rent session. - - link-window [-dk] [-s src-window] [-t dst-window] - (alias: linkw) - Link the window at src-window to the specified dst-window. If - dst-window is specified and no such window exists, the src-window - is linked there. If -k is given and dst-window exists, it is - killed, otherwise an error is generated. If -d is given, the - newly linked window is not selected. - - list-buffers [-t target-session] - (alias: lsb) - List the buffers in the given session. - - list-clients - (alias: lsc) - List all clients attached to the server. - - list-commands - (alias: lscm) - List the syntax of all commands supported by tmux. - - list-keys - (alias: lsk) - List all key bindings. - - list-sessions - (alias: ls) - List all sessions managed by the server. - - list-windows [-t target-session] - (alias: lsw) - List windows in the current session or in target-session. - - load-buffer [-b buffer-index] [-t target-session] path - (alias: loadb) - Load the contents of the specified paste buffer from path. - - lock-server - (alias: lock) - Lock the server until a password is entered. - - move-window [-d] [-s src-window] [-t dst-window] - (alias: movew) - This is similar to link-window, except the window at src-window - is moved to dst-window. - - new-session [-d] [-n window-name] [-s session-name] [command] - (alias: new) - Create a new session with name session-name. The new session is - attached to the current terminal unless -d is given. window-name - and command are the name of and command to execute in the initial - window. - - new-window [-d] [-n window-name] [-t target-window] [command] - (alias: neww) - Create a new window. If -d is given, the session does not make - the new window the current window. target-window represents the - window to be created. command is the command to execute. If - command is not specified, the default command is used. - - The TERM environment variable must be set to ``screen'' for all - programs running inside tmux. New windows will automatically - have ``TERM=screen'' added to their environment, but care must be - taken not to reset this in shell start-up files. - - next-layout [-t target-window] - (alias: nextl) - Move a window to the next layout and rearrange the panes to fit. - - next-window [-t target-session] - (alias: next) - Move to the next window in the session. - - paste-buffer [-d] [-b buffer-index] [-t target-window] - (alias: pasteb) - Insert the contents of a paste buffer into the current window. - - previous-window [-t target-session] - (alias: prev) - Move to the previous window in the session. - - refresh-client [-t target-client] - (alias: refresh) - Refresh the current client if bound to a key, or a single client - if one is given with -t. - - rename-session [-t target-session] new-name - (alias: rename) - Rename the session to new-name. - - rename-window [-t target-window] new-name - (alias: renamew) - Rename the current window, or the window at target-window if - specified, to new-name. - - resize-pane-down [-p pane-index] [-t target-window] [adjustment] - (alias: resizep-down) - - resize-pane-up [-p pane-index] [-t target-window] [adjustment] - (alias: resizep-up) - Resize a pane. The adjustment is given in lines (the default is - 1). - - respawn-window [-k] [-t target-window] [command] - (alias: respawnw) - Reactive a window in which the command has exited (see the - remain-on-exit window option). If command is not given, the com- - mand used when the window was created is executed. The window - must be already inactive, unless -k is given, in which case any - existing command is killed. - - rotate-window [-DU] [-t target-window] - (alias: rotatew) - Rotate the positions of the panes within a window, either upward - (numerically lower) with -U or downward (numerically higher). - - save-buffer [-a] [-b buffer-index] [-t target-session] path - (alias: saveb) - Save the contents of the specified paste buffer to path. The -a - option appends to rather than overwriting the file. - - scroll-mode [-u] [-t target-window] - Enter scroll mode. The -u has the same meaning as in the - copy-mode command. - - select-pane [-p pane-index] [-t target-window] - (alias: selectp) - Make pane pane-index the active pane in window target-window. - - select-prompt [-t target-client] - Open a prompt inside target-client allowing a window index to be - entered interactively. - - select-window [-t target-window] - (alias: selectw) - Select the window at target-window. - - send-keys [-t target-window] key ... - (alias: send) - Send a key or keys to a window. Each argument key is the name of - the key (such as 'C-a' or 'npage' ) to send; if the string is not - recognised as a key, it is sent as a series of characters. All - arguments are sent sequentially from first to last. - - send-prefix [-t target-window] - Send the prefix key to a window as if it was pressed. - - server-info - (alias: info) - Show server information and terminal details. - - set-buffer [-b buffer-index] [-t target-session] data - (alias: setb) - Set the contents of the specified buffer to data. - - set-option [-gu] [-t target-session] option value - (alias: set) - Set an option. If -g is specified, the option is set as a global - option. Global options apply to all sessions which don't have - the option explicitly set. If -g is not used, the option applies - only to target-session. The -u flag unsets an option, so a ses- - sion inherits the option from the global options - it is not pos- - sible to unset a global option. - - Possible options are: - - bell-action [any | none | current] - Set action on window bell. any means a bell in any win- - dow linked to a session causes a bell in the current win- - dow of that session, none means all bells are ignored and - current means only bell in windows other than the current - window are ignored. - - buffer-limit number - Set the number of buffers kept for each session; as new - buffers are added to the top of the stack, old ones are - removed from the bottom if necessary to maintain this - maximum length. - - default-command command - Set the command used for new windows (if not specified - when the window is created) to command. The default is - ``exec $SHELL''. - - default-path path - Set the default working directory for processes created - from keys, or interactively from the prompt. The default - is the current working directory when the server is - started. - - history-limit lines - Set the maximum number of lines held in window history. - This setting applies only to new windows - existing win- - dow histories are not resized and retain the limit at the - point they were created. - - lock-after-time number - Lock the server after number seconds of inactivity. The - default is off (set to 0). This has no effect as a ses- - sion option; it must be set as a global option using -g. - - message-attr attributes - Set status line message attributes, where attributes is - either default or a comma-delimited list of one or more - of: bright (or bold), dim, underscore, blink, reverse, - hidden, or italics. - - message-bg colour - Set status line message background colour, where colour - is one of: black, red, green, yellow, blue, magenta, - cyan, white or default. - - message-fg colour - Set status line message foreground colour. - - prefix key - Set the current prefix key. - - repeat-time number - Allow multiple commands to be entered without pressing - the prefix-key again in the specified number milliseconds - (the default is 500). Whether a key repeats may be set - when it is bound using the -r flag to bind-key. Repeat - is enabled for the default keys of the up-pane, - down-pane, resize-pane-up, and resize-pane-down commands. - - set-remain-on-exit [on | off] - Set the remain-on-exit window option for any windows - first created in this session. - - set-titles [on | off] - Attempt to set the window title using the \e]2;...\007 - xterm code and the terminal appears to be an xterm. This - option is enabled by default. Note that elinks(1) will - only attempt to set the window title if the STY environ- - ment variable is set. - - status [on | off] - Show or hide the status line. - - status-attr attributes - Set status line attributes. - - status-bg colour - Set status line background colour. - - status-fg colour - Set status line foreground colour. - - status-interval interval - Update the status bar every interval seconds. By - default, updates will occur every 15 seconds. A setting - of zero disables redrawing at interval. - - status-keys [vi | emacs] - Use vi(1) - or emacs(1) -style key bindings in the status - line, for example at the command prompt. Defaults to - emacs. - - status-left string - Display string to the left of the status bar. string - will be passed through strftime(3) before being used. By - default, the session name is shown. string may contain - any of the following special character pairs: - - Character pair Replaced with - #(command) First line of command's output - #H Hostname of local host - #S Session name - #T Current window title - ## A literal '#' - - Where appropriate, these may be prefixed with a number to - specify the maximum length, for example '#24T'. - - status-left-length length - Set the maximum length of the left component of the sta- - tus bar. The default is 10. - - status-right string - Display string to the right of the status bar. By - default, the date and time will be shown. As with - status-left, string will be passed to strftime(3) and - character pairs are replaced. - - status-right-length length - Set the maximum length of the right component of the sta- - tus bar. The default is 40. - - set-password [-c] password - (alias: pass) - Set the server password. If the -c option is given, a pre- - encrypted password may be specified. By default, the password is - blank, thus any entered password will be accepted when unlocking - the server (see the lock-server command). To prevent variable - expansion when an encrypted password is read from a configuration - file, enclose it in single quotes ('). - - set-window-option [-gu] [-t target-window] option value - (alias: setw) - Set a window-specific option. The -g and -u flags work similarly - to the set-option command. - - Supported options are: - - aggressive-resize [on | off] - Aggressively resize the chosen window. This means that - tmux will resize the window to the size of the smallest - session for which it is the current window, rather than - the smallest session to which it is attached. The window - may resize when the current window is changed on another - sessions; this option is good for full-screen programs - which support SIGWINCH and poor for interactive programs - such as shells. - - automatic-rename [on | off] - Control automatic window renaming. When this setting is - enabled, tmux will attempt - on supported platforms - to - rename the window to reflect the command currently run- - ning in it. This flag is automatically disabled for an - individual window when a name is specified at creation - with new-window or new-session, or later with - rename-window. It may be switched off globally with: - - set-window-option -g automatic-rename off - - clock-mode-colour colour - Set clock colour. - - clock-mode-style [12 | 24] - Set clock hour format. - - force-height height - - force-width width - Prevent tmux from resizing a window to greater than width - or height. A value of zero restores the default unlim- - ited setting. - - mode-attr attributes - Set window modes attributes. - - mode-bg colour - Set window modes background colour. - - mode-fg colour - Set window modes foreground colour. - - mode-keys [vi | emacs] - Use vi(1) - or emacs(1) -style key bindings in scroll and - copy modes. Key bindings default to emacs. - - monitor-activity [on | off] - Monitor for activity in the window. Windows with activ- - ity are highlighted in the status line. - - remain-on-exit [on | off] - A window with this flag set is not destroyed when the - program running in it exits. The window may be reacti- - vated with the respawn-window command. - - utf8 [on | off] - Instructs tmux to expect UTF-8 sequences to appear in - this window. - - window-status-attr attributes - Set status line attributes for a single window. - - window-status-bg colour - Set status line background colour for a single window. - - window-status-fg colour - Set status line foreground colour for a single window. - - xterm-keys [on | off] - If this option is set, tmux will generate xterm(1) -style - function key sequences; these have a number included to - indicate modifiers such as shift, meta or ctrl. - - show-buffer [-b buffer-index] [-t target-session] - (alias: showb) - Display the contents of the specified buffer. - - show-options [-t target-session] option value - (alias: show) - Show the currently set options. If a target-session is speci- - fied, the options for that session are shown; otherwise, the - global options are listed. - - show-window-options [-t target-window] option value - (alias: showw) - List the current options for the given window. - - source-file path - (alias: source) - Execute commands from path. - - split-window [-d] [-l lines | -p percentage] [-t target-window] [command] - (alias: splitw) - Creates a new window by splitting it vertically. The -l and -p - options specify the size of the new window in lines, or as a per- - centage, respectively. All other options have the same meaning - as in the new-window command. - - A few notes with regard to panes: - 1. If attempting to split a window with less than eight lines, - an error will be shown. - 2. If the window is resized, as many panes are shown as can fit - without reducing them below four lines. - 3. The minimum pane size is four lines (including the separator - line). - 4. The panes are indexed from top (0) to bottom, with no num- - bers skipped. - - start-server - (alias: start) - Start the tmux server, if not already running, without creating - any sessions. - - suspend-client [-c -target-client] - (alias: suspendc) - Suspend a client by sending SIGTSTP (tty stop). - - swap-pane [-dDU] [-p src-index] [-t target-window] [-q dst-index] - (alias: swapp) - Swap two panes within a window. If -U is used, the pane is - swapped with the pane above (before it numerically); -D swaps - with the pane below (the next numerically); or dst-index may be - give to swap with a specific pane. - - swap-window [-d] [-s src-window] [-t dst-window] - (alias: swapw) - This is similar to link-window, except the source and destination - windows are swapped. It is an error if no window exists at - src-window. - - switch-client [-c target-client -t target-session] - (alias: switchc) - Switch the current session for client target-client to - target-session. - - unbind-key key - (alias: unbind) - Unbind the key bound to key. - - unlink-window [-t target-window] - (alias: unlinkw) - Unlink target-window. A window may be unlinked only if it is - linked to multiple sessions - windows may not be linked to no - sessions. - - up-pane [-p pane-index] [-t target-window] - (alias: upp) - Move up a pane. - -FILES - ~/.tmux.conf - default tmux configuration file - -SEE ALSO - pty(4) - -AUTHORS - Nicholas Marriott - -BSD April 20, 2009 BSD diff --git a/manual/0.9 b/manual/0.9 deleted file mode 100644 index e0fb1eed6e6..00000000000 --- a/manual/0.9 +++ /dev/null @@ -1,917 +0,0 @@ -TMUX(1) BSD General Commands Manual TMUX(1) - -NAME - tmux -- terminal multiplexer - -SYNOPSIS - tmux [-28dqUuv] [-f file] [-L socket-name] [-S socket-path] - [command [flags]] - -DESCRIPTION - tmux is a terminal multiplexer: it enables a number of terminals to be - accessed and controlled from a single terminal. - - tmux runs as a server-client system. A server is created automatically - when necessary and holds a number of sessions, each of which may have a - number of windows linked to it. A window may be split on screen into one - or more panes, each of which is a separate terminal. Any number of - clients may connect to a session, or the server may be controlled by - issuing commands with tmux. Communication takes place through a socket, - by default placed in /tmp. - - The options are as follows: - - -2 Force tmux to assume the terminal supports 256 colours. - - -8 Like -2, but indicates that the terminal supports 88 - colours. - - -d Force tmux to assume the terminal supports default colours. - - -f file Specify an alternative configuration file. By default, - tmux will look for a config file at ~/.tmux.conf. The con- - figuration file is a set of tmux commands which are exe- - cuted in sequence when the server is first started. - - -L socket-name - tmux stores the server socket in a directory under /tmp; - the default socket is named default. This option allows a - different socket name to be specified, allowing several - independent tmux servers to be run. Unlike -S a full path - is not necessary: the sockets are all created in the same - directory. - - If the socket is accidentally removed, the SIGUSR1 signal - may be sent to the tmux server process to recreate it. - - -q Prevent the server sending various informational messages, - for example when window flags are altered. - - -S socket-path - Specify a full alternative path to the server socket. If - -S is specified, the default socket directory is not used - and any -L flag is ignored. - - -U Unlock the server. - - -u tmux attempts to guess if the terminal is likely to support - UTF-8 by checking the first of the LC_ALL, LC_CTYPE and - LANG environment variables to be set for the string - "UTF-8". This is not always correct: the -u flag explic- - itly informs tmux that UTF-8 is supported. - - -v Request verbose logging. This option may be specified mul- - tiple times for increasing verbosity. Log messages will be - saved into tmux-client-PID.log and tmux-server-PID.log - files in the current directory, where PID is the PID of the - server or client process. - - command [flags] - This specifies one of a set of commands used to control - tmux, as described in the following sections. If no com- - mand and flags are specified, the new-session command is - assumed. - -QUICK START - To create a new tmux session running vi(1): - - $ tmux new-session vi - - Most commands have a shorter form, known as an alias. For new-session, - this is new: - - $ tmux new vi - - Alternatively, the shortest unambiguous form of a command is accepted. - If there are several options, they are listed: - - $ tmux n - ambiguous command: n, could be: new-session, new-window, next-window - - Within an active session, a new window may be created by typing 'C-b c' - (Ctrl followed by the 'b' key followed by the 'c' key). - - Windows may be navigated with: 'C-b 0' (to select window 0), 'C-b 1' (to - select window 1), and so on; 'C-b n' to select the next window; and 'C-b - p' to select the previous window. - - A session may be detached using 'C-b d' and reattached with: - - $ tmux attach-session - - Typing 'C-b ?' lists the current key bindings in the current window; up - and down may be used to navigate the list or 'q' to exit from it. - - Commands to be run when the tmux server is started may be placed in the - ~/.tmux.conf configuration file. Common examples include: - - Changing the default prefix key: - - set-option -g prefix C-a - unbind-key C-b - bind-key C-a send-prefix - - Turning the status line off, or changing its colour: - - set-option -g status off - set-option -g status-bg blue - - Setting other options, such as the default command, or locking after 30 - minutes of inactivity: - - set-option -g default-command "exec /bin/ksh" - set-option -g lock-after-time 1800 - - Creating new key bindings: - - bind-key b set-option status - bind-key / command-prompt "split-window 'exec man %%'" - -KEY BINDINGS - tmux may be controlled from an attached client by using a key combination - of a prefix key, 'C-b' (Ctrl-b) by default, followed by a command key. - - Some of the default key bindings include: - - c Create new window. - d Detach current client. - l Move to last (previously selected) window in the current ses- - sion. - n Change to next window in the current session. - p Change to previous window in the current session. - t Display a large clock. - ? List current key bindings. - - A complete list may be obtained with the list-keys command (bound to '?' - by default). Key bindings may be changed with the bind-key and - unbind-key commands. - -HISTORY - tmux maintains a configurable history buffer for each window. By - default, up to 2000 lines are kept; this can be altered with the - history-limit option (see the set-option command below). - -MODES - A tmux window may be in one of several modes. The default permits direct - access to the terminal attached to the window. The others are: - - output mode - This is entered when a command which produces output, such as - list-keys, is executed from a key binding. - - scroll mode - This is entered with the scroll-mode command (bound to '=' by - default) and permits the window history buffer to be inspected. - - copy mode - This permits a section of a window or its history to be copied to - a paste buffer for later insertion into another window. This - mode is entered with the copy-mode command, bound to ['' by - default. - - The keys available depend on whether emacs or vi mode is selected (see - the mode-keys option). The following keys are supported as appropriate - for the mode: - - Function vi emacs - Start of line 0 or ^ C-a - Clear selection Escape C-g - Copy selection Enter M-w - Cursor down j Down - End of line $ C-e - Cursor left h Left - Next page C-f Page down - Next word w M-f - Previous page C-u Page up - Previous word b M-b - Quit mode q Escape - Cursor right l Right - Start selection Space C-Space - Cursor up k Up - Paste buffer p C-y - - The paste buffer key pastes the first line from the top paste buffer on - the stack. - -BUFFERS - tmux maintains a stack of paste buffers for each session. Up to the - value of the buffer-limit option are kept; when a new buffer is added, - the buffer at the bottom of the stack is removed. Buffers may be added - using copy-mode or the set-buffer command, and pasted into a window using - the paste-buffer command. - -PANES AND LAYOUTS - Each window displayed by tmux may be split into one or more panes; each - pane takes up a certain area of the display and is a separate terminal. - A window may be split into panes using the split-window command. - - Panes are numbered beginning from zero; in horizontal layouts zero is the - leftmost pane and in vertical the topmost. - - Panes may be arranged using several layouts. The layout may be cycled - with the next-layout command (bound to 'C-space' by default), the current - pane may be changed with the up-pane and down-pane commands and the - rotate-window and swap-pane commands may be used to swap panes without - changing the window layout. - - The following layouts are supported: - - active-only - Only the active pane is shown - all other panes are hidden. - - even-horizontal - Panes are spread out evenly from left to right across the window. - - even-vertical - Panes are spread evenly from top to bottom. - - main-horizontal - A large (main) pane is shown at the top of the window and the - remaining panes are spread from left to right in the leftover - space at the bottom. Use the main-pane-height window option to - specify the height of the top pane. - - main-vertical - Similar to main-horizontal but the large pane is placed on the - left and the others spread from top to bottom along the right. - See the main-pane-width window option. - - manual Manual layout splits windows vertically (running across); only - with this layout may panes be resized using the resize-pane com- - mand. - -STATUS LINE - tmux includes an optional status line which is displayed in the bottom - line of each terminal. By default, the status line is enabled (it may be - disabled with the status session option) and contains, from left-to- - right: the name of the current session in square brackets; the window - list; the current window title in double quotes; and the time and date. - - The status line is made of three parts: configurable left and right sec- - tions (which may contain dynamic content such as the time or output from - a shell command, see the status-left, status-left-length, status-right, - and status-right-length options below), and a central window list. The - window list shows the index, name and (if any) flag of the windows - present in the current session in ascending numerical order. The flag is - one of the following symbols appended to the window name: - - Symbol Meaning - * Denotes the current window. - - Marks the last window (previously selected). - # Window is monitored and activity has been detected. - ! A bell has occurred in the window. - + Window is monitored for content and it has appeared. - - The # symbol relates to the monitor-activity and + to the monitor-content - window options. The window name is printed in inverted colours if an - alert (bell, activity or content) is present. - - The colour and attributes of the status line may be configured, the - entire status line using the status-attr, status-fg and status-bg session - options and individual windows using the window-status-attr, - window-status-fg and window-status-bg window options. - - The status line is automatically refreshed at interval if it has changed, - the interval may be controlled with the status-interval session option. - -COMMANDS - This section contains a list of the commands supported by tmux. Most - commands accept the optional -t argument with one of target-client, - target-session or target-window. These specify the client, session or - window which a command should affect. target-client is the name of the - pty(4) file to which the client is connected, for example /dev/ttyp1. - Clients may be listed with the list-clients command. - - target-session is either the name of a session (as listed by the - list-sessions command) or the name of a client, target-client, in which - case the session attached to the client is used. An fnmatch(3) pattern - may be used to match the session name. If a session is omitted when - required, tmux attempts to use the current session; if no current session - is available, the most recently created is chosen. If no client is spec- - ified, the current client is chosen, if possible, or an error is - reported. - - target-window specifies a window in the form session:index, for example - mysession:1. The session is in the same form as for target-session. - session, index or both may be omitted. If session is omitted, the same - rules as for target-session are followed; if index is not present, the - current window for the given session is used. When the argument does not - contain a colon, tmux first attempts to parse it as window index; if that - fails, an attempt is made to match a session or client name. - - Multiple commands may be specified together as part of a command - sequence. Each command should be separated by spaces and a semicolon; - commands are executed sequentially from left to right. A literal semi- - colon may be included by escaping it with a backslash (for example, when - specifying a command sequence to bind-key). - - Examples include: - - refresh-client -t/dev/ttyp2 - - rename-session -tfirst newname - - set-window-option -t:0 monitor-activity on - - new-window ; split-window -d - - bind-key D detach-client \; lock-server - - The following commands are available: - - attach-session [-d] [-t target-session] - (alias: attach) - Create a new client in the current terminal and attach it to a - session. If -d is specified, any other clients attached to the - session are detached. - - If no server is started, attach-session will attempt to start it; - this will fail unless sessions are created in the configuration - file. - - bind-key [-r] key command [arguments] - (alias: bind) - Bind key key to command. Keys may be specified prefixed with - 'C-' or '^' for Ctrl keys, or 'M-' for Alt (meta) keys. The -r - flag indicates this key may repeat, see the repeat-time option. - - break-pane [-d] [-p pane-index] [-t target-window] - (alias: breakp) - Break the current pane off from its containing window to make it - the only pane in a new window. If -d is given, the new window - does not become the current window. - - choose-session [-t target-window] - Put a window into session choice mode, where the session for the - current client may be selected interactively from a list. This - command works only from inside tmux. - - choose-window [-t target-window] - Put a window into window choice mode, where the window for the - session attached to the current client may be selected interac- - tively from a list. This command works only from inside tmux. - - clear-history [-p pane-index] [-t target-window] - (alias: clearhist) - Remove and free the history for the specified pane. - - clock-mode [-t target-window] - Display a large clock. - - command-prompt [-t target-client] [template] - Open the command prompt in a client. This may be used from - inside tmux to execute commands interactively. If template is - specified, it is used as the command; any %% in the template will - be replaced by what is entered at the prompt. - - confirm-before [-t target-client] command - (alias: confirm) - Ask for confirmation before executing command. This command - works only from inside tmux. - - copy-buffer [-a src-index] [-b dst-index] [-s src-session] [-t - dst-session] - (alias: copyb) - Copy a session paste buffer to another session. If no sessions - are specified, the current one is used instead. - - copy-mode [-u] [-t target-window] - Enter copy mode. The -u option scrolls one page up. - - delete-buffer [-b buffer-index] [-t target-session] - (alias: deleteb) - Delete the buffer at buffer-index, or the top buffer if not spec- - ified. - - detach-client [-t target-client] - (alias: detach) - Detach the current client if bound to a key, or the specified - client with -t. - - down-pane [-p pane-index] [-t target-window] - (alias: downp) - Move down a pane. - - find-window [-t target-window] match-string - (alias: findw) - Search for the fnmatch(3) pattern match-string in window names, - titles, and visible content (but not history). If only one win- - dow is matched, it'll be automatically selected, otherwise a - choice list is shown. This command only works from inside tmux. - - has-session [-t target-session] - (alias: has) - Report an error and exit with 1 if the specified session does not - exist. If it does exist, exit with 0. - - kill-pane [-p pane-index] [-t target-window] - (alias: killp) - Destroy the given pane. - - kill-server - Kill the tmux server and clients and destroy all sessions. - - kill-session [-t target-session] - Destroy the given session, closing any windows linked to it and - no other sessions, and detaching all clients attached to it. - - kill-window [-t target-window] - (alias: killw) - Kill the current window or the window at target-window, removing - it from any sessions to which it is linked. - - last-window [-t target-session] - (alias: last) - Select the last (previously selected) window. If no - target-session is specified, select the last window of the cur- - rent session. - - link-window [-dk] [-s src-window] [-t dst-window] - (alias: linkw) - Link the window at src-window to the specified dst-window. If - dst-window is specified and no such window exists, the src-window - is linked there. If -k is given and dst-window exists, it is - killed, otherwise an error is generated. If -d is given, the - newly linked window is not selected. - - list-buffers [-t target-session] - (alias: lsb) - List the buffers in the given session. - - list-clients - (alias: lsc) - List all clients attached to the server. - - list-commands - (alias: lscm) - List the syntax of all commands supported by tmux. - - list-keys - (alias: lsk) - List all key bindings. - - list-sessions - (alias: ls) - List all sessions managed by the server. - - list-windows [-t target-session] - (alias: lsw) - List windows in the current session or in target-session. - - load-buffer [-b buffer-index] [-t target-session] path - (alias: loadb) - Load the contents of the specified paste buffer from path. - - lock-server - (alias: lock) - Lock the server until a password is entered. - - move-window [-d] [-s src-window] [-t dst-window] - (alias: movew) - This is similar to link-window, except the window at src-window - is moved to dst-window. - - new-session [-d] [-n window-name] [-s session-name] [command] - (alias: new) - Create a new session with name session-name. The new session is - attached to the current terminal unless -d is given. window-name - and command are the name of and command to execute in the initial - window. - - new-window [-d] [-n window-name] [-t target-window] [command] - (alias: neww) - Create a new window. If -d is given, the session does not make - the new window the current window. target-window represents the - window to be created. command is the command to execute. If - command is not specified, the default command is used. - - The TERM environment variable must be set to ``screen'' for all - programs running inside tmux. New windows will automatically - have ``TERM=screen'' added to their environment, but care must be - taken not to reset this in shell start-up files. - - next-layout [-t target-window] - (alias: nextl) - Move a window to the next layout and rearrange the panes to fit. - - next-window [-a] [-t target-session] - (alias: next) - Move to the next window in the session. If -a is used, move to - the next window with a bell, activity or content alert. - - paste-buffer [-d] [-b buffer-index] [-t target-window] - (alias: pasteb) - Insert the contents of a paste buffer into the current window. - - previous-window [-a] [-t target-session] - (alias: prev) - Move to the previous window in the session. With -a, move to the - previous window with a bell, activity or content alert. - - refresh-client [-t target-client] - (alias: refresh) - Refresh the current client if bound to a key, or a single client - if one is given with -t. - - rename-session [-t target-session] new-name - (alias: rename) - Rename the session to new-name. - - rename-window [-t target-window] new-name - (alias: renamew) - Rename the current window, or the window at target-window if - specified, to new-name. - - resize-pane [-DU] [-p pane-index] [-t target-window] [adjustment] - (alias: resizep) - Resize a pane, upward with -U (the default) or downward with -D. - The adjustment is given in lines (the default is 1). - - respawn-window [-k] [-t target-window] [command] - (alias: respawnw) - Reactive a window in which the command has exited (see the - remain-on-exit window option). If command is not given, the com- - mand used when the window was created is executed. The window - must be already inactive, unless -k is given, in which case any - existing command is killed. - - rotate-window [-DU] [-t target-window] - (alias: rotatew) - Rotate the positions of the panes within a window, either upward - (numerically lower) with -U or downward (numerically higher). - - save-buffer [-a] [-b buffer-index] [-t target-session] path - (alias: saveb) - Save the contents of the specified paste buffer to path. The -a - option appends to rather than overwriting the file. - - scroll-mode [-u] [-t target-window] - Enter scroll mode. The -u has the same meaning as in the - copy-mode command. - - select-layout [-t target-window] layout-name - (alias: selectl) - Choose a specific layout for a window. - - select-pane [-p pane-index] [-t target-window] - (alias: selectp) - Make pane pane-index the active pane in window target-window. - - select-prompt [-t target-client] - Open a prompt inside target-client allowing a window index to be - entered interactively. - - select-window [-t target-window] - (alias: selectw) - Select the window at target-window. - - send-keys [-t target-window] key ... - (alias: send) - Send a key or keys to a window. Each argument key is the name of - the key (such as 'C-a' or 'npage' ) to send; if the string is not - recognised as a key, it is sent as a series of characters. All - arguments are sent sequentially from first to last. - - send-prefix [-t target-window] - Send the prefix key to a window as if it was pressed. - - server-info - (alias: info) - Show server information and terminal details. - - set-buffer [-b buffer-index] [-t target-session] data - (alias: setb) - Set the contents of the specified buffer to data. - - set-option [-gu] [-t target-session] option value - (alias: set) - Set an option. If -g is specified, the option is set as a global - option. Global options apply to all sessions which don't have - the option explicitly set. If -g is not used, the option applies - only to target-session. The -u flag unsets an option, so a ses- - sion inherits the option from the global options - it is not pos- - sible to unset a global option. - - Possible options are: - - bell-action [any | none | current] - Set action on window bell. any means a bell in any win- - dow linked to a session causes a bell in the current win- - dow of that session, none means all bells are ignored and - current means only bell in windows other than the current - window are ignored. - - buffer-limit number - Set the number of buffers kept for each session; as new - buffers are added to the top of the stack, old ones are - removed from the bottom if necessary to maintain this - maximum length. - - default-command command - Set the command used for new windows (if not specified - when the window is created) to command. The default is - an empty string, which instructs tmux to create a login - shell using the SHELL environment variable or, if it is - unset, the user's shell returned by getpwuid(3). - - default-path path - Set the default working directory for processes created - from keys, or interactively from the prompt. The default - is the current working directory when the server is - started. - - history-limit lines - Set the maximum number of lines held in window history. - This setting applies only to new windows - existing win- - dow histories are not resized and retain the limit at the - point they were created. - - lock-after-time number - Lock the server after number seconds of inactivity. The - default is off (set to 0). This has no effect as a ses- - sion option; it must be set as a global option using -g. - - message-attr attributes - Set status line message attributes, where attributes is - either default or a comma-delimited list of one or more - of: bright (or bold), dim, underscore, blink, reverse, - hidden, or italics. - - message-bg colour - Set status line message background colour, where colour - is one of: black, red, green, yellow, blue, magenta, - cyan, white or default. - - message-fg colour - Set status line message foreground colour. - - prefix key - Set the current prefix key. - - repeat-time number - Allow multiple commands to be entered without pressing - the prefix-key again in the specified number milliseconds - (the default is 500). Whether a key repeats may be set - when it is bound using the -r flag to bind-key. Repeat - is enabled for the default keys of the up-pane, - down-pane, resize-pane-up, and resize-pane-down commands. - - set-remain-on-exit [on | off] - Set the remain-on-exit window option for any windows - first created in this session. - - set-titles [on | off] - Attempt to set the window title using the \e]2;...\007 - xterm code and the terminal appears to be an xterm. This - option is off by default. Note that elinks will only - attempt to set the window title if the STY environment - variable is set. - - status [on | off] - Show or hide the status line. - - status-attr attributes - Set status line attributes. - - status-bg colour - Set status line background colour. - - status-fg colour - Set status line foreground colour. - - status-interval interval - Update the status bar every interval seconds. By - default, updates will occur every 15 seconds. A setting - of zero disables redrawing at interval. - - status-keys [vi | emacs] - Use vi or emacs-style key bindings in the status line, - for example at the command prompt. Defaults to emacs. - - status-left string - Display string to the left of the status bar. string - will be passed through strftime(3) before being used. By - default, the session name is shown. string may contain - any of the following special character pairs: - - Character pair Replaced with - #(command) First line of command's output - #H Hostname of local host - #S Session name - #T Current window title - ## A literal '#' - - Where appropriate, these may be prefixed with a number to - specify the maximum length, for example '#24T'. - - By default, UTF-8 in string is not interpreted, to enable - UTF-8, use the status-utf8 option. - - status-left-length length - Set the maximum length of the left component of the sta- - tus bar. The default is 10. - - status-right string - Display string to the right of the status bar. By - default, the date and time will be shown. As with - status-left, string will be passed to strftime(3), char- - acter pairs are replaced, and UTF-8 is dependent on the - status-utf8 option. - - status-right-length length - Set the maximum length of the right component of the sta- - tus bar. The default is 40. - - status-utf8 [on | off] - Instruct tmux to treat top-bit-set characters in the - status-left and status-right strings as UTF-8; notably, - this is important for wide characters. This option - defaults to off. - - set-password [-c] password - (alias: pass) - Set the server password. If the -c option is given, a pre- - encrypted password may be specified. By default, the password is - blank, thus any entered password will be accepted when unlocking - the server (see the lock-server command). To prevent variable - expansion when an encrypted password is read from a configuration - file, enclose it in single quotes ('). - - set-window-option [-gu] [-t target-window] option value - (alias: setw) - Set a window-specific option. The -g and -u flags work similarly - to the set-option command. - - Supported options are: - - aggressive-resize [on | off] - Aggressively resize the chosen window. This means that - tmux will resize the window to the size of the smallest - session for which it is the current window, rather than - the smallest session to which it is attached. The window - may resize when the current window is changed on another - sessions; this option is good for full-screen programs - which support SIGWINCH and poor for interactive programs - such as shells. - - automatic-rename [on | off] - Control automatic window renaming. When this setting is - enabled, tmux will attempt - on supported platforms - to - rename the window to reflect the command currently run- - ning in it. This flag is automatically disabled for an - individual window when a name is specified at creation - with new-window or new-session, or later with - rename-window. It may be switched off globally with: - - set-window-option -g automatic-rename off - - clock-mode-colour colour - Set clock colour. - - clock-mode-style [12 | 24] - Set clock hour format. - - force-height height - - force-width width - Prevent tmux from resizing a window to greater than width - or height. A value of zero restores the default unlim- - ited setting. - - main-pane-width width - - main-pane-height height - Set the width or height of the main (left or top) pane in - the main-horizontal or main-vertical layouts. - - mode-attr attributes - Set window modes attributes. - - mode-bg colour - Set window modes background colour. - - mode-fg colour - Set window modes foreground colour. - - mode-keys [vi | emacs] - Use vi or emacs-style key bindings in scroll and copy - modes. Key bindings default to emacs. - - monitor-activity [on | off] - Monitor for activity in the window. Windows with activ- - ity are highlighted in the status line. - - monitor-content match-string - Monitor content in the window. When fnmatch(3) pattern - match-string appears in the window, it is highlighted in - the status line. - - remain-on-exit [on | off] - A window with this flag set is not destroyed when the - program running in it exits. The window may be reacti- - vated with the respawn-window command. - - utf8 [on | off] - Instructs tmux to expect UTF-8 sequences to appear in - this window. - - window-status-attr attributes - Set status line attributes for a single window. - - window-status-bg colour - Set status line background colour for a single window. - - window-status-fg colour - Set status line foreground colour for a single window. - - xterm-keys [on | off] - If this option is set, tmux will generate xterm(1) -style - function key sequences; these have a number included to - indicate modifiers such as Shift, Alt or Ctrl. - - show-buffer [-b buffer-index] [-t target-session] - (alias: showb) - Display the contents of the specified buffer. - - show-options [-t target-session] option value - (alias: show) - Show the currently set options. If a target-session is speci- - fied, the options for that session are shown; otherwise, the - global options are listed. - - show-window-options [-t target-window] option value - (alias: showw) - List the current options for the given window. - - source-file path - (alias: source) - Execute commands from path. - - split-window [-d] [-l lines | -p percentage] [-t target-window] [command] - (alias: splitw) - Creates a new window by splitting it vertically. The -l and -p - options specify the size of the new window in lines, or as a per- - centage, respectively. All other options have the same meaning - as in the new-window command. - - A few notes with regard to panes: - 1. If attempting to split a window with less than eight lines, - an error will be shown. - 2. If the window is resized, as many panes are shown as can fit - without reducing them below four lines. - 3. The minimum pane size is four lines (including the separator - line). - 4. The panes are indexed from top (0) to bottom, with no num- - bers skipped. - - start-server - (alias: start) - Start the tmux server, if not already running, without creating - any sessions. - - suspend-client [-c -target-client] - (alias: suspendc) - Suspend a client by sending SIGTSTP (tty stop). - - swap-pane [-dDU] [-p src-index] [-t target-window] [-q dst-index] - (alias: swapp) - Swap two panes within a window. If -U is used, the pane is - swapped with the pane above (before it numerically); -D swaps - with the pane below (the next numerically); or dst-index may be - give to swap with a specific pane. - - swap-window [-d] [-s src-window] [-t dst-window] - (alias: swapw) - This is similar to link-window, except the source and destination - windows are swapped. It is an error if no window exists at - src-window. - - switch-client [-c target-client -t target-session] - (alias: switchc) - Switch the current session for client target-client to - target-session. - - unbind-key key - (alias: unbind) - Unbind the key bound to key. - - unlink-window [-t target-window] - (alias: unlinkw) - Unlink target-window. A window may be unlinked only if it is - linked to multiple sessions - windows may not be linked to no - sessions. - - up-pane [-p pane-index] [-t target-window] - (alias: upp) - Move up a pane. - -FILES - ~/.tmux.conf Default tmux configuration file. - -SEE ALSO - pty(4) - -AUTHORS - Nicholas Marriott - -BSD October 19, 2013 BSD diff --git a/manual/1.0 b/manual/1.0 deleted file mode 100644 index 83260edd719..00000000000 --- a/manual/1.0 +++ /dev/null @@ -1,1244 +0,0 @@ -TMUX(1) BSD General Commands Manual TMUX(1) - -NAME - tmux -- terminal multiplexer - -SYNOPSIS - tmux [-28dlqUuv] [-f file] [-L socket-name] [-S socket-path] - [command [flags]] - -DESCRIPTION - tmux is a terminal multiplexer: it enables a number of terminals to be - created, accessed, and controlled from a single screen. tmux may be - detached from a screen and continue running in the background, then later - reattached. - - When tmux is started it creates a new session with a single window and - displays it on screen. A status line at the bottom of the screen shows - information on the current session and is used to enter interactive com- - mands. - - A session is a single collection of pseudo terminals under the management - of tmux. Each session has one or more windows linked to it. A window - occupies the entire screen and may be split into rectangular panes, each - of which is a separate pseudo terminal (the pty(4) manual page documents - the technical details of pseudo terminals). Any number of tmux instances - may connect to the same session, and any number of windows may be present - in the same session. Once all sessions are killed, tmux exits. - - Each session is persistent and will survive accidental disconnection - (such as ssh(1) connection timeout) or intentional detaching (with the - 'C-b d' key strokes). tmux may be reattached using: - - $ tmux attach - - In tmux, a session is displayed on screen by a client and all sessions - are managed by a single server. The server and each client are separate - processes which communicate through a socket in /tmp. - - The options are as follows: - - -2 Force tmux to assume the terminal supports 256 colours. - - -8 Like -2, but indicates that the terminal supports 88 - colours. - - -d Force tmux to assume the terminal supports default colours. - - -f file Specify an alternative configuration file. By default, - tmux loads the system configuration file from - /etc/tmux.conf, if present, then looks for a user configu- - ration file at ~/.tmux.conf. The configuration file is a - set of tmux commands which are executed in sequence when - the server is first started. - - If a command in the configuration file fails, tmux will - report an error and exit without executing further com- - mands. - - -l Behave as a login shell. This flag currently has no effect - and is for compatibility with other shells when using tmux - as a login shell. - - -L socket-name - tmux stores the server socket in a directory under /tmp; - the default socket is named default. This option allows a - different socket name to be specified, allowing several - independent tmux servers to be run. Unlike -S a full path - is not necessary: the sockets are all created in the same - directory. - - If the socket is accidentally removed, the SIGUSR1 signal - may be sent to the tmux server process to recreate it. - - -q Prevent the server sending various informational messages, - for example when window flags are altered. - - -S socket-path - Specify a full alternative path to the server socket. If - -S is specified, the default socket directory is not used - and any -L flag is ignored. - - -U Unlock the server. - - -u tmux attempts to guess if the terminal is likely to support - UTF-8 by checking the first of the LC_ALL, LC_CTYPE and - LANG environment variables to be set for the string - "UTF-8". This is not always correct: the -u flag explic- - itly informs tmux that UTF-8 is supported. - - If the server is started from a client passed -u or where - UTF-8 is detected, the utf8 and status-utf8 options are - enabled in the global window and session options respec- - tively. - - -v Request verbose logging. This option may be specified mul- - tiple times for increasing verbosity. Log messages will be - saved into tmux-client-PID.log and tmux-server-PID.log - files in the current directory, where PID is the PID of the - server or client process. - - command [flags] - This specifies one of a set of commands used to control - tmux, as described in the following sections. If no com- - mands are specified, the new-session command is assumed. - -KEY BINDINGS - tmux may be controlled from an attached client by using a key combination - of a prefix key, 'C-b' (Ctrl-b) by default, followed by a command key. - - Some of the default key bindings are: - - c Create a new window. - d Detach the current client. - l Move to the previously selected window. - n Change to the next window. - p Change to the previous window. - & Kill the current window. - , Rename the current window. - ? List all key bindings. - - A complete list may be obtained with the list-keys command (bound to '?' - by default). Key bindings may be changed with the bind-key and - unbind-key commands. - -COMMANDS - This section contains a list of the commands supported by tmux. Most - commands accept the optional -t argument with one of target-client, - target-session target-window, or target-pane. These specify the client, - session, window or pane which a command should affect. target-client is - the name of the pty(4) file to which the client is connected, for example - either of /dev/ttyp1 or ttyp1 for the client attached to /dev/ttyp1. If - no client is specified, the current client is chosen, if possible, or an - error is reported. Clients may be listed with the list-clients command. - - target-session is either the name of a session (as listed by the - list-sessions command) or the name of a client with the same syntax as - target-client, in which case the session attached to the client is used. - When looking for the session name, tmux initially searches for an exact - match; if none is found, the session names are checked for any for which - target-session is a prefix or for which it matches as an fnmatch(3) pat- - tern. If a single match is found, it is used as the target session; mul- - tiple matches produce an error. If a session is omitted, the current - session is used if available; if no current session is available, the - most recently created is chosen. - - target-window specifies a window in the form session:window. session - follows the same rules as for target-session, and window is looked for in - order: as a window index, for example mysession:1; as an exact window - name, such as mysession:mywindow; then as an fnmatch(3) pattern or the - start of a window name, such as mysession:mywin* or mysession:mywin. An - empty window name specifies the next unused index if appropriate (for - example the new-window and link-window commands) otherwise the current - window in session is chosen. When the argument does not contain a colon, - tmux first attempts to parse it as window; if that fails, an attempt is - made to match a session. - - target-pane takes a similar form to target-window but with the optional - addition of a period followed by a pane index, for example: myses- - sion:mywindow.1. If the pane index is omitted, the currently active pane - in the specified window is used. If neither a colon nor period appears, - tmux first attempts to use the argument as a pane index; if that fails, - it is looked up as for target-window. - - Multiple commands may be specified together as part of a command - sequence. Each command should be separated by spaces and a semicolon; - commands are executed sequentially from left to right. A literal semi- - colon may be included by escaping it with a backslash (for example, when - specifying a command sequence to bind-key). - - Examples include: - - refresh-client -t/dev/ttyp2 - - rename-session -tfirst newname - - set-window-option -t:0 monitor-activity on - - new-window ; split-window -d - - bind-key D detach-client \; lock-server - -CLIENTS AND SESSIONS - The following commands are available: - - attach-session [-d] [-t target-session] - (alias: attach) - If run from outside tmux, create a new client in the current ter- - minal and attach it to target-session. If used from inside, - switch the current client. If -d is specified, any other clients - attached to the session are detached. - - If no server is started, attach-session will attempt to start it; - this will fail unless sessions are created in the configuration - file. - - detach-client [-t target-client] - (alias: detach) - Detach the current client if bound to a key, or the specified - client with -t. - - has-session [-t target-session] - (alias: has) - Report an error and exit with 1 if the specified session does not - exist. If it does exist, exit with 0. - - kill-server - Kill the tmux server and clients and destroy all sessions. - - kill-session [-t target-session] - Destroy the given session, closing any windows linked to it and - no other sessions, and detaching all clients attached to it. - - list-clients - (alias: lsc) - List all clients attached to the server. - - list-commands - (alias: lscm) - List the syntax of all commands supported by tmux. - - list-sessions - (alias: ls) - List all sessions managed by the server. - - new-session [-d] [-n window-name] [-s session-name] [command] - (alias: new) - Create a new session with name session-name. The new session is - attached to the current terminal unless -d is given. window-name - and command are the name of and command to execute in the initial - window. - - If run from a terminal, any termios(4) special characters are - saved and used for new windows in the new session. - - refresh-client [-t target-client] - (alias: refresh) - Refresh the current client if bound to a key, or a single client - if one is given with -t. - - rename-session [-t target-session] new-name - (alias: rename) - Rename the session to new-name. - - source-file path - (alias: source) - Execute commands from path. - - start-server - (alias: start) - Start the tmux server, if not already running, without creating - any sessions. - - suspend-client [-c target-client] - (alias: suspendc) - Suspend a client by sending SIGTSTP (tty stop). - - switch-client [-c target-client] [-t target-session] - (alias: switchc) - Switch the current session for client target-client to - target-session. - -WINDOWS AND PANES - A tmux window may be in one of several modes. The default permits direct - access to the terminal attached to the window. The others are: - - output mode - This is entered when a command which produces output, such as - list-keys, is executed from a key binding. - - scroll mode - This is entered with the scroll-mode command (bound to '=' by - default) and permits the window history buffer to be inspected. - - copy mode - This permits a section of a window or its history to be copied to - a paste buffer for later insertion into another window. This - mode is entered with the copy-mode command, bound to ['' by - default. - - The keys available depend on whether emacs or vi mode is selected (see - the mode-keys option). The following keys are supported as appropriate - for the mode: - - Function vi emacs - Back to indentation ^ M-m - Clear selection Escape C-g - Copy selection Enter M-w - Cursor down j Down - Cursor left h Left - Cursor right l Right - Cursor up k Up - Delete entire line d C-u - Delete to end of line D C-k - End of line $ C-e - Goto line g g - Next page C-f Page down - Next word w M-f - Paste buffer p C-y - Previous page C-u Page up - Previous word b M-b - Quit mode q Escape - Search again n n - Search backward ? C-r - Search forward / C-s - Start of line 0 C-a - Start selection Space C-Space - Transpose chars C-t - - These key bindings are defined in a set of named tables: vi-edit and - emacs-edit for keys used when line editing at the command prompt; - vi-choice and emacs-choice for keys used when choosing from lists (such - as produced by the window-choose command) or in output mode; and vi-copy - and emacs-copy used in copy and scroll modes. The tables may be viewed - with the list-keys command and keys modified or removed with bind-key and - unbind-key. - - The paste buffer key pastes the first line from the top paste buffer on - the stack. - - The mode commands are as follows: - - copy-mode [-u] [-t target-pane] - Enter copy mode. The -u option scrolls one page up. - - scroll-mode [-u] [-t target-pane] - Enter scroll mode. The -u has the same meaning as in the - copy-mode command. - - Each window displayed by tmux may be split into one or more panes; each - pane takes up a certain area of the display and is a separate terminal. - A window may be split into panes using the split-window command. Windows - may be split horizontally (with the -h flag) or vertically. Panes may be - resized with the resize-pane command (bound to 'C-up', 'C-down' 'C-left' - and 'C-right' by default), the current pane may be changed with the - up-pane and down-pane commands and the rotate-window and swap-pane com- - mands may be used to swap panes without changing their position. Panes - are numbered beginning from zero in the order they are created. - - A number of preset layouts are available. These may be selected with the - select-layout command or cycled with next-layout (bound to 'C-space' by - default); once a layout is chosen, panes within it may be moved and - resized as normal. - - The following layouts are supported: - - even-horizontal - Panes are spread out evenly from left to right across the window. - - even-vertical - Panes are spread evenly from top to bottom. - - main-horizontal - A large (main) pane is shown at the top of the window and the - remaining panes are spread from left to right in the leftover - space at the bottom. Use the main-pane-height window option to - specify the height of the top pane. - - main-vertical - Similar to main-horizontal but the large pane is placed on the - left and the others spread from top to bottom along the right. - See the main-pane-width window option. - - Commands related to windows and panes are as follows: - - break-pane [-d] [-t target-pane] - (alias: breakp) - Break target-pane off from its containing window to make it the - only pane in a new window. If -d is given, the new window does - not become the current window. - - choose-client [-t target-window] [template] - Put a window into client choice mode, allowing a client to be - selected interactively from a list. After a client is chosen, - '%%' is replaced by the client pty(4) path in template and the - result executed as a command. If template is not given, "detach- - client -t '%%'" is used. This command works only from inside - tmux. - - choose-session [-t target-window] [template] - Put a window into session choice mode, where a session may be - selected interactively from a list. When one is chosen, '%%' is - replaced by the session name in template and the result executed - as a command. If template is not given, "switch-client -t '%%'" - is used. This command works only from inside tmux. - - choose-window [-t target-window] [template] - Put a window into window choice mode, where a window may be cho- - sen interactively from a list. After a window is selected, '%%' - is replaced by the session name and window index in template and - the result executed as a command. If template is not given, - "select-window -t '%%'" is used. This command works only from - inside tmux. - - display-panes [-t target-client] - (alias: displayp) - Display a visible indicator of each pane shown by target-client. - See the display-panes-time and display-panes-colour session - options. While the indicator is on screen, a pane may be - selected with the '0' to '9' keys. - - down-pane [-t target-pane] - (alias: downp) - Move down a pane. - - find-window [-t target-window] match-string - (alias: findw) - Search for the fnmatch(3) pattern match-string in window names, - titles, and visible content (but not history). If only one win- - dow is matched, it'll be automatically selected, otherwise a - choice list is shown. This command only works from inside tmux. - - kill-pane [-t target-pane] - (alias: killp) - Destroy the given pane. If no panes remain in the containing - window, it is also destroyed. - - kill-window [-t target-window] - (alias: killw) - Kill the current window or the window at target-window, removing - it from any sessions to which it is linked. - - last-window [-t target-session] - (alias: last) - Select the last (previously selected) window. If no - target-session is specified, select the last window of the cur- - rent session. - - link-window [-dk] [-s src-window] [-t dst-window] - (alias: linkw) - Link the window at src-window to the specified dst-window. If - dst-window is specified and no such window exists, the src-window - is linked there. If -k is given and dst-window exists, it is - killed, otherwise an error is generated. If -d is given, the - newly linked window is not selected. - - list-windows [-t target-session] - (alias: lsw) - List windows in the current session or in target-session. - - move-window [-d] [-s src-window] [-t dst-window] - (alias: movew) - This is similar to link-window, except the window at src-window - is moved to dst-window. - - new-window [-dk] [-n window-name] [-t target-window] [command] - (alias: neww) - Create a new window. If -d is given, the session does not make - the new window the current window. target-window represents the - window to be created; if the target already exists an error is - shown, unless the -k flag is used, in which case it is destroyed. - command is the command to execute. If command is not specified, - the default command is used. - - The TERM environment variable must be set to ``screen'' for all - programs running inside tmux. New windows will automatically - have ``TERM=screen'' added to their environment, but care must be - taken not to reset this in shell start-up files. - - next-layout [-t target-window] - (alias: nextl) - Move a window to the next layout and rearrange the panes to fit. - - next-window [-a] [-t target-session] - (alias: next) - Move to the next window in the session. If -a is used, move to - the next window with a bell, activity or content alert. - - previous-window [-a] [-t target-session] - (alias: prev) - Move to the previous window in the session. With -a, move to the - previous window with a bell, activity or content alert. - - rename-window [-t target-window] new-name - (alias: renamew) - Rename the current window, or the window at target-window if - specified, to new-name. - - resize-pane [-DLRU] [-t target-pane] [adjustment] - (alias: resizep) - Resize a pane, upward with -U (the default), downward with -D, to - the left with -L and to the right with -R. The adjustment is - given in lines or cells (the default is 1). - - respawn-window [-k] [-t target-window] [command] - (alias: respawnw) - Reactive a window in which the command has exited (see the - remain-on-exit window option). If command is not given, the com- - mand used when the window was created is executed. The window - must be already inactive, unless -k is given, in which case any - existing command is killed. - - rotate-window [-DU] [-t target-window] - (alias: rotatew) - Rotate the positions of the panes within a window, either upward - (numerically lower) with -U or downward (numerically higher). - - select-layout [-t target-window] [layout-name] - (alias: selectl) - Choose a specific layout for a window. If layout-name is not - given, the last layout used (if any) is reapplied. - - select-pane [-t target-pane] - (alias: selectp) - Make pane target-pane the active pane in window target-window. - - select-window [-t target-window] - (alias: selectw) - Select the window at target-window. - - split-window [-dhv] [-l size | -p percentage] [-t target-window] - [command] - (alias: splitw) - Creates a new pane by splitting the active pane: -h does a hori- - zontal split and -v a vertical split; if neither is specified, -v - is assumed. The -l and -p options specify the size of the new - window in lines (for vertical split) or in cells (for horizontal - split), or as a percentage, respectively. All other options have - the same meaning as in the new-window command. - - swap-pane [-dDU] [-s src-pane] [-t dst-pane] - (alias: swapp) - Swap two panes. If -U is used and no source pane is specified - with -s, dst-pane is swapped with the previous pane (before it - numerically); -D swaps with the next pane (after it numerically). - - swap-window [-d] [-s src-window] [-t dst-window] - (alias: swapw) - This is similar to link-window, except the source and destination - windows are swapped. It is an error if no window exists at - src-window. - - unlink-window [-k] [-t target-window] - (alias: unlinkw) - Unlink target-window. Unless -k is given, a window may be - unlinked only if it is linked to multiple sessions - windows may - not be linked to no sessions; if -k is specified and the window - is linked to only one session, it is unlinked and destroyed. - - up-pane [-t target-pane] - (alias: upp) - Move up a pane. - -KEY BINDINGS - Commands related to key bindings are as follows: - - bind-key [-cnr] [-t key-table] key command [arguments] - (alias: bind) - Bind key key to command. Keys may be specified prefixed with - 'C-' or '^' for Ctrl keys, or 'M-' for Alt (meta) keys. - - By default (without -t) the primary key bindings are modified - (those normally activated with the prefix key); in this case, if - -n is specified, it is not necessary to use the prefix key, - command is bound to key alone. The -r flag indicates this key - may repeat, see the repeat-time option. - - If -t is present, key is bound in key-table: the binding for com- - mand mode with -c or for normal mode without. To view the - default bindings and possible commands, see the list-keys com- - mand. - - list-keys [-t key-table] - (alias: lsk) - List all key bindings. Without -t the primary key bindings - - those executed when preceded by the prefix key - are printed. - Keys bound without the prefix key (see bind-key -n) are enclosed - in square brackets. - - With -t, the key bindings in key-table are listed; this may be - one of: vi-edit, emacs-edit, vi-choice, emacs-choice, vi-copy or - emacs-copy. - - send-keys [-t target-pane] key ... - (alias: send) - Send a key or keys to a window. Each argument key is the name of - the key (such as 'C-a' or 'npage' ) to send; if the string is not - recognised as a key, it is sent as a series of characters. All - arguments are sent sequentially from first to last. - - send-prefix [-t target-pane] - Send the prefix key to a window as if it was pressed. - - unbind-key [-cn] [-t key-table] key - (alias: unbind) - Unbind the command bound to key. Without -t the primary key - bindings are modified; in this case, if -n is specified, the com- - mand bound to key without a prefix (if any) is removed. - - If -t is present, key in key-table is unbound: the binding for - command mode with -c or for normal mode without. - -OPTIONS - The appearance and behaviour of tmux may be modified by changing the - value of various options. There are two types of option: session options - and window options. - - Each individual session may have a set of session options, and there is a - separate set of global session options. Sessions which do not have a - particular option configured inherit the value from the global session - options. Session options are set or unset with the set-option command - and may be listed with the show-options command. The available session - options are listed under the set-option command. - - Similarly, a set of window options is attached to each window, and there - is a set of global window options from which any unset options are inher- - ited. Window options are altered with the set-window-option command and - can be listed with the show-window-options command. All window options - are documented with the set-window-option command. - - Commands which set options are as follows: - - set-option [-agu] [-t target-session] option value - (alias: set) - Set a session option. With -a, and if the option expects a - string, value is appended to the existing setting. If -g is - specified, the global session option is set. The -u flag unsets - an option, so a session inherits the option from the global - options - it is not possible to unset a global option. - - Available session options are: - - base-index index - Set the base index from which an unused index should be - searched when a new window is created. The default is - zero. - - bell-action [any | none | current] - Set action on window bell. any means a bell in any win- - dow linked to a session causes a bell in the current win- - dow of that session, none means all bells are ignored and - current means only bell in windows other than the current - window are ignored. - - buffer-limit number - Set the number of buffers kept for each session; as new - buffers are added to the top of the stack, old ones are - removed from the bottom if necessary to maintain this - maximum length. - - default-command command - Set the command used for new windows (if not specified - when the window is created) to command, which may be any - sh(1) command. The default is an empty string, which - instructs tmux to create a login shell using the value of - the default-shell option. - - default-shell path - Specify the default shell. This is used as the login - shell for new windows when the default-command option is - set to empty, and must be the full path of the exe- - cutable. When started tmux tries to set a default value - from the first suitable of the SHELL environment vari- - able, the shell returned by getpwuid(3), or /bin/sh. - This option should be configured when tmux is used as a - login shell. - - default-path path - Set the default working directory for processes created - from keys, or interactively from the prompt. The default - is the current working directory when the server is - started. - - default-terminal terminal - Set the default terminal for new windows created in this - session - the default value of the TERM environment vari- - able. For tmux to work correctly, this must be set to - 'screen' or a derivative of it. - - display-panes-colour colour - Set the colour used for the display-panes command. - - display-panes-time time - Set the time in milliseconds for which the indicators - shown by the display-panes command appear. - - display-time time - Set the amount of time for which status line messages and - other on-screen indicators are displayed. time is in - milliseconds. - - history-limit lines - Set the maximum number of lines held in window history. - This setting applies only to new windows - existing win- - dow histories are not resized and retain the limit at the - point they were created. - - lock-after-time number - Lock the server after number seconds of inactivity. The - default is off (set to 0). This has no effect as a ses- - sion option; it must be set as a global option using -g. - When passwords are entered incorrectly, tmux follows the - behaviour of login(1) and ignores further password - attempts for an increasing timeout. - - message-attr attributes - Set status line message attributes, where attributes is - either default or a comma-delimited list of one or more - of: bright (or bold), dim, underscore, blink, reverse, - hidden, or italics. - - message-bg colour - Set status line message background colour, where colour - is one of: black, red, green, yellow, blue, magenta, - cyan, white, colour0 to colour255 from the 256-colour - palette, or default. - - message-fg colour - Set status line message foreground colour. - - prefix key - Set the current prefix key. - - repeat-time time - Allow multiple commands to be entered without pressing - the prefix-key again in the specified time milliseconds - (the default is 500). Whether a key repeats may be set - when it is bound using the -r flag to bind-key. Repeat - is enabled for the default keys bound to the resize-pane - command. - - set-remain-on-exit [on | off] - Set the remain-on-exit window option for any windows - first created in this session. - - set-titles [on | off] - Attempt to set the window title using the \e]2;...\007 - xterm code if the terminal appears to be an xterm. This - option is off by default. Note that elinks will only - attempt to set the window title if the STY environment - variable is set. - - set-titles-string string - String used to set the window title if set-titles is on. - Character sequences are replaced as for the status-left - option. - - status [on | off] - Show or hide the status line. - - status-attr attributes - Set status line attributes. - - status-bg colour - Set status line background colour. - - status-fg colour - Set status line foreground colour. - - status-interval interval - Update the status bar every interval seconds. By - default, updates will occur every 15 seconds. A setting - of zero disables redrawing at interval. - - status-justify [left | centre | right] - Set the position of the window list component of the sta- - tus line: left, centre or right justified. - - status-keys [vi | emacs] - Use vi or emacs-style key bindings in the status line, - for example at the command prompt. Defaults to emacs. - - status-left string - Display string to the left of the status bar. string - will be passed through strftime(3) before being used. By - default, the session name is shown. string may contain - any of the following special character sequences: - - Character pair Replaced with - #(command) First line of command's output - #[attributes] Colour or attribute change - #H Hostname of local host - #I Current window index - #P Current pane index - #S Session name - #T Current window title - #W Current window name - ## A literal '#' - - The #(command) form executes 'command' as a shell command - and inserts the first line of its output. #[attributes] - allows a comma-separated list of attributes to be speci- - fied, these may be 'fg=colour' to set the foreground - colour, 'bg=colour' to set the background colour, or one - of the attributes described under the message-attr - option. Examples are: - - #(sysctl vm.loadavg) - #[fg=yellow,bold]#(apm -l)%%#[default] [#S] - - Where appropriate, these may be prefixed with a number to - specify the maximum length, for example '#24T'. - - By default, UTF-8 in string is not interpreted, to enable - UTF-8, use the status-utf8 option. - - status-left-attr attributes - Set the attribute of the left part of the status line. - - status-left-fg colour - Set the foreground colour of the left part of the status - line. - - status-left-bg colour - Set the background colour of the left part of the status - line. - - status-left-length length - Set the maximum length of the left component of the sta- - tus bar. The default is 10. - - status-right string - Display string to the right of the status bar. By - default, the date and time will be shown. As with - status-left, string will be passed to strftime(3), char- - acter pairs are replaced, and UTF-8 is dependent on the - status-utf8 option. - - status-right-attr attributes - Set the attribute of the right part of the status line. - - status-right-fg colour - Set the foreground colour of the right part of the status - line. - - status-right-bg colour - Set the background colour of the right part of the status - line. - - status-right-length length - Set the maximum length of the right component of the sta- - tus bar. The default is 40. - - status-utf8 [on | off] - Instruct tmux to treat top-bit-set characters in the - status-left and status-right strings as UTF-8; notably, - this is important for wide characters. This option - defaults to off. - - terminal-overrides string - Contains a list of entries which override terminal - descriptions read using terminfo(5). string is a comma- - separated list of items each a colon-separated string - made up of a terminal type pattern (matched using - fnmatch(3)) and a set of name=value entries. - - For example, to set the 'clear' terminfo(5) entry to - '\e[H\e[2J' for all terminal types and the 'dch1' entry - to '\e[P' for the 'rxvt' terminal type, the option could - be set to the string: - - "*:clear=\e[H\e[2J,rxvt:dch1=\e[P" - - The terminal entry value is passed through strunvis(3) - before interpretation. The default value forcibly cor- - rects the 'colors' entry for terminals which support 88 - or 256 colours: - - "*88col*:colors=88,*256col*:colors=256" - - update-environment variables - Set a space-separated string containing a list of envi- - ronment variables to be copied into the session environ- - ment when a new session is created or an existing session - is attached. Any variables that do not exist in the - source environment are set to be removed from the session - environment (as if -r was given to the set-environment - command). The default is "DISPLAY WINDOWID SSH_ASKPASS - SSH_AUTH_SOCK SSH_AGENT_PID SSH_CONNECTION". - - visual-activity [on | off] - If on, display a status line message when activity occurs - in a window for which the monitor-activity window option - is enabled. - - visual-bell [on | off] - If this option is on, a message is shown on a bell - instead of it being passed through to the terminal (which - normally makes a sound). Also see the bell-action - option. - - visual-content [on | off] - Like visual-activity, display a message when content is - present in a window for which the monitor-content window - option is enabled. - - set-window-option [-agu] [-t target-window] option value - (alias: setw) - Set a window option. The -a, -g and -u flags work similarly to - the set-option command. - - Supported window options are: - - aggressive-resize [on | off] - Aggressively resize the chosen window. This means that - tmux will resize the window to the size of the smallest - session for which it is the current window, rather than - the smallest session to which it is attached. The window - may resize when the current window is changed on another - sessions; this option is good for full-screen programs - which support SIGWINCH and poor for interactive programs - such as shells. - - automatic-rename [on | off] - Control automatic window renaming. When this setting is - enabled, tmux will attempt - on supported platforms - to - rename the window to reflect the command currently run- - ning in it. This flag is automatically disabled for an - individual window when a name is specified at creation - with new-window or new-session, or later with - rename-window. It may be switched off globally with: - - set-window-option -g automatic-rename off - - clock-mode-colour colour - Set clock colour. - - clock-mode-style [12 | 24] - Set clock hour format. - - force-height height - force-width width - Prevent tmux from resizing a window to greater than width - or height. A value of zero restores the default unlim- - ited setting. - - main-pane-width width - main-pane-height height - Set the width or height of the main (left or top) pane in - the main-horizontal or main-vertical layouts. - - mode-attr attributes - Set window modes attributes. - - mode-bg colour - Set window modes background colour. - - mode-fg colour - Set window modes foreground colour. - - mode-keys [vi | emacs] - Use vi or emacs-style key bindings in scroll, copy and - choice modes. Key bindings default to emacs. - - mode-mouse [on | off] - Mouse state in modes. If on, tmux will respond to mouse - clicks by moving the cursor in copy mode or selecting an - option in choice mode. - - monitor-activity [on | off] - Monitor for activity in the window. Windows with activ- - ity are highlighted in the status line. - - monitor-content match-string - Monitor content in the window. When fnmatch(3) pattern - match-string appears in the window, it is highlighted in - the status line. - - remain-on-exit [on | off] - A window with this flag set is not destroyed when the - program running in it exits. The window may be reacti- - vated with the respawn-window command. - - utf8 [on | off] - Instructs tmux to expect UTF-8 sequences to appear in - this window. - - window-status-attr attributes - Set status line attributes for a single window. - - window-status-bg colour - Set status line background colour for a single window. - - window-status-fg colour - Set status line foreground colour for a single window. - - window-status-current-attr attributes - Set status line attributes for the currently active win- - dow. - - window-status-current-bg colour - Set status line background colour for the currently - active window. - - window-status-current-fg colour - Set status line foreground colour for the currently - active window. - - xterm-keys [on | off] - If this option is set, tmux will generate xterm(1) -style - function key sequences; these have a number included to - indicate modifiers such as Shift, Alt or Ctrl. - - show-options [-g] [-t target-session] - (alias: show) - Show the session options for target session, or the global ses- - sion options with -g. - - show-window-options [-g] [-t target-window] - (alias: showw) - List the window options for target-window, or the global window - options if -g is used. - -ENVIRONMENT - When the server is started, tmux copies the environment into the global - environment; in addition, each session has a session environment. When a - window is created, the session and global environments are merged with - the session environment overriding any variable present in both. This is - the initial environment passed to the new process. - - The update-environment session option may be used to update the session - environment from the client when a new session is created or an old reat- - tached. tmux also initialises the TMUX variable with some internal - information to allow commands to be executed from inside, and the TERM - variable with the correct terminal setting of 'screen'. - - Commands to alter and view the environment are: - - set-environment [-gru] [-t target-session] name [value] - Set or unset an environment variable. If -g is used, the change - is made in the global environment; otherwise, it is applied to - the session environment for target-session. The -u flag unsets a - variable. -r indicates the variable is to be removed from the - environment before starting a new process. - - show-environment [-g] [-t target-session] - Display the environment for target-session or the global environ- - ment with -g. Variables removed from the environment are pre- - fixed with '-'. - -STATUS LINE - tmux includes an optional status line which is displayed in the bottom - line of each terminal. By default, the status line is enabled (it may be - disabled with the status session option) and contains, from left-to- - right: the name of the current session in square brackets; the window - list; the current window title in double quotes; and the time and date. - - The status line is made of three parts: configurable left and right sec- - tions (which may contain dynamic content such as the time or output from - a shell command, see the status-left, status-left-length, status-right, - and status-right-length options below), and a central window list. The - window list shows the index, name and (if any) flag of the windows - present in the current session in ascending numerical order. The flag is - one of the following symbols appended to the window name: - - Symbol Meaning - * Denotes the current window. - - Marks the last window (previously selected). - # Window is monitored and activity has been detected. - ! A bell has occurred in the window. - + Window is monitored for content and it has appeared. - - The # symbol relates to the monitor-activity and + to the monitor-content - window options. The window name is printed in inverted colours if an - alert (bell, activity or content) is present. - - The colour and attributes of the status line may be configured, the - entire status line using the status-attr, status-fg and status-bg session - options and individual windows using the window-status-attr, - window-status-fg and window-status-bg window options. - - The status line is automatically refreshed at interval if it has changed, - the interval may be controlled with the status-interval session option. - - Commands related to the status line are as follows: - - command-prompt [-p prompts] [-t target-client] [template] - Open the command prompt in a client. This may be used from - inside tmux to execute commands interactively. If template is - specified, it is used as the command. If -p is given, prompts is - a comma-separated list of prompts which are displayed in order; - otherwise a single prompt is displayed, constructed from template - if it is present, or ':' if not. Before the command is executed, - the first occurrence of the string '%%' and all occurrences of - '%1' are replaced by the response to the first prompt, the second - '%%' and all '%2' are replaced with the response to the second - prompt, and so on for further prompts. Up to nine prompt - responses may be replaced ('%1' to '%9'). - - confirm-before [-t target-client] command - (alias: confirm) - Ask for confirmation before executing command. This command - works only from inside tmux. - - display-message [-t target-client] [message] - (alias: display) - Display a message (see the status-left option below) in the sta- - tus line. - - select-prompt [-t target-client] - Open a prompt inside target-client allowing a window index to be - entered interactively. - -BUFFERS - tmux maintains a stack of paste buffers for each session. Up to the - value of the buffer-limit option are kept; when a new buffer is added, - the buffer at the bottom of the stack is removed. Buffers may be added - using copy-mode or the set-buffer command, and pasted into a window using - the paste-buffer command. - - A configurable history buffer is also maintained for each window. By - default, up to 2000 lines are kept; this can be altered with the - history-limit option (see the set-option command above). - - The buffer commands are as follows: - - clear-history [-t target-pane] - (alias: clearhist) - Remove and free the history for the specified pane. - - copy-buffer [-a src-index] [-b dst-index] [-s src-session] [-t - dst-session] - (alias: copyb) - Copy a session paste buffer to another session. If no sessions - are specified, the current one is used instead. - - delete-buffer [-b buffer-index] [-t target-session] - (alias: deleteb) - Delete the buffer at buffer-index, or the top buffer if not spec- - ified. - - list-buffers [-t target-session] - (alias: lsb) - List the buffers in the given session. - - load-buffer [-b buffer-index] [-t target-session] path - (alias: loadb) - Load the contents of the specified paste buffer from path. - - paste-buffer [-dr] [-b buffer-index] [-t target-window] - (alias: pasteb) - Insert the contents of a paste buffer into the current window. - With -d, also delete the paste buffer from the stack. When out- - put, any linefeed (LF) characters in the paste buffer are - replaced with carriage returns (CR). This translation may be - disabled with the -r flag. - - save-buffer [-a] [-b buffer-index] [-t target-session] path - (alias: saveb) - Save the contents of the specified paste buffer to path. The -a - option appends to rather than overwriting the file. - - set-buffer [-b buffer-index] [-t target-session] data - (alias: setb) - Set the contents of the specified buffer to data. - - show-buffer [-b buffer-index] [-t target-session] - (alias: showb) - Display the contents of the specified buffer. - -MISCELLANEOUS - Miscellaneous commands are as follows: - - clock-mode [-t target-pane] - Display a large clock. - - if-shell shell-command command - (alias: if) - Execute command if shell-command returns success. - - lock-server - (alias: lock) - Lock the server until a password is entered. - - server-info - (alias: info) - Show server information and terminal details. - - set-password [-c] password - (alias: pass) - Set the server password. If the -c option is given, a pre- - encrypted password may be specified. By default, the password is - blank, thus any entered password will be accepted when unlocking - the server (see the lock-server command). To prevent variable - expansion when an encrypted password is read from a configuration - file, enclose it in single quotes ('). - -FILES - ~/.tmux.conf Default tmux configuration file. - /etc/tmux.conf System-wide configuration file. - -EXAMPLES - To create a new tmux session running vi(1): - - $ tmux new-session vi - - Most commands have a shorter form, known as an alias. For new-session, - this is new: - - $ tmux new vi - - Alternatively, the shortest unambiguous form of a command is accepted. - If there are several options, they are listed: - - $ tmux n - ambiguous command: n, could be: new-session, new-window, next-window - - Within an active session, a new window may be created by typing 'C-b c' - (Ctrl followed by the 'b' key followed by the 'c' key). - - Windows may be navigated with: 'C-b 0' (to select window 0), 'C-b 1' (to - select window 1), and so on; 'C-b n' to select the next window; and 'C-b - p' to select the previous window. - - A session may be detached using 'C-b d' (or by an external event such as - ssh(1) disconnection) and reattached with: - - $ tmux attach-session - - Typing 'C-b ?' lists the current key bindings in the current window; up - and down may be used to navigate the list or 'q' to exit from it. - - Commands to be run when the tmux server is started may be placed in the - ~/.tmux.conf configuration file. Common examples include: - - Changing the default prefix key: - - set-option -g prefix C-a - unbind-key C-b - bind-key C-a send-prefix - - Turning the status line off, or changing its colour: - - set-option -g status off - set-option -g status-bg blue - - Setting other options, such as the default command, or locking after 30 - minutes of inactivity: - - set-option -g default-command "exec /bin/ksh" - set-option -g lock-after-time 1800 - - Creating new key bindings: - - bind-key b set-option status - bind-key / command-prompt "split-window 'exec man %%'" - bind-key S command-prompt "new-window -n %1 'ssh %1'" - -SEE ALSO - pty(4) - -AUTHORS - Nicholas Marriott - -BSD October 19, 2013 BSD diff --git a/manual/1.1 b/manual/1.1 deleted file mode 100644 index ca93ff67080..00000000000 --- a/manual/1.1 +++ /dev/null @@ -1,1313 +0,0 @@ -TMUX(1) BSD General Commands Manual TMUX(1) - -NAME - tmux -- terminal multiplexer - -SYNOPSIS - tmux [-28lquv] [-c shell-command] [-f file] [-L socket-name] - [-S socket-path] [command [flags]] - -DESCRIPTION - tmux is a terminal multiplexer: it enables a number of terminals to be - created, accessed, and controlled from a single screen. tmux may be - detached from a screen and continue running in the background, then later - reattached. - - When tmux is started it creates a new session with a single window and - displays it on screen. A status line at the bottom of the screen shows - information on the current session and is used to enter interactive com- - mands. - - A session is a single collection of pseudo terminals under the management - of tmux. Each session has one or more windows linked to it. A window - occupies the entire screen and may be split into rectangular panes, each - of which is a separate pseudo terminal (the pty(4) manual page documents - the technical details of pseudo terminals). Any number of tmux instances - may connect to the same session, and any number of windows may be present - in the same session. Once all sessions are killed, tmux exits. - - Each session is persistent and will survive accidental disconnection - (such as ssh(1) connection timeout) or intentional detaching (with the - 'C-b d' key strokes). tmux may be reattached using: - - $ tmux attach - - In tmux, a session is displayed on screen by a client and all sessions - are managed by a single server. The server and each client are separate - processes which communicate through a socket in /tmp. - - The options are as follows: - - -2 Force tmux to assume the terminal supports 256 colours. - - -8 Like -2, but indicates that the terminal supports 88 - colours. - - -c shell-command - Execute shell-command using the default shell. If neces- - sary, the tmux server will be started to retrieve the - default-shell option. - - -f file Specify an alternative configuration file. By default, - tmux loads the system configuration file from - /etc/tmux.conf, if present, then looks for a user configu- - ration file at ~/.tmux.conf. The configuration file is a - set of tmux commands which are executed in sequence when - the server is first started. - - If a command in the configuration file fails, tmux will - report an error and exit without executing further com- - mands. - - -l Behave as a login shell. This flag currently has no effect - and is for compatibility with other shells when using tmux - as a login shell. - - -L socket-name - tmux stores the server socket in a directory under /tmp; - the default socket is named default. This option allows a - different socket name to be specified, allowing several - independent tmux servers to be run. Unlike -S a full path - is not necessary: the sockets are all created in the same - directory. - - If the socket is accidentally removed, the SIGUSR1 signal - may be sent to the tmux server process to recreate it. - - -q Prevent the server sending various informational messages, - for example when window flags are altered. - - -S socket-path - Specify a full alternative path to the server socket. If - -S is specified, the default socket directory is not used - and any -L flag is ignored. - - -u tmux attempts to guess if the terminal is likely to support - UTF-8 by checking the first of the LC_ALL, LC_CTYPE and - LANG environment variables to be set for the string - "UTF-8". This is not always correct: the -u flag explic- - itly informs tmux that UTF-8 is supported. - - If the server is started from a client passed -u or where - UTF-8 is detected, the utf8 and status-utf8 options are - enabled in the global window and session options respec- - tively. - - -v Request verbose logging. This option may be specified mul- - tiple times for increasing verbosity. Log messages will be - saved into tmux-client-PID.log and tmux-server-PID.log - files in the current directory, where PID is the PID of the - server or client process. - - command [flags] - This specifies one of a set of commands used to control - tmux, as described in the following sections. If no com- - mands are specified, the new-session command is assumed. - -KEY BINDINGS - tmux may be controlled from an attached client by using a key combination - of a prefix key, 'C-b' (Ctrl-b) by default, followed by a command key. - - Some of the default key bindings are: - - c Create a new window. - d Detach the current client. - l Move to the previously selected window. - n Change to the next window. - p Change to the previous window. - & Kill the current window. - , Rename the current window. - ? List all key bindings. - - A complete list may be obtained with the list-keys command (bound to '?' - by default). Key bindings may be changed with the bind-key and - unbind-key commands. - -COMMANDS - This section contains a list of the commands supported by tmux. Most - commands accept the optional -t argument with one of target-client, - target-session target-window, or target-pane. These specify the client, - session, window or pane which a command should affect. target-client is - the name of the pty(4) file to which the client is connected, for example - either of /dev/ttyp1 or ttyp1 for the client attached to /dev/ttyp1. If - no client is specified, the current client is chosen, if possible, or an - error is reported. Clients may be listed with the list-clients command. - - target-session is either the name of a session (as listed by the - list-sessions command) or the name of a client with the same syntax as - target-client, in which case the session attached to the client is used. - When looking for the session name, tmux initially searches for an exact - match; if none is found, the session names are checked for any for which - target-session is a prefix or for which it matches as an fnmatch(3) pat- - tern. If a single match is found, it is used as the target session; mul- - tiple matches produce an error. If a session is omitted, the current - session is used if available; if no current session is available, the - most recently used is chosen. - - target-window specifies a window in the form session:window. session - follows the same rules as for target-session, and window is looked for in - order: as a window index, for example mysession:1; as an exact window - name, such as mysession:mywindow; then as an fnmatch(3) pattern or the - start of a window name, such as mysession:mywin* or mysession:mywin. An - empty window name specifies the next unused index if appropriate (for - example the new-window and link-window commands) otherwise the current - window in session is chosen. When the argument does not contain a colon, - tmux first attempts to parse it as window; if that fails, an attempt is - made to match a session. - - target-pane takes a similar form to target-window but with the optional - addition of a period followed by a pane index, for example: myses- - sion:mywindow.1. If the pane index is omitted, the currently active pane - in the specified window is used. If neither a colon nor period appears, - tmux first attempts to use the argument as a pane index; if that fails, - it is looked up as for target-window. - - Multiple commands may be specified together as part of a command - sequence. Each command should be separated by spaces and a semicolon; - commands are executed sequentially from left to right. A literal semi- - colon may be included by escaping it with a backslash (for example, when - specifying a command sequence to bind-key). - - Examples include: - - refresh-client -t/dev/ttyp2 - - rename-session -tfirst newname - - set-window-option -t:0 monitor-activity on - - new-window ; split-window -d - -CLIENTS AND SESSIONS - The following commands are available: - - attach-session [-d] [-t target-session] - (alias: attach) - If run from outside tmux, create a new client in the current ter- - minal and attach it to target-session. If used from inside, - switch the current client. If -d is specified, any other clients - attached to the session are detached. - - If no server is started, attach-session will attempt to start it; - this will fail unless sessions are created in the configuration - file. - - detach-client [-t target-client] - (alias: detach) - Detach the current client if bound to a key, or the specified - client with -t. - - has-session [-t target-session] - (alias: has) - Report an error and exit with 1 if the specified session does not - exist. If it does exist, exit with 0. - - kill-server - Kill the tmux server and clients and destroy all sessions. - - kill-session [-t target-session] - Destroy the given session, closing any windows linked to it and - no other sessions, and detaching all clients attached to it. - - list-clients - (alias: lsc) - List all clients attached to the server. - - list-commands - (alias: lscm) - List the syntax of all commands supported by tmux. - - list-sessions - (alias: ls) - List all sessions managed by the server. - - lock-client [-t target-client] - Lock target-client, see the lock-server command. - - lock-session [-t target-session] - Lock all clients attached to target-session. - - new-session [-d] [-n window-name] [-s session-name] [-t target-session] - [command] - (alias: new) - Create a new session with name session-name. The new session is - attached to the current terminal unless -d is given. window-name - and command are the name of and command to execute in the initial - window. - - If run from a terminal, any termios(4) special characters are - saved and used for new windows in the new session. - - If -t is given, the new session is grouped with target-session. - This means they share the same set of windows - all windows from - target-session are linked to the new session and any subsequent - new windows or windows being closed are applied to both sessions. - The current and previous window and any session options remain - independent and either session may be killed without affecting - the other. Giving -n or command are invalid if -t is used. - - refresh-client [-t target-client] - (alias: refresh) - Refresh the current client if bound to a key, or a single client - if one is given with -t. - - rename-session [-t target-session] new-name - (alias: rename) - Rename the session to new-name. - - source-file path - (alias: source) - Execute commands from path. - - start-server - (alias: start) - Start the tmux server, if not already running, without creating - any sessions. - - suspend-client [-c target-client] - (alias: suspendc) - Suspend a client by sending SIGTSTP (tty stop). - - switch-client [-c target-client] [-t target-session] - (alias: switchc) - Switch the current session for client target-client to - target-session. - -WINDOWS AND PANES - A tmux window may be in one of several modes. The default permits direct - access to the terminal attached to the window. The others are: - - output mode - This is entered when a command which produces output, such as - list-keys, is executed from a key binding. - - copy mode - This permits a section of a window or its history to be copied to - a paste buffer for later insertion into another window. This - mode is entered with the copy-mode command, bound to '[' by - default. - - The keys available depend on whether emacs or vi mode is selected (see - the mode-keys option). The following keys are supported as appropriate - for the mode: - - Function vi emacs - Back to indentation ^ M-m - Clear selection Escape C-g - Copy selection Enter M-w - Cursor down j Down - Cursor left h Left - Cursor right l Right - Cursor to bottom line L - Cursor to middle line M M-r - Cursor to top line H M-R - Cursor up k Up - Delete entire line d C-u - Delete to end of line D C-k - End of line $ C-e - Goto line : g - Half page down C-d M-Down - Half page up C-u M-Up - Next page C-f Page down - Next word w M-f - Paste buffer p C-y - Previous page C-b Page up - Previous word b M-b - Quit mode q Escape - Scroll down C-Down or J C-Down - Scroll up C-Up or K C-Up - Search again n n - Search backward ? C-r - Search forward / C-s - Start of line 0 C-a - Start selection Space C-Space - Transpose chars C-t - - These key bindings are defined in a set of named tables: vi-edit and - emacs-edit for keys used when line editing at the command prompt; - vi-choice and emacs-choice for keys used when choosing from lists (such - as produced by the window-choose command) or in output mode; and vi-copy - and emacs-copy used in copy mode. The tables may be viewed with the - list-keys command and keys modified or removed with bind-key and - unbind-key. - - The paste buffer key pastes the first line from the top paste buffer on - the stack. - - The mode commands are as follows: - - copy-mode [-u] [-t target-pane] - Enter copy mode. The -u option scrolls one page up. - - Each window displayed by tmux may be split into one or more panes; each - pane takes up a certain area of the display and is a separate terminal. - A window may be split into panes using the split-window command. Windows - may be split horizontally (with the -h flag) or vertically. Panes may be - resized with the resize-pane command (bound to 'C-up', 'C-down' 'C-left' - and 'C-right' by default), the current pane may be changed with the - up-pane and down-pane commands and the rotate-window and swap-pane com- - mands may be used to swap panes without changing their position. Panes - are numbered beginning from zero in the order they are created. - - A number of preset layouts are available. These may be selected with the - select-layout command or cycled with next-layout (bound to 'C-space' by - default); once a layout is chosen, panes within it may be moved and - resized as normal. - - The following layouts are supported: - - even-horizontal - Panes are spread out evenly from left to right across the window. - - even-vertical - Panes are spread evenly from top to bottom. - - main-horizontal - A large (main) pane is shown at the top of the window and the - remaining panes are spread from left to right in the leftover - space at the bottom. Use the main-pane-height window option to - specify the height of the top pane. - - main-vertical - Similar to main-horizontal but the large pane is placed on the - left and the others spread from top to bottom along the right. - See the main-pane-width window option. - - Commands related to windows and panes are as follows: - - break-pane [-d] [-t target-pane] - (alias: breakp) - Break target-pane off from its containing window to make it the - only pane in a new window. If -d is given, the new window does - not become the current window. - - choose-client [-t target-window] [template] - Put a window into client choice mode, allowing a client to be - selected interactively from a list. After a client is chosen, - '%%' is replaced by the client pty(4) path in template and the - result executed as a command. If template is not given, "detach- - client -t '%%'" is used. This command works only from inside - tmux. - - choose-session [-t target-window] [template] - Put a window into session choice mode, where a session may be - selected interactively from a list. When one is chosen, '%%' is - replaced by the session name in template and the result executed - as a command. If template is not given, "switch-client -t '%%'" - is used. This command works only from inside tmux. - - choose-window [-t target-window] [template] - Put a window into window choice mode, where a window may be cho- - sen interactively from a list. After a window is selected, '%%' - is replaced by the session name and window index in template and - the result executed as a command. If template is not given, - "select-window -t '%%'" is used. This command works only from - inside tmux. - - display-panes [-t target-client] - (alias: displayp) - Display a visible indicator of each pane shown by target-client. - See the display-panes-time and display-panes-colour session - options. While the indicator is on screen, a pane may be - selected with the '0' to '9' keys. - - down-pane [-t target-pane] - (alias: downp) - Change the active pane to the next pane (higher index). - - find-window [-t target-window] match-string - (alias: findw) - Search for the fnmatch(3) pattern match-string in window names, - titles, and visible content (but not history). If only one win- - dow is matched, it'll be automatically selected, otherwise a - choice list is shown. This command only works from inside tmux. - - kill-pane [-a] [-t target-pane] - (alias: killp) - Destroy the given pane. If no panes remain in the containing - window, it is also destroyed. The -a option kills all but the - pane given with -t. - - kill-window [-t target-window] - (alias: killw) - Kill the current window or the window at target-window, removing - it from any sessions to which it is linked. - - last-window [-t target-session] - (alias: last) - Select the last (previously selected) window. If no - target-session is specified, select the last window of the cur- - rent session. - - link-window [-dk] [-s src-window] [-t dst-window] - (alias: linkw) - Link the window at src-window to the specified dst-window. If - dst-window is specified and no such window exists, the src-window - is linked there. If -k is given and dst-window exists, it is - killed, otherwise an error is generated. If -d is given, the - newly linked window is not selected. - - list-panes [-t target-window] - (alias: lsp) - List the panes in the current window or in target-window. - - list-windows [-t target-session] - (alias: lsw) - List windows in the current session or in target-session. - - move-window [-d] [-s src-window] [-t dst-window] - (alias: movew) - This is similar to link-window, except the window at src-window - is moved to dst-window. - - new-window [-dk] [-n window-name] [-t target-window] [command] - (alias: neww) - Create a new window. If -d is given, the session does not make - the new window the current window. target-window represents the - window to be created; if the target already exists an error is - shown, unless the -k flag is used, in which case it is destroyed. - command is the command to execute. If command is not specified, - the default command is used. - - The TERM environment variable must be set to ``screen'' for all - programs running inside tmux. New windows will automatically - have ``TERM=screen'' added to their environment, but care must be - taken not to reset this in shell start-up files. - - next-layout [-t target-window] - (alias: nextl) - Move a window to the next layout and rearrange the panes to fit. - - next-window [-a] [-t target-session] - (alias: next) - Move to the next window in the session. If -a is used, move to - the next window with a bell, activity or content alert. - - pipe-pane [-o] [-t target-pane] [command] - (alias: pipep) - Pipe any output sent by the program in target-pane to a shell - command. A pane may only be piped to one command at a time, any - existing pipe is closed before command is executed. If no - command is given, the current pipe (if any) is closed. - - The -o option only opens a new pipe if no previous pipe exists, - allowing a pipe to be toggled with a single key, for example: - - bind-key C-p pipe-pane -o 'cat >>~/output' - - previous-window [-a] [-t target-session] - (alias: prev) - Move to the previous window in the session. With -a, move to the - previous window with a bell, activity or content alert. - - rename-window [-t target-window] new-name - (alias: renamew) - Rename the current window, or the window at target-window if - specified, to new-name. - - resize-pane [-DLRU] [-t target-pane] [adjustment] - (alias: resizep) - Resize a pane, upward with -U (the default), downward with -D, to - the left with -L and to the right with -R. The adjustment is - given in lines or cells (the default is 1). - - respawn-window [-k] [-t target-window] [command] - (alias: respawnw) - Reactive a window in which the command has exited (see the - remain-on-exit window option). If command is not given, the com- - mand used when the window was created is executed. The window - must be already inactive, unless -k is given, in which case any - existing command is killed. - - rotate-window [-DU] [-t target-window] - (alias: rotatew) - Rotate the positions of the panes within a window, either upward - (numerically lower) with -U or downward (numerically higher). - - select-layout [-t target-window] [layout-name] - (alias: selectl) - Choose a specific layout for a window. If layout-name is not - given, the last layout used (if any) is reapplied. - - select-pane [-t target-pane] - (alias: selectp) - Make pane target-pane the active pane in window target-window. - - select-window [-t target-window] - (alias: selectw) - Select the window at target-window. - - split-window [-dhv] [-l size | -p percentage] [-t target-window] - [command] - (alias: splitw) - Creates a new pane by splitting the active pane: -h does a hori- - zontal split and -v a vertical split; if neither is specified, -v - is assumed. The -l and -p options specify the size of the new - window in lines (for vertical split) or in cells (for horizontal - split), or as a percentage, respectively. All other options have - the same meaning as in the new-window command. - - swap-pane [-dDU] [-s src-pane] [-t dst-pane] - (alias: swapp) - Swap two panes. If -U is used and no source pane is specified - with -s, dst-pane is swapped with the previous pane (before it - numerically); -D swaps with the next pane (after it numerically). - - swap-window [-d] [-s src-window] [-t dst-window] - (alias: swapw) - This is similar to link-window, except the source and destination - windows are swapped. It is an error if no window exists at - src-window. - - unlink-window [-k] [-t target-window] - (alias: unlinkw) - Unlink target-window. Unless -k is given, a window may be - unlinked only if it is linked to multiple sessions - windows may - not be linked to no sessions; if -k is specified and the window - is linked to only one session, it is unlinked and destroyed. - - up-pane [-t target-pane] - (alias: upp) - Change the active pane to the previous pane (lower index). - -KEY BINDINGS - tmux allows a command to be bound to most keys, with or without a prefix - key. When specifying keys, most represent themselves (for example 'A' to - 'Z'). Ctrl keys may be prefixed with 'C-' or '^', and Alt (meta) with - 'M-'. In addition, the following special key names are accepted: BSpace, - BTab, DC (Delete), End, Enter, Escape, F1 to F20, Home, IC (Insert), - NPage (Page Up), PPage (Page Down), Space, and Tab. Note that to bind - the '"' or ''' keys, quotation marks are necessary, for example: - - bind-key '"' split-window - bind-key "'" select-prompt - - Commands related to key bindings are as follows: - - bind-key [-cnr] [-t key-table] key command [arguments] - (alias: bind) - Bind key key to command. By default (without -t) the primary key - bindings are modified (those normally activated with the prefix - key); in this case, if -n is specified, it is not necessary to - use the prefix key, command is bound to key alone. The -r flag - indicates this key may repeat, see the repeat-time option. - - If -t is present, key is bound in key-table: the binding for com- - mand mode with -c or for normal mode without. To view the - default bindings and possible commands, see the list-keys com- - mand. - - list-keys [-t key-table] - (alias: lsk) - List all key bindings. Without -t the primary key bindings - - those executed when preceded by the prefix key - are printed. - Keys bound without the prefix key (see bind-key -n) are enclosed - in square brackets. - - With -t, the key bindings in key-table are listed; this may be - one of: vi-edit, emacs-edit, vi-choice, emacs-choice, vi-copy or - emacs-copy. - - send-keys [-t target-pane] key ... - (alias: send) - Send a key or keys to a window. Each argument key is the name of - the key (such as 'C-a' or 'npage' ) to send; if the string is not - recognised as a key, it is sent as a series of characters. All - arguments are sent sequentially from first to last. - - send-prefix [-t target-pane] - Send the prefix key to a window as if it was pressed. If multi- - ple prefix keys are configured, only the first is sent. - - unbind-key [-cn] [-t key-table] key - (alias: unbind) - Unbind the command bound to key. Without -t the primary key - bindings are modified; in this case, if -n is specified, the com- - mand bound to key without a prefix (if any) is removed. - - If -t is present, key in key-table is unbound: the binding for - command mode with -c or for normal mode without. - -OPTIONS - The appearance and behaviour of tmux may be modified by changing the - value of various options. There are two types of option: session options - and window options. - - Each individual session may have a set of session options, and there is a - separate set of global session options. Sessions which do not have a - particular option configured inherit the value from the global session - options. Session options are set or unset with the set-option command - and may be listed with the show-options command. The available session - options are listed under the set-option command. - - Similarly, a set of window options is attached to each window, and there - is a set of global window options from which any unset options are inher- - ited. Window options are altered with the set-window-option command and - can be listed with the show-window-options command. All window options - are documented with the set-window-option command. - - Commands which set options are as follows: - - set-option [-agu] [-t target-session] option value - (alias: set) - Set a session option. With -a, and if the option expects a - string, value is appended to the existing setting. If -g is - specified, the global session option is set. The -u flag unsets - an option, so a session inherits the option from the global - options - it is not possible to unset a global option. - - Available session options are: - - base-index index - Set the base index from which an unused index should be - searched when a new window is created. The default is - zero. - - bell-action [any | none | current] - Set action on window bell. any means a bell in any win- - dow linked to a session causes a bell in the current win- - dow of that session, none means all bells are ignored and - current means only bell in windows other than the current - window are ignored. - - buffer-limit number - Set the number of buffers kept for each session; as new - buffers are added to the top of the stack, old ones are - removed from the bottom if necessary to maintain this - maximum length. - - default-command command - Set the command used for new windows (if not specified - when the window is created) to command, which may be any - sh(1) command. The default is an empty string, which - instructs tmux to create a login shell using the value of - the default-shell option. - - default-shell path - Specify the default shell. This is used as the login - shell for new windows when the default-command option is - set to empty, and must be the full path of the exe- - cutable. When started tmux tries to set a default value - from the first suitable of the SHELL environment vari- - able, the shell returned by getpwuid(3), or /bin/sh. - This option should be configured when tmux is used as a - login shell. - - default-path path - Set the default working directory for processes created - from keys, or interactively from the prompt. The default - is the current working directory when the server is - started. - - default-terminal terminal - Set the default terminal for new windows created in this - session - the default value of the TERM environment vari- - able. For tmux to work correctly, this must be set to - 'screen' or a derivative of it. - - display-panes-colour colour - Set the colour used for the display-panes command. - - display-panes-time time - Set the time in milliseconds for which the indicators - shown by the display-panes command appear. - - display-time time - Set the amount of time for which status line messages and - other on-screen indicators are displayed. time is in - milliseconds. - - history-limit lines - Set the maximum number of lines held in window history. - This setting applies only to new windows - existing win- - dow histories are not resized and retain the limit at the - point they were created. - - lock-after-time number - Lock the session (like the lock-session command) after - number seconds of inactivity, or the entire server (all - sessions) if the lock-server option is set. The default - is not to lock (set to 0). - - lock-command command - Command to run when locking each client. The default is - to run lock(1) with -np. - - lock-server [on | off] - If this option is on (the default), instead of each ses- - sion locking individually as each has been idle for - lock-after-time, the entire server will lock after all - sessions would have locked. This has no effect as a ses- - sion option; it must be set as a global option. - - message-attr attributes - Set status line message attributes, where attributes is - either default or a comma-delimited list of one or more - of: bright (or bold), dim, underscore, blink, reverse, - hidden, or italics. - - message-bg colour - Set status line message background colour, where colour - is one of: black, red, green, yellow, blue, magenta, - cyan, white, colour0 to colour255 from the 256-colour - palette, or default. - - message-fg colour - Set status line message foreground colour. - - mouse-select-pane [on | off] - If on, tmux captures the mouse and when a window is split - into multiple panes the mouse may be used to select the - current pane. The mouse click is also passed through to - the application as normal. - - prefix keys - Set the keys accepted as a prefix key. keys is a comma- - separated list of key names, each of which individually - behave as the prefix key. - - repeat-time time - Allow multiple commands to be entered without pressing - the prefix-key again in the specified time milliseconds - (the default is 500). Whether a key repeats may be set - when it is bound using the -r flag to bind-key. Repeat - is enabled for the default keys bound to the resize-pane - command. - - set-remain-on-exit [on | off] - Set the remain-on-exit window option for any windows - first created in this session. - - set-titles [on | off] - Attempt to set the window title using the \e]2;...\007 - xterm code if the terminal appears to be an xterm. This - option is off by default. Note that elinks will only - attempt to set the window title if the STY environment - variable is set. - - set-titles-string string - String used to set the window title if set-titles is on. - Character sequences are replaced as for the status-left - option. - - status [on | off] - Show or hide the status line. - - status-attr attributes - Set status line attributes. - - status-bg colour - Set status line background colour. - - status-fg colour - Set status line foreground colour. - - status-interval interval - Update the status bar every interval seconds. By - default, updates will occur every 15 seconds. A setting - of zero disables redrawing at interval. - - status-justify [left | centre | right] - Set the position of the window list component of the sta- - tus line: left, centre or right justified. - - status-keys [vi | emacs] - Use vi or emacs-style key bindings in the status line, - for example at the command prompt. Defaults to emacs. - - status-left string - Display string to the left of the status bar. string - will be passed through strftime(3) before being used. By - default, the session name is shown. string may contain - any of the following special character sequences: - - Character pair Replaced with - #(command) First line of command's output - #[attributes] Colour or attribute change - #H Hostname of local host - #I Current window index - #P Current pane index - #S Session name - #T Current window title - #W Current window name - ## A literal '#' - - The #(command) form executes 'command' as a shell command - and inserts the first line of its output. Note that - shell commands are only executed once at the interval - specified by the status-interval option: if the status - line is redrawn in the meantime, the previous result is - used. - - #[attributes] allows a comma-separated list of attributes - to be specified, these may be 'fg=colour' to set the - foreground colour, 'bg=colour' to set the background - colour, the name of one of the attributes (listed under - the message-attr option) to turn an attribute on, or an - attribute prefixed with 'no' to turn one off, for example - nobright. Examples are: - - #(sysctl vm.loadavg) - #[fg=yellow,bold]#(apm -l)%%#[default] [#S] - - Where appropriate, special character sequences may be - prefixed with a number to specify the maximum length, for - example '#24T'. - - By default, UTF-8 in string is not interpreted, to enable - UTF-8, use the status-utf8 option. - - status-left-attr attributes - Set the attribute of the left part of the status line. - - status-left-fg colour - Set the foreground colour of the left part of the status - line. - - status-left-bg colour - Set the background colour of the left part of the status - line. - - status-left-length length - Set the maximum length of the left component of the sta- - tus bar. The default is 10. - - status-right string - Display string to the right of the status bar. By - default, the date and time will be shown. As with - status-left, string will be passed to strftime(3), char- - acter pairs are replaced, and UTF-8 is dependent on the - status-utf8 option. - - status-right-attr attributes - Set the attribute of the right part of the status line. - - status-right-fg colour - Set the foreground colour of the right part of the status - line. - - status-right-bg colour - Set the background colour of the right part of the status - line. - - status-right-length length - Set the maximum length of the right component of the sta- - tus bar. The default is 40. - - status-utf8 [on | off] - Instruct tmux to treat top-bit-set characters in the - status-left and status-right strings as UTF-8; notably, - this is important for wide characters. This option - defaults to off. - - terminal-overrides string - Contains a list of entries which override terminal - descriptions read using terminfo(5). string is a comma- - separated list of items each a colon-separated string - made up of a terminal type pattern (matched using - fnmatch(3)) and a set of name=value entries. - - For example, to set the 'clear' terminfo(5) entry to - '\e[H\e[2J' for all terminal types and the 'dch1' entry - to '\e[P' for the 'rxvt' terminal type, the option could - be set to the string: - - "*:clear=\e[H\e[2J,rxvt:dch1=\e[P" - - The terminal entry value is passed through strunvis(3) - before interpretation. The default value forcibly cor- - rects the 'colors' entry for terminals which support 88 - or 256 colours: - - "*88col*:colors=88,*256col*:colors=256" - - update-environment variables - Set a space-separated string containing a list of envi- - ronment variables to be copied into the session environ- - ment when a new session is created or an existing session - is attached. Any variables that do not exist in the - source environment are set to be removed from the session - environment (as if -r was given to the set-environment - command). The default is "DISPLAY WINDOWID SSH_ASKPASS - SSH_AUTH_SOCK SSH_AGENT_PID SSH_CONNECTION". - - visual-activity [on | off] - If on, display a status line message when activity occurs - in a window for which the monitor-activity window option - is enabled. - - visual-bell [on | off] - If this option is on, a message is shown on a bell - instead of it being passed through to the terminal (which - normally makes a sound). Also see the bell-action - option. - - visual-content [on | off] - Like visual-activity, display a message when content is - present in a window for which the monitor-content window - option is enabled. - - set-window-option [-agu] [-t target-window] option value - (alias: setw) - Set a window option. The -a, -g and -u flags work similarly to - the set-option command. - - Supported window options are: - - aggressive-resize [on | off] - Aggressively resize the chosen window. This means that - tmux will resize the window to the size of the smallest - session for which it is the current window, rather than - the smallest session to which it is attached. The window - may resize when the current window is changed on another - sessions; this option is good for full-screen programs - which support SIGWINCH and poor for interactive programs - such as shells. - - automatic-rename [on | off] - Control automatic window renaming. When this setting is - enabled, tmux will attempt - on supported platforms - to - rename the window to reflect the command currently run- - ning in it. This flag is automatically disabled for an - individual window when a name is specified at creation - with new-window or new-session, or later with - rename-window. It may be switched off globally with: - - set-window-option -g automatic-rename off - - clock-mode-colour colour - Set clock colour. - - clock-mode-style [12 | 24] - Set clock hour format. - - force-height height - force-width width - Prevent tmux from resizing a window to greater than width - or height. A value of zero restores the default unlim- - ited setting. - - main-pane-width width - main-pane-height height - Set the width or height of the main (left or top) pane in - the main-horizontal or main-vertical layouts. - - mode-attr attributes - Set window modes attributes. - - mode-bg colour - Set window modes background colour. - - mode-fg colour - Set window modes foreground colour. - - mode-keys [vi | emacs] - Use vi or emacs-style key bindings in copy and choice - modes. Key bindings default to emacs. - - mode-mouse [on | off] - Mouse state in modes. If on, tmux will respond to mouse - clicks by moving the cursor in copy mode or selecting an - option in choice mode. - - monitor-activity [on | off] - Monitor for activity in the window. Windows with activ- - ity are highlighted in the status line. - - monitor-content match-string - Monitor content in the window. When fnmatch(3) pattern - match-string appears in the window, it is highlighted in - the status line. - - remain-on-exit [on | off] - A window with this flag set is not destroyed when the - program running in it exits. The window may be reacti- - vated with the respawn-window command. - - synchronize-panes [on | off] - Duplicate input to any pane to all other panes in the - same window, except for panes that are not in output - mode. - utf8 [on | off] - Instructs tmux to expect UTF-8 sequences to appear in - this window. - - window-status-attr attributes - Set status line attributes for a single window. - - window-status-bg colour - Set status line background colour for a single window. - - window-status-fg colour - Set status line foreground colour for a single window. - - window-status-current-attr attributes - Set status line attributes for the currently active win- - dow. - - window-status-current-bg colour - Set status line background colour for the currently - active window. - - window-status-current-fg colour - Set status line foreground colour for the currently - active window. - - xterm-keys [on | off] - If this option is set, tmux will generate xterm(1) -style - function key sequences; these have a number included to - indicate modifiers such as Shift, Alt or Ctrl. - - show-options [-g] [-t target-session] - (alias: show) - Show the session options for target session, or the global ses- - sion options with -g. - - show-window-options [-g] [-t target-window] - (alias: showw) - List the window options for target-window, or the global window - options if -g is used. - -ENVIRONMENT - When the server is started, tmux copies the environment into the global - environment; in addition, each session has a session environment. When a - window is created, the session and global environments are merged with - the session environment overriding any variable present in both. This is - the initial environment passed to the new process. - - The update-environment session option may be used to update the session - environment from the client when a new session is created or an old reat- - tached. tmux also initialises the TMUX variable with some internal - information to allow commands to be executed from inside, and the TERM - variable with the correct terminal setting of 'screen'. - - Commands to alter and view the environment are: - - set-environment [-gru] [-t target-session] name [value] - (alias: setenv) - Set or unset an environment variable. If -g is used, the change - is made in the global environment; otherwise, it is applied to - the session environment for target-session. The -u flag unsets a - variable. -r indicates the variable is to be removed from the - environment before starting a new process. - - show-environment [-g] [-t target-session] - (alias: showenv) - Display the environment for target-session or the global environ- - ment with -g. Variables removed from the environment are pre- - fixed with '-'. - -STATUS LINE - tmux includes an optional status line which is displayed in the bottom - line of each terminal. By default, the status line is enabled (it may be - disabled with the status session option) and contains, from left-to- - right: the name of the current session in square brackets; the window - list; the current window title in double quotes; and the time and date. - - The status line is made of three parts: configurable left and right sec- - tions (which may contain dynamic content such as the time or output from - a shell command, see the status-left, status-left-length, status-right, - and status-right-length options below), and a central window list. The - window list shows the index, name and (if any) flag of the windows - present in the current session in ascending numerical order. The flag is - one of the following symbols appended to the window name: - - Symbol Meaning - * Denotes the current window. - - Marks the last window (previously selected). - # Window is monitored and activity has been detected. - ! A bell has occurred in the window. - + Window is monitored for content and it has appeared. - - The # symbol relates to the monitor-activity and + to the monitor-content - window options. The window name is printed in inverted colours if an - alert (bell, activity or content) is present. - - The colour and attributes of the status line may be configured, the - entire status line using the status-attr, status-fg and status-bg session - options and individual windows using the window-status-attr, - window-status-fg and window-status-bg window options. - - The status line is automatically refreshed at interval if it has changed, - the interval may be controlled with the status-interval session option. - - Commands related to the status line are as follows: - - command-prompt [-p prompts] [-t target-client] [template] - Open the command prompt in a client. This may be used from - inside tmux to execute commands interactively. If template is - specified, it is used as the command. If -p is given, prompts is - a comma-separated list of prompts which are displayed in order; - otherwise a single prompt is displayed, constructed from template - if it is present, or ':' if not. Before the command is executed, - the first occurrence of the string '%%' and all occurrences of - '%1' are replaced by the response to the first prompt, the second - '%%' and all '%2' are replaced with the response to the second - prompt, and so on for further prompts. Up to nine prompt - responses may be replaced ('%1' to '%9'). - - confirm-before [-t target-client] command - (alias: confirm) - Ask for confirmation before executing command. This command - works only from inside tmux. - - display-message [-t target-client] [message] - (alias: display) - Display a message (see the status-left option below) in the sta- - tus line. - - select-prompt [-t target-client] - Open a prompt inside target-client allowing a window index to be - entered interactively. - -BUFFERS - tmux maintains a stack of paste buffers for each session. Up to the - value of the buffer-limit option are kept; when a new buffer is added, - the buffer at the bottom of the stack is removed. Buffers may be added - using copy-mode or the set-buffer command, and pasted into a window using - the paste-buffer command. - - A configurable history buffer is also maintained for each window. By - default, up to 2000 lines are kept; this can be altered with the - history-limit option (see the set-option command above). - - The buffer commands are as follows: - - clear-history [-t target-pane] - (alias: clearhist) - Remove and free the history for the specified pane. - - copy-buffer [-a src-index] [-b dst-index] [-s src-session] [-t - dst-session] - (alias: copyb) - Copy a session paste buffer to another session. If no sessions - are specified, the current one is used instead. - - delete-buffer [-b buffer-index] [-t target-session] - (alias: deleteb) - Delete the buffer at buffer-index, or the top buffer if not spec- - ified. - - list-buffers [-t target-session] - (alias: lsb) - List the buffers in the given session. - - load-buffer [-b buffer-index] [-t target-session] path - (alias: loadb) - Load the contents of the specified paste buffer from path. - - paste-buffer [-dr] [-b buffer-index] [-t target-window] - (alias: pasteb) - Insert the contents of a paste buffer into the current window. - With -d, also delete the paste buffer from the stack. When out- - put, any linefeed (LF) characters in the paste buffer are - replaced with carriage returns (CR). This translation may be - disabled with the -r flag. - - save-buffer [-a] [-b buffer-index] [-t target-session] path - (alias: saveb) - Save the contents of the specified paste buffer to path. The -a - option appends to rather than overwriting the file. - - set-buffer [-b buffer-index] [-t target-session] data - (alias: setb) - Set the contents of the specified buffer to data. - - show-buffer [-b buffer-index] [-t target-session] - (alias: showb) - Display the contents of the specified buffer. - -MISCELLANEOUS - Miscellaneous commands are as follows: - - clock-mode [-t target-pane] - Display a large clock. - - if-shell shell-command command - (alias: if) - Execute command if shell-command returns success. - - lock-server - (alias: lock) - Lock each client individually by running the command specified by - the lock-command option. - - run-shell command - (alias: run) - Execute command in the background without creating a window. - After the command finishes, any output to stdout is displayed in - output mode. If command doesn't return success, the exit status - is also displayed. - - server-info - (alias: info) - Show server information and terminal details. - -FILES - ~/.tmux.conf Default tmux configuration file. - /etc/tmux.conf System-wide configuration file. - -EXAMPLES - To create a new tmux session running vi(1): - - $ tmux new-session vi - - Most commands have a shorter form, known as an alias. For new-session, - this is new: - - $ tmux new vi - - Alternatively, the shortest unambiguous form of a command is accepted. - If there are several options, they are listed: - - $ tmux n - ambiguous command: n, could be: new-session, new-window, next-window - - Within an active session, a new window may be created by typing 'C-b c' - (Ctrl followed by the 'b' key followed by the 'c' key). - - Windows may be navigated with: 'C-b 0' (to select window 0), 'C-b 1' (to - select window 1), and so on; 'C-b n' to select the next window; and 'C-b - p' to select the previous window. - - A session may be detached using 'C-b d' (or by an external event such as - ssh(1) disconnection) and reattached with: - - $ tmux attach-session - - Typing 'C-b ?' lists the current key bindings in the current window; up - and down may be used to navigate the list or 'q' to exit from it. - - Commands to be run when the tmux server is started may be placed in the - ~/.tmux.conf configuration file. Common examples include: - - Changing the default prefix key: - - set-option -g prefix C-a - unbind-key C-b - bind-key C-a send-prefix - - Turning the status line off, or changing its colour: - - set-option -g status off - set-option -g status-bg blue - - Setting other options, such as the default command, or locking after 30 - minutes of inactivity: - - set-option -g default-command "exec /bin/ksh" - set-option -g lock-after-time 1800 - - Creating new key bindings: - - bind-key b set-option status - bind-key / command-prompt "split-window 'exec man %%'" - bind-key S command-prompt "new-window -n %1 'ssh %1'" - -SEE ALSO - pty(4) - -AUTHORS - Nicholas Marriott - -BSD October 19, 2013 BSD diff --git a/manual/1.2 b/manual/1.2 deleted file mode 100644 index c76e1623b52..00000000000 --- a/manual/1.2 +++ /dev/null @@ -1,1473 +0,0 @@ -TMUX(1) BSD General Commands Manual TMUX(1) - -NAME - tmux -- terminal multiplexer - -SYNOPSIS - tmux [-28lquv] [-c shell-command] [-f file] [-L socket-name] - [-S socket-path] [command [flags]] - -DESCRIPTION - tmux is a terminal multiplexer: it enables a number of terminals to be - created, accessed, and controlled from a single screen. tmux may be - detached from a screen and continue running in the background, then later - reattached. - - When tmux is started it creates a new session with a single window and - displays it on screen. A status line at the bottom of the screen shows - information on the current session and is used to enter interactive com- - mands. - - A session is a single collection of pseudo terminals under the management - of tmux. Each session has one or more windows linked to it. A window - occupies the entire screen and may be split into rectangular panes, each - of which is a separate pseudo terminal (the pty(4) manual page documents - the technical details of pseudo terminals). Any number of tmux instances - may connect to the same session, and any number of windows may be present - in the same session. Once all sessions are killed, tmux exits. - - Each session is persistent and will survive accidental disconnection - (such as ssh(1) connection timeout) or intentional detaching (with the - 'C-b d' key strokes). tmux may be reattached using: - - $ tmux attach - - In tmux, a session is displayed on screen by a client and all sessions - are managed by a single server. The server and each client are separate - processes which communicate through a socket in /tmp. - - The options are as follows: - - -2 Force tmux to assume the terminal supports 256 colours. - - -8 Like -2, but indicates that the terminal supports 88 - colours. - - -c shell-command - Execute shell-command using the default shell. If neces- - sary, the tmux server will be started to retrieve the - default-shell option. This option is for compatibility - with sh(1) when tmux is used as a login shell. - - -f file Specify an alternative configuration file. By default, - tmux loads the system configuration file from - /etc/tmux.conf, if present, then looks for a user configu- - ration file at ~/.tmux.conf. The configuration file is a - set of tmux commands which are executed in sequence when - the server is first started. - - If a command in the configuration file fails, tmux will - report an error and exit without executing further com- - mands. - - -l Behave as a login shell. This flag currently has no effect - and is for compatibility with other shells when using tmux - as a login shell. - - -L socket-name - tmux stores the server socket in a directory under /tmp; - the default socket is named default. This option allows a - different socket name to be specified, allowing several - independent tmux servers to be run. Unlike -S a full path - is not necessary: the sockets are all created in the same - directory. - - If the socket is accidentally removed, the SIGUSR1 signal - may be sent to the tmux server process to recreate it. - - -q Set the quiet server option to prevent the server sending - various informational messages. - - -S socket-path - Specify a full alternative path to the server socket. If - -S is specified, the default socket directory is not used - and any -L flag is ignored. - - -u tmux attempts to guess if the terminal is likely to support - UTF-8 by checking the first of the LC_ALL, LC_CTYPE and - LANG environment variables to be set for the string - "UTF-8". This is not always correct: the -u flag explic- - itly informs tmux that UTF-8 is supported. - - If the server is started from a client passed -u or where - UTF-8 is detected, the utf8 and status-utf8 options are - enabled in the global window and session options respec- - tively. - - -v Request verbose logging. This option may be specified mul- - tiple times for increasing verbosity. Log messages will be - saved into tmux-client-PID.log and tmux-server-PID.log - files in the current directory, where PID is the PID of the - server or client process. - - command [flags] - This specifies one of a set of commands used to control - tmux, as described in the following sections. If no com- - mands are specified, the new-session command is assumed. - -KEY BINDINGS - tmux may be controlled from an attached client by using a key combination - of a prefix key, 'C-b' (Ctrl-b) by default, followed by a command key. - - Some of the default key bindings are: - - c Create a new window. - d Detach the current client. - l Move to the previously selected window. - n Change to the next window. - p Change to the previous window. - & Kill the current window. - , Rename the current window. - ? List all key bindings. - - A complete list may be obtained with the list-keys command (bound to '?' - by default). Key bindings may be changed with the bind-key and - unbind-key commands. - -COMMANDS - This section contains a list of the commands supported by tmux. Most - commands accept the optional -t argument with one of target-client, - target-session target-window, or target-pane. These specify the client, - session, window or pane which a command should affect. target-client is - the name of the pty(4) file to which the client is connected, for example - either of /dev/ttyp1 or ttyp1 for the client attached to /dev/ttyp1. If - no client is specified, the current client is chosen, if possible, or an - error is reported. Clients may be listed with the list-clients command. - - target-session is either the name of a session (as listed by the - list-sessions command) or the name of a client with the same syntax as - target-client, in which case the session attached to the client is used. - When looking for the session name, tmux initially searches for an exact - match; if none is found, the session names are checked for any for which - target-session is a prefix or for which it matches as an fnmatch(3) pat- - tern. If a single match is found, it is used as the target session; mul- - tiple matches produce an error. If a session is omitted, the current - session is used if available; if no current session is available, the - most recently used is chosen. - - target-window specifies a window in the form session:window. session - follows the same rules as for target-session, and window is looked for in - order: as a window index, for example mysession:1; as an exact window - name, such as mysession:mywindow; then as an fnmatch(3) pattern or the - start of a window name, such as mysession:mywin* or mysession:mywin. An - empty window name specifies the next unused index if appropriate (for - example the new-window and link-window commands) otherwise the current - window in session is chosen. The special character '!' uses the last - (previously current) window, or '+' and '-' are the next window or the - previous window by number. When the argument does not contain a colon, - tmux first attempts to parse it as window; if that fails, an attempt is - made to match a session. - - target-pane takes a similar form to target-window but with the optional - addition of a period followed by a pane index, for example: myses- - sion:mywindow.1. If the pane index is omitted, the currently active pane - in the specified window is used. If neither a colon nor period appears, - tmux first attempts to use the argument as a pane index; if that fails, - it is looked up as for target-window. One of the strings top, bottom, - left, right, top-left, top-right, bottom-left or bottom-right may be used - instead of a pane index. - - shell-command arguments are sh(1) commands. These must be passed as a - single item, which typically means quoting them, for example: - - new-window 'vi /etc/passwd' - - command [arguments] refers to a tmux command, passed with the command and - arguments separately, for example: - - bind-key F1 set-window-option force-width 81 - - Or if using sh(1): - - $ tmux bind-key F1 set-window-option force-width 81 - - Multiple commands may be specified together as part of a command - sequence. Each command should be separated by spaces and a semicolon; - commands are executed sequentially from left to right. A literal semi- - colon may be included by escaping it with a backslash (for example, when - specifying a command sequence to bind-key). - - Example tmux commands include: - - refresh-client -t/dev/ttyp2 - - rename-session -tfirst newname - - set-window-option -t:0 monitor-activity on - - new-window ; split-window -d - - Or from sh(1): - - $ tmux kill-window -t :1 - - $ tmux new-window \; split-window -d - - $ tmux new-session -d 'vi /etc/passwd' \; split-window -d \; attach - -CLIENTS AND SESSIONS - The tmux server manages clients, sessions, windows and panes. Clients - are attached to sessions to interact with them, either when they are cre- - ated with the new-session command, or later with the attach-session com- - mand. Each session has one of more windows linked into it. Windows may - be linked to multiple sessions and are made up of one or more panes, each - of which contains a pseudo terminal. Commands for creating, linking and - otherwise manipulating windows are covered in the WINDOWS AND PANES sec- - tion. - - The following commands are available to manage clients and sessions: - - attach-session [-dr] [-t target-session] - (alias: attach) - If run from outside tmux, create a new client in the current ter- - minal and attach it to target-session. If used from inside, - switch the current client. If -d is specified, any other clients - attached to the session are detached. -r signifies the client is - read-only (only keys bound to the detach-client command have any - effect) - - If no server is started, attach-session will attempt to start it; - this will fail unless sessions are created in the configuration - file. - - detach-client [-t target-client] - (alias: detach) - Detach the current client if bound to a key, or the specified - client with -t. - - has-session [-t target-session] - (alias: has) - Report an error and exit with 1 if the specified session does not - exist. If it does exist, exit with 0. - - kill-server - Kill the tmux server and clients and destroy all sessions. - - kill-session [-t target-session] - Destroy the given session, closing any windows linked to it and - no other sessions, and detaching all clients attached to it. - - list-clients - (alias: lsc) - List all clients attached to the server. - - list-commands - (alias: lscm) - List the syntax of all commands supported by tmux. - - list-sessions - (alias: ls) - List all sessions managed by the server. - - lock-client [-t target-client] - Lock target-client, see the lock-server command. - - lock-session [-t target-session] - Lock all clients attached to target-session. - - new-session [-d] [-n window-name] [-s session-name] [-t target-session] - [shell-command] - (alias: new) - Create a new session with name session-name. - - The new session is attached to the current terminal unless -d is - given. window-name and shell-command are the name of and shell - command to execute in the initial window. - - If run from a terminal, any termios(4) special characters are - saved and used for new windows in the new session. - - If -t is given, the new session is grouped with target-session. - This means they share the same set of windows - all windows from - target-session are linked to the new session and any subsequent - new windows or windows being closed are applied to both sessions. - The current and previous window and any session options remain - independent and either session may be killed without affecting - the other. Giving -n or shell-command are invalid if -t is used. - - refresh-client [-t target-client] - (alias: refresh) - Refresh the current client if bound to a key, or a single client - if one is given with -t. - - rename-session [-t target-session] new-name - (alias: rename) - Rename the session to new-name. - - show-messages [-t target-client] - (alias: showmsgs) - Any messages displayed on the status line are saved in a per- - client message log, up to a maximum of the limit set by the - message-limit session option for the session attached to that - client. This command displays the log for target-client. - - source-file path - (alias: source) - Execute commands from path. - - start-server - (alias: start) - Start the tmux server, if not already running, without creating - any sessions. - - suspend-client [-c target-client] - (alias: suspendc) - Suspend a client by sending SIGTSTP (tty stop). - - switch-client [-c target-client] [-t target-session] - (alias: switchc) - Switch the current session for client target-client to - target-session. - -WINDOWS AND PANES - A tmux window may be in one of several modes. The default permits direct - access to the terminal attached to the window. The others are: - - output mode - This is entered when a command which produces output, such as - list-keys, is executed from a key binding. - - copy mode - This permits a section of a window or its history to be copied to - a paste buffer for later insertion into another window. This - mode is entered with the copy-mode command, bound to '[' by - default. - - The keys available depend on whether emacs or vi mode is selected (see - the mode-keys option). The following keys are supported as appropriate - for the mode: - - Function vi emacs - Back to indentation ^ M-m - Bottom of history G M-< - Clear selection Escape C-g - Copy selection Enter M-w - Cursor down j Down - Cursor left h Left - Cursor right l Right - Cursor to bottom line L - Cursor to middle line M M-r - Cursor to top line H M-R - Cursor up k Up - Delete entire line d C-u - Delete to end of line D C-k - End of line $ C-e - Go to line : g - Half page down C-d M-Down - Half page up C-u M-Up - Next page C-f Page down - Next space W - Next space, end of word E - Next word w - Next word end e M-f - Paste buffer p C-y - Previous page C-b Page up - Previous word b M-b - Previous space B - Quit mode q Escape - Rectangle toggle v R - Scroll down C-Down or C-e C-Down - Scroll up C-Up or C-y C-Up - Search again n n - Search again in reverse N N - Search backward ? C-r - Search forward / C-s - Start of line 0 C-a - Start selection Space C-Space - Top of history g M-> - Transpose chars C-t - - The next and previous word keys use space and the '-', '_' and '@' char- - acters as word delimiters by default, but this can be adjusted by setting - the word-separators window option. Next word moves to the start of the - next word, next word end to the end of the next word and previous word to - the start of the previous word. The three next and previous space keys - work similarly but use a space alone as the word separator. - - Commands in copy mode may be prefaced by an optional repeat count. With - vi key bindings, a prefix is entered using the number keys; with emacs, - the Alt (meta) key and a number begins prefix entry. For example, to - move the cursor forward by ten words, use 'M-1 0 M-f' in emacs mode, and - '10w' in vi. - - Mode key bindings are defined in a set of named tables: vi-edit and - emacs-edit for keys used when line editing at the command prompt; - vi-choice and emacs-choice for keys used when choosing from lists (such - as produced by the choose-window command) or in output mode; and vi-copy - and emacs-copy used in copy mode. The tables may be viewed with the - list-keys command and keys modified or removed with bind-key and - unbind-key. - - The paste buffer key pastes the first line from the top paste buffer on - the stack. - - The mode commands are as follows: - - copy-mode [-u] [-t target-pane] - Enter copy mode. The -u option scrolls one page up. - - Each window displayed by tmux may be split into one or more panes; each - pane takes up a certain area of the display and is a separate terminal. - A window may be split into panes using the split-window command. Windows - may be split horizontally (with the -h flag) or vertically. Panes may be - resized with the resize-pane command (bound to 'C-up', 'C-down' 'C-left' - and 'C-right' by default), the current pane may be changed with the - up-pane and down-pane commands and the rotate-window and swap-pane com- - mands may be used to swap panes without changing their position. Panes - are numbered beginning from zero in the order they are created. - - A number of preset layouts are available. These may be selected with the - select-layout command or cycled with next-layout (bound to 'Space' by - default); once a layout is chosen, panes within it may be moved and - resized as normal. - - The following layouts are supported: - - even-horizontal - Panes are spread out evenly from left to right across the window. - - even-vertical - Panes are spread evenly from top to bottom. - - main-horizontal - A large (main) pane is shown at the top of the window and the - remaining panes are spread from left to right in the leftover - space at the bottom. Use the main-pane-height window option to - specify the height of the top pane. - - main-vertical - Similar to main-horizontal but the large pane is placed on the - left and the others spread from top to bottom along the right. - See the main-pane-width window option. - - Commands related to windows and panes are as follows: - - break-pane [-d] [-t target-pane] - (alias: breakp) - Break target-pane off from its containing window to make it the - only pane in a new window. If -d is given, the new window does - not become the current window. - - capture-pane [-b buffer-index] [-t target-pane] - (alias: capturep) - Capture the contents of a pane to the specified buffer, or a new - buffer if none is specified. - - choose-client [-t target-window] [template] - Put a window into client choice mode, allowing a client to be - selected interactively from a list. After a client is chosen, - '%%' is replaced by the client pty(4) path in template and the - result executed as a command. If template is not given, "detach- - client -t '%%'" is used. This command works only from inside - tmux. - - choose-session [-t target-window] [template] - Put a window into session choice mode, where a session may be - selected interactively from a list. When one is chosen, '%%' is - replaced by the session name in template and the result executed - as a command. If template is not given, "switch-client -t '%%'" - is used. This command works only from inside tmux. - - choose-window [-t target-window] [template] - Put a window into window choice mode, where a window may be cho- - sen interactively from a list. After a window is selected, '%%' - is replaced by the session name and window index in template and - the result executed as a command. If template is not given, - "select-window -t '%%'" is used. This command works only from - inside tmux. - - display-panes [-t target-client] - (alias: displayp) - Display a visible indicator of each pane shown by target-client. - See the display-panes-time, display-panes-colour, and - display-panes-active-colour session options. While the indicator - is on screen, a pane may be selected with the '0' to '9' keys. - - down-pane [-t target-pane] - (alias: downp) - Change the active pane to the next pane (higher index). - - find-window [-t target-window] match-string - (alias: findw) - Search for the fnmatch(3) pattern match-string in window names, - titles, and visible content (but not history). If only one win- - dow is matched, it'll be automatically selected, otherwise a - choice list is shown. This command only works from inside tmux. - - join-pane [-dhv] [-l size | -p percentage] [-s src-pane] [-t dst-pane] - (alias: joinp) - Like split-window, but instead of splitting dst-pane and creating - a new pane, split it and move src-pane into the space. This can - be used to reverse break-pane. - - kill-pane [-a] [-t target-pane] - (alias: killp) - Destroy the given pane. If no panes remain in the containing - window, it is also destroyed. The -a option kills all but the - pane given with -t. - - kill-window [-t target-window] - (alias: killw) - Kill the current window or the window at target-window, removing - it from any sessions to which it is linked. - - last-window [-t target-session] - (alias: last) - Select the last (previously selected) window. If no - target-session is specified, select the last window of the cur- - rent session. - - link-window [-dk] [-s src-window] [-t dst-window] - (alias: linkw) - Link the window at src-window to the specified dst-window. If - dst-window is specified and no such window exists, the src-window - is linked there. If -k is given and dst-window exists, it is - killed, otherwise an error is generated. If -d is given, the - newly linked window is not selected. - - list-panes [-t target-window] - (alias: lsp) - List the panes in the current window or in target-window. - - list-windows [-t target-session] - (alias: lsw) - List windows in the current session or in target-session. - - move-window [-d] [-s src-window] [-t dst-window] - (alias: movew) - This is similar to link-window, except the window at src-window - is moved to dst-window. - - new-window [-dk] [-n window-name] [-t target-window] [shell-command] - (alias: neww) - Create a new window. If -d is given, the session does not make - the new window the current window. target-window represents the - window to be created; if the target already exists an error is - shown, unless the -k flag is used, in which case it is destroyed. - shell-command is the command to execute. If shell-command is not - specified, the value of the default-command option is used. - - When the shell command completes, the window closes. See the - remain-on-exit option to change this behaviour. - - The TERM environment variable must be set to ``screen'' for all - programs running inside tmux. New windows will automatically - have ``TERM=screen'' added to their environment, but care must be - taken not to reset this in shell start-up files. - - next-layout [-t target-window] - (alias: nextl) - Move a window to the next layout and rearrange the panes to fit. - - next-window [-a] [-t target-session] - (alias: next) - Move to the next window in the session. If -a is used, move to - the next window with a bell, activity or content alert. - - pipe-pane [-o] [-t target-pane] [shell-command] - (alias: pipep) - Pipe any output sent by the program in target-pane to a shell - command. A pane may only be piped to one command at a time, any - existing pipe is closed before shell-command is executed. If no - shell-command is given, the current pipe (if any) is closed. - - The -o option only opens a new pipe if no previous pipe exists, - allowing a pipe to be toggled with a single key, for example: - - bind-key C-p pipe-pane -o 'cat >>~/output' - - previous-window [-a] [-t target-session] - (alias: prev) - Move to the previous window in the session. With -a, move to the - previous window with a bell, activity or content alert. - - rename-window [-t target-window] new-name - (alias: renamew) - Rename the current window, or the window at target-window if - specified, to new-name. - - resize-pane [-DLRU] [-t target-pane] [adjustment] - (alias: resizep) - Resize a pane, upward with -U (the default), downward with -D, to - the left with -L and to the right with -R. The adjustment is - given in lines or cells (the default is 1). - - respawn-window [-k] [-t target-window] [shell-command] - (alias: respawnw) - Reactivate a window in which the command has exited (see the - remain-on-exit window option). If shell-command is not given, - the command used when the window was created is executed. The - window must be already inactive, unless -k is given, in which - case any existing command is killed. - - rotate-window [-DU] [-t target-window] - (alias: rotatew) - Rotate the positions of the panes within a window, either upward - (numerically lower) with -U or downward (numerically higher). - - select-layout [-t target-window] [layout-name] - (alias: selectl) - Choose a specific layout for a window. If layout-name is not - given, the last layout used (if any) is reapplied. - - select-pane [-t target-pane] - (alias: selectp) - Make pane target-pane the active pane in window target-window. - - select-window [-t target-window] - (alias: selectw) - Select the window at target-window. - - split-window [-dhv] [-l size | -p percentage] [-t target-pane] - [shell-command] - (alias: splitw) - Create a new pane by splitting target-pane: -h does a horizontal - split and -v a vertical split; if neither is specified, -v is - assumed. The -l and -p options specify the size of the new pane - in lines (for vertical split) or in cells (for horizontal split), - or as a percentage, respectively. All other options have the - same meaning as for the new-window command. - - swap-pane [-dDU] [-s src-pane] [-t dst-pane] - (alias: swapp) - Swap two panes. If -U is used and no source pane is specified - with -s, dst-pane is swapped with the previous pane (before it - numerically); -D swaps with the next pane (after it numerically). - -d instructs tmux not to change the active pane. - - swap-window [-d] [-s src-window] [-t dst-window] - (alias: swapw) - This is similar to link-window, except the source and destination - windows are swapped. It is an error if no window exists at - src-window. - - unlink-window [-k] [-t target-window] - (alias: unlinkw) - Unlink target-window. Unless -k is given, a window may be - unlinked only if it is linked to multiple sessions - windows may - not be linked to no sessions; if -k is specified and the window - is linked to only one session, it is unlinked and destroyed. - - up-pane [-t target-pane] - (alias: upp) - Change the active pane to the previous pane (lower index). - -KEY BINDINGS - tmux allows a command to be bound to most keys, with or without a prefix - key. When specifying keys, most represent themselves (for example 'A' to - 'Z'). Ctrl keys may be prefixed with 'C-' or '^', and Alt (meta) with - 'M-'. In addition, the following special key names are accepted: Up, - Down, Left, Right, BSpace, BTab, DC (Delete), End, Enter, Escape, F1 to - F20, Home, IC (Insert), NPage (Page Up), PPage (Page Down), Space, and - Tab. Note that to bind the '"' or ''' keys, quotation marks are neces- - sary, for example: - - bind-key '"' split-window - bind-key "'" select-prompt - - Commands related to key bindings are as follows: - - bind-key [-cnr] [-t key-table] key command [arguments] - (alias: bind) - Bind key key to command. By default (without -t) the primary key - bindings are modified (those normally activated with the prefix - key); in this case, if -n is specified, it is not necessary to - use the prefix key, command is bound to key alone. The -r flag - indicates this key may repeat, see the repeat-time option. - - If -t is present, key is bound in key-table: the binding for com- - mand mode with -c or for normal mode without. To view the - default bindings and possible commands, see the list-keys com- - mand. - - list-keys [-t key-table] - (alias: lsk) - List all key bindings. Without -t the primary key bindings - - those executed when preceded by the prefix key - are printed. - Keys bound without the prefix key (see bind-key -n) are marked - with '(no prefix)'. - - With -t, the key bindings in key-table are listed; this may be - one of: vi-edit, emacs-edit, vi-choice, emacs-choice, vi-copy or - emacs-copy. - - send-keys [-t target-pane] key ... - (alias: send) - Send a key or keys to a window. Each argument key is the name of - the key (such as 'C-a' or 'npage' ) to send; if the string is not - recognised as a key, it is sent as a series of characters. All - arguments are sent sequentially from first to last. - - send-prefix [-t target-pane] - Send the prefix key to a window as if it was pressed. If multi- - ple prefix keys are configured, only the first is sent. - - unbind-key [-cn] [-t key-table] key - (alias: unbind) - Unbind the command bound to key. Without -t the primary key - bindings are modified; in this case, if -n is specified, the com- - mand bound to key without a prefix (if any) is removed. - - If -t is present, key in key-table is unbound: the binding for - command mode with -c or for normal mode without. - -OPTIONS - The appearance and behaviour of tmux may be modified by changing the - value of various options. There are three types of option: server - options, session options and window options. - - The tmux server has a set of global options which do not apply to any - particular window or session. These are altered with the set-option -s - command, or displayed with the show-options -s command. - - In addition, each individual session may have a set of session options, - and there is a separate set of global session options. Sessions which do - not have a particular option configured inherit the value from the global - session options. Session options are set or unset with the set-option - command and may be listed with the show-options command. The available - server and session options are listed under the set-option command. - - Similarly, a set of window options is attached to each window, and there - is a set of global window options from which any unset options are inher- - ited. Window options are altered with the set-window-option command and - can be listed with the show-window-options command. All window options - are documented with the set-window-option command. - - Commands which set options are as follows: - - set-option [-agsuw] [-t target-session | target-window] option value - (alias: set) - Set a window option with -w (equivalent to the set-window-option - command), a server option with -s, otherwise a session option. - - If -g is specified, the global session or window option is set. - With -a, and if the option expects a string, value is appended to - the existing setting. The -u flag unsets an option, so a session - inherits the option from the global options. It is not possible - to unset a global option. - - Available window options are listed under set-window-option. - - Available server options are: - - escape-time - Set the time in milliseconds for which tmux waits after - an escape is input to determine if it is part of a func- - tion or meta key sequences. The default is 500 millisec- - onds. - - quiet Enable or disable the display of various informational - messages (see also the -q command line flag). - - Available session options are: - - base-index index - Set the base index from which an unused index should be - searched when a new window is created. The default is - zero. - - bell-action [any | none | current] - Set action on window bell. any means a bell in any win- - dow linked to a session causes a bell in the current win- - dow of that session, none means all bells are ignored and - current means only bell in windows other than the current - window are ignored. - - buffer-limit number - Set the number of buffers kept for each session; as new - buffers are added to the top of the stack, old ones are - removed from the bottom if necessary to maintain this - maximum length. - - default-command shell-command - Set the command used for new windows (if not specified - when the window is created) to shell-command, which may - be any sh(1) command. The default is an empty string, - which instructs tmux to create a login shell using the - value of the default-shell option. - - default-shell path - Specify the default shell. This is used as the login - shell for new windows when the default-command option is - set to empty, and must be the full path of the exe- - cutable. When started tmux tries to set a default value - from the first suitable of the SHELL environment vari- - able, the shell returned by getpwuid(3), or /bin/sh. - This option should be configured when tmux is used as a - login shell. - - default-path path - Set the default working directory for processes created - from keys, or interactively from the prompt. The default - is the current working directory when the server is - started. - - default-terminal terminal - Set the default terminal for new windows created in this - session - the default value of the TERM environment vari- - able. For tmux to work correctly, this must be set to - 'screen' or a derivative of it. - - display-panes-active-colour colour - Set the colour used by the display-panes command to show - the indicator for the active pane. - - display-panes-colour colour - Set the colour used by the display-panes command to show - the indicators for inactive panes. - - display-panes-time time - Set the time in milliseconds for which the indicators - shown by the display-panes command appear. - - display-time time - Set the amount of time for which status line messages and - other on-screen indicators are displayed. time is in - milliseconds. - - history-limit lines - Set the maximum number of lines held in window history. - This setting applies only to new windows - existing win- - dow histories are not resized and retain the limit at the - point they were created. - - lock-after-time number - Lock the session (like the lock-session command) after - number seconds of inactivity, or the entire server (all - sessions) if the lock-server option is set. The default - is not to lock (set to 0). - - lock-command shell-command - Command to run when locking each client. The default is - to run lock(1) with -np. - - lock-server [on | off] - If this option is on (the default), instead of each ses- - sion locking individually as each has been idle for - lock-after-time, the entire server will lock after all - sessions would have locked. This has no effect as a ses- - sion option; it must be set as a global option. - - message-attr attributes - Set status line message attributes, where attributes is - either default or a comma-delimited list of one or more - of: bright (or bold), dim, underscore, blink, reverse, - hidden, or italics. - - message-bg colour - Set status line message background colour, where colour - is one of: black, red, green, yellow, blue, magenta, - cyan, white, colour0 to colour255 from the 256-colour - palette, or default. - - message-fg colour - Set status line message foreground colour. - - message-limit number - Set the number of error or information messages to save - in the message log for each client. The default is 20. - - mouse-select-pane [on | off] - If on, tmux captures the mouse and when a window is split - into multiple panes the mouse may be used to select the - current pane. The mouse click is also passed through to - the application as normal. - - pane-border-fg colour - - pane-border-bg colour - Set the pane border colour for panes aside from the - active pane. - - pane-active-border-fg colour - - pane-active-border-bg colour - Set the pane border colour for the currently active pane. - - prefix keys - Set the keys accepted as a prefix key. keys is a comma- - separated list of key names, each of which individually - behave as the prefix key. - - repeat-time time - Allow multiple commands to be entered without pressing - the prefix-key again in the specified time milliseconds - (the default is 500). Whether a key repeats may be set - when it is bound using the -r flag to bind-key. Repeat - is enabled for the default keys bound to the resize-pane - command. - - set-remain-on-exit [on | off] - Set the remain-on-exit window option for any windows - first created in this session. When this option is true, - windows in which the running program has exited do not - close, instead remaining open but inactivate. Use the - respawn-window command to reactivate such a window, or - the kill-window command to destroy it. - - set-titles [on | off] - Attempt to set the window title using the \e]2;...\007 - xterm code if the terminal appears to be an xterm. This - option is off by default. Note that elinks will only - attempt to set the window title if the STY environment - variable is set. - - set-titles-string string - String used to set the window title if set-titles is on. - Character sequences are replaced as for the status-left - option. - - status [on | off] - Show or hide the status line. - - status-attr attributes - Set status line attributes. - - status-bg colour - Set status line background colour. - - status-fg colour - Set status line foreground colour. - - status-interval interval - Update the status bar every interval seconds. By - default, updates will occur every 15 seconds. A setting - of zero disables redrawing at interval. - - status-justify [left | centre | right] - Set the position of the window list component of the sta- - tus line: left, centre or right justified. - - status-keys [vi | emacs] - Use vi or emacs-style key bindings in the status line, - for example at the command prompt. Defaults to emacs. - - status-left string - Display string to the left of the status bar. string - will be passed through strftime(3) before being used. By - default, the session name is shown. string may contain - any of the following special character sequences: - - Character pair Replaced with - #(shell-command) First line of the command's - output - #[attributes] Colour or attribute change - #H Hostname of local host - #F Current window flag - #I Current window index - #P Current pane index - #S Session name - #T Current window title - #W Current window name - ## A literal '#' - - The #(shell-command) form executes 'shell-command' and - inserts the first line of its output. Note that shell - commands are only executed once at the interval specified - by the status-interval option: if the status line is - redrawn in the meantime, the previous result is used. - - #[attributes] allows a comma-separated list of attributes - to be specified, these may be 'fg=colour' to set the - foreground colour, 'bg=colour' to set the background - colour, the name of one of the attributes (listed under - the message-attr option) to turn an attribute on, or an - attribute prefixed with 'no' to turn one off, for example - nobright. Examples are: - - #(sysctl vm.loadavg) - #[fg=yellow,bold]#(apm -l)%%#[default] [#S] - - Where appropriate, special character sequences may be - prefixed with a number to specify the maximum length, for - example '#24T'. - - By default, UTF-8 in string is not interpreted, to enable - UTF-8, use the status-utf8 option. - - status-left-attr attributes - Set the attribute of the left part of the status line. - - status-left-fg colour - Set the foreground colour of the left part of the status - line. - - status-left-bg colour - Set the background colour of the left part of the status - line. - - status-left-length length - Set the maximum length of the left component of the sta- - tus bar. The default is 10. - - status-right string - Display string to the right of the status bar. By - default, the current window title in double quotes, the - date and the time are shown. As with status-left, string - will be passed to strftime(3), character pairs are - replaced, and UTF-8 is dependent on the status-utf8 - option. - - status-right-attr attributes - Set the attribute of the right part of the status line. - - status-right-fg colour - Set the foreground colour of the right part of the status - line. - - status-right-bg colour - Set the background colour of the right part of the status - line. - - status-right-length length - Set the maximum length of the right component of the sta- - tus bar. The default is 40. - - status-utf8 [on | off] - Instruct tmux to treat top-bit-set characters in the - status-left and status-right strings as UTF-8; notably, - this is important for wide characters. This option - defaults to off. - - terminal-overrides string - Contains a list of entries which override terminal - descriptions read using terminfo(5). string is a comma- - separated list of items each a colon-separated string - made up of a terminal type pattern (matched using - fnmatch(3)) and a set of name=value entries. - - For example, to set the 'clear' terminfo(5) entry to - '\e[H\e[2J' for all terminal types and the 'dch1' entry - to '\e[P' for the 'rxvt' terminal type, the option could - be set to the string: - - "*:clear=\e[H\e[2J,rxvt:dch1=\e[P" - - The terminal entry value is passed through strunvis(3) - before interpretation. The default value forcibly cor- - rects the 'colors' entry for terminals which support 88 - or 256 colours: - - "*88col*:colors=88,*256col*:colors=256" - - update-environment variables - Set a space-separated string containing a list of envi- - ronment variables to be copied into the session environ- - ment when a new session is created or an existing session - is attached. Any variables that do not exist in the - source environment are set to be removed from the session - environment (as if -r was given to the set-environment - command). The default is "DISPLAY WINDOWID SSH_ASKPASS - SSH_AUTH_SOCK SSH_AGENT_PID SSH_CONNECTION". - - visual-activity [on | off] - If on, display a status line message when activity occurs - in a window for which the monitor-activity window option - is enabled. - - visual-bell [on | off] - If this option is on, a message is shown on a bell - instead of it being passed through to the terminal (which - normally makes a sound). Also see the bell-action - option. - - visual-content [on | off] - Like visual-activity, display a message when content is - present in a window for which the monitor-content window - option is enabled. - - set-window-option [-agu] [-t target-window] option value - (alias: setw) - Set a window option. The -a, -g and -u flags work similarly to - the set-option command. - - Supported window options are: - - aggressive-resize [on | off] - Aggressively resize the chosen window. This means that - tmux will resize the window to the size of the smallest - session for which it is the current window, rather than - the smallest session to which it is attached. The window - may resize when the current window is changed on another - sessions; this option is good for full-screen programs - which support SIGWINCH and poor for interactive programs - such as shells. - - automatic-rename [on | off] - Control automatic window renaming. When this setting is - enabled, tmux will attempt - on supported platforms - to - rename the window to reflect the command currently run- - ning in it. This flag is automatically disabled for an - individual window when a name is specified at creation - with new-window or new-session, or later with - rename-window. It may be switched off globally with: - - set-window-option -g automatic-rename off - - clock-mode-colour colour - Set clock colour. - - clock-mode-style [12 | 24] - Set clock hour format. - - force-height height - force-width width - Prevent tmux from resizing a window to greater than width - or height. A value of zero restores the default unlim- - ited setting. - - main-pane-width width - main-pane-height height - Set the width or height of the main (left or top) pane in - the main-horizontal or main-vertical layouts. - - mode-attr attributes - Set window modes attributes. - - mode-bg colour - Set window modes background colour. - - mode-fg colour - Set window modes foreground colour. - - mode-keys [vi | emacs] - Use vi or emacs-style key bindings in copy and choice - modes. Key bindings default to emacs. - - mode-mouse [on | off] - Mouse state in modes. If on, tmux will respond to mouse - clicks by moving the cursor in copy mode or selecting an - option in choice mode. - - monitor-activity [on | off] - Monitor for activity in the window. Windows with activ- - ity are highlighted in the status line. - - monitor-content match-string - Monitor content in the window. When fnmatch(3) pattern - match-string appears in the window, it is highlighted in - the status line. - - remain-on-exit [on | off] - A window with this flag set is not destroyed when the - program running in it exits. The window may be reacti- - vated with the respawn-window command. - - synchronize-panes [on | off] - Duplicate input to any pane to all other panes in the - same window, except for panes that are not in output - mode. - - alternate-screen [on | off] - This option configures whether programs running inside - tmux may use the terminal alternate screen feature, which - allows the smcup and rmcup terminfo(5) capabilities to be - issued to preserve the existing window content on start - and restore it on exit. - - utf8 [on | off] - Instructs tmux to expect UTF-8 sequences to appear in - this window. - - window-status-attr attributes - Set status line attributes for a single window. - - window-status-bg colour - Set status line background colour for a single window. - - window-status-fg colour - Set status line foreground colour for a single window. - - window-status-format string - Set the format in which the window is displayed in the - status line window list. See the status-left option for - details of special character sequences available. The - default is '#I:#W#F'. - - window-status-current-attr attributes - Set status line attributes for the currently active win- - dow. - - window-status-current-bg colour - Set status line background colour for the currently - active window. - - window-status-current-fg colour - Set status line foreground colour for the currently - active window. - - window-status-current-format string - Like window-status-format, but is the format used when - the window is the current window. - - word-separators string - Sets the window's conception of what characters are con- - sidered word separators, for the purposes of the next and - previous word commands in copy mode. The default is - ' -_@'. - - xterm-keys [on | off] - If this option is set, tmux will generate xterm(1) -style - function key sequences; these have a number included to - indicate modifiers such as Shift, Alt or Ctrl. The - default is off. - - show-options [-gsw] [-t target-session | target-window] - (alias: show) - Show the window options with -w (equivalent to - show-window-options), the server options with -s, otherwise the - session options for target session. Global session or window - options are listed if -g is used. - - show-window-options [-g] [-t target-window] - (alias: showw) - List the window options for target-window, or the global window - options if -g is used. - -ENVIRONMENT - When the server is started, tmux copies the environment into the global - environment; in addition, each session has a session environment. When a - window is created, the session and global environments are merged with - the session environment overriding any variable present in both. This is - the initial environment passed to the new process. - - The update-environment session option may be used to update the session - environment from the client when a new session is created or an old reat- - tached. tmux also initialises the TMUX variable with some internal - information to allow commands to be executed from inside, and the TERM - variable with the correct terminal setting of 'screen'. - - Commands to alter and view the environment are: - - set-environment [-gru] [-t target-session] name [value] - (alias: setenv) - Set or unset an environment variable. If -g is used, the change - is made in the global environment; otherwise, it is applied to - the session environment for target-session. The -u flag unsets a - variable. -r indicates the variable is to be removed from the - environment before starting a new process. - - show-environment [-g] [-t target-session] - (alias: showenv) - Display the environment for target-session or the global environ- - ment with -g. Variables removed from the environment are pre- - fixed with '-'. - -STATUS LINE - tmux includes an optional status line which is displayed in the bottom - line of each terminal. By default, the status line is enabled (it may be - disabled with the status session option) and contains, from left-to- - right: the name of the current session in square brackets; the window - list; the current window title in double quotes; and the time and date. - - The status line is made of three parts: configurable left and right sec- - tions (which may contain dynamic content such as the time or output from - a shell command, see the status-left, status-left-length, status-right, - and status-right-length options below), and a central window list. By - default, the window list shows the index, name and (if any) flag of the - windows present in the current session in ascending numerical order. It - may be customised with the window-status-format and - window-status-current-format options. The flag is one of the following - symbols appended to the window name: - - Symbol Meaning - * Denotes the current window. - - Marks the last window (previously selected). - # Window is monitored and activity has been detected. - ! A bell has occurred in the window. - + Window is monitored for content and it has appeared. - - The # symbol relates to the monitor-activity and + to the monitor-content - window options. The window name is printed in inverted colours if an - alert (bell, activity or content) is present. - - The colour and attributes of the status line may be configured, the - entire status line using the status-attr, status-fg and status-bg session - options and individual windows using the window-status-attr, - window-status-fg and window-status-bg window options. - - The status line is automatically refreshed at interval if it has changed, - the interval may be controlled with the status-interval session option. - - Commands related to the status line are as follows: - - command-prompt [-p prompts] [-t target-client] [template] - Open the command prompt in a client. This may be used from - inside tmux to execute commands interactively. If template is - specified, it is used as the command. If -p is given, prompts is - a comma-separated list of prompts which are displayed in order; - otherwise a single prompt is displayed, constructed from template - if it is present, or ':' if not. Before the command is executed, - the first occurrence of the string '%%' and all occurrences of - '%1' are replaced by the response to the first prompt, the second - '%%' and all '%2' are replaced with the response to the second - prompt, and so on for further prompts. Up to nine prompt - responses may be replaced ('%1' to '%9'). - - confirm-before [-t target-client] command - (alias: confirm) - Ask for confirmation before executing command. This command - works only from inside tmux. - - display-message [-p] [-t target-client] [message] - (alias: display) - Display a message. If -p is given, the output is printed to std- - out, otherwise it is displayed in the target-client status line. - The format of message is as for status-left, with the exception - that #() are not handled. - - select-prompt [-t target-client] - Open a prompt inside target-client allowing a window index to be - entered interactively. - -BUFFERS - tmux maintains a stack of paste buffers for each session. Up to the - value of the buffer-limit option are kept; when a new buffer is added, - the buffer at the bottom of the stack is removed. Buffers may be added - using copy-mode or the set-buffer command, and pasted into a window using - the paste-buffer command. - - A configurable history buffer is also maintained for each window. By - default, up to 2000 lines are kept; this can be altered with the - history-limit option (see the set-option command above). - - The buffer commands are as follows: - - clear-history [-t target-pane] - (alias: clearhist) - Remove and free the history for the specified pane. - - copy-buffer [-a src-index] [-b dst-index] [-s src-session] [-t - dst-session] - (alias: copyb) - Copy a session paste buffer to another session. If no sessions - are specified, the current one is used instead. - - delete-buffer [-b buffer-index] [-t target-session] - (alias: deleteb) - Delete the buffer at buffer-index, or the top buffer if not spec- - ified. - - list-buffers [-t target-session] - (alias: lsb) - List the buffers in the given session. - - load-buffer [-b buffer-index] [-t target-session] path - (alias: loadb) - Load the contents of the specified paste buffer from path. - - paste-buffer [-dr] [-b buffer-index] [-t target-window] - (alias: pasteb) - Insert the contents of a paste buffer into the current window. - With -d, also delete the paste buffer from the stack. When out- - put, any linefeed (LF) characters in the paste buffer are - replaced with carriage returns (CR). This translation may be - disabled with the -r flag. - - save-buffer [-a] [-b buffer-index] [-t target-session] path - (alias: saveb) - Save the contents of the specified paste buffer to path. The -a - option appends to rather than overwriting the file. - - set-buffer [-b buffer-index] [-t target-session] data - (alias: setb) - Set the contents of the specified buffer to data. - - show-buffer [-b buffer-index] [-t target-session] - (alias: showb) - Display the contents of the specified buffer. - -MISCELLANEOUS - Miscellaneous commands are as follows: - - clock-mode [-t target-pane] - Display a large clock. - - if-shell shell-command command - (alias: if) - Execute command if shell-command returns success. - - lock-server - (alias: lock) - Lock each client individually by running the command specified by - the lock-command option. - - run-shell shell-command - (alias: run) - Execute shell-command in the background without creating a win- - dow. After it finishes, any output to stdout is displayed in - output mode. If the command doesn't return success, the exit - status is also displayed. - - server-info - (alias: info) - Show server information and terminal details. - -FILES - ~/.tmux.conf Default tmux configuration file. - /etc/tmux.conf System-wide configuration file. - -EXAMPLES - To create a new tmux session running vi(1): - - $ tmux new-session vi - - Most commands have a shorter form, known as an alias. For new-session, - this is new: - - $ tmux new vi - - Alternatively, the shortest unambiguous form of a command is accepted. - If there are several options, they are listed: - - $ tmux n - ambiguous command: n, could be: new-session, new-window, next-window - - Within an active session, a new window may be created by typing 'C-b c' - (Ctrl followed by the 'b' key followed by the 'c' key). - - Windows may be navigated with: 'C-b 0' (to select window 0), 'C-b 1' (to - select window 1), and so on; 'C-b n' to select the next window; and 'C-b - p' to select the previous window. - - A session may be detached using 'C-b d' (or by an external event such as - ssh(1) disconnection) and reattached with: - - $ tmux attach-session - - Typing 'C-b ?' lists the current key bindings in the current window; up - and down may be used to navigate the list or 'q' to exit from it. - - Commands to be run when the tmux server is started may be placed in the - ~/.tmux.conf configuration file. Common examples include: - - Changing the default prefix key: - - set-option -g prefix C-a - unbind-key C-b - bind-key C-a send-prefix - - Turning the status line off, or changing its colour: - - set-option -g status off - set-option -g status-bg blue - - Setting other options, such as the default command, or locking after 30 - minutes of inactivity: - - set-option -g default-command "exec /bin/ksh" - set-option -g lock-after-time 1800 - - Creating new key bindings: - - bind-key b set-option status - bind-key / command-prompt "split-window 'exec man %%'" - bind-key S command-prompt "new-window -n %1 'ssh %1'" - -SEE ALSO - pty(4) - -AUTHORS - Nicholas Marriott - -BSD October 19, 2013 BSD diff --git a/manual/1.3 b/manual/1.3 deleted file mode 100644 index 7397d6b1918..00000000000 --- a/manual/1.3 +++ /dev/null @@ -1,1585 +0,0 @@ -TMUX(1) BSD General Commands Manual TMUX(1) - -NAME - tmux -- terminal multiplexer - -SYNOPSIS - tmux [-28lquv] [-c shell-command] [-f file] [-L socket-name] - [-S socket-path] [command [flags]] - -DESCRIPTION - tmux is a terminal multiplexer: it enables a number of terminals to be - created, accessed, and controlled from a single screen. tmux may be - detached from a screen and continue running in the background, then later - reattached. - - When tmux is started it creates a new session with a single window and - displays it on screen. A status line at the bottom of the screen shows - information on the current session and is used to enter interactive com- - mands. - - A session is a single collection of pseudo terminals under the management - of tmux. Each session has one or more windows linked to it. A window - occupies the entire screen and may be split into rectangular panes, each - of which is a separate pseudo terminal (the pty(4) manual page documents - the technical details of pseudo terminals). Any number of tmux instances - may connect to the same session, and any number of windows may be present - in the same session. Once all sessions are killed, tmux exits. - - Each session is persistent and will survive accidental disconnection - (such as ssh(1) connection timeout) or intentional detaching (with the - 'C-b d' key strokes). tmux may be reattached using: - - $ tmux attach - - In tmux, a session is displayed on screen by a client and all sessions - are managed by a single server. The server and each client are separate - processes which communicate through a socket in /tmp. - - The options are as follows: - - -2 Force tmux to assume the terminal supports 256 colours. - - -8 Like -2, but indicates that the terminal supports 88 - colours. - - -c shell-command - Execute shell-command using the default shell. If neces- - sary, the tmux server will be started to retrieve the - default-shell option. This option is for compatibility - with sh(1) when tmux is used as a login shell. - - -f file Specify an alternative configuration file. By default, - tmux loads the system configuration file from - /etc/tmux.conf, if present, then looks for a user configu- - ration file at ~/.tmux.conf. The configuration file is a - set of tmux commands which are executed in sequence when - the server is first started. - - If a command in the configuration file fails, tmux will - report an error and exit without executing further com- - mands. - - -L socket-name - tmux stores the server socket in a directory under /tmp; - the default socket is named default. This option allows a - different socket name to be specified, allowing several - independent tmux servers to be run. Unlike -S a full path - is not necessary: the sockets are all created in the same - directory. - - If the socket is accidentally removed, the SIGUSR1 signal - may be sent to the tmux server process to recreate it. - - -l Behave as a login shell. This flag currently has no effect - and is for compatibility with other shells when using tmux - as a login shell. - - -q Set the quiet server option to prevent the server sending - various informational messages. - - -S socket-path - Specify a full alternative path to the server socket. If - -S is specified, the default socket directory is not used - and any -L flag is ignored. - - -u tmux attempts to guess if the terminal is likely to support - UTF-8 by checking the first of the LC_ALL, LC_CTYPE and - LANG environment variables to be set for the string - "UTF-8". This is not always correct: the -u flag explic- - itly informs tmux that UTF-8 is supported. - - If the server is started from a client passed -u or where - UTF-8 is detected, the utf8 and status-utf8 options are - enabled in the global window and session options respec- - tively. - - -v Request verbose logging. This option may be specified mul- - tiple times for increasing verbosity. Log messages will be - saved into tmux-client-PID.log and tmux-server-PID.log - files in the current directory, where PID is the PID of the - server or client process. - - command [flags] - This specifies one of a set of commands used to control - tmux, as described in the following sections. If no com- - mands are specified, the new-session command is assumed. - -KEY BINDINGS - tmux may be controlled from an attached client by using a key combination - of a prefix key, 'C-b' (Ctrl-b) by default, followed by a command key. - - The default command key bindings are: - - C-b Send the prefix key (C-b) through to the application. - C-o Rotate the panes in the current window forwards. - C-z Suspend the tmux client. - ! Break the current pane out of the window. - " Split the current pane into two, top and bottom. - # List all paste buffers. - % Split the current pane into two, left and right. - & Kill the current window. - ' Prompt for a window index to select. - , Rename the current window. - - Delete the most recently copied buffer of text. - . Prompt for an index to move the current window. - 0 to 9 Select windows 0 to 9. - : Enter the tmux command prompt. - = Choose which buffer to paste interactively from a list. - ? List all key bindings. - D Choose a client to detach. - [ Enter copy mode to copy text or view the history. - ] Paste the most recently copied buffer of text. - c Create a new window. - d Detach the current client. - f Prompt to search for text in open windows. - i Display some information about the current window. - l Move to the previously selected window. - n Change to the next window. - o Select the next pane in the current window. - p Change to the previous window. - q Briefly display pane indexes. - r Force redraw of the attached client. - s Select a new session for the attached client interac- - tively. - t Show the time. - w Choose the current window interactively. - x Kill the current pane. - { Swap the current pane with the previous pane. - } Swap the current pane with the next pane. - ~ Show previous messages from tmux, if any. - Page Up Enter copy mode and scroll one page up. - Up, Down - Left, Right - Change to the pane above, below, to the left, or to the - right of the current pane. - M-1 to M-5 Arrange panes in one of the five preset layouts: even- - horizontal, even-vertical, main-horizontal, main-verti- - cal, or tiled. - M-n Move to the next window with a bell or activity marker. - M-o Rotate the panes in the current window backwards. - M-p Move to the previous window with a bell or activity - marker. - C-Up, C-Down - C-Left, C-Right - Resize the current pane in steps of one cell. - M-Up, M-Down - M-Left, M-Right - Resize the current pane in steps of five cells. - - Key bindings may be changed with the bind-key and unbind-key commands. - -COMMANDS - This section contains a list of the commands supported by tmux. Most - commands accept the optional -t argument with one of target-client, - target-session target-window, or target-pane. These specify the client, - session, window or pane which a command should affect. target-client is - the name of the pty(4) file to which the client is connected, for example - either of /dev/ttyp1 or ttyp1 for the client attached to /dev/ttyp1. If - no client is specified, the current client is chosen, if possible, or an - error is reported. Clients may be listed with the list-clients command. - - target-session is either the name of a session (as listed by the - list-sessions command) or the name of a client with the same syntax as - target-client, in which case the session attached to the client is used. - When looking for the session name, tmux initially searches for an exact - match; if none is found, the session names are checked for any for which - target-session is a prefix or for which it matches as an fnmatch(3) pat- - tern. If a single match is found, it is used as the target session; mul- - tiple matches produce an error. If a session is omitted, the current - session is used if available; if no current session is available, the - most recently used is chosen. - - target-window specifies a window in the form session:window. session - follows the same rules as for target-session, and window is looked for in - order: as a window index, for example mysession:1; as an exact window - name, such as mysession:mywindow; then as an fnmatch(3) pattern or the - start of a window name, such as mysession:mywin* or mysession:mywin. An - empty window name specifies the next unused index if appropriate (for - example the new-window and link-window commands) otherwise the current - window in session is chosen. The special character '!' uses the last - (previously current) window, or '+' and '-' are the next window or the - previous window by number. When the argument does not contain a colon, - tmux first attempts to parse it as window; if that fails, an attempt is - made to match a session. - - target-pane takes a similar form to target-window but with the optional - addition of a period followed by a pane index, for example: myses- - sion:mywindow.1. If the pane index is omitted, the currently active pane - in the specified window is used. If neither a colon nor period appears, - tmux first attempts to use the argument as a pane index; if that fails, - it is looked up as for target-window. A '+' or '-' indicate the next or - previous pane index, respectively. One of the strings top, bottom, left, - right, top-left, top-right, bottom-left or bottom-right may be used - instead of a pane index. - - The special characters '+' and '-' may be followed by an offset, for - example: - - select-window -t:+2 - - When dealing with a session that doesn't contain sequential window - indexes, they will be correctly skipped. - - shell-command arguments are sh(1) commands. These must be passed as a - single item, which typically means quoting them, for example: - - new-window 'vi /etc/passwd' - - command [arguments] refers to a tmux command, passed with the command and - arguments separately, for example: - - bind-key F1 set-window-option force-width 81 - - Or if using sh(1): - - $ tmux bind-key F1 set-window-option force-width 81 - - Multiple commands may be specified together as part of a command - sequence. Each command should be separated by spaces and a semicolon; - commands are executed sequentially from left to right. A literal semi- - colon may be included by escaping it with a backslash (for example, when - specifying a command sequence to bind-key). - - Example tmux commands include: - - refresh-client -t/dev/ttyp2 - - rename-session -tfirst newname - - set-window-option -t:0 monitor-activity on - - new-window ; split-window -d - - Or from sh(1): - - $ tmux kill-window -t :1 - - $ tmux new-window \; split-window -d - - $ tmux new-session -d 'vi /etc/passwd' \; split-window -d \; attach - -CLIENTS AND SESSIONS - The tmux server manages clients, sessions, windows and panes. Clients - are attached to sessions to interact with them, either when they are cre- - ated with the new-session command, or later with the attach-session com- - mand. Each session has one of more windows linked into it. Windows may - be linked to multiple sessions and are made up of one or more panes, each - of which contains a pseudo terminal. Commands for creating, linking and - otherwise manipulating windows are covered in the WINDOWS AND PANES sec- - tion. - - The following commands are available to manage clients and sessions: - - attach-session [-dr] [-t target-session] - (alias: attach) - If run from outside tmux, create a new client in the current ter- - minal and attach it to target-session. If used from inside, - switch the current client. If -d is specified, any other clients - attached to the session are detached. -r signifies the client is - read-only (only keys bound to the detach-client command have any - effect) - - If no server is started, attach-session will attempt to start it; - this will fail unless sessions are created in the configuration - file. - - detach-client [-t target-client] - (alias: detach) - Detach the current client if bound to a key, or the specified - client with -t. - - has-session [-t target-session] - (alias: has) - Report an error and exit with 1 if the specified session does not - exist. If it does exist, exit with 0. - - kill-server - Kill the tmux server and clients and destroy all sessions. - - kill-session [-t target-session] - Destroy the given session, closing any windows linked to it and - no other sessions, and detaching all clients attached to it. - - list-clients - (alias: lsc) - List all clients attached to the server. - - list-commands - (alias: lscm) - List the syntax of all commands supported by tmux. - - list-sessions - (alias: ls) - List all sessions managed by the server. - - lock-client [-t target-client] - (alias: lockc) - Lock target-client, see the lock-server command. - - lock-session [-t target-session] - (alias: locks) - Lock all clients attached to target-session. - - new-session [-d] [-n window-name] [-s session-name] [-t target-session] - [shell-command] - (alias: new) - Create a new session with name session-name. - - The new session is attached to the current terminal unless -d is - given. window-name and shell-command are the name of and shell - command to execute in the initial window. - - If run from a terminal, any termios(4) special characters are - saved and used for new windows in the new session. - - If -t is given, the new session is grouped with target-session. - This means they share the same set of windows - all windows from - target-session are linked to the new session and any subsequent - new windows or windows being closed are applied to both sessions. - The current and previous window and any session options remain - independent and either session may be killed without affecting - the other. Giving -n or shell-command are invalid if -t is used. - - refresh-client [-t target-client] - (alias: refresh) - Refresh the current client if bound to a key, or a single client - if one is given with -t. - - rename-session [-t target-session] new-name - (alias: rename) - Rename the session to new-name. - - show-messages [-t target-client] - (alias: showmsgs) - Any messages displayed on the status line are saved in a per- - client message log, up to a maximum of the limit set by the - message-limit session option for the session attached to that - client. This command displays the log for target-client. - - source-file path - (alias: source) - Execute commands from path. - - start-server - (alias: start) - Start the tmux server, if not already running, without creating - any sessions. - - suspend-client [-c target-client] - (alias: suspendc) - Suspend a client by sending SIGTSTP (tty stop). - - switch-client [-c target-client] [-t target-session] - (alias: switchc) - Switch the current session for client target-client to - target-session. - -WINDOWS AND PANES - A tmux window may be in one of several modes. The default permits direct - access to the terminal attached to the window. The other is copy mode, - which permits a section of a window or its history to be copied to a - paste buffer for later insertion into another window. This mode is - entered with the copy-mode command, bound to '[' by default. It is also - entered when a command that produces output, such as list-keys, is exe- - cuted from a key binding. - - The keys available depend on whether emacs or vi mode is selected (see - the mode-keys option). The following keys are supported as appropriate - for the mode: - - Function vi emacs - Back to indentation ^ M-m - Bottom of history G M-< - Clear selection Escape C-g - Copy selection Enter M-w - Cursor down j Down - Cursor left h Left - Cursor right l Right - Cursor to bottom line L - Cursor to middle line M M-r - Cursor to top line H M-R - Cursor up k Up - Delete entire line d C-u - Delete to end of line D C-k - End of line $ C-e - Go to line : g - Half page down C-d M-Down - Half page up C-u M-Up - Jump forward f f - Jump backward F F - Jump again ; ; - Jump again in reverse , , - Next page C-f Page down - Next space W - Next space, end of word E - Next word w - Next word end e M-f - Paste buffer p C-y - Previous page C-b Page up - Previous word b M-b - Previous space B - Quit mode q Escape - Rectangle toggle v R - Scroll down C-Down or C-e C-Down - Scroll up C-Up or C-y C-Up - Search again n n - Search again in reverse N N - Search backward ? C-r - Search forward / C-s - Start of line 0 C-a - Start selection Space C-Space - Top of history g M-> - Transpose chars C-t - - The next and previous word keys use space and the '-', '_' and '@' char- - acters as word delimiters by default, but this can be adjusted by setting - the word-separators window option. Next word moves to the start of the - next word, next word end to the end of the next word and previous word to - the start of the previous word. The three next and previous space keys - work similarly but use a space alone as the word separator. - - The jump commands enable quick movement within a line. For instance, - typing 'f' followed by '/' will move the cursor to the next '/' character - on the current line. A ';' will then jump to the next occurrence. - - Commands in copy mode may be prefaced by an optional repeat count. With - vi key bindings, a prefix is entered using the number keys; with emacs, - the Alt (meta) key and a number begins prefix entry. For example, to - move the cursor forward by ten words, use 'M-1 0 M-f' in emacs mode, and - '10w' in vi. - - Mode key bindings are defined in a set of named tables: vi-edit and - emacs-edit for keys used when line editing at the command prompt; - vi-choice and emacs-choice for keys used when choosing from lists (such - as produced by the choose-window command); and vi-copy and emacs-copy - used in copy mode. The tables may be viewed with the list-keys command - and keys modified or removed with bind-key and unbind-key. - - The paste buffer key pastes the first line from the top paste buffer on - the stack. - - The synopsis for the copy-mode command is: - - copy-mode [-u] [-t target-pane] - Enter copy mode. The -u option scrolls one page up. - - Each window displayed by tmux may be split into one or more panes; each - pane takes up a certain area of the display and is a separate terminal. - A window may be split into panes using the split-window command. Windows - may be split horizontally (with the -h flag) or vertically. Panes may be - resized with the resize-pane command (bound to 'C-up', 'C-down' 'C-left' - and 'C-right' by default), the current pane may be changed with the - select-pane command and the rotate-window and swap-pane commands may be - used to swap panes without changing their position. Panes are numbered - beginning from zero in the order they are created. - - A number of preset layouts are available. These may be selected with the - select-layout command or cycled with next-layout (bound to 'Space' by - default); once a layout is chosen, panes within it may be moved and - resized as normal. - - The following layouts are supported: - - even-horizontal - Panes are spread out evenly from left to right across the window. - - even-vertical - Panes are spread evenly from top to bottom. - - main-horizontal - A large (main) pane is shown at the top of the window and the - remaining panes are spread from left to right in the leftover - space at the bottom. Use the main-pane-height window option to - specify the height of the top pane. - - main-vertical - Similar to main-horizontal but the large pane is placed on the - left and the others spread from top to bottom along the right. - See the main-pane-width window option. - - tiled Panes are spread out as evenly as possible over the window in - both rows and columns. - - In addition, select-layout may be used to apply a previously used layout - - the list-windows command displays the layout of each window in a form - suitable for use with select-layout. For example: - - $ tmux list-windows - 0: ksh [159x48] - layout: bb62,159x48,0,0{79x48,0,0,79x48,80,0} - $ tmux select-layout bb62,159x48,0,0{79x48,0,0,79x48,80,0} - tmux automatically adjusts the size of the layout for the current window - size. Note that a layout cannot be applied to a window with more panes - than that from which the layout was originally defined. - - Commands related to windows and panes are as follows: - - break-pane [-d] [-t target-pane] - (alias: breakp) - Break target-pane off from its containing window to make it the - only pane in a new window. If -d is given, the new window does - not become the current window. - - capture-pane [-b buffer-index] [-t target-pane] - (alias: capturep) - Capture the contents of a pane to the specified buffer, or a new - buffer if none is specified. - - choose-client [-t target-window] [template] - Put a window into client choice mode, allowing a client to be - selected interactively from a list. After a client is chosen, - '%%' is replaced by the client pty(4) path in template and the - result executed as a command. If template is not given, "detach- - client -t '%%'" is used. This command works only from inside - tmux. - - choose-session [-t target-window] [template] - Put a window into session choice mode, where a session may be - selected interactively from a list. When one is chosen, '%%' is - replaced by the session name in template and the result executed - as a command. If template is not given, "switch-client -t '%%'" - is used. This command works only from inside tmux. - - choose-window [-t target-window] [template] - Put a window into window choice mode, where a window may be cho- - sen interactively from a list. After a window is selected, '%%' - is replaced by the session name and window index in template and - the result executed as a command. If template is not given, - "select-window -t '%%'" is used. This command works only from - inside tmux. - - display-panes [-t target-client] - (alias: displayp) - Display a visible indicator of each pane shown by target-client. - See the display-panes-time, display-panes-colour, and - display-panes-active-colour session options. While the indicator - is on screen, a pane may be selected with the '0' to '9' keys. - - find-window [-t target-window] match-string - (alias: findw) - Search for the fnmatch(3) pattern match-string in window names, - titles, and visible content (but not history). If only one win- - dow is matched, it'll be automatically selected, otherwise a - choice list is shown. This command only works from inside tmux. - - join-pane [-dhv] [-l size | -p percentage] [-s src-pane] [-t dst-pane] - (alias: joinp) - Like split-window, but instead of splitting dst-pane and creating - a new pane, split it and move src-pane into the space. This can - be used to reverse break-pane. - - kill-pane [-a] [-t target-pane] - (alias: killp) - Destroy the given pane. If no panes remain in the containing - window, it is also destroyed. The -a option kills all but the - pane given with -t. - - kill-window [-t target-window] - (alias: killw) - Kill the current window or the window at target-window, removing - it from any sessions to which it is linked. - - last-window [-t target-session] - (alias: last) - Select the last (previously selected) window. If no - target-session is specified, select the last window of the cur- - rent session. - - link-window [-dk] [-s src-window] [-t dst-window] - (alias: linkw) - Link the window at src-window to the specified dst-window. If - dst-window is specified and no such window exists, the src-window - is linked there. If -k is given and dst-window exists, it is - killed, otherwise an error is generated. If -d is given, the - newly linked window is not selected. - - list-panes [-t target-window] - (alias: lsp) - List the panes in the current window or in target-window. - - list-windows [-t target-session] - (alias: lsw) - List windows in the current session or in target-session. - - move-window [-dk] [-s src-window] [-t dst-window] - (alias: movew) - This is similar to link-window, except the window at src-window - is moved to dst-window. - - new-window [-adk] [-n window-name] [-t target-window] [shell-command] - (alias: neww) - Create a new window. With -a, the new window is inserted at the - next index up from the specified target-window, moving windows up - if necessary, otherwise target-window is the new window location. - - If -d is given, the session does not make the new window the cur- - rent window. target-window represents the window to be created; - if the target already exists an error is shown, unless the -k - flag is used, in which case it is destroyed. shell-command is - the command to execute. If shell-command is not specified, the - value of the default-command option is used. - - When the shell command completes, the window closes. See the - remain-on-exit option to change this behaviour. - - The TERM environment variable must be set to ``screen'' for all - programs running inside tmux. New windows will automatically - have ``TERM=screen'' added to their environment, but care must be - taken not to reset this in shell start-up files. - - next-layout [-t target-window] - (alias: nextl) - Move a window to the next layout and rearrange the panes to fit. - - next-window [-a] [-t target-session] - (alias: next) - Move to the next window in the session. If -a is used, move to - the next window with a bell, activity or content alert. - - pipe-pane [-o] [-t target-pane] [shell-command] - (alias: pipep) - Pipe any output sent by the program in target-pane to a shell - command. A pane may only be piped to one command at a time, any - existing pipe is closed before shell-command is executed. The - shell-command string may contain the special character sequences - supported by the status-left command. If no shell-command is - given, the current pipe (if any) is closed. - - The -o option only opens a new pipe if no previous pipe exists, - allowing a pipe to be toggled with a single key, for example: - - bind-key C-p pipe-pane -o 'cat >>~/output.#I-#P' - - previous-layout [-t target-window] - (alias: prevl) - Move to the previous layout in the session. - - previous-window [-a] [-t target-session] - (alias: prev) - Move to the previous window in the session. With -a, move to the - previous window with a bell, activity or content alert. - - rename-window [-t target-window] new-name - (alias: renamew) - Rename the current window, or the window at target-window if - specified, to new-name. - - resize-pane [-DLRU] [-t target-pane] [adjustment] - (alias: resizep) - Resize a pane, upward with -U (the default), downward with -D, to - the left with -L and to the right with -R. The adjustment is - given in lines or cells (the default is 1). - - respawn-window [-k] [-t target-window] [shell-command] - (alias: respawnw) - Reactivate a window in which the command has exited (see the - remain-on-exit window option). If shell-command is not given, - the command used when the window was created is executed. The - window must be already inactive, unless -k is given, in which - case any existing command is killed. - - rotate-window [-DU] [-t target-window] - (alias: rotatew) - Rotate the positions of the panes within a window, either upward - (numerically lower) with -U or downward (numerically higher). - - select-layout [-t target-window] [layout-name] - (alias: selectl) - Choose a specific layout for a window. If layout-name is not - given, the last preset layout used (if any) is reapplied. - - select-pane [-DLRU] [-t target-pane] - (alias: selectp) - Make pane target-pane the active pane in window target-window. - If one of -D, -L, -R, or -U is used, respectively the pane below, - to the left, to the right, or above the target pane is used. - - select-window [-t target-window] - (alias: selectw) - Select the window at target-window. - - split-window [-dhv] [-l size | -p percentage] [-t target-pane] - [shell-command] - (alias: splitw) - Create a new pane by splitting target-pane: -h does a horizontal - split and -v a vertical split; if neither is specified, -v is - assumed. The -l and -p options specify the size of the new pane - in lines (for vertical split) or in cells (for horizontal split), - or as a percentage, respectively. All other options have the - same meaning as for the new-window command. - - swap-pane [-dDU] [-s src-pane] [-t dst-pane] - (alias: swapp) - Swap two panes. If -U is used and no source pane is specified - with -s, dst-pane is swapped with the previous pane (before it - numerically); -D swaps with the next pane (after it numerically). - -d instructs tmux not to change the active pane. - - swap-window [-d] [-s src-window] [-t dst-window] - (alias: swapw) - This is similar to link-window, except the source and destination - windows are swapped. It is an error if no window exists at - src-window. - - unlink-window [-k] [-t target-window] - (alias: unlinkw) - Unlink target-window. Unless -k is given, a window may be - unlinked only if it is linked to multiple sessions - windows may - not be linked to no sessions; if -k is specified and the window - is linked to only one session, it is unlinked and destroyed. - -KEY BINDINGS - tmux allows a command to be bound to most keys, with or without a prefix - key. When specifying keys, most represent themselves (for example 'A' to - 'Z'). Ctrl keys may be prefixed with 'C-' or '^', and Alt (meta) with - 'M-'. In addition, the following special key names are accepted: Up, - Down, Left, Right, BSpace, BTab, DC (Delete), End, Enter, Escape, F1 to - F20, Home, IC (Insert), NPage (Page Up), PPage (Page Down), Space, and - Tab. Note that to bind the '"' or ''' keys, quotation marks are neces- - sary, for example: - - bind-key '"' split-window - bind-key "'" new-window - - Commands related to key bindings are as follows: - - bind-key [-cnr] [-t key-table] key command [arguments] - (alias: bind) - Bind key key to command. By default (without -t) the primary key - bindings are modified (those normally activated with the prefix - key); in this case, if -n is specified, it is not necessary to - use the prefix key, command is bound to key alone. The -r flag - indicates this key may repeat, see the repeat-time option. - - If -t is present, key is bound in key-table: the binding for com- - mand mode with -c or for normal mode without. To view the - default bindings and possible commands, see the list-keys com- - mand. - - list-keys [-t key-table] - (alias: lsk) - List all key bindings. Without -t the primary key bindings - - those executed when preceded by the prefix key - are printed. - Keys bound without the prefix key (see bind-key -n) are marked - with '(no prefix)'. - - With -t, the key bindings in key-table are listed; this may be - one of: vi-edit, emacs-edit, vi-choice, emacs-choice, vi-copy or - emacs-copy. - - send-keys [-t target-pane] key ... - (alias: send) - Send a key or keys to a window. Each argument key is the name of - the key (such as 'C-a' or 'npage' ) to send; if the string is not - recognised as a key, it is sent as a series of characters. All - arguments are sent sequentially from first to last. - - send-prefix [-t target-pane] - Send the prefix key to a window as if it was pressed. If multi- - ple prefix keys are configured, only the first is sent. - - unbind-key [-cn] [-t key-table] key - (alias: unbind) - Unbind the command bound to key. Without -t the primary key - bindings are modified; in this case, if -n is specified, the com- - mand bound to key without a prefix (if any) is removed. - - If -t is present, key in key-table is unbound: the binding for - command mode with -c or for normal mode without. - -OPTIONS - The appearance and behaviour of tmux may be modified by changing the - value of various options. There are three types of option: server - options, session options and window options. - - The tmux server has a set of global options which do not apply to any - particular window or session. These are altered with the set-option -s - command, or displayed with the show-options -s command. - - In addition, each individual session may have a set of session options, - and there is a separate set of global session options. Sessions which do - not have a particular option configured inherit the value from the global - session options. Session options are set or unset with the set-option - command and may be listed with the show-options command. The available - server and session options are listed under the set-option command. - - Similarly, a set of window options is attached to each window, and there - is a set of global window options from which any unset options are inher- - ited. Window options are altered with the set-window-option command and - can be listed with the show-window-options command. All window options - are documented with the set-window-option command. - - Commands which set options are as follows: - - set-option [-agsuw] [-t target-session | target-window] option value - (alias: set) - Set a window option with -w (equivalent to the set-window-option - command), a server option with -s, otherwise a session option. - - If -g is specified, the global session or window option is set. - With -a, and if the option expects a string, value is appended to - the existing setting. The -u flag unsets an option, so a session - inherits the option from the global options. It is not possible - to unset a global option. - - Available window options are listed under set-window-option. - - Available server options are: - - detach-on-destroy - If on (the default), the client is detached when the ses- - sion it is attached to is destroyed. If off, the client - is switched to the most recently active of the remaining - sessions. - - escape-time - Set the time in milliseconds for which tmux waits after - an escape is input to determine if it is part of a func- - tion or meta key sequences. The default is 500 millisec- - onds. - - quiet Enable or disable the display of various informational - messages (see also the -q command line flag). - - Available session options are: - - base-index index - Set the base index from which an unused index should be - searched when a new window is created. The default is - zero. - - bell-action [any | none | current] - Set action on window bell. any means a bell in any win- - dow linked to a session causes a bell in the current win- - dow of that session, none means all bells are ignored and - current means only bell in windows other than the current - window are ignored. - - buffer-limit number - Set the number of buffers kept for each session; as new - buffers are added to the top of the stack, old ones are - removed from the bottom if necessary to maintain this - maximum length. - - default-command shell-command - Set the command used for new windows (if not specified - when the window is created) to shell-command, which may - be any sh(1) command. The default is an empty string, - which instructs tmux to create a login shell using the - value of the default-shell option. - - default-shell path - Specify the default shell. This is used as the login - shell for new windows when the default-command option is - set to empty, and must be the full path of the exe- - cutable. When started tmux tries to set a default value - from the first suitable of the SHELL environment vari- - able, the shell returned by getpwuid(3), or /bin/sh. - This option should be configured when tmux is used as a - login shell. - - default-path path - Set the default working directory for processes created - from keys, or interactively from the prompt. The default - is empty, which means to use the working directory of the - shell from which the server was started if it is avail- - able or the user's home if not. - - default-terminal terminal - Set the default terminal for new windows created in this - session - the default value of the TERM environment vari- - able. For tmux to work correctly, this must be set to - 'screen' or a derivative of it. - - display-panes-active-colour colour - Set the colour used by the display-panes command to show - the indicator for the active pane. - - display-panes-colour colour - Set the colour used by the display-panes command to show - the indicators for inactive panes. - - display-panes-time time - Set the time in milliseconds for which the indicators - shown by the display-panes command appear. - - display-time time - Set the amount of time for which status line messages and - other on-screen indicators are displayed. time is in - milliseconds. - - history-limit lines - Set the maximum number of lines held in window history. - This setting applies only to new windows - existing win- - dow histories are not resized and retain the limit at the - point they were created. - - lock-after-time number - Lock the session (like the lock-session command) after - number seconds of inactivity, or the entire server (all - sessions) if the lock-server option is set. The default - is not to lock (set to 0). - - lock-command shell-command - Command to run when locking each client. The default is - to run lock(1) with -np. - - lock-server [on | off] - If this option is on (the default), instead of each ses- - sion locking individually as each has been idle for - lock-after-time, the entire server will lock after all - sessions would have locked. This has no effect as a ses- - sion option; it must be set as a global option. - - message-attr attributes - Set status line message attributes, where attributes is - either none or a comma-delimited list of one or more of: - bright (or bold), dim, underscore, blink, reverse, - hidden, or italics. - - message-bg colour - Set status line message background colour, where colour - is one of: black, red, green, yellow, blue, magenta, - cyan, white, colour0 to colour255 from the 256-colour - palette, or default. - - message-fg colour - Set status line message foreground colour. - - message-limit number - Set the number of error or information messages to save - in the message log for each client. The default is 20. - - mouse-select-pane [on | off] - If on, tmux captures the mouse and when a window is split - into multiple panes the mouse may be used to select the - current pane. The mouse click is also passed through to - the application as normal. - - pane-border-fg colour - - pane-border-bg colour - Set the pane border colour for panes aside from the - active pane. - - pane-active-border-fg colour - - pane-active-border-bg colour - Set the pane border colour for the currently active pane. - - prefix keys - Set the keys accepted as a prefix key. keys is a comma- - separated list of key names, each of which individually - behave as the prefix key. - - repeat-time time - Allow multiple commands to be entered without pressing - the prefix-key again in the specified time milliseconds - (the default is 500). Whether a key repeats may be set - when it is bound using the -r flag to bind-key. Repeat - is enabled for the default keys bound to the resize-pane - command. - - set-remain-on-exit [on | off] - Set the remain-on-exit window option for any windows - first created in this session. When this option is true, - windows in which the running program has exited do not - close, instead remaining open but inactivate. Use the - respawn-window command to reactivate such a window, or - the kill-window command to destroy it. - - set-titles [on | off] - Attempt to set the window title using the \e]2;...\007 - xterm code if the terminal appears to be an xterm. This - option is off by default. Note that elinks will only - attempt to set the window title if the STY environment - variable is set. - - set-titles-string string - String used to set the window title if set-titles is on. - Character sequences are replaced as for the status-left - option. - - status [on | off] - Show or hide the status line. - - status-attr attributes - Set status line attributes. - - status-bg colour - Set status line background colour. - - status-fg colour - Set status line foreground colour. - - status-interval interval - Update the status bar every interval seconds. By - default, updates will occur every 15 seconds. A setting - of zero disables redrawing at interval. - - status-justify [left | centre | right] - Set the position of the window list component of the sta- - tus line: left, centre or right justified. - - status-keys [vi | emacs] - Use vi or emacs-style key bindings in the status line, - for example at the command prompt. Defaults to emacs. - - status-left string - Display string to the left of the status bar. string - will be passed through strftime(3) before being used. By - default, the session name is shown. string may contain - any of the following special character sequences: - - Character pair Replaced with - #(shell-command) First line of the command's - output - #[attributes] Colour or attribute change - #H Hostname of local host - #F Current window flag - #I Current window index - #P Current pane index - #S Session name - #T Current window title - #W Current window name - ## A literal '#' - - The #(shell-command) form executes 'shell-command' and - inserts the first line of its output. Note that shell - commands are only executed once at the interval specified - by the status-interval option: if the status line is - redrawn in the meantime, the previous result is used. - Shell commands are executed with the tmux global environ- - ment set (see the ENVIRONMENT section). - - The window title (#T) is the title set by the program - running within the window using the OSC title setting - sequence, for example: - - $ printf '\033]2;My Title\033\\' - - When a window is first created, its title is the host- - name. - - #[attributes] allows a comma-separated list of attributes - to be specified, these may be 'fg=colour' to set the - foreground colour, 'bg=colour' to set the background - colour, the name of one of the attributes (listed under - the message-attr option) to turn an attribute on, or an - attribute prefixed with 'no' to turn one off, for example - nobright. Examples are: - - #(sysctl vm.loadavg) - #[fg=yellow,bold]#(apm -l)%%#[default] [#S] - - Where appropriate, special character sequences may be - prefixed with a number to specify the maximum length, for - example '#24T'. - - By default, UTF-8 in string is not interpreted, to enable - UTF-8, use the status-utf8 option. - - status-left-attr attributes - Set the attribute of the left part of the status line. - - status-left-fg colour - Set the foreground colour of the left part of the status - line. - - status-left-bg colour - Set the background colour of the left part of the status - line. - - status-left-length length - Set the maximum length of the left component of the sta- - tus bar. The default is 10. - - status-right string - Display string to the right of the status bar. By - default, the current window title in double quotes, the - date and the time are shown. As with status-left, string - will be passed to strftime(3), character pairs are - replaced, and UTF-8 is dependent on the status-utf8 - option. - - status-right-attr attributes - Set the attribute of the right part of the status line. - - status-right-fg colour - Set the foreground colour of the right part of the status - line. - - status-right-bg colour - Set the background colour of the right part of the status - line. - - status-right-length length - Set the maximum length of the right component of the sta- - tus bar. The default is 40. - - status-utf8 [on | off] - Instruct tmux to treat top-bit-set characters in the - status-left and status-right strings as UTF-8; notably, - this is important for wide characters. This option - defaults to off. - - terminal-overrides string - Contains a list of entries which override terminal - descriptions read using terminfo(5). string is a comma- - separated list of items each a colon-separated string - made up of a terminal type pattern (matched using - fnmatch(3)) and a set of name=value entries. - - For example, to set the 'clear' terminfo(5) entry to - '\e[H\e[2J' for all terminal types and the 'dch1' entry - to '\e[P' for the 'rxvt' terminal type, the option could - be set to the string: - - "*:clear=\e[H\e[2J,rxvt:dch1=\e[P" - - The terminal entry value is passed through strunvis(3) - before interpretation. The default value forcibly cor- - rects the 'colors' entry for terminals which support 88 - or 256 colours: - - "*88col*:colors=88,*256col*:colors=256" - - update-environment variables - Set a space-separated string containing a list of envi- - ronment variables to be copied into the session environ- - ment when a new session is created or an existing session - is attached. Any variables that do not exist in the - source environment are set to be removed from the session - environment (as if -r was given to the set-environment - command). The default is "DISPLAY WINDOWID SSH_ASKPASS - SSH_AUTH_SOCK SSH_AGENT_PID SSH_CONNECTION". - - visual-activity [on | off] - If on, display a status line message when activity occurs - in a window for which the monitor-activity window option - is enabled. - - visual-bell [on | off] - If this option is on, a message is shown on a bell - instead of it being passed through to the terminal (which - normally makes a sound). Also see the bell-action - option. - - visual-content [on | off] - Like visual-activity, display a message when content is - present in a window for which the monitor-content window - option is enabled. - - set-window-option [-agu] [-t target-window] option value - (alias: setw) - Set a window option. The -a, -g and -u flags work similarly to - the set-option command. - - Supported window options are: - - aggressive-resize [on | off] - Aggressively resize the chosen window. This means that - tmux will resize the window to the size of the smallest - session for which it is the current window, rather than - the smallest session to which it is attached. The window - may resize when the current window is changed on another - sessions; this option is good for full-screen programs - which support SIGWINCH and poor for interactive programs - such as shells. - - automatic-rename [on | off] - Control automatic window renaming. When this setting is - enabled, tmux will attempt - on supported platforms - to - rename the window to reflect the command currently run- - ning in it. This flag is automatically disabled for an - individual window when a name is specified at creation - with new-window or new-session, or later with - rename-window. It may be switched off globally with: - - set-window-option -g automatic-rename off - - clock-mode-colour colour - Set clock colour. - - clock-mode-style [12 | 24] - Set clock hour format. - - force-height height - force-width width - Prevent tmux from resizing a window to greater than width - or height. A value of zero restores the default unlim- - ited setting. - - main-pane-width width - main-pane-height height - Set the width or height of the main (left or top) pane in - the main-horizontal or main-vertical layouts. - - mode-attr attributes - Set window modes attributes. - - mode-bg colour - Set window modes background colour. - - mode-fg colour - Set window modes foreground colour. - - mode-keys [vi | emacs] - Use vi or emacs-style key bindings in copy and choice - modes. Key bindings default to emacs. - - mode-mouse [on | off] - Mouse state in modes. If on, the mouse may be used to - copy a selection by dragging in copy mode, or to select - an option in choice mode. - - monitor-activity [on | off] - Monitor for activity in the window. Windows with activ- - ity are highlighted in the status line. - - monitor-content match-string - Monitor content in the window. When fnmatch(3) pattern - match-string appears in the window, it is highlighted in - the status line. - - remain-on-exit [on | off] - A window with this flag set is not destroyed when the - program running in it exits. The window may be reacti- - vated with the respawn-window command. - - synchronize-panes [on | off] - Duplicate input to any pane to all other panes in the - same window (only for panes that are not in any special - mode). - - alternate-screen [on | off] - This option configures whether programs running inside - tmux may use the terminal alternate screen feature, which - allows the smcup and rmcup terminfo(5) capabilities to be - issued to preserve the existing window content on start - and restore it on exit. - - utf8 [on | off] - Instructs tmux to expect UTF-8 sequences to appear in - this window. - - window-status-attr attributes - Set status line attributes for a single window. - - window-status-bg colour - Set status line background colour for a single window. - - window-status-fg colour - Set status line foreground colour for a single window. - - window-status-format string - Set the format in which the window is displayed in the - status line window list. See the status-left option for - details of special character sequences available. The - default is '#I:#W#F'. - - window-status-alert-attr attributes - Set status line attributes for windows which have an - alert (bell, activity or content). - - window-status-alert-bg colour - Set status line background colour for windows with an - alert. - - window-status-alert-fg colour - Set status line foreground colour for windows with an - alert. - - window-status-current-attr attributes - Set status line attributes for the currently active win- - dow. - - window-status-current-bg colour - Set status line background colour for the currently - active window. - - window-status-current-fg colour - Set status line foreground colour for the currently - active window. - - window-status-current-format string - Like window-status-format, but is the format used when - the window is the current window. - - word-separators string - Sets the window's conception of what characters are con- - sidered word separators, for the purposes of the next and - previous word commands in copy mode. The default is - ' -_@'. - - xterm-keys [on | off] - If this option is set, tmux will generate xterm(1) -style - function key sequences; these have a number included to - indicate modifiers such as Shift, Alt or Ctrl. The - default is off. - - show-options [-gsw] [-t target-session | target-window] - (alias: show) - Show the window options with -w (equivalent to - show-window-options), the server options with -s, otherwise the - session options for target session. Global session or window - options are listed if -g is used. - - show-window-options [-g] [-t target-window] - (alias: showw) - List the window options for target-window, or the global window - options if -g is used. - -ENVIRONMENT - When the server is started, tmux copies the environment into the global - environment; in addition, each session has a session environment. When a - window is created, the session and global environments are merged with - the session environment overriding any variable present in both. This is - the initial environment passed to the new process. - - The update-environment session option may be used to update the session - environment from the client when a new session is created or an old reat- - tached. tmux also initialises the TMUX variable with some internal - information to allow commands to be executed from inside, and the TERM - variable with the correct terminal setting of 'screen'. - - Commands to alter and view the environment are: - - set-environment [-gru] [-t target-session] name [value] - (alias: setenv) - Set or unset an environment variable. If -g is used, the change - is made in the global environment; otherwise, it is applied to - the session environment for target-session. The -u flag unsets a - variable. -r indicates the variable is to be removed from the - environment before starting a new process. - - show-environment [-g] [-t target-session] - (alias: showenv) - Display the environment for target-session or the global environ- - ment with -g. Variables removed from the environment are pre- - fixed with '-'. - -STATUS LINE - tmux includes an optional status line which is displayed in the bottom - line of each terminal. By default, the status line is enabled (it may be - disabled with the status session option) and contains, from left-to- - right: the name of the current session in square brackets; the window - list; the current window title in double quotes; and the time and date. - - The status line is made of three parts: configurable left and right sec- - tions (which may contain dynamic content such as the time or output from - a shell command, see the status-left, status-left-length, status-right, - and status-right-length options below), and a central window list. By - default, the window list shows the index, name and (if any) flag of the - windows present in the current session in ascending numerical order. It - may be customised with the window-status-format and - window-status-current-format options. The flag is one of the following - symbols appended to the window name: - - Symbol Meaning - * Denotes the current window. - - Marks the last window (previously selected). - # Window is monitored and activity has been detected. - ! A bell has occurred in the window. - + Window is monitored for content and it has appeared. - - The # symbol relates to the monitor-activity and + to the monitor-content - window options. The window name is printed in inverted colours if an - alert (bell, activity or content) is present. - - The colour and attributes of the status line may be configured, the - entire status line using the status-attr, status-fg and status-bg session - options and individual windows using the window-status-attr, - window-status-fg and window-status-bg window options. - - The status line is automatically refreshed at interval if it has changed, - the interval may be controlled with the status-interval session option. - - Commands related to the status line are as follows: - - command-prompt [-p prompts] [-t target-client] [template] - Open the command prompt in a client. This may be used from - inside tmux to execute commands interactively. If template is - specified, it is used as the command. If -p is given, prompts is - a comma-separated list of prompts which are displayed in order; - otherwise a single prompt is displayed, constructed from template - if it is present, or ':' if not. Before the command is executed, - the first occurrence of the string '%%' and all occurrences of - '%1' are replaced by the response to the first prompt, the second - '%%' and all '%2' are replaced with the response to the second - prompt, and so on for further prompts. Up to nine prompt - responses may be replaced ('%1' to '%9'). - - confirm-before [-t target-client] command - (alias: confirm) - Ask for confirmation before executing command. This command - works only from inside tmux. - - display-message [-p] [-t target-client] [message] - (alias: display) - Display a message. If -p is given, the output is printed to std- - out, otherwise it is displayed in the target-client status line. - The format of message is as for status-left, with the exception - that #() are not handled. - -BUFFERS - tmux maintains a stack of paste buffers for each session. Up to the - value of the buffer-limit option are kept; when a new buffer is added, - the buffer at the bottom of the stack is removed. Buffers may be added - using copy-mode or the set-buffer command, and pasted into a window using - the paste-buffer command. - - A configurable history buffer is also maintained for each window. By - default, up to 2000 lines are kept; this can be altered with the - history-limit option (see the set-option command above). - - The buffer commands are as follows: - - choose-buffer [-t target-window] [template] - Put a window into buffer choice mode, where a buffer may be cho- - sen interactively from a list. After a buffer is selected, '%%' - is replaced by the buffer index in template and the result exe- - cuted as a command. If template is not given, "paste-buffer -b - '%%'" is used. This command works only from inside tmux. - - clear-history [-t target-pane] - (alias: clearhist) - Remove and free the history for the specified pane. - - copy-buffer [-a src-index] [-b dst-index] [-s src-session] [-t - dst-session] - (alias: copyb) - Copy a session paste buffer to another session. If no sessions - are specified, the current one is used instead. - - delete-buffer [-b buffer-index] [-t target-session] - (alias: deleteb) - Delete the buffer at buffer-index, or the top buffer if not spec- - ified. - - list-buffers [-t target-session] - (alias: lsb) - List the buffers in the given session. - - load-buffer [-b buffer-index] [-t target-session] path - (alias: loadb) - Load the contents of the specified paste buffer from path. - - paste-buffer [-dr] [-b buffer-index] [-s separator] [-t target-pane] - (alias: pasteb) - Insert the contents of a paste buffer into the specified pane. - If not specified, paste into the current one. With -d, also - delete the paste buffer from the stack. When output, any line- - feed (LF) characters in the paste buffer are replaced with a sep- - arator, by default carriage return (CR). A custom separator may - be specified using the -s flag. The -r flag means to do no - replacement (equivalent to a separator of LF). - - save-buffer [-a] [-b buffer-index] [-t target-session] path - (alias: saveb) - Save the contents of the specified paste buffer to path. The -a - option appends to rather than overwriting the file. - - set-buffer [-b buffer-index] [-t target-session] data - (alias: setb) - Set the contents of the specified buffer to data. - - show-buffer [-b buffer-index] [-t target-session] - (alias: showb) - Display the contents of the specified buffer. - -MISCELLANEOUS - Miscellaneous commands are as follows: - - clock-mode [-t target-pane] - Display a large clock. - - if-shell shell-command command - (alias: if) - Execute command if shell-command returns success. - - lock-server - (alias: lock) - Lock each client individually by running the command specified by - the lock-command option. - - run-shell shell-command - (alias: run) - Execute shell-command in the background without creating a win- - dow. After it finishes, any output to stdout is displayed in - copy mode. If the command doesn't return success, the exit sta- - tus is also displayed. - - server-info - (alias: info) - Show server information and terminal details. - -FILES - ~/.tmux.conf Default tmux configuration file. - /etc/tmux.conf System-wide configuration file. - -EXAMPLES - To create a new tmux session running vi(1): - - $ tmux new-session vi - - Most commands have a shorter form, known as an alias. For new-session, - this is new: - - $ tmux new vi - - Alternatively, the shortest unambiguous form of a command is accepted. - If there are several options, they are listed: - - $ tmux n - ambiguous command: n, could be: new-session, new-window, next-window - - Within an active session, a new window may be created by typing 'C-b c' - (Ctrl followed by the 'b' key followed by the 'c' key). - - Windows may be navigated with: 'C-b 0' (to select window 0), 'C-b 1' (to - select window 1), and so on; 'C-b n' to select the next window; and 'C-b - p' to select the previous window. - - A session may be detached using 'C-b d' (or by an external event such as - ssh(1) disconnection) and reattached with: - - $ tmux attach-session - - Typing 'C-b ?' lists the current key bindings in the current window; up - and down may be used to navigate the list or 'q' to exit from it. - - Commands to be run when the tmux server is started may be placed in the - ~/.tmux.conf configuration file. Common examples include: - - Changing the default prefix key: - - set-option -g prefix C-a - unbind-key C-b - bind-key C-a send-prefix - - Turning the status line off, or changing its colour: - - set-option -g status off - set-option -g status-bg blue - - Setting other options, such as the default command, or locking after 30 - minutes of inactivity: - - set-option -g default-command "exec /bin/ksh" - set-option -g lock-after-time 1800 - - Creating new key bindings: - - bind-key b set-option status - bind-key / command-prompt "split-window 'exec man %%'" - bind-key S command-prompt "new-window -n %1 'ssh %1'" - -SEE ALSO - pty(4) - -AUTHORS - Nicholas Marriott - -BSD October 19, 2013 BSD diff --git a/manual/1.4 b/manual/1.4 deleted file mode 100644 index 534902a01e0..00000000000 --- a/manual/1.4 +++ /dev/null @@ -1,1633 +0,0 @@ -TMUX(1) BSD General Commands Manual TMUX(1) - -NAME - tmux -- terminal multiplexer - -SYNOPSIS - tmux [-28lquvV] [-c shell-command] [-f file] [-L socket-name] - [-S socket-path] [command [flags]] - -DESCRIPTION - tmux is a terminal multiplexer: it enables a number of terminals to be - created, accessed, and controlled from a single screen. tmux may be - detached from a screen and continue running in the background, then later - reattached. - - When tmux is started it creates a new session with a single window and - displays it on screen. A status line at the bottom of the screen shows - information on the current session and is used to enter interactive com- - mands. - - A session is a single collection of pseudo terminals under the management - of tmux. Each session has one or more windows linked to it. A window - occupies the entire screen and may be split into rectangular panes, each - of which is a separate pseudo terminal (the pty(4) manual page documents - the technical details of pseudo terminals). Any number of tmux instances - may connect to the same session, and any number of windows may be present - in the same session. Once all sessions are killed, tmux exits. - - Each session is persistent and will survive accidental disconnection - (such as ssh(1) connection timeout) or intentional detaching (with the - 'C-b d' key strokes). tmux may be reattached using: - - $ tmux attach - - In tmux, a session is displayed on screen by a client and all sessions - are managed by a single server. The server and each client are separate - processes which communicate through a socket in /tmp. - - The options are as follows: - - -2 Force tmux to assume the terminal supports 256 colours. - - -8 Like -2, but indicates that the terminal supports 88 - colours. - - -c shell-command - Execute shell-command using the default shell. If neces- - sary, the tmux server will be started to retrieve the - default-shell option. This option is for compatibility - with sh(1) when tmux is used as a login shell. - - -f file Specify an alternative configuration file. By default, - tmux loads the system configuration file from - /etc/tmux.conf, if present, then looks for a user configu- - ration file at ~/.tmux.conf. The configuration file is a - set of tmux commands which are executed in sequence when - the server is first started. - - If a command in the configuration file fails, tmux will - report an error and exit without executing further com- - mands. - - -L socket-name - tmux stores the server socket in a directory under /tmp; - the default socket is named default. This option allows a - different socket name to be specified, allowing several - independent tmux servers to be run. Unlike -S a full path - is not necessary: the sockets are all created in the same - directory. - - If the socket is accidentally removed, the SIGUSR1 signal - may be sent to the tmux server process to recreate it. - - -l Behave as a login shell. This flag currently has no effect - and is for compatibility with other shells when using tmux - as a login shell. - - -q Set the quiet server option to prevent the server sending - various informational messages. - - -S socket-path - Specify a full alternative path to the server socket. If - -S is specified, the default socket directory is not used - and any -L flag is ignored. - - -u tmux attempts to guess if the terminal is likely to support - UTF-8 by checking the first of the LC_ALL, LC_CTYPE and - LANG environment variables to be set for the string - "UTF-8". This is not always correct: the -u flag explic- - itly informs tmux that UTF-8 is supported. - - If the server is started from a client passed -u or where - UTF-8 is detected, the utf8 and status-utf8 options are - enabled in the global window and session options respec- - tively. - - -v Request verbose logging. This option may be specified mul- - tiple times for increasing verbosity. Log messages will be - saved into tmux-client-PID.log and tmux-server-PID.log - files in the current directory, where PID is the PID of the - server or client process. - - -V Report the tmux version. - - command [flags] - This specifies one of a set of commands used to control - tmux, as described in the following sections. If no com- - mands are specified, the new-session command is assumed. - -KEY BINDINGS - tmux may be controlled from an attached client by using a key combination - of a prefix key, 'C-b' (Ctrl-b) by default, followed by a command key. - - The default command key bindings are: - - C-b Send the prefix key (C-b) through to the application. - C-o Rotate the panes in the current window forwards. - C-z Suspend the tmux client. - ! Break the current pane out of the window. - " Split the current pane into two, top and bottom. - # List all paste buffers. - % Split the current pane into two, left and right. - & Kill the current window. - ' Prompt for a window index to select. - , Rename the current window. - - Delete the most recently copied buffer of text. - . Prompt for an index to move the current window. - 0 to 9 Select windows 0 to 9. - : Enter the tmux command prompt. - ; Move to the previously active pane. - = Choose which buffer to paste interactively from a list. - ? List all key bindings. - D Choose a client to detach. - [ Enter copy mode to copy text or view the history. - ] Paste the most recently copied buffer of text. - c Create a new window. - d Detach the current client. - f Prompt to search for text in open windows. - i Display some information about the current window. - l Move to the previously selected window. - n Change to the next window. - o Select the next pane in the current window. - p Change to the previous window. - q Briefly display pane indexes. - r Force redraw of the attached client. - s Select a new session for the attached client interac- - tively. - L Switch the attached client back to the last session. - t Show the time. - w Choose the current window interactively. - x Kill the current pane. - { Swap the current pane with the previous pane. - } Swap the current pane with the next pane. - ~ Show previous messages from tmux, if any. - Page Up Enter copy mode and scroll one page up. - Up, Down - Left, Right - Change to the pane above, below, to the left, or to the - right of the current pane. - M-1 to M-5 Arrange panes in one of the five preset layouts: even- - horizontal, even-vertical, main-horizontal, main-verti- - cal, or tiled. - M-n Move to the next window with a bell or activity marker. - M-o Rotate the panes in the current window backwards. - M-p Move to the previous window with a bell or activity - marker. - C-Up, C-Down - C-Left, C-Right - Resize the current pane in steps of one cell. - M-Up, M-Down - M-Left, M-Right - Resize the current pane in steps of five cells. - - Key bindings may be changed with the bind-key and unbind-key commands. - -COMMANDS - This section contains a list of the commands supported by tmux. Most - commands accept the optional -t argument with one of target-client, - target-session target-window, or target-pane. These specify the client, - session, window or pane which a command should affect. target-client is - the name of the pty(4) file to which the client is connected, for example - either of /dev/ttyp1 or ttyp1 for the client attached to /dev/ttyp1. If - no client is specified, the current client is chosen, if possible, or an - error is reported. Clients may be listed with the list-clients command. - - target-session is either the name of a session (as listed by the - list-sessions command) or the name of a client with the same syntax as - target-client, in which case the session attached to the client is used. - When looking for the session name, tmux initially searches for an exact - match; if none is found, the session names are checked for any for which - target-session is a prefix or for which it matches as an fnmatch(3) pat- - tern. If a single match is found, it is used as the target session; mul- - tiple matches produce an error. If a session is omitted, the current - session is used if available; if no current session is available, the - most recently used is chosen. - - target-window specifies a window in the form session:window. session - follows the same rules as for target-session, and window is looked for in - order: as a window index, for example mysession:1; as an exact window - name, such as mysession:mywindow; then as an fnmatch(3) pattern or the - start of a window name, such as mysession:mywin* or mysession:mywin. An - empty window name specifies the next unused index if appropriate (for - example the new-window and link-window commands) otherwise the current - window in session is chosen. The special character '!' uses the last - (previously current) window, or '+' and '-' are the next window or the - previous window by number. When the argument does not contain a colon, - tmux first attempts to parse it as window; if that fails, an attempt is - made to match a session. - - target-pane takes a similar form to target-window but with the optional - addition of a period followed by a pane index, for example: myses- - sion:mywindow.1. If the pane index is omitted, the currently active pane - in the specified window is used. If neither a colon nor period appears, - tmux first attempts to use the argument as a pane index; if that fails, - it is looked up as for target-window. A '+' or '-' indicate the next or - previous pane index, respectively. One of the strings top, bottom, left, - right, top-left, top-right, bottom-left or bottom-right may be used - instead of a pane index. - - The special characters '+' and '-' may be followed by an offset, for - example: - - select-window -t:+2 - - When dealing with a session that doesn't contain sequential window - indexes, they will be correctly skipped. - - shell-command arguments are sh(1) commands. These must be passed as a - single item, which typically means quoting them, for example: - - new-window 'vi /etc/passwd' - - command [arguments] refers to a tmux command, passed with the command and - arguments separately, for example: - - bind-key F1 set-window-option force-width 81 - - Or if using sh(1): - - $ tmux bind-key F1 set-window-option force-width 81 - - Multiple commands may be specified together as part of a command - sequence. Each command should be separated by spaces and a semicolon; - commands are executed sequentially from left to right. A literal semi- - colon may be included by escaping it with a backslash (for example, when - specifying a command sequence to bind-key). - - Example tmux commands include: - - refresh-client -t/dev/ttyp2 - - rename-session -tfirst newname - - set-window-option -t:0 monitor-activity on - - new-window ; split-window -d - - Or from sh(1): - - $ tmux kill-window -t :1 - - $ tmux new-window \; split-window -d - - $ tmux new-session -d 'vi /etc/passwd' \; split-window -d \; attach - -CLIENTS AND SESSIONS - The tmux server manages clients, sessions, windows and panes. Clients - are attached to sessions to interact with them, either when they are cre- - ated with the new-session command, or later with the attach-session com- - mand. Each session has one or more windows linked into it. Windows may - be linked to multiple sessions and are made up of one or more panes, each - of which contains a pseudo terminal. Commands for creating, linking and - otherwise manipulating windows are covered in the WINDOWS AND PANES sec- - tion. - - The following commands are available to manage clients and sessions: - - attach-session [-dr] [-t target-session] - (alias: attach) - If run from outside tmux, create a new client in the current ter- - minal and attach it to target-session. If used from inside, - switch the current client. If -d is specified, any other clients - attached to the session are detached. -r signifies the client is - read-only (only keys bound to the detach-client command have any - effect) - - If no server is started, attach-session will attempt to start it; - this will fail unless sessions are created in the configuration - file. - - detach-client [-t target-client] - (alias: detach) - Detach the current client if bound to a key, or the specified - client with -t. - - has-session [-t target-session] - (alias: has) - Report an error and exit with 1 if the specified session does not - exist. If it does exist, exit with 0. - - kill-server - Kill the tmux server and clients and destroy all sessions. - - kill-session [-t target-session] - Destroy the given session, closing any windows linked to it and - no other sessions, and detaching all clients attached to it. - - list-clients - (alias: lsc) - List all clients attached to the server. - - list-commands - (alias: lscm) - List the syntax of all commands supported by tmux. - - list-sessions - (alias: ls) - List all sessions managed by the server. - - lock-client [-t target-client] - (alias: lockc) - Lock target-client, see the lock-server command. - - lock-session [-t target-session] - (alias: locks) - Lock all clients attached to target-session. - - new-session [-d] [-n window-name] [-s session-name] [-t target-session] - [shell-command] - (alias: new) - Create a new session with name session-name. - - The new session is attached to the current terminal unless -d is - given. window-name and shell-command are the name of and shell - command to execute in the initial window. - - If run from a terminal, any termios(4) special characters are - saved and used for new windows in the new session. - - If -t is given, the new session is grouped with target-session. - This means they share the same set of windows - all windows from - target-session are linked to the new session and any subsequent - new windows or windows being closed are applied to both sessions. - The current and previous window and any session options remain - independent and either session may be killed without affecting - the other. Giving -n or shell-command are invalid if -t is used. - - refresh-client [-t target-client] - (alias: refresh) - Refresh the current client if bound to a key, or a single client - if one is given with -t. - - rename-session [-t target-session] new-name - (alias: rename) - Rename the session to new-name. - - show-messages [-t target-client] - (alias: showmsgs) - Any messages displayed on the status line are saved in a per- - client message log, up to a maximum of the limit set by the - message-limit session option for the session attached to that - client. This command displays the log for target-client. - - source-file path - (alias: source) - Execute commands from path. - - start-server - (alias: start) - Start the tmux server, if not already running, without creating - any sessions. - - suspend-client [-c target-client] - (alias: suspendc) - Suspend a client by sending SIGTSTP (tty stop). - - switch-client [-lnp] [-c target-client] [-t target-session] - (alias: switchc) - Switch the current session for client target-client to - target-session. If -l, -n or -p is used, the client is moved to - the last, next or previous session respectively. - -WINDOWS AND PANES - A tmux window may be in one of several modes. The default permits direct - access to the terminal attached to the window. The other is copy mode, - which permits a section of a window or its history to be copied to a - paste buffer for later insertion into another window. This mode is - entered with the copy-mode command, bound to '[' by default. It is also - entered when a command that produces output, such as list-keys, is exe- - cuted from a key binding. - - The keys available depend on whether emacs or vi mode is selected (see - the mode-keys option). The following keys are supported as appropriate - for the mode: - - Function vi emacs - Back to indentation ^ M-m - Bottom of history G M-< - Clear selection Escape C-g - Copy selection Enter M-w - Cursor down j Down - Cursor left h Left - Cursor right l Right - Cursor to bottom line L - Cursor to middle line M M-r - Cursor to top line H M-R - Cursor up k Up - Delete entire line d C-u - Delete to end of line D C-k - End of line $ C-e - Go to line : g - Half page down C-d M-Down - Half page up C-u M-Up - Jump forward f f - Jump backward F F - Jump again ; ; - Jump again in reverse , , - Next page C-f Page down - Next space W - Next space, end of word E - Next word w - Next word end e M-f - Paste buffer p C-y - Previous page C-b Page up - Previous word b M-b - Previous space B - Quit mode q Escape - Rectangle toggle v R - Scroll down C-Down or C-e C-Down - Scroll up C-Up or C-y C-Up - Search again n n - Search again in reverse N N - Search backward ? C-r - Search forward / C-s - Start of line 0 C-a - Start selection Space C-Space - Top of history g M-> - Transpose chars C-t - - The next and previous word keys use space and the '-', '_' and '@' char- - acters as word delimiters by default, but this can be adjusted by setting - the word-separators window option. Next word moves to the start of the - next word, next word end to the end of the next word and previous word to - the start of the previous word. The three next and previous space keys - work similarly but use a space alone as the word separator. - - The jump commands enable quick movement within a line. For instance, - typing 'f' followed by '/' will move the cursor to the next '/' character - on the current line. A ';' will then jump to the next occurrence. - - Commands in copy mode may be prefaced by an optional repeat count. With - vi key bindings, a prefix is entered using the number keys; with emacs, - the Alt (meta) key and a number begins prefix entry. For example, to - move the cursor forward by ten words, use 'M-1 0 M-f' in emacs mode, and - '10w' in vi. - - Mode key bindings are defined in a set of named tables: vi-edit and - emacs-edit for keys used when line editing at the command prompt; - vi-choice and emacs-choice for keys used when choosing from lists (such - as produced by the choose-window command); and vi-copy and emacs-copy - used in copy mode. The tables may be viewed with the list-keys command - and keys modified or removed with bind-key and unbind-key. - - The paste buffer key pastes the first line from the top paste buffer on - the stack. - - The synopsis for the copy-mode command is: - - copy-mode [-u] [-t target-pane] - Enter copy mode. The -u option scrolls one page up. - - Each window displayed by tmux may be split into one or more panes; each - pane takes up a certain area of the display and is a separate terminal. - A window may be split into panes using the split-window command. Windows - may be split horizontally (with the -h flag) or vertically. Panes may be - resized with the resize-pane command (bound to 'C-up', 'C-down' 'C-left' - and 'C-right' by default), the current pane may be changed with the - select-pane command and the rotate-window and swap-pane commands may be - used to swap panes without changing their position. Panes are numbered - beginning from zero in the order they are created. - - A number of preset layouts are available. These may be selected with the - select-layout command or cycled with next-layout (bound to 'Space' by - default); once a layout is chosen, panes within it may be moved and - resized as normal. - - The following layouts are supported: - - even-horizontal - Panes are spread out evenly from left to right across the window. - - even-vertical - Panes are spread evenly from top to bottom. - - main-horizontal - A large (main) pane is shown at the top of the window and the - remaining panes are spread from left to right in the leftover - space at the bottom. Use the main-pane-height window option to - specify the height of the top pane. - - main-vertical - Similar to main-horizontal but the large pane is placed on the - left and the others spread from top to bottom along the right. - See the main-pane-width window option. - - tiled Panes are spread out as evenly as possible over the window in - both rows and columns. - - In addition, select-layout may be used to apply a previously used layout - - the list-windows command displays the layout of each window in a form - suitable for use with select-layout. For example: - - $ tmux list-windows - 0: ksh [159x48] - layout: bb62,159x48,0,0{79x48,0,0,79x48,80,0} - $ tmux select-layout bb62,159x48,0,0{79x48,0,0,79x48,80,0} - - tmux automatically adjusts the size of the layout for the current window - size. Note that a layout cannot be applied to a window with more panes - than that from which the layout was originally defined. - - Commands related to windows and panes are as follows: - - break-pane [-d] [-t target-pane] - (alias: breakp) - Break target-pane off from its containing window to make it the - only pane in a new window. If -d is given, the new window does - not become the current window. - - capture-pane [-b buffer-index] [-t target-pane] - (alias: capturep) - Capture the contents of a pane to the specified buffer, or a new - buffer if none is specified. - - choose-client [-t target-window] [template] - Put a window into client choice mode, allowing a client to be - selected interactively from a list. After a client is chosen, - '%%' is replaced by the client pty(4) path in template and the - result executed as a command. If template is not given, "detach- - client -t '%%'" is used. This command works only from inside - tmux. - - choose-session [-t target-window] [template] - Put a window into session choice mode, where a session may be - selected interactively from a list. When one is chosen, '%%' is - replaced by the session name in template and the result executed - as a command. If template is not given, "switch-client -t '%%'" - is used. This command works only from inside tmux. - - choose-window [-t target-window] [template] - Put a window into window choice mode, where a window may be cho- - sen interactively from a list. After a window is selected, '%%' - is replaced by the session name and window index in template and - the result executed as a command. If template is not given, - "select-window -t '%%'" is used. This command works only from - inside tmux. - - display-panes [-t target-client] - (alias: displayp) - Display a visible indicator of each pane shown by target-client. - See the display-panes-time, display-panes-colour, and - display-panes-active-colour session options. While the indicator - is on screen, a pane may be selected with the '0' to '9' keys. - - find-window [-t target-window] match-string - (alias: findw) - Search for the fnmatch(3) pattern match-string in window names, - titles, and visible content (but not history). If only one win- - dow is matched, it'll be automatically selected, otherwise a - choice list is shown. This command only works from inside tmux. - - join-pane [-dhv] [-l size | -p percentage] [-s src-pane] [-t dst-pane] - (alias: joinp) - Like split-window, but instead of splitting dst-pane and creating - a new pane, split it and move src-pane into the space. This can - be used to reverse break-pane. - - kill-pane [-a] [-t target-pane] - (alias: killp) - Destroy the given pane. If no panes remain in the containing - window, it is also destroyed. The -a option kills all but the - pane given with -t. - - kill-window [-t target-window] - (alias: killw) - Kill the current window or the window at target-window, removing - it from any sessions to which it is linked. - - last-pane [-t target-window] - (alias: lastp) - Select the last (previously selected) pane. - - last-window [-t target-session] - (alias: last) - Select the last (previously selected) window. If no - target-session is specified, select the last window of the cur- - rent session. - - link-window [-dk] [-s src-window] [-t dst-window] - (alias: linkw) - Link the window at src-window to the specified dst-window. If - dst-window is specified and no such window exists, the src-window - is linked there. If -k is given and dst-window exists, it is - killed, otherwise an error is generated. If -d is given, the - newly linked window is not selected. - - list-panes [-t target-window] - (alias: lsp) - List the panes in the current window or in target-window. - - list-windows [-t target-session] - (alias: lsw) - List windows in the current session or in target-session. - - move-window [-dk] [-s src-window] [-t dst-window] - (alias: movew) - This is similar to link-window, except the window at src-window - is moved to dst-window. - - new-window [-adk] [-n window-name] [-t target-window] [shell-command] - (alias: neww) - Create a new window. With -a, the new window is inserted at the - next index up from the specified target-window, moving windows up - if necessary, otherwise target-window is the new window location. - - If -d is given, the session does not make the new window the cur- - rent window. target-window represents the window to be created; - if the target already exists an error is shown, unless the -k - flag is used, in which case it is destroyed. shell-command is - the command to execute. If shell-command is not specified, the - value of the default-command option is used. - - When the shell command completes, the window closes. See the - remain-on-exit option to change this behaviour. - - The TERM environment variable must be set to ``screen'' for all - programs running inside tmux. New windows will automatically - have ``TERM=screen'' added to their environment, but care must be - taken not to reset this in shell start-up files. - - next-layout [-t target-window] - (alias: nextl) - Move a window to the next layout and rearrange the panes to fit. - - next-window [-a] [-t target-session] - (alias: next) - Move to the next window in the session. If -a is used, move to - the next window with a bell, activity or content alert. - - pipe-pane [-o] [-t target-pane] [shell-command] - (alias: pipep) - Pipe any output sent by the program in target-pane to a shell - command. A pane may only be piped to one command at a time, any - existing pipe is closed before shell-command is executed. The - shell-command string may contain the special character sequences - supported by the status-left command. If no shell-command is - given, the current pipe (if any) is closed. - - The -o option only opens a new pipe if no previous pipe exists, - allowing a pipe to be toggled with a single key, for example: - - bind-key C-p pipe-pane -o 'cat >>~/output.#I-#P' - - previous-layout [-t target-window] - (alias: prevl) - Move to the previous layout in the session. - - previous-window [-a] [-t target-session] - (alias: prev) - Move to the previous window in the session. With -a, move to the - previous window with a bell, activity or content alert. - - rename-window [-t target-window] new-name - (alias: renamew) - Rename the current window, or the window at target-window if - specified, to new-name. - - resize-pane [-DLRU] [-t target-pane] [adjustment] - (alias: resizep) - Resize a pane, upward with -U (the default), downward with -D, to - the left with -L and to the right with -R. The adjustment is - given in lines or cells (the default is 1). - - respawn-window [-k] [-t target-window] [shell-command] - (alias: respawnw) - Reactivate a window in which the command has exited (see the - remain-on-exit window option). If shell-command is not given, - the command used when the window was created is executed. The - window must be already inactive, unless -k is given, in which - case any existing command is killed. - - rotate-window [-DU] [-t target-window] - (alias: rotatew) - Rotate the positions of the panes within a window, either upward - (numerically lower) with -U or downward (numerically higher). - - select-layout [-t target-window] [layout-name] - (alias: selectl) - Choose a specific layout for a window. If layout-name is not - given, the last preset layout used (if any) is reapplied. - - select-pane [-DLRU] [-t target-pane] - (alias: selectp) - Make pane target-pane the active pane in window target-window. - If one of -D, -L, -R, or -U is used, respectively the pane below, - to the left, to the right, or above the target pane is used. - - select-window [-t target-window] - (alias: selectw) - Select the window at target-window. - - split-window [-dhv] [-l size | -p percentage] [-t target-pane] - [shell-command] - (alias: splitw) - Create a new pane by splitting target-pane: -h does a horizontal - split and -v a vertical split; if neither is specified, -v is - assumed. The -l and -p options specify the size of the new pane - in lines (for vertical split) or in cells (for horizontal split), - or as a percentage, respectively. All other options have the - same meaning as for the new-window command. - - swap-pane [-dDU] [-s src-pane] [-t dst-pane] - (alias: swapp) - Swap two panes. If -U is used and no source pane is specified - with -s, dst-pane is swapped with the previous pane (before it - numerically); -D swaps with the next pane (after it numerically). - -d instructs tmux not to change the active pane. - - swap-window [-d] [-s src-window] [-t dst-window] - (alias: swapw) - This is similar to link-window, except the source and destination - windows are swapped. It is an error if no window exists at - src-window. - - unlink-window [-k] [-t target-window] - (alias: unlinkw) - Unlink target-window. Unless -k is given, a window may be - unlinked only if it is linked to multiple sessions - windows may - not be linked to no sessions; if -k is specified and the window - is linked to only one session, it is unlinked and destroyed. - -KEY BINDINGS - tmux allows a command to be bound to most keys, with or without a prefix - key. When specifying keys, most represent themselves (for example 'A' to - 'Z'). Ctrl keys may be prefixed with 'C-' or '^', and Alt (meta) with - 'M-'. In addition, the following special key names are accepted: Up, - Down, Left, Right, BSpace, BTab, DC (Delete), End, Enter, Escape, F1 to - F20, Home, IC (Insert), NPage (Page Up), PPage (Page Down), Space, and - Tab. Note that to bind the '"' or ''' keys, quotation marks are neces- - sary, for example: - - bind-key '"' split-window - bind-key "'" new-window - - Commands related to key bindings are as follows: - - bind-key [-cnr] [-t key-table] key command [arguments] - (alias: bind) - Bind key key to command. By default (without -t) the primary key - bindings are modified (those normally activated with the prefix - key); in this case, if -n is specified, it is not necessary to - use the prefix key, command is bound to key alone. The -r flag - indicates this key may repeat, see the repeat-time option. - - If -t is present, key is bound in key-table: the binding for com- - mand mode with -c or for normal mode without. To view the - default bindings and possible commands, see the list-keys com- - mand. - - list-keys [-t key-table] - (alias: lsk) - List all key bindings. Without -t the primary key bindings - - those executed when preceded by the prefix key - are printed. - Keys bound without the prefix key (see bind-key -n) are marked - with '(no prefix)'. - - With -t, the key bindings in key-table are listed; this may be - one of: vi-edit, emacs-edit, vi-choice, emacs-choice, vi-copy or - emacs-copy. - - send-keys [-t target-pane] key ... - (alias: send) - Send a key or keys to a window. Each argument key is the name of - the key (such as 'C-a' or 'npage' ) to send; if the string is not - recognised as a key, it is sent as a series of characters. All - arguments are sent sequentially from first to last. - - send-prefix [-t target-pane] - Send the prefix key to a window as if it was pressed. If multi- - ple prefix keys are configured, only the first is sent. - - unbind-key [-acn] [-t key-table] key - (alias: unbind) - Unbind the command bound to key. Without -t the primary key - bindings are modified; in this case, if -n is specified, the com- - mand bound to key without a prefix (if any) is removed. If -a is - present, all key bindings are removed. - - If -t is present, key in key-table is unbound: the binding for - command mode with -c or for normal mode without. - -OPTIONS - The appearance and behaviour of tmux may be modified by changing the - value of various options. There are three types of option: server - options, session options and window options. - - The tmux server has a set of global options which do not apply to any - particular window or session. These are altered with the set-option -s - command, or displayed with the show-options -s command. - - In addition, each individual session may have a set of session options, - and there is a separate set of global session options. Sessions which do - not have a particular option configured inherit the value from the global - session options. Session options are set or unset with the set-option - command and may be listed with the show-options command. The available - server and session options are listed under the set-option command. - - Similarly, a set of window options is attached to each window, and there - is a set of global window options from which any unset options are inher- - ited. Window options are altered with the set-window-option command and - can be listed with the show-window-options command. All window options - are documented with the set-window-option command. - - Commands which set options are as follows: - - set-option [-agsuw] [-t target-session | target-window] option value - (alias: set) - Set a window option with -w (equivalent to the set-window-option - command), a server option with -s, otherwise a session option. - - If -g is specified, the global session or window option is set. - With -a, and if the option expects a string, value is appended to - the existing setting. The -u flag unsets an option, so a session - inherits the option from the global options. It is not possible - to unset a global option. - - Available window options are listed under set-window-option. - - Available server options are: - - escape-time - Set the time in milliseconds for which tmux waits after - an escape is input to determine if it is part of a func- - tion or meta key sequences. The default is 500 millisec- - onds. - - exit-unattached - If enabled, the server will exit when there are no - attached clients, rather than when there are no attached - sessions. - - quiet Enable or disable the display of various informational - messages (see also the -q command line flag). - - Available session options are: - - base-index index - Set the base index from which an unused index should be - searched when a new window is created. The default is - zero. - - bell-action [any | none | current] - Set action on window bell. any means a bell in any win- - dow linked to a session causes a bell in the current win- - dow of that session, none means all bells are ignored and - current means only bell in windows other than the current - window are ignored. - - buffer-limit number - Set the number of buffers kept for each session; as new - buffers are added to the top of the stack, old ones are - removed from the bottom if necessary to maintain this - maximum length. - - default-command shell-command - Set the command used for new windows (if not specified - when the window is created) to shell-command, which may - be any sh(1) command. The default is an empty string, - which instructs tmux to create a login shell using the - value of the default-shell option. - - default-path path - Set the default working directory for processes created - from keys, or interactively from the prompt. The default - is empty, which means to use the working directory of the - shell from which the server was started if it is avail- - able or the user's home if not. - - default-shell path - Specify the default shell. This is used as the login - shell for new windows when the default-command option is - set to empty, and must be the full path of the exe- - cutable. When started tmux tries to set a default value - from the first suitable of the SHELL environment vari- - able, the shell returned by getpwuid(3), or /bin/sh. - This option should be configured when tmux is used as a - login shell. - - default-terminal terminal - Set the default terminal for new windows created in this - session - the default value of the TERM environment vari- - able. For tmux to work correctly, this must be set to - 'screen' or a derivative of it. - - destroy-unattached - If enabled and the session is no longer attached to any - clients, it is destroyed. - - detach-on-destroy - If on (the default), the client is detached when the ses- - sion it is attached to is destroyed. If off, the client - is switched to the most recently active of the remaining - sessions. - - display-panes-active-colour colour - Set the colour used by the display-panes command to show - the indicator for the active pane. - - display-panes-colour colour - Set the colour used by the display-panes command to show - the indicators for inactive panes. - - display-panes-time time - Set the time in milliseconds for which the indicators - shown by the display-panes command appear. - - display-time time - Set the amount of time for which status line messages and - other on-screen indicators are displayed. time is in - milliseconds. - - history-limit lines - Set the maximum number of lines held in window history. - This setting applies only to new windows - existing win- - dow histories are not resized and retain the limit at the - point they were created. - - lock-after-time number - Lock the session (like the lock-session command) after - number seconds of inactivity, or the entire server (all - sessions) if the lock-server option is set. The default - is not to lock (set to 0). - - lock-command shell-command - Command to run when locking each client. The default is - to run lock(1) with -np. - - lock-server [on | off] - If this option is on (the default), instead of each ses- - sion locking individually as each has been idle for - lock-after-time, the entire server will lock after all - sessions would have locked. This has no effect as a ses- - sion option; it must be set as a global option. - - message-attr attributes - Set status line message attributes, where attributes is - either none or a comma-delimited list of one or more of: - bright (or bold), dim, underscore, blink, reverse, - hidden, or italics. - - message-bg colour - Set status line message background colour, where colour - is one of: black, red, green, yellow, blue, magenta, - cyan, white, colour0 to colour255 from the 256-colour - palette, or default. - - message-fg colour - Set status line message foreground colour. - - message-limit number - Set the number of error or information messages to save - in the message log for each client. The default is 20. - - mouse-select-pane [on | off] - If on, tmux captures the mouse and when a window is split - into multiple panes the mouse may be used to select the - current pane. The mouse click is also passed through to - the application as normal. - - pane-active-border-bg colour - - pane-active-border-fg colour - Set the pane border colour for the currently active pane. - - pane-border-bg colour - - pane-border-fg colour - Set the pane border colour for panes aside from the - active pane. - - prefix keys - Set the keys accepted as a prefix key. keys is a comma- - separated list of key names, each of which individually - behave as the prefix key. - - repeat-time time - Allow multiple commands to be entered without pressing - the prefix-key again in the specified time milliseconds - (the default is 500). Whether a key repeats may be set - when it is bound using the -r flag to bind-key. Repeat - is enabled for the default keys bound to the resize-pane - command. - - set-remain-on-exit [on | off] - Set the remain-on-exit window option for any windows - first created in this session. When this option is true, - windows in which the running program has exited do not - close, instead remaining open but inactivate. Use the - respawn-window command to reactivate such a window, or - the kill-window command to destroy it. - - set-titles [on | off] - Attempt to set the window title using the \e]2;...\007 - xterm code if the terminal appears to be an xterm. This - option is off by default. Note that elinks will only - attempt to set the window title if the STY environment - variable is set. - - set-titles-string string - String used to set the window title if set-titles is on. - Character sequences are replaced as for the status-left - option. - - status [on | off] - Show or hide the status line. - - status-attr attributes - Set status line attributes. - - status-bg colour - Set status line background colour. - - status-fg colour - Set status line foreground colour. - - status-interval interval - Update the status bar every interval seconds. By - default, updates will occur every 15 seconds. A setting - of zero disables redrawing at interval. - - status-justify [left | centre | right] - Set the position of the window list component of the sta- - tus line: left, centre or right justified. - - status-keys [vi | emacs] - Use vi or emacs-style key bindings in the status line, - for example at the command prompt. The default is emacs, - unless the VISUAL or EDITOR environment variables are set - and contain the string 'vi'. - - status-left string - Display string to the left of the status bar. string - will be passed through strftime(3) before being used. By - default, the session name is shown. string may contain - any of the following special character sequences: - - Character pair Replaced with - #(shell-command) First line of the command's - output - #[attributes] Colour or attribute change - #H Hostname of local host - #F Current window flag - #I Current window index - #P Current pane index - #S Session name - #T Current window title - #W Current window name - ## A literal '#' - - The #(shell-command) form executes 'shell-command' and - inserts the first line of its output. Note that shell - commands are only executed once at the interval specified - by the status-interval option: if the status line is - redrawn in the meantime, the previous result is used. - Shell commands are executed with the tmux global environ- - ment set (see the ENVIRONMENT section). - - The window title (#T) is the title set by the program - running within the window using the OSC title setting - sequence, for example: - - $ printf '\033]2;My Title\033\\' - - When a window is first created, its title is the host- - name. - - #[attributes] allows a comma-separated list of attributes - to be specified, these may be 'fg=colour' to set the - foreground colour, 'bg=colour' to set the background - colour, the name of one of the attributes (listed under - the message-attr option) to turn an attribute on, or an - attribute prefixed with 'no' to turn one off, for example - nobright. Examples are: - - #(sysctl vm.loadavg) - #[fg=yellow,bold]#(apm -l)%%#[default] [#S] - - Where appropriate, special character sequences may be - prefixed with a number to specify the maximum length, for - example '#24T'. - - By default, UTF-8 in string is not interpreted, to enable - UTF-8, use the status-utf8 option. - - status-left-attr attributes - Set the attribute of the left part of the status line. - - status-left-bg colour - Set the background colour of the left part of the status - line. - - status-left-fg colour - Set the foreground colour of the left part of the status - line. - - status-left-length length - Set the maximum length of the left component of the sta- - tus bar. The default is 10. - - status-right string - Display string to the right of the status bar. By - default, the current window title in double quotes, the - date and the time are shown. As with status-left, string - will be passed to strftime(3), character pairs are - replaced, and UTF-8 is dependent on the status-utf8 - option. - - status-right-attr attributes - Set the attribute of the right part of the status line. - - status-right-bg colour - Set the background colour of the right part of the status - line. - - status-right-fg colour - Set the foreground colour of the right part of the status - line. - - status-right-length length - Set the maximum length of the right component of the sta- - tus bar. The default is 40. - - status-utf8 [on | off] - Instruct tmux to treat top-bit-set characters in the - status-left and status-right strings as UTF-8; notably, - this is important for wide characters. This option - defaults to off. - - terminal-overrides string - Contains a list of entries which override terminal - descriptions read using terminfo(5). string is a comma- - separated list of items each a colon-separated string - made up of a terminal type pattern (matched using - fnmatch(3)) and a set of name=value entries. - - For example, to set the 'clear' terminfo(5) entry to - '\e[H\e[2J' for all terminal types and the 'dch1' entry - to '\e[P' for the 'rxvt' terminal type, the option could - be set to the string: - - "*:clear=\e[H\e[2J,rxvt:dch1=\e[P" - - The terminal entry value is passed through strunvis(3) - before interpretation. The default value forcibly cor- - rects the 'colors' entry for terminals which support 88 - or 256 colours: - - "*88col*:colors=88,*256col*:colors=256" - - update-environment variables - Set a space-separated string containing a list of envi- - ronment variables to be copied into the session environ- - ment when a new session is created or an existing session - is attached. Any variables that do not exist in the - source environment are set to be removed from the session - environment (as if -r was given to the set-environment - command). The default is "DISPLAY SSH_ASKPASS - SSH_AUTH_SOCK SSH_AGENT_PID SSH_CONNECTION WINDOWID XAU- - THORITY". - - visual-activity [on | off] - If on, display a status line message when activity occurs - in a window for which the monitor-activity window option - is enabled. - - visual-bell [on | off] - If this option is on, a message is shown on a bell - instead of it being passed through to the terminal (which - normally makes a sound). Also see the bell-action - option. - - visual-content [on | off] - Like visual-activity, display a message when content is - present in a window for which the monitor-content window - option is enabled. - - visual-silence [on | off] - If monitor-silence is enabled, prints a message after the - interval has expired on a given window. - - set-window-option [-agu] [-t target-window] option value - (alias: setw) - Set a window option. The -a, -g and -u flags work similarly to - the set-option command. - - Supported window options are: - - aggressive-resize [on | off] - Aggressively resize the chosen window. This means that - tmux will resize the window to the size of the smallest - session for which it is the current window, rather than - the smallest session to which it is attached. The window - may resize when the current window is changed on another - sessions; this option is good for full-screen programs - which support SIGWINCH and poor for interactive programs - such as shells. - - alternate-screen [on | off] - This option configures whether programs running inside - tmux may use the terminal alternate screen feature, which - allows the smcup and rmcup terminfo(5) capabilities to be - issued to preserve the existing window content on start - and restore it on exit. - - automatic-rename [on | off] - Control automatic window renaming. When this setting is - enabled, tmux will attempt - on supported platforms - to - rename the window to reflect the command currently run- - ning in it. This flag is automatically disabled for an - individual window when a name is specified at creation - with new-window or new-session, or later with - rename-window. It may be switched off globally with: - - set-window-option -g automatic-rename off - - clock-mode-colour colour - Set clock colour. - - clock-mode-style [12 | 24] - Set clock hour format. - - force-height height - force-width width - Prevent tmux from resizing a window to greater than width - or height. A value of zero restores the default unlim- - ited setting. - - main-pane-height height - main-pane-width width - Set the width or height of the main (left or top) pane in - the main-horizontal or main-vertical layouts. - - mode-attr attributes - Set window modes attributes. - - mode-bg colour - Set window modes background colour. - - mode-fg colour - Set window modes foreground colour. - - mode-keys [vi | emacs] - Use vi or emacs-style key bindings in copy and choice - modes. As with the status-keys option, the default is - emacs, unless VISUAL or EDITOR contains 'vi'. - - mode-mouse [on | off] - Mouse state in modes. If on, the mouse may be used to - copy a selection by dragging in copy mode, or to select - an option in choice mode. - - monitor-activity [on | off] - Monitor for activity in the window. Windows with activ- - ity are highlighted in the status line. - - monitor-content match-string - Monitor content in the window. When fnmatch(3) pattern - match-string appears in the window, it is highlighted in - the status line. - - monitor-silence [interval] - Monitor for silence (no activity) in the window within - interval seconds. Windows that have been silent for the - interval are highlighted in the status line. An interval - of zero disables the monitoring. - - other-pane-height height - Set the height of the other panes (not the main pane) in - the main-horizontal layout. If this option is set to 0 - (the default), it will have no effect. If both the - main-pane-height and other-pane-height options are set, - the main pane will grow taller to make the other panes - the specified height, but will never shrink to do so. - - other-pane-width width - Like other-pane-height, but set the width of other panes - in the main-vertical layout. - - remain-on-exit [on | off] - A window with this flag set is not destroyed when the - program running in it exits. The window may be reacti- - vated with the respawn-window command. - - synchronize-panes [on | off] - Duplicate input to any pane to all other panes in the - same window (only for panes that are not in any special - mode). - - utf8 [on | off] - Instructs tmux to expect UTF-8 sequences to appear in - this window. - - window-status-attr attributes - Set status line attributes for a single window. - - window-status-bg colour - Set status line background colour for a single window. - - window-status-fg colour - Set status line foreground colour for a single window. - - window-status-format string - Set the format in which the window is displayed in the - status line window list. See the status-left option for - details of special character sequences available. The - default is '#I:#W#F'. - - window-status-alert-attr attributes - Set status line attributes for windows which have an - alert (bell, activity or content). - - window-status-alert-bg colour - Set status line background colour for windows with an - alert. - - window-status-alert-fg colour - Set status line foreground colour for windows with an - alert. - - window-status-current-attr attributes - Set status line attributes for the currently active win- - dow. - - window-status-current-bg colour - Set status line background colour for the currently - active window. - - window-status-current-fg colour - Set status line foreground colour for the currently - active window. - - window-status-current-format string - Like window-status-format, but is the format used when - the window is the current window. - - word-separators string - Sets the window's conception of what characters are con- - sidered word separators, for the purposes of the next and - previous word commands in copy mode. The default is - ' -_@'. - - xterm-keys [on | off] - If this option is set, tmux will generate xterm(1) -style - function key sequences; these have a number included to - indicate modifiers such as Shift, Alt or Ctrl. The - default is off. - - show-options [-gsw] [-t target-session | target-window] - (alias: show) - Show the window options with -w (equivalent to - show-window-options), the server options with -s, otherwise the - session options for target session. Global session or window - options are listed if -g is used. - - show-window-options [-g] [-t target-window] - (alias: showw) - List the window options for target-window, or the global window - options if -g is used. - -ENVIRONMENT - When the server is started, tmux copies the environment into the global - environment; in addition, each session has a session environment. When a - window is created, the session and global environments are merged. If a - variable exists in both, the value from the session environment is used. - The result is the initial environment passed to the new process. - - The update-environment session option may be used to update the session - environment from the client when a new session is created or an old reat- - tached. tmux also initialises the TMUX variable with some internal - information to allow commands to be executed from inside, and the TERM - variable with the correct terminal setting of 'screen'. - - Commands to alter and view the environment are: - - set-environment [-gru] [-t target-session] name [value] - (alias: setenv) - Set or unset an environment variable. If -g is used, the change - is made in the global environment; otherwise, it is applied to - the session environment for target-session. The -u flag unsets a - variable. -r indicates the variable is to be removed from the - environment before starting a new process. - - show-environment [-g] [-t target-session] - (alias: showenv) - Display the environment for target-session or the global environ- - ment with -g. Variables removed from the environment are pre- - fixed with '-'. - -STATUS LINE - tmux includes an optional status line which is displayed in the bottom - line of each terminal. By default, the status line is enabled (it may be - disabled with the status session option) and contains, from left-to- - right: the name of the current session in square brackets; the window - list; the current window title in double quotes; and the time and date. - - The status line is made of three parts: configurable left and right sec- - tions (which may contain dynamic content such as the time or output from - a shell command, see the status-left, status-left-length, status-right, - and status-right-length options below), and a central window list. By - default, the window list shows the index, name and (if any) flag of the - windows present in the current session in ascending numerical order. It - may be customised with the window-status-format and - window-status-current-format options. The flag is one of the following - symbols appended to the window name: - - Symbol Meaning - * Denotes the current window. - - Marks the last window (previously selected). - # Window is monitored and activity has been detected. - ! A bell has occurred in the window. - + Window is monitored for content and it has appeared. - ~ The window has been silent for the monitor-silence - interval. - - The # symbol relates to the monitor-activity and + to the monitor-content - window options. The window name is printed in inverted colours if an - alert (bell, activity or content) is present. - - The colour and attributes of the status line may be configured, the - entire status line using the status-attr, status-fg and status-bg session - options and individual windows using the window-status-attr, - window-status-fg and window-status-bg window options. - - The status line is automatically refreshed at interval if it has changed, - the interval may be controlled with the status-interval session option. - - Commands related to the status line are as follows: - - command-prompt [-p prompts] [-t target-client] [template] - Open the command prompt in a client. This may be used from - inside tmux to execute commands interactively. If template is - specified, it is used as the command. If -p is given, prompts is - a comma-separated list of prompts which are displayed in order; - otherwise a single prompt is displayed, constructed from template - if it is present, or ':' if not. Before the command is executed, - the first occurrence of the string '%%' and all occurrences of - '%1' are replaced by the response to the first prompt, the second - '%%' and all '%2' are replaced with the response to the second - prompt, and so on for further prompts. Up to nine prompt - responses may be replaced ('%1' to '%9'). - - confirm-before [-t target-client] command - (alias: confirm) - Ask for confirmation before executing command. This command - works only from inside tmux. - - display-message [-p] [-t target-client] [message] - (alias: display) - Display a message. If -p is given, the output is printed to std- - out, otherwise it is displayed in the target-client status line. - The format of message is as for status-left, with the exception - that #() are not handled. - -BUFFERS - tmux maintains a stack of paste buffers for each session. Up to the - value of the buffer-limit option are kept; when a new buffer is added, - the buffer at the bottom of the stack is removed. Buffers may be added - using copy-mode or the set-buffer command, and pasted into a window using - the paste-buffer command. - - A configurable history buffer is also maintained for each window. By - default, up to 2000 lines are kept; this can be altered with the - history-limit option (see the set-option command above). - - The buffer commands are as follows: - - choose-buffer [-t target-window] [template] - Put a window into buffer choice mode, where a buffer may be cho- - sen interactively from a list. After a buffer is selected, '%%' - is replaced by the buffer index in template and the result exe- - cuted as a command. If template is not given, "paste-buffer -b - '%%'" is used. This command works only from inside tmux. - - clear-history [-t target-pane] - (alias: clearhist) - Remove and free the history for the specified pane. - - copy-buffer [-a src-index] [-b dst-index] [-s src-session] [-t - dst-session] - (alias: copyb) - Copy a session paste buffer to another session. If no sessions - are specified, the current one is used instead. - - delete-buffer [-b buffer-index] [-t target-session] - (alias: deleteb) - Delete the buffer at buffer-index, or the top buffer if not spec- - ified. - - list-buffers [-t target-session] - (alias: lsb) - List the buffers in the given session. - - load-buffer [-b buffer-index] [-t target-session] path - (alias: loadb) - Load the contents of the specified paste buffer from path. - - paste-buffer [-dr] [-b buffer-index] [-s separator] [-t target-pane] - (alias: pasteb) - Insert the contents of a paste buffer into the specified pane. - If not specified, paste into the current one. With -d, also - delete the paste buffer from the stack. When output, any line- - feed (LF) characters in the paste buffer are replaced with a sep- - arator, by default carriage return (CR). A custom separator may - be specified using the -s flag. The -r flag means to do no - replacement (equivalent to a separator of LF). - - save-buffer [-a] [-b buffer-index] [-t target-session] path - (alias: saveb) - Save the contents of the specified paste buffer to path. The -a - option appends to rather than overwriting the file. - - set-buffer [-b buffer-index] [-t target-session] data - (alias: setb) - Set the contents of the specified buffer to data. - - show-buffer [-b buffer-index] [-t target-session] - (alias: showb) - Display the contents of the specified buffer. - -MISCELLANEOUS - Miscellaneous commands are as follows: - - clock-mode [-t target-pane] - Display a large clock. - - if-shell shell-command command - (alias: if) - Execute command if shell-command returns success. - - lock-server - (alias: lock) - Lock each client individually by running the command specified by - the lock-command option. - - run-shell shell-command - (alias: run) - Execute shell-command in the background without creating a win- - dow. After it finishes, any output to stdout is displayed in - copy mode. If the command doesn't return success, the exit sta- - tus is also displayed. - - server-info - (alias: info) - Show server information and terminal details. - -FILES - ~/.tmux.conf Default tmux configuration file. - /etc/tmux.conf System-wide configuration file. - -EXAMPLES - To create a new tmux session running vi(1): - - $ tmux new-session vi - - Most commands have a shorter form, known as an alias. For new-session, - this is new: - - $ tmux new vi - - Alternatively, the shortest unambiguous form of a command is accepted. - If there are several options, they are listed: - - $ tmux n - ambiguous command: n, could be: new-session, new-window, next-window - - Within an active session, a new window may be created by typing 'C-b c' - (Ctrl followed by the 'b' key followed by the 'c' key). - - Windows may be navigated with: 'C-b 0' (to select window 0), 'C-b 1' (to - select window 1), and so on; 'C-b n' to select the next window; and 'C-b - p' to select the previous window. - - A session may be detached using 'C-b d' (or by an external event such as - ssh(1) disconnection) and reattached with: - - $ tmux attach-session - - Typing 'C-b ?' lists the current key bindings in the current window; up - and down may be used to navigate the list or 'q' to exit from it. - - Commands to be run when the tmux server is started may be placed in the - ~/.tmux.conf configuration file. Common examples include: - - Changing the default prefix key: - - set-option -g prefix C-a - unbind-key C-b - bind-key C-a send-prefix - - Turning the status line off, or changing its colour: - - set-option -g status off - set-option -g status-bg blue - - Setting other options, such as the default command, or locking after 30 - minutes of inactivity: - - set-option -g default-command "exec /bin/ksh" - set-option -g lock-after-time 1800 - - Creating new key bindings: - - bind-key b set-option status - bind-key / command-prompt "split-window 'exec man %%'" - bind-key S command-prompt "new-window -n %1 'ssh %1'" - -SEE ALSO - pty(4) - -AUTHORS - Nicholas Marriott - -BSD October 19, 2013 BSD diff --git a/manual/1.5 b/manual/1.5 deleted file mode 100644 index 6e96fa9ae9c..00000000000 --- a/manual/1.5 +++ /dev/null @@ -1,1741 +0,0 @@ -TMUX(1) BSD General Commands Manual TMUX(1) - -NAME - tmux -- terminal multiplexer - -SYNOPSIS - tmux [-28lquvV] [-c shell-command] [-f file] [-L socket-name] - [-S socket-path] [command [flags]] - -DESCRIPTION - tmux is a terminal multiplexer: it enables a number of terminals to be - created, accessed, and controlled from a single screen. tmux may be - detached from a screen and continue running in the background, then later - reattached. - - When tmux is started it creates a new session with a single window and - displays it on screen. A status line at the bottom of the screen shows - information on the current session and is used to enter interactive com- - mands. - - A session is a single collection of pseudo terminals under the management - of tmux. Each session has one or more windows linked to it. A window - occupies the entire screen and may be split into rectangular panes, each - of which is a separate pseudo terminal (the pty(4) manual page documents - the technical details of pseudo terminals). Any number of tmux instances - may connect to the same session, and any number of windows may be present - in the same session. Once all sessions are killed, tmux exits. - - Each session is persistent and will survive accidental disconnection - (such as ssh(1) connection timeout) or intentional detaching (with the - 'C-b d' key strokes). tmux may be reattached using: - - $ tmux attach - - In tmux, a session is displayed on screen by a client and all sessions - are managed by a single server. The server and each client are separate - processes which communicate through a socket in /tmp. - - The options are as follows: - - -2 Force tmux to assume the terminal supports 256 colours. - - -8 Like -2, but indicates that the terminal supports 88 - colours. - - -c shell-command - Execute shell-command using the default shell. If neces- - sary, the tmux server will be started to retrieve the - default-shell option. This option is for compatibility - with sh(1) when tmux is used as a login shell. - - -f file Specify an alternative configuration file. By default, - tmux loads the system configuration file from - /etc/tmux.conf, if present, then looks for a user configu- - ration file at ~/.tmux.conf. The configuration file is a - set of tmux commands which are executed in sequence when - the server is first started. - - If a command in the configuration file fails, tmux will - report an error and exit without executing further com- - mands. - - -L socket-name - tmux stores the server socket in a directory under /tmp (or - TMPDIR if set); the default socket is named default. This - option allows a different socket name to be specified, - allowing several independent tmux servers to be run. - Unlike -S a full path is not necessary: the sockets are all - created in the same directory. - - If the socket is accidentally removed, the SIGUSR1 signal - may be sent to the tmux server process to recreate it. - - -l Behave as a login shell. This flag currently has no effect - and is for compatibility with other shells when using tmux - as a login shell. - - -q Set the quiet server option to prevent the server sending - various informational messages. - - -S socket-path - Specify a full alternative path to the server socket. If - -S is specified, the default socket directory is not used - and any -L flag is ignored. - - -u tmux attempts to guess if the terminal is likely to support - UTF-8 by checking the first of the LC_ALL, LC_CTYPE and - LANG environment variables to be set for the string - "UTF-8". This is not always correct: the -u flag explic- - itly informs tmux that UTF-8 is supported. - - If the server is started from a client passed -u or where - UTF-8 is detected, the utf8 and status-utf8 options are - enabled in the global window and session options respec- - tively. - - -v Request verbose logging. This option may be specified mul- - tiple times for increasing verbosity. Log messages will be - saved into tmux-client-PID.log and tmux-server-PID.log - files in the current directory, where PID is the PID of the - server or client process. - - -V Report the tmux version. - - command [flags] - This specifies one of a set of commands used to control - tmux, as described in the following sections. If no com- - mands are specified, the new-session command is assumed. - -KEY BINDINGS - tmux may be controlled from an attached client by using a key combination - of a prefix key, 'C-b' (Ctrl-b) by default, followed by a command key. - - The default command key bindings are: - - C-b Send the prefix key (C-b) through to the application. - C-o Rotate the panes in the current window forwards. - C-z Suspend the tmux client. - ! Break the current pane out of the window. - " Split the current pane into two, top and bottom. - # List all paste buffers. - $ Rename the current session. - % Split the current pane into two, left and right. - & Kill the current window. - ' Prompt for a window index to select. - , Rename the current window. - - Delete the most recently copied buffer of text. - . Prompt for an index to move the current window. - 0 to 9 Select windows 0 to 9. - : Enter the tmux command prompt. - ; Move to the previously active pane. - = Choose which buffer to paste interactively from a list. - ? List all key bindings. - D Choose a client to detach. - [ Enter copy mode to copy text or view the history. - ] Paste the most recently copied buffer of text. - c Create a new window. - d Detach the current client. - f Prompt to search for text in open windows. - i Display some information about the current window. - l Move to the previously selected window. - n Change to the next window. - o Select the next pane in the current window. - p Change to the previous window. - q Briefly display pane indexes. - r Force redraw of the attached client. - s Select a new session for the attached client interac- - tively. - L Switch the attached client back to the last session. - t Show the time. - w Choose the current window interactively. - x Kill the current pane. - { Swap the current pane with the previous pane. - } Swap the current pane with the next pane. - ~ Show previous messages from tmux, if any. - Page Up Enter copy mode and scroll one page up. - Up, Down - Left, Right - Change to the pane above, below, to the left, or to the - right of the current pane. - M-1 to M-5 Arrange panes in one of the five preset layouts: even- - horizontal, even-vertical, main-horizontal, main-verti- - cal, or tiled. - M-n Move to the next window with a bell or activity marker. - M-o Rotate the panes in the current window backwards. - M-p Move to the previous window with a bell or activity - marker. - C-Up, C-Down - C-Left, C-Right - Resize the current pane in steps of one cell. - M-Up, M-Down - M-Left, M-Right - Resize the current pane in steps of five cells. - - Key bindings may be changed with the bind-key and unbind-key commands. - -COMMANDS - This section contains a list of the commands supported by tmux. Most - commands accept the optional -t argument with one of target-client, - target-session target-window, or target-pane. These specify the client, - session, window or pane which a command should affect. target-client is - the name of the pty(4) file to which the client is connected, for example - either of /dev/ttyp1 or ttyp1 for the client attached to /dev/ttyp1. If - no client is specified, the current client is chosen, if possible, or an - error is reported. Clients may be listed with the list-clients command. - - target-session is either the name of a session (as listed by the - list-sessions command) or the name of a client with the same syntax as - target-client, in which case the session attached to the client is used. - When looking for the session name, tmux initially searches for an exact - match; if none is found, the session names are checked for any for which - target-session is a prefix or for which it matches as an fnmatch(3) pat- - tern. If a single match is found, it is used as the target session; mul- - tiple matches produce an error. If a session is omitted, the current - session is used if available; if no current session is available, the - most recently used is chosen. - - target-window specifies a window in the form session:window. session - follows the same rules as for target-session, and window is looked for in - order: as a window index, for example mysession:1; as an exact window - name, such as mysession:mywindow; then as an fnmatch(3) pattern or the - start of a window name, such as mysession:mywin* or mysession:mywin. An - empty window name specifies the next unused index if appropriate (for - example the new-window and link-window commands) otherwise the current - window in session is chosen. The special character '!' uses the last - (previously current) window, or '+' and '-' are the next window or the - previous window by number. When the argument does not contain a colon, - tmux first attempts to parse it as window; if that fails, an attempt is - made to match a session. - - target-pane takes a similar form to target-window but with the optional - addition of a period followed by a pane index, for example: myses- - sion:mywindow.1. If the pane index is omitted, the currently active pane - in the specified window is used. If neither a colon nor period appears, - tmux first attempts to use the argument as a pane index; if that fails, - it is looked up as for target-window. A '+' or '-' indicate the next or - previous pane index, respectively. One of the strings top, bottom, left, - right, top-left, top-right, bottom-left or bottom-right may be used - instead of a pane index. - - The special characters '+' and '-' may be followed by an offset, for - example: - - select-window -t:+2 - - When dealing with a session that doesn't contain sequential window - indexes, they will be correctly skipped. - - tmux also gives each pane created in a server an identifier consisting of - a '%' and a number, starting from zero. A pane's identifier is unique - for the life of the tmux server and is passed to the child process of the - pane in the TMUX_PANE environment variable. It may be used alone to tar- - get a pane or the window containing it. - - shell-command arguments are sh(1) commands. These must be passed as a - single item, which typically means quoting them, for example: - - new-window 'vi /etc/passwd' - - command [arguments] refers to a tmux command, passed with the command and - arguments separately, for example: - - bind-key F1 set-window-option force-width 81 - - Or if using sh(1): - - $ tmux bind-key F1 set-window-option force-width 81 - - Multiple commands may be specified together as part of a command - sequence. Each command should be separated by spaces and a semicolon; - commands are executed sequentially from left to right. A literal semi- - colon may be included by escaping it with a backslash (for example, when - specifying a command sequence to bind-key). - - Example tmux commands include: - - refresh-client -t/dev/ttyp2 - - rename-session -tfirst newname - - set-window-option -t:0 monitor-activity on - - new-window ; split-window -d - - Or from sh(1): - - $ tmux kill-window -t :1 - - $ tmux new-window \; split-window -d - - $ tmux new-session -d 'vi /etc/passwd' \; split-window -d \; attach - -CLIENTS AND SESSIONS - The tmux server manages clients, sessions, windows and panes. Clients - are attached to sessions to interact with them, either when they are cre- - ated with the new-session command, or later with the attach-session com- - mand. Each session has one or more windows linked into it. Windows may - be linked to multiple sessions and are made up of one or more panes, each - of which contains a pseudo terminal. Commands for creating, linking and - otherwise manipulating windows are covered in the WINDOWS AND PANES sec- - tion. - - The following commands are available to manage clients and sessions: - - attach-session [-dr] [-t target-session] - (alias: attach) - If run from outside tmux, create a new client in the current ter- - minal and attach it to target-session. If used from inside, - switch the current client. If -d is specified, any other clients - attached to the session are detached. -r signifies the client is - read-only (only keys bound to the detach-client command have any - effect) - - If no server is started, attach-session will attempt to start it; - this will fail unless sessions are created in the configuration - file. - - The target-session rules for attach-session are slightly - adjusted: if tmux needs to select the most recently used session, - it will prefer the most recently used unattached session. - - detach-client [-P] [-s target-session] [-t target-client] - (alias: detach) - Detach the current client if bound to a key, the client specified - with -t, or all clients currently attached to to the session - specified by -s. If -P is given, send SIGHUP to the parent - process of the client, typically causing it to exit. - - has-session [-t target-session] - (alias: has) - Report an error and exit with 1 if the specified session does not - exist. If it does exist, exit with 0. - - kill-server - Kill the tmux server and clients and destroy all sessions. - - kill-session [-t target-session] - Destroy the given session, closing any windows linked to it and - no other sessions, and detaching all clients attached to it. - - list-clients [-t target-session] - (alias: lsc) - List all clients attached to the server. If target-session is - specified, list only clients connected to that session. - - list-commands - (alias: lscm) - List the syntax of all commands supported by tmux. - - list-sessions - (alias: ls) - List all sessions managed by the server. - - lock-client [-t target-client] - (alias: lockc) - Lock target-client, see the lock-server command. - - lock-session [-t target-session] - (alias: locks) - Lock all clients attached to target-session. - - new-session [-d] [-n window-name] [-s session-name] [-t target-session] - [-x width] [-y height] [shell-command] - (alias: new) - Create a new session with name session-name. - - The new session is attached to the current terminal unless -d is - given. window-name and shell-command are the name of and shell - command to execute in the initial window. If -d is used, -x and - -y specify the size of the initial window (80 by 24 if not - given). - - If run from a terminal, any termios(4) special characters are - saved and used for new windows in the new session. - - If -t is given, the new session is grouped with target-session. - This means they share the same set of windows - all windows from - target-session are linked to the new session and any subsequent - new windows or windows being closed are applied to both sessions. - The current and previous window and any session options remain - independent and either session may be killed without affecting - the other. Giving -n or shell-command are invalid if -t is used. - - refresh-client [-t target-client] - (alias: refresh) - Refresh the current client if bound to a key, or a single client - if one is given with -t. - - rename-session [-t target-session] new-name - (alias: rename) - Rename the session to new-name. - - show-messages [-t target-client] - (alias: showmsgs) - Any messages displayed on the status line are saved in a per- - client message log, up to a maximum of the limit set by the - message-limit session option for the session attached to that - client. This command displays the log for target-client. - - source-file path - (alias: source) - Execute commands from path. - - start-server - (alias: start) - Start the tmux server, if not already running, without creating - any sessions. - - suspend-client [-t target-client] - (alias: suspendc) - Suspend a client by sending SIGTSTP (tty stop). - - switch-client [-lnp] [-c target-client] [-t target-session] - (alias: switchc) - Switch the current session for client target-client to - target-session. If -l, -n or -p is used, the client is moved to - the last, next or previous session respectively. - -WINDOWS AND PANES - A tmux window may be in one of several modes. The default permits direct - access to the terminal attached to the window. The other is copy mode, - which permits a section of a window or its history to be copied to a - paste buffer for later insertion into another window. This mode is - entered with the copy-mode command, bound to '[' by default. It is also - entered when a command that produces output, such as list-keys, is exe- - cuted from a key binding. - - The keys available depend on whether emacs or vi mode is selected (see - the mode-keys option). The following keys are supported as appropriate - for the mode: - - Function vi emacs - Back to indentation ^ M-m - Bottom of history G M-< - Clear selection Escape C-g - Copy selection Enter M-w - Cursor down j Down - Cursor left h Left - Cursor right l Right - Cursor to bottom line L - Cursor to middle line M M-r - Cursor to top line H M-R - Cursor up k Up - Delete entire line d C-u - Delete/Copy to end of line D C-k - End of line $ C-e - Go to line : g - Half page down C-d M-Down - Half page up C-u M-Up - Jump forward f f - Jump backward F F - Jump again ; ; - Jump again in reverse , , - Next page C-f Page down - Next space W - Next space, end of word E - Next word w - Next word end e M-f - Paste buffer p C-y - Previous page C-b Page up - Previous word b M-b - Previous space B - Quit mode q Escape - Rectangle toggle v R - Scroll down C-Down or C-e C-Down - Scroll up C-Up or C-y C-Up - Search again n n - Search again in reverse N N - Search backward ? C-r - Search forward / C-s - Start of line 0 C-a - Start selection Space C-Space - Top of history g M-> - Transpose chars C-t - - The next and previous word keys use space and the '-', '_' and '@' char- - acters as word delimiters by default, but this can be adjusted by setting - the word-separators window option. Next word moves to the start of the - next word, next word end to the end of the next word and previous word to - the start of the previous word. The three next and previous space keys - work similarly but use a space alone as the word separator. - - The jump commands enable quick movement within a line. For instance, - typing 'f' followed by '/' will move the cursor to the next '/' character - on the current line. A ';' will then jump to the next occurrence. - - Commands in copy mode may be prefaced by an optional repeat count. With - vi key bindings, a prefix is entered using the number keys; with emacs, - the Alt (meta) key and a number begins prefix entry. For example, to - move the cursor forward by ten words, use 'M-1 0 M-f' in emacs mode, and - '10w' in vi. - - Mode key bindings are defined in a set of named tables: vi-edit and - emacs-edit for keys used when line editing at the command prompt; - vi-choice and emacs-choice for keys used when choosing from lists (such - as produced by the choose-window command); and vi-copy and emacs-copy - used in copy mode. The tables may be viewed with the list-keys command - and keys modified or removed with bind-key and unbind-key. - - The paste buffer key pastes the first line from the top paste buffer on - the stack. - - The synopsis for the copy-mode command is: - - copy-mode [-u] [-t target-pane] - Enter copy mode. The -u option scrolls one page up. - - Each window displayed by tmux may be split into one or more panes; each - pane takes up a certain area of the display and is a separate terminal. - A window may be split into panes using the split-window command. Windows - may be split horizontally (with the -h flag) or vertically. Panes may be - resized with the resize-pane command (bound to 'C-up', 'C-down' 'C-left' - and 'C-right' by default), the current pane may be changed with the - select-pane command and the rotate-window and swap-pane commands may be - used to swap panes without changing their position. Panes are numbered - beginning from zero in the order they are created. - - A number of preset layouts are available. These may be selected with the - select-layout command or cycled with next-layout (bound to 'Space' by - default); once a layout is chosen, panes within it may be moved and - resized as normal. - - The following layouts are supported: - - even-horizontal - Panes are spread out evenly from left to right across the window. - - even-vertical - Panes are spread evenly from top to bottom. - - main-horizontal - A large (main) pane is shown at the top of the window and the - remaining panes are spread from left to right in the leftover - space at the bottom. Use the main-pane-height window option to - specify the height of the top pane. - - main-vertical - Similar to main-horizontal but the large pane is placed on the - left and the others spread from top to bottom along the right. - See the main-pane-width window option. - - tiled Panes are spread out as evenly as possible over the window in - both rows and columns. - - In addition, select-layout may be used to apply a previously used layout - - the list-windows command displays the layout of each window in a form - suitable for use with select-layout. For example: - - $ tmux list-windows - 0: ksh [159x48] - layout: bb62,159x48,0,0{79x48,0,0,79x48,80,0} - $ tmux select-layout bb62,159x48,0,0{79x48,0,0,79x48,80,0} - - tmux automatically adjusts the size of the layout for the current window - size. Note that a layout cannot be applied to a window with more panes - than that from which the layout was originally defined. - - Commands related to windows and panes are as follows: - - break-pane [-d] [-t target-pane] - (alias: breakp) - Break target-pane off from its containing window to make it the - only pane in a new window. If -d is given, the new window does - not become the current window. - - capture-pane [-b buffer-index] [-E end-line] [-S start-line] [-t - target-pane] - (alias: capturep) - Capture the contents of a pane to the specified buffer, or a new - buffer if none is specified. - - -S and -E specify the starting and ending line numbers, zero is - the first line of the visible pane and negative numbers are lines - in the history. The default is to capture only the visible con- - tents of the pane. - - choose-client [-t target-window] [template] - Put a window into client choice mode, allowing a client to be - selected interactively from a list. After a client is chosen, - '%%' is replaced by the client pty(4) path in template and the - result executed as a command. If template is not given, "detach- - client -t '%%'" is used. This command works only from inside - tmux. - - choose-session [-t target-window] [template] - Put a window into session choice mode, where a session may be - selected interactively from a list. When one is chosen, '%%' is - replaced by the session name in template and the result executed - as a command. If template is not given, "switch-client -t '%%'" - is used. This command works only from inside tmux. - - choose-window [-t target-window] [template] - Put a window into window choice mode, where a window may be cho- - sen interactively from a list. After a window is selected, '%%' - is replaced by the session name and window index in template and - the result executed as a command. If template is not given, - "select-window -t '%%'" is used. This command works only from - inside tmux. - - display-panes [-t target-client] - (alias: displayp) - Display a visible indicator of each pane shown by target-client. - See the display-panes-time, display-panes-colour, and - display-panes-active-colour session options. While the indicator - is on screen, a pane may be selected with the '0' to '9' keys. - - find-window [-t target-window] match-string - (alias: findw) - Search for the fnmatch(3) pattern match-string in window names, - titles, and visible content (but not history). If only one win- - dow is matched, it'll be automatically selected, otherwise a - choice list is shown. This command only works from inside tmux. - - join-pane [-dhv] [-l size | -p percentage] [-s src-pane] [-t dst-pane] - (alias: joinp) - Like split-window, but instead of splitting dst-pane and creating - a new pane, split it and move src-pane into the space. This can - be used to reverse break-pane. - - kill-pane [-a] [-t target-pane] - (alias: killp) - Destroy the given pane. If no panes remain in the containing - window, it is also destroyed. The -a option kills all but the - pane given with -t. - - kill-window [-t target-window] - (alias: killw) - Kill the current window or the window at target-window, removing - it from any sessions to which it is linked. - - last-pane [-t target-window] - (alias: lastp) - Select the last (previously selected) pane. - - last-window [-t target-session] - (alias: last) - Select the last (previously selected) window. If no - target-session is specified, select the last window of the cur- - rent session. - - link-window [-dk] [-s src-window] [-t dst-window] - (alias: linkw) - Link the window at src-window to the specified dst-window. If - dst-window is specified and no such window exists, the src-window - is linked there. If -k is given and dst-window exists, it is - killed, otherwise an error is generated. If -d is given, the - newly linked window is not selected. - - list-panes [-as] [-t target] - (alias: lsp) - If -a is given, target is ignored and all panes on the server are - listed. If -s is given, target is a session (or the current ses- - sion). If neither is given, target is a window (or the current - window). - - list-windows [-a] [-t target-session] - (alias: lsw) - If -a is given, list all windows on the server. Otherwise, list - windows in the current session or in target-session. - - move-window [-dk] [-s src-window] [-t dst-window] - (alias: movew) - This is similar to link-window, except the window at src-window - is moved to dst-window. - - new-window [-adkP] [-n window-name] [-t target-window] [shell-command] - (alias: neww) - Create a new window. With -a, the new window is inserted at the - next index up from the specified target-window, moving windows up - if necessary, otherwise target-window is the new window location. - - If -d is given, the session does not make the new window the cur- - rent window. target-window represents the window to be created; - if the target already exists an error is shown, unless the -k - flag is used, in which case it is destroyed. shell-command is - the command to execute. If shell-command is not specified, the - value of the default-command option is used. - - When the shell command completes, the window closes. See the - remain-on-exit option to change this behaviour. - - The TERM environment variable must be set to ``screen'' for all - programs running inside tmux. New windows will automatically - have ``TERM=screen'' added to their environment, but care must be - taken not to reset this in shell start-up files. - - The -P option prints the location of the new window after it has - been created. - - next-layout [-t target-window] - (alias: nextl) - Move a window to the next layout and rearrange the panes to fit. - - next-window [-a] [-t target-session] - (alias: next) - Move to the next window in the session. If -a is used, move to - the next window with a bell, activity or content alert. - - pipe-pane [-o] [-t target-pane] [shell-command] - (alias: pipep) - Pipe any output sent by the program in target-pane to a shell - command. A pane may only be piped to one command at a time, any - existing pipe is closed before shell-command is executed. The - shell-command string may contain the special character sequences - supported by the status-left option. If no shell-command is - given, the current pipe (if any) is closed. - - The -o option only opens a new pipe if no previous pipe exists, - allowing a pipe to be toggled with a single key, for example: - - bind-key C-p pipe-pane -o 'cat >>~/output.#I-#P' - - previous-layout [-t target-window] - (alias: prevl) - Move to the previous layout in the session. - - previous-window [-a] [-t target-session] - (alias: prev) - Move to the previous window in the session. With -a, move to the - previous window with a bell, activity or content alert. - - rename-window [-t target-window] new-name - (alias: renamew) - Rename the current window, or the window at target-window if - specified, to new-name. - - resize-pane [-DLRU] [-t target-pane] [adjustment] - (alias: resizep) - Resize a pane, upward with -U (the default), downward with -D, to - the left with -L and to the right with -R. The adjustment is - given in lines or cells (the default is 1). - - respawn-pane [-k] [-t target-pane] [shell-command] - (alias: respawnp) - Reactivate a pane in which the command has exited (see the - remain-on-exit window option). If shell-command is not given, - the command used when the pane was created is executed. The pane - must be already inactive, unless -k is given, in which case any - existing command is killed. - - respawn-window [-k] [-t target-window] [shell-command] - (alias: respawnw) - Reactivate a window in which the command has exited (see the - remain-on-exit window option). If shell-command is not given, - the command used when the window was created is executed. The - window must be already inactive, unless -k is given, in which - case any existing command is killed. - - rotate-window [-DU] [-t target-window] - (alias: rotatew) - Rotate the positions of the panes within a window, either upward - (numerically lower) with -U or downward (numerically higher). - - select-layout [-np] [-t target-window] [layout-name] - (alias: selectl) - Choose a specific layout for a window. If layout-name is not - given, the last preset layout used (if any) is reapplied. -n and - -p are equivalent to the next-layout and previous-layout com- - mands. - - select-pane [-lDLRU] [-t target-pane] - (alias: selectp) - Make pane target-pane the active pane in window target-window. - If one of -D, -L, -R, or -U is used, respectively the pane below, - to the left, to the right, or above the target pane is used. -l - is the same as using the last-pane command. - - select-window [-lnp] [-t target-window] - (alias: selectw) - Select the window at target-window. -l, -n and -p are equivalent - to the last-window, next-window and previous-window commands. - - split-window [-dhvP] [-l size | -p percentage] [-t target-pane] - [shell-command] - (alias: splitw) - Create a new pane by splitting target-pane: -h does a horizontal - split and -v a vertical split; if neither is specified, -v is - assumed. The -l and -p options specify the size of the new pane - in lines (for vertical split) or in cells (for horizontal split), - or as a percentage, respectively. All other options have the - same meaning as for the new-window command. - - swap-pane [-dDU] [-s src-pane] [-t dst-pane] - (alias: swapp) - Swap two panes. If -U is used and no source pane is specified - with -s, dst-pane is swapped with the previous pane (before it - numerically); -D swaps with the next pane (after it numerically). - -d instructs tmux not to change the active pane. - - swap-window [-d] [-s src-window] [-t dst-window] - (alias: swapw) - This is similar to link-window, except the source and destination - windows are swapped. It is an error if no window exists at - src-window. - - unlink-window [-k] [-t target-window] - (alias: unlinkw) - Unlink target-window. Unless -k is given, a window may be - unlinked only if it is linked to multiple sessions - windows may - not be linked to no sessions; if -k is specified and the window - is linked to only one session, it is unlinked and destroyed. - -KEY BINDINGS - tmux allows a command to be bound to most keys, with or without a prefix - key. When specifying keys, most represent themselves (for example 'A' to - 'Z'). Ctrl keys may be prefixed with 'C-' or '^', and Alt (meta) with - 'M-'. In addition, the following special key names are accepted: Up, - Down, Left, Right, BSpace, BTab, DC (Delete), End, Enter, Escape, F1 to - F20, Home, IC (Insert), NPage (Page Up), PPage (Page Down), Space, and - Tab. Note that to bind the '"' or ''' keys, quotation marks are neces- - sary, for example: - - bind-key '"' split-window - bind-key "'" new-window - - Commands related to key bindings are as follows: - - bind-key [-cnr] [-t key-table] key command [arguments] - (alias: bind) - Bind key key to command. By default (without -t) the primary key - bindings are modified (those normally activated with the prefix - key); in this case, if -n is specified, it is not necessary to - use the prefix key, command is bound to key alone. The -r flag - indicates this key may repeat, see the repeat-time option. - - If -t is present, key is bound in key-table: the binding for com- - mand mode with -c or for normal mode without. To view the - default bindings and possible commands, see the list-keys com- - mand. - - list-keys [-t key-table] - (alias: lsk) - List all key bindings. Without -t the primary key bindings - - those executed when preceded by the prefix key - are printed. - Keys bound without the prefix key (see bind-key -n) are marked - with '(no prefix)'. - - With -t, the key bindings in key-table are listed; this may be - one of: vi-edit, emacs-edit, vi-choice, emacs-choice, vi-copy or - emacs-copy. - - send-keys [-t target-pane] key ... - (alias: send) - Send a key or keys to a window. Each argument key is the name of - the key (such as 'C-a' or 'npage' ) to send; if the string is not - recognised as a key, it is sent as a series of characters. All - arguments are sent sequentially from first to last. - - send-prefix [-t target-pane] - Send the prefix key to a window as if it was pressed. If multi- - ple prefix keys are configured, only the first is sent. - - unbind-key [-acn] [-t key-table] key - (alias: unbind) - Unbind the command bound to key. Without -t the primary key - bindings are modified; in this case, if -n is specified, the com- - mand bound to key without a prefix (if any) is removed. If -a is - present, all key bindings are removed. - - If -t is present, key in key-table is unbound: the binding for - command mode with -c or for normal mode without. - -OPTIONS - The appearance and behaviour of tmux may be modified by changing the - value of various options. There are three types of option: server - options, session options and window options. - - The tmux server has a set of global options which do not apply to any - particular window or session. These are altered with the set-option -s - command, or displayed with the show-options -s command. - - In addition, each individual session may have a set of session options, - and there is a separate set of global session options. Sessions which do - not have a particular option configured inherit the value from the global - session options. Session options are set or unset with the set-option - command and may be listed with the show-options command. The available - server and session options are listed under the set-option command. - - Similarly, a set of window options is attached to each window, and there - is a set of global window options from which any unset options are inher- - ited. Window options are altered with the set-window-option command and - can be listed with the show-window-options command. All window options - are documented with the set-window-option command. - - Commands which set options are as follows: - - set-option [-agsuw] [-t target-session | target-window] option value - (alias: set) - Set a window option with -w (equivalent to the set-window-option - command), a server option with -s, otherwise a session option. - - If -g is specified, the global session or window option is set. - With -a, and if the option expects a string, value is appended to - the existing setting. The -u flag unsets an option, so a session - inherits the option from the global options. It is not possible - to unset a global option. - - Available window options are listed under set-window-option. - - Available server options are: - - buffer-limit number - Set the number of buffers; as new buffers are added to - the top of the stack, old ones are removed from the bot- - tom if necessary to maintain this maximum length. - - set-clipboard [on | off] - Attempt to set the terminal clipboard content using the - \e]52;...\007 xterm(1) escape sequences. This option is - on by default if there is an Ms entry in the terminfo(5) - description for the client terminal. Note that this fea- - ture needs to be enabled in xterm(1) by setting the - resource: - - disallowedWindowOps: 20,21,SetXprop - - Or changing this property from the xterm(1) interactive - menu when required. - - escape-time time - Set the time in milliseconds for which tmux waits after - an escape is input to determine if it is part of a func- - tion or meta key sequences. The default is 500 millisec- - onds. - - exit-unattached [on | off] - If enabled, the server will exit when there are no - attached clients. - - quiet [on | off] - Enable or disable the display of various informational - messages (see also the -q command line flag). - - Available session options are: - - base-index index - Set the base index from which an unused index should be - searched when a new window is created. The default is - zero. - - bell-action [any | none | current] - Set action on window bell. any means a bell in any win- - dow linked to a session causes a bell in the current win- - dow of that session, none means all bells are ignored and - current means only bell in windows other than the current - window are ignored. - - bell-on-alert [on | off] - If on, ring the terminal bell when an activity, content - or silence alert occurs. - - default-command shell-command - Set the command used for new windows (if not specified - when the window is created) to shell-command, which may - be any sh(1) command. The default is an empty string, - which instructs tmux to create a login shell using the - value of the default-shell option. - - default-path path - Set the default working directory for processes created - from keys, or interactively from the prompt. The default - is empty, which means to use the working directory of the - shell from which the server was started if it is avail- - able or the user's home if not. - - default-shell path - Specify the default shell. This is used as the login - shell for new windows when the default-command option is - set to empty, and must be the full path of the exe- - cutable. When started tmux tries to set a default value - from the first suitable of the SHELL environment vari- - able, the shell returned by getpwuid(3), or /bin/sh. - This option should be configured when tmux is used as a - login shell. - - default-terminal terminal - Set the default terminal for new windows created in this - session - the default value of the TERM environment vari- - able. For tmux to work correctly, this must be set to - 'screen' or a derivative of it. - - destroy-unattached [on | off] - If enabled and the session is no longer attached to any - clients, it is destroyed. - - detach-on-destroy [on | off] - If on (the default), the client is detached when the ses- - sion it is attached to is destroyed. If off, the client - is switched to the most recently active of the remaining - sessions. - - display-panes-active-colour colour - Set the colour used by the display-panes command to show - the indicator for the active pane. - - display-panes-colour colour - Set the colour used by the display-panes command to show - the indicators for inactive panes. - - display-panes-time time - Set the time in milliseconds for which the indicators - shown by the display-panes command appear. - - display-time time - Set the amount of time for which status line messages and - other on-screen indicators are displayed. time is in - milliseconds. - - history-limit lines - Set the maximum number of lines held in window history. - This setting applies only to new windows - existing win- - dow histories are not resized and retain the limit at the - point they were created. - - lock-after-time number - Lock the session (like the lock-session command) after - number seconds of inactivity, or the entire server (all - sessions) if the lock-server option is set. The default - is not to lock (set to 0). - - lock-command shell-command - Command to run when locking each client. The default is - to run lock(1) with -np. - - lock-server [on | off] - If this option is on (the default), instead of each ses- - sion locking individually as each has been idle for - lock-after-time, the entire server will lock after all - sessions would have locked. This has no effect as a ses- - sion option; it must be set as a global option. - - message-attr attributes - Set status line message attributes, where attributes is - either none or a comma-delimited list of one or more of: - bright (or bold), dim, underscore, blink, reverse, - hidden, or italics. - - message-bg colour - Set status line message background colour, where colour - is one of: black, red, green, yellow, blue, magenta, - cyan, white, colour0 to colour255 from the 256-colour - set, default, or a hexadecimal RGB string such as - '#ffffff', which chooses the closest match from the - default 256-colour set. - - message-fg colour - Set status line message foreground colour. - - message-limit number - Set the number of error or information messages to save - in the message log for each client. The default is 20. - - mouse-resize-pane [on | off] - If on, tmux captures the mouse and allows panes to be - resized by dragging on their borders. - - mouse-select-pane [on | off] - If on, tmux captures the mouse and when a window is split - into multiple panes the mouse may be used to select the - current pane. The mouse click is also passed through to - the application as normal. - - mouse-select-window [on | off] - If on, clicking the mouse on a window name in the status - line will select that window. - - pane-active-border-bg colour - - pane-active-border-fg colour - Set the pane border colour for the currently active pane. - - pane-border-bg colour - - pane-border-fg colour - Set the pane border colour for panes aside from the - active pane. - - prefix keys - Set the keys accepted as a prefix key. keys is a comma- - separated list of key names, each of which individually - behave as the prefix key. - - repeat-time time - Allow multiple commands to be entered without pressing - the prefix-key again in the specified time milliseconds - (the default is 500). Whether a key repeats may be set - when it is bound using the -r flag to bind-key. Repeat - is enabled for the default keys bound to the resize-pane - command. - - mouse-utf8 [on | off] - If enabled, request mouse input as UTF-8 on UTF-8 termi- - nals. - - set-remain-on-exit [on | off] - Set the remain-on-exit window option for any windows - first created in this session. When this option is true, - windows in which the running program has exited do not - close, instead remaining open but inactivate. Use the - respawn-window command to reactivate such a window, or - the kill-window command to destroy it. - - set-titles [on | off] - Attempt to set the window title using the \e]2;...\007 - xterm code if the terminal appears to be an xterm. This - option is off by default. Note that elinks will only - attempt to set the window title if the STY environment - variable is set. - - set-titles-string string - String used to set the window title if set-titles is on. - Character sequences are replaced as for the status-left - option. - - status [on | off] - Show or hide the status line. - - status-attr attributes - Set status line attributes. - - status-bg colour - Set status line background colour. - - status-fg colour - Set status line foreground colour. - - status-interval interval - Update the status bar every interval seconds. By - default, updates will occur every 15 seconds. A setting - of zero disables redrawing at interval. - - status-justify [left | centre | right] - Set the position of the window list component of the sta- - tus line: left, centre or right justified. - - status-keys [vi | emacs] - Use vi or emacs-style key bindings in the status line, - for example at the command prompt. The default is emacs, - unless the VISUAL or EDITOR environment variables are set - and contain the string 'vi'. - - status-left string - Display string to the left of the status bar. string - will be passed through strftime(3) before being used. By - default, the session name is shown. string may contain - any of the following special character sequences: - - Character pair Replaced with - #(shell-command) First line of the command's - output - #[attributes] Colour or attribute change - #H Hostname of local host - #h Hostname of local host without - the domain name - #F Current window flag - #I Current window index - #P Current pane index - #S Session name - #T Current window title - #W Current window name - ## A literal '#' - - The #(shell-command) form executes 'shell-command' and - inserts the first line of its output. Note that shell - commands are only executed once at the interval specified - by the status-interval option: if the status line is - redrawn in the meantime, the previous result is used. - Shell commands are executed with the tmux global environ- - ment set (see the ENVIRONMENT section). - - The window title (#T) is the title set by the program - running within the window using the OSC title setting - sequence, for example: - - $ printf '\033]2;My Title\033\\' - - When a window is first created, its title is the host- - name. - - #[attributes] allows a comma-separated list of attributes - to be specified, these may be 'fg=colour' to set the - foreground colour, 'bg=colour' to set the background - colour, the name of one of the attributes (listed under - the message-attr option) to turn an attribute on, or an - attribute prefixed with 'no' to turn one off, for example - nobright. Examples are: - - #(sysctl vm.loadavg) - #[fg=yellow,bold]#(apm -l)%%#[default] [#S] - - Where appropriate, special character sequences may be - prefixed with a number to specify the maximum length, for - example '#24T'. - - By default, UTF-8 in string is not interpreted, to enable - UTF-8, use the status-utf8 option. - - status-left-attr attributes - Set the attribute of the left part of the status line. - - status-left-bg colour - Set the background colour of the left part of the status - line. - - status-left-fg colour - Set the foreground colour of the left part of the status - line. - - status-left-length length - Set the maximum length of the left component of the sta- - tus bar. The default is 10. - - status-right string - Display string to the right of the status bar. By - default, the current window title in double quotes, the - date and the time are shown. As with status-left, string - will be passed to strftime(3), character pairs are - replaced, and UTF-8 is dependent on the status-utf8 - option. - - status-right-attr attributes - Set the attribute of the right part of the status line. - - status-right-bg colour - Set the background colour of the right part of the status - line. - - status-right-fg colour - Set the foreground colour of the right part of the status - line. - - status-right-length length - Set the maximum length of the right component of the sta- - tus bar. The default is 40. - - status-utf8 [on | off] - Instruct tmux to treat top-bit-set characters in the - status-left and status-right strings as UTF-8; notably, - this is important for wide characters. This option - defaults to off. - - terminal-overrides string - Contains a list of entries which override terminal - descriptions read using terminfo(5). string is a comma- - separated list of items each a colon-separated string - made up of a terminal type pattern (matched using - fnmatch(3)) and a set of name=value entries. - - For example, to set the 'clear' terminfo(5) entry to - '\e[H\e[2J' for all terminal types and the 'dch1' entry - to '\e[P' for the 'rxvt' terminal type, the option could - be set to the string: - - "*:clear=\e[H\e[2J,rxvt:dch1=\e[P" - - The terminal entry value is passed through strunvis(3) - before interpretation. The default value forcibly cor- - rects the 'colors' entry for terminals which support 88 - or 256 colours: - - "*88col*:colors=88,*256col*:colors=256,xterm*:XT" - - update-environment variables - Set a space-separated string containing a list of envi- - ronment variables to be copied into the session environ- - ment when a new session is created or an existing session - is attached. Any variables that do not exist in the - source environment are set to be removed from the session - environment (as if -r was given to the set-environment - command). The default is "DISPLAY SSH_ASKPASS - SSH_AUTH_SOCK SSH_AGENT_PID SSH_CONNECTION WINDOWID XAU- - THORITY". - - visual-activity [on | off] - If on, display a status line message when activity occurs - in a window for which the monitor-activity window option - is enabled. - - visual-bell [on | off] - If this option is on, a message is shown on a bell - instead of it being passed through to the terminal (which - normally makes a sound). Also see the bell-action - option. - - visual-content [on | off] - Like visual-activity, display a message when content is - present in a window for which the monitor-content window - option is enabled. - - visual-silence [on | off] - If monitor-silence is enabled, prints a message after the - interval has expired on a given window. - - set-window-option [-agu] [-t target-window] option value - (alias: setw) - Set a window option. The -a, -g and -u flags work similarly to - the set-option command. - - Supported window options are: - - aggressive-resize [on | off] - Aggressively resize the chosen window. This means that - tmux will resize the window to the size of the smallest - session for which it is the current window, rather than - the smallest session to which it is attached. The window - may resize when the current window is changed on another - sessions; this option is good for full-screen programs - which support SIGWINCH and poor for interactive programs - such as shells. - - alternate-screen [on | off] - This option configures whether programs running inside - tmux may use the terminal alternate screen feature, which - allows the smcup and rmcup terminfo(5) capabilities. The - alternate screen feature preserves the contents of the - window when an interactive application starts and - restores it on exit, so that any output visible before - the application starts reappears unchanged after it - exits. The default is on. - - automatic-rename [on | off] - Control automatic window renaming. When this setting is - enabled, tmux will attempt - on supported platforms - to - rename the window to reflect the command currently run- - ning in it. This flag is automatically disabled for an - individual window when a name is specified at creation - with new-window or new-session, or later with - rename-window. It may be switched off globally with: - - set-window-option -g automatic-rename off - - clock-mode-colour colour - Set clock colour. - - clock-mode-style [12 | 24] - Set clock hour format. - - force-height height - force-width width - Prevent tmux from resizing a window to greater than width - or height. A value of zero restores the default unlim- - ited setting. - - main-pane-height height - main-pane-width width - Set the width or height of the main (left or top) pane in - the main-horizontal or main-vertical layouts. - - mode-attr attributes - Set window modes attributes. - - mode-bg colour - Set window modes background colour. - - mode-fg colour - Set window modes foreground colour. - - mode-keys [vi | emacs] - Use vi or emacs-style key bindings in copy and choice - modes. As with the status-keys option, the default is - emacs, unless VISUAL or EDITOR contains 'vi'. - - mode-mouse [on | off] - Mouse state in modes. If on, the mouse may be used to - enter copy mode and copy a selection by dragging, to - enter copy mode and scroll with the mouse wheel, or to - select an option in choice mode. - - monitor-activity [on | off] - Monitor for activity in the window. Windows with activ- - ity are highlighted in the status line. - - monitor-content match-string - Monitor content in the window. When fnmatch(3) pattern - match-string appears in the window, it is highlighted in - the status line. - - monitor-silence [interval] - Monitor for silence (no activity) in the window within - interval seconds. Windows that have been silent for the - interval are highlighted in the status line. An interval - of zero disables the monitoring. - - other-pane-height height - Set the height of the other panes (not the main pane) in - the main-horizontal layout. If this option is set to 0 - (the default), it will have no effect. If both the - main-pane-height and other-pane-height options are set, - the main pane will grow taller to make the other panes - the specified height, but will never shrink to do so. - - other-pane-width width - Like other-pane-height, but set the width of other panes - in the main-vertical layout. - - remain-on-exit [on | off] - A window with this flag set is not destroyed when the - program running in it exits. The window may be reacti- - vated with the respawn-window command. - - synchronize-panes [on | off] - Duplicate input to any pane to all other panes in the - same window (only for panes that are not in any special - mode). - - utf8 [on | off] - Instructs tmux to expect UTF-8 sequences to appear in - this window. - - window-status-attr attributes - Set status line attributes for a single window. - - window-status-bg colour - Set status line background colour for a single window. - - window-status-fg colour - Set status line foreground colour for a single window. - - window-status-format string - Set the format in which the window is displayed in the - status line window list. See the status-left option for - details of special character sequences available. The - default is '#I:#W#F'. - - window-status-alert-attr attributes - Set status line attributes for windows which have an - alert (bell, activity or content). - - window-status-alert-bg colour - Set status line background colour for windows with an - alert. - - window-status-alert-fg colour - Set status line foreground colour for windows with an - alert. - - window-status-current-attr attributes - Set status line attributes for the currently active win- - dow. - - window-status-current-bg colour - Set status line background colour for the currently - active window. - - window-status-current-fg colour - Set status line foreground colour for the currently - active window. - - window-status-current-format string - Like window-status-format, but is the format used when - the window is the current window. - - word-separators string - Sets the window's conception of what characters are con- - sidered word separators, for the purposes of the next and - previous word commands in copy mode. The default is - ' -_@'. - - xterm-keys [on | off] - If this option is set, tmux will generate xterm(1) -style - function key sequences; these have a number included to - indicate modifiers such as Shift, Alt or Ctrl. The - default is off. - - show-options [-gsw] [-t target-session | target-window] - (alias: show) - Show the window options with -w (equivalent to - show-window-options), the server options with -s, otherwise the - session options for target session. Global session or window - options are listed if -g is used. - - show-window-options [-g] [-t target-window] - (alias: showw) - List the window options for target-window, or the global window - options if -g is used. - -ENVIRONMENT - When the server is started, tmux copies the environment into the global - environment; in addition, each session has a session environment. When a - window is created, the session and global environments are merged. If a - variable exists in both, the value from the session environment is used. - The result is the initial environment passed to the new process. - - The update-environment session option may be used to update the session - environment from the client when a new session is created or an old reat- - tached. tmux also initialises the TMUX variable with some internal - information to allow commands to be executed from inside, and the TERM - variable with the correct terminal setting of 'screen'. - - Commands to alter and view the environment are: - - set-environment [-gru] [-t target-session] name [value] - (alias: setenv) - Set or unset an environment variable. If -g is used, the change - is made in the global environment; otherwise, it is applied to - the session environment for target-session. The -u flag unsets a - variable. -r indicates the variable is to be removed from the - environment before starting a new process. - - show-environment [-g] [-t target-session] - (alias: showenv) - Display the environment for target-session or the global environ- - ment with -g. Variables removed from the environment are pre- - fixed with '-'. - -STATUS LINE - tmux includes an optional status line which is displayed in the bottom - line of each terminal. By default, the status line is enabled (it may be - disabled with the status session option) and contains, from left-to- - right: the name of the current session in square brackets; the window - list; the current window title in double quotes; and the time and date. - - The status line is made of three parts: configurable left and right sec- - tions (which may contain dynamic content such as the time or output from - a shell command, see the status-left, status-left-length, status-right, - and status-right-length options below), and a central window list. By - default, the window list shows the index, name and (if any) flag of the - windows present in the current session in ascending numerical order. It - may be customised with the window-status-format and - window-status-current-format options. The flag is one of the following - symbols appended to the window name: - - Symbol Meaning - * Denotes the current window. - - Marks the last window (previously selected). - # Window is monitored and activity has been detected. - ! A bell has occurred in the window. - + Window is monitored for content and it has appeared. - ~ The window has been silent for the monitor-silence - interval. - - The # symbol relates to the monitor-activity and + to the monitor-content - window options. The window name is printed in inverted colours if an - alert (bell, activity or content) is present. - - The colour and attributes of the status line may be configured, the - entire status line using the status-attr, status-fg and status-bg session - options and individual windows using the window-status-attr, - window-status-fg and window-status-bg window options. - - The status line is automatically refreshed at interval if it has changed, - the interval may be controlled with the status-interval session option. - - Commands related to the status line are as follows: - - command-prompt [-I inputs] [-p prompts] [-t target-client] [template] - Open the command prompt in a client. This may be used from - inside tmux to execute commands interactively. - - If template is specified, it is used as the command. If present, - -I is a comma-separated list of the initial text for each prompt. - If -p is given, prompts is a comma-separated list of prompts - which are displayed in order; otherwise a single prompt is dis- - played, constructed from template if it is present, or ':' if - not. - - Both inputs and prompts may contain the special character - sequences supported by the status-left option. - - Before the command is executed, the first occurrence of the - string '%%' and all occurrences of '%1' are replaced by the - response to the first prompt, the second '%%' and all '%2' are - replaced with the response to the second prompt, and so on for - further prompts. Up to nine prompt responses may be replaced - ('%1' to '%9'). - - confirm-before [-p prompt] [-t target-client] command - (alias: confirm) - Ask for confirmation before executing command. If -p is given, - prompt is the prompt to display; otherwise a prompt is con- - structed from command. It may contain the special character - sequences supported by the status-left option. - - This command works only from inside tmux. - - display-message [-p] [-c target-client] [-t target-pane] [message] - (alias: display) - Display a message. If -p is given, the output is printed to std- - out, otherwise it is displayed in the target-client status line. - The format of message is as for status-left, with the exception - that #() are not handled; information is taken from target-pane - if -t is given, otherwise the active pane for the session - attached to target-client. - -BUFFERS - tmux maintains a stack of paste buffers. Up to the value of the - buffer-limit option are kept; when a new buffer is added, the buffer at - the bottom of the stack is removed. Buffers may be added using copy-mode - or the set-buffer command, and pasted into a window using the - paste-buffer command. - - A configurable history buffer is also maintained for each window. By - default, up to 2000 lines are kept; this can be altered with the - history-limit option (see the set-option command above). - - The buffer commands are as follows: - - choose-buffer [-t target-window] [template] - Put a window into buffer choice mode, where a buffer may be cho- - sen interactively from a list. After a buffer is selected, '%%' - is replaced by the buffer index in template and the result exe- - cuted as a command. If template is not given, "paste-buffer -b - '%%'" is used. This command works only from inside tmux. - - clear-history [-t target-pane] - (alias: clearhist) - Remove and free the history for the specified pane. - - delete-buffer [-b buffer-index] - (alias: deleteb) - Delete the buffer at buffer-index, or the top buffer if not spec- - ified. - - list-buffers - (alias: lsb) - List the global buffers. - - load-buffer [-b buffer-index] path - (alias: loadb) - Load the contents of the specified paste buffer from path. - - paste-buffer [-dr] [-b buffer-index] [-s separator] [-t target-pane] - (alias: pasteb) - Insert the contents of a paste buffer into the specified pane. - If not specified, paste into the current one. With -d, also - delete the paste buffer from the stack. When output, any line- - feed (LF) characters in the paste buffer are replaced with a sep- - arator, by default carriage return (CR). A custom separator may - be specified using the -s flag. The -r flag means to do no - replacement (equivalent to a separator of LF). - - save-buffer [-a] [-b buffer-index] path - (alias: saveb) - Save the contents of the specified paste buffer to path. The -a - option appends to rather than overwriting the file. - - set-buffer [-b buffer-index] data - (alias: setb) - Set the contents of the specified buffer to data. - - show-buffer [-b buffer-index] - (alias: showb) - Display the contents of the specified buffer. - -MISCELLANEOUS - Miscellaneous commands are as follows: - - clock-mode [-t target-pane] - Display a large clock. - - if-shell shell-command command - (alias: if) - Execute command if shell-command returns success. - - lock-server - (alias: lock) - Lock each client individually by running the command specified by - the lock-command option. - - run-shell shell-command - (alias: run) - Execute shell-command in the background without creating a win- - dow. After it finishes, any output to stdout is displayed in - copy mode. If the command doesn't return success, the exit sta- - tus is also displayed. - - server-info - (alias: info) - Show server information and terminal details. - -TERMINFO EXTENSIONS - tmux understands some extensions to terminfo(5): - - Cc, Cr Set the cursor colour. The first takes a single string argument - and is used to set the colour; the second takes no arguments and - restores the default cursor colour. If set, a sequence such as - this may be used to change the cursor colour from inside tmux: - - $ printf '\033]12;red\033\\' - - Cs, Csr - Change the cursor style. If set, a sequence such as this may be - used to change the cursor to an underline: - - $ printf '\033[4 q' - - If Csr is set, it will be used to reset the cursor style instead - of Cs. - - Ms This sequence can be used by tmux to store the current buffer in - the host terminal's selection (clipboard). See the set-clipboard - option above and the xterm(1) man page. - -FILES - ~/.tmux.conf Default tmux configuration file. - /etc/tmux.conf System-wide configuration file. - -EXAMPLES - To create a new tmux session running vi(1): - - $ tmux new-session vi - - Most commands have a shorter form, known as an alias. For new-session, - this is new: - - $ tmux new vi - - Alternatively, the shortest unambiguous form of a command is accepted. - If there are several options, they are listed: - - $ tmux n - ambiguous command: n, could be: new-session, new-window, next-window - - Within an active session, a new window may be created by typing 'C-b c' - (Ctrl followed by the 'b' key followed by the 'c' key). - - Windows may be navigated with: 'C-b 0' (to select window 0), 'C-b 1' (to - select window 1), and so on; 'C-b n' to select the next window; and 'C-b - p' to select the previous window. - - A session may be detached using 'C-b d' (or by an external event such as - ssh(1) disconnection) and reattached with: - - $ tmux attach-session - - Typing 'C-b ?' lists the current key bindings in the current window; up - and down may be used to navigate the list or 'q' to exit from it. - - Commands to be run when the tmux server is started may be placed in the - ~/.tmux.conf configuration file. Common examples include: - - Changing the default prefix key: - - set-option -g prefix C-a - unbind-key C-b - bind-key C-a send-prefix - - Turning the status line off, or changing its colour: - - set-option -g status off - set-option -g status-bg blue - - Setting other options, such as the default command, or locking after 30 - minutes of inactivity: - - set-option -g default-command "exec /bin/ksh" - set-option -g lock-after-time 1800 - - Creating new key bindings: - - bind-key b set-option status - bind-key / command-prompt "split-window 'exec man %%'" - bind-key S command-prompt "new-window -n %1 'ssh %1'" - -SEE ALSO - pty(4) - -AUTHORS - Nicholas Marriott - -BSD October 19, 2013 BSD diff --git a/manual/1.6 b/manual/1.6 deleted file mode 100644 index b3ecbd3985e..00000000000 --- a/manual/1.6 +++ /dev/null @@ -1,1891 +0,0 @@ -TMUX(1) BSD General Commands Manual TMUX(1) - -NAME - tmux -- terminal multiplexer - -SYNOPSIS - tmux [-28lquvV] [-c shell-command] [-f file] [-L socket-name] - [-S socket-path] [command [flags]] - -DESCRIPTION - tmux is a terminal multiplexer: it enables a number of terminals to be - created, accessed, and controlled from a single screen. tmux may be - detached from a screen and continue running in the background, then later - reattached. - - When tmux is started it creates a new session with a single window and - displays it on screen. A status line at the bottom of the screen shows - information on the current session and is used to enter interactive com- - mands. - - A session is a single collection of pseudo terminals under the management - of tmux. Each session has one or more windows linked to it. A window - occupies the entire screen and may be split into rectangular panes, each - of which is a separate pseudo terminal (the pty(4) manual page documents - the technical details of pseudo terminals). Any number of tmux instances - may connect to the same session, and any number of windows may be present - in the same session. Once all sessions are killed, tmux exits. - - Each session is persistent and will survive accidental disconnection - (such as ssh(1) connection timeout) or intentional detaching (with the - 'C-b d' key strokes). tmux may be reattached using: - - $ tmux attach - - In tmux, a session is displayed on screen by a client and all sessions - are managed by a single server. The server and each client are separate - processes which communicate through a socket in /tmp. - - The options are as follows: - - -2 Force tmux to assume the terminal supports 256 colours. - - -8 Like -2, but indicates that the terminal supports 88 - colours. - - -c shell-command - Execute shell-command using the default shell. If neces- - sary, the tmux server will be started to retrieve the - default-shell option. This option is for compatibility - with sh(1) when tmux is used as a login shell. - - -f file Specify an alternative configuration file. By default, - tmux loads the system configuration file from - /etc/tmux.conf, if present, then looks for a user configu- - ration file at ~/.tmux.conf. The configuration file is a - set of tmux commands which are executed in sequence when - the server is first started. - - If a command in the configuration file fails, tmux will - report an error and exit without executing further com- - mands. - - -L socket-name - tmux stores the server socket in a directory under /tmp (or - TMPDIR if set); the default socket is named default. This - option allows a different socket name to be specified, - allowing several independent tmux servers to be run. - Unlike -S a full path is not necessary: the sockets are all - created in the same directory. - - If the socket is accidentally removed, the SIGUSR1 signal - may be sent to the tmux server process to recreate it. - - -l Behave as a login shell. This flag currently has no effect - and is for compatibility with other shells when using tmux - as a login shell. - - -q Set the quiet server option to prevent the server sending - various informational messages. - - -S socket-path - Specify a full alternative path to the server socket. If - -S is specified, the default socket directory is not used - and any -L flag is ignored. - - -u tmux attempts to guess if the terminal is likely to support - UTF-8 by checking the first of the LC_ALL, LC_CTYPE and - LANG environment variables to be set for the string - "UTF-8". This is not always correct: the -u flag explic- - itly informs tmux that UTF-8 is supported. - - If the server is started from a client passed -u or where - UTF-8 is detected, the utf8 and status-utf8 options are - enabled in the global window and session options respec- - tively. - - -v Request verbose logging. This option may be specified mul- - tiple times for increasing verbosity. Log messages will be - saved into tmux-client-PID.log and tmux-server-PID.log - files in the current directory, where PID is the PID of the - server or client process. - - -V Report the tmux version. - - command [flags] - This specifies one of a set of commands used to control - tmux, as described in the following sections. If no com- - mands are specified, the new-session command is assumed. - -KEY BINDINGS - tmux may be controlled from an attached client by using a key combination - of a prefix key, 'C-b' (Ctrl-b) by default, followed by a command key. - - The default command key bindings are: - - C-b Send the prefix key (C-b) through to the application. - C-o Rotate the panes in the current window forwards. - C-z Suspend the tmux client. - ! Break the current pane out of the window. - " Split the current pane into two, top and bottom. - # List all paste buffers. - $ Rename the current session. - % Split the current pane into two, left and right. - & Kill the current window. - ' Prompt for a window index to select. - , Rename the current window. - - Delete the most recently copied buffer of text. - . Prompt for an index to move the current window. - 0 to 9 Select windows 0 to 9. - : Enter the tmux command prompt. - ; Move to the previously active pane. - = Choose which buffer to paste interactively from a list. - ? List all key bindings. - D Choose a client to detach. - [ Enter copy mode to copy text or view the history. - ] Paste the most recently copied buffer of text. - c Create a new window. - d Detach the current client. - f Prompt to search for text in open windows. - i Display some information about the current window. - l Move to the previously selected window. - n Change to the next window. - o Select the next pane in the current window. - p Change to the previous window. - q Briefly display pane indexes. - r Force redraw of the attached client. - s Select a new session for the attached client interac- - tively. - L Switch the attached client back to the last session. - t Show the time. - w Choose the current window interactively. - x Kill the current pane. - { Swap the current pane with the previous pane. - } Swap the current pane with the next pane. - ~ Show previous messages from tmux, if any. - Page Up Enter copy mode and scroll one page up. - Up, Down - Left, Right - Change to the pane above, below, to the left, or to the - right of the current pane. - M-1 to M-5 Arrange panes in one of the five preset layouts: even- - horizontal, even-vertical, main-horizontal, main-verti- - cal, or tiled. - M-n Move to the next window with a bell or activity marker. - M-o Rotate the panes in the current window backwards. - M-p Move to the previous window with a bell or activity - marker. - C-Up, C-Down - C-Left, C-Right - Resize the current pane in steps of one cell. - M-Up, M-Down - M-Left, M-Right - Resize the current pane in steps of five cells. - - Key bindings may be changed with the bind-key and unbind-key commands. - -COMMANDS - This section contains a list of the commands supported by tmux. Most - commands accept the optional -t argument with one of target-client, - target-session target-window, or target-pane. These specify the client, - session, window or pane which a command should affect. target-client is - the name of the pty(4) file to which the client is connected, for example - either of /dev/ttyp1 or ttyp1 for the client attached to /dev/ttyp1. If - no client is specified, the current client is chosen, if possible, or an - error is reported. Clients may be listed with the list-clients command. - - target-session is either the name of a session (as listed by the - list-sessions command) or the name of a client with the same syntax as - target-client, in which case the session attached to the client is used. - When looking for the session name, tmux initially searches for an exact - match; if none is found, the session names are checked for any for which - target-session is a prefix or for which it matches as an fnmatch(3) pat- - tern. If a single match is found, it is used as the target session; mul- - tiple matches produce an error. If a session is omitted, the current - session is used if available; if no current session is available, the - most recently used is chosen. - - target-window specifies a window in the form session:window. session - follows the same rules as for target-session, and window is looked for in - order: as a window index, for example mysession:1; as an exact window - name, such as mysession:mywindow; then as an fnmatch(3) pattern or the - start of a window name, such as mysession:mywin* or mysession:mywin. An - empty window name specifies the next unused index if appropriate (for - example the new-window and link-window commands) otherwise the current - window in session is chosen. The special character '!' uses the last - (previously current) window, or '+' and '-' are the next window or the - previous window by number. When the argument does not contain a colon, - tmux first attempts to parse it as window; if that fails, an attempt is - made to match a session. - - target-pane takes a similar form to target-window but with the optional - addition of a period followed by a pane index, for example: myses- - sion:mywindow.1. If the pane index is omitted, the currently active pane - in the specified window is used. If neither a colon nor period appears, - tmux first attempts to use the argument as a pane index; if that fails, - it is looked up as for target-window. A '+' or '-' indicate the next or - previous pane index, respectively. One of the strings top, bottom, left, - right, top-left, top-right, bottom-left or bottom-right may be used - instead of a pane index. - - The special characters '+' and '-' may be followed by an offset, for - example: - - select-window -t:+2 - - When dealing with a session that doesn't contain sequential window - indexes, they will be correctly skipped. - - tmux also gives each pane created in a server an identifier consisting of - a '%' and a number, starting from zero. A pane's identifier is unique - for the life of the tmux server and is passed to the child process of the - pane in the TMUX_PANE environment variable. It may be used alone to tar- - get a pane or the window containing it. - - shell-command arguments are sh(1) commands. These must be passed as a - single item, which typically means quoting them, for example: - - new-window 'vi /etc/passwd' - - command [arguments] refers to a tmux command, passed with the command and - arguments separately, for example: - - bind-key F1 set-window-option force-width 81 - - Or if using sh(1): - - $ tmux bind-key F1 set-window-option force-width 81 - - Multiple commands may be specified together as part of a command - sequence. Each command should be separated by spaces and a semicolon; - commands are executed sequentially from left to right and lines ending - with a backslash continue on to the next line. A literal semicolon may - be included by escaping it with a backslash (for example, when specifying - a command sequence to bind-key). - - Example tmux commands include: - - refresh-client -t/dev/ttyp2 - - rename-session -tfirst newname - - set-window-option -t:0 monitor-activity on - - new-window ; split-window -d - - bind-key R source-file ~/.tmux.conf \; \ - display-message "source-file done" - - Or from sh(1): - - $ tmux kill-window -t :1 - - $ tmux new-window \; split-window -d - - $ tmux new-session -d 'vi /etc/passwd' \; split-window -d \; attach - -CLIENTS AND SESSIONS - The tmux server manages clients, sessions, windows and panes. Clients - are attached to sessions to interact with them, either when they are cre- - ated with the new-session command, or later with the attach-session com- - mand. Each session has one or more windows linked into it. Windows may - be linked to multiple sessions and are made up of one or more panes, each - of which contains a pseudo terminal. Commands for creating, linking and - otherwise manipulating windows are covered in the WINDOWS AND PANES sec- - tion. - - The following commands are available to manage clients and sessions: - - attach-session [-dr] [-t target-session] - (alias: attach) - If run from outside tmux, create a new client in the current ter- - minal and attach it to target-session. If used from inside, - switch the current client. If -d is specified, any other clients - attached to the session are detached. -r signifies the client is - read-only (only keys bound to the detach-client or switch-client - commands have any effect) - - If no server is started, attach-session will attempt to start it; - this will fail unless sessions are created in the configuration - file. - - The target-session rules for attach-session are slightly - adjusted: if tmux needs to select the most recently used session, - it will prefer the most recently used unattached session. - - detach-client [-P] [-s target-session] [-t target-client] - (alias: detach) - Detach the current client if bound to a key, the client specified - with -t, or all clients currently attached to the session speci- - fied by -s. If -P is given, send SIGHUP to the parent process of - the client, typically causing it to exit. - - has-session [-t target-session] - (alias: has) - Report an error and exit with 1 if the specified session does not - exist. If it does exist, exit with 0. - - kill-server - Kill the tmux server and clients and destroy all sessions. - - kill-session [-t target-session] - Destroy the given session, closing any windows linked to it and - no other sessions, and detaching all clients attached to it. - - list-clients [-F format] [-t target-session] - (alias: lsc) - List all clients attached to the server. For the meaning of the - -F flag, see the FORMATS section. If target-session is speci- - fied, list only clients connected to that session. - - list-commands - (alias: lscm) - List the syntax of all commands supported by tmux. - - list-sessions [-F format] - (alias: ls) - List all sessions managed by the server. For the meaning of the - -F flag, see the FORMATS section. - - lock-client [-t target-client] - (alias: lockc) - Lock target-client, see the lock-server command. - - lock-session [-t target-session] - (alias: locks) - Lock all clients attached to target-session. - - new-session [-d] [-n window-name] [-s session-name] [-t target-session] - [-x width] [-y height] [shell-command] - (alias: new) - Create a new session with name session-name. - - The new session is attached to the current terminal unless -d is - given. window-name and shell-command are the name of and shell - command to execute in the initial window. If -d is used, -x and - -y specify the size of the initial window (80 by 24 if not - given). - - If run from a terminal, any termios(4) special characters are - saved and used for new windows in the new session. - - If -t is given, the new session is grouped with target-session. - This means they share the same set of windows - all windows from - target-session are linked to the new session and any subsequent - new windows or windows being closed are applied to both sessions. - The current and previous window and any session options remain - independent and either session may be killed without affecting - the other. Giving -n or shell-command are invalid if -t is used. - - refresh-client [-S] [-t target-client] - (alias: refresh) - Refresh the current client if bound to a key, or a single client - if one is given with -t. If -S is specified, only update the - client's status bar. - - rename-session [-t target-session] new-name - (alias: rename) - Rename the session to new-name. - - show-messages [-t target-client] - (alias: showmsgs) - Any messages displayed on the status line are saved in a per- - client message log, up to a maximum of the limit set by the - message-limit session option for the session attached to that - client. This command displays the log for target-client. - - source-file path - (alias: source) - Execute commands from path. - - start-server - (alias: start) - Start the tmux server, if not already running, without creating - any sessions. - - suspend-client [-t target-client] - (alias: suspendc) - Suspend a client by sending SIGTSTP (tty stop). - - switch-client [-lnpr] [-c target-client] [-t target-session] - (alias: switchc) - Switch the current session for client target-client to - target-session. If -l, -n or -p is used, the client is moved to - the last, next or previous session respectively. -r toggles - whether a client is read-only (see the attach-session command). - -WINDOWS AND PANES - A tmux window may be in one of several modes. The default permits direct - access to the terminal attached to the window. The other is copy mode, - which permits a section of a window or its history to be copied to a - paste buffer for later insertion into another window. This mode is - entered with the copy-mode command, bound to '[' by default. It is also - entered when a command that produces output, such as list-keys, is exe- - cuted from a key binding. - - The keys available depend on whether emacs or vi mode is selected (see - the mode-keys option). The following keys are supported as appropriate - for the mode: - - Function vi emacs - Back to indentation ^ M-m - Bottom of history G M-< - Clear selection Escape C-g - Copy selection Enter M-w - Cursor down j Down - Cursor left h Left - Cursor right l Right - Cursor to bottom line L - Cursor to middle line M M-r - Cursor to top line H M-R - Cursor up k Up - Delete entire line d C-u - Delete/Copy to end of line D C-k - End of line $ C-e - Go to line : g - Half page down C-d M-Down - Half page up C-u M-Up - Jump forward f f - Jump to forward t - Jump backward F F - Jump to backward T - Jump again ; ; - Jump again in reverse , , - Next page C-f Page down - Next space W - Next space, end of word E - Next word w - Next word end e M-f - Paste buffer p C-y - Previous page C-b Page up - Previous word b M-b - Previous space B - Quit mode q Escape - Rectangle toggle v R - Scroll down C-Down or C-e C-Down - Scroll up C-Up or C-y C-Up - Search again n n - Search again in reverse N N - Search backward ? C-r - Search forward / C-s - Start of line 0 C-a - Start selection Space C-Space - Top of history g M-> - Transpose chars C-t - - The next and previous word keys use space and the '-', '_' and '@' char- - acters as word delimiters by default, but this can be adjusted by setting - the word-separators session option. Next word moves to the start of the - next word, next word end to the end of the next word and previous word to - the start of the previous word. The three next and previous space keys - work similarly but use a space alone as the word separator. - - The jump commands enable quick movement within a line. For instance, - typing 'f' followed by '/' will move the cursor to the next '/' character - on the current line. A ';' will then jump to the next occurrence. - - Commands in copy mode may be prefaced by an optional repeat count. With - vi key bindings, a prefix is entered using the number keys; with emacs, - the Alt (meta) key and a number begins prefix entry. For example, to - move the cursor forward by ten words, use 'M-1 0 M-f' in emacs mode, and - '10w' in vi. - - When copying the selection, the repeat count indicates the buffer index - to replace, if used. - - Mode key bindings are defined in a set of named tables: vi-edit and - emacs-edit for keys used when line editing at the command prompt; - vi-choice and emacs-choice for keys used when choosing from lists (such - as produced by the choose-window command); and vi-copy and emacs-copy - used in copy mode. The tables may be viewed with the list-keys command - and keys modified or removed with bind-key and unbind-key. - - The paste buffer key pastes the first line from the top paste buffer on - the stack. - - The synopsis for the copy-mode command is: - - copy-mode [-u] [-t target-pane] - Enter copy mode. The -u option scrolls one page up. - - Each window displayed by tmux may be split into one or more panes; each - pane takes up a certain area of the display and is a separate terminal. - A window may be split into panes using the split-window command. Windows - may be split horizontally (with the -h flag) or vertically. Panes may be - resized with the resize-pane command (bound to 'C-up', 'C-down' 'C-left' - and 'C-right' by default), the current pane may be changed with the - select-pane command and the rotate-window and swap-pane commands may be - used to swap panes without changing their position. Panes are numbered - beginning from zero in the order they are created. - - A number of preset layouts are available. These may be selected with the - select-layout command or cycled with next-layout (bound to 'Space' by - default); once a layout is chosen, panes within it may be moved and - resized as normal. - - The following layouts are supported: - - even-horizontal - Panes are spread out evenly from left to right across the window. - - even-vertical - Panes are spread evenly from top to bottom. - - main-horizontal - A large (main) pane is shown at the top of the window and the - remaining panes are spread from left to right in the leftover - space at the bottom. Use the main-pane-height window option to - specify the height of the top pane. - - main-vertical - Similar to main-horizontal but the large pane is placed on the - left and the others spread from top to bottom along the right. - See the main-pane-width window option. - - tiled Panes are spread out as evenly as possible over the window in - both rows and columns. - - In addition, select-layout may be used to apply a previously used layout - - the list-windows command displays the layout of each window in a form - suitable for use with select-layout. For example: - - $ tmux list-windows - 0: ksh [159x48] - layout: bb62,159x48,0,0{79x48,0,0,79x48,80,0} - $ tmux select-layout bb62,159x48,0,0{79x48,0,0,79x48,80,0} - - tmux automatically adjusts the size of the layout for the current window - size. Note that a layout cannot be applied to a window with more panes - than that from which the layout was originally defined. - - Commands related to windows and panes are as follows: - - break-pane [-d] [-t target-pane] - (alias: breakp) - Break target-pane off from its containing window to make it the - only pane in a new window. If -d is given, the new window does - not become the current window. - - capture-pane [-b buffer-index] [-E end-line] [-S start-line] [-t - target-pane] - (alias: capturep) - Capture the contents of a pane to the specified buffer, or a new - buffer if none is specified. - - -S and -E specify the starting and ending line numbers, zero is - the first line of the visible pane and negative numbers are lines - in the history. The default is to capture only the visible con- - tents of the pane. - - choose-client [-t target-window] [template] - Put a window into client choice mode, allowing a client to be - selected interactively from a list. After a client is chosen, - '%%' is replaced by the client pty(4) path in template and the - result executed as a command. If template is not given, "detach- - client -t '%%'" is used. This command works only from inside - tmux. - - choose-session [-t target-window] [template] - Put a window into session choice mode, where a session may be - selected interactively from a list. When one is chosen, '%%' is - replaced by the session name in template and the result executed - as a command. If template is not given, "switch-client -t '%%'" - is used. This command works only from inside tmux. - - choose-window [-t target-window] [template] - Put a window into window choice mode, where a window may be cho- - sen interactively from a list. After a window is selected, '%%' - is replaced by the session name and window index in template and - the result executed as a command. If template is not given, - "select-window -t '%%'" is used. This command works only from - inside tmux. - - display-panes [-t target-client] - (alias: displayp) - Display a visible indicator of each pane shown by target-client. - See the display-panes-time, display-panes-colour, and - display-panes-active-colour session options. While the indicator - is on screen, a pane may be selected with the '0' to '9' keys. - - find-window [-t target-window] match-string - (alias: findw) - Search for the fnmatch(3) pattern match-string in window names, - titles, and visible content (but not history). If only one win- - dow is matched, it'll be automatically selected, otherwise a - choice list is shown. This command only works from inside tmux. - - join-pane [-dhv] [-l size | -p percentage] [-s src-pane] [-t dst-pane] - (alias: joinp) - Like split-window, but instead of splitting dst-pane and creating - a new pane, split it and move src-pane into the space. This can - be used to reverse break-pane. - - kill-pane [-a] [-t target-pane] - (alias: killp) - Destroy the given pane. If no panes remain in the containing - window, it is also destroyed. The -a option kills all but the - pane given with -t. - - kill-window [-t target-window] - (alias: killw) - Kill the current window or the window at target-window, removing - it from any sessions to which it is linked. - - last-pane [-t target-window] - (alias: lastp) - Select the last (previously selected) pane. - - last-window [-t target-session] - (alias: last) - Select the last (previously selected) window. If no - target-session is specified, select the last window of the cur- - rent session. - - link-window [-dk] [-s src-window] [-t dst-window] - (alias: linkw) - Link the window at src-window to the specified dst-window. If - dst-window is specified and no such window exists, the src-window - is linked there. If -k is given and dst-window exists, it is - killed, otherwise an error is generated. If -d is given, the - newly linked window is not selected. - - list-panes [-as] [-F format] [-t target] - (alias: lsp) - If -a is given, target is ignored and all panes on the server are - listed. If -s is given, target is a session (or the current ses- - sion). If neither is given, target is a window (or the current - window). For the meaning of the -F flag, see the FORMATS sec- - tion. - - list-windows [-a] [-F format] [-t target-session] - (alias: lsw) - If -a is given, list all windows on the server. Otherwise, list - windows in the current session or in target-session. For the - meaning of the -F flag, see the FORMATS section. - - move-window [-dk] [-s src-window] [-t dst-window] - (alias: movew) - This is similar to link-window, except the window at src-window - is moved to dst-window. - - new-window [-adkP] [-n window-name] [-t target-window] [shell-command] - (alias: neww) - Create a new window. With -a, the new window is inserted at the - next index up from the specified target-window, moving windows up - if necessary, otherwise target-window is the new window location. - - If -d is given, the session does not make the new window the cur- - rent window. target-window represents the window to be created; - if the target already exists an error is shown, unless the -k - flag is used, in which case it is destroyed. shell-command is - the command to execute. If shell-command is not specified, the - value of the default-command option is used. - - When the shell command completes, the window closes. See the - remain-on-exit option to change this behaviour. - - The TERM environment variable must be set to ``screen'' for all - programs running inside tmux. New windows will automatically - have ``TERM=screen'' added to their environment, but care must be - taken not to reset this in shell start-up files. - - The -P option prints the location of the new window after it has - been created. - - next-layout [-t target-window] - (alias: nextl) - Move a window to the next layout and rearrange the panes to fit. - - next-window [-a] [-t target-session] - (alias: next) - Move to the next window in the session. If -a is used, move to - the next window with a bell, activity or content alert. - - pipe-pane [-o] [-t target-pane] [shell-command] - (alias: pipep) - Pipe any output sent by the program in target-pane to a shell - command. A pane may only be piped to one command at a time, any - existing pipe is closed before shell-command is executed. The - shell-command string may contain the special character sequences - supported by the status-left option. If no shell-command is - given, the current pipe (if any) is closed. - - The -o option only opens a new pipe if no previous pipe exists, - allowing a pipe to be toggled with a single key, for example: - - bind-key C-p pipe-pane -o 'cat >>~/output.#I-#P' - - previous-layout [-t target-window] - (alias: prevl) - Move to the previous layout in the session. - - previous-window [-a] [-t target-session] - (alias: prev) - Move to the previous window in the session. With -a, move to the - previous window with a bell, activity or content alert. - - rename-window [-t target-window] new-name - (alias: renamew) - Rename the current window, or the window at target-window if - specified, to new-name. - - resize-pane [-DLRU] [-t target-pane] [adjustment] - (alias: resizep) - Resize a pane, upward with -U (the default), downward with -D, to - the left with -L and to the right with -R. The adjustment is - given in lines or cells (the default is 1). - - respawn-pane [-k] [-t target-pane] [shell-command] - (alias: respawnp) - Reactivate a pane in which the command has exited (see the - remain-on-exit window option). If shell-command is not given, - the command used when the pane was created is executed. The pane - must be already inactive, unless -k is given, in which case any - existing command is killed. - - respawn-window [-k] [-t target-window] [shell-command] - (alias: respawnw) - Reactivate a window in which the command has exited (see the - remain-on-exit window option). If shell-command is not given, - the command used when the window was created is executed. The - window must be already inactive, unless -k is given, in which - case any existing command is killed. - - rotate-window [-DU] [-t target-window] - (alias: rotatew) - Rotate the positions of the panes within a window, either upward - (numerically lower) with -U or downward (numerically higher). - - select-layout [-np] [-t target-window] [layout-name] - (alias: selectl) - Choose a specific layout for a window. If layout-name is not - given, the last preset layout used (if any) is reapplied. -n and - -p are equivalent to the next-layout and previous-layout com- - mands. - - select-pane [-lDLRU] [-t target-pane] - (alias: selectp) - Make pane target-pane the active pane in window target-window. - If one of -D, -L, -R, or -U is used, respectively the pane below, - to the left, to the right, or above the target pane is used. -l - is the same as using the last-pane command. - - select-window [-lnp] [-t target-window] - (alias: selectw) - Select the window at target-window. -l, -n and -p are equivalent - to the last-window, next-window and previous-window commands. - - split-window [-dhvP] [-l size | -p percentage] [-t target-pane] - [shell-command] - (alias: splitw) - Create a new pane by splitting target-pane: -h does a horizontal - split and -v a vertical split; if neither is specified, -v is - assumed. The -l and -p options specify the size of the new pane - in lines (for vertical split) or in cells (for horizontal split), - or as a percentage, respectively. All other options have the - same meaning as for the new-window command. - - swap-pane [-dDU] [-s src-pane] [-t dst-pane] - (alias: swapp) - Swap two panes. If -U is used and no source pane is specified - with -s, dst-pane is swapped with the previous pane (before it - numerically); -D swaps with the next pane (after it numerically). - -d instructs tmux not to change the active pane. - - swap-window [-d] [-s src-window] [-t dst-window] - (alias: swapw) - This is similar to link-window, except the source and destination - windows are swapped. It is an error if no window exists at - src-window. - - unlink-window [-k] [-t target-window] - (alias: unlinkw) - Unlink target-window. Unless -k is given, a window may be - unlinked only if it is linked to multiple sessions - windows may - not be linked to no sessions; if -k is specified and the window - is linked to only one session, it is unlinked and destroyed. - -KEY BINDINGS - tmux allows a command to be bound to most keys, with or without a prefix - key. When specifying keys, most represent themselves (for example 'A' to - 'Z'). Ctrl keys may be prefixed with 'C-' or '^', and Alt (meta) with - 'M-'. In addition, the following special key names are accepted: Up, - Down, Left, Right, BSpace, BTab, DC (Delete), End, Enter, Escape, F1 to - F20, Home, IC (Insert), NPage/PageDown/PgDn, PPage/PageUp/PgUp, Space, - and Tab. Note that to bind the '"' or ''' keys, quotation marks are nec- - essary, for example: - - bind-key '"' split-window - bind-key "'" new-window - - Commands related to key bindings are as follows: - - bind-key [-cnr] [-t key-table] key command [arguments] - (alias: bind) - Bind key key to command. By default (without -t) the primary key - bindings are modified (those normally activated with the prefix - key); in this case, if -n is specified, it is not necessary to - use the prefix key, command is bound to key alone. The -r flag - indicates this key may repeat, see the repeat-time option. - - If -t is present, key is bound in key-table: the binding for com- - mand mode with -c or for normal mode without. To view the - default bindings and possible commands, see the list-keys com- - mand. - - list-keys [-t key-table] - (alias: lsk) - List all key bindings. Without -t the primary key bindings - - those executed when preceded by the prefix key - are printed. - Keys bound without the prefix key (see bind-key -n) are marked - with '(no prefix)'. - - With -t, the key bindings in key-table are listed; this may be - one of: vi-edit, emacs-edit, vi-choice, emacs-choice, vi-copy or - emacs-copy. - - send-keys -R [-t target-pane] key ... - (alias: send) - Send a key or keys to a window. Each argument key is the name of - the key (such as 'C-a' or 'npage' ) to send; if the string is not - recognised as a key, it is sent as a series of characters. All - arguments are sent sequentially from first to last. The -R flag - causes the terminal state to be reset. - - send-prefix [-2] [-t target-pane] - Send the prefix key, or with -2 the secondary prefix key, to a - window as if it was pressed. - - unbind-key [-acn] [-t key-table] key - (alias: unbind) - Unbind the command bound to key. Without -t the primary key - bindings are modified; in this case, if -n is specified, the com- - mand bound to key without a prefix (if any) is removed. If -a is - present, all key bindings are removed. - - If -t is present, key in key-table is unbound: the binding for - command mode with -c or for normal mode without. - -OPTIONS - The appearance and behaviour of tmux may be modified by changing the - value of various options. There are three types of option: server - options, session options and window options. - - The tmux server has a set of global options which do not apply to any - particular window or session. These are altered with the set-option -s - command, or displayed with the show-options -s command. - - In addition, each individual session may have a set of session options, - and there is a separate set of global session options. Sessions which do - not have a particular option configured inherit the value from the global - session options. Session options are set or unset with the set-option - command and may be listed with the show-options command. The available - server and session options are listed under the set-option command. - - Similarly, a set of window options is attached to each window, and there - is a set of global window options from which any unset options are inher- - ited. Window options are altered with the set-window-option command and - can be listed with the show-window-options command. All window options - are documented with the set-window-option command. - - Commands which set options are as follows: - - set-option [-agsuw] [-t target-session | target-window] option value - (alias: set) - Set a window option with -w (equivalent to the set-window-option - command), a server option with -s, otherwise a session option. - - If -g is specified, the global session or window option is set. - With -a, and if the option expects a string, value is appended to - the existing setting. The -u flag unsets an option, so a session - inherits the option from the global options. It is not possible - to unset a global option. - - Available window options are listed under set-window-option. - - Available server options are: - - buffer-limit number - Set the number of buffers; as new buffers are added to - the top of the stack, old ones are removed from the bot- - tom if necessary to maintain this maximum length. - - escape-time time - Set the time in milliseconds for which tmux waits after - an escape is input to determine if it is part of a func- - tion or meta key sequences. The default is 500 millisec- - onds. - - exit-unattached [on | off] - If enabled, the server will exit when there are no - attached clients. - - quiet [on | off] - Enable or disable the display of various informational - messages (see also the -q command line flag). - - set-clipboard [on | off] - Attempt to set the terminal clipboard content using the - \e]52;...\007 xterm(1) escape sequences. This option is - on by default if there is an Ms entry in the terminfo(5) - description for the client terminal. Note that this fea- - ture needs to be enabled in xterm(1) by setting the - resource: - - disallowedWindowOps: 20,21,SetXprop - - Or changing this property from the xterm(1) interactive - menu when required. - - Available session options are: - - base-index index - Set the base index from which an unused index should be - searched when a new window is created. The default is - zero. - - bell-action [any | none | current] - Set action on window bell. any means a bell in any win- - dow linked to a session causes a bell in the current win- - dow of that session, none means all bells are ignored and - current means only bell in windows other than the current - window are ignored. - - bell-on-alert [on | off] - If on, ring the terminal bell when an activity, content - or silence alert occurs. - - default-command shell-command - Set the command used for new windows (if not specified - when the window is created) to shell-command, which may - be any sh(1) command. The default is an empty string, - which instructs tmux to create a login shell using the - value of the default-shell option. - - default-path path - Set the default working directory for new panes. If - empty (the default), the working directory is determined - from the process running in the active pane, from the - command line environment or from the working directory - where the session was created. If path is "$HOME" or - "~", the value of the HOME environment variable is used. - If path is ".", the working directory when tmux was - started is used. - - default-shell path - Specify the default shell. This is used as the login - shell for new windows when the default-command option is - set to empty, and must be the full path of the exe- - cutable. When started tmux tries to set a default value - from the first suitable of the SHELL environment vari- - able, the shell returned by getpwuid(3), or /bin/sh. - This option should be configured when tmux is used as a - login shell. - - default-terminal terminal - Set the default terminal for new windows created in this - session - the default value of the TERM environment vari- - able. For tmux to work correctly, this must be set to - 'screen' or a derivative of it. - - destroy-unattached [on | off] - If enabled and the session is no longer attached to any - clients, it is destroyed. - - detach-on-destroy [on | off] - If on (the default), the client is detached when the ses- - sion it is attached to is destroyed. If off, the client - is switched to the most recently active of the remaining - sessions. - - display-panes-active-colour colour - Set the colour used by the display-panes command to show - the indicator for the active pane. - - display-panes-colour colour - Set the colour used by the display-panes command to show - the indicators for inactive panes. - - display-panes-time time - Set the time in milliseconds for which the indicators - shown by the display-panes command appear. - - display-time time - Set the amount of time for which status line messages and - other on-screen indicators are displayed. time is in - milliseconds. - - history-limit lines - Set the maximum number of lines held in window history. - This setting applies only to new windows - existing win- - dow histories are not resized and retain the limit at the - point they were created. - - lock-after-time number - Lock the session (like the lock-session command) after - number seconds of inactivity, or the entire server (all - sessions) if the lock-server option is set. The default - is not to lock (set to 0). - - lock-command shell-command - Command to run when locking each client. The default is - to run lock(1) with -np. - - lock-server [on | off] - If this option is on (the default), instead of each ses- - sion locking individually as each has been idle for - lock-after-time, the entire server will lock after all - sessions would have locked. This has no effect as a ses- - sion option; it must be set as a global option. - - message-attr attributes - Set status line message attributes, where attributes is - either none or a comma-delimited list of one or more of: - bright (or bold), dim, underscore, blink, reverse, - hidden, or italics. - - message-bg colour - Set status line message background colour, where colour - is one of: black, red, green, yellow, blue, magenta, - cyan, white, aixterm bright variants (if supported: - brightred, brightgreen, and so on), colour0 to colour255 - from the 256-colour set, default, or a hexadecimal RGB - string such as '#ffffff', which chooses the closest match - from the default 256-colour set. - - message-command-attr attributes - Set status line message attributes when in command mode. - - message-command-bg colour - Set status line message background colour when in command - mode. - - message-command-fg colour - Set status line message foreground colour when in command - mode. - - message-fg colour - Set status line message foreground colour. - - message-limit number - Set the number of error or information messages to save - in the message log for each client. The default is 20. - - mouse-resize-pane [on | off] - If on, tmux captures the mouse and allows panes to be - resized by dragging on their borders. - - mouse-select-pane [on | off] - If on, tmux captures the mouse and when a window is split - into multiple panes the mouse may be used to select the - current pane. The mouse click is also passed through to - the application as normal. - - mouse-select-window [on | off] - If on, clicking the mouse on a window name in the status - line will select that window. - - mouse-utf8 [on | off] - If enabled, request mouse input as UTF-8 on UTF-8 termi- - nals. - - pane-active-border-bg colour - - pane-active-border-fg colour - Set the pane border colour for the currently active pane. - - pane-border-bg colour - - pane-border-fg colour - Set the pane border colour for panes aside from the - active pane. - - prefix key - Set the key accepted as a prefix key. - - prefix2 key - Set a secondary key accepted as a prefix key. - - repeat-time time - Allow multiple commands to be entered without pressing - the prefix-key again in the specified time milliseconds - (the default is 500). Whether a key repeats may be set - when it is bound using the -r flag to bind-key. Repeat - is enabled for the default keys bound to the resize-pane - command. - - set-remain-on-exit [on | off] - Set the remain-on-exit window option for any windows - first created in this session. When this option is true, - windows in which the running program has exited do not - close, instead remaining open but inactivate. Use the - respawn-window command to reactivate such a window, or - the kill-window command to destroy it. - - set-titles [on | off] - Attempt to set the client terminal title using the tsl - and fsl terminfo(5) entries if they exist. tmux automat- - ically sets these to the \e]2;...\007 sequence if the - terminal appears to be an xterm. This option is off by - default. Note that elinks will only attempt to set the - window title if the STY environment variable is set. - - set-titles-string string - String used to set the window title if set-titles is on. - Character sequences are replaced as for the status-left - option. - - status [on | off] - Show or hide the status line. - - status-attr attributes - Set status line attributes. - - status-bg colour - Set status line background colour. - - status-fg colour - Set status line foreground colour. - - status-interval interval - Update the status bar every interval seconds. By - default, updates will occur every 15 seconds. A setting - of zero disables redrawing at interval. - - status-justify [left | centre | right] - Set the position of the window list component of the sta- - tus line: left, centre or right justified. - - status-keys [vi | emacs] - Use vi or emacs-style key bindings in the status line, - for example at the command prompt. The default is emacs, - unless the VISUAL or EDITOR environment variables are set - and contain the string 'vi'. - - status-left string - Display string to the left of the status bar. string - will be passed through strftime(3) before being used. By - default, the session name is shown. string may contain - any of the following special character sequences: - - Character pair Replaced with - #(shell-command) First line of the command's - output - #[attributes] Colour or attribute change - #H Hostname of local host - #h Hostname of local host without - the domain name - #F Current window flag - #I Current window index - #P Current pane index - #S Session name - #T Current pane title - #W Current window name - ## A literal '#' - - The #(shell-command) form executes 'shell-command' and - inserts the first line of its output. Note that shell - commands are only executed once at the interval specified - by the status-interval option: if the status line is - redrawn in the meantime, the previous result is used. - Shell commands are executed with the tmux global environ- - ment set (see the ENVIRONMENT section). - - For details on how the names and titles can be set see - the NAMES AND TITLES section. - - #[attributes] allows a comma-separated list of attributes - to be specified, these may be 'fg=colour' to set the - foreground colour, 'bg=colour' to set the background - colour, the name of one of the attributes (listed under - the message-attr option) to turn an attribute on, or an - attribute prefixed with 'no' to turn one off, for example - nobright. Examples are: - - #(sysctl vm.loadavg) - #[fg=yellow,bold]#(apm -l)%%#[default] [#S] - - Where appropriate, special character sequences may be - prefixed with a number to specify the maximum length, for - example '#24T'. - - By default, UTF-8 in string is not interpreted, to enable - UTF-8, use the status-utf8 option. - - status-left-attr attributes - Set the attribute of the left part of the status line. - - status-left-bg colour - Set the background colour of the left part of the status - line. - - status-left-fg colour - Set the foreground colour of the left part of the status - line. - - status-left-length length - Set the maximum length of the left component of the sta- - tus bar. The default is 10. - - status-right string - Display string to the right of the status bar. By - default, the current window title in double quotes, the - date and the time are shown. As with status-left, string - will be passed to strftime(3), character pairs are - replaced, and UTF-8 is dependent on the status-utf8 - option. - - status-right-attr attributes - Set the attribute of the right part of the status line. - - status-right-bg colour - Set the background colour of the right part of the status - line. - - status-right-fg colour - Set the foreground colour of the right part of the status - line. - - status-right-length length - Set the maximum length of the right component of the sta- - tus bar. The default is 40. - - status-utf8 [on | off] - Instruct tmux to treat top-bit-set characters in the - status-left and status-right strings as UTF-8; notably, - this is important for wide characters. This option - defaults to off. - - terminal-overrides string - Contains a list of entries which override terminal - descriptions read using terminfo(5). string is a comma- - separated list of items each a colon-separated string - made up of a terminal type pattern (matched using - fnmatch(3)) and a set of name=value entries. - - For example, to set the 'clear' terminfo(5) entry to - '\e[H\e[2J' for all terminal types and the 'dch1' entry - to '\e[P' for the 'rxvt' terminal type, the option could - be set to the string: - - "*:clear=\e[H\e[2J,rxvt:dch1=\e[P" - - The terminal entry value is passed through strunvis(3) - before interpretation. The default value forcibly cor- - rects the 'colors' entry for terminals which support 88 - or 256 colours: - - "*88col*:colors=88,*256col*:colors=256,xterm*:XT" - - update-environment variables - Set a space-separated string containing a list of envi- - ronment variables to be copied into the session environ- - ment when a new session is created or an existing session - is attached. Any variables that do not exist in the - source environment are set to be removed from the session - environment (as if -r was given to the set-environment - command). The default is "DISPLAY SSH_ASKPASS - SSH_AUTH_SOCK SSH_AGENT_PID SSH_CONNECTION WINDOWID XAU- - THORITY". - - visual-activity [on | off] - If on, display a status line message when activity occurs - in a window for which the monitor-activity window option - is enabled. - - visual-bell [on | off] - If this option is on, a message is shown on a bell - instead of it being passed through to the terminal (which - normally makes a sound). Also see the bell-action - option. - - visual-content [on | off] - Like visual-activity, display a message when content is - present in a window for which the monitor-content window - option is enabled. - - visual-silence [on | off] - If monitor-silence is enabled, prints a message after the - interval has expired on a given window. - - word-separators string - Sets the session's conception of what characters are con- - sidered word separators, for the purposes of the next and - previous word commands in copy mode. The default is - ' -_@'. - - set-window-option [-agu] [-t target-window] option value - (alias: setw) - Set a window option. The -a, -g and -u flags work similarly to - the set-option command. - - Supported window options are: - - aggressive-resize [on | off] - Aggressively resize the chosen window. This means that - tmux will resize the window to the size of the smallest - session for which it is the current window, rather than - the smallest session to which it is attached. The window - may resize when the current window is changed on another - sessions; this option is good for full-screen programs - which support SIGWINCH and poor for interactive programs - such as shells. - - allow-rename [on | off] - Allow programs to change the window name using a terminal - escape sequence (\033k...\033\\). The default is on. - - alternate-screen [on | off] - This option configures whether programs running inside - tmux may use the terminal alternate screen feature, which - allows the smcup and rmcup terminfo(5) capabilities. The - alternate screen feature preserves the contents of the - window when an interactive application starts and - restores it on exit, so that any output visible before - the application starts reappears unchanged after it - exits. The default is on. - - automatic-rename [on | off] - Control automatic window renaming. When this setting is - enabled, tmux will attempt - on supported platforms - to - rename the window to reflect the command currently run- - ning in it. This flag is automatically disabled for an - individual window when a name is specified at creation - with new-window or new-session, or later with - rename-window, or with a terminal escape sequence. It - may be switched off globally with: - - set-window-option -g automatic-rename off - - clock-mode-colour colour - Set clock colour. - - clock-mode-style [12 | 24] - Set clock hour format. - - force-height height - force-width width - Prevent tmux from resizing a window to greater than width - or height. A value of zero restores the default unlim- - ited setting. - - main-pane-height height - main-pane-width width - Set the width or height of the main (left or top) pane in - the main-horizontal or main-vertical layouts. - - mode-attr attributes - Set window modes attributes. - - mode-bg colour - Set window modes background colour. - - mode-fg colour - Set window modes foreground colour. - - mode-keys [vi | emacs] - Use vi or emacs-style key bindings in copy and choice - modes. As with the status-keys option, the default is - emacs, unless VISUAL or EDITOR contains 'vi'. - - mode-mouse [on | off | copy-mode] - Mouse state in modes. If on, the mouse may be used to - enter copy mode and copy a selection by dragging, to - enter copy mode and scroll with the mouse wheel, or to - select an option in choice mode. If set to copy-mode, - the mouse behaves as set to on, but cannot be used to - enter copy mode. - - monitor-activity [on | off] - Monitor for activity in the window. Windows with activ- - ity are highlighted in the status line. - - monitor-content match-string - Monitor content in the window. When fnmatch(3) pattern - match-string appears in the window, it is highlighted in - the status line. - - monitor-silence [interval] - Monitor for silence (no activity) in the window within - interval seconds. Windows that have been silent for the - interval are highlighted in the status line. An interval - of zero disables the monitoring. - - other-pane-height height - Set the height of the other panes (not the main pane) in - the main-horizontal layout. If this option is set to 0 - (the default), it will have no effect. If both the - main-pane-height and other-pane-height options are set, - the main pane will grow taller to make the other panes - the specified height, but will never shrink to do so. - - other-pane-width width - Like other-pane-height, but set the width of other panes - in the main-vertical layout. - - pane-base-index index - Like base-index, but set the starting index for pane num- - bers. - - remain-on-exit [on | off] - A window with this flag set is not destroyed when the - program running in it exits. The window may be reacti- - vated with the respawn-window command. - - synchronize-panes [on | off] - Duplicate input to any pane to all other panes in the - same window (only for panes that are not in any special - mode). - - utf8 [on | off] - Instructs tmux to expect UTF-8 sequences to appear in - this window. - - window-status-bell-attr attributes - Set status line attributes for windows which have a bell - alert. - - window-status-bell-bg colour - Set status line background colour for windows with a bell - alert. - - window-status-bell-fg colour - Set status line foreground colour for windows with a bell - alert. - - window-status-content-attr attributes - Set status line attributes for windows which have a con- - tent alert. - - window-status-content-bg colour - Set status line background colour for windows with a con- - tent alert. - - window-status-content-fg colour - Set status line foreground colour for windows with a con- - tent alert. - - window-status-activity-attr attributes - Set status line attributes for windows which have an - activity (or silence) alert. - - window-status-activity-bg colour - Set status line background colour for windows with an - activity alert. - - window-status-activity-fg colour - Set status line foreground colour for windows with an - activity alert. - - window-status-attr attributes - Set status line attributes for a single window. - - window-status-bg colour - Set status line background colour for a single window. - - window-status-current-attr attributes - Set status line attributes for the currently active win- - dow. - - window-status-current-bg colour - Set status line background colour for the currently - active window. - - window-status-current-fg colour - Set status line foreground colour for the currently - active window. - - window-status-current-format string - Like window-status-format, but is the format used when - the window is the current window. - - window-status-fg colour - Set status line foreground colour for a single window. - - window-status-format string - Set the format in which the window is displayed in the - status line window list. See the status-left option for - details of special character sequences available. The - default is '#I:#W#F'. - - xterm-keys [on | off] - If this option is set, tmux will generate xterm(1) -style - function key sequences; these have a number included to - indicate modifiers such as Shift, Alt or Ctrl. The - default is off. - - show-options [-gsw] [-t target-session | target-window] - (alias: show) - Show the window options with -w (equivalent to - show-window-options), the server options with -s, otherwise the - session options for target session. Global session or window - options are listed if -g is used. - - show-window-options [-g] [-t target-window] - (alias: showw) - List the window options for target-window, or the global window - options if -g is used. - -FORMATS - The list-clients, list-sessions, list-windows and list-panes commands - accept the -F flag with a format argument. This is a string which con- - trols the output format of the command. Special character sequences are - replaced as documented under the status-left option and an additional - long form is accepted. Replacement variables are enclosed in '#{' and - '}', for example '#{session_name}' is equivalent to '#S'. Conditionals - are also accepted by prefixing with '?' and separating two alternatives - with a comma; if the specified variable exists and is not zero, the first - alternative is chosen, otherwise the second is used. For example - '#{?session_attached,attached,not attached}' will include the string - 'attached' if the session is attached and the string 'not attached' if it - is unattached. - - The following variables are available, where appropriate: - - Variable name Replaced with - client_activity Integer time client last had activity - client_activity_string String time client last had activity - client_created Integer time client created - client_created_string String time client created - client_cwd Working directory of client - client_height Height of client - client_readonly 1 if client is readonly - client_termname Terminal name of client - client_tty Pseudo terminal of client - client_utf8 1 if client supports utf8 - client_width Width of client - host Hostname of local host - line Line number in the list - pane_active 1 if active pane - pane_dead 1 if pane is dead - pane_height Height of pane - pane_id Unique pane id - pane_pid PID of first process in pane - pane_start_command Command pane started with - pane_start_path Path pane started with - pane_title Title of pane - pane_tty Pseudo terminal of pane - pane_width Width of pane - session_attached 1 if session attached - session_created Integer time session created - session_created_string String time session created - session_group Number of session group - session_grouped 1 if session in a group - session_height Height of session - session_name Name of session - session_width Width of session - session_windows Number of windows in session - window_active 1 if window active - window_flags Window flags - window_height Height of window - window_index Index of window - window_layout Window layout description - window_name Name of window - window_width Width of window - -NAMES AND TITLES - tmux distinguishes between names and titles. Windows and sessions have - names, which may be used to specify them in targets and are displayed in - the status line and various lists: the name is the tmux identifier for a - window or session. Only panes have titles. A pane's title is typically - set by the program running inside the pane and is not modified by tmux. - It is the same mechanism used to set for example the xterm(1) window - title in an X(7) window manager. Windows themselves do not have titles - - a window's title is the title of its active pane. tmux itself may set - the title of the terminal in which the client is running, see the - set-titles option. - - A session's name is set with the new-session and rename-session commands. - A window's name is set with one of: - - 1. A command argument (such as -n for new-window or new-session). - - 2. An escape sequence: - - $ printf '\033kWINDOW_NAME\033\\' - - 3. Automatic renaming, which sets the name to the active command in - the window's active pane. See the automatic-rename option. - - When a pane is first created, its title is the hostname. A pane's title - can be set via the OSC title setting sequence, for example: - - $ printf '\033]2;My Title\033\\' - -ENVIRONMENT - When the server is started, tmux copies the environment into the global - environment; in addition, each session has a session environment. When a - window is created, the session and global environments are merged. If a - variable exists in both, the value from the session environment is used. - The result is the initial environment passed to the new process. - - The update-environment session option may be used to update the session - environment from the client when a new session is created or an old reat- - tached. tmux also initialises the TMUX variable with some internal - information to allow commands to be executed from inside, and the TERM - variable with the correct terminal setting of 'screen'. - - Commands to alter and view the environment are: - - set-environment [-gru] [-t target-session] name [value] - (alias: setenv) - Set or unset an environment variable. If -g is used, the change - is made in the global environment; otherwise, it is applied to - the session environment for target-session. The -u flag unsets a - variable. -r indicates the variable is to be removed from the - environment before starting a new process. - - show-environment [-g] [-t target-session] - (alias: showenv) - Display the environment for target-session or the global environ- - ment with -g. Variables removed from the environment are pre- - fixed with '-'. - -STATUS LINE - tmux includes an optional status line which is displayed in the bottom - line of each terminal. By default, the status line is enabled (it may be - disabled with the status session option) and contains, from left-to- - right: the name of the current session in square brackets; the window - list; the title of the active pane in double quotes; and the time and - date. - - The status line is made of three parts: configurable left and right sec- - tions (which may contain dynamic content such as the time or output from - a shell command, see the status-left, status-left-length, status-right, - and status-right-length options below), and a central window list. By - default, the window list shows the index, name and (if any) flag of the - windows present in the current session in ascending numerical order. It - may be customised with the window-status-format and - window-status-current-format options. The flag is one of the following - symbols appended to the window name: - - Symbol Meaning - * Denotes the current window. - - Marks the last window (previously selected). - # Window is monitored and activity has been detected. - ! A bell has occurred in the window. - + Window is monitored for content and it has appeared. - ~ The window has been silent for the monitor-silence - interval. - - The # symbol relates to the monitor-activity and + to the monitor-content - window options. The window name is printed in inverted colours if an - alert (bell, activity or content) is present. - - The colour and attributes of the status line may be configured, the - entire status line using the status-attr, status-fg and status-bg session - options and individual windows using the window-status-attr, - window-status-fg and window-status-bg window options. - - The status line is automatically refreshed at interval if it has changed, - the interval may be controlled with the status-interval session option. - - Commands related to the status line are as follows: - - command-prompt [-I inputs] [-p prompts] [-t target-client] [template] - Open the command prompt in a client. This may be used from - inside tmux to execute commands interactively. - - If template is specified, it is used as the command. If present, - -I is a comma-separated list of the initial text for each prompt. - If -p is given, prompts is a comma-separated list of prompts - which are displayed in order; otherwise a single prompt is dis- - played, constructed from template if it is present, or ':' if - not. - - Both inputs and prompts may contain the special character - sequences supported by the status-left option. - - Before the command is executed, the first occurrence of the - string '%%' and all occurrences of '%1' are replaced by the - response to the first prompt, the second '%%' and all '%2' are - replaced with the response to the second prompt, and so on for - further prompts. Up to nine prompt responses may be replaced - ('%1' to '%9'). - - confirm-before [-p prompt] [-t target-client] command - (alias: confirm) - Ask for confirmation before executing command. If -p is given, - prompt is the prompt to display; otherwise a prompt is con- - structed from command. It may contain the special character - sequences supported by the status-left option. - - This command works only from inside tmux. - - display-message [-p] [-c target-client] [-t target-pane] [message] - (alias: display) - Display a message. If -p is given, the output is printed to std- - out, otherwise it is displayed in the target-client status line. - The format of message is as for status-left, with the exception - that #() are not handled; information is taken from target-pane - if -t is given, otherwise the active pane for the session - attached to target-client. - -BUFFERS - tmux maintains a stack of paste buffers. Up to the value of the - buffer-limit option are kept; when a new buffer is added, the buffer at - the bottom of the stack is removed. Buffers may be added using copy-mode - or the set-buffer command, and pasted into a window using the - paste-buffer command. - - A configurable history buffer is also maintained for each window. By - default, up to 2000 lines are kept; this can be altered with the - history-limit option (see the set-option command above). - - The buffer commands are as follows: - - choose-buffer [-t target-window] [template] - Put a window into buffer choice mode, where a buffer may be cho- - sen interactively from a list. After a buffer is selected, '%%' - is replaced by the buffer index in template and the result exe- - cuted as a command. If template is not given, "paste-buffer -b - '%%'" is used. This command works only from inside tmux. - - clear-history [-t target-pane] - (alias: clearhist) - Remove and free the history for the specified pane. - - delete-buffer [-b buffer-index] - (alias: deleteb) - Delete the buffer at buffer-index, or the top buffer if not spec- - ified. - - list-buffers - (alias: lsb) - List the global buffers. - - load-buffer [-b buffer-index] path - (alias: loadb) - Load the contents of the specified paste buffer from path. - - paste-buffer [-dr] [-b buffer-index] [-s separator] [-t target-pane] - (alias: pasteb) - Insert the contents of a paste buffer into the specified pane. - If not specified, paste into the current one. With -d, also - delete the paste buffer from the stack. When output, any line- - feed (LF) characters in the paste buffer are replaced with a sep- - arator, by default carriage return (CR). A custom separator may - be specified using the -s flag. The -r flag means to do no - replacement (equivalent to a separator of LF). - - save-buffer [-a] [-b buffer-index] path - (alias: saveb) - Save the contents of the specified paste buffer to path. The -a - option appends to rather than overwriting the file. - - set-buffer [-b buffer-index] data - (alias: setb) - Set the contents of the specified buffer to data. - - show-buffer [-b buffer-index] - (alias: showb) - Display the contents of the specified buffer. - -MISCELLANEOUS - Miscellaneous commands are as follows: - - clock-mode [-t target-pane] - Display a large clock. - - if-shell shell-command command [command] - (alias: if) - Execute the first command if shell-command returns success or the - second command otherwise. - - lock-server - (alias: lock) - Lock each client individually by running the command specified by - the lock-command option. - - run-shell shell-command - (alias: run) - Execute shell-command in the background without creating a win- - dow. After it finishes, any output to stdout is displayed in - copy mode. If the command doesn't return success, the exit sta- - tus is also displayed. - - server-info - (alias: info) - Show server information and terminal details. - -TERMINFO EXTENSIONS - tmux understands some extensions to terminfo(5): - - Cc, Cr Set the cursor colour. The first takes a single string argument - and is used to set the colour; the second takes no arguments and - restores the default cursor colour. If set, a sequence such as - this may be used to change the cursor colour from inside tmux: - - $ printf '\033]12;red\033\\' - - Cs, Csr - Change the cursor style. If set, a sequence such as this may be - used to change the cursor to an underline: - - $ printf '\033[4 q' - - If Csr is set, it will be used to reset the cursor style instead - of Cs. - - Ms This sequence can be used by tmux to store the current buffer in - the host terminal's selection (clipboard). See the set-clipboard - option above and the xterm(1) man page. - -FILES - ~/.tmux.conf Default tmux configuration file. - /etc/tmux.conf System-wide configuration file. - -EXAMPLES - To create a new tmux session running vi(1): - - $ tmux new-session vi - - Most commands have a shorter form, known as an alias. For new-session, - this is new: - - $ tmux new vi - - Alternatively, the shortest unambiguous form of a command is accepted. - If there are several options, they are listed: - - $ tmux n - ambiguous command: n, could be: new-session, new-window, next-window - - Within an active session, a new window may be created by typing 'C-b c' - (Ctrl followed by the 'b' key followed by the 'c' key). - - Windows may be navigated with: 'C-b 0' (to select window 0), 'C-b 1' (to - select window 1), and so on; 'C-b n' to select the next window; and 'C-b - p' to select the previous window. - - A session may be detached using 'C-b d' (or by an external event such as - ssh(1) disconnection) and reattached with: - - $ tmux attach-session - - Typing 'C-b ?' lists the current key bindings in the current window; up - and down may be used to navigate the list or 'q' to exit from it. - - Commands to be run when the tmux server is started may be placed in the - ~/.tmux.conf configuration file. Common examples include: - - Changing the default prefix key: - - set-option -g prefix C-a - unbind-key C-b - bind-key C-a send-prefix - - Turning the status line off, or changing its colour: - - set-option -g status off - set-option -g status-bg blue - - Setting other options, such as the default command, or locking after 30 - minutes of inactivity: - - set-option -g default-command "exec /bin/ksh" - set-option -g lock-after-time 1800 - - Creating new key bindings: - - bind-key b set-option status - bind-key / command-prompt "split-window 'exec man %%'" - bind-key S command-prompt "new-window -n %1 'ssh %1'" - -SEE ALSO - pty(4) - -AUTHORS - Nicholas Marriott - -BSD October 19, 2013 BSD diff --git a/manual/1.7 b/manual/1.7 deleted file mode 100644 index 1a7164201b2..00000000000 --- a/manual/1.7 +++ /dev/null @@ -1,2010 +0,0 @@ -TMUX(1) BSD General Commands Manual TMUX(1) - -NAME - tmux -- terminal multiplexer - -SYNOPSIS - tmux [-28lquvV] [-c shell-command] [-f file] [-L socket-name] - [-S socket-path] [command [flags]] - -DESCRIPTION - tmux is a terminal multiplexer: it enables a number of terminals to be - created, accessed, and controlled from a single screen. tmux may be - detached from a screen and continue running in the background, then later - reattached. - - When tmux is started it creates a new session with a single window and - displays it on screen. A status line at the bottom of the screen shows - information on the current session and is used to enter interactive com- - mands. - - A session is a single collection of pseudo terminals under the management - of tmux. Each session has one or more windows linked to it. A window - occupies the entire screen and may be split into rectangular panes, each - of which is a separate pseudo terminal (the pty(4) manual page documents - the technical details of pseudo terminals). Any number of tmux instances - may connect to the same session, and any number of windows may be present - in the same session. Once all sessions are killed, tmux exits. - - Each session is persistent and will survive accidental disconnection - (such as ssh(1) connection timeout) or intentional detaching (with the - 'C-b d' key strokes). tmux may be reattached using: - - $ tmux attach - - In tmux, a session is displayed on screen by a client and all sessions - are managed by a single server. The server and each client are separate - processes which communicate through a socket in /tmp. - - The options are as follows: - - -2 Force tmux to assume the terminal supports 256 colours. - - -8 Like -2, but indicates that the terminal supports 88 - colours. - - -c shell-command - Execute shell-command using the default shell. If neces- - sary, the tmux server will be started to retrieve the - default-shell option. This option is for compatibility - with sh(1) when tmux is used as a login shell. - - -f file Specify an alternative configuration file. By default, - tmux loads the system configuration file from - /etc/tmux.conf, if present, then looks for a user configu- - ration file at ~/.tmux.conf. The configuration file is a - set of tmux commands which are executed in sequence when - the server is first started. - - If a command in the configuration file fails, tmux will - report an error and exit without executing further com- - mands. - - -L socket-name - tmux stores the server socket in a directory under /tmp (or - TMPDIR if set); the default socket is named default. This - option allows a different socket name to be specified, - allowing several independent tmux servers to be run. - Unlike -S a full path is not necessary: the sockets are all - created in the same directory. - - If the socket is accidentally removed, the SIGUSR1 signal - may be sent to the tmux server process to recreate it. - - -l Behave as a login shell. This flag currently has no effect - and is for compatibility with other shells when using tmux - as a login shell. - - -q Set the quiet server option to prevent the server sending - various informational messages. - - -S socket-path - Specify a full alternative path to the server socket. If - -S is specified, the default socket directory is not used - and any -L flag is ignored. - - -u tmux attempts to guess if the terminal is likely to support - UTF-8 by checking the first of the LC_ALL, LC_CTYPE and - LANG environment variables to be set for the string - "UTF-8". This is not always correct: the -u flag explic- - itly informs tmux that UTF-8 is supported. - - If the server is started from a client passed -u or where - UTF-8 is detected, the utf8 and status-utf8 options are - enabled in the global window and session options respec- - tively. - - -v Request verbose logging. This option may be specified mul- - tiple times for increasing verbosity. Log messages will be - saved into tmux-client-PID.log and tmux-server-PID.log - files in the current directory, where PID is the PID of the - server or client process. - - -V Report the tmux version. - - command [flags] - This specifies one of a set of commands used to control - tmux, as described in the following sections. If no com- - mands are specified, the new-session command is assumed. - -KEY BINDINGS - tmux may be controlled from an attached client by using a key combination - of a prefix key, 'C-b' (Ctrl-b) by default, followed by a command key. - - The default command key bindings are: - - C-b Send the prefix key (C-b) through to the application. - C-o Rotate the panes in the current window forwards. - C-z Suspend the tmux client. - ! Break the current pane out of the window. - " Split the current pane into two, top and bottom. - # List all paste buffers. - $ Rename the current session. - % Split the current pane into two, left and right. - & Kill the current window. - ' Prompt for a window index to select. - , Rename the current window. - - Delete the most recently copied buffer of text. - . Prompt for an index to move the current window. - 0 to 9 Select windows 0 to 9. - : Enter the tmux command prompt. - ; Move to the previously active pane. - = Choose which buffer to paste interactively from a list. - ? List all key bindings. - D Choose a client to detach. - [ Enter copy mode to copy text or view the history. - ] Paste the most recently copied buffer of text. - c Create a new window. - d Detach the current client. - f Prompt to search for text in open windows. - i Display some information about the current window. - l Move to the previously selected window. - n Change to the next window. - o Select the next pane in the current window. - p Change to the previous window. - q Briefly display pane indexes. - r Force redraw of the attached client. - s Select a new session for the attached client interac- - tively. - L Switch the attached client back to the last session. - t Show the time. - w Choose the current window interactively. - x Kill the current pane. - { Swap the current pane with the previous pane. - } Swap the current pane with the next pane. - ~ Show previous messages from tmux, if any. - Page Up Enter copy mode and scroll one page up. - Up, Down - Left, Right - Change to the pane above, below, to the left, or to the - right of the current pane. - M-1 to M-5 Arrange panes in one of the five preset layouts: even- - horizontal, even-vertical, main-horizontal, main-verti- - cal, or tiled. - M-n Move to the next window with a bell or activity marker. - M-o Rotate the panes in the current window backwards. - M-p Move to the previous window with a bell or activity - marker. - C-Up, C-Down - C-Left, C-Right - Resize the current pane in steps of one cell. - M-Up, M-Down - M-Left, M-Right - Resize the current pane in steps of five cells. - - Key bindings may be changed with the bind-key and unbind-key commands. - -COMMANDS - This section contains a list of the commands supported by tmux. Most - commands accept the optional -t argument with one of target-client, - target-session target-window, or target-pane. These specify the client, - session, window or pane which a command should affect. target-client is - the name of the pty(4) file to which the client is connected, for example - either of /dev/ttyp1 or ttyp1 for the client attached to /dev/ttyp1. If - no client is specified, the current client is chosen, if possible, or an - error is reported. Clients may be listed with the list-clients command. - - target-session is either the name of a session (as listed by the - list-sessions command) or the name of a client with the same syntax as - target-client, in which case the session attached to the client is used. - When looking for the session name, tmux initially searches for an exact - match; if none is found, the session names are checked for any for which - target-session is a prefix or for which it matches as an fnmatch(3) pat- - tern. If a single match is found, it is used as the target session; mul- - tiple matches produce an error. If a session is omitted, the current - session is used if available; if no current session is available, the - most recently used is chosen. - - target-window specifies a window in the form session:window. session - follows the same rules as for target-session, and window is looked for in - order: as a window index, for example mysession:1; as a window ID, such - as @1; as an exact window name, such as mysession:mywindow; then as an - fnmatch(3) pattern or the start of a window name, such as myses- - sion:mywin* or mysession:mywin. An empty window name specifies the next - unused index if appropriate (for example the new-window and link-window - commands) otherwise the current window in session is chosen. The special - character '!' uses the last (previously current) window, or '+' and '-' - are the next window or the previous window by number. When the argument - does not contain a colon, tmux first attempts to parse it as window; if - that fails, an attempt is made to match a session. - - target-pane takes a similar form to target-window but with the optional - addition of a period followed by a pane index, for example: myses- - sion:mywindow.1. If the pane index is omitted, the currently active pane - in the specified window is used. If neither a colon nor period appears, - tmux first attempts to use the argument as a pane index; if that fails, - it is looked up as for target-window. A '+' or '-' indicate the next or - previous pane index, respectively. One of the strings top, bottom, left, - right, top-left, top-right, bottom-left or bottom-right may be used - instead of a pane index. - - The special characters '+' and '-' may be followed by an offset, for - example: - - select-window -t:+2 - - When dealing with a session that doesn't contain sequential window - indexes, they will be correctly skipped. - - tmux also gives each pane created in a server an identifier consisting of - a '%' and a number, starting from zero. A pane's identifier is unique - for the life of the tmux server and is passed to the child process of the - pane in the TMUX_PANE environment variable. It may be used alone to tar- - get a pane or the window containing it. - - shell-command arguments are sh(1) commands. These must be passed as a - single item, which typically means quoting them, for example: - - new-window 'vi /etc/passwd' - - command [arguments] refers to a tmux command, passed with the command and - arguments separately, for example: - - bind-key F1 set-window-option force-width 81 - - Or if using sh(1): - - $ tmux bind-key F1 set-window-option force-width 81 - - Multiple commands may be specified together as part of a command - sequence. Each command should be separated by spaces and a semicolon; - commands are executed sequentially from left to right and lines ending - with a backslash continue on to the next line, except when escaped by - another backslash. A literal semicolon may be included by escaping it - with a backslash (for example, when specifying a command sequence to - bind-key). - - Example tmux commands include: - - refresh-client -t/dev/ttyp2 - - rename-session -tfirst newname - - set-window-option -t:0 monitor-activity on - - new-window ; split-window -d - - bind-key R source-file ~/.tmux.conf \; \ - display-message "source-file done" - - Or from sh(1): - - $ tmux kill-window -t :1 - - $ tmux new-window \; split-window -d - - $ tmux new-session -d 'vi /etc/passwd' \; split-window -d \; attach - -CLIENTS AND SESSIONS - The tmux server manages clients, sessions, windows and panes. Clients - are attached to sessions to interact with them, either when they are cre- - ated with the new-session command, or later with the attach-session com- - mand. Each session has one or more windows linked into it. Windows may - be linked to multiple sessions and are made up of one or more panes, each - of which contains a pseudo terminal. Commands for creating, linking and - otherwise manipulating windows are covered in the WINDOWS AND PANES sec- - tion. - - The following commands are available to manage clients and sessions: - - attach-session [-dr] [-t target-session] - (alias: attach) - If run from outside tmux, create a new client in the current ter- - minal and attach it to target-session. If used from inside, - switch the current client. If -d is specified, any other clients - attached to the session are detached. -r signifies the client is - read-only (only keys bound to the detach-client or switch-client - commands have any effect) - - If no server is started, attach-session will attempt to start it; - this will fail unless sessions are created in the configuration - file. - - The target-session rules for attach-session are slightly - adjusted: if tmux needs to select the most recently used session, - it will prefer the most recently used unattached session. - - detach-client [-P] [-a] [-s target-session] [-t target-client] - (alias: detach) - Detach the current client if bound to a key, the client specified - with -t, or all clients currently attached to the session speci- - fied by -s. The -a option kills all but the client given with - -t. If -P is given, send SIGHUP to the parent process of the - client, typically causing it to exit. - - has-session [-t target-session] - (alias: has) - Report an error and exit with 1 if the specified session does not - exist. If it does exist, exit with 0. - - kill-server - Kill the tmux server and clients and destroy all sessions. - - kill-session - [-a] [-t target-session] Destroy the given session, closing any - windows linked to it and no other sessions, and detaching all - clients attached to it. If -a is given, all sessions but the - specified one is killed. - - list-clients [-F format] [-t target-session] - (alias: lsc) - List all clients attached to the server. For the meaning of the - -F flag, see the FORMATS section. If target-session is speci- - fied, list only clients connected to that session. - - list-commands - (alias: lscm) - List the syntax of all commands supported by tmux. - - list-sessions [-F format] - (alias: ls) - List all sessions managed by the server. For the meaning of the - -F flag, see the FORMATS section. - - lock-client [-t target-client] - (alias: lockc) - Lock target-client, see the lock-server command. - - lock-session [-t target-session] - (alias: locks) - Lock all clients attached to target-session. - - new-session [-d] [-n window-name] [-s session-name] [-t target-session] - [-x width] [-y height] [shell-command] - (alias: new) - Create a new session with name session-name. - - The new session is attached to the current terminal unless -d is - given. window-name and shell-command are the name of and shell - command to execute in the initial window. If -d is used, -x and - -y specify the size of the initial window (80 by 24 if not - given). - - If run from a terminal, any termios(4) special characters are - saved and used for new windows in the new session. - - If -t is given, the new session is grouped with target-session. - This means they share the same set of windows - all windows from - target-session are linked to the new session and any subsequent - new windows or windows being closed are applied to both sessions. - The current and previous window and any session options remain - independent and either session may be killed without affecting - the other. Giving -n or shell-command are invalid if -t is used. - - refresh-client [-S] [-t target-client] - (alias: refresh) - Refresh the current client if bound to a key, or a single client - if one is given with -t. If -S is specified, only update the - client's status bar. - - rename-session [-t target-session] new-name - (alias: rename) - Rename the session to new-name. - - show-messages [-t target-client] - (alias: showmsgs) - Any messages displayed on the status line are saved in a per- - client message log, up to a maximum of the limit set by the - message-limit session option for the session attached to that - client. This command displays the log for target-client. - - source-file path - (alias: source) - Execute commands from path. - - start-server - (alias: start) - Start the tmux server, if not already running, without creating - any sessions. - - suspend-client [-t target-client] - (alias: suspendc) - Suspend a client by sending SIGTSTP (tty stop). - - switch-client [-lnpr] [-c target-client] [-t target-session] - (alias: switchc) - Switch the current session for client target-client to - target-session. If -l, -n or -p is used, the client is moved to - the last, next or previous session respectively. -r toggles - whether a client is read-only (see the attach-session command). - -WINDOWS AND PANES - A tmux window may be in one of several modes. The default permits direct - access to the terminal attached to the window. The other is copy mode, - which permits a section of a window or its history to be copied to a - paste buffer for later insertion into another window. This mode is - entered with the copy-mode command, bound to '[' by default. It is also - entered when a command that produces output, such as list-keys, is exe- - cuted from a key binding. - - The keys available depend on whether emacs or vi mode is selected (see - the mode-keys option). The following keys are supported as appropriate - for the mode: - - Function vi emacs - Back to indentation ^ M-m - Bottom of history G M-< - Clear selection Escape C-g - Copy selection Enter M-w - Cursor down j Down - Cursor left h Left - Cursor right l Right - Cursor to bottom line L - Cursor to middle line M M-r - Cursor to top line H M-R - Cursor up k Up - Delete entire line d C-u - Delete/Copy to end of line D C-k - End of line $ C-e - Go to line : g - Half page down C-d M-Down - Half page up C-u M-Up - Jump forward f f - Jump to forward t - Jump backward F F - Jump to backward T - Jump again ; ; - Jump again in reverse , , - Next page C-f Page down - Next space W - Next space, end of word E - Next word w - Next word end e M-f - Paste buffer p C-y - Previous page C-b Page up - Previous word b M-b - Previous space B - Quit mode q Escape - Rectangle toggle v R - Scroll down C-Down or C-e C-Down - Scroll up C-Up or C-y C-Up - Search again n n - Search again in reverse N N - Search backward ? C-r - Search forward / C-s - Start of line 0 C-a - Start selection Space C-Space - Top of history g M-> - Transpose chars C-t - - The next and previous word keys use space and the '-', '_' and '@' char- - acters as word delimiters by default, but this can be adjusted by setting - the word-separators session option. Next word moves to the start of the - next word, next word end to the end of the next word and previous word to - the start of the previous word. The three next and previous space keys - work similarly but use a space alone as the word separator. - - The jump commands enable quick movement within a line. For instance, - typing 'f' followed by '/' will move the cursor to the next '/' character - on the current line. A ';' will then jump to the next occurrence. - - Commands in copy mode may be prefaced by an optional repeat count. With - vi key bindings, a prefix is entered using the number keys; with emacs, - the Alt (meta) key and a number begins prefix entry. For example, to - move the cursor forward by ten words, use 'M-1 0 M-f' in emacs mode, and - '10w' in vi. - - When copying the selection, the repeat count indicates the buffer index - to replace, if used. - - Mode key bindings are defined in a set of named tables: vi-edit and - emacs-edit for keys used when line editing at the command prompt; - vi-choice and emacs-choice for keys used when choosing from lists (such - as produced by the choose-window command); and vi-copy and emacs-copy - used in copy mode. The tables may be viewed with the list-keys command - and keys modified or removed with bind-key and unbind-key. - - The paste buffer key pastes the first line from the top paste buffer on - the stack. - - The synopsis for the copy-mode command is: - - copy-mode [-u] [-t target-pane] - Enter copy mode. The -u option scrolls one page up. - - Each window displayed by tmux may be split into one or more panes; each - pane takes up a certain area of the display and is a separate terminal. - A window may be split into panes using the split-window command. Windows - may be split horizontally (with the -h flag) or vertically. Panes may be - resized with the resize-pane command (bound to 'C-up', 'C-down' 'C-left' - and 'C-right' by default), the current pane may be changed with the - select-pane command and the rotate-window and swap-pane commands may be - used to swap panes without changing their position. Panes are numbered - beginning from zero in the order they are created. - - A number of preset layouts are available. These may be selected with the - select-layout command or cycled with next-layout (bound to 'Space' by - default); once a layout is chosen, panes within it may be moved and - resized as normal. - - The following layouts are supported: - - even-horizontal - Panes are spread out evenly from left to right across the window. - - even-vertical - Panes are spread evenly from top to bottom. - - main-horizontal - A large (main) pane is shown at the top of the window and the - remaining panes are spread from left to right in the leftover - space at the bottom. Use the main-pane-height window option to - specify the height of the top pane. - - main-vertical - Similar to main-horizontal but the large pane is placed on the - left and the others spread from top to bottom along the right. - See the main-pane-width window option. - - tiled Panes are spread out as evenly as possible over the window in - both rows and columns. - - In addition, select-layout may be used to apply a previously used layout - - the list-windows command displays the layout of each window in a form - suitable for use with select-layout. For example: - - $ tmux list-windows - 0: ksh [159x48] - layout: bb62,159x48,0,0{79x48,0,0,79x48,80,0} - $ tmux select-layout bb62,159x48,0,0{79x48,0,0,79x48,80,0} - - tmux automatically adjusts the size of the layout for the current window - size. Note that a layout cannot be applied to a window with more panes - than that from which the layout was originally defined. - - Commands related to windows and panes are as follows: - - break-pane [-dP] [-F format] [-t target-pane] - (alias: breakp) - Break target-pane off from its containing window to make it the - only pane in a new window. If -d is given, the new window does - not become the current window. The -P option prints information - about the new window after it has been created. By default, it - uses the format '#{session_name}:#{window_index}' but a different - format may be specified with -F. - - capture-pane [-b buffer-index] [-E end-line] [-S start-line] [-t - target-pane] - (alias: capturep) - Capture the contents of a pane to the specified buffer, or a new - buffer if none is specified. - - -S and -E specify the starting and ending line numbers, zero is - the first line of the visible pane and negative numbers are lines - in the history. The default is to capture only the visible con- - tents of the pane. - - choose-client [-F format] [-t target-window] [template] - Put a window into client choice mode, allowing a client to be - selected interactively from a list. After a client is chosen, - '%%' is replaced by the client pty(4) path in template and the - result executed as a command. If template is not given, "detach- - client -t '%%'" is used. For the meaning of the -F flag, see the - FORMATS section. This command works only from inside tmux. - - choose-list [-l items] [-t target-window] [template] - Put a window into list choice mode, allowing items to be - selected. items can be a comma-separated list to display more - than one item. If an item has spaces, that entry must be quoted. - After an item is chosen, '%%' is replaced by the chosen item in - the template and the result is executed as a command. If - template is not given, "run-shell '%%'" is used. items also - accepts format specifiers. For the meaning of this see the - FORMATS section. This command works only from inside tmux. - - choose-session [-F format] [-t target-window] [template] - Put a window into session choice mode, where a session may be - selected interactively from a list. When one is chosen, '%%' is - replaced by the session name in template and the result executed - as a command. If template is not given, "switch-client -t '%%'" - is used. For the meaning of the -F flag, see the FORMATS sec- - tion. This command works only from inside tmux. - - choose-tree [-s] [-w] [-b session-template] [-c window-template] [-S - format] [-W format] [-t target-window] - Put a window into tree choice mode, where either sessions or win- - dows may be selected interactively from a list. By default, win- - dows belonging to a session are indented to show their relation- - ship to a session. - - Note that the choose-window and choose-session commands are wrap- - pers around choose-tree. - - If -s is given, will show sessions. If -w is given, will show - windows. If -b is given, will override the default session com- - mand. Note that '%%' can be used, and will be replaced with the - session name. The default option if not specified is "switch- - client -t '%%'". If -c is given, will override the default win- - dow command. Note that '%%' can be used, and will be replaced - with the session name and window index. This command will run - session-template before it. If -S is given will display the - specified format instead of the default session format. If -W is - given will display the specified format instead of the default - window format. For the meaning of the -s and -w options, see the - FORMATS section. This command only works from inside tmux. - - choose-window [-F format] [-t target-window] [template] - Put a window into window choice mode, where a window may be cho- - sen interactively from a list. After a window is selected, '%%' - is replaced by the session name and window index in template and - the result executed as a command. If template is not given, - "select-window -t '%%'" is used. For the meaning of the -F flag, - see the FORMATS section. This command works only from inside - tmux. - - display-panes [-t target-client] - (alias: displayp) - Display a visible indicator of each pane shown by target-client. - See the display-panes-time, display-panes-colour, and - display-panes-active-colour session options. While the indicator - is on screen, a pane may be selected with the '0' to '9' keys. - - find-window [-CNT] [-F format] [-t target-window] match-string - (alias: findw) - Search for the fnmatch(3) pattern match-string in window names, - titles, and visible content (but not history). The flags control - matching behavior: -C matches only visible window contents, -N - matches only the window name and -T matches only the window - title. The default is -CNT. If only one window is matched, - it'll be automatically selected, otherwise a choice list is - shown. For the meaning of the -F flag, see the FORMATS section. - This command only works from inside tmux. - - join-pane [-bdhv] [-l size | -p percentage] [-s src-pane] [-t dst-pane] - (alias: joinp) - Like split-window, but instead of splitting dst-pane and creating - a new pane, split it and move src-pane into the space. This can - be used to reverse break-pane. The -b option causes src-pane to - be joined to left of or above dst-pane. - - kill-pane [-a] [-t target-pane] - (alias: killp) - Destroy the given pane. If no panes remain in the containing - window, it is also destroyed. The -a option kills all but the - pane given with -t. - - kill-window [-a] [-t target-window] - (alias: killw) - Kill the current window or the window at target-window, removing - it from any sessions to which it is linked. The -a option kills - all but the window given with -t. - - last-pane [-t target-window] - (alias: lastp) - Select the last (previously selected) pane. - - last-window [-t target-session] - (alias: last) - Select the last (previously selected) window. If no - target-session is specified, select the last window of the cur- - rent session. - - link-window [-dk] [-s src-window] [-t dst-window] - (alias: linkw) - Link the window at src-window to the specified dst-window. If - dst-window is specified and no such window exists, the src-window - is linked there. If -k is given and dst-window exists, it is - killed, otherwise an error is generated. If -d is given, the - newly linked window is not selected. - - list-panes [-as] [-F format] [-t target] - (alias: lsp) - If -a is given, target is ignored and all panes on the server are - listed. If -s is given, target is a session (or the current ses- - sion). If neither is given, target is a window (or the current - window). For the meaning of the -F flag, see the FORMATS sec- - tion. - - list-windows [-a] [-F format] [-t target-session] - (alias: lsw) - If -a is given, list all windows on the server. Otherwise, list - windows in the current session or in target-session. For the - meaning of the -F flag, see the FORMATS section. - - move-pane [-bdhv] [-l size | -p percentage] [-s src-pane] [-t dst-pane] - (alias: movep) - Like join-pane, but src-pane and dst-pane may belong to the same - window. - - move-window [-rdk] [-s src-window] [-t dst-window] - (alias: movew) - This is similar to link-window, except the window at src-window - is moved to dst-window. With -r, all windows in the session are - renumbered in sequential order, respecting the base-index option. - - new-window [-adkP] [-c start-directory] [-n window-name] [-t - target-window] [-F format] [shell-command] - (alias: neww) - Create a new window. With -a, the new window is inserted at the - next index up from the specified target-window, moving windows up - if necessary, otherwise target-window is the new window location. - - If -d is given, the session does not make the new window the cur- - rent window. target-window represents the window to be created; - if the target already exists an error is shown, unless the -k - flag is used, in which case it is destroyed. shell-command is - the command to execute. If shell-command is not specified, the - value of the default-command option is used. -c specifies the - working directory in which the new window is created. It may - have an absolute path or one of the following values (or a subdi- - rectory): - - Empty string Current pane's directory - ~ User's home directory - - Where session was started - . Where server was started - - When the shell command completes, the window closes. See the - remain-on-exit option to change this behaviour. - - The TERM environment variable must be set to ``screen'' for all - programs running inside tmux. New windows will automatically - have ``TERM=screen'' added to their environment, but care must be - taken not to reset this in shell start-up files. - - The -P option prints information about the new window after it - has been created. By default, it uses the format - '#{session_name}:#{window_index}' but a different format may be - specified with -F. - - next-layout [-t target-window] - (alias: nextl) - Move a window to the next layout and rearrange the panes to fit. - - next-window [-a] [-t target-session] - (alias: next) - Move to the next window in the session. If -a is used, move to - the next window with an alert. - - pipe-pane [-o] [-t target-pane] [shell-command] - (alias: pipep) - Pipe any output sent by the program in target-pane to a shell - command. A pane may only be piped to one command at a time, any - existing pipe is closed before shell-command is executed. The - shell-command string may contain the special character sequences - supported by the status-left option. If no shell-command is - given, the current pipe (if any) is closed. - - The -o option only opens a new pipe if no previous pipe exists, - allowing a pipe to be toggled with a single key, for example: - - bind-key C-p pipe-pane -o 'cat >>~/output.#I-#P' - - previous-layout [-t target-window] - (alias: prevl) - Move to the previous layout in the session. - - previous-window [-a] [-t target-session] - (alias: prev) - Move to the previous window in the session. With -a, move to the - previous window with an alert. - - rename-window [-t target-window] new-name - (alias: renamew) - Rename the current window, or the window at target-window if - specified, to new-name. - - resize-pane [-DLRU] [-t target-pane] [adjustment] - (alias: resizep) - Resize a pane, upward with -U (the default), downward with -D, to - the left with -L and to the right with -R. The adjustment is - given in lines or cells (the default is 1). - - respawn-pane [-k] [-t target-pane] [shell-command] - (alias: respawnp) - Reactivate a pane in which the command has exited (see the - remain-on-exit window option). If shell-command is not given, - the command used when the pane was created is executed. The pane - must be already inactive, unless -k is given, in which case any - existing command is killed. - - respawn-window [-k] [-t target-window] [shell-command] - (alias: respawnw) - Reactivate a window in which the command has exited (see the - remain-on-exit window option). If shell-command is not given, - the command used when the window was created is executed. The - window must be already inactive, unless -k is given, in which - case any existing command is killed. - - rotate-window [-DU] [-t target-window] - (alias: rotatew) - Rotate the positions of the panes within a window, either upward - (numerically lower) with -U or downward (numerically higher). - - select-layout [-npUu] [-t target-window] [layout-name] - (alias: selectl) - Choose a specific layout for a window. If layout-name is not - given, the last preset layout used (if any) is reapplied. -n and - -p are equivalent to the next-layout and previous-layout com- - mands. - - -U and -u step forward and back through previous layouts, up to - the maximum set by the layout-history-limit option. - - select-pane [-lDLRU] [-t target-pane] - (alias: selectp) - Make pane target-pane the active pane in window target-window. - If one of -D, -L, -R, or -U is used, respectively the pane below, - to the left, to the right, or above the target pane is used. -l - is the same as using the last-pane command. - - select-window [-lnp] [-t target-window] - (alias: selectw) - Select the window at target-window. -l, -n and -p are equivalent - to the last-window, next-window and previous-window commands. - - split-window [-dhvP] [-c start-directory] [-l size | -p percentage] [-t - target-pane] [shell-command] [-F format] - (alias: splitw) - Create a new pane by splitting target-pane: -h does a horizontal - split and -v a vertical split; if neither is specified, -v is - assumed. The -l and -p options specify the size of the new pane - in lines (for vertical split) or in cells (for horizontal split), - or as a percentage, respectively. All other options have the - same meaning as for the new-window command. - - swap-pane [-dDU] [-s src-pane] [-t dst-pane] - (alias: swapp) - Swap two panes. If -U is used and no source pane is specified - with -s, dst-pane is swapped with the previous pane (before it - numerically); -D swaps with the next pane (after it numerically). - -d instructs tmux not to change the active pane. - - swap-window [-d] [-s src-window] [-t dst-window] - (alias: swapw) - This is similar to link-window, except the source and destination - windows are swapped. It is an error if no window exists at - src-window. - - unlink-window [-k] [-t target-window] - (alias: unlinkw) - Unlink target-window. Unless -k is given, a window may be - unlinked only if it is linked to multiple sessions - windows may - not be linked to no sessions; if -k is specified and the window - is linked to only one session, it is unlinked and destroyed. - -KEY BINDINGS - tmux allows a command to be bound to most keys, with or without a prefix - key. When specifying keys, most represent themselves (for example 'A' to - 'Z'). Ctrl keys may be prefixed with 'C-' or '^', and Alt (meta) with - 'M-'. In addition, the following special key names are accepted: Up, - Down, Left, Right, BSpace, BTab, DC (Delete), End, Enter, Escape, F1 to - F20, Home, IC (Insert), NPage/PageDown/PgDn, PPage/PageUp/PgUp, Space, - and Tab. Note that to bind the '"' or ''' keys, quotation marks are nec- - essary, for example: - - bind-key '"' split-window - bind-key "'" new-window - - Commands related to key bindings are as follows: - - bind-key [-cnr] [-t key-table] key command [arguments] - (alias: bind) - Bind key key to command. By default (without -t) the primary key - bindings are modified (those normally activated with the prefix - key); in this case, if -n is specified, it is not necessary to - use the prefix key, command is bound to key alone. The -r flag - indicates this key may repeat, see the repeat-time option. - - If -t is present, key is bound in key-table: the binding for com- - mand mode with -c or for normal mode without. To view the - default bindings and possible commands, see the list-keys com- - mand. - - list-keys [-t key-table] - (alias: lsk) - List all key bindings. Without -t the primary key bindings - - those executed when preceded by the prefix key - are printed. - - With -t, the key bindings in key-table are listed; this may be - one of: vi-edit, emacs-edit, vi-choice, emacs-choice, vi-copy or - emacs-copy. - - send-keys [-lR] [-t target-pane] key ... - (alias: send) - Send a key or keys to a window. Each argument key is the name of - the key (such as 'C-a' or 'npage' ) to send; if the string is not - recognised as a key, it is sent as a series of characters. The - -l flag disables key name lookup and sends the keys literally. - All arguments are sent sequentially from first to last. The -R - flag causes the terminal state to be reset. - - send-prefix [-2] [-t target-pane] - Send the prefix key, or with -2 the secondary prefix key, to a - window as if it was pressed. - - unbind-key [-acn] [-t key-table] key - (alias: unbind) - Unbind the command bound to key. Without -t the primary key - bindings are modified; in this case, if -n is specified, the com- - mand bound to key without a prefix (if any) is removed. If -a is - present, all key bindings are removed. - - If -t is present, key in key-table is unbound: the binding for - command mode with -c or for normal mode without. - -OPTIONS - The appearance and behaviour of tmux may be modified by changing the - value of various options. There are three types of option: server - options, session options and window options. - - The tmux server has a set of global options which do not apply to any - particular window or session. These are altered with the set-option -s - command, or displayed with the show-options -s command. - - In addition, each individual session may have a set of session options, - and there is a separate set of global session options. Sessions which do - not have a particular option configured inherit the value from the global - session options. Session options are set or unset with the set-option - command and may be listed with the show-options command. The available - server and session options are listed under the set-option command. - - Similarly, a set of window options is attached to each window, and there - is a set of global window options from which any unset options are inher- - ited. Window options are altered with the set-window-option command and - can be listed with the show-window-options command. All window options - are documented with the set-window-option command. - - Commands which set options are as follows: - - set-option [-agqsuw] [-t target-session | target-window] option value - (alias: set) - Set a window option with -w (equivalent to the set-window-option - command), a server option with -s, otherwise a session option. - - If -g is specified, the global session or window option is set. - With -a, and if the option expects a string, value is appended to - the existing setting. The -u flag unsets an option, so a session - inherits the option from the global options. It is not possible - to unset a global option. - - The -q flag suppresses the informational message (as if the quiet - server option was set). - - Available window options are listed under set-window-option. - - value depends on the option and may be a number, a string, or a - flag (on, off, or omitted to toggle). - - Available server options are: - - buffer-limit number - Set the number of buffers; as new buffers are added to - the top of the stack, old ones are removed from the bot- - tom if necessary to maintain this maximum length. - - escape-time time - Set the time in milliseconds for which tmux waits after - an escape is input to determine if it is part of a func- - tion or meta key sequences. The default is 500 millisec- - onds. - - exit-unattached [on | off] - If enabled, the server will exit when there are no - attached clients. - - quiet [on | off] - Enable or disable the display of various informational - messages (see also the -q command line flag). - - set-clipboard [on | off] - Attempt to set the terminal clipboard content using the - \e]52;...\007 xterm(1) escape sequences. This option is - on by default if there is an Ms entry in the terminfo(5) - description for the client terminal. Note that this fea- - ture needs to be enabled in xterm(1) by setting the - resource: - - disallowedWindowOps: 20,21,SetXprop - - Or changing this property from the xterm(1) interactive - menu when required. - - Available session options are: - - base-index index - Set the base index from which an unused index should be - searched when a new window is created. The default is - zero. - - bell-action [any | none | current] - Set action on window bell. any means a bell in any win- - dow linked to a session causes a bell in the current win- - dow of that session, none means all bells are ignored and - current means only bell in windows other than the current - window are ignored. - - bell-on-alert [on | off] - If on, ring the terminal bell when an alert occurs. - - default-command shell-command - Set the command used for new windows (if not specified - when the window is created) to shell-command, which may - be any sh(1) command. The default is an empty string, - which instructs tmux to create a login shell using the - value of the default-shell option. - - default-path path - Set the default working directory for new panes. If - empty (the default), the working directory is determined - from the process running in the active pane, from the - command line environment or from the working directory - where the session was created. Otherwise the same - options are available as for the -c flag to new-window. - - default-shell path - Specify the default shell. This is used as the login - shell for new windows when the default-command option is - set to empty, and must be the full path of the exe- - cutable. When started tmux tries to set a default value - from the first suitable of the SHELL environment vari- - able, the shell returned by getpwuid(3), or /bin/sh. - This option should be configured when tmux is used as a - login shell. - - default-terminal terminal - Set the default terminal for new windows created in this - session - the default value of the TERM environment vari- - able. For tmux to work correctly, this must be set to - 'screen' or a derivative of it. - - destroy-unattached [on | off] - If enabled and the session is no longer attached to any - clients, it is destroyed. - - detach-on-destroy [on | off] - If on (the default), the client is detached when the ses- - sion it is attached to is destroyed. If off, the client - is switched to the most recently active of the remaining - sessions. - - display-panes-active-colour colour - Set the colour used by the display-panes command to show - the indicator for the active pane. - - display-panes-colour colour - Set the colour used by the display-panes command to show - the indicators for inactive panes. - - display-panes-time time - Set the time in milliseconds for which the indicators - shown by the display-panes command appear. - - display-time time - Set the amount of time for which status line messages and - other on-screen indicators are displayed. time is in - milliseconds. - - history-limit lines - Set the maximum number of lines held in window history. - This setting applies only to new windows - existing win- - dow histories are not resized and retain the limit at the - point they were created. - - lock-after-time number - Lock the session (like the lock-session command) after - number seconds of inactivity, or the entire server (all - sessions) if the lock-server option is set. The default - is not to lock (set to 0). - - lock-command shell-command - Command to run when locking each client. The default is - to run lock(1) with -np. - - lock-server [on | off] - If this option is on (the default), instead of each ses- - sion locking individually as each has been idle for - lock-after-time, the entire server will lock after all - sessions would have locked. This has no effect as a ses- - sion option; it must be set as a global option. - - message-attr attributes - Set status line message attributes, where attributes is - either none or a comma-delimited list of one or more of: - bright (or bold), dim, underscore, blink, reverse, - hidden, or italics. - - message-bg colour - Set status line message background colour, where colour - is one of: black, red, green, yellow, blue, magenta, - cyan, white, aixterm bright variants (if supported: - brightred, brightgreen, and so on), colour0 to colour255 - from the 256-colour set, default, or a hexadecimal RGB - string such as '#ffffff', which chooses the closest match - from the default 256-colour set. - - message-command-attr attributes - Set status line message attributes when in command mode. - - message-command-bg colour - Set status line message background colour when in command - mode. - - message-command-fg colour - Set status line message foreground colour when in command - mode. - - message-fg colour - Set status line message foreground colour. - - message-limit number - Set the number of error or information messages to save - in the message log for each client. The default is 20. - - mouse-resize-pane [on | off] - If on, tmux captures the mouse and allows panes to be - resized by dragging on their borders. - - mouse-select-pane [on | off] - If on, tmux captures the mouse and when a window is split - into multiple panes the mouse may be used to select the - current pane. The mouse click is also passed through to - the application as normal. - - mouse-select-window [on | off] - If on, clicking the mouse on a window name in the status - line will select that window. - - mouse-utf8 [on | off] - If enabled, request mouse input as UTF-8 on UTF-8 termi- - nals. - - pane-active-border-bg colour - - pane-active-border-fg colour - Set the pane border colour for the currently active pane. - - pane-border-bg colour - - pane-border-fg colour - Set the pane border colour for panes aside from the - active pane. - - prefix key - Set the key accepted as a prefix key. - - prefix2 key - Set a secondary key accepted as a prefix key. - - renumber-windows [on | off] - If on, when a window is closed in a session, automati- - cally renumber the other windows in numerical order. - This respects the base-index option if it has been set. - If off, do not renumber the windows. - - repeat-time time - Allow multiple commands to be entered without pressing - the prefix-key again in the specified time milliseconds - (the default is 500). Whether a key repeats may be set - when it is bound using the -r flag to bind-key. Repeat - is enabled for the default keys bound to the resize-pane - command. - - set-remain-on-exit [on | off] - Set the remain-on-exit window option for any windows - first created in this session. When this option is true, - windows in which the running program has exited do not - close, instead remaining open but inactivate. Use the - respawn-window command to reactivate such a window, or - the kill-window command to destroy it. - - set-titles [on | off] - Attempt to set the client terminal title using the tsl - and fsl terminfo(5) entries if they exist. tmux automat- - ically sets these to the \e]2;...\007 sequence if the - terminal appears to be an xterm. This option is off by - default. Note that elinks will only attempt to set the - window title if the STY environment variable is set. - - set-titles-string string - String used to set the window title if set-titles is on. - Character sequences are replaced as for the status-left - option. - - status [on | off] - Show or hide the status line. - - status-attr attributes - Set status line attributes. - - status-bg colour - Set status line background colour. - - status-fg colour - Set status line foreground colour. - - status-interval interval - Update the status bar every interval seconds. By - default, updates will occur every 15 seconds. A setting - of zero disables redrawing at interval. - - status-justify [left | centre | right] - Set the position of the window list component of the sta- - tus line: left, centre or right justified. - - status-keys [vi | emacs] - Use vi or emacs-style key bindings in the status line, - for example at the command prompt. The default is emacs, - unless the VISUAL or EDITOR environment variables are set - and contain the string 'vi'. - - status-left string - Display string to the left of the status bar. string - will be passed through strftime(3) before being used. By - default, the session name is shown. string may contain - any of the following special character sequences: - - Character pair Replaced with - #(shell-command) First line of the command's - output - #[attributes] Colour or attribute change - #H Hostname of local host - #h Hostname of local host without - the domain name - #F Current window flag - #I Current window index - #D Current pane unique identifier - #P Current pane index - #S Session name - #T Current pane title - #W Current window name - ## A literal '#' - - The #(shell-command) form executes 'shell-command' and - inserts the first line of its output. Note that shell - commands are only executed once at the interval specified - by the status-interval option: if the status line is - redrawn in the meantime, the previous result is used. - Shell commands are executed with the tmux global environ- - ment set (see the ENVIRONMENT section). - - For details on how the names and titles can be set see - the NAMES AND TITLES section. - - #[attributes] allows a comma-separated list of attributes - to be specified, these may be 'fg=colour' to set the - foreground colour, 'bg=colour' to set the background - colour, the name of one of the attributes (listed under - the message-attr option) to turn an attribute on, or an - attribute prefixed with 'no' to turn one off, for example - nobright. Examples are: - - #(sysctl vm.loadavg) - #[fg=yellow,bold]#(apm -l)%%#[default] [#S] - - Where appropriate, special character sequences may be - prefixed with a number to specify the maximum length, for - example '#24T'. - - By default, UTF-8 in string is not interpreted, to enable - UTF-8, use the status-utf8 option. - - status-left-attr attributes - Set the attribute of the left part of the status line. - - status-left-bg colour - Set the background colour of the left part of the status - line. - - status-left-fg colour - Set the foreground colour of the left part of the status - line. - - status-left-length length - Set the maximum length of the left component of the sta- - tus bar. The default is 10. - - status-position [top | bottom] - Set the position of the status line. - - status-right string - Display string to the right of the status bar. By - default, the current window title in double quotes, the - date and the time are shown. As with status-left, string - will be passed to strftime(3), character pairs are - replaced, and UTF-8 is dependent on the status-utf8 - option. - - status-right-attr attributes - Set the attribute of the right part of the status line. - - status-right-bg colour - Set the background colour of the right part of the status - line. - - status-right-fg colour - Set the foreground colour of the right part of the status - line. - - status-right-length length - Set the maximum length of the right component of the sta- - tus bar. The default is 40. - - status-utf8 [on | off] - Instruct tmux to treat top-bit-set characters in the - status-left and status-right strings as UTF-8; notably, - this is important for wide characters. This option - defaults to off. - - terminal-overrides string - Contains a list of entries which override terminal - descriptions read using terminfo(5). string is a comma- - separated list of items each a colon-separated string - made up of a terminal type pattern (matched using - fnmatch(3)) and a set of name=value entries. - - For example, to set the 'clear' terminfo(5) entry to - '\e[H\e[2J' for all terminal types and the 'dch1' entry - to '\e[P' for the 'rxvt' terminal type, the option could - be set to the string: - - "*:clear=\e[H\e[2J,rxvt:dch1=\e[P" - - The terminal entry value is passed through strunvis(3) - before interpretation. The default value forcibly cor- - rects the 'colors' entry for terminals which support 88 - or 256 colours: - - "*88col*:colors=88,*256col*:colors=256,xterm*:XT" - - update-environment variables - Set a space-separated string containing a list of envi- - ronment variables to be copied into the session environ- - ment when a new session is created or an existing session - is attached. Any variables that do not exist in the - source environment are set to be removed from the session - environment (as if -r was given to the set-environment - command). The default is "DISPLAY SSH_ASKPASS - SSH_AUTH_SOCK SSH_AGENT_PID SSH_CONNECTION WINDOWID XAU- - THORITY". - - visual-activity [on | off] - If on, display a status line message when activity occurs - in a window for which the monitor-activity window option - is enabled. - - visual-bell [on | off] - If this option is on, a message is shown on a bell - instead of it being passed through to the terminal (which - normally makes a sound). Also see the bell-action - option. - - visual-content [on | off] - Like visual-activity, display a message when content is - present in a window for which the monitor-content window - option is enabled. - - visual-silence [on | off] - If monitor-silence is enabled, prints a message after the - interval has expired on a given window. - - word-separators string - Sets the session's conception of what characters are con- - sidered word separators, for the purposes of the next and - previous word commands in copy mode. The default is - ' -_@'. - - set-window-option [-agqu] [-t target-window] option value - (alias: setw) - Set a window option. The -a, -g, -q and -u flags work similarly - to the set-option command. - - Supported window options are: - - aggressive-resize [on | off] - Aggressively resize the chosen window. This means that - tmux will resize the window to the size of the smallest - session for which it is the current window, rather than - the smallest session to which it is attached. The window - may resize when the current window is changed on another - sessions; this option is good for full-screen programs - which support SIGWINCH and poor for interactive programs - such as shells. - - allow-rename [on | off] - Allow programs to change the window name using a terminal - escape sequence (\033k...\033\\). The default is on. - - alternate-screen [on | off] - This option configures whether programs running inside - tmux may use the terminal alternate screen feature, which - allows the smcup and rmcup terminfo(5) capabilities. The - alternate screen feature preserves the contents of the - window when an interactive application starts and - restores it on exit, so that any output visible before - the application starts reappears unchanged after it - exits. The default is on. - - automatic-rename [on | off] - Control automatic window renaming. When this setting is - enabled, tmux will attempt - on supported platforms - to - rename the window to reflect the command currently run- - ning in it. This flag is automatically disabled for an - individual window when a name is specified at creation - with new-window or new-session, or later with - rename-window, or with a terminal escape sequence. It - may be switched off globally with: - - set-window-option -g automatic-rename off - - c0-change-interval interval - c0-change-trigger trigger - These two options configure a simple form of rate limit- - ing for a pane. If tmux sees more than trigger C0 - sequences that modify the screen (for example, carriage - returns, linefeeds or backspaces) in one millisecond, it - will stop updating the pane immediately and instead - redraw it entirely every interval milliseconds. This - helps to prevent fast output (such as yes(1) overwhelming - the terminal). The default is a trigger of 250 and an - interval of 100. A trigger of zero disables the rate - limiting. - - clock-mode-colour colour - Set clock colour. - - clock-mode-style [12 | 24] - Set clock hour format. - - force-height height - force-width width - Prevent tmux from resizing a window to greater than width - or height. A value of zero restores the default unlim- - ited setting. - - layout-history-limit limit - Set the number of previous layouts stored for recovery - with select-layout -U and -u. - - main-pane-height height - main-pane-width width - Set the width or height of the main (left or top) pane in - the main-horizontal or main-vertical layouts. - - mode-attr attributes - Set window modes attributes. - - mode-bg colour - Set window modes background colour. - - mode-fg colour - Set window modes foreground colour. - - mode-keys [vi | emacs] - Use vi or emacs-style key bindings in copy and choice - modes. As with the status-keys option, the default is - emacs, unless VISUAL or EDITOR contains 'vi'. - - mode-mouse [on | off | copy-mode] - Mouse state in modes. If on, the mouse may be used to - enter copy mode and copy a selection by dragging, to - enter copy mode and scroll with the mouse wheel, or to - select an option in choice mode. If set to copy-mode, - the mouse behaves as set to on, but cannot be used to - enter copy mode. - - monitor-activity [on | off] - Monitor for activity in the window. Windows with activ- - ity are highlighted in the status line. - - monitor-content match-string - Monitor content in the window. When fnmatch(3) pattern - match-string appears in the window, it is highlighted in - the status line. - - monitor-silence [interval] - Monitor for silence (no activity) in the window within - interval seconds. Windows that have been silent for the - interval are highlighted in the status line. An interval - of zero disables the monitoring. - - other-pane-height height - Set the height of the other panes (not the main pane) in - the main-horizontal layout. If this option is set to 0 - (the default), it will have no effect. If both the - main-pane-height and other-pane-height options are set, - the main pane will grow taller to make the other panes - the specified height, but will never shrink to do so. - - other-pane-width width - Like other-pane-height, but set the width of other panes - in the main-vertical layout. - - pane-base-index index - Like base-index, but set the starting index for pane num- - bers. - - remain-on-exit [on | off] - A window with this flag set is not destroyed when the - program running in it exits. The window may be reacti- - vated with the respawn-window command. - - synchronize-panes [on | off] - Duplicate input to any pane to all other panes in the - same window (only for panes that are not in any special - mode). - - utf8 [on | off] - Instructs tmux to expect UTF-8 sequences to appear in - this window. - - window-status-bell-attr attributes - Set status line attributes for windows which have a bell - alert. - - window-status-bell-bg colour - Set status line background colour for windows with a bell - alert. - - window-status-bell-fg colour - Set status line foreground colour for windows with a bell - alert. - - window-status-content-attr attributes - Set status line attributes for windows which have a con- - tent alert. - - window-status-content-bg colour - Set status line background colour for windows with a con- - tent alert. - - window-status-content-fg colour - Set status line foreground colour for windows with a con- - tent alert. - - window-status-activity-attr attributes - Set status line attributes for windows which have an - activity (or silence) alert. - - window-status-activity-bg colour - Set status line background colour for windows with an - activity alert. - - window-status-activity-fg colour - Set status line foreground colour for windows with an - activity alert. - - window-status-attr attributes - Set status line attributes for a single window. - - window-status-bg colour - Set status line background colour for a single window. - - window-status-current-attr attributes - Set status line attributes for the currently active win- - dow. - - window-status-current-bg colour - Set status line background colour for the currently - active window. - - window-status-current-fg colour - Set status line foreground colour for the currently - active window. - - window-status-current-format string - Like window-status-format, but is the format used when - the window is the current window. - - window-status-fg colour - Set status line foreground colour for a single window. - - window-status-format string - Set the format in which the window is displayed in the - status line window list. See the status-left option for - details of special character sequences available. The - default is '#I:#W#F'. - - window-status-separator string - Sets the separator drawn between windows in the status - line. The default is a single space character. - - xterm-keys [on | off] - If this option is set, tmux will generate xterm(1) -style - function key sequences; these have a number included to - indicate modifiers such as Shift, Alt or Ctrl. The - default is off. - - wrap-search [on | off] - If this option is set, searches will wrap around the end - of the pane contents. The default is on. - - show-options [-gsw] [-t target-session | target-window] [option] - (alias: show) - Show the window options (or a single window option if given) with - -w (equivalent to show-window-options), the server options with - -s, otherwise the session options for target session. Global - session or window options are listed if -g is used. - - show-window-options [-g] [-t target-window] [option] - (alias: showw) - List the window options or a single option for target-window, or - the global window options if -g is used. - -FORMATS - Certain commands accept the -F flag with a format argument. This is a - string which controls the output format of the command. Special charac- - ter sequences are replaced as documented under the status-left option and - an additional long form is accepted. Replacement variables are enclosed - in '#{' and '}', for example '#{session_name}' is equivalent to '#S'. - Conditionals are also accepted by prefixing with '?' and separating two - alternatives with a comma; if the specified variable exists and is not - zero, the first alternative is chosen, otherwise the second is used. For - example '#{?session_attached,attached,not attached}' will include the - string 'attached' if the session is attached and the string 'not - attached' if it is unattached. - - The following variables are available, where appropriate: - - Variable name Replaced with - buffer_sample First 50 characters from the specified - buffer - buffer_size Size of the specified buffer in bytes - client_activity Integer time client last had activity - client_activity_string String time client last had activity - client_created Integer time client created - client_created_string String time client created - client_cwd Working directory of client - client_height Height of client - client_readonly 1 if client is readonly - client_termname Terminal name of client - client_tty Pseudo terminal of client - client_utf8 1 if client supports utf8 - client_width Width of client - host Hostname of local host - history_bytes Number of bytes in window history - history_limit Maximum window history lines - history_size Size of history in bytes - line Line number in the list - pane_active 1 if active pane - pane_current_path Current path if available - pane_dead 1 if pane is dead - pane_height Height of pane - pane_id Unique pane ID - pane_index Index of pane - pane_pid PID of first process in pane - pane_start_command Command pane started with - pane_start_path Path pane started with - pane_title Title of pane - pane_tty Pseudo terminal of pane - pane_width Width of pane - session_attached 1 if session attached - session_created Integer time session created - session_created_string String time session created - session_group Number of session group - session_grouped 1 if session in a group - session_height Height of session - session_name Name of session - session_width Width of session - session_windows Number of windows in session - window_active 1 if window active - window_find_matches Matched data from the find-window command - if available - window_flags Window flags - window_height Height of window - window_id Unique window ID - window_index Index of window - window_layout Window layout description - window_name Name of window - window_panes Number of panes in window - window_width Width of window - -NAMES AND TITLES - tmux distinguishes between names and titles. Windows and sessions have - names, which may be used to specify them in targets and are displayed in - the status line and various lists: the name is the tmux identifier for a - window or session. Only panes have titles. A pane's title is typically - set by the program running inside the pane and is not modified by tmux. - It is the same mechanism used to set for example the xterm(1) window - title in an X(7) window manager. Windows themselves do not have titles - - a window's title is the title of its active pane. tmux itself may set - the title of the terminal in which the client is running, see the - set-titles option. - - A session's name is set with the new-session and rename-session commands. - A window's name is set with one of: - - 1. A command argument (such as -n for new-window or new-session). - - 2. An escape sequence: - - $ printf '\033kWINDOW_NAME\033\\' - - 3. Automatic renaming, which sets the name to the active command in - the window's active pane. See the automatic-rename option. - - When a pane is first created, its title is the hostname. A pane's title - can be set via the OSC title setting sequence, for example: - - $ printf '\033]2;My Title\033\\' - -ENVIRONMENT - When the server is started, tmux copies the environment into the global - environment; in addition, each session has a session environment. When a - window is created, the session and global environments are merged. If a - variable exists in both, the value from the session environment is used. - The result is the initial environment passed to the new process. - - The update-environment session option may be used to update the session - environment from the client when a new session is created or an old reat- - tached. tmux also initialises the TMUX variable with some internal - information to allow commands to be executed from inside, and the TERM - variable with the correct terminal setting of 'screen'. - - Commands to alter and view the environment are: - - set-environment [-gru] [-t target-session] name [value] - (alias: setenv) - Set or unset an environment variable. If -g is used, the change - is made in the global environment; otherwise, it is applied to - the session environment for target-session. The -u flag unsets a - variable. -r indicates the variable is to be removed from the - environment before starting a new process. - - show-environment [-g] [-t target-session] [variable] - (alias: showenv) - Display the environment for target-session or the global environ- - ment with -g. If variable is omitted, all variables are shown. - Variables removed from the environment are prefixed with '-'. - -STATUS LINE - tmux includes an optional status line which is displayed in the bottom - line of each terminal. By default, the status line is enabled (it may be - disabled with the status session option) and contains, from left-to- - right: the name of the current session in square brackets; the window - list; the title of the active pane in double quotes; and the time and - date. - - The status line is made of three parts: configurable left and right sec- - tions (which may contain dynamic content such as the time or output from - a shell command, see the status-left, status-left-length, status-right, - and status-right-length options below), and a central window list. By - default, the window list shows the index, name and (if any) flag of the - windows present in the current session in ascending numerical order. It - may be customised with the window-status-format and - window-status-current-format options. The flag is one of the following - symbols appended to the window name: - - Symbol Meaning - * Denotes the current window. - - Marks the last window (previously selected). - # Window is monitored and activity has been detected. - ! A bell has occurred in the window. - + Window is monitored for content and it has appeared. - ~ The window has been silent for the monitor-silence - interval. - - The # symbol relates to the monitor-activity and + to the monitor-content - window options. The window name is printed in inverted colours if an - alert (bell, activity or content) is present. - - The colour and attributes of the status line may be configured, the - entire status line using the status-attr, status-fg and status-bg session - options and individual windows using the window-status-attr, - window-status-fg and window-status-bg window options. - - The status line is automatically refreshed at interval if it has changed, - the interval may be controlled with the status-interval session option. - - Commands related to the status line are as follows: - - command-prompt [-I inputs] [-p prompts] [-t target-client] [template] - Open the command prompt in a client. This may be used from - inside tmux to execute commands interactively. - - If template is specified, it is used as the command. If present, - -I is a comma-separated list of the initial text for each prompt. - If -p is given, prompts is a comma-separated list of prompts - which are displayed in order; otherwise a single prompt is dis- - played, constructed from template if it is present, or ':' if - not. - - Both inputs and prompts may contain the special character - sequences supported by the status-left option. - - Before the command is executed, the first occurrence of the - string '%%' and all occurrences of '%1' are replaced by the - response to the first prompt, the second '%%' and all '%2' are - replaced with the response to the second prompt, and so on for - further prompts. Up to nine prompt responses may be replaced - ('%1' to '%9'). - - confirm-before [-p prompt] [-t target-client] command - (alias: confirm) - Ask for confirmation before executing command. If -p is given, - prompt is the prompt to display; otherwise a prompt is con- - structed from command. It may contain the special character - sequences supported by the status-left option. - - This command works only from inside tmux. - - display-message [-p] [-c target-client] [-t target-pane] [message] - (alias: display) - Display a message. If -p is given, the output is printed to std- - out, otherwise it is displayed in the target-client status line. - The format of message is described in the FORMATS section; infor- - mation is taken from target-pane if -t is given, otherwise the - active pane for the session attached to target-client. - -BUFFERS - tmux maintains a stack of paste buffers. Up to the value of the - buffer-limit option are kept; when a new buffer is added, the buffer at - the bottom of the stack is removed. Buffers may be added using copy-mode - or the set-buffer command, and pasted into a window using the - paste-buffer command. - - A configurable history buffer is also maintained for each window. By - default, up to 2000 lines are kept; this can be altered with the - history-limit option (see the set-option command above). - - The buffer commands are as follows: - - choose-buffer [-F format] [-t target-window] [template] - Put a window into buffer choice mode, where a buffer may be cho- - sen interactively from a list. After a buffer is selected, '%%' - is replaced by the buffer index in template and the result exe- - cuted as a command. If template is not given, "paste-buffer -b - '%%'" is used. For the meaning of the -F flag, see the FORMATS - section. This command works only from inside tmux. - - clear-history [-t target-pane] - (alias: clearhist) - Remove and free the history for the specified pane. - - delete-buffer [-b buffer-index] - (alias: deleteb) - Delete the buffer at buffer-index, or the top buffer if not spec- - ified. - - list-buffers [-F format] - (alias: lsb) - List the global buffers. For the meaning of the -F flag, see the - FORMATS section. - - load-buffer [-b buffer-index] path - (alias: loadb) - Load the contents of the specified paste buffer from path. - - paste-buffer [-dpr] [-b buffer-index] [-s separator] [-t target-pane] - (alias: pasteb) - Insert the contents of a paste buffer into the specified pane. - If not specified, paste into the current one. With -d, also - delete the paste buffer from the stack. When output, any line- - feed (LF) characters in the paste buffer are replaced with a sep- - arator, by default carriage return (CR). A custom separator may - be specified using the -s flag. The -r flag means to do no - replacement (equivalent to a separator of LF). If -p is speci- - fied, paste bracket control codes are inserted around the buffer - if the application has requested bracketed paste mode. - - save-buffer [-a] [-b buffer-index] path - (alias: saveb) - Save the contents of the specified paste buffer to path. The -a - option appends to rather than overwriting the file. - - set-buffer [-b buffer-index] data - (alias: setb) - Set the contents of the specified buffer to data. - - show-buffer [-b buffer-index] - (alias: showb) - Display the contents of the specified buffer. - -MISCELLANEOUS - Miscellaneous commands are as follows: - - clock-mode [-t target-pane] - Display a large clock. - - if-shell shell-command command [command] - (alias: if) - Execute the first command if shell-command returns success or the - second command otherwise. - - lock-server - (alias: lock) - Lock each client individually by running the command specified by - the lock-command option. - - run-shell shell-command - (alias: run) - Execute shell-command in the background without creating a win- - dow. After it finishes, any output to stdout is displayed in - copy mode. If the command doesn't return success, the exit sta- - tus is also displayed. - - server-info - (alias: info) - Show server information and terminal details. - -TERMINFO EXTENSIONS - tmux understands some extensions to terminfo(5): - - Cc, Cr Set the cursor colour. The first takes a single string argument - and is used to set the colour; the second takes no arguments and - restores the default cursor colour. If set, a sequence such as - this may be used to change the cursor colour from inside tmux: - - $ printf '\033]12;red\033\\' - - Cs, Csr - Change the cursor style. If set, a sequence such as this may be - used to change the cursor to an underline: - - $ printf '\033[4 q' - - If Csr is set, it will be used to reset the cursor style instead - of Cs. - - Ms This sequence can be used by tmux to store the current buffer in - the host terminal's selection (clipboard). See the set-clipboard - option above and the xterm(1) man page. - -FILES - ~/.tmux.conf Default tmux configuration file. - /etc/tmux.conf System-wide configuration file. - -EXAMPLES - To create a new tmux session running vi(1): - - $ tmux new-session vi - - Most commands have a shorter form, known as an alias. For new-session, - this is new: - - $ tmux new vi - - Alternatively, the shortest unambiguous form of a command is accepted. - If there are several options, they are listed: - - $ tmux n - ambiguous command: n, could be: new-session, new-window, next-window - - Within an active session, a new window may be created by typing 'C-b c' - (Ctrl followed by the 'b' key followed by the 'c' key). - - Windows may be navigated with: 'C-b 0' (to select window 0), 'C-b 1' (to - select window 1), and so on; 'C-b n' to select the next window; and 'C-b - p' to select the previous window. - - A session may be detached using 'C-b d' (or by an external event such as - ssh(1) disconnection) and reattached with: - - $ tmux attach-session - - Typing 'C-b ?' lists the current key bindings in the current window; up - and down may be used to navigate the list or 'q' to exit from it. - - Commands to be run when the tmux server is started may be placed in the - ~/.tmux.conf configuration file. Common examples include: - - Changing the default prefix key: - - set-option -g prefix C-a - unbind-key C-b - bind-key C-a send-prefix - - Turning the status line off, or changing its colour: - - set-option -g status off - set-option -g status-bg blue - - Setting other options, such as the default command, or locking after 30 - minutes of inactivity: - - set-option -g default-command "exec /bin/ksh" - set-option -g lock-after-time 1800 - - Creating new key bindings: - - bind-key b set-option status - bind-key / command-prompt "split-window 'exec man %%'" - bind-key S command-prompt "new-window -n %1 'ssh %1'" - -SEE ALSO - pty(4) - -AUTHORS - Nicholas Marriott - -BSD October 19, 2013 BSD diff --git a/manual/1.8 b/manual/1.8 deleted file mode 100644 index f8c7f065f2b..00000000000 --- a/manual/1.8 +++ /dev/null @@ -1,2178 +0,0 @@ -TMUX(1) BSD General Commands Manual TMUX(1) - -NAME - tmux -- terminal multiplexer - -SYNOPSIS - tmux [-28lCquvV] [-c shell-command] [-f file] [-L socket-name] - [-S socket-path] [command [flags]] - -DESCRIPTION - tmux is a terminal multiplexer: it enables a number of terminals to be - created, accessed, and controlled from a single screen. tmux may be - detached from a screen and continue running in the background, then later - reattached. - - When tmux is started it creates a new session with a single window and - displays it on screen. A status line at the bottom of the screen shows - information on the current session and is used to enter interactive com- - mands. - - A session is a single collection of pseudo terminals under the management - of tmux. Each session has one or more windows linked to it. A window - occupies the entire screen and may be split into rectangular panes, each - of which is a separate pseudo terminal (the pty(4) manual page documents - the technical details of pseudo terminals). Any number of tmux instances - may connect to the same session, and any number of windows may be present - in the same session. Once all sessions are killed, tmux exits. - - Each session is persistent and will survive accidental disconnection - (such as ssh(1) connection timeout) or intentional detaching (with the - 'C-b d' key strokes). tmux may be reattached using: - - $ tmux attach - - In tmux, a session is displayed on screen by a client and all sessions - are managed by a single server. The server and each client are separate - processes which communicate through a socket in /tmp. - - The options are as follows: - - -2 Force tmux to assume the terminal supports 256 colours. - - -8 Like -2, but indicates that the terminal supports 88 - colours. - - -C Start in control mode. Given twice (-CC) disables echo. - - -c shell-command - Execute shell-command using the default shell. If neces- - sary, the tmux server will be started to retrieve the - default-shell option. This option is for compatibility - with sh(1) when tmux is used as a login shell. - - -f file Specify an alternative configuration file. By default, - tmux loads the system configuration file from - /etc/tmux.conf, if present, then looks for a user configu- - ration file at ~/.tmux.conf. - - The configuration file is a set of tmux commands which are - executed in sequence when the server is first started. - tmux loads configuration files once when the server process - has started. The source-file command may be used to load a - file later. - - tmux shows any error messages from commands in configura- - tion files in the first session created, and continues to - process the rest of the configuration file. - - -L socket-name - tmux stores the server socket in a directory under /tmp (or - TMPDIR if set); the default socket is named default. This - option allows a different socket name to be specified, - allowing several independent tmux servers to be run. - Unlike -S a full path is not necessary: the sockets are all - created in the same directory. - - If the socket is accidentally removed, the SIGUSR1 signal - may be sent to the tmux server process to recreate it. - - -l Behave as a login shell. This flag currently has no effect - and is for compatibility with other shells when using tmux - as a login shell. - - -q Set the quiet server option to prevent the server sending - various informational messages. - - -S socket-path - Specify a full alternative path to the server socket. If - -S is specified, the default socket directory is not used - and any -L flag is ignored. - - -u tmux attempts to guess if the terminal is likely to support - UTF-8 by checking the first of the LC_ALL, LC_CTYPE and - LANG environment variables to be set for the string - "UTF-8". This is not always correct: the -u flag explic- - itly informs tmux that UTF-8 is supported. - - If the server is started from a client passed -u or where - UTF-8 is detected, the utf8 and status-utf8 options are - enabled in the global window and session options respec- - tively. - - -v Request verbose logging. This option may be specified mul- - tiple times for increasing verbosity. Log messages will be - saved into tmux-client-PID.log and tmux-server-PID.log - files in the current directory, where PID is the PID of the - server or client process. - - -V Report the tmux version. - - command [flags] - This specifies one of a set of commands used to control - tmux, as described in the following sections. If no com- - mands are specified, the new-session command is assumed. - -KEY BINDINGS - tmux may be controlled from an attached client by using a key combination - of a prefix key, 'C-b' (Ctrl-b) by default, followed by a command key. - - The default command key bindings are: - - C-b Send the prefix key (C-b) through to the application. - C-o Rotate the panes in the current window forwards. - C-z Suspend the tmux client. - ! Break the current pane out of the window. - " Split the current pane into two, top and bottom. - # List all paste buffers. - $ Rename the current session. - % Split the current pane into two, left and right. - & Kill the current window. - ' Prompt for a window index to select. - , Rename the current window. - - Delete the most recently copied buffer of text. - . Prompt for an index to move the current window. - 0 to 9 Select windows 0 to 9. - : Enter the tmux command prompt. - ; Move to the previously active pane. - = Choose which buffer to paste interactively from a list. - ? List all key bindings. - D Choose a client to detach. - [ Enter copy mode to copy text or view the history. - ] Paste the most recently copied buffer of text. - c Create a new window. - d Detach the current client. - f Prompt to search for text in open windows. - i Display some information about the current window. - l Move to the previously selected window. - n Change to the next window. - o Select the next pane in the current window. - p Change to the previous window. - q Briefly display pane indexes. - r Force redraw of the attached client. - s Select a new session for the attached client interac- - tively. - L Switch the attached client back to the last session. - t Show the time. - w Choose the current window interactively. - x Kill the current pane. - { Swap the current pane with the previous pane. - } Swap the current pane with the next pane. - ~ Show previous messages from tmux, if any. - Page Up Enter copy mode and scroll one page up. - Up, Down - Left, Right - Change to the pane above, below, to the left, or to the - right of the current pane. - M-1 to M-5 Arrange panes in one of the five preset layouts: even- - horizontal, even-vertical, main-horizontal, main-verti- - cal, or tiled. - M-n Move to the next window with a bell or activity marker. - M-o Rotate the panes in the current window backwards. - M-p Move to the previous window with a bell or activity - marker. - C-Up, C-Down - C-Left, C-Right - Resize the current pane in steps of one cell. - M-Up, M-Down - M-Left, M-Right - Resize the current pane in steps of five cells. - - Key bindings may be changed with the bind-key and unbind-key commands. - -COMMANDS - This section contains a list of the commands supported by tmux. Most - commands accept the optional -t argument with one of target-client, - target-session target-window, or target-pane. These specify the client, - session, window or pane which a command should affect. target-client is - the name of the pty(4) file to which the client is connected, for example - either of /dev/ttyp1 or ttyp1 for the client attached to /dev/ttyp1. If - no client is specified, the current client is chosen, if possible, or an - error is reported. Clients may be listed with the list-clients command. - - target-session is the session id prefixed with a $, the name of a session - (as listed by the list-sessions command), or the name of a client with - the same syntax as target-client, in which case the session attached to - the client is used. When looking for the session name, tmux initially - searches for an exact match; if none is found, the session names are - checked for any for which target-session is a prefix or for which it - matches as an fnmatch(3) pattern. If a single match is found, it is used - as the target session; multiple matches produce an error. If a session - is omitted, the current session is used if available; if no current ses- - sion is available, the most recently used is chosen. - - target-window specifies a window in the form session:window. session - follows the same rules as for target-session, and window is looked for in - order: as a window index, for example mysession:1; as a window ID, such - as @1; as an exact window name, such as mysession:mywindow; then as an - fnmatch(3) pattern or the start of a window name, such as myses- - sion:mywin* or mysession:mywin. An empty window name specifies the next - unused index if appropriate (for example the new-window and link-window - commands) otherwise the current window in session is chosen. The special - character '!' uses the last (previously current) window, '^' selects the - highest numbered window, '$' selects the lowest numbered window, and '+' - and '-' select the next window or the previous window by number. When - the argument does not contain a colon, tmux first attempts to parse it as - window; if that fails, an attempt is made to match a session. - - target-pane takes a similar form to target-window but with the optional - addition of a period followed by a pane index, for example: myses- - sion:mywindow.1. If the pane index is omitted, the currently active pane - in the specified window is used. If neither a colon nor period appears, - tmux first attempts to use the argument as a pane index; if that fails, - it is looked up as for target-window. A '+' or '-' indicate the next or - previous pane index, respectively. One of the strings top, bottom, left, - right, top-left, top-right, bottom-left or bottom-right may be used - instead of a pane index. - - The special characters '+' and '-' may be followed by an offset, for - example: - - select-window -t:+2 - - When dealing with a session that doesn't contain sequential window - indexes, they will be correctly skipped. - - tmux also gives each pane created in a server an identifier consisting of - a '%' and a number, starting from zero. A pane's identifier is unique - for the life of the tmux server and is passed to the child process of the - pane in the TMUX_PANE environment variable. It may be used alone to tar- - get a pane or the window containing it. - - shell-command arguments are sh(1) commands. These must be passed as a - single item, which typically means quoting them, for example: - - new-window 'vi /etc/passwd' - - command [arguments] refers to a tmux command, passed with the command and - arguments separately, for example: - - bind-key F1 set-window-option force-width 81 - - Or if using sh(1): - - $ tmux bind-key F1 set-window-option force-width 81 - - Multiple commands may be specified together as part of a command - sequence. Each command should be separated by spaces and a semicolon; - commands are executed sequentially from left to right and lines ending - with a backslash continue on to the next line, except when escaped by - another backslash. A literal semicolon may be included by escaping it - with a backslash (for example, when specifying a command sequence to - bind-key). - - Example tmux commands include: - - refresh-client -t/dev/ttyp2 - - rename-session -tfirst newname - - set-window-option -t:0 monitor-activity on - - new-window ; split-window -d - - bind-key R source-file ~/.tmux.conf \; \ - display-message "source-file done" - - Or from sh(1): - - $ tmux kill-window -t :1 - - $ tmux new-window \; split-window -d - - $ tmux new-session -d 'vi /etc/passwd' \; split-window -d \; attach - -CLIENTS AND SESSIONS - The tmux server manages clients, sessions, windows and panes. Clients - are attached to sessions to interact with them, either when they are cre- - ated with the new-session command, or later with the attach-session com- - mand. Each session has one or more windows linked into it. Windows may - be linked to multiple sessions and are made up of one or more panes, each - of which contains a pseudo terminal. Commands for creating, linking and - otherwise manipulating windows are covered in the WINDOWS AND PANES sec- - tion. - - The following commands are available to manage clients and sessions: - - attach-session [-dr] [-t target-session] - (alias: attach) - If run from outside tmux, create a new client in the current ter- - minal and attach it to target-session. If used from inside, - switch the current client. If -d is specified, any other clients - attached to the session are detached. -r signifies the client is - read-only (only keys bound to the detach-client or switch-client - commands have any effect) - - If no server is started, attach-session will attempt to start it; - this will fail unless sessions are created in the configuration - file. - - The target-session rules for attach-session are slightly - adjusted: if tmux needs to select the most recently used session, - it will prefer the most recently used unattached session. - - detach-client [-P] [-a] [-s target-session] [-t target-client] - (alias: detach) - Detach the current client if bound to a key, the client specified - with -t, or all clients currently attached to the session speci- - fied by -s. The -a option kills all but the client given with - -t. If -P is given, send SIGHUP to the parent process of the - client, typically causing it to exit. - - has-session [-t target-session] - (alias: has) - Report an error and exit with 1 if the specified session does not - exist. If it does exist, exit with 0. - - kill-server - Kill the tmux server and clients and destroy all sessions. - - kill-session - [-a] [-t target-session] Destroy the given session, closing any - windows linked to it and no other sessions, and detaching all - clients attached to it. If -a is given, all sessions but the - specified one is killed. - - list-clients [-F format] [-t target-session] - (alias: lsc) - List all clients attached to the server. For the meaning of the - -F flag, see the FORMATS section. If target-session is speci- - fied, list only clients connected to that session. - - list-commands - (alias: lscm) - List the syntax of all commands supported by tmux. - - list-sessions [-F format] - (alias: ls) - List all sessions managed by the server. For the meaning of the - -F flag, see the FORMATS section. - - lock-client [-t target-client] - (alias: lockc) - Lock target-client, see the lock-server command. - - lock-session [-t target-session] - (alias: locks) - Lock all clients attached to target-session. - - new-session [-AdDP] [-F format] [-n window-name] [-s session-name] [-t - target-session] [-x width] [-y height] [shell-command] - (alias: new) - Create a new session with name session-name. - - The new session is attached to the current terminal unless -d is - given. window-name and shell-command are the name of and shell - command to execute in the initial window. If -d is used, -x and - -y specify the size of the initial window (80 by 24 if not - given). - - If run from a terminal, any termios(4) special characters are - saved and used for new windows in the new session. - - The -A flag makes new-session behave like attach-session if - session-name already exists; in the case, -D behaves like -d to - attach-session. - - If -t is given, the new session is grouped with target-session. - This means they share the same set of windows - all windows from - target-session are linked to the new session and any subsequent - new windows or windows being closed are applied to both sessions. - The current and previous window and any session options remain - independent and either session may be killed without affecting - the other. Giving -n or shell-command are invalid if -t is used. - - The -P option prints information about the new session after it - has been created. By default, it uses the format - '#{session_name}:' but a different format may be specified with - -F. - - refresh-client [-S] [-t target-client] - (alias: refresh) - Refresh the current client if bound to a key, or a single client - if one is given with -t. If -S is specified, only update the - client's status bar. - - rename-session [-t target-session] new-name - (alias: rename) - Rename the session to new-name. - - show-messages [-t target-client] - (alias: showmsgs) - Any messages displayed on the status line are saved in a per- - client message log, up to a maximum of the limit set by the - message-limit session option for the session attached to that - client. This command displays the log for target-client. - - source-file path - (alias: source) - Execute commands from path. - - start-server - (alias: start) - Start the tmux server, if not already running, without creating - any sessions. - - suspend-client [-t target-client] - (alias: suspendc) - Suspend a client by sending SIGTSTP (tty stop). - - switch-client [-lnpr] [-c target-client] [-t target-session] - (alias: switchc) - Switch the current session for client target-client to - target-session. If -l, -n or -p is used, the client is moved to - the last, next or previous session respectively. -r toggles - whether a client is read-only (see the attach-session command). - -WINDOWS AND PANES - A tmux window may be in one of several modes. The default permits direct - access to the terminal attached to the window. The other is copy mode, - which permits a section of a window or its history to be copied to a - paste buffer for later insertion into another window. This mode is - entered with the copy-mode command, bound to '[' by default. It is also - entered when a command that produces output, such as list-keys, is exe- - cuted from a key binding. - - The keys available depend on whether emacs or vi mode is selected (see - the mode-keys option). The following keys are supported as appropriate - for the mode: - - Function vi emacs - Back to indentation ^ M-m - Bottom of history G M-< - Clear selection Escape C-g - Copy selection Enter M-w - Cursor down j Down - Cursor left h Left - Cursor right l Right - Cursor to bottom line L - Cursor to middle line M M-r - Cursor to top line H M-R - Cursor up k Up - Delete entire line d C-u - Delete/Copy to end of line D C-k - End of line $ C-e - Go to line : g - Half page down C-d M-Down - Half page up C-u M-Up - Jump forward f f - Jump to forward t - Jump backward F F - Jump to backward T - Jump again ; ; - Jump again in reverse , , - Next page C-f Page down - Next space W - Next space, end of word E - Next word w - Next word end e M-f - Paste buffer p C-y - Previous page C-b Page up - Previous word b M-b - Previous space B - Quit mode q Escape - Rectangle toggle v R - Scroll down C-Down or C-e C-Down - Scroll up C-Up or C-y C-Up - Search again n n - Search again in reverse N N - Search backward ? C-r - Search forward / C-s - Start of line 0 C-a - Start selection Space C-Space - Top of history g M-> - Transpose characters C-t - - The next and previous word keys use space and the '-', '_' and '@' char- - acters as word delimiters by default, but this can be adjusted by setting - the word-separators session option. Next word moves to the start of the - next word, next word end to the end of the next word and previous word to - the start of the previous word. The three next and previous space keys - work similarly but use a space alone as the word separator. - - The jump commands enable quick movement within a line. For instance, - typing 'f' followed by '/' will move the cursor to the next '/' character - on the current line. A ';' will then jump to the next occurrence. - - Commands in copy mode may be prefaced by an optional repeat count. With - vi key bindings, a prefix is entered using the number keys; with emacs, - the Alt (meta) key and a number begins prefix entry. For example, to - move the cursor forward by ten words, use 'M-1 0 M-f' in emacs mode, and - '10w' in vi. - - When copying the selection, the repeat count indicates the buffer index - to replace, if used. - - Mode key bindings are defined in a set of named tables: vi-edit and - emacs-edit for keys used when line editing at the command prompt; - vi-choice and emacs-choice for keys used when choosing from lists (such - as produced by the choose-window command); and vi-copy and emacs-copy - used in copy mode. The tables may be viewed with the list-keys command - and keys modified or removed with bind-key and unbind-key. One command - accepts an argument, copy-pipe, which copies the selection and pipes it - to a command. For example the following will bind 'C-q' to copy the - selection into /tmp as well as the paste buffer: - - bind-key -temacs-copy C-q copy-pipe "cat >/tmp/out" - - The paste buffer key pastes the first line from the top paste buffer on - the stack. - - The synopsis for the copy-mode command is: - - copy-mode [-u] [-t target-pane] - Enter copy mode. The -u option scrolls one page up. - - Each window displayed by tmux may be split into one or more panes; each - pane takes up a certain area of the display and is a separate terminal. - A window may be split into panes using the split-window command. Windows - may be split horizontally (with the -h flag) or vertically. Panes may be - resized with the resize-pane command (bound to 'C-up', 'C-down' 'C-left' - and 'C-right' by default), the current pane may be changed with the - select-pane command and the rotate-window and swap-pane commands may be - used to swap panes without changing their position. Panes are numbered - beginning from zero in the order they are created. - - A number of preset layouts are available. These may be selected with the - select-layout command or cycled with next-layout (bound to 'Space' by - default); once a layout is chosen, panes within it may be moved and - resized as normal. - - The following layouts are supported: - - even-horizontal - Panes are spread out evenly from left to right across the window. - - even-vertical - Panes are spread evenly from top to bottom. - - main-horizontal - A large (main) pane is shown at the top of the window and the - remaining panes are spread from left to right in the leftover - space at the bottom. Use the main-pane-height window option to - specify the height of the top pane. - - main-vertical - Similar to main-horizontal but the large pane is placed on the - left and the others spread from top to bottom along the right. - See the main-pane-width window option. - - tiled Panes are spread out as evenly as possible over the window in - both rows and columns. - - In addition, select-layout may be used to apply a previously used layout - - the list-windows command displays the layout of each window in a form - suitable for use with select-layout. For example: - - $ tmux list-windows - 0: ksh [159x48] - layout: bb62,159x48,0,0{79x48,0,0,79x48,80,0} - $ tmux select-layout bb62,159x48,0,0{79x48,0,0,79x48,80,0} - - tmux automatically adjusts the size of the layout for the current window - size. Note that a layout cannot be applied to a window with more panes - than that from which the layout was originally defined. - - Commands related to windows and panes are as follows: - - break-pane [-dP] [-F format] [-t target-pane] - (alias: breakp) - Break target-pane off from its containing window to make it the - only pane in a new window. If -d is given, the new window does - not become the current window. The -P option prints information - about the new window after it has been created. By default, it - uses the format '#{session_name}:#{window_index}' but a different - format may be specified with -F. - - capture-pane [-aepPq] [-b buffer-index] [-E end-line] [-S start-line] [-t - target-pane] - (alias: capturep) - Capture the contents of a pane. If -p is given, the output goes - to stdout, otherwise to the buffer specified with -b or a new - buffer if omitted. If -a is given, the alternate screen is used, - and the history is not accessible. If no alternate screen - exists, an error will be returned unless -q is given. If -e is - given, the output includes escape sequences for text and back- - ground attributes. -C also escapes non-printable characters as - octal \xxx. -J joins wrapped lines and preserves trailing spaces - at each line's end. -P captures only any output that the pane - has received that is the beginning of an as-yet incomplete escape - sequence. - - -S and -E specify the starting and ending line numbers, zero is - the first line of the visible pane and negative numbers are lines - in the history. The default is to capture only the visible con- - tents of the pane. - - choose-client [-F format] [-t target-window] [template] - Put a window into client choice mode, allowing a client to be - selected interactively from a list. After a client is chosen, - '%%' is replaced by the client pty(4) path in template and the - result executed as a command. If template is not given, "detach- - client -t '%%'" is used. For the meaning of the -F flag, see the - FORMATS section. This command works only if at least one client - is attached. - - choose-list [-l items] [-t target-window] [template] - Put a window into list choice mode, allowing items to be - selected. items can be a comma-separated list to display more - than one item. If an item has spaces, that entry must be quoted. - After an item is chosen, '%%' is replaced by the chosen item in - the template and the result is executed as a command. If - template is not given, "run-shell '%%'" is used. items also - accepts format specifiers. For the meaning of this see the - FORMATS section. This command works only if at least one client - is attached. - - choose-session [-F format] [-t target-window] [template] - Put a window into session choice mode, where a session may be - selected interactively from a list. When one is chosen, '%%' is - replaced by the session name in template and the result executed - as a command. If template is not given, "switch-client -t '%%'" - is used. For the meaning of the -F flag, see the FORMATS sec- - tion. This command works only if at least one client is - attached. - - choose-tree [-suw] [-b session-template] [-c window-template] [-S format] - [-W format] [-t target-window] - Put a window into tree choice mode, where either sessions or win- - dows may be selected interactively from a list. By default, win- - dows belonging to a session are indented to show their relation- - ship to a session. - - Note that the choose-window and choose-session commands are wrap- - pers around choose-tree. - - If -s is given, will show sessions. If -w is given, will show - windows. - - By default, the tree is collapsed and sessions must be expanded - to windows with the right arrow key. The -u option will start - with all sessions expanded instead. - - If -b is given, will override the default session command. Note - that '%%' can be used and will be replaced with the session name. - The default option if not specified is "switch-client -t '%%'". - If -c is given, will override the default window command. Like - -b, '%%' can be used and will be replaced with the session name - and window index. When a window is chosen from the list, the - session command is run before the window command. - - If -S is given will display the specified format instead of the - default session format. If -W is given will display the speci- - fied format instead of the default window format. For the mean- - ing of the -s and -w options, see the FORMATS section. - - This command works only if at least one client is attached. - - choose-window [-F format] [-t target-window] [template] - Put a window into window choice mode, where a window may be cho- - sen interactively from a list. After a window is selected, '%%' - is replaced by the session name and window index in template and - the result executed as a command. If template is not given, - "select-window -t '%%'" is used. For the meaning of the -F flag, - see the FORMATS section. This command works only if at least one - client is attached. - - display-panes [-t target-client] - (alias: displayp) - Display a visible indicator of each pane shown by target-client. - See the display-panes-time, display-panes-colour, and - display-panes-active-colour session options. While the indicator - is on screen, a pane may be selected with the '0' to '9' keys. - - find-window [-CNT] [-F format] [-t target-window] match-string - (alias: findw) - Search for the fnmatch(3) pattern match-string in window names, - titles, and visible content (but not history). The flags control - matching behavior: -C matches only visible window contents, -N - matches only the window name and -T matches only the window - title. The default is -CNT. If only one window is matched, - it'll be automatically selected, otherwise a choice list is - shown. For the meaning of the -F flag, see the FORMATS section. - This command works only if at least one client is attached. - - join-pane [-bdhv] [-l size | -p percentage] [-s src-pane] [-t dst-pane] - (alias: joinp) - Like split-window, but instead of splitting dst-pane and creating - a new pane, split it and move src-pane into the space. This can - be used to reverse break-pane. The -b option causes src-pane to - be joined to left of or above dst-pane. - - kill-pane [-a] [-t target-pane] - (alias: killp) - Destroy the given pane. If no panes remain in the containing - window, it is also destroyed. The -a option kills all but the - pane given with -t. - - kill-window [-a] [-t target-window] - (alias: killw) - Kill the current window or the window at target-window, removing - it from any sessions to which it is linked. The -a option kills - all but the window given with -t. - - last-pane [-t target-window] - (alias: lastp) - Select the last (previously selected) pane. - - last-window [-t target-session] - (alias: last) - Select the last (previously selected) window. If no - target-session is specified, select the last window of the cur- - rent session. - - link-window [-dk] [-s src-window] [-t dst-window] - (alias: linkw) - Link the window at src-window to the specified dst-window. If - dst-window is specified and no such window exists, the src-window - is linked there. If -k is given and dst-window exists, it is - killed, otherwise an error is generated. If -d is given, the - newly linked window is not selected. - - list-panes [-as] [-F format] [-t target] - (alias: lsp) - If -a is given, target is ignored and all panes on the server are - listed. If -s is given, target is a session (or the current ses- - sion). If neither is given, target is a window (or the current - window). For the meaning of the -F flag, see the FORMATS sec- - tion. - - list-windows [-a] [-F format] [-t target-session] - (alias: lsw) - If -a is given, list all windows on the server. Otherwise, list - windows in the current session or in target-session. For the - meaning of the -F flag, see the FORMATS section. - - move-pane [-bdhv] [-l size | -p percentage] [-s src-pane] [-t dst-pane] - (alias: movep) - Like join-pane, but src-pane and dst-pane may belong to the same - window. - - move-window [-rdk] [-s src-window] [-t dst-window] - (alias: movew) - This is similar to link-window, except the window at src-window - is moved to dst-window. With -r, all windows in the session are - renumbered in sequential order, respecting the base-index option. - - new-window [-adkP] [-c start-directory] [-F format] [-n window-name] [-t - target-window] [shell-command] - (alias: neww) - Create a new window. With -a, the new window is inserted at the - next index up from the specified target-window, moving windows up - if necessary, otherwise target-window is the new window location. - - If -d is given, the session does not make the new window the cur- - rent window. target-window represents the window to be created; - if the target already exists an error is shown, unless the -k - flag is used, in which case it is destroyed. shell-command is - the command to execute. If shell-command is not specified, the - value of the default-command option is used. -c specifies the - working directory in which the new window is created. It may - have an absolute path or one of the following values (or a subdi- - rectory): - - Empty string Current pane's directory - ~ User's home directory - - Where session was started - . Where server was started - - When the shell command completes, the window closes. See the - remain-on-exit option to change this behaviour. - - The TERM environment variable must be set to ``screen'' for all - programs running inside tmux. New windows will automatically - have ``TERM=screen'' added to their environment, but care must be - taken not to reset this in shell start-up files. - - The -P option prints information about the new window after it - has been created. By default, it uses the format - '#{session_name}:#{window_index}' but a different format may be - specified with -F. - - next-layout [-t target-window] - (alias: nextl) - Move a window to the next layout and rearrange the panes to fit. - - next-window [-a] [-t target-session] - (alias: next) - Move to the next window in the session. If -a is used, move to - the next window with an alert. - - pipe-pane [-o] [-t target-pane] [shell-command] - (alias: pipep) - Pipe any output sent by the program in target-pane to a shell - command. A pane may only be piped to one command at a time, any - existing pipe is closed before shell-command is executed. The - shell-command string may contain the special character sequences - supported by the status-left option. If no shell-command is - given, the current pipe (if any) is closed. - - The -o option only opens a new pipe if no previous pipe exists, - allowing a pipe to be toggled with a single key, for example: - - bind-key C-p pipe-pane -o 'cat >>~/output.#I-#P' - - previous-layout [-t target-window] - (alias: prevl) - Move to the previous layout in the session. - - previous-window [-a] [-t target-session] - (alias: prev) - Move to the previous window in the session. With -a, move to the - previous window with an alert. - - rename-window [-t target-window] new-name - (alias: renamew) - Rename the current window, or the window at target-window if - specified, to new-name. - - resize-pane [-DLRUZ] [-t target-pane] [-x width] [-y height] [adjustment] - (alias: resizep) - Resize a pane, up, down, left or right by adjustment with -U, -D, - -L or -R, or to an absolute size with -x or -y. The adjustment - is given in lines or cells (the default is 1). - - With -Z, the active pane is toggled between zoomed (occupying the - whole of the window) and unzoomed (its normal position in the - layout). - - respawn-pane [-k] [-t target-pane] [shell-command] - (alias: respawnp) - Reactivate a pane in which the command has exited (see the - remain-on-exit window option). If shell-command is not given, - the command used when the pane was created is executed. The pane - must be already inactive, unless -k is given, in which case any - existing command is killed. - - respawn-window [-k] [-t target-window] [shell-command] - (alias: respawnw) - Reactivate a window in which the command has exited (see the - remain-on-exit window option). If shell-command is not given, - the command used when the window was created is executed. The - window must be already inactive, unless -k is given, in which - case any existing command is killed. - - rotate-window [-DU] [-t target-window] - (alias: rotatew) - Rotate the positions of the panes within a window, either upward - (numerically lower) with -U or downward (numerically higher). - - select-layout [-np] [-t target-window] [layout-name] - (alias: selectl) - Choose a specific layout for a window. If layout-name is not - given, the last preset layout used (if any) is reapplied. -n and - -p are equivalent to the next-layout and previous-layout com- - mands. - - select-pane [-lDLRU] [-t target-pane] - (alias: selectp) - Make pane target-pane the active pane in window target-window. - If one of -D, -L, -R, or -U is used, respectively the pane below, - to the left, to the right, or above the target pane is used. -l - is the same as using the last-pane command. - - select-window [-lnpT] [-t target-window] - (alias: selectw) - Select the window at target-window. -l, -n and -p are equivalent - to the last-window, next-window and previous-window commands. If - -T is given and the selected window is already the current win- - dow, the command behaves like last-window. - - split-window [-dhvP] [-c start-directory] [-l size | -p percentage] [-t - target-pane] [shell-command] [-F format] - (alias: splitw) - Create a new pane by splitting target-pane: -h does a horizontal - split and -v a vertical split; if neither is specified, -v is - assumed. The -l and -p options specify the size of the new pane - in lines (for vertical split) or in cells (for horizontal split), - or as a percentage, respectively. All other options have the - same meaning as for the new-window command. - - swap-pane [-dDU] [-s src-pane] [-t dst-pane] - (alias: swapp) - Swap two panes. If -U is used and no source pane is specified - with -s, dst-pane is swapped with the previous pane (before it - numerically); -D swaps with the next pane (after it numerically). - -d instructs tmux not to change the active pane. - - swap-window [-d] [-s src-window] [-t dst-window] - (alias: swapw) - This is similar to link-window, except the source and destination - windows are swapped. It is an error if no window exists at - src-window. - - unlink-window [-k] [-t target-window] - (alias: unlinkw) - Unlink target-window. Unless -k is given, a window may be - unlinked only if it is linked to multiple sessions - windows may - not be linked to no sessions; if -k is specified and the window - is linked to only one session, it is unlinked and destroyed. - -KEY BINDINGS - tmux allows a command to be bound to most keys, with or without a prefix - key. When specifying keys, most represent themselves (for example 'A' to - 'Z'). Ctrl keys may be prefixed with 'C-' or '^', and Alt (meta) with - 'M-'. In addition, the following special key names are accepted: Up, - Down, Left, Right, BSpace, BTab, DC (Delete), End, Enter, Escape, F1 to - F20, Home, IC (Insert), NPage/PageDown/PgDn, PPage/PageUp/PgUp, Space, - and Tab. Note that to bind the '"' or ''' keys, quotation marks are nec- - essary, for example: - - bind-key '"' split-window - bind-key "'" new-window - - Commands related to key bindings are as follows: - - bind-key [-cnr] [-t key-table] key command [arguments] - (alias: bind) - Bind key key to command. By default (without -t) the primary key - bindings are modified (those normally activated with the prefix - key); in this case, if -n is specified, it is not necessary to - use the prefix key, command is bound to key alone. The -r flag - indicates this key may repeat, see the repeat-time option. - - If -t is present, key is bound in key-table: the binding for com- - mand mode with -c or for normal mode without. To view the - default bindings and possible commands, see the list-keys com- - mand. - - list-keys [-t key-table] - (alias: lsk) - List all key bindings. Without -t the primary key bindings - - those executed when preceded by the prefix key - are printed. - - With -t, the key bindings in key-table are listed; this may be - one of: vi-edit, emacs-edit, vi-choice, emacs-choice, vi-copy or - emacs-copy. - - send-keys [-lR] [-t target-pane] key ... - (alias: send) - Send a key or keys to a window. Each argument key is the name of - the key (such as 'C-a' or 'npage' ) to send; if the string is not - recognised as a key, it is sent as a series of characters. The - -l flag disables key name lookup and sends the keys literally. - All arguments are sent sequentially from first to last. The -R - flag causes the terminal state to be reset. - - send-prefix [-2] [-t target-pane] - Send the prefix key, or with -2 the secondary prefix key, to a - window as if it was pressed. - - unbind-key [-acn] [-t key-table] key - (alias: unbind) - Unbind the command bound to key. Without -t the primary key - bindings are modified; in this case, if -n is specified, the com- - mand bound to key without a prefix (if any) is removed. If -a is - present, all key bindings are removed. - - If -t is present, key in key-table is unbound: the binding for - command mode with -c or for normal mode without. - -OPTIONS - The appearance and behaviour of tmux may be modified by changing the - value of various options. There are three types of option: server - options, session options and window options. - - The tmux server has a set of global options which do not apply to any - particular window or session. These are altered with the set-option -s - command, or displayed with the show-options -s command. - - In addition, each individual session may have a set of session options, - and there is a separate set of global session options. Sessions which do - not have a particular option configured inherit the value from the global - session options. Session options are set or unset with the set-option - command and may be listed with the show-options command. The available - server and session options are listed under the set-option command. - - Similarly, a set of window options is attached to each window, and there - is a set of global window options from which any unset options are inher- - ited. Window options are altered with the set-window-option command and - can be listed with the show-window-options command. All window options - are documented with the set-window-option command. - - tmux also supports user options which are prefixed with a '@'. User - options may have any name, so long as they are prefixed with '@', and be - set to any string. For example - - $ tmux setw -q @foo "abc123" - $ tmux showw -v @foo - abc123 - - Commands which set options are as follows: - - set-option [-agoqsuw] [-t target-session | target-window] option value - (alias: set) - Set a window option with -w (equivalent to the set-window-option - command), a server option with -s, otherwise a session option. - - If -g is specified, the global session or window option is set. - With -a, and if the option expects a string, value is appended to - the existing setting. The -u flag unsets an option, so a session - inherits the option from the global options. It is not possible - to unset a global option. - - The -o flag prevents setting an option that is already set. - - The -q flag suppresses the informational message (as if the quiet - server option was set). - - Available window options are listed under set-window-option. - - value depends on the option and may be a number, a string, or a - flag (on, off, or omitted to toggle). - - Available server options are: - - buffer-limit number - Set the number of buffers; as new buffers are added to - the top of the stack, old ones are removed from the bot- - tom if necessary to maintain this maximum length. - - escape-time time - Set the time in milliseconds for which tmux waits after - an escape is input to determine if it is part of a func- - tion or meta key sequences. The default is 500 millisec- - onds. - - exit-unattached [on | off] - If enabled, the server will exit when there are no - attached clients. - - quiet [on | off] - Enable or disable the display of various informational - messages (see also the -q command line flag). - - set-clipboard [on | off] - Attempt to set the terminal clipboard content using the - \e]52;...\007 xterm(1) escape sequences. This option is - on by default if there is an Ms entry in the terminfo(5) - description for the client terminal. Note that this fea- - ture needs to be enabled in xterm(1) by setting the - resource: - - disallowedWindowOps: 20,21,SetXprop - - Or changing this property from the xterm(1) interactive - menu when required. - - Available session options are: - - assume-paste-time milliseconds - If keys are entered faster than one in milliseconds, they - are assumed to have been pasted rather than typed and - tmux key bindings are not processed. The default is one - millisecond and zero disables. - - base-index index - Set the base index from which an unused index should be - searched when a new window is created. The default is - zero. - - bell-action [any | none | current] - Set action on window bell. any means a bell in any win- - dow linked to a session causes a bell in the current win- - dow of that session, none means all bells are ignored and - current means only bells in windows other than the cur- - rent window are ignored. - - bell-on-alert [on | off] - If on, ring the terminal bell when an alert occurs. - - default-command shell-command - Set the command used for new windows (if not specified - when the window is created) to shell-command, which may - be any sh(1) command. The default is an empty string, - which instructs tmux to create a login shell using the - value of the default-shell option. - - default-path path - Set the default working directory for new panes. If - empty (the default), the working directory is determined - from the process running in the active pane, from the - command line environment or from the working directory - where the session was created. Otherwise the same - options are available as for the -c flag to new-window. - - default-shell path - Specify the default shell. This is used as the login - shell for new windows when the default-command option is - set to empty, and must be the full path of the exe- - cutable. When started tmux tries to set a default value - from the first suitable of the SHELL environment vari- - able, the shell returned by getpwuid(3), or /bin/sh. - This option should be configured when tmux is used as a - login shell. - - default-terminal terminal - Set the default terminal for new windows created in this - session - the default value of the TERM environment vari- - able. For tmux to work correctly, this must be set to - 'screen' or a derivative of it. - - destroy-unattached [on | off] - If enabled and the session is no longer attached to any - clients, it is destroyed. - - detach-on-destroy [on | off] - If on (the default), the client is detached when the ses- - sion it is attached to is destroyed. If off, the client - is switched to the most recently active of the remaining - sessions. - - display-panes-active-colour colour - Set the colour used by the display-panes command to show - the indicator for the active pane. - - display-panes-colour colour - Set the colour used by the display-panes command to show - the indicators for inactive panes. - - display-panes-time time - Set the time in milliseconds for which the indicators - shown by the display-panes command appear. - - display-time time - Set the amount of time for which status line messages and - other on-screen indicators are displayed. time is in - milliseconds. - - history-limit lines - Set the maximum number of lines held in window history. - This setting applies only to new windows - existing win- - dow histories are not resized and retain the limit at the - point they were created. - - lock-after-time number - Lock the session (like the lock-session command) after - number seconds of inactivity, or the entire server (all - sessions) if the lock-server option is set. The default - is not to lock (set to 0). - - lock-command shell-command - Command to run when locking each client. The default is - to run lock(1) with -np. - - lock-server [on | off] - If this option is on (the default), instead of each ses- - sion locking individually as each has been idle for - lock-after-time, the entire server will lock after all - sessions would have locked. This has no effect as a ses- - sion option; it must be set as a global option. - - message-attr attributes - Set status line message attributes, where attributes is - either none or a comma-delimited list of one or more of: - bright (or bold), dim, underscore, blink, reverse, - hidden, or italics. - - message-bg colour - Set status line message background colour, where colour - is one of: black, red, green, yellow, blue, magenta, - cyan, white, aixterm bright variants (if supported: - brightred, brightgreen, and so on), colour0 to colour255 - from the 256-colour set, default, or a hexadecimal RGB - string such as '#ffffff', which chooses the closest match - from the default 256-colour set. - - message-command-attr attributes - Set status line message attributes when in command mode. - - message-command-bg colour - Set status line message background colour when in command - mode. - - message-command-fg colour - Set status line message foreground colour when in command - mode. - - message-fg colour - Set status line message foreground colour. - - message-limit number - Set the number of error or information messages to save - in the message log for each client. The default is 20. - - mouse-resize-pane [on | off] - If on, tmux captures the mouse and allows panes to be - resized by dragging on their borders. - - mouse-select-pane [on | off] - If on, tmux captures the mouse and when a window is split - into multiple panes the mouse may be used to select the - current pane. The mouse click is also passed through to - the application as normal. - - mouse-select-window [on | off] - If on, clicking the mouse on a window name in the status - line will select that window. - - mouse-utf8 [on | off] - If enabled, request mouse input as UTF-8 on UTF-8 termi- - nals. - - pane-active-border-bg colour - - pane-active-border-fg colour - Set the pane border colour for the currently active pane. - - pane-border-bg colour - - pane-border-fg colour - Set the pane border colour for panes aside from the - active pane. - - prefix key - Set the key accepted as a prefix key. - - prefix2 key - Set a secondary key accepted as a prefix key. - - renumber-windows [on | off] - If on, when a window is closed in a session, automati- - cally renumber the other windows in numerical order. - This respects the base-index option if it has been set. - If off, do not renumber the windows. - - repeat-time time - Allow multiple commands to be entered without pressing - the prefix-key again in the specified time milliseconds - (the default is 500). Whether a key repeats may be set - when it is bound using the -r flag to bind-key. Repeat - is enabled for the default keys bound to the resize-pane - command. - - set-remain-on-exit [on | off] - Set the remain-on-exit window option for any windows - first created in this session. When this option is true, - windows in which the running program has exited do not - close, instead remaining open but inactivate. Use the - respawn-window command to reactivate such a window, or - the kill-window command to destroy it. - - set-titles [on | off] - Attempt to set the client terminal title using the tsl - and fsl terminfo(5) entries if they exist. tmux automat- - ically sets these to the \e]2;...\007 sequence if the - terminal appears to be an xterm. This option is off by - default. Note that elinks will only attempt to set the - window title if the STY environment variable is set. - - set-titles-string string - String used to set the window title if set-titles is on. - Character sequences are replaced as for the status-left - option. - - status [on | off] - Show or hide the status line. - - status-attr attributes - Set status line attributes. - - status-bg colour - Set status line background colour. - - status-fg colour - Set status line foreground colour. - - status-interval interval - Update the status bar every interval seconds. By - default, updates will occur every 15 seconds. A setting - of zero disables redrawing at interval. - - status-justify [left | centre | right] - Set the position of the window list component of the sta- - tus line: left, centre or right justified. - - status-keys [vi | emacs] - Use vi or emacs-style key bindings in the status line, - for example at the command prompt. The default is emacs, - unless the VISUAL or EDITOR environment variables are set - and contain the string 'vi'. - - status-left string - Display string to the left of the status bar. string - will be passed through strftime(3) before being used. By - default, the session name is shown. string may contain - any of the following special character sequences: - - Character pair Replaced with - #(shell-command) First line of the command's - output - #[attributes] Colour or attribute change - #H Hostname of local host - #h Hostname of local host without - the domain name - #F Current window flag - #I Current window index - #D Current pane unique identifier - #P Current pane index - #S Session name - #T Current pane title - #W Current window name - ## A literal '#' - - The #(shell-command) form executes 'shell-command' and - inserts the first line of its output. Note that shell - commands are only executed once at the interval specified - by the status-interval option: if the status line is - redrawn in the meantime, the previous result is used. - Shell commands are executed with the tmux global environ- - ment set (see the ENVIRONMENT section). - - For details on how the names and titles can be set see - the NAMES AND TITLES section. - - #[attributes] allows a comma-separated list of attributes - to be specified, these may be 'fg=colour' to set the - foreground colour, 'bg=colour' to set the background - colour, the name of one of the attributes (listed under - the message-attr option) to turn an attribute on, or an - attribute prefixed with 'no' to turn one off, for example - nobright. Examples are: - - #(sysctl vm.loadavg) - #[fg=yellow,bold]#(apm -l)%%#[default] [#S] - - Where appropriate, special character sequences may be - prefixed with a number to specify the maximum length, for - example '#24T'. - - By default, UTF-8 in string is not interpreted, to enable - UTF-8, use the status-utf8 option. - - status-left-attr attributes - Set the attribute of the left part of the status line. - - status-left-bg colour - Set the background colour of the left part of the status - line. - - status-left-fg colour - Set the foreground colour of the left part of the status - line. - - status-left-length length - Set the maximum length of the left component of the sta- - tus bar. The default is 10. - - status-position [top | bottom] - Set the position of the status line. - - status-right string - Display string to the right of the status bar. By - default, the current window title in double quotes, the - date and the time are shown. As with status-left, string - will be passed to strftime(3), character pairs are - replaced, and UTF-8 is dependent on the status-utf8 - option. - - status-right-attr attributes - Set the attribute of the right part of the status line. - - status-right-bg colour - Set the background colour of the right part of the status - line. - - status-right-fg colour - Set the foreground colour of the right part of the status - line. - - status-right-length length - Set the maximum length of the right component of the sta- - tus bar. The default is 40. - - status-utf8 [on | off] - Instruct tmux to treat top-bit-set characters in the - status-left and status-right strings as UTF-8; notably, - this is important for wide characters. This option - defaults to off. - - terminal-overrides string - Contains a list of entries which override terminal - descriptions read using terminfo(5). string is a comma- - separated list of items each a colon-separated string - made up of a terminal type pattern (matched using - fnmatch(3)) and a set of name=value entries. - - For example, to set the 'clear' terminfo(5) entry to - '\e[H\e[2J' for all terminal types and the 'dch1' entry - to '\e[P' for the 'rxvt' terminal type, the option could - be set to the string: - - "*:clear=\e[H\e[2J,rxvt:dch1=\e[P" - - The terminal entry value is passed through strunvis(3) - before interpretation. The default value forcibly cor- - rects the 'colors' entry for terminals which support 88 - or 256 colours: - - "*88col*:colors=88,*256col*:colors=256,xterm*:XT" - - update-environment variables - Set a space-separated string containing a list of envi- - ronment variables to be copied into the session environ- - ment when a new session is created or an existing session - is attached. Any variables that do not exist in the - source environment are set to be removed from the session - environment (as if -r was given to the set-environment - command). The default is "DISPLAY SSH_ASKPASS - SSH_AUTH_SOCK SSH_AGENT_PID SSH_CONNECTION WINDOWID XAU- - THORITY". - - visual-activity [on | off] - If on, display a status line message when activity occurs - in a window for which the monitor-activity window option - is enabled. - - visual-bell [on | off] - If this option is on, a message is shown on a bell - instead of it being passed through to the terminal (which - normally makes a sound). Also see the bell-action - option. - - visual-content [on | off] - Like visual-activity, display a message when content is - present in a window for which the monitor-content window - option is enabled. - - visual-silence [on | off] - If monitor-silence is enabled, prints a message after the - interval has expired on a given window. - - word-separators string - Sets the session's conception of what characters are con- - sidered word separators, for the purposes of the next and - previous word commands in copy mode. The default is - ' -_@'. - - set-window-option [-agqu] [-t target-window] option value - (alias: setw) - Set a window option. The -a, -g, -q and -u flags work similarly - to the set-option command. - - Supported window options are: - - aggressive-resize [on | off] - Aggressively resize the chosen window. This means that - tmux will resize the window to the size of the smallest - session for which it is the current window, rather than - the smallest session to which it is attached. The window - may resize when the current window is changed on another - sessions; this option is good for full-screen programs - which support SIGWINCH and poor for interactive programs - such as shells. - - allow-rename [on | off] - Allow programs to change the window name using a terminal - escape sequence (\033k...\033\\). The default is on. - - alternate-screen [on | off] - This option configures whether programs running inside - tmux may use the terminal alternate screen feature, which - allows the smcup and rmcup terminfo(5) capabilities. The - alternate screen feature preserves the contents of the - window when an interactive application starts and - restores it on exit, so that any output visible before - the application starts reappears unchanged after it - exits. The default is on. - - automatic-rename [on | off] - Control automatic window renaming. When this setting is - enabled, tmux will attempt - on supported platforms - to - rename the window to reflect the command currently run- - ning in it. This flag is automatically disabled for an - individual window when a name is specified at creation - with new-window or new-session, or later with - rename-window, or with a terminal escape sequence. It - may be switched off globally with: - - set-window-option -g automatic-rename off - - c0-change-interval interval - c0-change-trigger trigger - These two options configure a simple form of rate limit- - ing for a pane. If tmux sees more than trigger C0 - sequences that modify the screen (for example, carriage - returns, linefeeds or backspaces) in one millisecond, it - will stop updating the pane immediately and instead - redraw it entirely every interval milliseconds. This - helps to prevent fast output (such as yes(1) overwhelming - the terminal). The default is a trigger of 250 and an - interval of 100. A trigger of zero disables the rate - limiting. - - clock-mode-colour colour - Set clock colour. - - clock-mode-style [12 | 24] - Set clock hour format. - - force-height height - force-width width - Prevent tmux from resizing a window to greater than width - or height. A value of zero restores the default unlim- - ited setting. - - main-pane-height height - main-pane-width width - Set the width or height of the main (left or top) pane in - the main-horizontal or main-vertical layouts. - - mode-attr attributes - Set window modes attributes. - - mode-bg colour - Set window modes background colour. - - mode-fg colour - Set window modes foreground colour. - - mode-keys [vi | emacs] - Use vi or emacs-style key bindings in copy and choice - modes. As with the status-keys option, the default is - emacs, unless VISUAL or EDITOR contains 'vi'. - - mode-mouse [on | off | copy-mode] - Mouse state in modes. If on, the mouse may be used to - enter copy mode and copy a selection by dragging, to - enter copy mode and scroll with the mouse wheel, or to - select an option in choice mode. If set to copy-mode, - the mouse behaves as set to on, but cannot be used to - enter copy mode. - - monitor-activity [on | off] - Monitor for activity in the window. Windows with activ- - ity are highlighted in the status line. - - monitor-content match-string - Monitor content in the window. When fnmatch(3) pattern - match-string appears in the window, it is highlighted in - the status line. - - monitor-silence [interval] - Monitor for silence (no activity) in the window within - interval seconds. Windows that have been silent for the - interval are highlighted in the status line. An interval - of zero disables the monitoring. - - other-pane-height height - Set the height of the other panes (not the main pane) in - the main-horizontal layout. If this option is set to 0 - (the default), it will have no effect. If both the - main-pane-height and other-pane-height options are set, - the main pane will grow taller to make the other panes - the specified height, but will never shrink to do so. - - other-pane-width width - Like other-pane-height, but set the width of other panes - in the main-vertical layout. - - pane-base-index index - Like base-index, but set the starting index for pane num- - bers. - - remain-on-exit [on | off] - A window with this flag set is not destroyed when the - program running in it exits. The window may be reacti- - vated with the respawn-window command. - - synchronize-panes [on | off] - Duplicate input to any pane to all other panes in the - same window (only for panes that are not in any special - mode). - - utf8 [on | off] - Instructs tmux to expect UTF-8 sequences to appear in - this window. - - window-status-bell-attr attributes - Set status line attributes for windows which have a bell - alert. - - window-status-bell-bg colour - Set status line background colour for windows with a bell - alert. - - window-status-bell-fg colour - Set status line foreground colour for windows with a bell - alert. - - window-status-content-attr attributes - Set status line attributes for windows which have a con- - tent alert. - - window-status-content-bg colour - Set status line background colour for windows with a con- - tent alert. - - window-status-content-fg colour - Set status line foreground colour for windows with a con- - tent alert. - - window-status-activity-attr attributes - Set status line attributes for windows which have an - activity (or silence) alert. - - window-status-activity-bg colour - Set status line background colour for windows with an - activity alert. - - window-status-activity-fg colour - Set status line foreground colour for windows with an - activity alert. - - window-status-attr attributes - Set status line attributes for a single window. - - window-status-bg colour - Set status line background colour for a single window. - - window-status-current-attr attributes - Set status line attributes for the currently active win- - dow. - - window-status-current-bg colour - Set status line background colour for the currently - active window. - - window-status-current-fg colour - Set status line foreground colour for the currently - active window. - - window-status-current-format string - Like window-status-format, but is the format used when - the window is the current window. - - window-status-last-attr attributes - Set status line attributes for the last active window. - - window-status-last-bg colour - Set status line background colour for the last active - window. - - window-status-last-fg colour - Set status line foreground colour for the last active - window. - - window-status-fg colour - Set status line foreground colour for a single window. - - window-status-format string - Set the format in which the window is displayed in the - status line window list. See the status-left option for - details of special character sequences available. The - default is '#I:#W#F'. - - window-status-separator string - Sets the separator drawn between windows in the status - line. The default is a single space character. - - xterm-keys [on | off] - If this option is set, tmux will generate xterm(1) -style - function key sequences; these have a number included to - indicate modifiers such as Shift, Alt or Ctrl. The - default is off. - - wrap-search [on | off] - If this option is set, searches will wrap around the end - of the pane contents. The default is on. - - show-options [-gqsvw] [-t target-session | target-window] [option] - (alias: show) - Show the window options (or a single window option if given) with - -w (equivalent to show-window-options), the server options with - -s, otherwise the session options for target session. Global - session or window options are listed if -g is used. -v shows - only the option value, not the name. If -q is set, no error will - be returned if option is unset. - - show-window-options [-gv] [-t target-window] [option] - (alias: showw) - List the window options or a single option for target-window, or - the global window options if -g is used. -v shows only the - option value, not the name. - -FORMATS - Certain commands accept the -F flag with a format argument. This is a - string which controls the output format of the command. Special charac- - ter sequences are replaced as documented under the status-left option and - an additional long form is accepted. Replacement variables are enclosed - in '#{' and '}', for example '#{session_name}' is equivalent to '#S'. - Conditionals are also accepted by prefixing with '?' and separating two - alternatives with a comma; if the specified variable exists and is not - zero, the first alternative is chosen, otherwise the second is used. For - example '#{?session_attached,attached,not attached}' will include the - string 'attached' if the session is attached and the string 'not - attached' if it is unattached. - - The following variables are available, where appropriate: - - Variable name Replaced with - alternate_on If pane is in alternate screen - alternate_saved_x Saved cursor X in alternate screen - alternate_saved_y Saved cursor Y in alternate screen - buffer_sample First 50 characters from the specified - buffer - buffer_size Size of the specified buffer in bytes - client_activity Integer time client last had activity - client_activity_string String time client last had activity - client_created Integer time client created - client_created_string String time client created - client_cwd Working directory of client - client_height Height of client - client_last_session Name of the client's last session - client_prefix 1 if prefix key has been pressed - client_readonly 1 if client is readonly - client_session Name of the client's session - client_termname Terminal name of client - client_tty Pseudo terminal of client - client_utf8 1 if client supports utf8 - client_width Width of client - cursor_flag Pane cursor flag - cursor_x Cursor X position in pane - cursor_y Cursor Y position in pane - history_bytes Number of bytes in window history - history_limit Maximum window history lines - history_size Size of history in bytes - host Hostname of local host - insert_flag Pane insert flag - keypad_cursor_flag Pane keypad cursor flag - keypad_flag Pane keypad flag - line Line number in the list - mouse_any_flag Pane mouse any flag - mouse_button_flag Pane mouse button flag - mouse_standard_flag Pane mouse standard flag - mouse_utf8_flag Pane mouse UTF-8 flag - pane_active 1 if active pane - pane_current_command Current command if available - pane_current_path Current path if available - pane_dead 1 if pane is dead - pane_height Height of pane - pane_id Unique pane ID - pane_in_mode If pane is in a mode - pane_index Index of pane - pane_pid PID of first process in pane - pane_start_command Command pane started with - pane_start_path Path pane started with - pane_tabs Pane tab positions - pane_title Title of pane - pane_tty Pseudo terminal of pane - pane_width Width of pane - saved_cursor_x Saved cursor X in pane - saved_cursor_y Saved cursor Y in pane - scroll_region_lower Bottom of scroll region in pane - scroll_region_upper Top of scroll region in pane - session_attached 1 if session attached - session_created Integer time session created - session_created_string String time session created - session_group Number of session group - session_grouped 1 if session in a group - session_height Height of session - session_id Unique session ID - session_name Name of session - session_width Width of session - session_windows Number of windows in session - window_active 1 if window active - window_find_matches Matched data from the find-window command - if available - window_flags Window flags - window_height Height of window - window_id Unique window ID - window_index Index of window - window_layout Window layout description - window_name Name of window - window_panes Number of panes in window - window_width Width of window - wrap_flag Pane wrap flag - -NAMES AND TITLES - tmux distinguishes between names and titles. Windows and sessions have - names, which may be used to specify them in targets and are displayed in - the status line and various lists: the name is the tmux identifier for a - window or session. Only panes have titles. A pane's title is typically - set by the program running inside the pane and is not modified by tmux. - It is the same mechanism used to set for example the xterm(1) window - title in an X(7) window manager. Windows themselves do not have titles - - a window's title is the title of its active pane. tmux itself may set - the title of the terminal in which the client is running, see the - set-titles option. - - A session's name is set with the new-session and rename-session commands. - A window's name is set with one of: - - 1. A command argument (such as -n for new-window or new-session). - - 2. An escape sequence: - - $ printf '\033kWINDOW_NAME\033\\' - - 3. Automatic renaming, which sets the name to the active command in - the window's active pane. See the automatic-rename option. - - When a pane is first created, its title is the hostname. A pane's title - can be set via the OSC title setting sequence, for example: - - $ printf '\033]2;My Title\033\\' - -ENVIRONMENT - When the server is started, tmux copies the environment into the global - environment; in addition, each session has a session environment. When a - window is created, the session and global environments are merged. If a - variable exists in both, the value from the session environment is used. - The result is the initial environment passed to the new process. - - The update-environment session option may be used to update the session - environment from the client when a new session is created or an old reat- - tached. tmux also initialises the TMUX variable with some internal - information to allow commands to be executed from inside, and the TERM - variable with the correct terminal setting of 'screen'. - - Commands to alter and view the environment are: - - set-environment [-gru] [-t target-session] name [value] - (alias: setenv) - Set or unset an environment variable. If -g is used, the change - is made in the global environment; otherwise, it is applied to - the session environment for target-session. The -u flag unsets a - variable. -r indicates the variable is to be removed from the - environment before starting a new process. - - show-environment [-g] [-t target-session] [variable] - (alias: showenv) - Display the environment for target-session or the global environ- - ment with -g. If variable is omitted, all variables are shown. - Variables removed from the environment are prefixed with '-'. - -STATUS LINE - tmux includes an optional status line which is displayed in the bottom - line of each terminal. By default, the status line is enabled (it may be - disabled with the status session option) and contains, from left-to- - right: the name of the current session in square brackets; the window - list; the title of the active pane in double quotes; and the time and - date. - - The status line is made of three parts: configurable left and right sec- - tions (which may contain dynamic content such as the time or output from - a shell command, see the status-left, status-left-length, status-right, - and status-right-length options below), and a central window list. By - default, the window list shows the index, name and (if any) flag of the - windows present in the current session in ascending numerical order. It - may be customised with the window-status-format and - window-status-current-format options. The flag is one of the following - symbols appended to the window name: - - Symbol Meaning - * Denotes the current window. - - Marks the last window (previously selected). - # Window is monitored and activity has been detected. - ! A bell has occurred in the window. - + Window is monitored for content and it has appeared. - ~ The window has been silent for the monitor-silence - interval. - Z The window's active pane is zoomed. - - The # symbol relates to the monitor-activity and + to the monitor-content - window options. The window name is printed in inverted colours if an - alert (bell, activity or content) is present. - - The colour and attributes of the status line may be configured, the - entire status line using the status-attr, status-fg and status-bg session - options and individual windows using the window-status-attr, - window-status-fg and window-status-bg window options. - - The status line is automatically refreshed at interval if it has changed, - the interval may be controlled with the status-interval session option. - - Commands related to the status line are as follows: - - command-prompt [-I inputs] [-p prompts] [-t target-client] [template] - Open the command prompt in a client. This may be used from - inside tmux to execute commands interactively. - - If template is specified, it is used as the command. If present, - -I is a comma-separated list of the initial text for each prompt. - If -p is given, prompts is a comma-separated list of prompts - which are displayed in order; otherwise a single prompt is dis- - played, constructed from template if it is present, or ':' if - not. - - Both inputs and prompts may contain the special character - sequences supported by the status-left option. - - Before the command is executed, the first occurrence of the - string '%%' and all occurrences of '%1' are replaced by the - response to the first prompt, the second '%%' and all '%2' are - replaced with the response to the second prompt, and so on for - further prompts. Up to nine prompt responses may be replaced - ('%1' to '%9'). - - confirm-before [-p prompt] [-t target-client] command - (alias: confirm) - Ask for confirmation before executing command. If -p is given, - prompt is the prompt to display; otherwise a prompt is con- - structed from command. It may contain the special character - sequences supported by the status-left option. - - This command works only from inside tmux. - - display-message [-p] [-c target-client] [-t target-pane] [message] - (alias: display) - Display a message. If -p is given, the output is printed to std- - out, otherwise it is displayed in the target-client status line. - The format of message is described in the FORMATS section; infor- - mation is taken from target-pane if -t is given, otherwise the - active pane for the session attached to target-client. - -BUFFERS - tmux maintains a stack of paste buffers. Up to the value of the - buffer-limit option are kept; when a new buffer is added, the buffer at - the bottom of the stack is removed. Buffers may be added using copy-mode - or the set-buffer command, and pasted into a window using the - paste-buffer command. - - A configurable history buffer is also maintained for each window. By - default, up to 2000 lines are kept; this can be altered with the - history-limit option (see the set-option command above). - - The buffer commands are as follows: - - choose-buffer [-F format] [-t target-window] [template] - Put a window into buffer choice mode, where a buffer may be cho- - sen interactively from a list. After a buffer is selected, '%%' - is replaced by the buffer index in template and the result exe- - cuted as a command. If template is not given, "paste-buffer -b - '%%'" is used. For the meaning of the -F flag, see the FORMATS - section. This command works only if at least one client is - attached. - - clear-history [-t target-pane] - (alias: clearhist) - Remove and free the history for the specified pane. - - delete-buffer [-b buffer-index] - (alias: deleteb) - Delete the buffer at buffer-index, or the top buffer if not spec- - ified. - - list-buffers [-F format] - (alias: lsb) - List the global buffers. For the meaning of the -F flag, see the - FORMATS section. - - load-buffer [-b buffer-index] path - (alias: loadb) - Load the contents of the specified paste buffer from path. - - paste-buffer [-dpr] [-b buffer-index] [-s separator] [-t target-pane] - (alias: pasteb) - Insert the contents of a paste buffer into the specified pane. - If not specified, paste into the current one. With -d, also - delete the paste buffer from the stack. When output, any line- - feed (LF) characters in the paste buffer are replaced with a sep- - arator, by default carriage return (CR). A custom separator may - be specified using the -s flag. The -r flag means to do no - replacement (equivalent to a separator of LF). If -p is speci- - fied, paste bracket control codes are inserted around the buffer - if the application has requested bracketed paste mode. - - save-buffer [-a] [-b buffer-index] path - (alias: saveb) - Save the contents of the specified paste buffer to path. The -a - option appends to rather than overwriting the file. - - set-buffer [-b buffer-index] data - (alias: setb) - Set the contents of the specified buffer to data. - - show-buffer [-b buffer-index] - (alias: showb) - Display the contents of the specified buffer. - -MISCELLANEOUS - Miscellaneous commands are as follows: - - clock-mode [-t target-pane] - Display a large clock. - - if-shell [-b] [-t target-pane] shell-command command [command] - (alias: if) - Execute the first command if shell-command returns success or the - second command otherwise. Before being executed, shell-command - is expanded using the rules specified in the FORMATS section, - including those relevant to target-pane. With -b, shell-command - is run in the background. - - lock-server - (alias: lock) - Lock each client individually by running the command specified by - the lock-command option. - - run-shell -b [-t target-pane] shell-command - (alias: run) - Execute shell-command in the background without creating a win- - dow. Before being executed, shell-command is expanded using the - rules specified in the FORMATS section. With -b, the command is - run in the background. After it finishes, any output to stdout - is displayed in copy mode (in the pane specified by -t or the - current pane if omitted). If the command doesn't return success, - the exit status is also displayed. - - server-info - (alias: info) - Show server information and terminal details. - - wait-for -LSU channel - (alias: wait) - When used without options, prevents the client from exiting until - woken using wait-for -S with the same channel. When -L is used, - the channel is locked and any clients that try to lock the same - channel are made to wait until the channel is unlocked with - wait-for -U. This command only works from outside tmux. - -TERMINFO EXTENSIONS - tmux understands some extensions to terminfo(5): - - Cc, Cr Set the cursor colour. The first takes a single string argument - and is used to set the colour; the second takes no arguments and - restores the default cursor colour. If set, a sequence such as - this may be used to change the cursor colour from inside tmux: - - $ printf '\033]12;red\033\\' - - Cs, Csr - Change the cursor style. If set, a sequence such as this may be - used to change the cursor to an underline: - - $ printf '\033[4 q' - - If Csr is set, it will be used to reset the cursor style instead - of Cs. - - Ms This sequence can be used by tmux to store the current buffer in - the host terminal's selection (clipboard). See the set-clipboard - option above and the xterm(1) man page. - -CONTROL MODE - tmux offers a textual interface called control mode. This allows appli- - cations to communicate with tmux using a simple text-only protocol. - - In control mode, a client sends tmux commands or command sequences termi- - nated by newlines on standard input. Each command will produce one block - of output on standard output. An output block consists of a %begin line - followed by the output (which may be empty). The output block ends with - a %end or %error. %begin and matching %end or %error have two arguments: - an integer time (as seconds from epoch) and command number. For example: - - %begin 1363006971 2 - 0: ksh* (1 panes) [80x24] [layout b25f,80x24,0,0,2] @2 (active) - %end 1363006971 2 - - In control mode, tmux outputs notifications. A notification will never - occur inside an output block. - - The following notifications are defined: - - %exit [reason] - The tmux client is exiting immediately, either because it is not - attached to any session or an error occurred. If present, reason - describes why the client exited. - - %layout-change window-id window-layout - The layout of a window with ID window-id changed. The new layout - is window-layout. - - %output pane-id value - A window pane produced output. value escapes non-printable char- - acters and backslash as octal \xxx. - - %session-changed session-id name - The client is now attached to the session with ID session-id, - which is named name. - - %session-renamed name - The current session was renamed to name. - - %sessions-changed - A session was created or destroyed. - - %unlinked-window-add window-id - The window with ID window-id was created but is not linked to the - current session. - - %window-add window-id - The window with ID window-id was linked to the current session. - - %window-close window-id - The window with ID window-id closed. - - %window-renamed window-id name - The window with ID window-id was renamed to name. - -FILES - ~/.tmux.conf Default tmux configuration file. - /etc/tmux.conf System-wide configuration file. - -EXAMPLES - To create a new tmux session running vi(1): - - $ tmux new-session vi - - Most commands have a shorter form, known as an alias. For new-session, - this is new: - - $ tmux new vi - - Alternatively, the shortest unambiguous form of a command is accepted. - If there are several options, they are listed: - - $ tmux n - ambiguous command: n, could be: new-session, new-window, next-window - - Within an active session, a new window may be created by typing 'C-b c' - (Ctrl followed by the 'b' key followed by the 'c' key). - - Windows may be navigated with: 'C-b 0' (to select window 0), 'C-b 1' (to - select window 1), and so on; 'C-b n' to select the next window; and 'C-b - p' to select the previous window. - - A session may be detached using 'C-b d' (or by an external event such as - ssh(1) disconnection) and reattached with: - - $ tmux attach-session - - Typing 'C-b ?' lists the current key bindings in the current window; up - and down may be used to navigate the list or 'q' to exit from it. - - Commands to be run when the tmux server is started may be placed in the - ~/.tmux.conf configuration file. Common examples include: - - Changing the default prefix key: - - set-option -g prefix C-a - unbind-key C-b - bind-key C-a send-prefix - - Turning the status line off, or changing its colour: - - set-option -g status off - set-option -g status-bg blue - - Setting other options, such as the default command, or locking after 30 - minutes of inactivity: - - set-option -g default-command "exec /bin/ksh" - set-option -g lock-after-time 1800 - - Creating new key bindings: - - bind-key b set-option status - bind-key / command-prompt "split-window 'exec man %%'" - bind-key S command-prompt "new-window -n %1 'ssh %1'" - -SEE ALSO - pty(4) - -AUTHORS - Nicholas Marriott - -BSD October 19, 2013 BSD diff --git a/manual/NOTES.rst b/manual/NOTES.rst deleted file mode 100644 index 080d4bf9426..00000000000 --- a/manual/NOTES.rst +++ /dev/null @@ -1,88 +0,0 @@ -=================== -Compatibility notes -=================== - -master ------- - -src: http://sourceforge.net/p/tmux/tmux-code/ci/master/tree/ - -1.8 ---- - -src: http://sourceforge.net/p/tmux/tmux-code/ci/1.8/tree/ - -1.7 ---- - -src: http://sourceforge.net/p/tmux/tmux-code/ci/1.7/tree/ - -1.6 ---- - -src: http://sourceforge.net/p/tmux/tmux-code/ci/1.6/tree/ - -new-window -"""""""""" - -src: http://sourceforge.net/p/tmux/tmux-code/ci/1.6/tree/cmd-new-window.c - -.. code-block:: c - - if (args_has(args, 'P')) - ctx->print(ctx, "%s:%u", s->name, wl->idx); - return (0); - -split-window -"""""""""""" - -src: http://sourceforge.net/p/tmux/tmux-code/ci/1.6/tree/cmd-split-window.c - -.. code-block:: c - - if (args_has(args, 'P')) { - if (window_pane_index(new_wp, &paneidx) != 0) - fatalx("index not found"); - ctx->print(ctx, "%s:%u.%u", s->name, wl->idx, paneidx); - } - return (0); - -list-sessions -""""""""""""" - -src: http://sourceforge.net/p/tmux/tmux-code/ci/1.6/tree/cmd-list-sessions.c - -.. code-block:: c - - template = "#{session_name}: #{session_windows} windows " - "(created #{session_created_string}) [#{session_width}x" - "#{session_height}]#{?session_grouped, (group ,}" - "#{session_group}#{?session_grouped,),}" - "#{?session_attached, (attached),}"; - -list-windows -"""""""""""" - -src: http://sourceforge.net/p/tmux/tmux-code/ci/1.6/tree/cmd-list-windows.c - -.. code-block:: c - - template = "#{session_name}:#{window_index}: " - "#{window_name} " - "[#{window_width}x#{window_height}] " - "[layout #{window_layout}]" - "#{?window_active, (active),}"; - - -list-panes -"""""""""" - -src: http://sourceforge.net/p/tmux/tmux-code/ci/1.6/tree/cmd-list-panes.c - -.. code-block:: c - - template = "#{session_name}:#{window_index}.#{pane_index}: " - "[#{pane_width}x#{pane_height}] [history " - "#{history_size}/#{history_limit}, " - "#{history_bytes} bytes] #{pane_id}" - "#{?pane_active, (active),}#{?pane_dead, (dead),}"; diff --git a/manual/README.rst b/manual/README.rst deleted file mode 100644 index 161af64ca24..00000000000 --- a/manual/README.rst +++ /dev/null @@ -1,26 +0,0 @@ -For studying the differences between prior tmux versions to check -compatibility with legacy versions. - -Get source: - -.. code-block:: bash - - $ git clone git://git.code.sf.net/p/tmux/tmux-code tmux-tmux-code tmux - $ cd tmux - -Converted with: - -.. code-block:: bash - - $ git checkout - $ ./configure - $ make - $ groff -t -e -mandoc -Tascii tmux.1 | col -bx > manpage.txt - -repeat for versions. - -Create a git-diff style diff of version manuals: - -.. code-block:: bash - - $ diff -u 1.6 1.8 > 1_6__1_8.diff diff --git a/pkg/tmuxp.bash b/pkg/tmuxp.bash deleted file mode 100755 index 3d16af8cc79..00000000000 --- a/pkg/tmuxp.bash +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env bash - -_python_argcomplete() { - local IFS='\013' - COMPREPLY=( $(IFS="$IFS" \ - COMP_LINE="$COMP_LINE" \ - COMP_POINT="$COMP_POINT" \ - _ARGCOMPLETE_COMP_WORDBREAKS="$COMP_WORDBREAKS" \ - _ARGCOMPLETE=1 \ - "$1" 8>&1 9>&2 1>/dev/null 2>/dev/null) ) - if [[ $? != 0 ]]; then - unset COMPREPLY - fi -} - - - -# SHELLNAME=`lsof -p $$ | awk '(NR==2) {print $1}'` - -# if [ $SHELLNAME = "bash" ]; then -# elif [ $SHELLNAME = "zsh" ]; then -# elif [ $SHELLNAME = "tcsh" ]; then -# fi - -eval "$(register-python-argcomplete tmuxp)" diff --git a/pkg/tmuxp.tcsh b/pkg/tmuxp.tcsh deleted file mode 100644 index b92945eabfe..00000000000 --- a/pkg/tmuxp.tcsh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh - -# testing tcsh from @redstreet's argcopmlete issue -# https://github.com/kislyuk/argcomplete/issues/49 -export IFS='' -export COMP_LINE=${COMMAND_LINE} -export COMP_WORDBREAKS= -export COMP_POINT=${#COMMAND_LINE} -export _ARGCOMPLETE=1 -tmuxp 8>&1 9>&2 1>/dev/null 2>/dev/null diff --git a/pkg/tmuxp.zsh b/pkg/tmuxp.zsh deleted file mode 100644 index 6adb017a8e3..00000000000 --- a/pkg/tmuxp.zsh +++ /dev/null @@ -1,66 +0,0 @@ -# This is from https://github.com/aws/aws-cli in /bin/aws_zsh_completer.sh -# -# LICENSE: https://github.com/aws/aws-cli/blob/develop/LICENSE.txt -# -# Source this file to activate auto completion for zsh using the bash compatibility -# helper. -# -# source /path/to/zsh_complete.sh -# -# Typically that would be called somewhere in your .zshrc -# -# Note, the overwrite of _bash_complete() is to export COMP_LINE and COMP_POINT -# That is only required for zsh <= edab1d3dbe61da7efe5f1ac0e40444b2ec9b9570 -# -# https://github.com/zsh-users/zsh/commit/edab1d3dbe61da7efe5f1ac0e40444b2ec9b9570 -# -# zsh relases prior to that version do not export the required env variables! -# -# It is planned to write a proper zsh auto completion soon. Please talk -# to Frank Becker . - -autoload -U bashcompinit -bashcompinit -i - -_bash_complete() { - local ret=1 - local -a suf matches - local -x COMP_POINT COMP_CWORD - local -a COMP_WORDS COMPREPLY BASH_VERSINFO - local -x COMP_LINE="$words" - local -A savejobstates savejobtexts - - (( COMP_POINT = 1 + ${#${(j. .)words[1,CURRENT]}} + $#QIPREFIX + $#IPREFIX + $#PREFIX )) - (( COMP_CWORD = CURRENT - 1)) - COMP_WORDS=( $words ) - BASH_VERSINFO=( 2 05b 0 1 release ) - - savejobstates=( ${(kv)jobstates} ) - savejobtexts=( ${(kv)jobtexts} ) - - [[ ${argv[${argv[(I)nospace]:-0}-1]} = -o ]] && suf=( -S '' ) - - matches=( ${(f)"$(compgen $@ -- ${words[CURRENT]})"} ) - - if [[ -n $matches ]]; then - if [[ ${argv[${argv[(I)filenames]:-0}-1]} = -o ]]; then - compset -P '*/' && matches=( ${matches##*/} ) - compset -S '/*' && matches=( ${matches%%/*} ) - compadd -Q -f "${suf[@]}" -a matches && ret=0 - else - compadd -Q "${suf[@]}" -a matches && ret=0 - fi - fi - - if (( ret )); then - if [[ ${argv[${argv[(I)default]:-0}-1]} = -o ]]; then - _default "${suf[@]}" && ret=0 - elif [[ ${argv[${argv[(I)dirnames]:-0}-1]} = -o ]]; then - _directories "${suf[@]}" && ret=0 - fi - fi - - return ret -} - -eval "$(register-python-argcomplete tmuxp)" diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000000..c58c454bb36 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,243 @@ +[project] +name = "tmuxp" +version = "1.55.0" +description = "Session manager for tmux, which allows users to save and load tmux sessions through simple configuration files." +requires-python = ">=3.9,<4.0" +authors = [ + {name = "Tony Narlock", email = "tony@git-pull.com"} +] +license = { text = "MIT" } +classifiers = [ + "Development Status :: 5 - Production/Stable", + "License :: OSI Approved :: MIT License", + "Operating System :: POSIX", + "Operating System :: MacOS :: MacOS X", + "Environment :: Web Environment", + "Intended Audience :: Developers", + "Programming Language :: Python", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: Implementation :: PyPy", + "Topic :: Utilities", + "Topic :: System :: Shells", +] +keywords = ["tmux", "session manager", "terminal", "ncurses"] +homepage = "http://github.com/tmux-python/tmuxp/" +readme = "README.md" +packages = [ + { include = "*", from = "src" }, +] +include = [ + { path = "CHANGES", format = "sdist" }, + { path = ".tmuxp.yaml", format = "sdist" }, + { path = "tests", format = "sdist" }, + { path = "examples", format = "sdist" }, + { path = "docs", format = "sdist" }, + { path = "conftest.py", format = "sdist" }, +] +dependencies = [ + "libtmux~=0.46.0", + "colorama>=0.3.9", + "PyYAML>=6.0" +] + +[project.urls] +"Bug Tracker" = "https://github.com/tmux-python/tmuxp/issues" +Documentation = "https://tmuxp.git-pull.com" +Repository = "https://github.com/tmux-python/tmuxp" +Changes = "https://github.com/tmux-python/tmuxp/blob/master/CHANGES" + +[project.scripts] +tmuxp = 'tmuxp:cli.cli' + +[tool.uv] +dev-dependencies = [ + # Docs + "aafigure", + "pillow", + "sphinx", + "furo", + "gp-libs", + "sphinx-autobuild", + "sphinx-autodoc-typehints", + "sphinx-inline-tabs", + "sphinxext-opengraph", + "sphinx-copybutton", + "sphinxext-rediraffe", + "sphinx-argparse", + "myst-parser", + "linkify-it-py", + # Testing + "gp-libs", + "pytest", + "pytest-rerunfailures", + "pytest-mock", + "pytest-watcher", + # Coverage + "codecov", + "coverage", + "pytest-cov", + # Lint + "ruff", + "mypy", + "types-colorama", + "types-docutils", + "types-PyYAML", +] + +[dependency-groups] +docs = [ + "aafigure", + "pillow", + "sphinx", + "furo", + "gp-libs", + "sphinx-autobuild", + "sphinx-autodoc-typehints", + "sphinx-inline-tabs", + "sphinxext-opengraph", + "sphinx-copybutton", + "sphinxext-rediraffe", + "sphinx-argparse", + "myst-parser", + "linkify-it-py", +] +testing = [ + "gp-libs", + "pytest", + "pytest-rerunfailures", + "pytest-mock", + "pytest-watcher", +] +coverage =[ + "codecov", + "coverage", + "pytest-cov", +] +lint = [ + "ruff", + "mypy", + "types-colorama", + "types-docutils", + "types-PyYAML", +] + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.coverage.run] +branch = true +source = [ + "tmuxp", +] +omit = [ + "tests/*", + "*/_vendor/*", + "*/_*", + "pkg/*", + "*/log.py", +] + +[tool.coverage.report] +exclude_lines = [ + "pragma: no cover", + "def __repr__", + "raise NotImplementedError", + "if __name__ == .__main__.:", + "def parse_args", + "from __future__ import annotations", + "if TYPE_CHECKING:", + "if t.TYPE_CHECKING:", +] + +[tool.mypy] +strict = true +python_version = "3.9" +files = [ + "src/", + "tests/", +] +enable_incomplete_feature = [] + +[[tool.mypy.overrides]] +module = [ + "shtab", + "aafigure", + "IPython.*", + "ptpython.*", + "prompt_toolkit.*", + "bpython", +] +ignore_missing_imports = true + +[tool.ruff] +target-version = "py39" + +[tool.ruff.lint] +select = [ + "E", # pycodestyle + "F", # pyflakes + "I", # isort + "UP", # pyupgrade + "A", # flake8-builtins + "B", # flake8-bugbear + "C4", # flake8-comprehensions + "COM", # flake8-commas + "EM", # flake8-errmsg + "Q", # flake8-quotes + "PTH", # flake8-use-pathlib + "SIM", # flake8-simplify + "TRY", # Trycertatops + "PERF", # Perflint + "RUF", # Ruff-specific rules + "D", # pydocstyle + "FA100", # future annotations +] +ignore = [ + "COM812", # missing trailing comma, ruff format conflict +] +extend-safe-fixes = [ + "UP006", + "UP007", +] +pyupgrade.keep-runtime-typing = false + +[tool.ruff.lint.isort] +known-first-party = [ + "tmuxp", +] +combine-as-imports = true +required-imports = [ + "from __future__ import annotations", +] + +[tool.ruff.lint.flake8-builtins] +builtins-allowed-modules = [ + "types", +] + +[tool.ruff.lint.pydocstyle] +convention = "numpy" + +[tool.ruff.lint.per-file-ignores] +"*/__init__.py" = ["F401"] +"src/tmuxp/workspace/finders.py" = ["PTH"] +"src/tmuxp/cli/*.py" = ["PTH"] +"docs/_ext/aafig.py" = ["PTH"] + +[tool.pytest.ini_options] +addopts = "--reruns=0 --tb=short --no-header --showlocals --doctest-modules" +doctest_optionflags = "ELLIPSIS NORMALIZE_WHITESPACE" +testpaths = [ + "src/tmuxp", + "tests", + "docs", +] +filterwarnings = [ + "ignore:The frontend.Option(Parser)? class.*:DeprecationWarning::", + "ignore:invalid escape sequence:SyntaxWarning::", +] diff --git a/requirements.pip b/requirements.pip deleted file mode 100644 index 6b5fed8757d..00000000000 --- a/requirements.pip +++ /dev/null @@ -1,3 +0,0 @@ -kaptan -colorama -argcomplete diff --git a/run_tests.py b/run_tests.py deleted file mode 100755 index f9a309368a6..00000000000 --- a/run_tests.py +++ /dev/null @@ -1,189 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -from __future__ import absolute_import, division, print_function, with_statement - -import unittest -import sys -import os -import subprocess -import argparse -import tmuxp.testsuite -from tmuxp.util import tmux - -t = tmuxp.testsuite.t - -tmux_path = sys.path.insert(0, os.path.abspath(os.path.dirname(__file__))) -if tmux_path not in sys.path: - sys.path.insert(0, tmux_path) - -from time import sleep -import itertools - - -def main(verbosity=2, failfast=False): - - # from tmuxp import log - # import logging - - # logger = logging.getLogger() - # channel = logging.StreamHandler() - # channel.setFormatter(log.LogFormatter()) - # logger.setLevel('INFO') - # logger.addHandler(channel) - - def has_virtualenv(): - if os.environ.get('VIRTUAL_ENV'): - return os.environ.get('VIRTUAL_ENV') - else: - False - - def in_tmux(): - if os.environ.get('TMUX'): - return True - else: - return False - - tmuxclient = None - - def la(): - if not in_tmux(): - shell_commands = [] - if has_virtualenv(): - shell_commands.append( - 'source %s/bin/activate' % has_virtualenv()) - shell_commands.append('echo wat lol %s' % has_virtualenv()) - session_name = 'tmuxp' - t.tmux('new-session', '-d', '-s', session_name) - for shell_command in shell_commands: - t.tmux('send-keys', '-t', session_name, shell_command, '^M') - - t.tmux('send-keys', '-R', '-t', session_name, - 'python run_tests.py --pypid=%s' % os.getpid(), '^M') - - os.environ['pypid'] = str(os.getpid()) - - # os.execl('/usr/local/bin/tmux', 'tmux', 'attach-session', '-t', session_name) - # t.hotswap(session_name=session_name) - def output(line): - pass - # tmuxclient = t.tmux('-C') - # tmuxclient = subprocess.Popen(['tmux', '-C', '-Lhi', 'attach'], - # stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - else: - print(has_virtualenv()) - print(in_tmux()) - print(os.environ.get('pypid')) - args = vars(parser.parse_args()) - if 'pypid' in args: - print(args['pypid']) - - # todo create a hook to run after suite / loader to detach - # and killall tmuxp + tmuxp_-prefixed sessions. - # tmux('detach') - # os.kill(args['pypid'], 9) - # t.kill_server() - suites = unittest.TestLoader().discover( - 'tmuxp.testsuite', pattern="*.py") - result = unittest.TextTestRunner(verbosity=verbosity).run(suites) - if result.wasSuccessful(): - sys.exit(0) - else: - sys.exit(1) - session_name = 'tmuxp' - t.tmux('new-session', '-d', '-s', session_name) - suites = unittest.TestLoader().discover('tmuxp.testsuite', pattern="*.py") - result = unittest.TextTestRunner(verbosity=verbosity, failfast=failfast).run(suites) - if result.wasSuccessful(): - sys.exit(0) - else: - sys.exit(1) - -if __name__ == '__main__': - parser = argparse.ArgumentParser( - description='''\ - Run tests suite for tmuxp. With no arguments, runs all test suites in tmuxp.testsuite. - - Default usage: - $ ./run_tests.py - ''', - formatter_class=argparse.RawTextHelpFormatter - ) - parser.add_argument( - '--visual', - action='store_true', - help='''\ - Run the session builder testsuite in visual mode. requires having - tmux client in a second terminal open with: - - Terminal 1: - $ tmux -L tmuxp_test - - Terminal 2: - $ ./run_tests.py --visual - ''' - ) - parser.add_argument( - '--tests', - nargs='*', - default=None, - help='''\ - Test individual, TestCase or TestSuites, or multiple. Example for test_config TestSuite: - - by TestSuite (module): - $ ./run_tests.py test_config - - by TestCase: - $ ./run_tests.py test_config.ImportExportTest - individual tests: - $ ./run_tests.py test_config.ImportExportTest.test_export_json - - Multiple can be separated by spaces: - $ ./run_tests.py test_config.ImportExportTest.test_export_json \\ - test_config.ImportExportTest.test_window - - ./run_tests will automatically assume the package namespace ``tmuxp.testsuite``. - - $ ./run_tests.py test_config.ImportExportTest - - is the same as: - - $ ./run_tests.py tmuxp.testsuite.test_config.ImportExportTest - ''' - ) - parser.add_argument('-l', '--log-level', dest='log_level', default='INFO', - help='Log level') - parser.add_argument('-v', '--verbosity', dest='verbosity', type=int, default=2, - help='unittest verbosity level') - parser.add_argument('-F', '--failfast', dest='failfast', action='store_true', - - help='Stop on first test failure. failfast=True') - args = parser.parse_args() - - verbosity = args.verbosity - - import logging - logging.getLogger('tmuxp.testsuite').setLevel(args.log_level.upper()) - - if 'help' in args: - parser.print_help() - elif 'visual' in args and args.visual: - # todo, we can have this test build, and on completion, take the user - # to the new session with os.exec and attach the session. - loader = unittest.TestLoader() - suites = loader.loadTestsFromName('tmuxp.testsuite.test_builder') - result = unittest.TextTestRunner(verbosity=verbosity, failfast=args.failfast).run(suites) - - if result.wasSuccessful(): - sys.exit(0) - else: - sys.exit(1) - if args.tests and len(args.tests) > int(0): - for arg in args.tests: - if not arg.startswith('tmuxp.testsuite'): - loc = args.tests.index(arg) - args.tests[loc] = 'tmuxp.testsuite.%s' % arg - suites = unittest.TestLoader().loadTestsFromNames(args.tests) - result = unittest.TextTestRunner(verbosity=verbosity, failfast=args.failfast).run(suites) - else: - main(verbosity=verbosity, failfast=args.failfast) diff --git a/setup.py b/setup.py deleted file mode 100644 index 74b34c5278b..00000000000 --- a/setup.py +++ /dev/null @@ -1,62 +0,0 @@ -""" -tmuxp ------ - -Manage tmux workspaces from JSON and YAML, pythonic API, shell completion. - - -""" -from setuptools import setup -try: - from pip.req import parse_requirements -except ImportError: - def requirements(f): - reqs = open(f, 'r').read().splitlines() - reqs = [r for r in reqs if not r.strip().startswith('#')] - return reqs -else: - def requirements(f): - install_reqs = parse_requirements(f) - reqs = [str(r.req) for r in install_reqs] - return reqs - -import re -VERSIONFILE = "tmuxp/__init__.py" -verstrline = open(VERSIONFILE, "rt").read() -VSRE = r"^__version__ = ['\"]([^'\"]*)['\"]" -mo = re.search(VSRE, verstrline, re.M) -if mo: - __version__ = mo.group(1) -else: - raise RuntimeError("Unable to find version string in %s." % (VERSIONFILE,)) - - -setup( - name='tmuxp', - version=__version__, - url='http://github.com/tony/tmuxp/', - download_url='https://pypi.python.org/pypi/tmuxp', - license='BSD', - author='Tony Narlock', - author_email='tony@git-pull.com', - description='Manage tmux workspaces from JSON and YAML, pythonic API, ' - 'shell completion', - long_description=open('README.rst').read(), - packages=['tmuxp', 'tmuxp.testsuite'], - include_package_data=True, - install_requires=requirements('requirements.pip'), - scripts=['pkg/tmuxp.bash', 'pkg/tmuxp.zsh', 'pkg/tmuxp.tcsh'], - entry_points=dict(console_scripts=['tmuxp=tmuxp:main']), - classifiers=[ - 'Development Status :: 3 - Alpha', - "License :: OSI Approved :: BSD License", - "Operating System :: POSIX", - 'Environment :: Web Environment', - 'Intended Audience :: Developers', - 'Programming Language :: Python', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3.3', - "Topic :: Utilities", - "Topic :: System :: Shells", - ], -) diff --git a/src/tmuxp/__about__.py b/src/tmuxp/__about__.py new file mode 100644 index 00000000000..2651224455b --- /dev/null +++ b/src/tmuxp/__about__.py @@ -0,0 +1,17 @@ +"""Metadata for tmuxp package.""" + +from __future__ import annotations + +__title__ = "tmuxp" +__package_name__ = "tmuxp" +__version__ = "1.55.0" +__description__ = "tmux session manager" +__email__ = "tony@git-pull.com" +__author__ = "Tony Narlock" +__github__ = "https://github.com/tmux-python/tmuxp" +__docs__ = "https://tmuxp.git-pull.com" +__tracker__ = "https://github.com/tmux-python/tmuxp/issues" +__changes__ = "https://github.com/tmux-python/tmuxp/blob/master/CHANGES" +__pypi__ = "https://pypi.org/project/tmuxp/" +__license__ = "MIT" +__copyright__ = "Copyright 2013- Tony Narlock" diff --git a/src/tmuxp/__init__.py b/src/tmuxp/__init__.py new file mode 100644 index 00000000000..25c46f4d217 --- /dev/null +++ b/src/tmuxp/__init__.py @@ -0,0 +1,19 @@ +"""tmux session manager. + +:copyright: Copyright 2013- Tony Narlock. +:license: MIT, see LICENSE for details +""" + +from __future__ import annotations + +from . import cli, util +from .__about__ import ( + __author__, + __copyright__, + __description__, + __email__, + __license__, + __package_name__, + __title__, + __version__, +) diff --git a/src/tmuxp/_compat.py b/src/tmuxp/_compat.py new file mode 100644 index 00000000000..ca839626418 --- /dev/null +++ b/src/tmuxp/_compat.py @@ -0,0 +1,18 @@ +# flake8: NOQA +import sys + +PY3 = sys.version_info[0] == 3 +PYMINOR = sys.version_info[1] +PYPATCH = sys.version_info[2] + +_identity = lambda x: x + +if PY3 and PYMINOR >= 7: + breakpoint = breakpoint +else: + import pdb + + breakpoint = pdb.set_trace + + +implements_to_string = _identity diff --git a/src/tmuxp/_internal/__init__.py b/src/tmuxp/_internal/__init__.py new file mode 100644 index 00000000000..01dccbcfcb4 --- /dev/null +++ b/src/tmuxp/_internal/__init__.py @@ -0,0 +1 @@ +"""Internal APIs for tmuxp.""" diff --git a/src/tmuxp/_internal/config_reader.py b/src/tmuxp/_internal/config_reader.py new file mode 100644 index 00000000000..f7db20e48de --- /dev/null +++ b/src/tmuxp/_internal/config_reader.py @@ -0,0 +1,208 @@ +"""Configuration parser for YAML and JSON files.""" + +from __future__ import annotations + +import json +import pathlib +import typing as t + +import yaml + +if t.TYPE_CHECKING: + from typing_extensions import TypeAlias + + FormatLiteral = t.Literal["json", "yaml"] + + RawConfigData: TypeAlias = dict[t.Any, t.Any] + + +class ConfigReader: + r"""Parse string data (YAML and JSON) into a dictionary. + + >>> cfg = ConfigReader({ "session_name": "my session" }) + >>> cfg.dump("yaml") + 'session_name: my session\n' + >>> cfg.dump("json") + '{\n "session_name": "my session"\n}' + """ + + def __init__(self, content: RawConfigData) -> None: + self.content = content + + @staticmethod + def _load(fmt: FormatLiteral, content: str) -> dict[str, t.Any]: + """Load raw config data and directly return it. + + >>> ConfigReader._load("json", '{ "session_name": "my session" }') + {'session_name': 'my session'} + + >>> ConfigReader._load("yaml", 'session_name: my session') + {'session_name': 'my session'} + """ + if fmt == "yaml": + return t.cast( + "dict[str, t.Any]", + yaml.load( + content, + Loader=yaml.SafeLoader, + ), + ) + if fmt == "json": + return t.cast("dict[str, t.Any]", json.loads(content)) + msg = f"{fmt} not supported in configuration" + raise NotImplementedError(msg) + + @classmethod + def load(cls, fmt: FormatLiteral, content: str) -> ConfigReader: + """Load raw config data into a ConfigReader instance (to dump later). + + >>> cfg = ConfigReader.load("json", '{ "session_name": "my session" }') + >>> cfg + + >>> cfg.content + {'session_name': 'my session'} + + >>> cfg = ConfigReader.load("yaml", 'session_name: my session') + >>> cfg + + >>> cfg.content + {'session_name': 'my session'} + """ + return cls( + content=cls._load( + fmt=fmt, + content=content, + ), + ) + + @classmethod + def _from_file(cls, path: pathlib.Path) -> dict[str, t.Any]: + r"""Load data from file path directly to dictionary. + + **YAML file** + + *For demonstration only,* create a YAML file: + + >>> yaml_file = tmp_path / 'my_config.yaml' + >>> yaml_file.write_text('session_name: my session', encoding='utf-8') + 24 + + *Read YAML file*: + + >>> ConfigReader._from_file(yaml_file) + {'session_name': 'my session'} + + **JSON file** + + *For demonstration only,* create a JSON file: + + >>> json_file = tmp_path / 'my_config.json' + >>> json_file.write_text('{"session_name": "my session"}', encoding='utf-8') + 30 + + *Read JSON file*: + + >>> ConfigReader._from_file(json_file) + {'session_name': 'my session'} + """ + assert isinstance(path, pathlib.Path) + content = path.open().read() + + if path.suffix in {".yaml", ".yml"}: + fmt: FormatLiteral = "yaml" + elif path.suffix == ".json": + fmt = "json" + else: + msg = f"{path.suffix} not supported in {path}" + raise NotImplementedError(msg) + + return cls._load( + fmt=fmt, + content=content, + ) + + @classmethod + def from_file(cls, path: pathlib.Path) -> ConfigReader: + r"""Load data from file path. + + **YAML file** + + *For demonstration only,* create a YAML file: + + >>> yaml_file = tmp_path / 'my_config.yaml' + >>> yaml_file.write_text('session_name: my session', encoding='utf-8') + 24 + + *Read YAML file*: + + >>> cfg = ConfigReader.from_file(yaml_file) + >>> cfg + + + >>> cfg.content + {'session_name': 'my session'} + + **JSON file** + + *For demonstration only,* create a JSON file: + + >>> json_file = tmp_path / 'my_config.json' + >>> json_file.write_text('{"session_name": "my session"}', encoding='utf-8') + 30 + + *Read JSON file*: + + >>> cfg = ConfigReader.from_file(json_file) + >>> cfg + + + >>> cfg.content + {'session_name': 'my session'} + """ + return cls(content=cls._from_file(path=path)) + + @staticmethod + def _dump( + fmt: FormatLiteral, + content: RawConfigData, + indent: int = 2, + **kwargs: t.Any, + ) -> str: + r"""Dump directly. + + >>> ConfigReader._dump("yaml", { "session_name": "my session" }) + 'session_name: my session\n' + + >>> ConfigReader._dump("json", { "session_name": "my session" }) + '{\n "session_name": "my session"\n}' + """ + if fmt == "yaml": + return yaml.dump( + content, + indent=2, + default_flow_style=False, + Dumper=yaml.SafeDumper, + ) + if fmt == "json": + return json.dumps( + content, + indent=2, + ) + msg = f"{fmt} not supported in config" + raise NotImplementedError(msg) + + def dump(self, fmt: FormatLiteral, indent: int = 2, **kwargs: t.Any) -> str: + r"""Dump via ConfigReader instance. + + >>> cfg = ConfigReader({ "session_name": "my session" }) + >>> cfg.dump("yaml") + 'session_name: my session\n' + >>> cfg.dump("json") + '{\n "session_name": "my session"\n}' + """ + return self._dump( + fmt=fmt, + content=self.content, + indent=indent, + **kwargs, + ) diff --git a/src/tmuxp/_internal/types.py b/src/tmuxp/_internal/types.py new file mode 100644 index 00000000000..a3521f5832a --- /dev/null +++ b/src/tmuxp/_internal/types.py @@ -0,0 +1,37 @@ +"""Internal, :const:`typing.TYPE_CHECKING` guarded :term:`typings `. + +These are _not_ to be imported at runtime as `typing_extensions` is not +bundled with tmuxp. Usage example: + +>>> import typing as t + +>>> if t.TYPE_CHECKING: +... from tmuxp._internal.types import PluginConfigSchema +... +""" + +from __future__ import annotations + +import typing as t +from typing import TypedDict + +if t.TYPE_CHECKING: + import sys + + if sys.version_info >= (3, 11): + from typing import NotRequired + else: + from typing_extensions import NotRequired + + +class PluginConfigSchema(TypedDict): + plugin_name: NotRequired[str] + tmux_min_version: NotRequired[str] + tmux_max_version: NotRequired[str] + tmux_version_incompatible: NotRequired[list[str]] + libtmux_min_version: NotRequired[str] + libtmux_max_version: NotRequired[str] + libtmux_version_incompatible: NotRequired[list[str]] + tmuxp_min_version: NotRequired[str] + tmuxp_max_version: NotRequired[str] + tmuxp_version_incompatible: NotRequired[list[str]] diff --git a/src/tmuxp/cli/__init__.py b/src/tmuxp/cli/__init__.py new file mode 100644 index 00000000000..8af6a50547f --- /dev/null +++ b/src/tmuxp/cli/__init__.py @@ -0,0 +1,205 @@ +"""CLI utilities for tmuxp.""" + +from __future__ import annotations + +import argparse +import logging +import os +import sys +import typing as t + +from libtmux.__about__ import __version__ as libtmux_version +from libtmux.common import has_minimum_version +from libtmux.exc import TmuxCommandNotFound + +from tmuxp import exc +from tmuxp.__about__ import __version__ +from tmuxp.log import setup_logger + +from .convert import command_convert, create_convert_subparser +from .debug_info import command_debug_info, create_debug_info_subparser +from .edit import command_edit, create_edit_subparser +from .freeze import CLIFreezeNamespace, command_freeze, create_freeze_subparser +from .import_config import ( + command_import_teamocil, + command_import_tmuxinator, + create_import_subparser, +) +from .load import CLILoadNamespace, command_load, create_load_subparser +from .ls import command_ls, create_ls_subparser +from .shell import CLIShellNamespace, command_shell, create_shell_subparser +from .utils import tmuxp_echo + +logger = logging.getLogger(__name__) + +if t.TYPE_CHECKING: + import pathlib + + from typing_extensions import TypeAlias + + CLIVerbosity: TypeAlias = t.Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] + CLISubparserName: TypeAlias = t.Literal[ + "ls", + "load", + "freeze", + "convert", + "edit", + "import", + "shell", + "debug-info", + ] + CLIImportSubparserName: TypeAlias = t.Literal["teamocil", "tmuxinator"] + + +def create_parser() -> argparse.ArgumentParser: + """Create CLI :class:`argparse.ArgumentParser` for tmuxp.""" + parser = argparse.ArgumentParser(prog="tmuxp") + parser.add_argument( + "--version", + "-V", + action="version", + version=f"%(prog)s {__version__}, libtmux {libtmux_version}", + ) + parser.add_argument( + "--log-level", + action="store", + metavar="log-level", + default="info", + choices=["debug", "info", "warning", "error", "critical"], + help='log level (debug, info, warning, error, critical) (default "info")', + ) + subparsers = parser.add_subparsers(dest="subparser_name") + load_parser = subparsers.add_parser("load", help="load tmuxp workspaces") + create_load_subparser(load_parser) + shell_parser = subparsers.add_parser( + "shell", + help="launch python shell for tmux server, session, window and pane", + ) + create_shell_subparser(shell_parser) + import_parser = subparsers.add_parser( + "import", + help="import workspaces from teamocil and tmuxinator.", + ) + create_import_subparser(import_parser) + + convert_parser = subparsers.add_parser( + "convert", + help="convert workspace files between yaml and json.", + ) + create_convert_subparser(convert_parser) + + debug_info_parser = subparsers.add_parser( + "debug-info", + help="print out all diagnostic info", + ) + create_debug_info_subparser(debug_info_parser) + + ls_parser = subparsers.add_parser("ls", help="list workspaces in tmuxp directory") + create_ls_subparser(ls_parser) + + edit_parser = subparsers.add_parser("edit", help="run $EDITOR on workspace file") + create_edit_subparser(edit_parser) + + freeze_parser = subparsers.add_parser( + "freeze", + help="freeze a live tmux session to a tmuxp workspace file", + ) + create_freeze_subparser(freeze_parser) + + return parser + + +class CLINamespace(argparse.Namespace): + """Typed :class:`argparse.Namespace` for tmuxp root-level CLI.""" + + log_level: CLIVerbosity + subparser_name: CLISubparserName + import_subparser_name: CLIImportSubparserName | None + version: bool + + +ns = CLINamespace() + + +def cli(_args: list[str] | None = None) -> None: + """Manage tmux sessions. + + Pass the "--help" argument to any command to see detailed help. + See detailed documentation and examples at: + http://tmuxp.git-pull.com/ + """ + try: + has_minimum_version() + except TmuxCommandNotFound: + tmuxp_echo("tmux not found. tmuxp requires you install tmux first.") + sys.exit() + except exc.TmuxpException as e: + tmuxp_echo(str(e)) + sys.exit() + + parser = create_parser() + args = parser.parse_args(_args, namespace=ns) + + setup_logger(logger=logger, level=args.log_level.upper()) + + if args.subparser_name is None: + parser.print_help() + return + if args.subparser_name == "load": + command_load( + args=CLILoadNamespace(**vars(args)), + parser=parser, + ) + elif args.subparser_name == "shell": + command_shell( + args=CLIShellNamespace(**vars(args)), + parser=parser, + ) + elif args.subparser_name == "import": + import_subparser_name = getattr(args, "import_subparser_name", None) + if import_subparser_name is None: + parser.print_help() + return + if import_subparser_name == "teamocil": + command_import_teamocil( + workspace_file=args.workspace_file, + parser=parser, + ) + elif import_subparser_name == "tmuxinator": + command_import_tmuxinator( + workspace_file=args.workspace_file, + parser=parser, + ) + elif args.subparser_name == "convert": + command_convert( + workspace_file=args.workspace_file, + answer_yes=args.answer_yes, + parser=parser, + ) + elif args.subparser_name == "debug-info": + command_debug_info(parser=parser) + + elif args.subparser_name == "edit": + command_edit( + workspace_file=args.workspace_file, + parser=parser, + ) + elif args.subparser_name == "freeze": + command_freeze( + args=CLIFreezeNamespace(**vars(args)), + parser=parser, + ) + elif args.subparser_name == "ls": + command_ls(parser=parser) + + +def startup(config_dir: pathlib.Path) -> None: + """ + Initialize CLI. + + Parameters + ---------- + str : get_workspace_dir(): Config directory to search + """ + if not os.path.exists(config_dir): + os.makedirs(config_dir) diff --git a/src/tmuxp/cli/convert.py b/src/tmuxp/cli/convert.py new file mode 100644 index 00000000000..aee50356f77 --- /dev/null +++ b/src/tmuxp/cli/convert.py @@ -0,0 +1,101 @@ +"""CLI for ``tmuxp convert`` subcommand.""" + +from __future__ import annotations + +import locale +import os +import pathlib +import typing as t + +from tmuxp import exc +from tmuxp._internal.config_reader import ConfigReader +from tmuxp.workspace.finders import find_workspace_file, get_workspace_dir + +from .utils import prompt_yes_no + +if t.TYPE_CHECKING: + import argparse + + AllowedFileTypes = t.Literal["json", "yaml"] + + +def create_convert_subparser( + parser: argparse.ArgumentParser, +) -> argparse.ArgumentParser: + """Augment :class:`argparse.ArgumentParser` with ``convert`` subcommand.""" + workspace_file = parser.add_argument( + dest="workspace_file", + type=str, + metavar="workspace-file", + help="checks tmuxp and current directory for workspace files.", + ) + try: + import shtab + + workspace_file.complete = shtab.FILE # type: ignore + except ImportError: + pass + + parser.add_argument( + "--yes", + "-y", + dest="answer_yes", + action="store_true", + help="always answer yes", + ) + return parser + + +class ConvertUnknownFileType(exc.TmuxpException): + """Raise if tmuxp convert encounters an unknown filetype.""" + + def __init__(self, ext: str, *args: object, **kwargs: object) -> None: + return super().__init__( + f"Unknown filetype: {ext} (valid: [.json, .yaml, .yml])", + ) + + +def command_convert( + workspace_file: str | pathlib.Path, + answer_yes: bool, + parser: argparse.ArgumentParser | None = None, +) -> None: + """Entrypoint for ``tmuxp convert`` convert a tmuxp config between JSON and YAML.""" + workspace_file = find_workspace_file( + workspace_file, + workspace_dir=get_workspace_dir(), + ) + + if isinstance(workspace_file, str): + workspace_file = pathlib.Path(workspace_file) + + _, ext = os.path.splitext(workspace_file) + ext = ext.lower() + to_filetype: AllowedFileTypes + if ext == ".json": + to_filetype = "yaml" + elif ext in {".yaml", ".yml"}: + to_filetype = "json" + else: + raise ConvertUnknownFileType(ext) + + configparser = ConfigReader.from_file(workspace_file) + newfile = workspace_file.parent / (str(workspace_file.stem) + f".{to_filetype}") + + new_workspace = configparser.dump( + fmt=to_filetype, + indent=2, + **{"default_flow_style": False} if to_filetype == "yaml" else {}, + ) + + if ( + not answer_yes + and prompt_yes_no(f"Convert to <{workspace_file}> to {to_filetype}?") + and prompt_yes_no(f"Save workspace to {newfile}?") + ): + answer_yes = True + + if answer_yes: + with open(newfile, "w", encoding=locale.getpreferredencoding(False)) as buf: + buf.write(new_workspace) + print(f"New workspace file saved to <{newfile}>.") # NOQA: T201 RUF100 diff --git a/src/tmuxp/cli/debug_info.py b/src/tmuxp/cli/debug_info.py new file mode 100644 index 00000000000..fdd55c83fdc --- /dev/null +++ b/src/tmuxp/cli/debug_info.py @@ -0,0 +1,92 @@ +"""CLI for ``tmuxp debug-info`` subcommand.""" + +from __future__ import annotations + +import os +import pathlib +import platform +import shutil +import sys +import typing as t + +from colorama import Fore +from libtmux.__about__ import __version__ as libtmux_version +from libtmux.common import get_version, tmux_cmd + +from tmuxp.__about__ import __version__ + +from .utils import tmuxp_echo + +if t.TYPE_CHECKING: + import argparse + +tmuxp_path = pathlib.Path(__file__).parent.parent + + +def create_debug_info_subparser( + parser: argparse.ArgumentParser, +) -> argparse.ArgumentParser: + """Augment :class:`argparse.ArgumentParser` with ``debug-info`` subcommand.""" + return parser + + +def command_debug_info( + parser: argparse.ArgumentParser | None = None, +) -> None: + """Entrypoint for ``tmuxp debug-info`` to print debug info to submit with issues.""" + + def prepend_tab(strings: list[str]) -> list[str]: + """Prepend tab to strings in list.""" + return [f"\t{x}" for x in strings] + + def output_break() -> str: + """Generate output break.""" + return "-" * 25 + + def format_tmux_resp(std_resp: tmux_cmd) -> str: + """Format tmux command response for tmuxp stdout.""" + return "\n".join( + [ + "\n".join(prepend_tab(std_resp.stdout)), + Fore.RED, + "\n".join(prepend_tab(std_resp.stderr)), + Fore.RESET, + ], + ) + + output = [ + output_break(), + "environment:\n{}".format( + "\n".join( + prepend_tab( + [ + f"dist: {platform.platform()}", + f"arch: {platform.machine()}", + "uname: {}".format("; ".join(platform.uname()[:3])), + f"version: {platform.version()}", + ], + ), + ), + ), + output_break(), + "python version: {}".format(" ".join(sys.version.split("\n"))), + "system PATH: {}".format(os.environ["PATH"]), + f"tmux version: {get_version()}", + f"libtmux version: {libtmux_version}", + f"tmuxp version: {__version__}", + "tmux path: {}".format(shutil.which("tmux")), + f"tmuxp path: {tmuxp_path}", + "shell: {}".format(os.environ["SHELL"]), + output_break(), + "tmux sessions:\n{}".format(format_tmux_resp(tmux_cmd("list-sessions"))), + "tmux windows:\n{}".format(format_tmux_resp(tmux_cmd("list-windows"))), + "tmux panes:\n{}".format(format_tmux_resp(tmux_cmd("list-panes"))), + "tmux global options:\n{}".format( + format_tmux_resp(tmux_cmd("show-options", "-g")), + ), + "tmux window options:\n{}".format( + format_tmux_resp(tmux_cmd("show-window-options", "-g")), + ), + ] + + tmuxp_echo("\n".join(output)) diff --git a/src/tmuxp/cli/edit.py b/src/tmuxp/cli/edit.py new file mode 100644 index 00000000000..075ca201dd7 --- /dev/null +++ b/src/tmuxp/cli/edit.py @@ -0,0 +1,37 @@ +"""CLI for ``tmuxp edit`` subcommand.""" + +from __future__ import annotations + +import os +import subprocess +import typing as t + +from tmuxp.workspace.finders import find_workspace_file + +if t.TYPE_CHECKING: + import argparse + import pathlib + + +def create_edit_subparser( + parser: argparse.ArgumentParser, +) -> argparse.ArgumentParser: + """Augment :class:`argparse.ArgumentParser` with ``edit`` subcommand.""" + parser.add_argument( + dest="workspace_file", + metavar="workspace-file", + type=str, + help="checks current tmuxp and current directory for workspace files.", + ) + return parser + + +def command_edit( + workspace_file: str | pathlib.Path, + parser: argparse.ArgumentParser | None = None, +) -> None: + """Entrypoint for ``tmuxp edit``, open tmuxp workspace file in system editor.""" + workspace_file = find_workspace_file(workspace_file) + + sys_editor = os.environ.get("EDITOR", "vim") + subprocess.call([sys_editor, workspace_file]) diff --git a/src/tmuxp/cli/freeze.py b/src/tmuxp/cli/freeze.py new file mode 100644 index 00000000000..fcff1004041 --- /dev/null +++ b/src/tmuxp/cli/freeze.py @@ -0,0 +1,217 @@ +"""CLI for ``tmuxp freeze`` subcommand.""" + +from __future__ import annotations + +import argparse +import locale +import os +import pathlib +import sys +import typing as t + +from libtmux.server import Server + +from tmuxp import exc, util +from tmuxp._internal.config_reader import ConfigReader +from tmuxp.exc import TmuxpException +from tmuxp.workspace import freezer +from tmuxp.workspace.finders import get_workspace_dir + +from .utils import prompt, prompt_choices, prompt_yes_no + +if t.TYPE_CHECKING: + from typing_extensions import TypeAlias, TypeGuard + + CLIOutputFormatLiteral: TypeAlias = t.Literal["yaml", "json"] + + +class CLIFreezeNamespace(argparse.Namespace): + """Typed :class:`argparse.Namespace` for tmuxp freeze command.""" + + session_name: str + socket_name: str | None + socket_path: str | None + workspace_format: CLIOutputFormatLiteral | None + save_to: str | None + answer_yes: bool | None + quiet: bool | None + force: bool | None + + +def create_freeze_subparser( + parser: argparse.ArgumentParser, +) -> argparse.ArgumentParser: + """Augment :class:`argparse.ArgumentParser` with ``freeze`` subcommand.""" + parser.add_argument( + dest="session_name", + metavar="session-name", + nargs="?", + action="store", + ) + parser.add_argument( + "-S", + dest="socket_path", + metavar="socket-path", + help="pass-through for tmux -S", + ) + parser.add_argument( + "-L", + dest="socket_name", + metavar="socket-name", + help="pass-through for tmux -L", + ) + parser.add_argument( + "-f", + "--workspace-format", + choices=["yaml", "json"], + help="format to save in", + ) + parser.add_argument( + "-o", + "--save-to", + metavar="output-path", + type=pathlib.Path, + help="file to save to", + ) + parser.add_argument( + "--yes", + "-y", + dest="answer_yes", + action="store_true", + help="always answer yes", + ) + parser.add_argument( + "--quiet", + "-q", + dest="quiet", + action="store_true", + help="don't prompt for confirmation", + ) + parser.add_argument( + "--force", + dest="force", + action="store_true", + help="overwrite the workspace file", + ) + + return parser + + +def command_freeze( + args: CLIFreezeNamespace, + parser: argparse.ArgumentParser | None = None, +) -> None: + """Entrypoint for ``tmuxp freeze``, snapshot a tmux session into a tmuxp workspace. + + If SESSION_NAME is provided, snapshot that session. Otherwise, use the current + session. + """ + server = Server(socket_name=args.socket_name, socket_path=args.socket_path) + + try: + if args.session_name: + session = server.sessions.get(session_name=args.session_name, default=None) + else: + session = util.get_session(server) + + if not session: + raise exc.SessionNotFound + except TmuxpException as e: + print(e) # NOQA: T201 RUF100 + return + + frozen_workspace = freezer.freeze(session) + workspace = freezer.inline(frozen_workspace) + configparser = ConfigReader(workspace) + + if not args.quiet: + print( # NOQA: T201 RUF100 + "---------------------------------------------------------------" + "\n" + "Freeze does its best to snapshot live tmux sessions.\n", + ) + if not ( + args.answer_yes + or prompt_yes_no( + "The new workspace will require adjusting afterwards. Save workspace file?", + ) + ): + if not args.quiet: + print( # NOQA: T201 RUF100 + "tmuxp has examples in JSON and YAML format at " + "\n" + "View tmuxp docs at .", + ) + sys.exit() + + dest = args.save_to + while not dest: + save_to = os.path.abspath( + os.path.join( + get_workspace_dir(), + "{}.{}".format( + frozen_workspace.get("session_name"), + args.workspace_format or "yaml", + ), + ), + ) + dest_prompt = prompt( + f"Save to: {save_to}", + default=save_to, + ) + if not args.force and os.path.exists(dest_prompt): + print(f"{dest_prompt} exists. Pick a new filename.") # NOQA: T201 RUF100 + continue + + dest = dest_prompt + dest = os.path.abspath(os.path.relpath(os.path.expanduser(dest))) + workspace_format = args.workspace_format + + valid_workspace_formats: list[CLIOutputFormatLiteral] = ["json", "yaml"] + + def is_valid_ext(stem: str | None) -> TypeGuard[CLIOutputFormatLiteral]: + return stem in valid_workspace_formats + + if not is_valid_ext(workspace_format): + + def extract_workspace_format( + val: str, + ) -> CLIOutputFormatLiteral | None: + suffix = pathlib.Path(val).suffix + if isinstance(suffix, str): + suffix = suffix.lower().lstrip(".") + if is_valid_ext(suffix): + return suffix + return None + + workspace_format = extract_workspace_format(dest) + if not is_valid_ext(workspace_format): + workspace_format_ = prompt_choices( + "Couldn't ascertain one of [{}] from file name. Convert to".format( + ", ".join(valid_workspace_formats), + ), + choices=t.cast("list[str]", valid_workspace_formats), + default="yaml", + ) + assert is_valid_ext(workspace_format_) + workspace_format = workspace_format_ + + if workspace_format == "yaml": + workspace = configparser.dump( + fmt="yaml", + indent=2, + default_flow_style=False, + safe=True, + ) + elif workspace_format == "json": + workspace = configparser.dump(fmt="json", indent=2) + + if args.answer_yes or prompt_yes_no(f"Save to {dest}?"): + destdir = os.path.dirname(dest) + if not os.path.isdir(destdir): + os.makedirs(destdir) + with open(dest, "w", encoding=locale.getpreferredencoding(False)) as buf: + buf.write(workspace) + + if not args.quiet: + print(f"Saved to {dest}.") # NOQA: T201 RUF100 diff --git a/src/tmuxp/cli/import_config.py b/src/tmuxp/cli/import_config.py new file mode 100644 index 00000000000..a014e1558e3 --- /dev/null +++ b/src/tmuxp/cli/import_config.py @@ -0,0 +1,224 @@ +"""CLI for ``tmuxp shell`` subcommand.""" + +from __future__ import annotations + +import locale +import os +import pathlib +import sys +import typing as t + +from tmuxp._internal.config_reader import ConfigReader +from tmuxp.workspace import importers +from tmuxp.workspace.finders import find_workspace_file + +from .utils import prompt, prompt_choices, prompt_yes_no, tmuxp_echo + +if t.TYPE_CHECKING: + import argparse + + +def get_tmuxinator_dir() -> pathlib.Path: + """Return tmuxinator configuration directory. + + Checks for ``TMUXINATOR_CONFIG`` environmental variable. + + Returns + ------- + pathlib.Path : + absolute path to tmuxinator config directory + + See Also + -------- + :meth:`tmuxp.workspace.importers.import_tmuxinator` + """ + if "TMUXINATOR_CONFIG" in os.environ: + return pathlib.Path(os.environ["TMUXINATOR_CONFIG"]).expanduser() + + return pathlib.Path("~/.tmuxinator/").expanduser() + + +def get_teamocil_dir() -> pathlib.Path: + """Return teamocil configuration directory. + + Returns + ------- + pathlib.Path : + absolute path to teamocil config directory + + See Also + -------- + :meth:`tmuxp.workspace.importers.import_teamocil` + """ + return pathlib.Path("~/.teamocil/").expanduser() + + +def _resolve_path_no_overwrite(workspace_file: str) -> str: + path = pathlib.Path(workspace_file).resolve() + if path.exists(): + msg = f"{path} exists. Pick a new filename." + raise ValueError(msg) + return str(path) + + +def command_import( + workspace_file: str, + print_list: str, + parser: argparse.ArgumentParser, +) -> None: + """Import a teamocil/tmuxinator config.""" + + +def create_import_subparser( + parser: argparse.ArgumentParser, +) -> argparse.ArgumentParser: + """Augment :class:`argparse.ArgumentParser` with ``import`` subparser.""" + importsubparser = parser.add_subparsers( + title="commands", + description="valid commands", + help="additional help", + ) + + import_teamocil = importsubparser.add_parser( + "teamocil", + help="convert and import a teamocil config", + ) + + import_teamocilgroup = import_teamocil.add_mutually_exclusive_group(required=True) + teamocil_workspace_file = import_teamocilgroup.add_argument( + dest="workspace_file", + type=str, + nargs="?", + metavar="workspace-file", + help="checks current ~/.teamocil and current directory for yaml files", + ) + import_teamocil.set_defaults( + callback=command_import_teamocil, + import_subparser_name="teamocil", + ) + + import_tmuxinator = importsubparser.add_parser( + "tmuxinator", + help="convert and import a tmuxinator config", + ) + + import_tmuxinatorgroup = import_tmuxinator.add_mutually_exclusive_group( + required=True, + ) + tmuxinator_workspace_file = import_tmuxinatorgroup.add_argument( + dest="workspace_file", + type=str, + nargs="?", + metavar="workspace-file", + help="checks current ~/.tmuxinator and current directory for yaml files", + ) + + import_tmuxinator.set_defaults( + callback=command_import_tmuxinator, + import_subparser_name="tmuxinator", + ) + + try: + import shtab + + teamocil_workspace_file.complete = shtab.FILE # type: ignore + tmuxinator_workspace_file.complete = shtab.FILE # type: ignore + except ImportError: + pass + + return parser + + +class ImportConfigFn(t.Protocol): + """Typing for import configuration callback function.""" + + def __call__(self, workspace_dict: dict[str, t.Any]) -> dict[str, t.Any]: + """Execute tmuxp import function.""" + ... + + +def import_config( + workspace_file: str, + importfunc: ImportConfigFn, + parser: argparse.ArgumentParser | None = None, +) -> None: + """Import a configuration from a workspace_file.""" + existing_workspace_file = ConfigReader._from_file(pathlib.Path(workspace_file)) + cfg_reader = ConfigReader(importfunc(existing_workspace_file)) + + workspace_file_format = prompt_choices( + "Convert to", + choices=["yaml", "json"], + default="yaml", + ) + + if workspace_file_format == "yaml": + new_config = cfg_reader.dump("yaml", indent=2, default_flow_style=False) + elif workspace_file_format == "json": + new_config = cfg_reader.dump("json", indent=2) + else: + sys.exit("Unknown config format.") + + tmuxp_echo( + new_config + "---------------------------------------------------------------" + "\n" + "Configuration import does its best to convert files.\n", + ) + if prompt_yes_no( + "The new config *WILL* require adjusting afterwards. Save config?", + ): + dest = None + while not dest: + dest_path = prompt( + f"Save to [{os.getcwd()}]", + value_proc=_resolve_path_no_overwrite, + ) + + # dest = dest_prompt + if prompt_yes_no(f"Save to {dest_path}?"): + dest = dest_path + + with open(dest, "w", encoding=locale.getpreferredencoding(False)) as buf: + buf.write(new_config) + + tmuxp_echo(f"Saved to {dest}.") + else: + tmuxp_echo( + "tmuxp has examples in JSON and YAML format at " + "\n" + "View tmuxp docs at ", + ) + sys.exit() + + +def command_import_tmuxinator( + workspace_file: str, + parser: argparse.ArgumentParser | None = None, +) -> None: + """Entrypoint for ``tmuxp import tmuxinator`` subcommand. + + Converts a tmuxinator config from workspace_file to tmuxp format and import + it into tmuxp. + """ + workspace_file = find_workspace_file( + workspace_file, + workspace_dir=get_tmuxinator_dir(), + ) + import_config(workspace_file, importers.import_tmuxinator) + + +def command_import_teamocil( + workspace_file: str, + parser: argparse.ArgumentParser | None = None, +) -> None: + """Entrypoint for ``tmuxp import teamocil`` subcommand. + + Convert a teamocil config from workspace_file to tmuxp format and import + it into tmuxp. + """ + workspace_file = find_workspace_file( + workspace_file, + workspace_dir=get_teamocil_dir(), + ) + + import_config(workspace_file, importers.import_teamocil) diff --git a/src/tmuxp/cli/load.py b/src/tmuxp/cli/load.py new file mode 100644 index 00000000000..fa65e1b8f5a --- /dev/null +++ b/src/tmuxp/cli/load.py @@ -0,0 +1,586 @@ +"""CLI for ``tmuxp load`` subcommand.""" + +from __future__ import annotations + +import argparse +import importlib +import logging +import os +import pathlib +import shutil +import sys +import typing as t + +from libtmux.server import Server + +from tmuxp import exc, log, util +from tmuxp._internal import config_reader +from tmuxp.workspace import loader +from tmuxp.workspace.builder import WorkspaceBuilder +from tmuxp.workspace.finders import find_workspace_file, get_workspace_dir + +from .utils import prompt_choices, prompt_yes_no, style, tmuxp_echo + +if t.TYPE_CHECKING: + from libtmux.session import Session + from typing_extensions import NotRequired, TypeAlias, TypedDict + + from tmuxp.types import StrPath + + CLIColorsLiteral: TypeAlias = t.Literal[56, 88] + + class OptionOverrides(TypedDict): + """Optional argument overrides for tmuxp load.""" + + detached: NotRequired[bool] + new_session_name: NotRequired[str | None] + + +class CLILoadNamespace(argparse.Namespace): + """Typed :class:`argparse.Namespace` for tmuxp load command.""" + + workspace_files: list[str] + socket_name: str | None + socket_path: str | None + tmux_config_file: str | None + new_session_name: str | None + answer_yes: bool | None + append: bool | None + colors: CLIColorsLiteral | None + log_file: str | None + + +def load_plugins(session_config: dict[str, t.Any]) -> list[t.Any]: + """Load and return plugins in workspace.""" + plugins = [] + if "plugins" in session_config: + for plugin in session_config["plugins"]: + try: + module_name = plugin.split(".") + module_name = ".".join(module_name[:-1]) + plugin_name = plugin.split(".")[-1] + except Exception as error: + tmuxp_echo( + style("[Plugin Error] ", fg="red") + + f"Couldn't load {plugin}\n" + + style(f"{error}", fg="yellow"), + ) + sys.exit(1) + + try: + plugin = getattr(importlib.import_module(module_name), plugin_name) + plugins.append(plugin()) + except exc.TmuxpPluginException as error: + if not prompt_yes_no( + "{}Skip loading {}?".format( + style( + str(error), + fg="yellow", + ), + plugin_name, + ), + default=True, + ): + tmuxp_echo( + style("[Not Skipping] ", fg="yellow") + + "Plugin versions constraint not met. Exiting...", + ) + sys.exit(1) + except Exception as error: + tmuxp_echo( + style("[Plugin Error] ", fg="red") + + f"Couldn't load {plugin}\n" + + style(f"{error}", fg="yellow"), + ) + sys.exit(1) + + return plugins + + +def _reattach(builder: WorkspaceBuilder) -> None: + """ + Reattach session (depending on env being inside tmux already or not). + + Parameters + ---------- + builder: :class:`workspace.builder.WorkspaceBuilder` + + Notes + ----- + If ``TMUX`` environmental variable exists in the environment this script is + running, that means we're in a tmux client. So ``tmux switch-client`` will + load the session. + + If not, ``tmux attach-session`` loads the client to the target session. + """ + assert builder.session is not None + for plugin in builder.plugins: + plugin.reattach(builder.session) + proc = builder.session.cmd("display-message", "-p", "'#S'") + for line in proc.stdout: + print(line) # NOQA: T201 RUF100 + + if "TMUX" in os.environ: + builder.session.switch_client() + + else: + builder.session.attach_session() + + +def _load_attached(builder: WorkspaceBuilder, detached: bool) -> None: + """ + Load workspace in new session. + + Parameters + ---------- + builder: :class:`workspace.builder.WorkspaceBuilder` + detached : bool + """ + builder.build() + assert builder.session is not None + + if "TMUX" in os.environ: # tmuxp ran from inside tmux + # unset TMUX, save it, e.g. '/tmp/tmux-1000/default,30668,0' + tmux_env = os.environ.pop("TMUX") + + builder.session.switch_client() # switch client to new session + + os.environ["TMUX"] = tmux_env # set TMUX back again + elif not detached: + builder.session.attach_session() + + +def _load_detached(builder: WorkspaceBuilder) -> None: + """ + Load workspace in new session but don't attach. + + Parameters + ---------- + builder: :class:`workspace.builder.WorkspaceBuilder` + """ + builder.build() + + assert builder.session is not None + + print("Session created in detached state.") # NOQA: T201 RUF100 + + +def _load_append_windows_to_current_session(builder: WorkspaceBuilder) -> None: + """ + Load workspace as new windows in current session. + + Parameters + ---------- + builder: :class:`workspace.builder.WorkspaceBuilder` + """ + current_attached_session = builder.find_current_attached_session() + builder.build(current_attached_session, append=True) + assert builder.session is not None + + +def _setup_plugins(builder: WorkspaceBuilder) -> Session: + """Execute hooks for plugins running after ``before_script``. + + Parameters + ---------- + builder: :class:`workspace.builder.WorkspaceBuilder` + """ + assert builder.session is not None + for plugin in builder.plugins: + plugin.before_script(builder.session) + + return builder.session + + +def load_workspace( + workspace_file: StrPath, + socket_name: str | None = None, + socket_path: None = None, + tmux_config_file: str | None = None, + new_session_name: str | None = None, + colors: int | None = None, + detached: bool = False, + answer_yes: bool = False, + append: bool = False, +) -> Session | None: + """Entrypoint for ``tmuxp load``, load a tmuxp "workspace" session via config file. + + Parameters + ---------- + workspace_file : list of str + paths or session names to workspace files + socket_name : str, optional + ``tmux -L `` + socket_path: str, optional + ``tmux -S `` + new_session_name: str, options + ``tmux new -s `` + colors : str, optional + '-2' + Force tmux to support 256 colors + detached : bool + Force detached state. default False. + answer_yes : bool + Assume yes when given prompt to attach in new session. + Default False. + append : bool + Assume current when given prompt to append windows in same session. + Default False. + + Notes + ----- + tmuxp will check and load a workspace file. The file will use ConfigReader + to load a JSON/YAML into a :py:obj:`dict`. Then :func:`loader.expand` and + :func:`loader.trickle` will be used to expand any shorthands, template + variables, or file paths relative to where the config/script is executed + from. + + :func:`loader.expand` accepts the directory of the config file, so the + user's workspace can resolve absolute paths relative to where the + workspace file is. In otherwords, if a workspace file at */var/moo/hi.yaml* + has *./* in its workspaces, we want to be sure any file path with *./* is + relative to */var/moo*, not the user's PWD. + + A :class:`libtmux.Server` object is created. No tmux server is started yet, + just the object. + + The prepared workspace and its server object is passed into an instance + of :class:`~tmuxp.workspace.builder.WorkspaceBuilder`. + + A sanity check against :meth:`libtmux.common.which` is ran. It will raise + an exception if tmux isn't found. + + If a tmux session under the same name as ``session_name`` in the tmuxp + workspace exists, tmuxp offers to attach the session. Currently, tmuxp + does not allow appending a workspace / incremental building on top of a + current session (pull requests are welcome!). + + :meth:`~tmuxp.workspace.builder.build` will build the session in + the background via using tmux's detached state (``-d``). + + After the session (workspace) is built, unless the user decided to load + the session in the background via ``tmuxp -d`` (which is in the spirit + of tmux's ``-d``), we need to prompt the user to attach the session. + + If the user is already inside a tmux client, which we detect via the + ``TMUX`` environment variable bring present, we will prompt the user to + switch their current client to it. + + If they're outside of tmux client - in a plain-old PTY - we will + automatically ``attach``. + + If an exception is raised during the building of the workspace, tmuxp will + prompt to cleanup (``$ tmux kill-session``) the session on the user's + behalf. An exception raised during this process means it's not easy to + predict how broken the session is. + + .. versionchanged:: tmux 2.6+ + + In tmux 2.6, the way layout and proportion's work when interfacing + with tmux in a detached state (outside of a client) changed. Since + tmuxp builds workspaces in a detached state, the WorkspaceBuilder isn't + able to rely on functionality requiring awarness of session geometry, + e.g. ``set-layout``. + + Thankfully, tmux is able to defer commands to run after the user + performs certain actions, such as loading a client via + ``attach-session`` or ``switch-client``. + + Upon client switch, ``client-session-changed`` is triggered [1]_. + + References + ---------- + .. [1] cmd-switch-client.c hook. GitHub repo for tmux. + + https://github.com/tmux/tmux/blob/2.6/cmd-switch-client.c#L132. + Accessed April 8th, 2018. + """ + # get the canonical path, eliminating any symlinks + if isinstance(workspace_file, (str, os.PathLike)): + workspace_file = pathlib.Path(workspace_file) + + tmuxp_echo( + style("[Loading] ", fg="green") + + style(str(workspace_file), fg="blue", bold=True), + ) + + # ConfigReader allows us to open a yaml or json file as a dict + raw_workspace = config_reader.ConfigReader._from_file(workspace_file) or {} + + # shapes workspaces relative to config / profile file location + expanded_workspace = loader.expand( + raw_workspace, + cwd=os.path.dirname(workspace_file), + ) + + # Overridden session name + if new_session_name: + expanded_workspace["session_name"] = new_session_name + + # propagate workspace inheritance (e.g. session -> window, window -> pane) + expanded_workspace = loader.trickle(expanded_workspace) + + t = Server( # create tmux server object + socket_name=socket_name, + socket_path=socket_path, + config_file=tmux_config_file, + colors=colors, + ) + + shutil.which("tmux") # raise exception if tmux not found + + try: # load WorkspaceBuilder object for tmuxp workspace / tmux server + builder = WorkspaceBuilder( + session_config=expanded_workspace, + plugins=load_plugins(expanded_workspace), + server=t, + ) + except exc.EmptyWorkspaceException: + tmuxp_echo(f"{workspace_file} is empty or parsed no workspace data") + return None + + session_name = expanded_workspace["session_name"] + + # if the session already exists, prompt the user to attach + if builder.session_exists(session_name) and not append: + if not detached and ( + answer_yes + or prompt_yes_no( + "{} is already running. Attach?".format( + style(session_name, fg="green"), + ), + default=True, + ) + ): + _reattach(builder) + return None + + try: + if detached: + _load_detached(builder) + return _setup_plugins(builder) + + if append: + if "TMUX" in os.environ: # tmuxp ran from inside tmux + _load_append_windows_to_current_session(builder) + else: + _load_attached(builder, detached) + + return _setup_plugins(builder) + + # append and answer_yes have no meaning if specified together + if answer_yes: + _load_attached(builder, detached) + return _setup_plugins(builder) + + if "TMUX" in os.environ: # tmuxp ran from inside tmux + msg = ( + "Already inside TMUX, switch to session? yes/no\n" + "Or (a)ppend windows in the current active session?\n[y/n/a]" + ) + options = ["y", "n", "a"] + choice = prompt_choices(msg, choices=options) + + if choice == "y": + _load_attached(builder, detached) + elif choice == "a": + _load_append_windows_to_current_session(builder) + else: + _load_detached(builder) + else: + _load_attached(builder, detached) + + except exc.TmuxpException as e: + import traceback + + tmuxp_echo(traceback.format_exc()) + tmuxp_echo(str(e)) + + choice = prompt_choices( + "Error loading workspace. (k)ill, (a)ttach, (d)etach?", + choices=["k", "a", "d"], + default="k", + ) + + if choice == "k": + if builder.session is not None: + builder.session.kill() + tmuxp_echo("Session killed.") + elif choice == "a": + _reattach(builder) + else: + sys.exit() + + return _setup_plugins(builder) + + +def create_load_subparser(parser: argparse.ArgumentParser) -> argparse.ArgumentParser: + """Augment :class:`argparse.ArgumentParser` with ``load`` subcommand.""" + workspace_files = parser.add_argument( + "workspace_files", + nargs="+", + metavar="workspace-file", + help="filepath to session or filename of session in tmuxp workspace directory", + ) + parser.add_argument( + "-L", + dest="socket_name", + metavar="socket_name", + action="store", + help="passthru to tmux(1) -L", + ) + parser.add_argument( + "-S", + dest="socket_path", + metavar="socket_path", + action="store", + help="passthru to tmux(1) -S", + ) + + tmux_config_file = parser.add_argument( + "-f", + dest="tmux_config_file", + metavar="tmux_config_file", + help="passthru to tmux(1) -f", + ) + + parser.add_argument( + "-s", + dest="new_session_name", + metavar="new_session_name", + help="start new session with new session name", + ) + parser.add_argument( + "--yes", + "-y", + dest="answer_yes", + action="store_true", + help="always answer yes", + ) + parser.add_argument( + "-d", + dest="detached", + action="store_true", + help="load the session without attaching it", + ) + parser.add_argument( + "-a", + "--append", + dest="append", + action="store_true", + help="load workspace, appending windows to the current session", + ) + colorsgroup = parser.add_mutually_exclusive_group() + + colorsgroup.add_argument( + "-2", + dest="colors", + action="store_const", + const=256, + help="force tmux to assume the terminal supports 256 colours.", + ) + + colorsgroup.add_argument( + "-8", + dest="colors", + action="store_const", + const=88, + help="like -2, but indicates that the terminal supports 88 colours.", + ) + parser.set_defaults(colors=None) + + log_file = parser.add_argument( + "--log-file", + metavar="file_path", + action="store", + help="file to log errors/output to", + ) + + try: + import shtab + + workspace_files.complete = shtab.FILE # type: ignore + tmux_config_file.complete = shtab.FILE # type: ignore + log_file.complete = shtab.FILE # type: ignore + except ImportError: + pass + + return parser + + +def command_load( + args: CLILoadNamespace, + parser: argparse.ArgumentParser | None = None, +) -> None: + """Load a tmux workspace from each WORKSPACE_FILE. + + WORKSPACE_FILE is a specifier for a workspace file. + + If WORKSPACE_FILE is a path to a directory, tmuxp will search it for + ".tmuxp.{yaml,yml,json}". + + If WORKSPACE_FILE is has no directory component and only a filename, e.g. + "myworkspace.yaml", tmuxp will search the users's workspace directory for that + file. + + If WORKSPACE_FILE has no directory component, and only a name with no extension, + e.g. "myworkspace", tmuxp will search the users's workspace directory for any + file with the extension ".yaml", ".yml", or ".json" that matches that name. + + If multiple workspace files that match a given WORKSPACE_FILE are found, tmuxp + will warn and pick the first one found. + + If multiple WORKSPACE_FILEs are provided, workspaces will be created for all of + them. The last one provided will be attached. The others will be created in + detached mode. + """ + util.oh_my_zsh_auto_title() + + if args.log_file: + logfile_handler = logging.FileHandler(args.log_file) + logfile_handler.setFormatter(log.LogFormatter()) + from . import logger + + logger.addHandler(logfile_handler) + + tmux_options = { + "socket_name": args.socket_name, + "socket_path": args.socket_path, + "tmux_config_file": args.tmux_config_file, + "new_session_name": args.new_session_name, + "answer_yes": args.answer_yes, + "colors": args.colors, + "detached": args.detached, + "append": args.append, + } + + if args.workspace_files is None or len(args.workspace_files) == 0: + tmuxp_echo("Enter at least one config") + if parser is not None: + parser.print_help() + sys.exit() + return + + last_idx = len(args.workspace_files) - 1 + original_detached_option = tmux_options.pop("detached") + original_new_session_name = tmux_options.pop("new_session_name") + + for idx, workspace_file in enumerate(args.workspace_files): + workspace_file = find_workspace_file( + workspace_file, + workspace_dir=get_workspace_dir(), + ) + + detached = original_detached_option + new_session_name = original_new_session_name + + if last_idx > 0 and idx < last_idx: + detached = True + new_session_name = None + + load_workspace( + workspace_file, + detached=detached, + new_session_name=new_session_name, + **tmux_options, + ) diff --git a/src/tmuxp/cli/ls.py b/src/tmuxp/cli/ls.py new file mode 100644 index 00000000000..e1a8c7a6109 --- /dev/null +++ b/src/tmuxp/cli/ls.py @@ -0,0 +1,32 @@ +"""CLI for ``tmuxp ls`` subcommand.""" + +from __future__ import annotations + +import os +import typing as t + +from tmuxp.workspace.constants import VALID_WORKSPACE_DIR_FILE_EXTENSIONS +from tmuxp.workspace.finders import get_workspace_dir + +if t.TYPE_CHECKING: + import argparse + + +def create_ls_subparser( + parser: argparse.ArgumentParser, +) -> argparse.ArgumentParser: + """Augment :class:`argparse.ArgumentParser` with ``ls`` subcommand.""" + return parser + + +def command_ls( + parser: argparse.ArgumentParser | None = None, +) -> None: + """Entrypoint for ``tmuxp ls`` subcommand.""" + tmuxp_dir = get_workspace_dir() + if os.path.exists(tmuxp_dir) and os.path.isdir(tmuxp_dir): + for f in sorted(os.listdir(tmuxp_dir)): + stem, ext = os.path.splitext(f) + if os.path.isdir(f) or ext not in VALID_WORKSPACE_DIR_FILE_EXTENSIONS: + continue + print(stem) # NOQA: T201 RUF100 diff --git a/src/tmuxp/cli/shell.py b/src/tmuxp/cli/shell.py new file mode 100644 index 00000000000..c3b005c287a --- /dev/null +++ b/src/tmuxp/cli/shell.py @@ -0,0 +1,215 @@ +"""CLI for ``tmuxp shell`` subcommand.""" + +from __future__ import annotations + +import argparse +import os +import pathlib +import typing as t + +from libtmux.server import Server + +from tmuxp import util +from tmuxp._compat import PY3, PYMINOR + +if t.TYPE_CHECKING: + from typing_extensions import TypeAlias + + CLIColorsLiteral: TypeAlias = t.Literal[56, 88] + CLIShellLiteral: TypeAlias = t.Literal[ + "best", + "pdb", + "code", + "ptipython", + "ptpython", + "ipython", + "bpython", + ] + + +class CLIShellNamespace(argparse.Namespace): + """Typed :class:`argparse.Namespace` for tmuxp shell command.""" + + session_name: str + socket_name: str | None + socket_path: str | None + colors: CLIColorsLiteral | None + log_file: str | None + window_name: str | None + command: str | None + shell: CLIShellLiteral | None + use_pythonrc: bool + use_vi_mode: bool + + +def create_shell_subparser(parser: argparse.ArgumentParser) -> argparse.ArgumentParser: + """Augment :class:`argparse.ArgumentParser` with ``shell`` subcommand.""" + parser.add_argument("session_name", metavar="session-name", nargs="?") + parser.add_argument("window_name", metavar="window-name", nargs="?") + parser.add_argument( + "-S", + dest="socket_path", + metavar="socket-path", + help="pass-through for tmux -S", + ) + parser.add_argument( + "-L", + dest="socket_name", + metavar="socket-name", + help="pass-through for tmux -L", + ) + parser.add_argument( + "-c", + dest="command", + help="instead of opening shell, execute python code in libtmux and exit", + ) + + shells = parser.add_mutually_exclusive_group() + shells.add_argument( + "--best", + dest="shell", + const="best", + action="store_const", + help="use best shell available in site packages", + default="best", + ) + shells.add_argument( + "--pdb", + dest="shell", + const="pdb", + action="store_const", + help="use plain pdb", + ) + shells.add_argument( + "--code", + dest="shell", + const="code", + action="store_const", + help="use stdlib's code.interact()", + ) + shells.add_argument( + "--ptipython", + dest="shell", + const="ptipython", + action="store_const", + help="use ptpython + ipython", + ) + shells.add_argument( + "--ptpython", + dest="shell", + const="ptpython", + action="store_const", + help="use ptpython", + ) + shells.add_argument( + "--ipython", + dest="shell", + const="ipython", + action="store_const", + help="use ipython", + ) + shells.add_argument( + "--bpython", + dest="shell", + const="bpython", + action="store_const", + help="use bpython", + ) + + parser.add_argument( + "--use-pythonrc", + dest="use_pythonrc", + action="store_true", + help="load PYTHONSTARTUP env var and ~/.pythonrc.py script in --code", + default=False, + ) + parser.add_argument( + "--no-startup", + dest="use_pythonrc", + action="store_false", + help="load PYTHONSTARTUP env var and ~/.pythonrc.py script in --code", + default=False, + ) + parser.add_argument( + "--use-vi-mode", + dest="use_vi_mode", + action="store_true", + help="use vi-mode in ptpython/ptipython", + default=False, + ) + parser.add_argument( + "--no-vi-mode", + dest="use_vi_mode", + action="store_false", + help="use vi-mode in ptpython/ptipython", + default=False, + ) + return parser + + +def command_shell( + args: CLIShellNamespace, + parser: argparse.ArgumentParser | None = None, +) -> None: + """Entrypoint for ``tmuxp shell`` for tmux server, session, window and pane. + + Priority given to loaded session/window/pane objects: + + - session_name and window_name arguments + - current shell: envvar ``TMUX_PANE`` for determining window and session + - :attr:`libtmux.Server.attached_sessions`, :attr:`libtmux.Session.active_window`, + :attr:`libtmux.Window.active_pane` + """ + # If inside a server, detect socket_path + env_tmux = os.getenv("TMUX") + if env_tmux is not None and isinstance(env_tmux, str): + env_socket_path = pathlib.Path(env_tmux.split(",")[0]) + if ( + env_socket_path.exists() + and args.socket_path is None + and args.socket_name is None + ): + args.socket_path = str(env_socket_path) + + server = Server(socket_name=args.socket_name, socket_path=args.socket_path) + + server.raise_if_dead() + + current_pane = util.get_current_pane(server=server) + + session = util.get_session( + server=server, + session_name=args.session_name, + current_pane=current_pane, + ) + + window = util.get_window( + session=session, + window_name=args.window_name, + current_pane=current_pane, + ) + + pane = util.get_pane(window=window, current_pane=current_pane) + + if args.command is not None: + exec(args.command) + elif args.shell == "pdb" or ( + os.getenv("PYTHONBREAKPOINT") and PY3 and PYMINOR >= 7 + ): + from tmuxp._compat import breakpoint as tmuxp_breakpoint + + tmuxp_breakpoint() + return + else: + from tmuxp.shell import launch + + launch( + shell=args.shell, + use_pythonrc=args.use_pythonrc, # shell: code + use_vi_mode=args.use_vi_mode, # shell: ptpython, ptipython + # tmux environment / libtmux variables + server=server, + session=session, + window=window, + pane=pane, + ) diff --git a/src/tmuxp/cli/utils.py b/src/tmuxp/cli/utils.py new file mode 100644 index 00000000000..dbb40b08b75 --- /dev/null +++ b/src/tmuxp/cli/utils.py @@ -0,0 +1,292 @@ +"""CLI utility helpers for tmuxp.""" + +from __future__ import annotations + +import logging +import re +import typing as t + +from tmuxp import log + +if t.TYPE_CHECKING: + from collections.abc import Callable, Sequence + + from typing_extensions import TypeAlias + + CLIColour: TypeAlias = t.Union[int, tuple[int, int, int], str] + + +logger = logging.getLogger(__name__) + + +def tmuxp_echo( + message: str | None = None, + log_level: str = "INFO", + style_log: bool = False, +) -> None: + """Combine logging.log and click.echo.""" + if message is None: + return + + if style_log: + logger.log(log.LOG_LEVELS[log_level], message) + else: + logger.log(log.LOG_LEVELS[log_level], unstyle(message)) + + print(message) # NOQA: T201 RUF100 + + +def prompt( + name: str, + default: str | None = None, + value_proc: Callable[[str], str] | None = None, +) -> str: + """Return user input from command line. + + Parameters + ---------- + name : + prompt text + default : + default value if no input provided. + + Returns + ------- + str + + See Also + -------- + :meth:`~prompt`, :meth:`~prompt_bool` and :meth:`~prompt_choices` are from + `flask-script `_. See the + `flask-script license `_. + """ + prompt_ = name + ((default and f" [{default}]") or "") + prompt_ += (name.endswith("?") and " ") or ": " + while True: + rv = input(prompt_) or default + try: + if value_proc is not None and callable(value_proc): + assert isinstance(rv, str) + value_proc(rv) + except ValueError as e: + return prompt(str(e), default=default, value_proc=value_proc) + + if rv: + return rv + if default is not None: + return default + + +def prompt_bool( + name: str, + default: bool = False, + yes_choices: Sequence[t.Any] | None = None, + no_choices: Sequence[t.Any] | None = None, +) -> bool: + """Return True / False by prompting user input from command line. + + Parameters + ---------- + name : + prompt text + default : + default value if no input provided. + yes_choices : + default 'y', 'yes', '1', 'on', 'true', 't' + no_choices : + default 'n', 'no', '0', 'off', 'false', 'f' + + Returns + ------- + bool + """ + yes_choices = yes_choices or ("y", "yes", "1", "on", "true", "t") + no_choices = no_choices or ("n", "no", "0", "off", "false", "f") + + if default is None: + prompt_choice = "y/n" + elif default is True: + prompt_choice = "Y/n" + else: + prompt_choice = "y/N" + + prompt_ = name + f" [{prompt_choice}]" + prompt_ += (name.endswith("?") and " ") or ": " + + while True: + rv = input(prompt_) + if not rv: + return default + if rv.lower() in yes_choices: + return True + if rv.lower() in no_choices: + return False + + +def prompt_yes_no(name: str, default: bool = True) -> bool: + """:meth:`prompt_bool()` returning yes by default.""" + return prompt_bool(name, default=default) + + +def prompt_choices( + name: str, + choices: list[str] | tuple[str, str], + default: str | None = None, + no_choice: Sequence[str] = ("none",), +) -> str | None: + """Return user input from command line from set of provided choices. + + Parameters + ---------- + name : + prompt text + choices : + list or tuple of available choices. Choices may be single strings or + (key, value) tuples. + default : + default value if no input provided. + no_choice : + acceptable list of strings for "null choice" + + Returns + ------- + str + """ + choices_: list[str] = [] + options: list[str] = [] + + for choice in choices: + if isinstance(choice, str): + options.append(choice) + elif isinstance(choice, tuple): + options.append(f"{choice} [{choice[0]}]") + choice = choice[0] + choices_.append(choice) + + while True: + rv = prompt(name + " - ({})".format(", ".join(options)), default=default) + if not rv or rv == default: + return default + rv = rv.lower() + if rv in no_choice: + return None + if rv in choices_: + return rv + + +_ansi_re = re.compile(r"\033\[[;?0-9]*[a-zA-Z]") + + +def strip_ansi(value: str) -> str: + """Clear ANSI from a string value.""" + return _ansi_re.sub("", value) + + +_ansi_colors = { + "black": 30, + "red": 31, + "green": 32, + "yellow": 33, + "blue": 34, + "magenta": 35, + "cyan": 36, + "white": 37, + "reset": 39, + "bright_black": 90, + "bright_red": 91, + "bright_green": 92, + "bright_yellow": 93, + "bright_blue": 94, + "bright_magenta": 95, + "bright_cyan": 96, + "bright_white": 97, +} +_ansi_reset_all = "\033[0m" + + +def _interpret_color( + color: int | tuple[int, int, int] | str, + offset: int = 0, +) -> str: + if isinstance(color, int): + return f"{38 + offset};5;{color:d}" + + if isinstance(color, (tuple, list)): + r, g, b = color + return f"{38 + offset};2;{r:d};{g:d};{b:d}" + + return str(_ansi_colors[color] + offset) + + +class UnknownStyleColor(Exception): + """Raised when encountering an unknown terminal style color.""" + + def __init__(self, color: CLIColour, *args: object, **kwargs: object) -> None: + return super().__init__(f"Unknown color {color!r}", *args, **kwargs) + + +def style( + text: t.Any, + fg: CLIColour | None = None, + bg: CLIColour | None = None, + bold: bool | None = None, + dim: bool | None = None, + underline: bool | None = None, + overline: bool | None = None, + italic: bool | None = None, + blink: bool | None = None, + reverse: bool | None = None, + strikethrough: bool | None = None, + reset: bool = True, +) -> str: + """Credit: click.""" + if not isinstance(text, str): + text = str(text) + + bits = [] + + if fg: + try: + bits.append(f"\033[{_interpret_color(fg)}m") + except KeyError: + raise UnknownStyleColor(color=fg) from None + + if bg: + try: + bits.append(f"\033[{_interpret_color(bg, 10)}m") + except KeyError: + raise UnknownStyleColor(color=bg) from None + + if bold is not None: + bits.append(f"\033[{1 if bold else 22}m") + if dim is not None: + bits.append(f"\033[{2 if dim else 22}m") + if underline is not None: + bits.append(f"\033[{4 if underline else 24}m") + if overline is not None: + bits.append(f"\033[{53 if overline else 55}m") + if italic is not None: + bits.append(f"\033[{3 if italic else 23}m") + if blink is not None: + bits.append(f"\033[{5 if blink else 25}m") + if reverse is not None: + bits.append(f"\033[{7 if reverse else 27}m") + if strikethrough is not None: + bits.append(f"\033[{9 if strikethrough else 29}m") + bits.append(text) + if reset: + bits.append(_ansi_reset_all) + return "".join(bits) + + +def unstyle(text: str) -> str: + """Remove ANSI styling information from a string. + + Usually it's not necessary to use this function as tmuxp_echo function will + automatically remove styling if necessary. + + Credit: click. + + text : the text to remove style information from. + """ + return strip_ansi(text) diff --git a/src/tmuxp/exc.py b/src/tmuxp/exc.py new file mode 100644 index 00000000000..525599270f9 --- /dev/null +++ b/src/tmuxp/exc.py @@ -0,0 +1,132 @@ +"""Exceptions for tmuxp.""" + +from __future__ import annotations + +from libtmux._internal.query_list import ObjectDoesNotExist + +from ._compat import implements_to_string + + +class TmuxpException(Exception): + """Base Exception for Tmuxp Errors.""" + + +class WorkspaceError(TmuxpException): + """Error parsing tmuxp workspace data.""" + + +class SessionNotFound(TmuxpException): + """tmux session not found.""" + + def __init__( + self, + session_target: str | None = None, + *args: object, + **kwargs: object, + ) -> None: + msg = "Session not found" + if session_target is not None: + msg += f": {session_target}" + return super().__init__(msg, *args, **kwargs) + + +class WindowNotFound(TmuxpException): + """tmux window not found.""" + + def __init__( + self, + window_target: str | None = None, + *args: object, + **kwargs: object, + ) -> None: + msg = "Window not found" + if window_target is not None: + msg += f": {window_target}" + return super().__init__(msg, *args, **kwargs) + + +class PaneNotFound(TmuxpException): + """tmux pane not found.""" + + def __init__( + self, + pane_target: str | None = None, + *args: object, + **kwargs: object, + ) -> None: + msg = "Pane not found" + if pane_target is not None: + msg += f": {pane_target}" + return super().__init__(msg, *args, **kwargs) + + +class EmptyWorkspaceException(WorkspaceError): + """Workspace file is empty.""" + + def __init__( + self, + *args: object, + **kwargs: object, + ) -> None: + return super().__init__("Session configuration is empty.", *args, **kwargs) + + +class SessionMissingWorkspaceException(WorkspaceError, ObjectDoesNotExist): + """Session missing while loading tmuxp workspace.""" + + def __init__(self, *args: object, **kwargs: object) -> None: + return super().__init__( + "No session object exists for WorkspaceBuilder. " + "Tip: Add session_name in constructor or run WorkspaceBuilder.build()", + *args, + **kwargs, + ) + + +class ActiveSessionMissingWorkspaceException(WorkspaceError): + """Active session cannot be found while loading tmuxp workspace.""" + + def __init__(self, *args: object, **kwargs: object) -> None: + return super().__init__("No session active.", *args, **kwargs) + + +class TmuxpPluginException(TmuxpException): + """Base Exception for Tmuxp Errors.""" + + +class BeforeLoadScriptNotExists(OSError): + """Raises if shell script could not be found.""" + + def __init__(self, *args: object, **kwargs: object) -> None: + super().__init__(*args, **kwargs) + + self.strerror = f"before_script file '{self.strerror}' doesn't exist." + + +@implements_to_string +class BeforeLoadScriptError(Exception): + """Shell script execution error. + + Replaces :py:class:`subprocess.CalledProcessError` for + :meth:`tmuxp.util.run_before_script`. + """ + + def __init__( + self, + returncode: int, + cmd: str, + output: str | None = None, + ) -> None: + self.returncode = returncode + self.cmd = cmd + self.output = output + self.message = ( + f"before_script failed with returncode {self.returncode}.\n" + f"command: {self.cmd}\n" + "Error output:\n" + f"{self.output}" + ) + + def __str__(self) -> str: + """Return shell error message.""" + return self.message diff --git a/src/tmuxp/log.py b/src/tmuxp/log.py new file mode 100644 index 00000000000..613b140a659 --- /dev/null +++ b/src/tmuxp/log.py @@ -0,0 +1,204 @@ +#!/usr/bin/env python +"""Log utilities for tmuxp.""" + +from __future__ import annotations + +import logging +import time +import typing as t + +from colorama import Fore, Style + +LEVEL_COLORS = { + "DEBUG": Fore.BLUE, # Blue + "INFO": Fore.GREEN, # Green + "WARNING": Fore.YELLOW, + "ERROR": Fore.RED, + "CRITICAL": Fore.RED, +} + +LOG_LEVELS = { + "CRITICAL": 50, + "ERROR": 40, + "WARNING": 30, + "INFO": 20, + "DEBUG": 10, + "NOTSET": 0, +} + + +def setup_logger( + logger: logging.Logger | None = None, + level: str = "INFO", +) -> None: + """Configure tmuxp's logging for CLI use. + + Can checks for any existing loggers to prevent loading handlers twice. + + Parameters + ---------- + logger : :py:class:`Logger` + logger instance for tmuxp + """ + if not logger: # if no logger exists, make one + logger = logging.getLogger() + + if not logger.handlers: # setup logger handlers + logger.setLevel(level) + + +def set_style( + message: str, + stylized: bool, + style_before: str = "", + style_after: str = "", + prefix: str = "", + suffix: str = "", +) -> str: + """Stylize terminal logging output.""" + if stylized: + return prefix + style_before + message + style_after + suffix + + return prefix + message + suffix + + +class LogFormatter(logging.Formatter): + """Format logs for tmuxp.""" + + def template( + self: logging.Formatter, + record: logging.LogRecord, + stylized: bool = False, + **kwargs: t.Any, + ) -> str: + """ + Return the prefix for the log message. Template for Formatter. + + Parameters + ---------- + :py:class:`logging.LogRecord` : + object. this is passed in from inside the + :py:meth:`logging.Formatter.format` record. + + Returns + ------- + str + template for logger message + """ + reset = Style.RESET_ALL + levelname = set_style( + "(%(levelname)s)", + stylized, + style_before=(LEVEL_COLORS.get(record.levelname, "") + Style.BRIGHT), + style_after=Style.RESET_ALL, + suffix=" ", + ) + asctime = set_style( + "%(asctime)s", + stylized, + style_before=(Fore.BLACK + Style.DIM + Style.BRIGHT), + style_after=(Fore.RESET + Style.RESET_ALL), + prefix="[", + suffix="]", + ) + name = set_style( + "%(name)s", + stylized, + style_before=(Fore.WHITE + Style.DIM + Style.BRIGHT), + style_after=(Fore.RESET + Style.RESET_ALL), + prefix=" ", + suffix=" ", + ) + + if stylized: + return reset + levelname + asctime + name + reset + + return levelname + asctime + name + + def __init__(self, color: bool = True, **kwargs: t.Any) -> None: + logging.Formatter.__init__(self, **kwargs) + + def format(self, record: logging.LogRecord) -> str: + """Format a log record.""" + try: + record.message = record.getMessage() + except Exception as e: + record.message = f"Bad message ({e!r}): {record.__dict__!r}" + + date_format = "%H:%m:%S" + formatting = self.converter(record.created) + record.asctime = time.strftime(date_format, formatting) + + prefix = self.template(record) % record.__dict__ + + parts = prefix.split(record.message) + formatted = prefix + " " + record.message + return formatted.replace("\n", "\n" + parts[0] + " ") + + +def debug_log_template( + self: type[logging.Formatter], + record: logging.LogRecord, + stylized: bool | None = False, + **kwargs: t.Any, +) -> str: + """ + Return the prefix for the log message. Template for Formatter. + + Parameters + ---------- + record : :py:class:`logging.LogRecord` + This is passed in from inside the :py:meth:`logging.Formatter.format` + record. + + Returns + ------- + str + Log template. + """ + reset = Style.RESET_ALL + levelname = ( + LEVEL_COLORS.get(record.levelname, "") + + Style.BRIGHT + + "(%(levelname)1.1s)" + + Style.RESET_ALL + + " " + ) + asctime = ( + "[" + + Fore.BLACK + + Style.DIM + + Style.BRIGHT + + "%(asctime)s" + + Fore.RESET + + Style.RESET_ALL + + "]" + ) + name = ( + " " + + Fore.WHITE + + Style.DIM + + Style.BRIGHT + + "%(name)s" + + Fore.RESET + + Style.RESET_ALL + + " " + ) + module_funcName = Fore.GREEN + Style.BRIGHT + "%(module)s.%(funcName)s()" + lineno = ( + Fore.BLACK + + Style.DIM + + Style.BRIGHT + + ":" + + Style.RESET_ALL + + Fore.CYAN + + "%(lineno)d" + ) + + return reset + levelname + asctime + name + module_funcName + lineno + reset + + +class DebugLogFormatter(LogFormatter): + """Provides greater technical details than standard log Formatter.""" + + template = debug_log_template diff --git a/src/tmuxp/plugin.py b/src/tmuxp/plugin.py new file mode 100644 index 00000000000..4873112c8ba --- /dev/null +++ b/src/tmuxp/plugin.py @@ -0,0 +1,289 @@ +"""Plugin system for tmuxp.""" + +from __future__ import annotations + +import typing as t + +import libtmux +from libtmux._compat import LegacyVersion as Version +from libtmux.common import get_version + +from .__about__ import __version__ +from .exc import TmuxpPluginException + +#: Minimum version of tmux required to run libtmux +TMUX_MIN_VERSION = "1.8" + +#: Most recent version of tmux supported +TMUX_MAX_VERSION = None + +#: Minimum version of libtmux required to run libtmux +LIBTMUX_MIN_VERSION = "0.8.3" + +#: Most recent version of libtmux supported +LIBTMUX_MAX_VERSION = None + +#: Minimum version of tmuxp required to use plugins +TMUXP_MIN_VERSION = "1.6.0" + +#: Most recent version of tmuxp +TMUXP_MAX_VERSION = None + + +if t.TYPE_CHECKING: + from libtmux.session import Session + from libtmux.window import Window + from typing_extensions import TypedDict, TypeGuard, Unpack + + from ._internal.types import PluginConfigSchema + + class VersionConstraints(TypedDict): + """Version constraints mapping for a tmuxp plugin.""" + + version: Version | str + vmin: str + vmax: str | None + incompatible: list[t.Any | str] + + class TmuxpPluginVersionConstraints(TypedDict): + """Version constraints for a tmuxp plugin.""" + + tmux: VersionConstraints + tmuxp: VersionConstraints + libtmux: VersionConstraints + + +class Config(t.TypedDict): + """tmuxp plugin configuration mapping.""" + + plugin_name: str + tmux_min_version: str + tmux_max_version: str | None + tmux_version_incompatible: list[str] | None + libtmux_min_version: str + libtmux_max_version: str | None + libtmux_version_incompatible: list[str] | None + tmuxp_min_version: str + tmuxp_max_version: str | None + tmuxp_version_incompatible: list[str] | None + + +DEFAULT_CONFIG: Config = { + "plugin_name": "tmuxp-plugin", + "tmux_min_version": TMUX_MIN_VERSION, + "tmux_max_version": TMUX_MAX_VERSION, + "tmux_version_incompatible": None, + "libtmux_min_version": LIBTMUX_MIN_VERSION, + "libtmux_max_version": LIBTMUX_MAX_VERSION, + "libtmux_version_incompatible": None, + "tmuxp_min_version": TMUXP_MIN_VERSION, + "tmuxp_max_version": TMUXP_MAX_VERSION, + "tmuxp_version_incompatible": None, +} + + +def validate_plugin_config(config: PluginConfigSchema) -> TypeGuard[Config]: + """Return True if tmuxp plugin configuration valid, also upcasts.""" + return isinstance(config, dict) + + +def setup_plugin_config( + config: PluginConfigSchema, + default_config: Config = DEFAULT_CONFIG, +) -> Config: + """Initialize tmuxp plugin configuration.""" + new_config = config.copy() + for default_key, default_value in default_config.items(): + if default_key not in new_config: + new_config[default_key] = default_value # type:ignore + + assert validate_plugin_config(new_config) + + return new_config + + +class TmuxpPlugin: + """Base class for a tmuxp plugin.""" + + def __init__(self, **kwargs: Unpack[PluginConfigSchema]) -> None: + """ + Initialize plugin. + + The default version values are set to the versions that the plugin + system requires. + + Parameters + ---------- + plugin_name : str + Name of the child plugin. Used in error message plugin fails to + load + + tmux_min_version : str + Min version of tmux that the plugin supports + + tmux_max_version : str + Min version of tmux that the plugin supports + + tmux_version_incompatible : list + Versions of tmux that are incompatible with the plugin + + libtmux_min_version : str + Min version of libtmux that the plugin supports + + libtmux_max_version : str + Max version of libtmux that the plugin supports + + libtmux_version_incompatible : list + Versions of libtmux that are incompatible with the plugin + + tmuxp_min_version : str + Min version of tmuxp that the plugin supports + + tmuxp_max_version : str + Max version of tmuxp that the plugin supports + + tmuxp_version_incompatible : list + Versions of tmuxp that are incompatible with the plugin + + """ + config = setup_plugin_config(config=kwargs) + self.plugin_name = config["plugin_name"] + + # Dependency versions + self.tmux_version = get_version() + self.libtmux_version = libtmux.__about__.__version__ + self.tmuxp_version = Version(__version__) + + self.version_constraints: TmuxpPluginVersionConstraints = { + "tmux": { + "version": self.tmux_version, + "vmin": config["tmux_min_version"], + "vmax": config["tmux_max_version"], + "incompatible": config["tmux_version_incompatible"] or [], + }, + "libtmux": { + "version": self.libtmux_version, + "vmin": config["libtmux_min_version"], + "vmax": config["libtmux_max_version"], + "incompatible": config["libtmux_version_incompatible"] or [], + }, + "tmuxp": { + "version": self.tmuxp_version, + "vmin": config["tmuxp_min_version"], + "vmax": config["tmuxp_max_version"], + "incompatible": config["tmuxp_version_incompatible"] or [], + }, + } + + self._version_check() + + def _version_check(self) -> None: + """Check all dependency versions for compatibility.""" + for dep, constraints in self.version_constraints.items(): + assert isinstance(constraints, dict) + try: + assert self._pass_version_check(**constraints) + except AssertionError as e: + msg = ( + "Incompatible {dep} version: {version}\n{plugin_name} " + "requirements:\nmin: {vmin} | max: {vmax} | " + "incompatible: {incompatible}\n".format( + dep=dep, + plugin_name=self.plugin_name, + **constraints, + ) + ) + raise TmuxpPluginException( + msg, + ) from e + + def _pass_version_check( + self, + version: str | Version, + vmin: str, + vmax: str | None, + incompatible: list[t.Any | str], + ) -> bool: + """Provide affirmative if version compatibility is correct.""" + if vmin and version < Version(vmin): + return False + if vmax and version > Version(vmax): + return False + return version not in incompatible + + def before_workspace_builder(self, session: Session) -> None: + """ + Provide a session hook previous to creating the workspace. + + This runs after the session has been created but before any of + the windows/panes/commands are entered. + + Parameters + ---------- + session : :class:`libtmux.Session` + session to hook into + """ + + def on_window_create(self, window: Window) -> None: + """ + Provide a window hook previous to doing anything with a window. + + This runs runs before anything is created in the windows, like panes. + + Parameters + ---------- + window: :class:`libtmux.Window` + window to hook into + """ + + def after_window_finished(self, window: Window) -> None: + """ + Provide a window hook after creating the window. + + This runs after everything has been created in the window, including + the panes and all of the commands for the panes. It also runs after the + ``options_after`` has been applied to the window. + + Parameters + ---------- + window: :class:`libtmux.Window` + window to hook into + """ + + def before_script(self, session: Session) -> None: + """ + Provide a session hook after the workspace has been built. + + This runs after the workspace has been loaded with ``tmuxp load``. It + augments instead of replaces the ``before_script`` section of the + workspace data. + + This hook provides access to the LibTmux.session object for any + behavior that would be used in the ``before_script`` section of the + workspace file that needs access directly to the session object. + This runs after the workspace has been loaded with ``tmuxp load``. + + The hook augments, rather than replaces, the ``before_script`` section + of the workspace. While it is possible to do all of the + ``before_script`` workspace in this function, if a shell script + is currently being used for the workspace, it would be cleaner to + continue using the script in the ``before_section``. + + If changes to the session need to be made prior to + anything being built, please use ``before_workspace_builder`` instead. + + Parameters + ---------- + session : :class:`libtmux.Session` + session to hook into + """ + + def reattach(self, session: Session) -> None: + """ + Provide a session hook before reattaching to the session. + + Parameters + ---------- + session : :class:`libtmux.Session` + session to hook into + """ diff --git a/src/tmuxp/shell.py b/src/tmuxp/shell.py new file mode 100644 index 00000000000..78f9857ca25 --- /dev/null +++ b/src/tmuxp/shell.py @@ -0,0 +1,318 @@ +# mypy: allow-untyped-calls +"""Utility and helper methods for tmuxp.""" + +from __future__ import annotations + +import logging +import os +import pathlib +import typing as t + +logger = logging.getLogger(__name__) + +if t.TYPE_CHECKING: + from collections.abc import Callable + from types import ModuleType + + from libtmux.pane import Pane + from libtmux.server import Server + from libtmux.session import Session + from libtmux.window import Window + from typing_extensions import NotRequired, TypeAlias, TypedDict, Unpack + + CLIShellLiteral: TypeAlias = t.Literal[ + "best", + "pdb", + "code", + "ptipython", + "ptpython", + "ipython", + "bpython", + ] + + class LaunchOptionalImports(TypedDict): + """tmuxp shell optional imports.""" + + server: NotRequired[Server] + session: NotRequired[Session] + window: NotRequired[Window] + pane: NotRequired[Pane] + + class LaunchImports(t.TypedDict): + """tmuxp shell launch import mapping.""" + + libtmux: ModuleType + Server: type[Server] + Session: type[Session] + Window: type[Window] + Pane: type[Pane] + server: Server | None + session: Session | None + window: Window | None + pane: Pane | None + + +def has_ipython() -> bool: + """Return True if ipython is installed.""" + try: + from IPython import start_ipython # NOQA: F401 + except ImportError: + try: + from IPython.Shell import IPShell # NOQA: F401 + except ImportError: + return False + + return True + + +def has_ptpython() -> bool: + """Return True if ptpython is installed.""" + try: + from ptpython.repl import embed, run_config # F401 + except ImportError: + try: + from prompt_toolkit.contrib.repl import embed, run_config # NOQA: F401 + except ImportError: + return False + + return True + + +def has_ptipython() -> bool: + """Return True if ptpython + ipython are both installed.""" + try: + from ptpython.ipython import embed # F401 + from ptpython.repl import run_config # F401 + except ImportError: + try: + from prompt_toolkit.contrib.ipython import embed # NOQA: F401 + from prompt_toolkit.contrib.repl import run_config # NOQA: F401 + except ImportError: + return False + + return True + + +def has_bpython() -> bool: + """Return True if bpython is installed.""" + try: + from bpython import embed # NOQA: F401 + except ImportError: + return False + return True + + +def detect_best_shell() -> CLIShellLiteral: + """Return the best, most feature-rich shell available.""" + if has_ptipython(): + return "ptipython" + if has_ptpython(): + return "ptpython" + if has_ipython(): + return "ipython" + if has_bpython(): + return "bpython" + return "code" + + +def get_bpython( + options: LaunchOptionalImports, + extra_args: dict[str, t.Any] | None = None, +) -> Callable[[], None]: + """Return bpython shell.""" + if extra_args is None: + extra_args = {} + + from bpython import embed + + def launch_bpython() -> None: + imported_objects = get_launch_args(**options) + kwargs = {} + if extra_args: + kwargs["args"] = extra_args + embed(imported_objects, **kwargs) + + return launch_bpython + + +def get_ipython_arguments() -> list[str]: + """Return ipython shell args via ``IPYTHON_ARGUMENTS`` environment variables.""" + ipython_args = "IPYTHON_ARGUMENTS" + return os.environ.get(ipython_args, "").split() + + +def get_ipython( + options: LaunchOptionalImports, + **extra_args: dict[str, t.Any], +) -> t.Any: + """Return ipython shell.""" + try: + from IPython import start_ipython + + def launch_ipython() -> None: + imported_objects = get_launch_args(**options) + ipython_arguments = extra_args or get_ipython_arguments() + start_ipython(argv=ipython_arguments, user_ns=imported_objects) + + return launch_ipython # NOQA: TRY300 + except ImportError: + # IPython < 0.11 + # Explicitly pass an empty list as arguments, because otherwise + # IPython would use sys.argv from this script. + # Notebook not supported for IPython < 0.11. + from IPython.Shell import IPShell + + def launch_ipython() -> None: + imported_objects = get_launch_args(**options) + shell = IPShell(argv=[], user_ns=imported_objects) + shell.mainloop() + + return launch_ipython + + +def get_ptpython(options: LaunchOptionalImports, vi_mode: bool = False) -> t.Any: + """Return ptpython shell.""" + try: + from ptpython.repl import embed, run_config + except ImportError: + from prompt_toolkit.contrib.repl import embed, run_config + + def launch_ptpython() -> None: + imported_objects = get_launch_args(**options) + history_filename = str(pathlib.Path("~/.ptpython_history").expanduser()) + embed( + globals=imported_objects, + history_filename=history_filename, + vi_mode=vi_mode, + configure=run_config, + ) + + return launch_ptpython + + +def get_ptipython(options: LaunchOptionalImports, vi_mode: bool = False) -> t.Any: + """Based on django-extensions. + + Run renamed to launch, get_imported_objects renamed to get_launch_args + """ + try: + from ptpython.ipython import embed + from ptpython.repl import run_config + except ImportError: + # prompt_toolkit < v0.27 + from prompt_toolkit.contrib.ipython import embed + from prompt_toolkit.contrib.repl import run_config + + def launch_ptipython() -> None: + imported_objects = get_launch_args(**options) + history_filename = str(pathlib.Path("~/.ptpython_history").expanduser()) + embed( + user_ns=imported_objects, + history_filename=history_filename, + vi_mode=vi_mode, + configure=run_config, + ) + + return launch_ptipython + + +def get_launch_args(**kwargs: Unpack[LaunchOptionalImports]) -> LaunchImports: + """Return tmuxp shell launch arguments, counting for overrides.""" + import libtmux + from libtmux.pane import Pane + from libtmux.server import Server + from libtmux.session import Session + from libtmux.window import Window + + return { + "libtmux": libtmux, + "Server": Server, + "Session": Session, + "Window": Window, + "Pane": Pane, + "server": kwargs.get("server"), + "session": kwargs.get("session"), + "window": kwargs.get("window"), + "pane": kwargs.get("pane"), + } + + +def get_code(use_pythonrc: bool, imported_objects: LaunchImports) -> t.Any: + """Launch basic python shell via :mod:`code`.""" + import code + + try: + # Try activating rlcompleter, because it's handy. + import readline + except ImportError: + pass + else: + # We don't have to wrap the following import in a 'try', because + # we already know 'readline' was imported successfully. + import rlcompleter + + readline.set_completer( + rlcompleter.Completer( + imported_objects, # type:ignore + ).complete, + ) + # Enable tab completion on systems using libedit (e.g. macOS). + # These lines are copied from Lib/site.py on Python 3.4. + readline_doc = getattr(readline, "__doc__", "") + if readline_doc is not None and "libedit" in readline_doc: + readline.parse_and_bind("bind ^I rl_complete") + else: + readline.parse_and_bind("tab:complete") + + # We want to honor both $PYTHONSTARTUP and .pythonrc.py, so follow system + # conventions and get $PYTHONSTARTUP first then .pythonrc.py. + if use_pythonrc: + PYTHONSTARTUP = os.environ.get("PYTHONSTARTUP") + for pythonrc in { + *([pathlib.Path(PYTHONSTARTUP)] if PYTHONSTARTUP is not None else []), + pathlib.Path("~/.pythonrc.py").expanduser(), + }: + if not pythonrc: + continue + if not pythonrc.is_file(): + continue + with pythonrc.open() as handle: + pythonrc_code = handle.read() + # Match the behavior of the cpython shell where an error in + # PYTHONSTARTUP prints an exception and continues. + exec( + compile(pythonrc_code, pythonrc, "exec"), + imported_objects, # type:ignore + ) + + def launch_code() -> None: + code.interact(local=imported_objects) + + return launch_code + + +def launch( + shell: CLIShellLiteral | None = "best", + use_pythonrc: bool = False, + use_vi_mode: bool = False, + **kwargs: Unpack[LaunchOptionalImports], +) -> None: + """Launch interactive libtmux shell for tmuxp shell.""" + # Also allowing passing shell='code' to force using code.interact + imported_objects = get_launch_args(**kwargs) + + if shell == "best": + shell = detect_best_shell() + + if shell == "ptipython": + launch = get_ptipython(options=kwargs, vi_mode=use_vi_mode) + elif shell == "ptpython": + launch = get_ptpython(options=kwargs, vi_mode=use_vi_mode) + elif shell == "ipython": + launch = get_ipython(options=kwargs) + elif shell == "bpython": + launch = get_bpython(options=kwargs) + else: + launch = get_code(use_pythonrc=use_pythonrc, imported_objects=imported_objects) + + launch() diff --git a/src/tmuxp/types.py b/src/tmuxp/types.py new file mode 100644 index 00000000000..4d267ae2eeb --- /dev/null +++ b/src/tmuxp/types.py @@ -0,0 +1,18 @@ +"""Internal :term:`type annotations `. + +Notes +----- +:class:`StrPath` and :class:`StrOrBytesPath` is based on `typeshed's`_. + +.. _typeshed's: https://github.com/python/typeshed/blob/9687d5/stdlib/_typeshed/__init__.pyi#L98 +""" # E501 + +from __future__ import annotations + +import typing as t + +if t.TYPE_CHECKING: + from os import PathLike + +StrPath = t.Union[str, "PathLike[str]"] +""":class:`os.PathLike` or :class:`str`""" diff --git a/src/tmuxp/util.py b/src/tmuxp/util.py new file mode 100644 index 00000000000..490ee7e9402 --- /dev/null +++ b/src/tmuxp/util.py @@ -0,0 +1,200 @@ +"""Utility and helper methods for tmuxp.""" + +from __future__ import annotations + +import logging +import os +import shlex +import subprocess +import sys +import typing as t + +from . import exc + +if t.TYPE_CHECKING: + import pathlib + + from libtmux.pane import Pane + from libtmux.server import Server + from libtmux.session import Session + from libtmux.window import Window + +logger = logging.getLogger(__name__) + +PY2 = sys.version_info[0] == 2 + + +def run_before_script( + script_file: str | pathlib.Path, + cwd: pathlib.Path | None = None, +) -> int: + """Execute shell script, ``tee``-ing output to both terminal (if TTY) and buffer.""" + script_cmd = shlex.split(str(script_file)) + + try: + proc = subprocess.Popen( + script_cmd, + cwd=cwd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, # decode to str + errors="backslashreplace", + ) + except FileNotFoundError as e: + raise exc.BeforeLoadScriptNotExists( + e, + os.path.abspath(script_file), # NOQA: PTH100 + ) from e + + out_buffer = [] + err_buffer = [] + + # While process is running, read lines from stdout/stderr + # and write them to this process's stdout/stderr if isatty + is_out_tty = sys.stdout.isatty() + is_err_tty = sys.stderr.isatty() + + # You can do a simple loop reading in real-time: + while True: + # Use .poll() to check if the child has exited + return_code = proc.poll() + + # Read one line from stdout, if available + line_out = proc.stdout.readline() if proc.stdout else "" + + # Read one line from stderr, if available + line_err = proc.stderr.readline() if proc.stderr else "" + + if line_out and line_out.strip(): + out_buffer.append(line_out) + if is_out_tty: + sys.stdout.write(line_out) + sys.stdout.flush() + + if line_err and line_err.strip(): + err_buffer.append(line_err) + if is_err_tty: + sys.stderr.write(line_err) + sys.stderr.flush() + + # If no more data from pipes and process ended, break + if not line_out and not line_err and return_code is not None: + break + + # At this point, the process has finished + return_code = proc.wait() + + if return_code != 0: + # Join captured stderr lines for your exception + stderr_str = "".join(err_buffer).strip() + raise exc.BeforeLoadScriptError( + return_code, + os.path.abspath(script_file), # NOQA: PTH100 + stderr_str, + ) + + return return_code + + +def oh_my_zsh_auto_title() -> None: + """Give warning and offer to fix ``DISABLE_AUTO_TITLE``. + + See: https://github.com/robbyrussell/oh-my-zsh/pull/257 + """ + if ( + "SHELL" in os.environ + and "zsh" in os.environ.get("SHELL", "") + and os.path.exists(os.path.expanduser("~/.oh-my-zsh")) # NOQA: PTH110, PTH111 + and ( + "DISABLE_AUTO_TITLE" not in os.environ + or os.environ.get("DISABLE_AUTO_TITLE") == "false" + ) + ): + print( # NOQA: T201 RUF100 + "Please set:\n\n" + "\texport DISABLE_AUTO_TITLE='true'\n\n" + "in ~/.zshrc or where your zsh profile is stored.\n" + 'Remember the "export" at the beginning!\n\n' + "Then create a new shell or type:\n\n" + "\t$ source ~/.zshrc", + ) + + +def get_current_pane(server: Server) -> Pane | None: + """Return Pane if one found in env.""" + if os.getenv("TMUX_PANE") is not None: + try: + return next(p for p in server.panes if p.pane_id == os.getenv("TMUX_PANE")) + except StopIteration: + pass + return None + + +def get_session( + server: Server, + session_name: str | None = None, + current_pane: Pane | None = None, +) -> Session: + """Get tmux session for server by session name, respects current pane, if passed.""" + try: + if session_name: + session = server.sessions.get(session_name=session_name) + elif current_pane is not None: + session = server.sessions.get(session_id=current_pane.session_id) + else: + current_pane = get_current_pane(server) + if current_pane: + session = server.sessions.get(session_id=current_pane.session_id) + else: + session = server.sessions[0] + + except Exception as e: + if session_name: + raise exc.SessionNotFound(session_name) from e + raise exc.SessionNotFound from e + + assert session is not None + return session + + +def get_window( + session: Session, + window_name: str | None = None, + current_pane: Pane | None = None, +) -> Window: + """Get tmux window for server by window name, respects current pane, if passed.""" + try: + if window_name: + window = session.windows.get(window_name=window_name) + elif current_pane is not None: + window = session.windows.get(window_id=current_pane.window_id) + else: + window = session.windows[0] + except Exception as e: + if window_name: + raise exc.WindowNotFound(window_target=window_name) from e + if current_pane: + raise exc.WindowNotFound(window_target=str(current_pane)) from e + raise exc.WindowNotFound from e + + assert window is not None + return window + + +def get_pane(window: Window, current_pane: Pane | None = None) -> Pane: + """Get tmux pane for server by pane name, respects current pane, if passed.""" + pane = None + try: + if current_pane is not None: + pane = window.panes.get(pane_id=current_pane.pane_id) + else: + pane = window.active_pane + except exc.TmuxpException as e: + print(e) # NOQA: T201 RUF100 + + if pane is None: + if current_pane: + raise exc.PaneNotFound(str(current_pane)) + raise exc.PaneNotFound + + return pane diff --git a/src/tmuxp/workspace/__init__.py b/src/tmuxp/workspace/__init__.py new file mode 100644 index 00000000000..ac87e57f625 --- /dev/null +++ b/src/tmuxp/workspace/__init__.py @@ -0,0 +1 @@ +"""tmuxp workspace functionality.""" diff --git a/src/tmuxp/workspace/builder.py b/src/tmuxp/workspace/builder.py new file mode 100644 index 00000000000..b02d80d70e6 --- /dev/null +++ b/src/tmuxp/workspace/builder.py @@ -0,0 +1,617 @@ +"""Create a tmux workspace from a workspace :py:obj:`dict`.""" + +from __future__ import annotations + +import logging +import os +import shutil +import time +import typing as t + +from libtmux._internal.query_list import ObjectDoesNotExist +from libtmux.common import has_gte_version, has_lt_version +from libtmux.pane import Pane +from libtmux.server import Server +from libtmux.session import Session +from libtmux.window import Window + +from tmuxp import exc +from tmuxp.util import get_current_pane, run_before_script + +if t.TYPE_CHECKING: + from collections.abc import Iterator + +logger = logging.getLogger(__name__) + +COLUMNS_FALLBACK = 80 + + +def get_default_columns() -> int: + """Return default session column size use when building new tmux sessions.""" + return int( + os.getenv("TMUXP_DEFAULT_COLUMNS", os.getenv("COLUMNS", COLUMNS_FALLBACK)), + ) + + +ROWS_FALLBACK = int(os.getenv("TMUXP_DEFAULT_ROWS", os.getenv("ROWS", 24))) + + +def get_default_rows() -> int: + """Return default session row size use when building new tmux sessions.""" + return int(os.getenv("TMUXP_DEFAULT_ROWS", os.getenv("ROWS", ROWS_FALLBACK))) + + +class WorkspaceBuilder: + """Load workspace from workspace :py:obj:`dict` object. + + Build tmux workspace from a configuration. Creates and names windows, sets options, + splits windows into panes. + + Examples + -------- + >>> import yaml + + >>> session_config = yaml.load(''' + ... session_name: sample workspace + ... start_directory: '~' + ... windows: + ... - window_name: editor + ... layout: main-vertical + ... panes: + ... - shell_command: + ... - cmd: vim + ... - shell_command: + ... - cmd: echo "hey" + ... + ... - window_name: logging + ... panes: + ... - shell_command: + ... - cmd: tail | echo 'hi' + ... + ... - window_name: test + ... panes: + ... - shell_command: + ... - cmd: htop + ... ''', Loader=yaml.Loader) + + >>> builder = WorkspaceBuilder(session_config=session_config, server=server) + + **New session:** + + >>> builder.build() + + >>> new_session = builder.session + + >>> new_session.name == 'sample workspace' + True + + >>> len(new_session.windows) + 3 + + >>> sorted([window.name for window in new_session.windows]) + ['editor', 'logging', 'test'] + + **Existing session:** + + >>> len(session.windows) + 1 + + >>> builder.build(session=session) + + _Caveat:_ Preserves old session name: + + >>> session.name == 'sample workspace' + False + + >>> len(session.windows) + 3 + + >>> sorted([window.name for window in session.windows]) + ['editor', 'logging', 'test'] + + The normal phase of loading is: + + 1. Load JSON / YAML file via via :class:`pathlib.Path`:: + + from tmuxp._internal import config_reader + session_config = config_reader.ConfigReader._load(raw_yaml) + + The reader automatically detects the file type from :attr:`pathlib.suffix`. + + We can also parse raw file:: + + import pathlib + from tmuxp._internal import config_reader + + session_config = config_reader.ConfigReader._from_file( + pathlib.Path('path/to/config.yaml') + ) + + 2. :meth:`config.expand` session_config inline shorthand:: + + from tmuxp import config + session_config = config.expand(session_config) + + 3. :meth:`config.trickle` passes down default values from session + -> window -> pane if applicable:: + + session_config = config.trickle(session_config) + + 4. (You are here) We will create a :class:`libtmux.Session` (a real + ``tmux(1)`` session) and iterate through the list of windows, and + their panes, returning full :class:`libtmux.Window` and + :class:`libtmux.Pane` objects each step of the way:: + + workspace = WorkspaceBuilder(session_config=session_config, server=server) + + It handles the magic of cases where the user may want to start + a session inside tmux (when `$TMUX` is in the env variables). + """ + + server: Server + _session: Session | None + session_name: str + + def __init__( + self, + session_config: dict[str, t.Any], + server: Server, + plugins: list[t.Any] | None = None, + ) -> None: + """Initialize workspace loading. + + Parameters + ---------- + session_config : dict + session config, includes a :py:obj:`list` of ``windows``. + + plugins : list + plugins to be used for this session + + server : :class:`libtmux.Server` + tmux server to build session in + + Notes + ----- + TODO: Initialize :class:`libtmux.Session` from here, in + ``self.session``. + """ + if plugins is None: + plugins = [] + if not session_config: + raise exc.EmptyWorkspaceException + + # validation.validate_schema(session_config) + + assert isinstance(server, Server) + self.server = server + + self.session_config = session_config + self.plugins = plugins + + if self.server is not None and self.session_exists( + session_name=self.session_config["session_name"], + ): + try: + session = self.server.sessions.get( + session_name=self.session_config["session_name"], + ) + assert session is not None + self._session = session + except ObjectDoesNotExist: + pass + + @property + def session(self) -> Session: + """Return tmux session using in workspace builder session.""" + if self._session is None: + raise exc.SessionMissingWorkspaceException + return self._session + + def session_exists(self, session_name: str) -> bool: + """Return true if tmux session already exists.""" + assert session_name is not None + assert isinstance(session_name, str) + assert self.server is not None + exists = self.server.has_session(session_name) + if not exists: + return exists + + try: + self.server.sessions.get(session_name=session_name) + except ObjectDoesNotExist: + return False + return True + + def build(self, session: Session | None = None, append: bool = False) -> None: + """Build tmux workspace in session. + + Optionally accepts ``session`` to build with only session object. + + Without ``session``, it will use :class:`libmtux.Server` at ``self.server`` + passed in on initialization to create a new Session object. + + Parameters + ---------- + session : :class:`libtmux.Session` + session to build workspace in + append : bool + append windows in current active session + """ + if not session: + if not self.server: + msg = ( + "WorkspaceBuilder.build requires server to be passed " + "on initialization, or pass in session object to here." + ) + raise exc.TmuxpException( + msg, + ) + new_session_kwargs = {} + if "start_directory" in self.session_config: + new_session_kwargs["start_directory"] = self.session_config[ + "start_directory" + ] + + if ( + has_gte_version("2.6") + and os.getenv("TMUXP_DETECT_TERMINAL_SIZE", "1") == "1" + ): + terminal_size = shutil.get_terminal_size( + fallback=(get_default_columns(), get_default_rows()), + ) + new_session_kwargs["x"] = terminal_size.columns + new_session_kwargs["y"] = terminal_size.lines + + session = self.server.new_session( + session_name=self.session_config["session_name"], + **new_session_kwargs, + ) + assert session is not None + + assert self.session_config["session_name"] == session.name + assert len(self.session_config["session_name"]) > 0 + + assert session is not None + assert session.name is not None + + self._session = session + + assert session.server is not None + + self.server: Server = session.server + assert self.server.sessions is not None + assert self.server.has_session(session.name) + assert session.id + + assert isinstance(session, Session) + + for plugin in self.plugins: + plugin.before_workspace_builder(self.session) + + focus = None + + if "before_script" in self.session_config: + try: + cwd = None + + # we want to run the before_script file cwd'd from the + # session start directory, if it exists. + if "start_directory" in self.session_config: + cwd = self.session_config["start_directory"] + run_before_script(self.session_config["before_script"], cwd=cwd) + except Exception: + self.session.kill() + raise + + if "options" in self.session_config: + for option, value in self.session_config["options"].items(): + self.session.set_option(option, value) + + if "global_options" in self.session_config: + for option, value in self.session_config["global_options"].items(): + self.session.set_option(option, value, global_=True) + + if "environment" in self.session_config: + for option, value in self.session_config["environment"].items(): + self.session.set_environment(option, value) + + for window, window_config in self.iter_create_windows(session, append): + assert isinstance(window, Window) + + for plugin in self.plugins: + plugin.on_window_create(window) + + focus_pane = None + for pane, pane_config in self.iter_create_panes(window, window_config): + assert isinstance(pane, Pane) + pane = pane + + if "layout" in window_config: + window.select_layout(window_config["layout"]) + + if pane_config.get("focus"): + focus_pane = pane + + if window_config.get("focus"): + focus = window + + self.config_after_window(window, window_config) + + for plugin in self.plugins: + plugin.after_window_finished(window) + + if focus_pane: + focus_pane.select() + + if focus: + focus.select() + + def iter_create_windows( + self, + session: Session, + append: bool = False, + ) -> Iterator[t.Any]: + """Return :class:`libtmux.Window` iterating through session config dict. + + Generator yielding :class:`libtmux.Window` by iterating through + ``session_config['windows']``. + + Applies ``window_options`` to window. + + Parameters + ---------- + session : :class:`libtmux.Session` + session to create windows in + append : bool + append windows in current active session + + Returns + ------- + tuple of (:class:`libtmux.Window`, ``window_config``) + Newly created window, and the section from the tmuxp configuration + that was used to create the window. + """ + for window_iterator, window_config in enumerate( + self.session_config["windows"], + start=1, + ): + window_name = window_config.get("window_name", None) + + is_first_window_pass = self.first_window_pass( + window_iterator, + session, + append, + ) + + w1 = None + if is_first_window_pass: # if first window, use window 1 + w1 = session.active_window + w1.move_window("99") + + start_directory = window_config.get("start_directory", None) + + # If the first pane specifies a start_directory, use that instead. + panes = window_config["panes"] + if panes and "start_directory" in panes[0]: + start_directory = panes[0]["start_directory"] + + window_shell = window_config.get("window_shell", None) + + # If the first pane specifies a shell, use that instead. + try: + if window_config["panes"][0]["shell"] != "": + window_shell = window_config["panes"][0]["shell"] + except (KeyError, IndexError): + pass + + environment = panes[0].get("environment", window_config.get("environment")) + if environment and has_lt_version("3.0"): + # Falling back to use the environment of the first pane for the window + # creation is nice but yields misleading error messages. + pane_env = panes[0].get("environment") + win_env = window_config.get("environment") + if pane_env and win_env: + target = "panes and windows" + elif pane_env: + target = "panes" + else: + target = "windows" + logger.warning( + f"Cannot set environment for new {target}. " + "You need tmux 3.0 or newer for this.", + ) + environment = None + + window = session.new_window( + window_name=window_name, + start_directory=start_directory, + attach=False, # do not move to the new window + window_index=window_config.get("window_index", ""), + window_shell=window_shell, + environment=environment, + ) + assert isinstance(window, Window) + + if is_first_window_pass: # if first window, use window 1 + session.active_window.kill() + + if "options" in window_config and isinstance( + window_config["options"], + dict, + ): + for key, val in window_config["options"].items(): + window.set_window_option(key, val) + + if window_config.get("focus"): + window.select() + + yield window, window_config + + def iter_create_panes( + self, + window: Window, + window_config: dict[str, t.Any], + ) -> Iterator[t.Any]: + """Return :class:`libtmux.Pane` iterating through window config dict. + + Run ``shell_command`` with ``$ tmux send-keys``. + + Parameters + ---------- + window : :class:`libtmux.Window` + window to create panes for + window_config : dict + config section for window + + Returns + ------- + tuple of (:class:`libtmux.Pane`, ``pane_config``) + Newly created pane, and the section from the tmuxp configuration + that was used to create the pane. + """ + assert isinstance(window, Window) + + pane_base_index_str = window.show_window_option("pane-base-index", g=True) + assert pane_base_index_str is not None + pane_base_index = int(pane_base_index_str) + + pane = None + + for pane_index, pane_config in enumerate( + window_config["panes"], + start=pane_base_index, + ): + if pane_index == int(pane_base_index): + pane = window.active_pane + else: + + def get_pane_start_directory( + pane_config: dict[str, str], + window_config: dict[str, str], + ) -> str | None: + if "start_directory" in pane_config: + return pane_config["start_directory"] + if "start_directory" in window_config: + return window_config["start_directory"] + return None + + def get_pane_shell( + pane_config: dict[str, str], + window_config: dict[str, str], + ) -> str | None: + if "shell" in pane_config: + return pane_config["shell"] + if "window_shell" in window_config: + return window_config["window_shell"] + return None + + environment = pane_config.get( + "environment", + window_config.get("environment"), + ) + if environment and has_lt_version("3.0"): + # Just issue a warning when the environment comes from the pane + # configuration as a warning for the window was already issued when + # the window was created. + if pane_config.get("environment"): + logger.warning( + "Cannot set environment for new panes. " + "You need tmux 3.0 or newer for this.", + ) + environment = None + + assert pane is not None + + pane = pane.split( + attach=True, + start_directory=get_pane_start_directory( + pane_config=pane_config, + window_config=window_config, + ), + shell=get_pane_shell( + pane_config=pane_config, + window_config=window_config, + ), + environment=environment, + ) + + assert isinstance(pane, Pane) + + if "layout" in window_config: + window.select_layout(window_config["layout"]) + + if "suppress_history" in pane_config: + suppress = pane_config["suppress_history"] + elif "suppress_history" in window_config: + suppress = window_config["suppress_history"] + else: + suppress = True + + enter = pane_config.get("enter", True) + sleep_before = pane_config.get("sleep_before", None) + sleep_after = pane_config.get("sleep_after", None) + for cmd in pane_config["shell_command"]: + enter = cmd.get("enter", enter) + sleep_before = cmd.get("sleep_before", sleep_before) + sleep_after = cmd.get("sleep_after", sleep_after) + + if sleep_before is not None: + time.sleep(sleep_before) + + pane.send_keys(cmd["cmd"], suppress_history=suppress, enter=enter) + + if sleep_after is not None: + time.sleep(sleep_after) + + if pane_config.get("focus"): + assert pane.pane_id is not None + window.select_pane(pane.pane_id) + + yield pane, pane_config + + def config_after_window( + self, + window: Window, + window_config: dict[str, t.Any], + ) -> None: + """Actions to apply to window after window and pane finished. + + When building a tmux session, sometimes its easier to postpone things + like setting options until after things are already structurally + prepared. + + Parameters + ---------- + window : :class:`libtmux.Window` + window to create panes for + window_config : dict + config section for window + """ + if "options_after" in window_config and isinstance( + window_config["options_after"], + dict, + ): + for key, val in window_config["options_after"].items(): + window.set_window_option(key, val) + + def find_current_attached_session(self) -> Session: + """Return current attached session.""" + assert self.server is not None + + current_active_pane = get_current_pane(self.server) + + if current_active_pane is None: + raise exc.ActiveSessionMissingWorkspaceException + + return next( + ( + s + for s in self.server.sessions + if s.session_id == current_active_pane.session_id + ), + ) + + def first_window_pass(self, i: int, session: Session, append: bool) -> bool: + """Return True first window, used when iterating session windows.""" + return len(session.windows) == 1 and i == 1 and not append diff --git a/src/tmuxp/workspace/constants.py b/src/tmuxp/workspace/constants.py new file mode 100644 index 00000000000..48ecc9c1f6c --- /dev/null +++ b/src/tmuxp/workspace/constants.py @@ -0,0 +1,5 @@ +"""Constant variables for tmuxp workspace functionality.""" + +from __future__ import annotations + +VALID_WORKSPACE_DIR_FILE_EXTENSIONS = [".yaml", ".yml", ".json"] diff --git a/src/tmuxp/workspace/finders.py b/src/tmuxp/workspace/finders.py new file mode 100644 index 00000000000..946ba4cc918 --- /dev/null +++ b/src/tmuxp/workspace/finders.py @@ -0,0 +1,253 @@ +"""Workspace (configuration file) finders for tmuxp.""" + +from __future__ import annotations + +import logging +import os +import typing as t + +from colorama import Fore + +from tmuxp.cli.utils import tmuxp_echo +from tmuxp.workspace.constants import VALID_WORKSPACE_DIR_FILE_EXTENSIONS + +logger = logging.getLogger(__name__) + +if t.TYPE_CHECKING: + import pathlib + + from typing_extensions import TypeAlias + + from tmuxp.types import StrPath + + ValidExtensions: TypeAlias = t.Literal[".yml", ".yaml", ".json"] + + +def is_workspace_file( + filename: str, + extensions: ValidExtensions | list[ValidExtensions] | None = None, +) -> bool: + """ + Return True if file has a valid workspace file type. + + Parameters + ---------- + filename : str + filename to check (e.g. ``mysession.json``). + extensions : str or list + filetypes to check (e.g. ``['.yaml', '.json']``). + + Returns + ------- + bool + """ + if extensions is None: + extensions = [".yml", ".yaml", ".json"] + extensions = [extensions] if isinstance(extensions, str) else extensions + return any(filename.endswith(e) for e in extensions) + + +def in_dir( + workspace_dir: pathlib.Path | str | None = None, + extensions: list[ValidExtensions] | None = None, +) -> list[str]: + """ + Return a list of workspace_files in ``workspace_dir``. + + Parameters + ---------- + workspace_dir : str + directory to search + extensions : list + filetypes to check (e.g. ``['.yaml', '.json']``). + + Returns + ------- + list + """ + if workspace_dir is None: + workspace_dir = os.path.expanduser("~/.tmuxp") + + if extensions is None: + extensions = [".yml", ".yaml", ".json"] + + return [ + filename + for filename in os.listdir(workspace_dir) + if is_workspace_file(filename, extensions) and not filename.startswith(".") + ] + + +def in_cwd() -> list[str]: + """ + Return list of workspace_files in current working directory. + + If filename is ``.tmuxp.py``, ``.tmuxp.json``, ``.tmuxp.yaml``. + + Returns + ------- + list + workspace_files in current working directory + + Examples + -------- + >>> sorted(in_cwd()) + ['.tmuxp.json', '.tmuxp.yaml'] + """ + return [ + filename + for filename in os.listdir(os.getcwd()) + if filename.startswith(".tmuxp") and is_workspace_file(filename) + ] + + +def get_workspace_dir() -> str: + """ + Return tmuxp workspace directory. + + ``TMUXP_CONFIGDIR`` environmental variable has precedence if set. We also + evaluate XDG default directory from XDG_CONFIG_HOME environmental variable + if set or its default. Then the old default ~/.tmuxp is returned for + compatibility. + + Returns + ------- + str : + absolute path to tmuxp config directory + """ + paths = [] + if "TMUXP_CONFIGDIR" in os.environ: + paths.append(os.environ["TMUXP_CONFIGDIR"]) + if "XDG_CONFIG_HOME" in os.environ: + paths.append(os.path.join(os.environ["XDG_CONFIG_HOME"], "tmuxp")) + else: + paths.append("~/.config/tmuxp/") + paths.append("~/.tmuxp") + + for path in paths: + path = os.path.expanduser(path) + if os.path.isdir(path): + return path + # Return last path as default if none of the previous ones matched + return path + + +def find_workspace_file( + workspace_file: StrPath, + workspace_dir: StrPath | None = None, +) -> str: + """ + Return the real config path or raise an exception. + + If workspace file is directory, scan for .tmuxp.{yaml,yml,json} in directory. If + one or more found, it will warn and pick the first. + + If workspace file is ".", "./" or None, it will scan current directory. + + If workspace file is has no path and only a filename, e.g. "my_workspace.yaml" it + will search workspace dir. + + If workspace file has no path and no extension, e.g. "my_workspace", it will scan + for file name with yaml, yml and json. If multiple exist, it will warn and pick the + first. + + Parameters + ---------- + workspace_file : str + workspace file, valid examples: + + - a file name, my_workspace.yaml + - relative path, ../my_workspace.yaml or ../project + - a period, . + """ + if not workspace_dir: + workspace_dir = get_workspace_dir() + path = os.path + exists, join, isabs = path.exists, path.join, path.isabs + dirname, normpath, splitext = path.dirname, path.normpath, path.splitext + cwd = os.getcwd() + is_name = False + file_error = None + + workspace_file = os.path.expanduser(workspace_file) + # if purename, resolve to confg dir + if is_pure_name(workspace_file): + is_name = True + elif ( + not isabs(workspace_file) + or len(dirname(workspace_file)) > 1 + or workspace_file in {".", "", "./"} + ): # if relative, fill in full path + workspace_file = normpath(join(cwd, workspace_file)) + + # no extension, scan + if path.isdir(workspace_file) or not splitext(workspace_file)[1]: + if is_name: + candidates = [ + x + for x in [ + f"{join(workspace_dir, workspace_file)}{ext}" + for ext in VALID_WORKSPACE_DIR_FILE_EXTENSIONS + ] + if exists(x) + ] + if not candidates: + file_error = ( + "workspace-file not found " + f"in workspace dir (yaml/yml/json) {workspace_dir} for name" + ) + else: + candidates = [ + x + for x in [ + join(workspace_file, ext) + for ext in [".tmuxp.yaml", ".tmuxp.yml", ".tmuxp.json"] + ] + if exists(x) + ] + + if len(candidates) > 1: + tmuxp_echo( + Fore.RED + + "Multiple .tmuxp.{yml,yaml,json} workspace_files in " + + dirname(workspace_file) + + Fore.RESET, + ) + tmuxp_echo( + "This is undefined behavior, use only one. " + "Use file names e.g. myproject.json, coolproject.yaml. " + "You can load them by filename.", + ) + elif not len(candidates): + file_error = "No tmuxp files found in directory" + if len(candidates): + workspace_file = candidates[0] + elif not exists(workspace_file): + file_error = "file not found" + + if file_error: + raise FileNotFoundError(file_error, workspace_file) + + return workspace_file + + +def is_pure_name(path: str) -> bool: + """ + Return True if path is a name and not a file path. + + Parameters + ---------- + path : str + Path (can be absolute, relative, etc.) + + Returns + ------- + bool + True if path is a name of workspace in workspace dir, not file path. + """ + return ( + not os.path.isabs(path) + and len(os.path.dirname(path)) == 0 + and not os.path.splitext(path)[1] + and path not in {".", ""} + ) diff --git a/src/tmuxp/workspace/freezer.py b/src/tmuxp/workspace/freezer.py new file mode 100644 index 00000000000..648a7755f10 --- /dev/null +++ b/src/tmuxp/workspace/freezer.py @@ -0,0 +1,123 @@ +"""Tmux session freezing functionality for tmuxp.""" + +from __future__ import annotations + +import typing as t + +if t.TYPE_CHECKING: + from libtmux.pane import Pane + from libtmux.session import Session + from libtmux.window import Window + + +def inline(workspace_dict: dict[str, t.Any]) -> t.Any: + """Return workspace with inlined shorthands. Opposite of :meth:`loader.expand`. + + Parameters + ---------- + workspace_dict : dict + + Returns + ------- + dict + workspace with shorthands inlined. + """ + if ( + "shell_command" in workspace_dict + and isinstance(workspace_dict["shell_command"], list) + and len(workspace_dict["shell_command"]) == 1 + ): + workspace_dict["shell_command"] = workspace_dict["shell_command"][0] + + if len(workspace_dict.keys()) == 1: + return workspace_dict["shell_command"] + if ( + "shell_command_before" in workspace_dict + and isinstance(workspace_dict["shell_command_before"], list) + and len(workspace_dict["shell_command_before"]) == 1 + ): + workspace_dict["shell_command_before"] = workspace_dict["shell_command_before"][ + 0 + ] + + # recurse into window and pane workspace items + if "windows" in workspace_dict: + workspace_dict["windows"] = [ + inline(window) for window in workspace_dict["windows"] + ] + if "panes" in workspace_dict: + workspace_dict["panes"] = [inline(pane) for pane in workspace_dict["panes"]] + + return workspace_dict + + +def freeze(session: Session) -> dict[str, t.Any]: + """Freeze live tmux session into a tmuxp workspacee. + + Parameters + ---------- + session : :class:`libtmux.Session` + session object + + Returns + ------- + dict + tmuxp compatible workspace + """ + session_config: dict[str, t.Any] = { + "session_name": session.session_name, + "windows": [], + } + + for window in session.windows: + window_config: dict[str, t.Any] = { + "options": window.show_window_options(), + "window_name": window.name, + "layout": window.window_layout, + "panes": [], + } + + if getattr(window, "window_active", "0") == "1": + window_config["focus"] = "true" + + # If all panes have same path, set 'start_directory' instead + # of using 'cd' shell commands. + def pane_has_same_path(window: Window, pane: Pane) -> bool: + return window.panes[0].pane_current_path == pane.pane_current_path + + if all(pane_has_same_path(window=window, pane=pane) for pane in window.panes): + window_config["start_directory"] = window.panes[0].pane_current_path + + for pane in window.panes: + pane_config: str | dict[str, t.Any] = {"shell_command": []} + assert isinstance(pane_config, dict) + + if "start_directory" not in window_config and pane.pane_current_path: + pane_config["shell_command"].append("cd " + pane.pane_current_path) + + if getattr(pane, "pane_active", "0") == "1": + pane_config["focus"] = "true" + + current_cmd = pane.pane_current_command + + def filter_interpreters_and_shells(current_cmd: str | None) -> bool: + return current_cmd is not None and ( + current_cmd.startswith("-") + or any( + current_cmd.endswith(cmd) for cmd in ["python", "ruby", "node"] + ) + ) + + if filter_interpreters_and_shells(current_cmd=current_cmd): + current_cmd = None + + if current_cmd: + pane_config["shell_command"].append(current_cmd) + elif not len(pane_config["shell_command"]): + pane_config = "pane" + + window_config["panes"].append(pane_config) + + session_config["windows"].append(window_config) + + return session_config diff --git a/src/tmuxp/workspace/importers.py b/src/tmuxp/workspace/importers.py new file mode 100644 index 00000000000..fda361ca6f1 --- /dev/null +++ b/src/tmuxp/workspace/importers.py @@ -0,0 +1,170 @@ +"""Configuration import adapters to load teamocil, tmuxinator, etc. in tmuxp.""" + +from __future__ import annotations + +import typing as t + + +def import_tmuxinator(workspace_dict: dict[str, t.Any]) -> dict[str, t.Any]: + """Return tmuxp workspace from a `tmuxinator`_ yaml workspace. + + .. _tmuxinator: https://github.com/aziz/tmuxinator + + Parameters + ---------- + workspace_dict : dict + python dict for tmuxp workspace. + + Returns + ------- + dict + """ + tmuxp_workspace: dict[str, t.Any] = {} + + if "project_name" in workspace_dict: + tmuxp_workspace["session_name"] = workspace_dict.pop("project_name") + elif "name" in workspace_dict: + tmuxp_workspace["session_name"] = workspace_dict.pop("name") + else: + tmuxp_workspace["session_name"] = None + + if "project_root" in workspace_dict: + tmuxp_workspace["start_directory"] = workspace_dict.pop("project_root") + elif "root" in workspace_dict: + tmuxp_workspace["start_directory"] = workspace_dict.pop("root") + + if "cli_args" in workspace_dict: + tmuxp_workspace["config"] = workspace_dict["cli_args"] + + if "-f" in tmuxp_workspace["config"]: + tmuxp_workspace["config"] = ( + tmuxp_workspace["config"].replace("-f", "").strip() + ) + elif "tmux_options" in workspace_dict: + tmuxp_workspace["config"] = workspace_dict["tmux_options"] + + if "-f" in tmuxp_workspace["config"]: + tmuxp_workspace["config"] = ( + tmuxp_workspace["config"].replace("-f", "").strip() + ) + + if "socket_name" in workspace_dict: + tmuxp_workspace["socket_name"] = workspace_dict["socket_name"] + + tmuxp_workspace["windows"] = [] + + if "tabs" in workspace_dict: + workspace_dict["windows"] = workspace_dict.pop("tabs") + + if "pre" in workspace_dict and "pre_window" in workspace_dict: + tmuxp_workspace["shell_command"] = workspace_dict["pre"] + + if isinstance(workspace_dict["pre"], str): + tmuxp_workspace["shell_command_before"] = [workspace_dict["pre_window"]] + else: + tmuxp_workspace["shell_command_before"] = workspace_dict["pre_window"] + elif "pre" in workspace_dict: + if isinstance(workspace_dict["pre"], str): + tmuxp_workspace["shell_command_before"] = [workspace_dict["pre"]] + else: + tmuxp_workspace["shell_command_before"] = workspace_dict["pre"] + + if "rbenv" in workspace_dict: + if "shell_command_before" not in tmuxp_workspace: + tmuxp_workspace["shell_command_before"] = [] + tmuxp_workspace["shell_command_before"].append( + "rbenv shell {}".format(workspace_dict["rbenv"]), + ) + + for window_dict in workspace_dict["windows"]: + for k, v in window_dict.items(): + window_dict = {"window_name": k} + + if isinstance(v, str) or v is None: + window_dict["panes"] = [v] + tmuxp_workspace["windows"].append(window_dict) + continue + if isinstance(v, list): + window_dict["panes"] = v + tmuxp_workspace["windows"].append(window_dict) + continue + + if "pre" in v: + window_dict["shell_command_before"] = v["pre"] + if "panes" in v: + window_dict["panes"] = v["panes"] + if "root" in v: + window_dict["start_directory"] = v["root"] + + if "layout" in v: + window_dict["layout"] = v["layout"] + tmuxp_workspace["windows"].append(window_dict) + return tmuxp_workspace + + +def import_teamocil(workspace_dict: dict[str, t.Any]) -> dict[str, t.Any]: + """Return tmuxp workspace from a `teamocil`_ yaml workspace. + + .. _teamocil: https://github.com/remiprev/teamocil + + Parameters + ---------- + workspace_dict : dict + python dict for tmuxp workspace + + Notes + ----- + Todos: + + - change 'root' to a cd or start_directory + - width in pane -> main-pain-width + - with_env_var + - clear + - cmd_separator + """ + tmuxp_workspace: dict[str, t.Any] = {} + + if "session" in workspace_dict: + workspace_dict = workspace_dict["session"] + + tmuxp_workspace["session_name"] = workspace_dict.get("name", None) + + if "root" in workspace_dict: + tmuxp_workspace["start_directory"] = workspace_dict.pop("root") + + tmuxp_workspace["windows"] = [] + + for w in workspace_dict["windows"]: + window_dict = {"window_name": w["name"]} + + if "clear" in w: + window_dict["clear"] = w["clear"] + + if "filters" in w: + if "before" in w["filters"]: + for _b in w["filters"]["before"]: + window_dict["shell_command_before"] = w["filters"]["before"] + if "after" in w["filters"]: + for _b in w["filters"]["after"]: + window_dict["shell_command_after"] = w["filters"]["after"] + + if "root" in w: + window_dict["start_directory"] = w.pop("root") + + if "splits" in w: + w["panes"] = w.pop("splits") + + if "panes" in w: + for p in w["panes"]: + if "cmd" in p: + p["shell_command"] = p.pop("cmd") + if "width" in p: + # TODO support for height/width + p.pop("width") + window_dict["panes"] = w["panes"] + + if "layout" in w: + window_dict["layout"] = w["layout"] + tmuxp_workspace["windows"].append(window_dict) + + return tmuxp_workspace diff --git a/src/tmuxp/workspace/loader.py b/src/tmuxp/workspace/loader.py new file mode 100644 index 00000000000..613b2b589df --- /dev/null +++ b/src/tmuxp/workspace/loader.py @@ -0,0 +1,264 @@ +"""Workspace hydration and loading for tmuxp.""" + +from __future__ import annotations + +import logging +import os +import pathlib +import typing as t + +logger = logging.getLogger(__name__) + + +def expandshell(value: str) -> str: + """Resolve shell variables based on user's ``$HOME`` and ``env``. + + :py:func:`os.path.expanduser` and :py:func:`os.path.expandvars`. + + Parameters + ---------- + value : str + value to expand + + Returns + ------- + str + value with shell variables expanded + """ + return os.path.expandvars(os.path.expanduser(value)) # NOQA: PTH111 + + +def expand_cmd(p: dict[str, t.Any]) -> dict[str, t.Any]: + """Resolve shell variables and expand shorthands in a tmuxp config mapping.""" + if isinstance(p, str): + p = {"shell_command": [p]} + elif isinstance(p, list): + p = {"shell_command": p} + elif not p: + p = {"shell_command": []} + + assert isinstance(p, dict) + if "shell_command" in p: + cmds = p["shell_command"] + + if isinstance(p["shell_command"], str): + cmds = [cmds] + + if not cmds or any(a == cmds for a in [None, "blank", "pane"]): + cmds = [] + + if ( + isinstance(cmds, list) + and len(cmds) == 1 + and any(a in cmds for a in [None, "blank", "pane"]) + ): + cmds = [] + + for cmd_idx, cmd in enumerate(cmds): + if isinstance(cmd, str): + cmds[cmd_idx] = {"cmd": cmd} + cmds[cmd_idx]["cmd"] = expandshell(cmds[cmd_idx]["cmd"]) + + p["shell_command"] = cmds + else: + p["shell_command"] = [] + return p + + +def expand( + workspace_dict: dict[str, t.Any], + cwd: pathlib.Path | str | None = None, + parent: t.Any | None = None, +) -> dict[str, t.Any]: + """Resolve workspace variables and expand shorthand style / inline properties. + + This is necessary to keep the code in the :class:`WorkspaceBuilder` clean + and also allow for neat, short-hand "sugarified" syntax. + + As a simple example, internally, tmuxp expects that workspace options + like ``shell_command`` are a list (array):: + + 'shell_command': ['htop'] + + tmuxp workspace allow for it to be simply a string:: + + 'shell_command': 'htop' + + ConfigReader will load JSON/YAML files into python dicts for you. + + Parameters + ---------- + workspace_dict : dict + the tmuxp workspace for the session + cwd : str + directory to expand relative paths against. should be the dir of the + workspace directory. + parent : str + (used on recursive entries) start_directory of parent window or session + object. + + Returns + ------- + dict + """ + # Note: cli.py will expand workspaces relative to project's workspace directory + # for the first cwd argument. + cwd = pathlib.Path().cwd() if not cwd else pathlib.Path(cwd) + + if "session_name" in workspace_dict: + workspace_dict["session_name"] = expandshell(workspace_dict["session_name"]) + if "window_name" in workspace_dict: + workspace_dict["window_name"] = expandshell(workspace_dict["window_name"]) + if "environment" in workspace_dict: + for key in workspace_dict["environment"]: + val = workspace_dict["environment"][key] + val = expandshell(val) + if any(val.startswith(a) for a in [".", "./"]): + val = str(cwd / val) + workspace_dict["environment"][key] = val + if "global_options" in workspace_dict: + for key in workspace_dict["global_options"]: + val = workspace_dict["global_options"][key] + if isinstance(val, str): + val = expandshell(val) + if any(val.startswith(a) for a in [".", "./"]): + val = str(cwd / val) + workspace_dict["global_options"][key] = val + if "options" in workspace_dict: + for key in workspace_dict["options"]: + val = workspace_dict["options"][key] + if isinstance(val, str): + val = expandshell(val) + if any(val.startswith(a) for a in [".", "./"]): + val = str(cwd / val) + workspace_dict["options"][key] = val + + # Any workspace section, session, window, pane that can contain the + # 'shell_command' value + if "start_directory" in workspace_dict: + workspace_dict["start_directory"] = expandshell( + workspace_dict["start_directory"], + ) + start_path = workspace_dict["start_directory"] + if any(start_path.startswith(a) for a in [".", "./"]): + # if window has a session, or pane has a window with a + # start_directory of . or ./, make sure the start_directory can be + # relative to the parent. + # + # This is for the case where you may be loading a workspace from + # outside your shell current directory. + if parent: + cwd = pathlib.Path(parent["start_directory"]) + + start_path = str((cwd / start_path).resolve(strict=False)) + + workspace_dict["start_directory"] = start_path + + if "before_script" in workspace_dict: + workspace_dict["before_script"] = expandshell(workspace_dict["before_script"]) + if any(workspace_dict["before_script"].startswith(a) for a in [".", "./"]): + workspace_dict["before_script"] = str(cwd / workspace_dict["before_script"]) + + if "shell_command" in workspace_dict and isinstance( + workspace_dict["shell_command"], + str, + ): + workspace_dict["shell_command"] = [workspace_dict["shell_command"]] + + if "shell_command_before" in workspace_dict: + shell_command_before = workspace_dict["shell_command_before"] + + workspace_dict["shell_command_before"] = expand_cmd(shell_command_before) + + # recurse into window and pane workspace items + if "windows" in workspace_dict: + workspace_dict["windows"] = [ + expand(window, parent=workspace_dict) + for window in workspace_dict["windows"] + ] + elif "panes" in workspace_dict: + pane_dicts = workspace_dict["panes"] + for pane_idx, pane_dict in enumerate(pane_dicts): + pane_dicts[pane_idx] = {} + pane_dicts[pane_idx].update(expand_cmd(pane_dict)) + workspace_dict["panes"] = [ + expand(pane, parent=workspace_dict) for pane in pane_dicts + ] + + return workspace_dict + + +def trickle(workspace_dict: dict[str, t.Any]) -> dict[str, t.Any]: + """Return a dict with "trickled down" / inherited workspace values. + + This will only work if workspace has been expanded to full form with + :meth:`loader.expand`. + + tmuxp allows certain commands to be default at the session, window + level. shell_command_before trickles down and prepends the + ``shell_command`` for the pane. + + Parameters + ---------- + workspace_dict : dict + the tmuxp workspace. + + Returns + ------- + dict + """ + # prepends a pane's ``shell_command`` list with the window and sessions' + # ``shell_command_before``. + + session_start_directory = workspace_dict.get("start_directory") + + suppress_history = workspace_dict.get("suppress_history") + + for window_dict in workspace_dict["windows"]: + # Prepend start_directory to relative window commands + if session_start_directory: + session_start_directory = session_start_directory + if "start_directory" not in window_dict: + window_dict["start_directory"] = session_start_directory + elif not any( + window_dict["start_directory"].startswith(a) for a in ["~", "/"] + ): + window_start_path = ( + pathlib.Path(session_start_directory) + / window_dict["start_directory"] + ) + window_dict["start_directory"] = str(window_start_path) + + # We only need to trickle to the window, workspace builder checks wconf + if suppress_history is not None and "suppress_history" not in window_dict: + window_dict["suppress_history"] = suppress_history + + # If panes were NOT specified for a window, assume that a single pane + # with no shell commands is desired + if "panes" not in window_dict: + window_dict["panes"] = [{"shell_command": []}] + + for pane_idx, pane_dict in enumerate(window_dict["panes"]): + commands_before = [] + + # Prepend shell_command_before to commands + if "shell_command_before" in workspace_dict: + commands_before.extend( + workspace_dict["shell_command_before"]["shell_command"], + ) + if "shell_command_before" in window_dict: + commands_before.extend( + window_dict["shell_command_before"]["shell_command"], + ) + if "shell_command_before" in pane_dict: + commands_before.extend( + pane_dict["shell_command_before"]["shell_command"], + ) + + if "shell_command" in pane_dict: + commands_before.extend(pane_dict["shell_command"]) + + window_dict["panes"][pane_idx]["shell_command"] = commands_before + # pane_dict['shell_command'] = commands_before + + return workspace_dict diff --git a/src/tmuxp/workspace/validation.py b/src/tmuxp/workspace/validation.py new file mode 100644 index 00000000000..2573209a5d2 --- /dev/null +++ b/src/tmuxp/workspace/validation.py @@ -0,0 +1,87 @@ +"""Validation errors for tmuxp configuration files.""" + +from __future__ import annotations + +import typing as t + +from tmuxp import exc + + +class SchemaValidationError(exc.WorkspaceError): + """Tmuxp configuration validation base error.""" + + +class SessionNameMissingValidationError(SchemaValidationError): + """Tmuxp configuration error for session name missing.""" + + def __init__(self, *args: object, **kwargs: object) -> None: + return super().__init__( + 'workspace requires "session_name"', + *args, + **kwargs, + ) + + +class WindowListMissingValidationError(SchemaValidationError): + """Tmuxp configuration error for window list missing.""" + + def __init__(self, *args: object, **kwargs: object) -> None: + return super().__init__( + 'workspace requires list of "windows"', + *args, + **kwargs, + ) + + +class WindowNameMissingValidationError(SchemaValidationError): + """Tmuxp configuration error for missing window_name.""" + + def __init__(self, *args: object, **kwargs: object) -> None: + return super().__init__( + 'workspace window is missing "window_name"', + *args, + **kwargs, + ) + + +class InvalidPluginsValidationError(SchemaValidationError): + """Tmuxp configuration error for invalid plugins.""" + + def __init__(self, plugins: t.Any, *args: object, **kwargs: object) -> None: + return super().__init__( + '"plugins" only supports list type. ' + f" Received {type(plugins)}, " + f"value: {plugins}", + *args, + **kwargs, + ) + + +def validate_schema(workspace_dict: t.Any) -> bool: + """ + Return True if workspace schema is correct. + + Parameters + ---------- + workspace_dict : dict + tmuxp workspace data + + Returns + ------- + bool + """ + # verify session_name + if "session_name" not in workspace_dict: + raise SessionNameMissingValidationError + + if "windows" not in workspace_dict: + raise WindowListMissingValidationError + + for window in workspace_dict["windows"]: + if "window_name" not in window: + raise WindowNameMissingValidationError + + if "plugins" in workspace_dict and not isinstance(workspace_dict["plugins"], list): + raise InvalidPluginsValidationError(plugins=workspace_dict.get("plugins")) + + return True diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 00000000000..6940f228687 --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1 @@ +"""Tests for tmuxp.""" diff --git a/tests/cli/__init__.py b/tests/cli/__init__.py new file mode 100644 index 00000000000..95437011db7 --- /dev/null +++ b/tests/cli/__init__.py @@ -0,0 +1 @@ +"""CLI tests for tmuxp.""" diff --git a/tests/cli/test_cli.py b/tests/cli/test_cli.py new file mode 100644 index 00000000000..34524946d4c --- /dev/null +++ b/tests/cli/test_cli.py @@ -0,0 +1,175 @@ +"""CLI tests for tmuxp's core shell functionality.""" + +from __future__ import annotations + +import argparse +import contextlib +import pathlib +import typing as t + +import libtmux +import pytest + +from tests.fixtures import utils as test_utils +from tmuxp import cli +from tmuxp._internal.config_reader import ConfigReader +from tmuxp.cli.import_config import get_teamocil_dir, get_tmuxinator_dir +from tmuxp.cli.load import _reattach, load_plugins +from tmuxp.cli.utils import tmuxp_echo +from tmuxp.workspace import loader +from tmuxp.workspace.builder import WorkspaceBuilder +from tmuxp.workspace.finders import find_workspace_file + +if t.TYPE_CHECKING: + import _pytest.capture + from libtmux.server import Server + + +def test_creates_config_dir_not_exists(tmp_path: pathlib.Path) -> None: + """cli.startup() creates config dir if not exists.""" + cli.startup(tmp_path) + assert tmp_path.exists() + + +class HelpTestFixture(t.NamedTuple): + """Test fixture for help command tests.""" + + test_id: str + cli_args: list[str] + + +HELP_TEST_FIXTURES: list[HelpTestFixture] = [ + HelpTestFixture( + test_id="help_long_flag", + cli_args=["--help"], + ), + HelpTestFixture( + test_id="help_short_flag", + cli_args=["-h"], + ), +] + + +@pytest.mark.parametrize( + list(HelpTestFixture._fields), + HELP_TEST_FIXTURES, + ids=[test.test_id for test in HELP_TEST_FIXTURES], +) +def test_help( + test_id: str, + cli_args: list[str], + tmp_path: pathlib.Path, + monkeypatch: pytest.MonkeyPatch, + capsys: pytest.CaptureFixture[str], +) -> None: + """Test tmuxp --help / -h.""" + # In scrunched terminals, prevent width causing differentiation in result.out. + monkeypatch.setenv("COLUMNS", "100") + monkeypatch.setenv("LINES", "100") + + with contextlib.suppress(SystemExit): + cli.cli(cli_args) + + result = capsys.readouterr() + + assert "usage: tmuxp [-h] [--version] [--log-level log-level]" in result.out + + +def test_resolve_behavior( + tmp_path: pathlib.Path, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Test resolution of file paths.""" + expect = tmp_path + monkeypatch.chdir(tmp_path) + assert pathlib.Path("../").resolve() == expect.parent + assert pathlib.Path.cwd() == expect + assert pathlib.Path("./").resolve() == expect + assert pathlib.Path(expect).resolve() == expect + + +def test_get_tmuxinator_dir( + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Test get_tmuxinator_dir() helper function.""" + assert get_tmuxinator_dir() == pathlib.Path("~/.tmuxinator").expanduser() + + monkeypatch.setenv("HOME", "/moo") + assert get_tmuxinator_dir() == pathlib.Path("/moo/.tmuxinator/") + assert str(get_tmuxinator_dir()) == "/moo/.tmuxinator" + assert get_tmuxinator_dir() == pathlib.Path("~/.tmuxinator/").expanduser() + + +def test_get_teamocil_dir( + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Test get_teamocil_dir() helper function.""" + assert get_teamocil_dir() == pathlib.Path("~/.teamocil/").expanduser() + + monkeypatch.setenv("HOME", "/moo") + assert get_teamocil_dir() == pathlib.Path("/moo/.teamocil/") + assert str(get_teamocil_dir()) == "/moo/.teamocil" + assert get_teamocil_dir() == pathlib.Path("~/.teamocil/").expanduser() + + +def test_pass_config_dir_argparse( + tmp_path: pathlib.Path, + monkeypatch: pytest.MonkeyPatch, + capsys: pytest.CaptureFixture[str], +) -> None: + """Test workspace configurations can be detected via directory.""" + configdir = tmp_path / "myconfigdir" + configdir.mkdir() + user_config_name = "myconfig" + user_config = configdir / f"{user_config_name}.yaml" + user_config.touch() + + expect = str(user_config) + + parser = argparse.ArgumentParser() + parser.add_argument("workspace_file", type=str) + + def config_cmd(workspace_file: str) -> None: + tmuxp_echo(find_workspace_file(workspace_file, workspace_dir=configdir)) + + def check_cmd(config_arg: str) -> _pytest.capture.CaptureResult[str]: + args = parser.parse_args([config_arg]) + config_cmd(workspace_file=args.workspace_file) + return capsys.readouterr() + + monkeypatch.chdir(configdir) + + assert expect in check_cmd("myconfig").out + assert expect in check_cmd("myconfig.yaml").out + assert expect in check_cmd("./myconfig.yaml").out + assert str(user_config) in check_cmd(str(configdir / "myconfig.yaml")).out + + with pytest.raises(FileNotFoundError): + assert "FileNotFoundError" in check_cmd(".tmuxp.json").out + + +def test_reattach_plugins( + monkeypatch_plugin_test_packages: None, + server: Server, +) -> None: + """Test reattach plugin hook.""" + config_plugins = test_utils.read_workspace_file("workspace/builder/plugin_r.yaml") + + session_config = ConfigReader._load(fmt="yaml", content=config_plugins) + session_config = loader.expand(session_config) + + # open it detached + builder = WorkspaceBuilder( + session_config=session_config, + plugins=load_plugins(session_config), + server=server, + ) + builder.build() + + with contextlib.suppress(libtmux.exc.LibTmuxException): + _reattach(builder) + + assert builder.session is not None + proc = builder.session.cmd("display-message", "-p", "'#S'") + + assert proc.stdout[0] == "'plugin_test_r'" diff --git a/tests/cli/test_convert.py b/tests/cli/test_convert.py new file mode 100644 index 00000000000..8cafecd12ca --- /dev/null +++ b/tests/cli/test_convert.py @@ -0,0 +1,140 @@ +"""CLI tests for tmuxp convert.""" + +from __future__ import annotations + +import contextlib +import io +import json +import typing as t + +import pytest + +from tmuxp import cli + +if t.TYPE_CHECKING: + import pathlib + + +class ConvertTestFixture(t.NamedTuple): + """Test fixture for tmuxp convert command tests.""" + + test_id: str + cli_args: list[str] + + +CONVERT_TEST_FIXTURES: list[ConvertTestFixture] = [ + ConvertTestFixture( + test_id="convert_current_dir", + cli_args=["convert", "."], + ), + ConvertTestFixture( + test_id="convert_yaml_file", + cli_args=["convert", ".tmuxp.yaml"], + ), + ConvertTestFixture( + test_id="convert_yaml_file_auto_confirm", + cli_args=["convert", ".tmuxp.yaml", "-y"], + ), + ConvertTestFixture( + test_id="convert_yml_file", + cli_args=["convert", ".tmuxp.yml"], + ), + ConvertTestFixture( + test_id="convert_yml_file_auto_confirm", + cli_args=["convert", ".tmuxp.yml", "-y"], + ), +] + + +@pytest.mark.parametrize( + list(ConvertTestFixture._fields), + CONVERT_TEST_FIXTURES, + ids=[test.test_id for test in CONVERT_TEST_FIXTURES], +) +def test_convert( + test_id: str, + cli_args: list[str], + tmp_path: pathlib.Path, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Parametrized tests for tmuxp convert.""" + # create dummy tmuxp yaml so we don't get yelled at + filename = cli_args[1] + if filename == ".": + filename = ".tmuxp.yaml" + file_ext = filename.rsplit(".", 1)[-1] + assert file_ext in {"yaml", "yml"}, file_ext + workspace_file_path = tmp_path / filename + workspace_file_path.write_text("\nsession_name: hello\n", encoding="utf-8") + oh_my_zsh_path = tmp_path / ".oh-my-zsh" + oh_my_zsh_path.mkdir() + monkeypatch.setenv("HOME", str(tmp_path)) + + monkeypatch.chdir(tmp_path) + + # If autoconfirm (-y) no need to prompt y + input_args = "y\ny\n" if "-y" not in cli_args else "" + + monkeypatch.setattr("sys.stdin", io.StringIO(input_args)) + with contextlib.suppress(SystemExit): + cli.cli(cli_args) + + tmuxp_json = tmp_path / ".tmuxp.json" + assert tmuxp_json.exists() + assert tmuxp_json.open().read() == json.dumps({"session_name": "hello"}, indent=2) + + +class ConvertJsonTestFixture(t.NamedTuple): + """Test fixture for tmuxp convert json command tests.""" + + test_id: str + cli_args: list[str] + + +CONVERT_JSON_TEST_FIXTURES: list[ConvertJsonTestFixture] = [ + ConvertJsonTestFixture( + test_id="convert_json_current_dir", + cli_args=["convert", "."], + ), + ConvertJsonTestFixture( + test_id="convert_json_file", + cli_args=["convert", ".tmuxp.json"], + ), + ConvertJsonTestFixture( + test_id="convert_json_file_auto_confirm", + cli_args=["convert", ".tmuxp.json", "-y"], + ), +] + + +@pytest.mark.parametrize( + list(ConvertJsonTestFixture._fields), + CONVERT_JSON_TEST_FIXTURES, + ids=[test.test_id for test in CONVERT_JSON_TEST_FIXTURES], +) +def test_convert_json( + test_id: str, + cli_args: list[str], + tmp_path: pathlib.Path, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """CLI test using tmuxp convert to convert configuration from json to yaml.""" + # create dummy tmuxp yaml so we don't get yelled at + json_config = tmp_path / ".tmuxp.json" + json_config.write_text('{"session_name": "hello"}', encoding="utf-8") + oh_my_zsh_path = tmp_path / ".oh-my-zsh" + oh_my_zsh_path.mkdir() + monkeypatch.setenv("HOME", str(tmp_path)) + + monkeypatch.chdir(tmp_path) + + # If autoconfirm (-y) no need to prompt y + input_args = "y\ny\n" if "-y" not in cli_args else "" + + monkeypatch.setattr("sys.stdin", io.StringIO(input_args)) + with contextlib.suppress(SystemExit): + cli.cli(cli_args) + + tmuxp_yaml = tmp_path / ".tmuxp.yaml" + assert tmuxp_yaml.exists() + assert tmuxp_yaml.open().read() == "session_name: hello\n" diff --git a/tests/cli/test_debug_info.py b/tests/cli/test_debug_info.py new file mode 100644 index 00000000000..1729f1e9cc8 --- /dev/null +++ b/tests/cli/test_debug_info.py @@ -0,0 +1,38 @@ +"""CLI tests for tmuxp debuginfo.""" + +from __future__ import annotations + +import typing as t + +from tmuxp import cli + +if t.TYPE_CHECKING: + import pathlib + + import pytest + + +def test_debug_info_cli( + monkeypatch: pytest.MonkeyPatch, + tmp_path: pathlib.Path, + capsys: pytest.CaptureFixture[str], +) -> None: + """Basic CLI test for tmuxp debug-info.""" + monkeypatch.setenv("SHELL", "/bin/bash") + + cli.cli(["debug-info"]) + cli_output = capsys.readouterr().out + assert "environment" in cli_output + assert "python version" in cli_output + assert "system PATH" in cli_output + assert "tmux version" in cli_output + assert "libtmux version" in cli_output + assert "tmuxp version" in cli_output + assert "tmux path" in cli_output + assert "tmuxp path" in cli_output + assert "shell" in cli_output + assert "tmux session" in cli_output + assert "tmux windows" in cli_output + assert "tmux panes" in cli_output + assert "tmux global options" in cli_output + assert "tmux window options" in cli_output diff --git a/tests/cli/test_freeze.py b/tests/cli/test_freeze.py new file mode 100644 index 00000000000..fd828dbb4a2 --- /dev/null +++ b/tests/cli/test_freeze.py @@ -0,0 +1,149 @@ +"""Test workspace freezing functionality for tmuxp.""" + +from __future__ import annotations + +import contextlib +import io +import typing as t + +import pytest + +from tmuxp import cli +from tmuxp._internal.config_reader import ConfigReader + +if t.TYPE_CHECKING: + import pathlib + + from libtmux.server import Server + + +class FreezeTestFixture(t.NamedTuple): + """Test fixture for tmuxp freeze command tests.""" + + test_id: str + cli_args: list[str] + inputs: list[str] + + +class FreezeOverwriteTestFixture(t.NamedTuple): + """Test fixture for tmuxp freeze overwrite command tests.""" + + test_id: str + cli_args: list[str] + inputs: list[str] + + +FREEZE_TEST_FIXTURES: list[FreezeTestFixture] = [ + FreezeTestFixture( + test_id="freeze_named_session", + cli_args=["freeze", "myfrozensession"], + inputs=["y\n", "./la.yaml\n", "y\n"], + ), + FreezeTestFixture( + test_id="freeze_named_session_exists", + cli_args=["freeze", "myfrozensession"], + inputs=["y\n", "./exists.yaml\n", "./la.yaml\n", "y\n"], + ), + FreezeTestFixture( + test_id="freeze_current_session", + cli_args=["freeze"], + inputs=["y\n", "./la.yaml\n", "y\n"], + ), + FreezeTestFixture( + test_id="freeze_current_session_exists", + cli_args=["freeze"], + inputs=["y\n", "./exists.yaml\n", "./la.yaml\n", "y\n"], + ), +] + + +FREEZE_OVERWRITE_TEST_FIXTURES: list[FreezeOverwriteTestFixture] = [ + FreezeOverwriteTestFixture( + test_id="force_overwrite_named_session", + cli_args=["freeze", "mysession", "--force"], + inputs=["\n", "\n", "y\n", "./exists.yaml\n", "y\n"], + ), + FreezeOverwriteTestFixture( + test_id="force_overwrite_current_session", + cli_args=["freeze", "--force"], + inputs=["\n", "\n", "y\n", "./exists.yaml\n", "y\n"], + ), +] + + +@pytest.mark.parametrize( + list(FreezeTestFixture._fields), + FREEZE_TEST_FIXTURES, + ids=[test.test_id for test in FREEZE_TEST_FIXTURES], +) +def test_freeze( + server: Server, + test_id: str, + cli_args: list[str], + inputs: list[str], + tmp_path: pathlib.Path, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Parametrized test for freezing a tmux session to a tmuxp config file.""" + monkeypatch.setenv("HOME", str(tmp_path)) + exists_yaml = tmp_path / "exists.yaml" + exists_yaml.touch() + + server.new_session(session_name="myfirstsession") + server.new_session(session_name="myfrozensession") + + # Assign an active pane to the session + second_session = server.sessions[1] + first_pane_on_second_session_id = second_session.windows[0].panes[0].pane_id + assert first_pane_on_second_session_id + monkeypatch.setenv("TMUX_PANE", first_pane_on_second_session_id) + + monkeypatch.chdir(tmp_path) + # Use tmux server (socket name) used in the test + assert server.socket_name is not None + cli_args = [*cli_args, "-L", server.socket_name] + + monkeypatch.setattr("sys.stdin", io.StringIO("".join(inputs))) + with contextlib.suppress(SystemExit): + cli.cli(cli_args) + + yaml_config_path = tmp_path / "la.yaml" + assert yaml_config_path.exists() + + yaml_config = yaml_config_path.open().read() + frozen_config = ConfigReader._load(fmt="yaml", content=yaml_config) + + assert frozen_config["session_name"] == "myfrozensession" + + +@pytest.mark.parametrize( + list(FreezeOverwriteTestFixture._fields), + FREEZE_OVERWRITE_TEST_FIXTURES, + ids=[test.test_id for test in FREEZE_OVERWRITE_TEST_FIXTURES], +) +def test_freeze_overwrite( + server: Server, + test_id: str, + cli_args: list[str], + inputs: list[str], + tmp_path: pathlib.Path, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Test overwrite prompt when freezing a tmuxp configuration file.""" + monkeypatch.setenv("HOME", str(tmp_path)) + exists_yaml = tmp_path / "exists.yaml" + exists_yaml.touch() + + server.new_session(session_name="mysession") + + monkeypatch.chdir(tmp_path) + # Use tmux server (socket name) used in the test + assert server.socket_name is not None + cli_args = [*cli_args, "-L", server.socket_name] + + monkeypatch.setattr("sys.stdin", io.StringIO("".join(inputs))) + with contextlib.suppress(SystemExit): + cli.cli(cli_args) + + yaml_config_path = tmp_path / "exists.yaml" + assert yaml_config_path.exists() diff --git a/tests/cli/test_import.py b/tests/cli/test_import.py new file mode 100644 index 00000000000..4faad8fe2cb --- /dev/null +++ b/tests/cli/test_import.py @@ -0,0 +1,175 @@ +"""CLI tests for tmuxp import.""" + +from __future__ import annotations + +import contextlib +import io +import typing as t + +import pytest + +from tests.fixtures import utils as test_utils +from tmuxp import cli + +if t.TYPE_CHECKING: + import pathlib + + +class ImportTestFixture(t.NamedTuple): + """Test fixture for basic tmuxp import command tests.""" + + test_id: str + cli_args: list[str] + + +IMPORT_TEST_FIXTURES: list[ImportTestFixture] = [ + ImportTestFixture( + test_id="basic_import", + cli_args=["import"], + ), +] + + +@pytest.mark.parametrize( + list(ImportTestFixture._fields), + IMPORT_TEST_FIXTURES, + ids=[test.test_id for test in IMPORT_TEST_FIXTURES], +) +def test_import( + test_id: str, + cli_args: list[str], + tmp_path: pathlib.Path, + monkeypatch: pytest.MonkeyPatch, + capsys: pytest.CaptureFixture[str], +) -> None: + """Basic CLI test for tmuxp import.""" + cli.cli(cli_args) + result = capsys.readouterr() + assert "tmuxinator" in result.out + assert "teamocil" in result.out + + +class ImportTeamocilTestFixture(t.NamedTuple): + """Test fixture for tmuxp import teamocil command tests.""" + + test_id: str + cli_args: list[str] + inputs: list[str] + + +IMPORT_TEAMOCIL_TEST_FIXTURES: list[ImportTeamocilTestFixture] = [ + ImportTeamocilTestFixture( + test_id="import_teamocil_config_file", + cli_args=["import", "teamocil", "./.teamocil/config.yaml"], + inputs=["\n", "y\n", "./la.yaml\n", "y\n"], + ), + ImportTeamocilTestFixture( + test_id="import_teamocil_config_file_exists", + cli_args=["import", "teamocil", "./.teamocil/config.yaml"], + inputs=["\n", "y\n", "./exists.yaml\n", "./la.yaml\n", "y\n"], + ), + ImportTeamocilTestFixture( + test_id="import_teamocil_config_name", + cli_args=["import", "teamocil", "config"], + inputs=["\n", "y\n", "./exists.yaml\n", "./la.yaml\n", "y\n"], + ), +] + + +@pytest.mark.parametrize( + list(ImportTeamocilTestFixture._fields), + IMPORT_TEAMOCIL_TEST_FIXTURES, + ids=[test.test_id for test in IMPORT_TEAMOCIL_TEST_FIXTURES], +) +def test_import_teamocil( + test_id: str, + cli_args: list[str], + inputs: list[str], + tmp_path: pathlib.Path, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """CLI test for tmuxp import w/ teamocil.""" + teamocil_config = test_utils.read_workspace_file("import_teamocil/test4.yaml") + + teamocil_path = tmp_path / ".teamocil" + teamocil_path.mkdir() + + teamocil_config_path = teamocil_path / "config.yaml" + teamocil_config_path.write_text(teamocil_config, encoding="utf-8") + + exists_yaml = tmp_path / "exists.yaml" + exists_yaml.touch() + + monkeypatch.setenv("HOME", str(tmp_path)) + + monkeypatch.chdir(tmp_path) + monkeypatch.setattr("sys.stdin", io.StringIO("".join(inputs))) + + with contextlib.suppress(SystemExit): + cli.cli(cli_args) + + new_config_yaml = tmp_path / "la.yaml" + assert new_config_yaml.exists() + + +class ImportTmuxinatorTestFixture(t.NamedTuple): + """Test fixture for tmuxp import tmuxinator command tests.""" + + test_id: str + cli_args: list[str] + inputs: list[str] + + +IMPORT_TMUXINATOR_TEST_FIXTURES: list[ImportTmuxinatorTestFixture] = [ + ImportTmuxinatorTestFixture( + test_id="import_tmuxinator_config_file", + cli_args=["import", "tmuxinator", "./.tmuxinator/config.yaml"], + inputs=["\n", "y\n", "./la.yaml\n", "y\n"], + ), + ImportTmuxinatorTestFixture( + test_id="import_tmuxinator_config_file_exists", + cli_args=["import", "tmuxinator", "./.tmuxinator/config.yaml"], + inputs=["\n", "y\n", "./exists.yaml\n", "./la.yaml\n", "y\n"], + ), + ImportTmuxinatorTestFixture( + test_id="import_tmuxinator_config_name", + cli_args=["import", "tmuxinator", "config"], + inputs=["\n", "y\n", "./exists.yaml\n", "./la.yaml\n", "y\n"], + ), +] + + +@pytest.mark.parametrize( + list(ImportTmuxinatorTestFixture._fields), + IMPORT_TMUXINATOR_TEST_FIXTURES, + ids=[test.test_id for test in IMPORT_TMUXINATOR_TEST_FIXTURES], +) +def test_import_tmuxinator( + test_id: str, + cli_args: list[str], + inputs: list[str], + tmp_path: pathlib.Path, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """CLI test for tmuxp import w/ tmuxinator.""" + tmuxinator_config = test_utils.read_workspace_file("import_tmuxinator/test3.yaml") + + tmuxinator_path = tmp_path / ".tmuxinator" + tmuxinator_path.mkdir() + + tmuxinator_config_path = tmuxinator_path / "config.yaml" + tmuxinator_config_path.write_text(tmuxinator_config, encoding="utf-8") + + exists_yaml = tmp_path / "exists.yaml" + exists_yaml.touch() + + monkeypatch.setenv("HOME", str(tmp_path)) + + monkeypatch.chdir(tmp_path) + + monkeypatch.setattr("sys.stdin", io.StringIO("".join(inputs))) + with contextlib.suppress(SystemExit): + cli.cli(cli_args) + + new_config_yaml = tmp_path / "la.yaml" + assert new_config_yaml.exists() diff --git a/tests/cli/test_load.py b/tests/cli/test_load.py new file mode 100644 index 00000000000..da473b95432 --- /dev/null +++ b/tests/cli/test_load.py @@ -0,0 +1,757 @@ +"""CLI tests for tmuxp load.""" + +from __future__ import annotations + +import contextlib +import io +import pathlib +import typing as t + +import libtmux +import pytest +from libtmux.common import has_lt_version +from libtmux.server import Server +from libtmux.session import Session + +from tests.constants import FIXTURE_PATH +from tests.fixtures import utils as test_utils +from tmuxp import cli +from tmuxp._internal.config_reader import ConfigReader +from tmuxp.cli.load import ( + _load_append_windows_to_current_session, + _load_attached, + load_plugins, + load_workspace, +) +from tmuxp.workspace import loader +from tmuxp.workspace.builder import WorkspaceBuilder + + +def test_load_workspace( + server: Server, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Generic test for loading a tmuxp workspace via tmuxp load.""" + # this is an implementation test. Since this testsuite may be ran within + # a tmux session by the developer himself, delete the TMUX variable + # temporarily. + monkeypatch.delenv("TMUX", raising=False) + session_file = FIXTURE_PATH / "workspace/builder" / "two_pane.yaml" + + # open it detached + session = load_workspace( + session_file, + socket_name=server.socket_name, + detached=True, + ) + + assert isinstance(session, Session) + assert session.name == "sample workspace" + + +def test_load_workspace_passes_tmux_config( + server: Server, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Test tmuxp load with a tmux configuration file.""" + # this is an implementation test. Since this testsuite may be ran within + # a tmux session by the developer himself, delete the TMUX variable + # temporarily. + monkeypatch.delenv("TMUX", raising=False) + session_file = FIXTURE_PATH / "workspace/builder" / "two_pane.yaml" + + # open it detached + session = load_workspace( + session_file, + socket_name=server.socket_name, + tmux_config_file=str(FIXTURE_PATH / "tmux" / "tmux.conf"), + detached=True, + ) + + assert isinstance(session, Session) + assert isinstance(session.server, Server) + assert session.server.config_file == str(FIXTURE_PATH / "tmux" / "tmux.conf") + + +def test_load_workspace_named_session( + server: Server, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Test tmuxp load with a custom tmux session name.""" + # this is an implementation test. Since this testsuite may be ran within + # a tmux session by the developer himself, delete the TMUX variable + # temporarily. + monkeypatch.delenv("TMUX", raising=False) + session_file = FIXTURE_PATH / "workspace/builder" / "two_pane.yaml" + + # open it detached + session = load_workspace( + session_file, + socket_name=server.socket_name, + new_session_name="tmuxp-new", + detached=True, + ) + + assert isinstance(session, Session) + assert session.name == "tmuxp-new" + + +@pytest.mark.skipif( + has_lt_version("2.1"), + reason="exact session name matches only tmux >= 2.1", +) +def test_load_workspace_name_match_regression_252( + tmp_path: pathlib.Path, + server: Server, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Test tmuxp load for a regression where tmux shell names would not match.""" + monkeypatch.delenv("TMUX", raising=False) + session_file = FIXTURE_PATH / "workspace/builder" / "two_pane.yaml" + + # open it detached + session = load_workspace( + session_file, + socket_name=server.socket_name, + detached=True, + ) + + assert isinstance(session, Session) + assert session.name == "sample workspace" + + workspace_file = tmp_path / "simple.yaml" + + workspace_file.write_text( + """ +session_name: sampleconfi +start_directory: './' +windows: +- panes: + - echo 'hey'""", + encoding="utf-8", + ) + + # open it detached + session = load_workspace( + str(workspace_file), + socket_name=server.socket_name, + detached=True, + ) + assert session is not None + assert session.name == "sampleconfi" + + +def test_load_symlinked_workspace( + server: Server, + tmp_path: pathlib.Path, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Test tmuxp load can follow a symlinked tmuxp config file.""" + # this is an implementation test. Since this testsuite may be ran within + # a tmux session by the developer himself, delete the TMUX variable + # temporarily. + monkeypatch.delenv("TMUX", raising=False) + + realtemp = tmp_path / "myrealtemp" + realtemp.mkdir() + linktemp = tmp_path / "symlinktemp" + linktemp.symlink_to(realtemp) + workspace_file = linktemp / "simple.yaml" + + workspace_file.write_text( + """ +session_name: samplesimple +start_directory: './' +windows: +- panes: + - echo 'hey'""", + encoding="utf-8", + ) + + # open it detached + session = load_workspace( + str(workspace_file), + socket_name=server.socket_name, + detached=True, + ) + assert session is not None + assert session.active_window is not None + pane = session.active_window.active_pane + + assert isinstance(session, Session) + assert session.name == "samplesimple" + + assert pane is not None + assert pane.pane_current_path == str(realtemp) + + +if t.TYPE_CHECKING: + from pytest_mock import MockerFixture + from typing_extensions import TypeAlias + + ExpectedOutput: TypeAlias = t.Optional[t.Union[str, list[str]]] + + +class CLILoadFixture(t.NamedTuple): + """Test fixture for tmuxp load tests.""" + + # pytest (internal): Test fixture name + test_id: str + + # test params + cli_args: list[str | list[str]] + config_paths: list[str] + session_names: list[str] + expected_exit_code: int + expected_in_out: ExpectedOutput = None + expected_not_in_out: ExpectedOutput = None + expected_in_err: ExpectedOutput = None + expected_not_in_err: ExpectedOutput = None + + +TEST_LOAD_FIXTURES: list[CLILoadFixture] = [ + CLILoadFixture( + test_id="dir-relative-dot-samedir", + cli_args=["load", "."], + config_paths=["{tmp_path}/.tmuxp.yaml"], + session_names=["my_config"], + expected_exit_code=0, + expected_in_out=None, + expected_not_in_out=None, + ), + CLILoadFixture( + test_id="dir-relative-dot-slash-samedir", + cli_args=["load", "./"], + config_paths=["{tmp_path}/.tmuxp.yaml"], + session_names=["my_config"], + expected_exit_code=0, + expected_in_out=None, + expected_not_in_out=None, + ), + CLILoadFixture( + test_id="dir-relative-file-samedir", + cli_args=["load", "./.tmuxp.yaml"], + config_paths=["{tmp_path}/.tmuxp.yaml"], + session_names=["my_config"], + expected_exit_code=0, + expected_in_out=None, + expected_not_in_out=None, + ), + CLILoadFixture( + test_id="filename-relative-file-samedir", + cli_args=["load", "./my_config.yaml"], + config_paths=["{tmp_path}/my_config.yaml"], + session_names=["my_config"], + expected_exit_code=0, + expected_in_out=None, + expected_not_in_out=None, + ), + CLILoadFixture( + test_id="configdir-session-name", + cli_args=["load", "my_config"], + config_paths=["{TMUXP_CONFIGDIR}/my_config.yaml"], + session_names=["my_config"], + expected_exit_code=0, + expected_in_out=None, + expected_not_in_out=None, + ), + CLILoadFixture( + test_id="configdir-absolute", + cli_args=["load", "~/.config/tmuxp/my_config.yaml"], + config_paths=["{TMUXP_CONFIGDIR}/my_config.yaml"], + session_names=["my_config"], + expected_exit_code=0, + expected_in_out=None, + expected_not_in_out=None, + ), + # + # Multiple configs + # + CLILoadFixture( + test_id="configdir-session-name-double", + cli_args=["load", "my_config", "second_config"], + config_paths=[ + "{TMUXP_CONFIGDIR}/my_config.yaml", + "{TMUXP_CONFIGDIR}/second_config.yaml", + ], + session_names=["my_config", "second_config"], + expected_exit_code=0, + expected_in_out=None, + expected_not_in_out=None, + ), +] + + +@pytest.mark.parametrize( + list(CLILoadFixture._fields), + TEST_LOAD_FIXTURES, + ids=[test.test_id for test in TEST_LOAD_FIXTURES], +) +@pytest.mark.usefixtures("tmuxp_configdir_default") +def test_load( + tmp_path: pathlib.Path, + tmuxp_configdir: pathlib.Path, + server: Server, + session: Session, + capsys: pytest.CaptureFixture[str], + monkeypatch: pytest.MonkeyPatch, + test_id: str, + cli_args: list[str], + config_paths: list[str], + session_names: list[str], + expected_exit_code: int, + expected_in_out: ExpectedOutput, + expected_not_in_out: ExpectedOutput, + expected_in_err: ExpectedOutput, + expected_not_in_err: ExpectedOutput, +) -> None: + """Parametrized test battery for tmuxp load CLI command.""" + assert server.socket_name is not None + + monkeypatch.chdir(tmp_path) + for session_name, config_path in zip(session_names, config_paths): + tmuxp_config = pathlib.Path( + config_path.format(tmp_path=tmp_path, TMUXP_CONFIGDIR=tmuxp_configdir), + ) + tmuxp_config.write_text( + f""" + session_name: {session_name} + windows: + - window_name: test + panes: + - + """, + encoding="utf-8", + ) + + with contextlib.suppress(SystemExit): + cli.cli([*cli_args, "-d", "-L", server.socket_name, "-y"]) + + result = capsys.readouterr() + output = "".join(list(result.out)) + + if expected_in_out is not None: + if isinstance(expected_in_out, str): + expected_in_out = [expected_in_out] + for needle in expected_in_out: + assert needle in output + + if expected_not_in_out is not None: + if isinstance(expected_not_in_out, str): + expected_not_in_out = [expected_not_in_out] + for needle in expected_not_in_out: + assert needle not in output + + for session_name in session_names: + assert server.has_session(session_name) + + +def test_regression_00132_session_name_with_dots( + tmp_path: pathlib.Path, + server: Server, + session: Session, + capsys: pytest.CaptureFixture[str], +) -> None: + """Regression test for session names with dots.""" + yaml_config = FIXTURE_PATH / "workspace/builder" / "regression_00132_dots.yaml" + cli_args = [str(yaml_config)] + with pytest.raises(libtmux.exc.BadSessionName): + cli.cli(["load", *cli_args]) + + +class ZshAutotitleTestFixture(t.NamedTuple): + """Test fixture for zsh auto title warning tests.""" + + test_id: str + cli_args: list[str] + + +ZSH_AUTOTITLE_TEST_FIXTURES: list[ZshAutotitleTestFixture] = [ + ZshAutotitleTestFixture( + test_id="load_dot_detached", + cli_args=["load", ".", "-d"], + ), + ZshAutotitleTestFixture( + test_id="load_yaml_detached", + cli_args=["load", ".tmuxp.yaml", "-d"], + ), +] + + +@pytest.mark.parametrize( + list(ZshAutotitleTestFixture._fields), + ZSH_AUTOTITLE_TEST_FIXTURES, + ids=[test.test_id for test in ZSH_AUTOTITLE_TEST_FIXTURES], +) +def test_load_zsh_autotitle_warning( + test_id: str, + cli_args: list[str], + tmp_path: pathlib.Path, + monkeypatch: pytest.MonkeyPatch, + capsys: pytest.CaptureFixture[str], + server: Server, +) -> None: + """Test warning when ZSH auto title is enabled.""" + # create dummy tmuxp yaml so we don't get yelled at + yaml_config = tmp_path / ".tmuxp.yaml" + yaml_config.write_text( + """ + session_name: test + windows: + - window_name: test + panes: + - + """, + encoding="utf-8", + ) + oh_my_zsh_path = tmp_path / ".oh-my-zsh" + oh_my_zsh_path.mkdir() + monkeypatch.setenv("HOME", str(tmp_path)) + + monkeypatch.chdir(tmp_path) + + monkeypatch.delenv("DISABLE_AUTO_TITLE", raising=False) + monkeypatch.setenv("SHELL", "zsh") + + # Use tmux server (socket name) used in the test + assert server.socket_name is not None + cli_args = [*cli_args, "-L", server.socket_name] + + cli.cli(cli_args) + result = capsys.readouterr() + assert "Please set" in result.out + + monkeypatch.setenv("DISABLE_AUTO_TITLE", "false") + cli.cli(cli_args) + result = capsys.readouterr() + assert "Please set" in result.out + + monkeypatch.setenv("DISABLE_AUTO_TITLE", "true") + cli.cli(cli_args) + result = capsys.readouterr() + assert "Please set" not in result.out + + monkeypatch.delenv("DISABLE_AUTO_TITLE", raising=False) + monkeypatch.setenv("SHELL", "sh") + cli.cli(cli_args) + result = capsys.readouterr() + assert "Please set" not in result.out + + +class LogFileTestFixture(t.NamedTuple): + """Test fixture for log file tests.""" + + test_id: str + cli_args: list[str] + + +LOG_FILE_TEST_FIXTURES: list[LogFileTestFixture] = [ + LogFileTestFixture( + test_id="load_with_log_file", + cli_args=["load", ".", "--log-file", "log.txt", "-d"], + ), +] + + +@pytest.mark.parametrize( + list(LogFileTestFixture._fields), + LOG_FILE_TEST_FIXTURES, + ids=[test.test_id for test in LOG_FILE_TEST_FIXTURES], +) +def test_load_log_file( + test_id: str, + cli_args: list[str], + tmp_path: pathlib.Path, + monkeypatch: pytest.MonkeyPatch, + capsys: pytest.CaptureFixture[str], +) -> None: + """Test loading with a log file.""" + # create dummy tmuxp yaml that breaks to prevent actually loading tmux + tmuxp_config_path = tmp_path / ".tmuxp.yaml" + tmuxp_config_path.write_text( + """ +session_name: hello + - + """, + encoding="utf-8", + ) + oh_my_zsh_path = tmp_path / ".oh-my-zsh" + oh_my_zsh_path.mkdir() + monkeypatch.setenv("HOME", str(tmp_path)) + + monkeypatch.chdir(tmp_path) + + with contextlib.suppress(Exception): + cli.cli(cli_args) + + result = capsys.readouterr() + log_file_path = tmp_path / "log.txt" + assert "Loading" in log_file_path.open().read() + assert result.out is not None + + +def test_load_plugins( + monkeypatch_plugin_test_packages: None, +) -> None: + """Test loading via tmuxp load with plugins.""" + from tmuxp_test_plugin_bwb.plugin import ( # type: ignore + PluginBeforeWorkspaceBuilder, + ) + + plugins_config = test_utils.read_workspace_file("workspace/builder/plugin_bwb.yaml") + + session_config = ConfigReader._load(fmt="yaml", content=plugins_config) + session_config = loader.expand(session_config) + + plugins = load_plugins(session_config) + + assert len(plugins) == 1 + + test_plugin_class_types = [ + PluginBeforeWorkspaceBuilder().__class__, + ] + for plugin in plugins: + assert plugin.__class__ in test_plugin_class_types + + +class PluginVersionTestFixture(t.NamedTuple): + """Test fixture for plugin version tests.""" + + test_id: str + cli_args: list[str] + inputs: list[str] + + +PLUGIN_VERSION_SKIP_TEST_FIXTURES: list[PluginVersionTestFixture] = [ + PluginVersionTestFixture( + test_id="skip_version_fail", + cli_args=["load", "tests/fixtures/workspace/builder/plugin_versions_fail.yaml"], + inputs=["y\n"], + ), +] + + +@pytest.mark.skip("Not sure how to clean up the tmux session this makes") +@pytest.mark.parametrize( + list(PluginVersionTestFixture._fields), + PLUGIN_VERSION_SKIP_TEST_FIXTURES, + ids=[test.test_id for test in PLUGIN_VERSION_SKIP_TEST_FIXTURES], +) +def test_load_plugins_version_fail_skip( + monkeypatch_plugin_test_packages: None, + test_id: str, + cli_args: list[str], + inputs: list[str], + capsys: pytest.CaptureFixture[str], +) -> None: + """Test plugin version failure with skip.""" + with contextlib.suppress(SystemExit): + cli.cli(cli_args) + + result = capsys.readouterr() + + assert "[Loading]" in result.out + + +PLUGIN_VERSION_NO_SKIP_TEST_FIXTURES: list[PluginVersionTestFixture] = [ + PluginVersionTestFixture( + test_id="no_skip_version_fail", + cli_args=["load", "tests/fixtures/workspace/builder/plugin_versions_fail.yaml"], + inputs=["n\n"], + ), +] + + +@pytest.mark.parametrize( + list(PluginVersionTestFixture._fields), + PLUGIN_VERSION_NO_SKIP_TEST_FIXTURES, + ids=[test.test_id for test in PLUGIN_VERSION_NO_SKIP_TEST_FIXTURES], +) +def test_load_plugins_version_fail_no_skip( + monkeypatch_plugin_test_packages: None, + test_id: str, + cli_args: list[str], + inputs: list[str], + monkeypatch: pytest.MonkeyPatch, + capsys: pytest.CaptureFixture[str], +) -> None: + """Test plugin version failure without skip.""" + monkeypatch.setattr("sys.stdin", io.StringIO("".join(inputs))) + + with contextlib.suppress(SystemExit): + cli.cli(cli_args) + + result = capsys.readouterr() + + assert "[Not Skipping]" in result.out + + +class PluginMissingTestFixture(t.NamedTuple): + """Test fixture for plugin missing tests.""" + + test_id: str + cli_args: list[str] + + +PLUGIN_MISSING_TEST_FIXTURES: list[PluginMissingTestFixture] = [ + PluginMissingTestFixture( + test_id="missing_plugin", + cli_args=["load", "tests/fixtures/workspace/builder/plugin_missing_fail.yaml"], + ), +] + + +@pytest.mark.parametrize( + list(PluginMissingTestFixture._fields), + PLUGIN_MISSING_TEST_FIXTURES, + ids=[test.test_id for test in PLUGIN_MISSING_TEST_FIXTURES], +) +def test_load_plugins_plugin_missing( + monkeypatch_plugin_test_packages: None, + test_id: str, + cli_args: list[str], + capsys: pytest.CaptureFixture[str], +) -> None: + """Test loading with missing plugin.""" + with contextlib.suppress(SystemExit): + cli.cli(cli_args) + + result = capsys.readouterr() + + assert "[Plugin Error]" in result.out + + +def test_plugin_system_before_script( + monkeypatch_plugin_test_packages: None, + server: Server, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Test tmuxp load with sessions using before_script.""" + # this is an implementation test. Since this testsuite may be ran within + # a tmux session by the developer himself, delete the TMUX variable + # temporarily. + monkeypatch.delenv("TMUX", raising=False) + session_file = FIXTURE_PATH / "workspace/builder" / "plugin_bs.yaml" + + # open it detached + session = load_workspace( + session_file, + socket_name=server.socket_name, + detached=True, + ) + + assert isinstance(session, Session) + assert session.name == "plugin_test_bs" + + +def test_load_attached( + server: Server, + monkeypatch: pytest.MonkeyPatch, + mocker: MockerFixture, +) -> None: + """Test tmuxp load's attachment behavior.""" + # Load a session and attach from outside tmux + monkeypatch.delenv("TMUX", raising=False) + + attach_session_mock = mocker.patch("libtmux.session.Session.attach_session") + attach_session_mock.return_value.stderr = None + + yaml_config = test_utils.read_workspace_file("workspace/builder/two_pane.yaml") + session_config = ConfigReader._load(fmt="yaml", content=yaml_config) + + builder = WorkspaceBuilder(session_config=session_config, server=server) + + _load_attached(builder, False) + + assert attach_session_mock.call_count == 1 + + +def test_load_attached_detached( + server: Server, + monkeypatch: pytest.MonkeyPatch, + mocker: MockerFixture, +) -> None: + """Test tmuxp load when sessions are build without attaching client.""" + # Load a session but don't attach + monkeypatch.delenv("TMUX", raising=False) + + attach_session_mock = mocker.patch("libtmux.session.Session.attach_session") + attach_session_mock.return_value.stderr = None + + yaml_config = test_utils.read_workspace_file("workspace/builder/two_pane.yaml") + session_config = ConfigReader._load(fmt="yaml", content=yaml_config) + + builder = WorkspaceBuilder(session_config=session_config, server=server) + + _load_attached(builder, True) + + assert attach_session_mock.call_count == 0 + + +def test_load_attached_within_tmux( + server: Server, + monkeypatch: pytest.MonkeyPatch, + mocker: MockerFixture, +) -> None: + """Test loading via tmuxp load when already within a tmux session.""" + # Load a session and attach from within tmux + monkeypatch.setenv("TMUX", "/tmp/tmux-1234/default,123,0") + + switch_client_mock = mocker.patch("libtmux.session.Session.switch_client") + switch_client_mock.return_value.stderr = None + + yaml_config = test_utils.read_workspace_file("workspace/builder/two_pane.yaml") + session_config = ConfigReader._load(fmt="yaml", content=yaml_config) + + builder = WorkspaceBuilder(session_config=session_config, server=server) + + _load_attached(builder, False) + + assert switch_client_mock.call_count == 1 + + +def test_load_attached_within_tmux_detached( + server: Server, + monkeypatch: pytest.MonkeyPatch, + mocker: MockerFixture, +) -> None: + """Test loading via tmuxp load within a tmux session switches clients.""" + # Load a session and attach from within tmux + monkeypatch.setenv("TMUX", "/tmp/tmux-1234/default,123,0") + + switch_client_mock = mocker.patch("libtmux.session.Session.switch_client") + switch_client_mock.return_value.stderr = None + + yaml_config = test_utils.read_workspace_file("workspace/builder/two_pane.yaml") + session_config = ConfigReader._load(fmt="yaml", content=yaml_config) + + builder = WorkspaceBuilder(session_config=session_config, server=server) + + _load_attached(builder, True) + + assert switch_client_mock.call_count == 1 + + +def test_load_append_windows_to_current_session( + server: Server, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Test tmuxp load when windows are appended to the current session.""" + yaml_config = test_utils.read_workspace_file("workspace/builder/two_pane.yaml") + session_config = ConfigReader._load(fmt="yaml", content=yaml_config) + + builder = WorkspaceBuilder(session_config=session_config, server=server) + builder.build() + + assert len(server.sessions) == 1 + assert len(server.windows) == 3 + + # Assign an active pane to the session + assert server.panes[0].pane_id + monkeypatch.setenv("TMUX_PANE", server.panes[0].pane_id) + + builder = WorkspaceBuilder(session_config=session_config, server=server) + _load_append_windows_to_current_session(builder) + + assert len(server.sessions) == 1 + assert len(server.windows) == 6 diff --git a/tests/cli/test_ls.py b/tests/cli/test_ls.py new file mode 100644 index 00000000000..1811e636c42 --- /dev/null +++ b/tests/cli/test_ls.py @@ -0,0 +1,51 @@ +"""CLI tests for tmuxp ls command.""" + +from __future__ import annotations + +import contextlib +import pathlib +import typing as t + +from tmuxp import cli + +if t.TYPE_CHECKING: + import pytest + + +def test_ls_cli( + monkeypatch: pytest.MonkeyPatch, + tmp_path: pathlib.Path, + capsys: pytest.CaptureFixture[str], +) -> None: + """CLI test for tmuxp ls.""" + monkeypatch.setenv("HOME", str(tmp_path)) + monkeypatch.setenv("XDG_CONFIG_HOME", str(tmp_path / ".config")) + + filenames = [ + ".git/", + ".gitignore/", + "session_1.yaml", + "session_2.yaml", + "session_3.json", + "session_4.txt", + ] + + # should ignore: + # - directories should be ignored + # - extensions not covered in VALID_WORKSPACE_DIR_FILE_EXTENSIONS + ignored_filenames = [".git/", ".gitignore/", "session_4.txt"] + stems = [pathlib.Path(f).stem for f in filenames if f not in ignored_filenames] + + for filename in filenames: + location = tmp_path / f".tmuxp/{filename}" + if filename.endswith("/"): + location.mkdir(parents=True) + else: + location.touch() + + with contextlib.suppress(SystemExit): + cli.cli(["ls"]) + + cli_output = capsys.readouterr().out + + assert cli_output == "\n".join(stems) + "\n" diff --git a/tests/cli/test_shell.py b/tests/cli/test_shell.py new file mode 100644 index 00000000000..79e130e493e --- /dev/null +++ b/tests/cli/test_shell.py @@ -0,0 +1,470 @@ +"""CLI tests for tmuxp shell.""" + +from __future__ import annotations + +import contextlib +import io +import subprocess +import typing as t + +import pytest + +from tmuxp import cli, exc + +if t.TYPE_CHECKING: + import pathlib + + from libtmux.server import Server + from libtmux.session import Session + + +class CLIShellFixture(t.NamedTuple): + """Test fixture for tmuxp shell tests.""" + + # pytest (internal): Test fixture name + test_id: str + + # test params + cli_cmd: list[str] + cli_args: list[str] + inputs: list[t.Any] + env: dict[str, str] + expected_output: str + + +TEST_SHELL_FIXTURES: list[CLIShellFixture] = [ + # Regular shell command + CLIShellFixture( + test_id="print-socket-name", + cli_cmd=["shell"], + cli_args=["-L{SOCKET_NAME}", "-c", "print(str(server.socket_name))"], + inputs=[], + env={}, + expected_output="{SERVER_SOCKET_NAME}", + ), + CLIShellFixture( + test_id="print-session-name", + cli_cmd=["shell"], + cli_args=[ + "-L{SOCKET_NAME}", + "{SESSION_NAME}", + "-c", + "print(session.name)", + ], + inputs=[], + env={}, + expected_output="{SESSION_NAME}", + ), + CLIShellFixture( + test_id="print-has-session", + cli_cmd=["shell"], + cli_args=[ + "-L{SOCKET_NAME}", + "{SESSION_NAME}", + "{WINDOW_NAME}", + "-c", + "print(server.has_session(session.name))", + ], + inputs=[], + env={}, + expected_output="True", + ), + CLIShellFixture( + test_id="print-window-name", + cli_cmd=["shell"], + cli_args=[ + "-L{SOCKET_NAME}", + "{SESSION_NAME}", + "{WINDOW_NAME}", + "-c", + "print(window.name)", + ], + inputs=[], + env={}, + expected_output="{WINDOW_NAME}", + ), + CLIShellFixture( + test_id="print-pane-id", + cli_cmd=["shell"], + cli_args=[ + "-L{SOCKET_NAME}", + "{SESSION_NAME}", + "{WINDOW_NAME}", + "-c", + "print(pane.id)", + ], + inputs=[], + env={}, + expected_output="{PANE_ID}", + ), + CLIShellFixture( + test_id="print-pane-id-obeys-tmux-pane-env-var", + cli_cmd=["shell"], + cli_args=[ + "-L{SOCKET_NAME}", + "-c", + "print(pane.id)", + ], + inputs=[], + env={"TMUX_PANE": "{PANE_ID}"}, + expected_output="{PANE_ID}", + ), + # Shell with --pdb + CLIShellFixture( + test_id="print-socket-name-pdb", + cli_cmd=["shell", "--pdb"], + cli_args=["-L{SOCKET_NAME}", "-c", "print(str(server.socket_name))"], + inputs=[], + env={}, + expected_output="{SERVER_SOCKET_NAME}", + ), + CLIShellFixture( + test_id="print-session-name-pdb", + cli_cmd=["shell", "--pdb"], + cli_args=[ + "-L{SOCKET_NAME}", + "{SESSION_NAME}", + "-c", + "print(session.name)", + ], + inputs=[], + env={}, + expected_output="{SESSION_NAME}", + ), + CLIShellFixture( + test_id="print-has-session-pdb", + cli_cmd=["shell", "--pdb"], + cli_args=[ + "-L{SOCKET_NAME}", + "{SESSION_NAME}", + "{WINDOW_NAME}", + "-c", + "print(server.has_session(session.name))", + ], + inputs=[], + env={}, + expected_output="True", + ), + CLIShellFixture( + test_id="print-window-name-pdb", + cli_cmd=["shell", "--pdb"], + cli_args=[ + "-L{SOCKET_NAME}", + "{SESSION_NAME}", + "{WINDOW_NAME}", + "-c", + "print(window.name)", + ], + inputs=[], + env={}, + expected_output="{WINDOW_NAME}", + ), + CLIShellFixture( + test_id="print-pane-id-pdb", + cli_cmd=["shell", "--pdb"], + cli_args=[ + "-L{SOCKET_NAME}", + "{SESSION_NAME}", + "{WINDOW_NAME}", + "-c", + "print(pane.id)", + ], + inputs=[], + env={}, + expected_output="{PANE_ID}", + ), + CLIShellFixture( + test_id="print-pane-id-obeys-tmux-pane-env-var-pdb", + cli_cmd=["shell", "--pdb"], + cli_args=[ + "-L{SOCKET_NAME}", + "-c", + "print(pane.id)", + ], + inputs=[], + env={"TMUX_PANE": "{PANE_ID}"}, + expected_output="{PANE_ID}", + ), +] + + +@pytest.mark.parametrize( + list(CLIShellFixture._fields), + TEST_SHELL_FIXTURES, + ids=[test.test_id for test in TEST_SHELL_FIXTURES], +) +def test_shell( + test_id: str, + cli_cmd: list[str], + cli_args: list[str], + inputs: list[t.Any], + env: dict[str, str], + expected_output: str, + server: Server, + session: Session, + tmp_path: pathlib.Path, + monkeypatch: pytest.MonkeyPatch, + capsys: pytest.CaptureFixture[str], +) -> None: + """CLI tests for tmuxp shell.""" + monkeypatch.setenv("HOME", str(tmp_path)) + window_name = "my_window" + window = session.new_window(window_name=window_name) + window.split() + + assert window.active_pane is not None + + template_ctx = { + "SOCKET_NAME": server.socket_name, + "SESSION_NAME": session.name, + "WINDOW_NAME": window_name, + "PANE_ID": window.active_pane.id, + "SERVER_SOCKET_NAME": server.socket_name, + } + + cli_args = cli_cmd + [cli_arg.format(**template_ctx) for cli_arg in cli_args] + + for k, v in env.items(): + monkeypatch.setenv(k, v.format(**template_ctx)) + + monkeypatch.chdir(tmp_path) + + cli.cli(cli_args) + result = capsys.readouterr() + assert expected_output.format(**template_ctx) in result.out + + +class CLIShellTargetMissingFixture(t.NamedTuple): + """Test fixture for tmuxp shell target missing tests.""" + + test_id: str + cli_cmd: list[str] + cli_args: list[str] + inputs: list[t.Any] + env: dict[t.Any, t.Any] + template_ctx: dict[str, str] + exception: type[exc.TmuxpException | subprocess.CalledProcessError] + message: str + + +TEST_SHELL_TARGET_MISSING_FIXTURES: list[CLIShellTargetMissingFixture] = [ + # Regular shell command + CLIShellTargetMissingFixture( + test_id="nonexistent_socket", + cli_cmd=["shell"], + cli_args=["-LDoesNotExist", "-c", "print(str(server.socket_name))"], + inputs=[], + env={}, + template_ctx={}, + exception=subprocess.CalledProcessError, + message=r".*DoesNotExist.*", + ), + CLIShellTargetMissingFixture( + test_id="nonexistent_session", + cli_cmd=["shell"], + cli_args=[ + "-L{SOCKET_NAME}", + "nonexistent_session", + "-c", + "print(str(server.socket_name))", + ], + inputs=[], + env={}, + template_ctx={"session_name": "nonexistent_session"}, + exception=exc.TmuxpException, + message="Session not found: nonexistent_session", + ), + CLIShellTargetMissingFixture( + test_id="nonexistent_window", + cli_cmd=["shell"], + cli_args=[ + "-L{SOCKET_NAME}", + "{SESSION_NAME}", + "nonexistent_window", + "-c", + "print(str(server.socket_name))", + ], + inputs=[], + env={}, + template_ctx={"window_name": "nonexistent_window"}, + exception=exc.TmuxpException, + message="Window not found: {WINDOW_NAME}", + ), + # Shell with --pdb + CLIShellTargetMissingFixture( + test_id="nonexistent_socket_pdb", + cli_cmd=["shell", "--pdb"], + cli_args=["-LDoesNotExist", "-c", "print(str(server.socket_name))"], + inputs=[], + env={}, + template_ctx={}, + exception=subprocess.CalledProcessError, + message=r".*DoesNotExist.*", + ), + CLIShellTargetMissingFixture( + test_id="nonexistent_session_pdb", + cli_cmd=["shell", "--pdb"], + cli_args=[ + "-L{SOCKET_NAME}", + "nonexistent_session", + "-c", + "print(str(server.socket_name))", + ], + inputs=[], + env={}, + template_ctx={"session_name": "nonexistent_session"}, + exception=exc.TmuxpException, + message="Session not found: nonexistent_session", + ), + CLIShellTargetMissingFixture( + test_id="nonexistent_window_pdb", + cli_cmd=["shell", "--pdb"], + cli_args=[ + "-L{SOCKET_NAME}", + "{SESSION_NAME}", + "nonexistent_window", + "-c", + "print(str(server.socket_name))", + ], + inputs=[], + env={}, + template_ctx={"window_name": "nonexistent_window"}, + exception=exc.TmuxpException, + message="Window not found: {WINDOW_NAME}", + ), +] + + +@pytest.mark.parametrize( + list(CLIShellTargetMissingFixture._fields), + TEST_SHELL_TARGET_MISSING_FIXTURES, + ids=[test.test_id for test in TEST_SHELL_TARGET_MISSING_FIXTURES], +) +def test_shell_target_missing( + test_id: str, + cli_cmd: list[str], + cli_args: list[str], + inputs: list[t.Any], + env: dict[t.Any, t.Any], + template_ctx: dict[str, str], + exception: type[exc.TmuxpException | subprocess.CalledProcessError], + message: str, + server: Server, + session: Session, + tmp_path: pathlib.Path, + monkeypatch: pytest.MonkeyPatch, + capsys: pytest.CaptureFixture[str], +) -> None: + """CLI tests for tmuxp shell when target is not specified.""" + monkeypatch.setenv("HOME", str(tmp_path)) + window_name = "my_window" + window = session.new_window(window_name=window_name) + window.split() + + assert server.socket_name is not None + assert session.name is not None + + template_ctx.update( + { + "SOCKET_NAME": server.socket_name, + "SESSION_NAME": session.name, + "WINDOW_NAME": template_ctx.get("window_name", window_name), + }, + ) + cli_args = cli_cmd + [cli_arg.format(**template_ctx) for cli_arg in cli_args] + + for k, v in env.items(): + monkeypatch.setenv(k, v.format(**template_ctx)) + + monkeypatch.chdir(tmp_path) + + if exception is not None: + with pytest.raises(exception, match=message.format(**template_ctx)): + cli.cli(cli_args) + else: + cli.cli(cli_args) + result = capsys.readouterr() + assert message.format(**template_ctx) in result.out + + +class CLIShellInteractiveFixture(t.NamedTuple): + """Test fixture for tmuxp shell interactive tests.""" + + test_id: str + cli_cmd: list[str] + cli_args: list[str] + inputs: list[t.Any] + env: dict[str, str] + message: str + + +TEST_SHELL_INTERACTIVE_FIXTURES: list[CLIShellInteractiveFixture] = [ + CLIShellInteractiveFixture( + test_id="basic_interactive", + cli_cmd=["shell", "--code"], + cli_args=[ + "-L{SOCKET_NAME}", + ], + inputs=[], + env={}, + message="(InteractiveConsole)", + ), + CLIShellInteractiveFixture( + test_id="interactive_with_pane_id", + cli_cmd=["shell", "--code"], + cli_args=[ + "-L{SOCKET_NAME}", + ], + inputs=[], + env={"PANE_ID": "{PANE_ID}"}, + message="(InteractiveConsole)", + ), +] + + +@pytest.mark.parametrize( + list(CLIShellInteractiveFixture._fields), + TEST_SHELL_INTERACTIVE_FIXTURES, + ids=[test.test_id for test in TEST_SHELL_INTERACTIVE_FIXTURES], +) +def test_shell_interactive( + test_id: str, + cli_cmd: list[str], + cli_args: list[str], + inputs: list[t.Any], + env: dict[str, str], + message: str, + server: Server, + session: Session, + tmp_path: pathlib.Path, + monkeypatch: pytest.MonkeyPatch, + capsys: pytest.CaptureFixture[str], +) -> None: + """CLI tests for tmuxp shell when shell is specified.""" + monkeypatch.setenv("HOME", str(tmp_path)) + window_name = "my_window" + window = session.new_window(window_name=window_name) + window.split() + + assert window.active_pane is not None + + template_ctx = { + "SOCKET_NAME": server.socket_name, + "SESSION_NAME": session.name, + "WINDOW_NAME": window_name, + "PANE_ID": window.active_pane.id, + "SERVER_SOCKET_NAME": server.socket_name, + } + + cli_args = cli_cmd + [cli_arg.format(**template_ctx) for cli_arg in cli_args] + + for k, v in env.items(): + monkeypatch.setenv(k, v.format(**template_ctx)) + + monkeypatch.chdir(tmp_path) + monkeypatch.setattr("sys.stdin", io.StringIO("exit()\r")) + with contextlib.suppress(SystemExit): + cli.cli(cli_args) + + result = capsys.readouterr() + assert message.format(**template_ctx) in result.err diff --git a/tests/constants.py b/tests/constants.py new file mode 100644 index 00000000000..ee67d4c7708 --- /dev/null +++ b/tests/constants.py @@ -0,0 +1,9 @@ +"""Constant variables for tmuxp tests.""" + +from __future__ import annotations + +import pathlib + +TESTS_PATH = pathlib.Path(__file__).parent +EXAMPLE_PATH = TESTS_PATH.parent / "examples" +FIXTURE_PATH = TESTS_PATH / "fixtures" diff --git a/tests/fixtures/__init__.py b/tests/fixtures/__init__.py new file mode 100644 index 00000000000..2f0841acd81 --- /dev/null +++ b/tests/fixtures/__init__.py @@ -0,0 +1,5 @@ +"""Fixture test data for tmuxp.""" + +from __future__ import annotations + +from . import utils diff --git a/tests/fixtures/import_teamocil/__init__.py b/tests/fixtures/import_teamocil/__init__.py new file mode 100644 index 00000000000..1ec7c59fd55 --- /dev/null +++ b/tests/fixtures/import_teamocil/__init__.py @@ -0,0 +1,5 @@ +"""Teamocil data fixtures for import_teamocil tests.""" + +from __future__ import annotations + +from . import layouts, test1, test2, test3, test4 diff --git a/tests/fixtures/import_teamocil/layouts.py b/tests/fixtures/import_teamocil/layouts.py new file mode 100644 index 00000000000..6e3e06bf7f9 --- /dev/null +++ b/tests/fixtures/import_teamocil/layouts.py @@ -0,0 +1,171 @@ +"""Teamocil data fixtures for import_teamocil tests, for layout testing.""" + +from __future__ import annotations + +from tests.fixtures import utils as test_utils + +teamocil_yaml_file = test_utils.get_workspace_file("import_teamocil/layouts.yaml") +teamocil_yaml = test_utils.read_workspace_file("import_teamocil/layouts.yaml") + +teamocil_dict = { + "two-windows": { + "windows": [ + { + "name": "foo", + "clear": True, + "root": "/foo", + "layout": "tiled", + "panes": [{"cmd": "echo 'foo'"}, {"cmd": "echo 'foo again'"}], + }, + { + "name": "bar", + "root": "/bar", + "splits": [ + { + "cmd": ["echo 'bar'", "echo 'bar in an array'"], + "target": "bottom-right", + }, + {"cmd": "echo 'bar again'", "focus": True, "width": 50}, + ], + }, + ], + }, + "two-windows-with-filters": { + "windows": [ + { + "name": "foo", + "root": "/foo", + "filters": { + "before": ["echo first before filter", "echo second before filter"], + "after": ["echo first after filter", "echo second after filter"], + }, + "panes": [ + {"cmd": "echo 'foo'"}, + {"cmd": "echo 'foo again'", "width": 50}, + ], + }, + ], + }, + "two-windows-with-custom-command-options": { + "windows": [ + { + "name": "foo", + "cmd_separator": "\n", + "with_env_var": False, + "clear": True, + "root": "/foo", + "layout": "tiled", + "panes": [{"cmd": "echo 'foo'"}, {"cmd": "echo 'foo again'"}], + }, + { + "name": "bar", + "cmd_separator": " && ", + "with_env_var": True, + "root": "/bar", + "splits": [ + { + "cmd": ["echo 'bar'", "echo 'bar in an array'"], + "target": "bottom-right", + }, + {"cmd": "echo 'bar again'", "focus": True, "width": 50}, + ], + }, + ], + }, + "three-windows-within-a-session": { + "session": { + "name": "my awesome session", + "windows": [ + {"name": "first window", "panes": [{"cmd": "echo 'foo'"}]}, + {"name": "second window", "panes": [{"cmd": "echo 'foo'"}]}, + {"name": "third window", "panes": [{"cmd": "echo 'foo'"}]}, + ], + }, + }, +} + + +two_windows = { + "session_name": None, + "windows": [ + { + "window_name": "foo", + "start_directory": "/foo", + "clear": True, + "layout": "tiled", + "panes": [ + {"shell_command": "echo 'foo'"}, + {"shell_command": "echo 'foo again'"}, + ], + }, + { + "window_name": "bar", + "start_directory": "/bar", + "panes": [ + { + "shell_command": ["echo 'bar'", "echo 'bar in an array'"], + "target": "bottom-right", + }, + {"shell_command": "echo 'bar again'", "focus": True}, + ], + }, + ], +} + +two_windows_with_filters = { + "session_name": None, + "windows": [ + { + "window_name": "foo", + "start_directory": "/foo", + "shell_command_before": [ + "echo first before filter", + "echo second before filter", + ], + "shell_command_after": [ + "echo first after filter", + "echo second after filter", + ], + "panes": [ + {"shell_command": "echo 'foo'"}, + {"shell_command": "echo 'foo again'"}, + ], + }, + ], +} + +two_windows_with_custom_command_options = { + "session_name": None, + "windows": [ + { + "window_name": "foo", + "start_directory": "/foo", + "clear": True, + "layout": "tiled", + "panes": [ + {"shell_command": "echo 'foo'"}, + {"shell_command": "echo 'foo again'"}, + ], + }, + { + "window_name": "bar", + "start_directory": "/bar", + "panes": [ + { + "shell_command": ["echo 'bar'", "echo 'bar in an array'"], + "target": "bottom-right", + }, + {"shell_command": "echo 'bar again'", "focus": True}, + ], + }, + ], +} + +three_windows_within_a_session = { + "session_name": "my awesome session", + "windows": [ + {"window_name": "first window", "panes": [{"shell_command": "echo 'foo'"}]}, + {"window_name": "second window", "panes": [{"shell_command": "echo 'foo'"}]}, + {"window_name": "third window", "panes": [{"shell_command": "echo 'foo'"}]}, + ], +} diff --git a/tests/fixtures/import_teamocil/layouts.yaml b/tests/fixtures/import_teamocil/layouts.yaml new file mode 100644 index 00000000000..46b39054604 --- /dev/null +++ b/tests/fixtures/import_teamocil/layouts.yaml @@ -0,0 +1,75 @@ +# Simple two windows layout +two-windows: + windows: + - name: "foo" + clear: true + root: "/foo" + layout: "tiled" + panes: + - cmd: "echo 'foo'" + - cmd: "echo 'foo again'" + - name: "bar" + root: "/bar" + splits: + - cmd: + - "echo 'bar'" + - "echo 'bar in an array'" + target: bottom-right + - cmd: "echo 'bar again'" + focus: true + width: 50 + +# Simple two windows layout with filters +two-windows-with-filters: + windows: + - name: "foo" + root: "/foo" + filters: + before: + - "echo first before filter" + - "echo second before filter" + after: + - "echo first after filter" + - "echo second after filter" + panes: + - cmd: "echo 'foo'" + - cmd: "echo 'foo again'" + width: 50 + +two-windows-with-custom-command-options: + windows: + - name: "foo" + cmd_separator: "\n" + with_env_var: false + clear: true + root: "/foo" + layout: "tiled" + panes: + - cmd: "echo 'foo'" + - cmd: "echo 'foo again'" + - name: "bar" + cmd_separator: " && " + with_env_var: true + root: "/bar" + splits: + - cmd: + - "echo 'bar'" + - "echo 'bar in an array'" + target: bottom-right + - cmd: "echo 'bar again'" + focus: true + width: 50 + +three-windows-within-a-session: + session: + name: "my awesome session" + windows: + - name: "first window" + panes: + - cmd: "echo 'foo'" + - name: "second window" + panes: + - cmd: "echo 'foo'" + - name: "third window" + panes: + - cmd: "echo 'foo'" diff --git a/tests/fixtures/import_teamocil/test1.py b/tests/fixtures/import_teamocil/test1.py new file mode 100644 index 00000000000..8e2065fec21 --- /dev/null +++ b/tests/fixtures/import_teamocil/test1.py @@ -0,0 +1,32 @@ +"""Teamocil data fixtures for import_teamocil tests, 1st test.""" + +from __future__ import annotations + +from tests.fixtures import utils as test_utils + +teamocil_yaml = test_utils.read_workspace_file("import_teamocil/test1.yaml") +teamocil_conf = { + "windows": [ + { + "name": "sample-two-panes", + "root": "~/Code/sample/www", + "layout": "even-horizontal", + "panes": [{"cmd": ["pwd", "ls -la"]}, {"cmd": "rails server --port 3000"}], + }, + ], +} + +expected = { + "session_name": None, + "windows": [ + { + "window_name": "sample-two-panes", + "layout": "even-horizontal", + "start_directory": "~/Code/sample/www", + "panes": [ + {"shell_command": ["pwd", "ls -la"]}, + {"shell_command": "rails server --port 3000"}, + ], + }, + ], +} diff --git a/tests/fixtures/import_teamocil/test1.yaml b/tests/fixtures/import_teamocil/test1.yaml new file mode 100644 index 00000000000..33ffc491cc5 --- /dev/null +++ b/tests/fixtures/import_teamocil/test1.yaml @@ -0,0 +1,7 @@ +windows: +- name: "sample-two-panes" + root: "~/Code/sample/www" + layout: even-horizontal + panes: + - cmd: ["pwd", "ls -la"] + - cmd: "rails server --port 3000" diff --git a/tests/fixtures/import_teamocil/test2.py b/tests/fixtures/import_teamocil/test2.py new file mode 100644 index 00000000000..0353a0edbf5 --- /dev/null +++ b/tests/fixtures/import_teamocil/test2.py @@ -0,0 +1,34 @@ +"""Teamocil data fixtures for import_teamocil tests, 2nd test.""" + +from __future__ import annotations + +from tests.fixtures import utils as test_utils + +teamocil_yaml = test_utils.read_workspace_file("import_teamocil/test2.yaml") +teamocil_dict = { + "windows": [ + { + "name": "sample-four-panes", + "root": "~/Code/sample/www", + "layout": "tiled", + "panes": [{"cmd": "pwd"}, {"cmd": "pwd"}, {"cmd": "pwd"}, {"cmd": "pwd"}], + }, + ], +} + +expected = { + "session_name": None, + "windows": [ + { + "window_name": "sample-four-panes", + "layout": "tiled", + "start_directory": "~/Code/sample/www", + "panes": [ + {"shell_command": "pwd"}, + {"shell_command": "pwd"}, + {"shell_command": "pwd"}, + {"shell_command": "pwd"}, + ], + }, + ], +} diff --git a/tests/fixtures/import_teamocil/test2.yaml b/tests/fixtures/import_teamocil/test2.yaml new file mode 100644 index 00000000000..9c69c560ca9 --- /dev/null +++ b/tests/fixtures/import_teamocil/test2.yaml @@ -0,0 +1,9 @@ +windows: +- name: "sample-four-panes" + root: "~/Code/sample/www" + layout: tiled + panes: + - cmd: "pwd" + - cmd: "pwd" + - cmd: "pwd" + - cmd: "pwd" diff --git a/tests/fixtures/import_teamocil/test3.py b/tests/fixtures/import_teamocil/test3.py new file mode 100644 index 00000000000..1bcd32fef13 --- /dev/null +++ b/tests/fixtures/import_teamocil/test3.py @@ -0,0 +1,49 @@ +"""Teamocil data fixtures for import_teamocil tests, 3rd test.""" + +from __future__ import annotations + +from tests.fixtures import utils as test_utils + +teamocil_yaml = test_utils.read_workspace_file("import_teamocil/test3.yaml") + +teamocil_dict = { + "windows": [ + { + "name": "my-first-window", + "root": "~/Projects/foo-www", + "layout": "even-vertical", + "filters": { + "before": "rbenv local 2.0.0-p0", + "after": "echo 'I am done initializing this pane.'", + }, + "panes": [ + {"cmd": "git status"}, + {"cmd": "bundle exec rails server --port 40", "focus": True}, + {"cmd": ["sudo service memcached start", "sudo service mongodb start"]}, + ], + }, + ], +} + +expected = { + "session_name": None, + "windows": [ + { + "window_name": "my-first-window", + "layout": "even-vertical", + "start_directory": "~/Projects/foo-www", + "shell_command_before": "rbenv local 2.0.0-p0", + "shell_command_after": ("echo 'I am done initializing this pane.'"), + "panes": [ + {"shell_command": "git status"}, + {"shell_command": "bundle exec rails server --port 40", "focus": True}, + { + "shell_command": [ + "sudo service memcached start", + "sudo service mongodb start", + ], + }, + ], + }, + ], +} diff --git a/tests/fixtures/import_teamocil/test3.yaml b/tests/fixtures/import_teamocil/test3.yaml new file mode 100644 index 00000000000..f3d2b144f57 --- /dev/null +++ b/tests/fixtures/import_teamocil/test3.yaml @@ -0,0 +1,14 @@ +windows: +- name: "my-first-window" + root: "~/Projects/foo-www" + layout: even-vertical + filters: + before: "rbenv local 2.0.0-p0" + after: "echo 'I am done initializing this pane.'" + panes: + - cmd: "git status" + - cmd: "bundle exec rails server --port 40" + focus: true + - cmd: + - "sudo service memcached start" + - "sudo service mongodb start" diff --git a/tests/fixtures/import_teamocil/test4.py b/tests/fixtures/import_teamocil/test4.py new file mode 100644 index 00000000000..1837abf5086 --- /dev/null +++ b/tests/fixtures/import_teamocil/test4.py @@ -0,0 +1,28 @@ +"""Teamocil data fixtures for import_teamocil tests, 4th test.""" + +from __future__ import annotations + +from tests.fixtures import utils as test_utils + +teamocil_yaml = test_utils.read_workspace_file("import_teamocil/test4.yaml") + +teamocil_dict = { + "windows": [ + { + "name": "erb-example", + "root": "<%= ENV['MY_PROJECT_ROOT'] %>", + "panes": [{"cmd": "pwd"}], + }, + ], +} + +expected = { + "session_name": None, + "windows": [ + { + "window_name": "erb-example", + "start_directory": "<%= ENV['MY_PROJECT_ROOT'] %>", + "panes": [{"shell_command": "pwd"}], + }, + ], +} diff --git a/tests/fixtures/import_teamocil/test4.yaml b/tests/fixtures/import_teamocil/test4.yaml new file mode 100644 index 00000000000..a2f679e8aa1 --- /dev/null +++ b/tests/fixtures/import_teamocil/test4.yaml @@ -0,0 +1,5 @@ +windows: +- name: "erb-example" + root: <%= ENV['MY_PROJECT_ROOT'] %> + panes: + - cmd: "pwd" diff --git a/tests/fixtures/import_tmuxinator/__init__.py b/tests/fixtures/import_tmuxinator/__init__.py new file mode 100644 index 00000000000..84508e04051 --- /dev/null +++ b/tests/fixtures/import_tmuxinator/__init__.py @@ -0,0 +1,5 @@ +"""Tmuxinator data fixtures for import_tmuxinator tests.""" + +from __future__ import annotations + +from . import test1, test2, test3 diff --git a/tests/fixtures/import_tmuxinator/test1.py b/tests/fixtures/import_tmuxinator/test1.py new file mode 100644 index 00000000000..7e08f976d0e --- /dev/null +++ b/tests/fixtures/import_tmuxinator/test1.py @@ -0,0 +1,23 @@ +"""Tmuxinator data fixtures for import_tmuxinator tests, 1st dataset.""" + +from __future__ import annotations + +from tests.fixtures import utils as test_utils + +tmuxinator_yaml = test_utils.read_workspace_file("import_tmuxinator/test1.yaml") +tmuxinator_dict = { + "windows": [ + {"editor": {"layout": "main-vertical", "panes": ["vim", "guard"]}}, + {"server": "bundle exec rails s"}, + {"logs": "tail -f logs/development.log"}, + ], +} + +expected = { + "session_name": None, + "windows": [ + {"window_name": "editor", "layout": "main-vertical", "panes": ["vim", "guard"]}, + {"window_name": "server", "panes": ["bundle exec rails s"]}, + {"window_name": "logs", "panes": ["tail -f logs/development.log"]}, + ], +} diff --git a/tests/fixtures/import_tmuxinator/test1.yaml b/tests/fixtures/import_tmuxinator/test1.yaml new file mode 100644 index 00000000000..8905c608b36 --- /dev/null +++ b/tests/fixtures/import_tmuxinator/test1.yaml @@ -0,0 +1,8 @@ +windows: +- editor: + layout: main-vertical + panes: + - vim + - guard +- server: bundle exec rails s +- logs: tail -f logs/development.log diff --git a/tests/fixtures/import_tmuxinator/test2.py b/tests/fixtures/import_tmuxinator/test2.py new file mode 100644 index 00000000000..97d923a912d --- /dev/null +++ b/tests/fixtures/import_tmuxinator/test2.py @@ -0,0 +1,80 @@ +"""Tmuxinator data fixtures for import_tmuxinator tests, 2nd dataset.""" + +from __future__ import annotations + +from tests.fixtures import utils as test_utils + +tmuxinator_yaml = test_utils.read_workspace_file("import_tmuxinator/test2.yaml") + +tmuxinator_dict = { + "project_name": "sample", + "project_root": "~/test", + "socket_name": "foo", + "pre": "sudo /etc/rc.d/mysqld start", + "rbenv": "2.0.0-p247", + "cli_args": "-f ~/.tmux.mac.conf", + "tabs": [ + { + "editor": { + "pre": [ + 'echo "I get run in each pane, before each pane command!"', + None, + ], + "layout": "main-vertical", + "panes": ["vim", None, "top"], + }, + }, + {"shell": "git pull"}, + { + "guard": { + "layout": "tiled", + "pre": [ + 'echo "I get run in each pane."', + 'echo "Before each pane command!"', + ], + "panes": [None, None, None], + }, + }, + {"database": "bundle exec rails db"}, + {"server": "bundle exec rails s"}, + {"logs": "tail -f log/development.log"}, + {"console": "bundle exec rails c"}, + {"capistrano": None}, + {"server": "ssh user@example.com"}, + ], +} + +expected = { + "session_name": "sample", + "socket_name": "foo", + "config": "~/.tmux.mac.conf", + "start_directory": "~/test", + "shell_command_before": ["sudo /etc/rc.d/mysqld start", "rbenv shell 2.0.0-p247"], + "windows": [ + { + "window_name": "editor", + "shell_command_before": [ + 'echo "I get run in each pane, before each pane command!"', + None, + ], + "layout": "main-vertical", + "panes": ["vim", None, "top"], + }, + {"window_name": "shell", "panes": ["git pull"]}, + { + "window_name": "guard", + "layout": "tiled", + "shell_command_before": [ + 'echo "I get run in each pane."', + 'echo "Before each pane command!"', + ], + "panes": [None, None, None], + }, + {"window_name": "database", "panes": ["bundle exec rails db"]}, + {"window_name": "server", "panes": ["bundle exec rails s"]}, + {"window_name": "logs", "panes": ["tail -f log/development.log"]}, + {"window_name": "console", "panes": ["bundle exec rails c"]}, + {"window_name": "capistrano", "panes": [None]}, + {"window_name": "server", "panes": ["ssh user@example.com"]}, + ], +} diff --git a/tests/fixtures/import_tmuxinator/test2.yaml b/tests/fixtures/import_tmuxinator/test2.yaml new file mode 100644 index 00000000000..91f99969b2a --- /dev/null +++ b/tests/fixtures/import_tmuxinator/test2.yaml @@ -0,0 +1,32 @@ +project_name: sample +project_root: ~/test +socket_name: foo # Remove to use default socket +pre: sudo /etc/rc.d/mysqld start # Runs before everything +rbenv: 2.0.0-p247 +cli_args: -f ~/.tmux.mac.conf # Pass arguments to tmux +tabs: +- editor: + pre: + - echo "I get run in each pane, before each pane command!" + - + layout: main-vertical + panes: + - vim + - #empty, will just run plain bash + - top +- shell: git pull +- guard: + layout: tiled + pre: + - echo "I get run in each pane." + - echo "Before each pane command!" + panes: + - + - #empty, will just run plain bash + - +- database: bundle exec rails db +- server: bundle exec rails s +- logs: tail -f log/development.log +- console: bundle exec rails c +- capistrano: +- server: ssh user@example.com diff --git a/tests/fixtures/import_tmuxinator/test3.py b/tests/fixtures/import_tmuxinator/test3.py new file mode 100644 index 00000000000..86ebd22c16e --- /dev/null +++ b/tests/fixtures/import_tmuxinator/test3.py @@ -0,0 +1,83 @@ +"""Tmuxinator data fixtures for import_tmuxinator tests, 3rd dataset.""" + +from __future__ import annotations + +from tests.fixtures import utils as test_utils + +tmuxinator_yaml = test_utils.read_workspace_file("import_tmuxinator/test3.yaml") + +tmuxinator_dict = { + "name": "sample", + "root": "~/test", + "socket_name": "foo", + "tmux_options": "-f ~/.tmux.mac.conf", + "pre": "sudo /etc/rc.d/mysqld start", + "pre_window": "rbenv shell 2.0.0-p247", + "windows": [ + { + "editor": { + "pre": [ + 'echo "I get run in each pane, before each pane command!"', + None, + ], + "layout": "main-vertical", + "root": "~/test/editor", + "panes": ["vim", None, "top"], + }, + }, + {"shell": ["git pull", "git merge"]}, + { + "guard": { + "layout": "tiled", + "pre": [ + 'echo "I get run in each pane."', + 'echo "Before each pane command!"', + ], + "panes": [None, None, None], + }, + }, + {"database": "bundle exec rails db"}, + {"server": "bundle exec rails s"}, + {"logs": "tail -f log/development.log"}, + {"console": "bundle exec rails c"}, + {"capistrano": None}, + {"server": "ssh user@example.com"}, + ], +} + +expected = { + "session_name": "sample", + "socket_name": "foo", + "start_directory": "~/test", + "config": "~/.tmux.mac.conf", + "shell_command": "sudo /etc/rc.d/mysqld start", + "shell_command_before": ["rbenv shell 2.0.0-p247"], + "windows": [ + { + "window_name": "editor", + "shell_command_before": [ + 'echo "I get run in each pane, before each pane command!"', + None, + ], + "layout": "main-vertical", + "start_directory": "~/test/editor", + "panes": ["vim", None, "top"], + }, + {"window_name": "shell", "panes": ["git pull", "git merge"]}, + { + "window_name": "guard", + "layout": "tiled", + "shell_command_before": [ + 'echo "I get run in each pane."', + 'echo "Before each pane command!"', + ], + "panes": [None, None, None], + }, + {"window_name": "database", "panes": ["bundle exec rails db"]}, + {"window_name": "server", "panes": ["bundle exec rails s"]}, + {"window_name": "logs", "panes": ["tail -f log/development.log"]}, + {"window_name": "console", "panes": ["bundle exec rails c"]}, + {"window_name": "capistrano", "panes": [None]}, + {"window_name": "server", "panes": ["ssh user@example.com"]}, + ], +} diff --git a/tests/fixtures/import_tmuxinator/test3.yaml b/tests/fixtures/import_tmuxinator/test3.yaml new file mode 100644 index 00000000000..ab17b87853e --- /dev/null +++ b/tests/fixtures/import_tmuxinator/test3.yaml @@ -0,0 +1,38 @@ +# ~/.tmuxinator/sample.yml +# you can make as many tabs as you wish... + +name: sample +root: ~/test +socket_name: foo # Remove to use default socket +pre: sudo /etc/rc.d/mysqld start # Runs before everything +pre_window: rbenv shell 2.0.0-p247 # Runs in each tab and pane +tmux_options: -f ~/.tmux.mac.conf # Pass arguments to tmux +windows: +- editor: + pre: + - echo "I get run in each pane, before each pane command!" + - + layout: main-vertical + root: ~/test/editor + panes: + - vim + - #empty, will just run plain bash + - top +- shell: + - git pull + - git merge +- guard: + layout: tiled + pre: + - echo "I get run in each pane." + - echo "Before each pane command!" + panes: + - + - #empty, will just run plain bash + - +- database: bundle exec rails db +- server: bundle exec rails s +- logs: tail -f log/development.log +- console: bundle exec rails c +- capistrano: +- server: ssh user@example.com diff --git a/tests/fixtures/pluginsystem/__init__.py b/tests/fixtures/pluginsystem/__init__.py new file mode 100644 index 00000000000..e4aa4bbf4dd --- /dev/null +++ b/tests/fixtures/pluginsystem/__init__.py @@ -0,0 +1 @@ +"""Test data for tmuxp plugin system.""" diff --git a/tests/fixtures/pluginsystem/partials/__init__.py b/tests/fixtures/pluginsystem/partials/__init__.py new file mode 100644 index 00000000000..793b5237355 --- /dev/null +++ b/tests/fixtures/pluginsystem/partials/__init__.py @@ -0,0 +1 @@ +"""Tmuxp tests for plugins.""" diff --git a/tests/fixtures/pluginsystem/partials/_types.py b/tests/fixtures/pluginsystem/partials/_types.py new file mode 100644 index 00000000000..0809596bdf7 --- /dev/null +++ b/tests/fixtures/pluginsystem/partials/_types.py @@ -0,0 +1,35 @@ +"""Internal, :const:`typing.TYPE_CHECKING` scoped :term:`type annotations `. + +These are _not_ to be imported at runtime as `typing_extensions` is not +bundled with tmuxp. Usage example: + +>>> import typing as t + +>>> if t.TYPE_CHECKING: +... from tmuxp.fixtures.pluginsystem.partials._types import PluginConfigSchema +... +""" + +from __future__ import annotations + +from typing_extensions import NotRequired, TypedDict + + +class PluginTestConfigSchema(TypedDict): + """Same as PluginConfigSchema, but with tmux, libtmux, and tmuxp version.""" + + tmux_version: NotRequired[str] + libtmux_version: NotRequired[str] + tmuxp_version: NotRequired[str] + + # Normal keys + plugin_name: NotRequired[str] + tmux_min_version: NotRequired[str] + tmux_max_version: NotRequired[str] + tmux_version_incompatible: NotRequired[list[str]] + libtmux_min_version: NotRequired[str] + libtmux_max_version: NotRequired[str] + libtmux_version_incompatible: NotRequired[list[str]] + tmuxp_min_version: NotRequired[str] + tmuxp_max_version: NotRequired[str] + tmuxp_version_incompatible: NotRequired[list[str]] diff --git a/tests/fixtures/pluginsystem/partials/all_pass.py b/tests/fixtures/pluginsystem/partials/all_pass.py new file mode 100644 index 00000000000..293d70bef1b --- /dev/null +++ b/tests/fixtures/pluginsystem/partials/all_pass.py @@ -0,0 +1,31 @@ +"""Tmuxp test plugin with version constraints guaranteed to pass.""" + +from __future__ import annotations + +import typing as t + +from .test_plugin_helpers import MyTestTmuxpPlugin + +if t.TYPE_CHECKING: + from ._types import PluginTestConfigSchema + + +class AllVersionPassPlugin(MyTestTmuxpPlugin): + """Tmuxp plugin with config constraints guaranteed to validate.""" + + def __init__(self) -> None: + config: PluginTestConfigSchema = { + "plugin_name": "tmuxp-plugin-my-tmuxp-plugin", + "tmux_min_version": "1.8", + "tmux_max_version": "100.0", + "tmux_version_incompatible": ["2.3"], + "libtmux_min_version": "0.8.3", + "libtmux_max_version": "100.0", + "libtmux_version_incompatible": ["0.7.1"], + "tmuxp_min_version": "1.7.0", + "tmuxp_max_version": "100.0.0", + "tmuxp_version_incompatible": ["1.5.6"], + "tmux_version": "3.0", + "tmuxp_version": "1.7.0", + } + MyTestTmuxpPlugin.__init__(self, **config) diff --git a/tests/fixtures/pluginsystem/partials/libtmux_version_fail.py b/tests/fixtures/pluginsystem/partials/libtmux_version_fail.py new file mode 100644 index 00000000000..e69efaa3f09 --- /dev/null +++ b/tests/fixtures/pluginsystem/partials/libtmux_version_fail.py @@ -0,0 +1,46 @@ +"""Fixtures for tmuxp plugins for libtmux version exceptions.""" + +from __future__ import annotations + +import typing as t + +from .test_plugin_helpers import MyTestTmuxpPlugin + +if t.TYPE_CHECKING: + from ._types import PluginTestConfigSchema + + +class LibtmuxVersionFailMinPlugin(MyTestTmuxpPlugin): + """Tmuxp plugin that fails when libtmux below minimum version constraint.""" + + def __init__(self) -> None: + config: PluginTestConfigSchema = { + "plugin_name": "libtmux-min-version-fail", + "libtmux_min_version": "0.8.3", + "libtmux_version": "0.7.0", + } + MyTestTmuxpPlugin.__init__(self, **config) + + +class LibtmuxVersionFailMaxPlugin(MyTestTmuxpPlugin): + """Tmuxp plugin that fails when libtmux above maximum version constraint.""" + + def __init__(self) -> None: + config: PluginTestConfigSchema = { + "plugin_name": "libtmux-max-version-fail", + "libtmux_max_version": "3.0", + "libtmux_version": "3.5", + } + MyTestTmuxpPlugin.__init__(self, **config) + + +class LibtmuxVersionFailIncompatiblePlugin(MyTestTmuxpPlugin): + """Tmuxp plugin that fails when libtmux version constraint is invalid.""" + + def __init__(self) -> None: + config: PluginTestConfigSchema = { + "plugin_name": "libtmux-incompatible-version-fail", + "libtmux_version_incompatible": ["0.7.1"], + "libtmux_version": "0.7.1", + } + MyTestTmuxpPlugin.__init__(self, **config) diff --git a/tests/fixtures/pluginsystem/partials/test_plugin_helpers.py b/tests/fixtures/pluginsystem/partials/test_plugin_helpers.py new file mode 100644 index 00000000000..d73674cf3f7 --- /dev/null +++ b/tests/fixtures/pluginsystem/partials/test_plugin_helpers.py @@ -0,0 +1,42 @@ +"""Tmuxp test plugin for asserting version constraints.""" + +from __future__ import annotations + +import typing as t + +from tmuxp.plugin import TmuxpPlugin + +if t.TYPE_CHECKING: + from typing_extensions import Unpack + + from tmuxp._internal.types import PluginConfigSchema + + from ._types import PluginTestConfigSchema + + +class MyTestTmuxpPlugin(TmuxpPlugin): + """Base class for testing tmuxp plugins with version constraints.""" + + def __init__(self, **config: Unpack[PluginTestConfigSchema]) -> None: + assert isinstance(config, dict) + tmux_version = config.pop("tmux_version", None) + libtmux_version = config.pop("libtmux_version", None) + tmuxp_version = config.pop("tmuxp_version", None) + + t.cast("PluginConfigSchema", config) + + assert "tmux_version" not in config + + # tests/fixtures/pluginsystem/partials/test_plugin_helpers.py:24: error: Extra + # argument "tmux_version" from **args for "__init__" of "TmuxpPlugin" [misc] + super().__init__(**config) # type:ignore + + # WARNING! This should not be done in anything but a test + if tmux_version: + self.version_constraints["tmux"]["version"] = tmux_version + if libtmux_version: + self.version_constraints["libtmux"]["version"] = libtmux_version + if tmuxp_version: + self.version_constraints["tmuxp"]["version"] = tmuxp_version + + self._version_check() diff --git a/tests/fixtures/pluginsystem/partials/tmux_version_fail.py b/tests/fixtures/pluginsystem/partials/tmux_version_fail.py new file mode 100644 index 00000000000..deaa89d2cf6 --- /dev/null +++ b/tests/fixtures/pluginsystem/partials/tmux_version_fail.py @@ -0,0 +1,47 @@ +"""Fixtures for tmuxp plugins for tmux version exceptions.""" + +from __future__ import annotations + +import typing as t + +from .test_plugin_helpers import MyTestTmuxpPlugin + +if t.TYPE_CHECKING: + from ._types import PluginTestConfigSchema + + +class TmuxVersionFailMinPlugin(MyTestTmuxpPlugin): + """Tmuxp plugin that fails when tmux below minimum version constraint.""" + + def __init__(self) -> None: + config: PluginTestConfigSchema = { + "plugin_name": "tmux-min-version-fail", + "tmux_min_version": "1.8", + "tmux_version": "1.7", + } + MyTestTmuxpPlugin.__init__(self, **config) + + +class TmuxVersionFailMaxPlugin(MyTestTmuxpPlugin): + """Tmuxp plugin that fails when tmux above maximum version constraint.""" + + def __init__(self) -> None: + config: PluginTestConfigSchema = { + "plugin_name": "tmux-max-version-fail", + "tmux_max_version": "3.0", + "tmux_version": "3.5", + } + MyTestTmuxpPlugin.__init__(self, **config) + + +class TmuxVersionFailIncompatiblePlugin(MyTestTmuxpPlugin): + """Tmuxp plugin that fails when tmux version constraint is invalid.""" + + def __init__(self) -> None: + config: PluginTestConfigSchema = { + "plugin_name": "tmux-incompatible-version-fail", + "tmux_version_incompatible": ["2.3"], + "tmux_version": "2.3", + } + + MyTestTmuxpPlugin.__init__(self, **config) diff --git a/tests/fixtures/pluginsystem/partials/tmuxp_version_fail.py b/tests/fixtures/pluginsystem/partials/tmuxp_version_fail.py new file mode 100644 index 00000000000..aa5b8e77834 --- /dev/null +++ b/tests/fixtures/pluginsystem/partials/tmuxp_version_fail.py @@ -0,0 +1,46 @@ +"""Fixtures for tmuxp plugins for tmuxp version exceptions.""" + +from __future__ import annotations + +import typing as t + +from .test_plugin_helpers import MyTestTmuxpPlugin + +if t.TYPE_CHECKING: + from ._types import PluginTestConfigSchema + + +class TmuxpVersionFailMinPlugin(MyTestTmuxpPlugin): + """Tmuxp plugin that fails when tmuxp below minimum version constraint.""" + + def __init__(self) -> None: + config: PluginTestConfigSchema = { + "plugin_name": "tmuxp-min-version-fail", + "tmuxp_min_version": "1.7.0", + "tmuxp_version": "1.6.3", + } + MyTestTmuxpPlugin.__init__(self, **config) + + +class TmuxpVersionFailMaxPlugin(MyTestTmuxpPlugin): + """Tmuxp plugin that fails when tmuxp above maximum version constraint.""" + + def __init__(self) -> None: + config: PluginTestConfigSchema = { + "plugin_name": "tmuxp-max-version-fail", + "tmuxp_max_version": "2.0.0", + "tmuxp_version": "2.5", + } + MyTestTmuxpPlugin.__init__(self, **config) + + +class TmuxpVersionFailIncompatiblePlugin(MyTestTmuxpPlugin): + """Tmuxp plugin that fails when tmuxp version constraint is invalid.""" + + def __init__(self) -> None: + config: PluginTestConfigSchema = { + "plugin_name": "tmuxp-incompatible-version-fail", + "tmuxp_version_incompatible": ["1.5.0"], + "tmuxp_version": "1.5.0", + } + MyTestTmuxpPlugin.__init__(self, **config) diff --git a/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_awf/pyproject.toml b/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_awf/pyproject.toml new file mode 100644 index 00000000000..27beec81b18 --- /dev/null +++ b/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_awf/pyproject.toml @@ -0,0 +1,15 @@ +[project] +name = "tmuxp_test_plugin_awf" +version = "0.0.2" +description = "A tmuxp plugin to test after_window_finished part of the tmuxp plugin system" +authors = [ + {name = "Joseph Flinn", email = "joseph.s.flinn@gmail.com"} +] +requires-python = ">=3.8,<4.0" +dependencies = [ + "tmuxp>=1.7.0" +] + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" diff --git a/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_awf/tmuxp_test_plugin_awf/__init__.py b/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_awf/tmuxp_test_plugin_awf/__init__.py new file mode 100644 index 00000000000..9cbdb91f118 --- /dev/null +++ b/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_awf/tmuxp_test_plugin_awf/__init__.py @@ -0,0 +1 @@ +"""Example tmuxp plugin that runs after window creation completions.""" diff --git a/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_awf/tmuxp_test_plugin_awf/plugin.py b/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_awf/tmuxp_test_plugin_awf/plugin.py new file mode 100644 index 00000000000..a3b323dc65b --- /dev/null +++ b/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_awf/tmuxp_test_plugin_awf/plugin.py @@ -0,0 +1,28 @@ +"""Tmuxp example plugin for after_window_finished.""" + +from __future__ import annotations + +import typing as t + +from tmuxp.plugin import TmuxpPlugin + +if t.TYPE_CHECKING: + from libtmux.window import Window + + +class PluginAfterWindowFinished(TmuxpPlugin): + """Tmuxp plugin that runs after window creation completes.""" + + def __init__(self) -> None: + self.message: str = "[+] This is the Tmuxp Test Plugin" + + def after_window_finished(self, window: Window) -> None: + """Run hook after window creation completed.""" + if window.name == "editor": + window.rename_window("plugin_test_awf") + elif window.name == "awf_mw_test": + window.rename_window("plugin_test_awf_mw") + elif window.name == "awf_mw_test_2": + window.rename_window("plugin_test_awf_mw_2") + elif window.name == "mp_test_owc": + window.rename_window("mp_test_awf") diff --git a/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_bs/pyproject.toml b/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_bs/pyproject.toml new file mode 100644 index 00000000000..3e624b80f4b --- /dev/null +++ b/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_bs/pyproject.toml @@ -0,0 +1,15 @@ +[project] +name = "tmuxp_test_plugin_bs" +version = "0.0.2" +description = "A tmuxp plugin to test before_script part of the tmuxp plugin system" +authors = [ + {name = "Joseph Flinn", email = "joseph.s.flinn@gmail.com"} +] +requires-python = ">=3.8,<4.0" +dependencies = [ + "tmuxp>=1.7.0" +] + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" diff --git a/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_bs/tmuxp_test_plugin_bs/__init__.py b/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_bs/tmuxp_test_plugin_bs/__init__.py new file mode 100644 index 00000000000..4329e60aae7 --- /dev/null +++ b/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_bs/tmuxp_test_plugin_bs/__init__.py @@ -0,0 +1 @@ +"""Example tmuxp plugin module that hooks in before_script, if declared.""" diff --git a/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_bs/tmuxp_test_plugin_bs/plugin.py b/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_bs/tmuxp_test_plugin_bs/plugin.py new file mode 100644 index 00000000000..9bc64c9e37f --- /dev/null +++ b/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_bs/tmuxp_test_plugin_bs/plugin.py @@ -0,0 +1,21 @@ +"""Tmux plugin that runs before_script, if it is declared in configuration.""" + +from __future__ import annotations + +import typing as t + +from tmuxp.plugin import TmuxpPlugin + +if t.TYPE_CHECKING: + from libtmux.session import Session + + +class PluginBeforeScript(TmuxpPlugin): + """Tmuxp plugin that runs before_script.""" + + def __init__(self) -> None: + self.message: str = "[+] This is the Tmuxp Test Plugin" + + def before_script(self, session: Session) -> None: + """Run hook during before_script, if it is declared.""" + session.rename_session("plugin_test_bs") diff --git a/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_bwb/pyproject.toml b/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_bwb/pyproject.toml new file mode 100644 index 00000000000..f469ebfb44c --- /dev/null +++ b/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_bwb/pyproject.toml @@ -0,0 +1,15 @@ +[project] +name = "tmuxp_test_plugin_bwb" +version = "0.0.2" +description = "A tmuxp plugin to test before_workspace_build part of the tmuxp plugin system" +authors = [ + {name = "Joseph Flinn", email = "joseph.s.flinn@gmail.com"} +] +requires-python = ">=3.8,<4.0" +dependencies = [ + "tmuxp>=1.7.0" +] + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" diff --git a/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_bwb/tmuxp_test_plugin_bwb/__init__.py b/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_bwb/tmuxp_test_plugin_bwb/__init__.py new file mode 100644 index 00000000000..98ca0ca0fc7 --- /dev/null +++ b/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_bwb/tmuxp_test_plugin_bwb/__init__.py @@ -0,0 +1 @@ +"""Example tmuxp plugin that runs before workspace builder inits.""" diff --git a/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_bwb/tmuxp_test_plugin_bwb/plugin.py b/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_bwb/tmuxp_test_plugin_bwb/plugin.py new file mode 100644 index 00000000000..b564ec58298 --- /dev/null +++ b/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_bwb/tmuxp_test_plugin_bwb/plugin.py @@ -0,0 +1,21 @@ +"""Tmuxp example plugin for before_worksplace_builder.""" + +from __future__ import annotations + +import typing as t + +from tmuxp.plugin import TmuxpPlugin + +if t.TYPE_CHECKING: + from libtmux.session import Session + + +class PluginBeforeWorkspaceBuilder(TmuxpPlugin): + """Tmuxp plugin that runs before worksplace builder starts.""" + + def __init__(self) -> None: + self.message: str = "[+] This is the Tmuxp Test Plugin" + + def before_workspace_builder(self, session: Session) -> None: + """Run hook before workspace builder begins.""" + session.rename_session("plugin_test_bwb") diff --git a/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_fail/pyproject.toml b/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_fail/pyproject.toml new file mode 100644 index 00000000000..dad2701978d --- /dev/null +++ b/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_fail/pyproject.toml @@ -0,0 +1,15 @@ +[project] +name = "tmuxp_test_plugin_fail" +version = "0.1.0" +description = "A test plugin designed to fail to test the cli" +authors = [ + {name = "Joseph Flinn", email = "joseph.s.flinn@gmail.com"} +] +requires-python = ">=3.8,<4.0" +dependencies = [ + "tmuxp>=1.7.0" +] + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" diff --git a/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_fail/tmuxp_test_plugin_fail/__init__.py b/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_fail/tmuxp_test_plugin_fail/__init__.py new file mode 100644 index 00000000000..2afcb8fce89 --- /dev/null +++ b/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_fail/tmuxp_test_plugin_fail/__init__.py @@ -0,0 +1 @@ +"""Tmuxp plugin test, that is destined for failure.""" diff --git a/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_fail/tmuxp_test_plugin_fail/plugin.py b/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_fail/tmuxp_test_plugin_fail/plugin.py new file mode 100644 index 00000000000..9be75a6e93e --- /dev/null +++ b/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_fail/tmuxp_test_plugin_fail/plugin.py @@ -0,0 +1,21 @@ +"""Tmuxp example plugin that fails on initialization.""" + +from __future__ import annotations + +import typing as t + +from tmuxp.plugin import TmuxpPlugin + +if t.TYPE_CHECKING: + from tmuxp._internal.types import PluginConfigSchema + + +class PluginFailVersion(TmuxpPlugin): + """A tmuxp plugin that is doomed to fail. DOOMED.""" + + def __init__(self) -> None: + config: PluginConfigSchema = { + "plugin_name": "tmuxp-plugin-fail-version", + "tmuxp_max_version": "0.0.0", + } + TmuxpPlugin.__init__(self, **config) diff --git a/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_owc/pyproject.toml b/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_owc/pyproject.toml new file mode 100644 index 00000000000..485a6442972 --- /dev/null +++ b/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_owc/pyproject.toml @@ -0,0 +1,15 @@ +[project] +name = "tmuxp_test_plugin_owc" +version = "0.0.2" +description = "A tmuxp plugin to test on_window_create part of the tmuxp plugin system" +authors = [ + {name = "Joseph Flinn", email = "joseph.s.flinn@gmail.com"} +] +requires-python = ">=3.8,<4.0" +dependencies = [ + "tmuxp>=1.7.0" +] + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" diff --git a/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_owc/tmuxp_test_plugin_owc/__init__.py b/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_owc/tmuxp_test_plugin_owc/__init__.py new file mode 100644 index 00000000000..f649064840a --- /dev/null +++ b/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_owc/tmuxp_test_plugin_owc/__init__.py @@ -0,0 +1 @@ +"""Example tmuxp plugin module for hooks on window creation.""" diff --git a/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_owc/tmuxp_test_plugin_owc/plugin.py b/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_owc/tmuxp_test_plugin_owc/plugin.py new file mode 100644 index 00000000000..acb9268725a --- /dev/null +++ b/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_owc/tmuxp_test_plugin_owc/plugin.py @@ -0,0 +1,28 @@ +"""Tmuxp example plugin for on_window_create.""" + +from __future__ import annotations + +import typing as t + +from tmuxp.plugin import TmuxpPlugin + +if t.TYPE_CHECKING: + from libtmux.window import Window + + +class PluginOnWindowCreate(TmuxpPlugin): + """Tmuxp plugin to test custom functionality on window creation.""" + + def __init__(self) -> None: + self.message: str = "[+] This is the Tmuxp Test Plugin" + + def on_window_create(self, window: Window) -> None: + """Apply hook that runs for tmux on session reattach.""" + if window.name == "editor": + window.rename_window("plugin_test_owc") + elif window.name == "owc_mw_test": + window.rename_window("plugin_test_owc_mw") + elif window.name == "owc_mw_test_2": + window.rename_window("plugin_test_owc_mw_2") + elif window.name == "mp_test": + window.rename_window("mp_test_owc") diff --git a/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_r/pyproject.toml b/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_r/pyproject.toml new file mode 100644 index 00000000000..a212e10ad86 --- /dev/null +++ b/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_r/pyproject.toml @@ -0,0 +1,15 @@ +[project] +name = "tmuxp_test_plugin_r" +version = "0.0.2" +description = "A tmuxp plugin to test reattach part of the tmuxp plugin system" +authors = [ + {name = "Joseph Flinn", email = "joseph.s.flinn@gmail.com"} +] +requires-python = ">=3.8,<4.0" +dependencies = [ + "tmuxp>=1.7.0" +] + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" diff --git a/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_r/tmuxp_test_plugin_r/__init__.py b/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_r/tmuxp_test_plugin_r/__init__.py new file mode 100644 index 00000000000..6e01504fd64 --- /dev/null +++ b/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_r/tmuxp_test_plugin_r/__init__.py @@ -0,0 +1 @@ +"""Example tmuxp plugin module for reattaching sessions.""" diff --git a/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_r/tmuxp_test_plugin_r/plugin.py b/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_r/tmuxp_test_plugin_r/plugin.py new file mode 100644 index 00000000000..396afabf5a0 --- /dev/null +++ b/tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_r/tmuxp_test_plugin_r/plugin.py @@ -0,0 +1,21 @@ +"""Tmuxp example plugin for reattaching session.""" + +from __future__ import annotations + +import typing as t + +from tmuxp.plugin import TmuxpPlugin + +if t.TYPE_CHECKING: + from libtmux.session import Session + + +class PluginReattach(TmuxpPlugin): + """Tmuxp plugin to test renaming session on reattach.""" + + def __init__(self) -> None: + self.message: str = "[+] This is the Tmuxp Test Plugin" + + def reattach(self, session: Session) -> None: + """Apply hook that runs for tmux on session reattach.""" + session.rename_session("plugin_test_r") diff --git a/tests/fixtures/regressions/issue_800_default_size_many_windows.yaml b/tests/fixtures/regressions/issue_800_default_size_many_windows.yaml new file mode 100644 index 00000000000..e18607b3806 --- /dev/null +++ b/tests/fixtures/regressions/issue_800_default_size_many_windows.yaml @@ -0,0 +1,12 @@ +session_name: many-windows-issue +windows: +- window_name: moo + layout: main-horizontal + panes: + - echo hello + - echo hello + - echo hello + - echo hello + - echo hello + - echo hello + - echo hello diff --git a/tests/fixtures/script_complete.sh b/tests/fixtures/script_complete.sh new file mode 100755 index 00000000000..26155d2e1e4 --- /dev/null +++ b/tests/fixtures/script_complete.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +echo hello +echo $? # Exit status 0 returned because command executed successfully. diff --git a/tests/fixtures/script_failed.sh b/tests/fixtures/script_failed.sh new file mode 100755 index 00000000000..82d5b6bb971 --- /dev/null +++ b/tests/fixtures/script_failed.sh @@ -0,0 +1,8 @@ +#!/bin/sh + + +echoerr() { echo "$@" 1>&2; } +echoerr An error has occurred + +exit 113 # Will return 113 to shell. + # To verify this, type "echo $?" after script terminates. diff --git a/tests/fixtures/structures.py b/tests/fixtures/structures.py new file mode 100644 index 00000000000..5c7587e4ce1 --- /dev/null +++ b/tests/fixtures/structures.py @@ -0,0 +1,19 @@ +"""Typings / structures for tmuxp fixtures.""" + +from __future__ import annotations + +import dataclasses +import typing as t + + +@dataclasses.dataclass +class WorkspaceTestData: + """Workspace data fixtures for tmuxp tests.""" + + expand1: t.Any + expand2: t.Any + expand_blank: t.Any + sample_workspace: t.Any + shell_command_before: t.Any + shell_command_before_session: t.Any + trickle: t.Any diff --git a/tests/fixtures/tmux/tmux.conf b/tests/fixtures/tmux/tmux.conf new file mode 100644 index 00000000000..ed2abd7ef9f --- /dev/null +++ b/tests/fixtures/tmux/tmux.conf @@ -0,0 +1 @@ +display-message "Hello World" diff --git a/tests/fixtures/utils.py b/tests/fixtures/utils.py new file mode 100644 index 00000000000..23c4ac26fd4 --- /dev/null +++ b/tests/fixtures/utils.py @@ -0,0 +1,38 @@ +"""Utility functions for tmuxp fixtures.""" + +from __future__ import annotations + +import pathlib + +from tests.constants import FIXTURE_PATH + + +def get_workspace_file( + file: str | pathlib.Path, +) -> pathlib.Path: + """Return fixture data, relative to __file__.""" + if isinstance(file, str): + file = pathlib.Path(file) + + return FIXTURE_PATH / file + + +def read_workspace_file( + file: pathlib.Path | str, +) -> str: + """Return fixture data, relative to __file__.""" + if isinstance(file, str): + file = pathlib.Path(file) + + return get_workspace_file(file).open().read() + + +def write_config( + config_path: pathlib.Path, + filename: str, + content: str, +) -> pathlib.Path: + """Write configuration content to file.""" + config = config_path / filename + config.write_text(content, encoding="utf-8") + return config diff --git a/tests/fixtures/workspace/__init__.py b/tests/fixtures/workspace/__init__.py new file mode 100644 index 00000000000..b097ef9cb65 --- /dev/null +++ b/tests/fixtures/workspace/__init__.py @@ -0,0 +1,13 @@ +"""Workspace data fixtures for tmuxp tests.""" + +from __future__ import annotations + +from . import ( + expand1, + expand2, + expand_blank, + sample_workspace, + shell_command_before, + shell_command_before_session, + trickle, +) diff --git a/tests/fixtures/workspace/builder/config_script_completes.yaml b/tests/fixtures/workspace/builder/config_script_completes.yaml new file mode 100644 index 00000000000..3772ee8aca2 --- /dev/null +++ b/tests/fixtures/workspace/builder/config_script_completes.yaml @@ -0,0 +1,5 @@ +session_name: sample workspace +before_script: {script_complete} +windows: +- panes: + - pane diff --git a/tests/fixtures/workspace/builder/config_script_fails.yaml b/tests/fixtures/workspace/builder/config_script_fails.yaml new file mode 100644 index 00000000000..25b3b899d49 --- /dev/null +++ b/tests/fixtures/workspace/builder/config_script_fails.yaml @@ -0,0 +1,5 @@ + session_name: sample workspace + before_script: {script_failed} + windows: + - panes: + - pane diff --git a/tests/fixtures/workspace/builder/config_script_not_exists.yaml b/tests/fixtures/workspace/builder/config_script_not_exists.yaml new file mode 100644 index 00000000000..51c76226b30 --- /dev/null +++ b/tests/fixtures/workspace/builder/config_script_not_exists.yaml @@ -0,0 +1,5 @@ +session_name: sample workspace +before_script: {script_not_exists} +windows: +- panes: + - pane diff --git a/tests/fixtures/workspace/builder/env_var_options.yaml b/tests/fixtures/workspace/builder/env_var_options.yaml new file mode 100644 index 00000000000..494fbe36cec --- /dev/null +++ b/tests/fixtures/workspace/builder/env_var_options.yaml @@ -0,0 +1,13 @@ +session_name: test env vars for options +start_directory: '~' +global_options: + visual-silence: ${VISUAL_SILENCE} +options: + repeat-time: 738 +windows: +- window_name: moo + layout: main-horizontal + options: + main-pane-height: ${MAIN_PANE_HEIGHT} + panes: + - pane diff --git a/tests/fixtures/workspace/builder/environment_vars.yaml b/tests/fixtures/workspace/builder/environment_vars.yaml new file mode 100644 index 00000000000..1160dae67c6 --- /dev/null +++ b/tests/fixtures/workspace/builder/environment_vars.yaml @@ -0,0 +1,32 @@ +session_name: test env vars +start_directory: "~" +environment: + FOO: SESSION + PATH: /tmp +windows: +- window_name: no_overrides + panes: + - pane +- window_name: window_overrides + environment: + FOO: WINDOW + panes: + - pane +- window_name: pane_overrides + panes: + - environment: + FOO: PANE +- window_name: both_overrides + environment: + FOO: WINDOW + panes: + - pane + - environment: + FOO: PANE +# This test case it just needed for warnings issued in old versions of tmux. +- window_name: both_overrides_on_first_pane + environment: + FOO: WINDOW + panes: + - environment: + FOO: PANE diff --git a/tests/fixtures/workspace/builder/first_pane_start_directory.yaml b/tests/fixtures/workspace/builder/first_pane_start_directory.yaml new file mode 100644 index 00000000000..1f9ae7ed23d --- /dev/null +++ b/tests/fixtures/workspace/builder/first_pane_start_directory.yaml @@ -0,0 +1,5 @@ +session_name: sample workspace +windows: + - panes: + - start_directory: /usr + - start_directory: /etc diff --git a/tests/fixtures/workspace/builder/focus_and_pane.yaml b/tests/fixtures/workspace/builder/focus_and_pane.yaml new file mode 100644 index 00000000000..1dd456eb879 --- /dev/null +++ b/tests/fixtures/workspace/builder/focus_and_pane.yaml @@ -0,0 +1,31 @@ +session_name: sample workspace +start_directory: '~' +windows: +- window_name: focused window + focus: true + panes: + - shell_command: + - cmd: cd ~ + - shell_command: + - cmd: cd /usr + focus: true + - shell_command: + - cmd: cd ~ + - cmd: echo "moo" + - cmd: top +- window_name: window 2 + panes: + - shell_command: + - cmd: top + focus: true + - shell_command: + - cmd: echo "hey" + - shell_command: + - cmd: echo "moo" +- window_name: window 3 + panes: + - shell_command: + - cmd: cd / + focus: true + - pane + - pane diff --git a/tests/fixtures/workspace/builder/global_options.yaml b/tests/fixtures/workspace/builder/global_options.yaml new file mode 100644 index 00000000000..cf34d9eb605 --- /dev/null +++ b/tests/fixtures/workspace/builder/global_options.yaml @@ -0,0 +1,11 @@ +session_name: test global options +start_directory: '~' +global_options: + repeat-time: 493 + status-position: 'top' +windows: +- window_name: moo + panes: + - pane + - pane + - pane diff --git a/tests/fixtures/workspace/builder/pane_ordering.yaml b/tests/fixtures/workspace/builder/pane_ordering.yaml new file mode 100644 index 00000000000..59858582def --- /dev/null +++ b/tests/fixtures/workspace/builder/pane_ordering.yaml @@ -0,0 +1,11 @@ +session_name: sample workspace +start_directory: {HOME} +windows: +- options: + automatic-rename: on + layout: tiled + panes: + - cd /usr/bin + - cd /usr + - cd /etc + - cd {HOME} diff --git a/tests/fixtures/workspace/builder/plugin_awf.yaml b/tests/fixtures/workspace/builder/plugin_awf.yaml new file mode 100644 index 00000000000..5111833e14a --- /dev/null +++ b/tests/fixtures/workspace/builder/plugin_awf.yaml @@ -0,0 +1,15 @@ +session_name: plugin-test-awf +plugins: +- 'tmuxp_test_plugin_awf.plugin.PluginAfterWindowFinished' +windows: +- window_name: editor + layout: tiled + shell_command_before: + - cd ~/ + panes: + - shell_command: + - cd /var/log + - ls -al | grep \.log + - echo hello + - echo hello + - echo hello diff --git a/tests/fixtures/workspace/builder/plugin_awf_multiple_windows.yaml b/tests/fixtures/workspace/builder/plugin_awf_multiple_windows.yaml new file mode 100644 index 00000000000..994c2a7c4db --- /dev/null +++ b/tests/fixtures/workspace/builder/plugin_awf_multiple_windows.yaml @@ -0,0 +1,18 @@ +session_name: plugin-test-awf-mw +plugins: +- 'tmuxp_test_plugin_awf.plugin.PluginAfterWindowFinished' +windows: +- window_name: awf_mw_test + layout: tiled + shell_command_before: + - cd ~/ + panes: + - shell_command: + - cd /var/log + - ls -al | grep \.log +- window_name: awf_mw_test_2 + layout: tiled + shell_command_before: + - cd ~/ + panes: + - echo hello \ No newline at end of file diff --git a/tests/fixtures/workspace/builder/plugin_bs.yaml b/tests/fixtures/workspace/builder/plugin_bs.yaml new file mode 100644 index 00000000000..5785afb4203 --- /dev/null +++ b/tests/fixtures/workspace/builder/plugin_bs.yaml @@ -0,0 +1,15 @@ +session_name: plugin-test-bs +plugins: +- 'tmuxp_test_plugin_bs.plugin.PluginBeforeScript' +windows: +- window_name: editor + layout: tiled + shell_command_before: + - cmd: cd ~/ + panes: + - shell_command: + - cmd: cd /var/log + - cmd: ls -al | grep \.log + - cmd: echo hello + - cmd: echo hello + - cmd: echo hello diff --git a/tests/fixtures/workspace/builder/plugin_bwb.yaml b/tests/fixtures/workspace/builder/plugin_bwb.yaml new file mode 100644 index 00000000000..8241f279462 --- /dev/null +++ b/tests/fixtures/workspace/builder/plugin_bwb.yaml @@ -0,0 +1,15 @@ +session_name: plugin-test-bwb +plugins: +- 'tmuxp_test_plugin_bwb.plugin.PluginBeforeWorkspaceBuilder' +windows: +- window_name: editor + layout: tiled + shell_command_before: + - cd ~/ + panes: + - shell_command: + - cd /var/log + - ls -al | grep \.log + - echo hello + - echo hello + - echo hello diff --git a/tests/fixtures/workspace/builder/plugin_missing_fail.yaml b/tests/fixtures/workspace/builder/plugin_missing_fail.yaml new file mode 100644 index 00000000000..4e1097debd5 --- /dev/null +++ b/tests/fixtures/workspace/builder/plugin_missing_fail.yaml @@ -0,0 +1,6 @@ +session_name: plugin-test-missing-fail +plugins: +- 'tmuxp_test_plugin_fail.plugin.PluginFailMissing' +windows: +- panes: + - echo "hey" \ No newline at end of file diff --git a/tests/fixtures/workspace/builder/plugin_multiple_plugins.yaml b/tests/fixtures/workspace/builder/plugin_multiple_plugins.yaml new file mode 100644 index 00000000000..113347db6e9 --- /dev/null +++ b/tests/fixtures/workspace/builder/plugin_multiple_plugins.yaml @@ -0,0 +1,14 @@ +session_name: plugin-test-multiple-plugins +plugins: +- 'tmuxp_test_plugin_bwb.plugin.PluginBeforeWorkspaceBuilder' +- 'tmuxp_test_plugin_owc.plugin.PluginOnWindowCreate' +- 'tmuxp_test_plugin_awf.plugin.PluginAfterWindowFinished' +windows: +- window_name: mp_test + layout: tiled + shell_command_before: + - cd ~/ + panes: + - shell_command: + - cd /var/log + - ls -al | grep \.log \ No newline at end of file diff --git a/tests/fixtures/workspace/builder/plugin_owc.yaml b/tests/fixtures/workspace/builder/plugin_owc.yaml new file mode 100644 index 00000000000..abc81d9192f --- /dev/null +++ b/tests/fixtures/workspace/builder/plugin_owc.yaml @@ -0,0 +1,15 @@ +session_name: plugin-test-owc +plugins: +- 'tmuxp_test_plugin_owc.plugin.PluginOnWindowCreate' +windows: +- window_name: editor + layout: tiled + shell_command_before: + - cd ~/ + panes: + - shell_command: + - cd /var/log + - ls -al | grep \.log + - echo hello + - echo hello + - echo hello diff --git a/tests/fixtures/workspace/builder/plugin_owc_multiple_windows.yaml b/tests/fixtures/workspace/builder/plugin_owc_multiple_windows.yaml new file mode 100644 index 00000000000..c7d437e5847 --- /dev/null +++ b/tests/fixtures/workspace/builder/plugin_owc_multiple_windows.yaml @@ -0,0 +1,17 @@ +session_name: plugin-test-owc-mw +plugins: +- 'tmuxp_test_plugin_owc.plugin.PluginOnWindowCreate' +windows: +- window_name: owc_mw_test + shell_command_before: + - cd ~/ + panes: + - shell_command: + - cd /var/log + - ls -al | grep \.log +- window_name: owc_mw_test_2 + layout: tiled + shell_command_before: + - cd ~/ + panes: + - echo hello \ No newline at end of file diff --git a/tests/fixtures/workspace/builder/plugin_r.yaml b/tests/fixtures/workspace/builder/plugin_r.yaml new file mode 100644 index 00000000000..b220aab6b83 --- /dev/null +++ b/tests/fixtures/workspace/builder/plugin_r.yaml @@ -0,0 +1,15 @@ +session_name: plugin-test-r +plugins: +- 'tmuxp_test_plugin_r.plugin.PluginReattach' +windows: +- window_name: editor + layout: tiled + shell_command_before: + - cd ~/ + panes: + - shell_command: + - cd /var/log + - ls -al | grep \.log + - echo hello + - echo hello + - echo hello diff --git a/tests/fixtures/workspace/builder/plugin_versions_fail.yaml b/tests/fixtures/workspace/builder/plugin_versions_fail.yaml new file mode 100644 index 00000000000..e4e7a4910bf --- /dev/null +++ b/tests/fixtures/workspace/builder/plugin_versions_fail.yaml @@ -0,0 +1,6 @@ +session_name: plugin-test-version-fail +plugins: +- 'tmuxp_test_plugin_fail.plugin.PluginFailVersion' +windows: +- panes: + - echo "hey" \ No newline at end of file diff --git a/tests/fixtures/workspace/builder/regression_00132_dots.yaml b/tests/fixtures/workspace/builder/regression_00132_dots.yaml new file mode 100644 index 00000000000..d05857c38c8 --- /dev/null +++ b/tests/fixtures/workspace/builder/regression_00132_dots.yaml @@ -0,0 +1,7 @@ +# regression for https://github.com/tmux-python/tmuxp/issues/132 +session_name: dot.session-name +windows: +- layout: main-vertical + panes: + - shell_command: + - echo 'hi' diff --git a/tests/fixtures/workspace/builder/session_options.yaml b/tests/fixtures/workspace/builder/session_options.yaml new file mode 100644 index 00000000000..fd2fd81a54f --- /dev/null +++ b/tests/fixtures/workspace/builder/session_options.yaml @@ -0,0 +1,11 @@ +session_name: test session options +start_directory: '~' +options: + default-shell: /bin/sh + default-command: /bin/sh +windows: +- window_name: moo + panes: + - pane + - pane + - pane diff --git a/tests/fixtures/workspace/builder/start_directory.yaml b/tests/fixtures/workspace/builder/start_directory.yaml new file mode 100644 index 00000000000..e5ed2ec5dfe --- /dev/null +++ b/tests/fixtures/workspace/builder/start_directory.yaml @@ -0,0 +1,52 @@ +session_name: sample workspace +start_directory: '/usr' +windows: +- window_name: supposed to be /usr/bin + window_index: 1 + start_directory: /usr/bin + options: + main-pane-height: 50 + panes: + - shell_command: + - echo "hey" + - shell_command: + - echo "moo" +- window_name: support to be /dev + window_index: 2 + start_directory: /dev + panes: + - shell_command: + - echo hello + - shell_command: + - echo "hey" + - shell_command: + - echo "moo" +- window_name: cwd containing a space + window_index: 3 + start_directory: {TEST_DIR} + panes: + - shell_command: + - echo hello + - shell_command: + - echo "hey" + - shell_command: + - echo "moo" +- window_name: testsa3 + window_index: 4 + panes: + - shell_command: + - echo hello + - shell_command: + - echo "hey" + - shell_command: + - echo "moo3" +- window_name: cwd relative to start_directory since no rel dir entered + window_index: 5 + start_directory: ./ + panes: + - shell_command: + - echo hello + - shell_command: + - echo "hey" + - shell_command: + - echo "moo3" diff --git a/tests/fixtures/workspace/builder/start_directory_relative.yaml b/tests/fixtures/workspace/builder/start_directory_relative.yaml new file mode 100644 index 00000000000..c6b0e592990 --- /dev/null +++ b/tests/fixtures/workspace/builder/start_directory_relative.yaml @@ -0,0 +1,47 @@ +session_name: sample workspace +start_directory: ./ +windows: +- window_name: supposed to be /usr/bin + start_directory: '/usr/bin' + options: + main-pane-height: 50 + panes: + - shell_command: + - echo "hey" + - shell_command: + - echo "moo" +- window_name: support to be /dev + start_directory: '/dev' + panes: + - shell_command: + - echo hello + - shell_command: + - echo "hey" + - shell_command: + - echo "moo" +- window_name: cwd containing a space + start_directory: {TEST_DIR} + panes: + - shell_command: + - echo hello + - shell_command: + - echo "hey" + - shell_command: + - echo "moo" +- window_name: inherit start_directory which is rel to workspace file + panes: + - shell_command: + - echo hello + - shell_command: + - echo "hey" + - shell_command: + - echo "moo3" +- window_name: cwd relative to workspace file + start_directory: ./ + panes: + - shell_command: + - echo hello + - shell_command: + - echo "hey" + - shell_command: + - echo "moo3" diff --git a/tests/fixtures/workspace/builder/start_directory_session_path.yaml b/tests/fixtures/workspace/builder/start_directory_session_path.yaml new file mode 100644 index 00000000000..5fd35b2f4ec --- /dev/null +++ b/tests/fixtures/workspace/builder/start_directory_session_path.yaml @@ -0,0 +1,6 @@ +--- +session_name: sample_start_dir_session_path +start_directory: '/usr' +windows: + - panes: + - diff --git a/tests/fixtures/workspace/builder/suppress_history.yaml b/tests/fixtures/workspace/builder/suppress_history.yaml new file mode 100644 index 00000000000..04722f9f3ce --- /dev/null +++ b/tests/fixtures/workspace/builder/suppress_history.yaml @@ -0,0 +1,11 @@ +session_name: sample workspace +start_directory: '~' +suppress_history: false +windows: +- window_name: inHistory + panes: + - echo inHistory +- window_name: isMissing + suppress_history: true + panes: + - echo isMissing diff --git a/tests/fixtures/workspace/builder/three_pane.yaml b/tests/fixtures/workspace/builder/three_pane.yaml new file mode 100644 index 00000000000..860fcd9371a --- /dev/null +++ b/tests/fixtures/workspace/builder/three_pane.yaml @@ -0,0 +1,12 @@ +session_name: sample workspace +start_directory: '~' +windows: +- window_name: test + layout: main-horizontal + panes: + - shell_command: + - cmd: vim + - shell_command: + - cmd: echo "hey" + - shell_command: + - cmd: echo "moo" diff --git a/tests/fixtures/workspace/builder/three_windows.yaml b/tests/fixtures/workspace/builder/three_windows.yaml new file mode 100644 index 00000000000..b883a7da57f --- /dev/null +++ b/tests/fixtures/workspace/builder/three_windows.yaml @@ -0,0 +1,14 @@ +session_name: sample_three_windows +windows: +- window_name: first + panes: + - shell_command: + - cmd: echo 'first window' +- window_name: second + panes: + - shell_command: + - cmd: echo 'second window' +- window_name: third + panes: + - shell_command: + - cmd: echo 'third window' diff --git a/tests/fixtures/workspace/builder/two_pane.yaml b/tests/fixtures/workspace/builder/two_pane.yaml new file mode 100644 index 00000000000..216080feb27 --- /dev/null +++ b/tests/fixtures/workspace/builder/two_pane.yaml @@ -0,0 +1,18 @@ +session_name: sample workspace +start_directory: '~' +windows: +- layout: main-vertical + panes: + - shell_command: + - cmd: vim + - shell_command: + - cmd: echo "hey" + window_name: editor +- panes: + - shell_command: + - cmd: tail | echo 'hi' + window_name: logging +- window_name: test + panes: + - shell_command: + - cmd: htop diff --git a/tests/fixtures/workspace/builder/two_windows.yaml b/tests/fixtures/workspace/builder/two_windows.yaml new file mode 100644 index 00000000000..490382fe5f3 --- /dev/null +++ b/tests/fixtures/workspace/builder/two_windows.yaml @@ -0,0 +1,10 @@ +session_name: sample_two_windows +windows: +- window_name: first + panes: + - shell_command: + - cmd: echo 'first window' +- window_name: second + panes: + - shell_command: + - cmd: echo 'second window' diff --git a/tests/fixtures/workspace/builder/window_automatic_rename.yaml b/tests/fixtures/workspace/builder/window_automatic_rename.yaml new file mode 100644 index 00000000000..2584363930e --- /dev/null +++ b/tests/fixtures/workspace/builder/window_automatic_rename.yaml @@ -0,0 +1,16 @@ +session_name: test window options +start_directory: '~' +windows: +- window_name: renamed_window + layout: main-horizontal + options: + automatic-rename: on + panes: + - shell_command: + - cmd: man ls + start_directory: '~' + focus: true + - shell_command: + - cmd: echo "hey" + - shell_command: + - cmd: echo "moo" diff --git a/tests/fixtures/workspace/builder/window_index.yaml b/tests/fixtures/workspace/builder/window_index.yaml new file mode 100644 index 00000000000..dda27298595 --- /dev/null +++ b/tests/fixtures/workspace/builder/window_index.yaml @@ -0,0 +1,12 @@ +session_name: sample workspace +windows: +- window_name: zero + panes: + - echo 'zero' +- window_name: five + panes: + - echo 'five' + window_index: 5 +- window_name: one + panes: + - echo 'one' diff --git a/tests/fixtures/workspace/builder/window_options.yaml b/tests/fixtures/workspace/builder/window_options.yaml new file mode 100644 index 00000000000..714d2347284 --- /dev/null +++ b/tests/fixtures/workspace/builder/window_options.yaml @@ -0,0 +1,11 @@ +session_name: test window options +start_directory: '~' +windows: +- layout: main-horizontal + options: + main-pane-height: 5 + panes: + - pane + - pane + - pane + window_name: editor diff --git a/tests/fixtures/workspace/builder/window_options_after.yaml b/tests/fixtures/workspace/builder/window_options_after.yaml new file mode 100644 index 00000000000..3d60c97ffa5 --- /dev/null +++ b/tests/fixtures/workspace/builder/window_options_after.yaml @@ -0,0 +1,11 @@ +session_name: tmuxp test window_options_after +options: + default-shell: /bin/bash +windows: + - window_name: test + suppress_history: false + panes: + - echo 0 + - echo 1 + options_after: + synchronize-panes: on diff --git a/tests/fixtures/workspace/builder/window_shell.yaml b/tests/fixtures/workspace/builder/window_shell.yaml new file mode 100644 index 00000000000..f602b3e3961 --- /dev/null +++ b/tests/fixtures/workspace/builder/window_shell.yaml @@ -0,0 +1,12 @@ +session_name: test window options +start_directory: '~' +windows: +- layout: main-horizontal + options: + main-pane-height: 5 + panes: + - pane + - pane + - pane + window_name: editor + window_shell: top diff --git a/tests/fixtures/workspace/expand1.py b/tests/fixtures/workspace/expand1.py new file mode 100644 index 00000000000..6ad0cbb9e39 --- /dev/null +++ b/tests/fixtures/workspace/expand1.py @@ -0,0 +1,79 @@ +"""Examples of expansion of tmuxp configurations from shorthand style.""" + +from __future__ import annotations + +import pathlib +import typing as t + +before_workspace = { + "session_name": "sample workspace", + "start_directory": "~", + "windows": [ + { + "window_name": "editor", + "panes": [ + {"shell_command": ["vim", "top"]}, + {"shell_command": ["vim"]}, + {"shell_command": 'cowsay "hey"'}, + ], + "layout": "main-vertical", + }, + { + "window_name": "logging", + "panes": [{"shell_command": ["tail -F /var/log/syslog"]}], + }, + { + "start_directory": "/var/log", + "options": {"automatic-rename": True}, + "panes": [{"shell_command": "htop"}, "vim"], + }, + {"start_directory": "./", "panes": ["pwd"]}, + {"start_directory": "./asdf/", "panes": ["pwd"]}, + {"start_directory": "../", "panes": ["pwd"]}, + {"panes": ["top"]}, + ], +} + + +def after_workspace() -> dict[str, t.Any]: + """After expansion of shorthand style.""" + return { + "session_name": "sample workspace", + "start_directory": str(pathlib.Path().home()), + "windows": [ + { + "window_name": "editor", + "panes": [ + {"shell_command": [{"cmd": "vim"}, {"cmd": "top"}]}, + {"shell_command": [{"cmd": "vim"}]}, + {"shell_command": [{"cmd": 'cowsay "hey"'}]}, + ], + "layout": "main-vertical", + }, + { + "window_name": "logging", + "panes": [{"shell_command": [{"cmd": "tail -F /var/log/syslog"}]}], + }, + { + "start_directory": "/var/log", + "options": {"automatic-rename": True}, + "panes": [ + {"shell_command": [{"cmd": "htop"}]}, + {"shell_command": [{"cmd": "vim"}]}, + ], + }, + { + "start_directory": str(pathlib.Path().home()), + "panes": [{"shell_command": [{"cmd": "pwd"}]}], + }, + { + "start_directory": str(pathlib.Path().home() / "asdf"), + "panes": [{"shell_command": [{"cmd": "pwd"}]}], + }, + { + "start_directory": str(pathlib.Path().home().parent.resolve()), + "panes": [{"shell_command": [{"cmd": "pwd"}]}], + }, + {"panes": [{"shell_command": [{"cmd": "top"}]}]}, + ], + } diff --git a/tests/fixtures/workspace/expand2-expanded.yaml b/tests/fixtures/workspace/expand2-expanded.yaml new file mode 100644 index 00000000000..0809447aaae --- /dev/null +++ b/tests/fixtures/workspace/expand2-expanded.yaml @@ -0,0 +1,32 @@ +session_name: sample workspace +start_directory: {HOME} +windows: +- window_name: focused window + layout: main-horizontal + focus: true + panes: + - shell_command: + - cmd: cd ~ + - shell_command: + - cmd: cd /usr + focus: true + - shell_command: + - cmd: cd ~ + - cmd: echo "moo" + - cmd: top +- window_name: window 2 + panes: + - shell_command: + - cmd: top + focus: true + - shell_command: + - cmd: echo "hey" + - shell_command: + - cmd: echo "moo" +- window_name: window 3 + panes: + - shell_command: + - cmd: cd / + focus: true + - shell_command: [] + - shell_command: [] diff --git a/tests/fixtures/workspace/expand2-unexpanded.yaml b/tests/fixtures/workspace/expand2-unexpanded.yaml new file mode 100644 index 00000000000..6c1a49ed51e --- /dev/null +++ b/tests/fixtures/workspace/expand2-unexpanded.yaml @@ -0,0 +1,32 @@ +session_name: sample workspace +start_directory: '~' +windows: +- window_name: focused window + layout: main-horizontal + focus: true + panes: + - shell_command: + - cd ~ + - shell_command: + - cd /usr + focus: true + - shell_command: + - cd ~ + - echo "moo" + - top +- window_name: window 2 + panes: + - shell_command: + - top + focus: true + - shell_command: + - echo "hey" + - shell_command: + - echo "moo" +- window_name: window 3 + panes: + - shell_command: cd / + focus: true + - pane + - pane + diff --git a/tests/fixtures/workspace/expand2.py b/tests/fixtures/workspace/expand2.py new file mode 100644 index 00000000000..7e8089bbbc6 --- /dev/null +++ b/tests/fixtures/workspace/expand2.py @@ -0,0 +1,19 @@ +"""YAML examples of expansion of tmuxp configurations from shorthand style.""" + +from __future__ import annotations + +import pathlib + +from tests.fixtures import utils as test_utils + + +def unexpanded_yaml() -> str: + """Return unexpanded, shorthand YAML tmuxp configuration.""" + return test_utils.read_workspace_file("workspace/expand2-unexpanded.yaml") + + +def expanded_yaml() -> str: + """Return expanded, verbose YAML tmuxp configuration.""" + return test_utils.read_workspace_file("workspace/expand2-expanded.yaml").format( + HOME=str(pathlib.Path().home()), + ) diff --git a/tests/fixtures/workspace/expand_blank.py b/tests/fixtures/workspace/expand_blank.py new file mode 100644 index 00000000000..51376052976 --- /dev/null +++ b/tests/fixtures/workspace/expand_blank.py @@ -0,0 +1,40 @@ +"""Expected expanded configuration for empty workspace panes.""" + +from __future__ import annotations + +expected = { + "session_name": "Blank pane test", + "windows": [ + { + "window_name": "Blank pane test", + "panes": [ + {"shell_command": []}, + {"shell_command": []}, + {"shell_command": []}, + ], + }, + { + "window_name": "More blank panes", + "panes": [ + {"shell_command": []}, + {"shell_command": []}, + {"shell_command": []}, + ], + }, + { + "window_name": "Empty string (return)", + "panes": [ + {"shell_command": [{"cmd": ""}]}, + {"shell_command": [{"cmd": ""}]}, + {"shell_command": [{"cmd": ""}]}, + ], + }, + { + "window_name": "Blank with options", + "panes": [ + {"shell_command": [], "focus": True}, + {"shell_command": [], "start_directory": "/tmp"}, + ], + }, + ], +} diff --git a/tests/fixtures/workspace/freezer/sample_workspace.yaml b/tests/fixtures/workspace/freezer/sample_workspace.yaml new file mode 100644 index 00000000000..ebd3ab3f9aa --- /dev/null +++ b/tests/fixtures/workspace/freezer/sample_workspace.yaml @@ -0,0 +1,21 @@ +session_name: sample workspace +start_directory: "~" +windows: +- layout: main-vertical + panes: + - shell_command: + - cmd: vim + start_directory: "~" + - shell_command: + - cmd: echo "hey" + - cmd: cd ../ + window_name: editor +- panes: + - shell_command: + - cmd: pane + start_directory: /usr/bin + window_name: logging +- window_name: test + panes: + - shell_command: + - cmd: top diff --git a/tests/fixtures/workspace/sample_workspace.py b/tests/fixtures/workspace/sample_workspace.py new file mode 100644 index 00000000000..e00b15113af --- /dev/null +++ b/tests/fixtures/workspace/sample_workspace.py @@ -0,0 +1,28 @@ +"""Example workspace fixture for tmuxp WorkspaceBuilder.""" + +from __future__ import annotations + +sample_workspace_dict = { + "session_name": "sample workspace", + "start_directory": "~", + "windows": [ + { + "window_name": "editor", + "panes": [ + {"start_directory": "~", "shell_command": ["vim"]}, + {"shell_command": ['cowsay "hey"']}, + ], + "layout": "main-vertical", + }, + { + "window_name": "logging", + "panes": [ + { + "shell_command": ["tail -F /var/log/syslog"], + "start_directory": "/var/log", + }, + ], + }, + {"options": {"automatic_rename": True}, "panes": [{"shell_command": ["htop"]}]}, + ], +} diff --git a/tests/fixtures/workspace/shell_command_before.py b/tests/fixtures/workspace/shell_command_before.py new file mode 100644 index 00000000000..53f8e1587f8 --- /dev/null +++ b/tests/fixtures/workspace/shell_command_before.py @@ -0,0 +1,168 @@ +"""Test fixture for tmuxp to demonstrate shell_command_before.""" + +from __future__ import annotations + +import pathlib +import typing as t + +config_unexpanded = { # shell_command_before is string in some areas + "session_name": "sample workspace", + "start_directory": "/", + "windows": [ + { + "window_name": "editor", + "start_directory": "~", + "shell_command_before": "source .venv/bin/activate", + "panes": [ + {"shell_command": ["vim"]}, + { + "shell_command_before": ["rbenv local 2.0.0-p0"], + "shell_command": ['cowsay "hey"'], + }, + ], + "layout": "main-vertical", + }, + { + "shell_command_before": "rbenv local 2.0.0-p0", + "window_name": "logging", + "panes": [{"shell_command": ["tail -F /var/log/syslog"]}, {}], + }, + { + "window_name": "shufu", + "panes": [ + { + "shell_command_before": ["rbenv local 2.0.0-p0"], + "shell_command": ["htop"], + }, + ], + }, + {"options": {"automatic-rename": True}, "panes": [{"shell_command": ["htop"]}]}, + {"panes": ["top"]}, + ], +} + + +def config_expanded() -> dict[str, t.Any]: + """Return expanded configuration for shell_command_before example.""" + return { # shell_command_before is string in some areas + "session_name": "sample workspace", + "start_directory": "/", + "windows": [ + { + "window_name": "editor", + "start_directory": str(pathlib.Path().home()), + "shell_command_before": { + "shell_command": [{"cmd": "source .venv/bin/activate"}], + }, + "panes": [ + {"shell_command": [{"cmd": "vim"}]}, + { + "shell_command_before": { + "shell_command": [{"cmd": "rbenv local 2.0.0-p0"}], + }, + "shell_command": [{"cmd": 'cowsay "hey"'}], + }, + ], + "layout": "main-vertical", + }, + { + "shell_command_before": { + "shell_command": [{"cmd": "rbenv local 2.0.0-p0"}], + }, + "window_name": "logging", + "panes": [ + {"shell_command": [{"cmd": "tail -F /var/log/syslog"}]}, + {"shell_command": []}, + ], + }, + { + "window_name": "shufu", + "panes": [ + { + "shell_command_before": { + "shell_command": [{"cmd": "rbenv local 2.0.0-p0"}], + }, + "shell_command": [{"cmd": "htop"}], + }, + ], + }, + { + "options": {"automatic-rename": True}, + "panes": [{"shell_command": [{"cmd": "htop"}]}], + }, + {"panes": [{"shell_command": [{"cmd": "top"}]}]}, + ], + } + + +def config_after() -> dict[str, t.Any]: + """Return expected configuration for shell_command_before example.""" + return { # shell_command_before is string in some areas + "session_name": "sample workspace", + "start_directory": "/", + "windows": [ + { + "window_name": "editor", + "start_directory": str(pathlib.Path().home()), + "shell_command_before": { + "shell_command": [{"cmd": "source .venv/bin/activate"}], + }, + "panes": [ + { + "shell_command": [ + {"cmd": "source .venv/bin/activate"}, + {"cmd": "vim"}, + ], + }, + { + "shell_command_before": { + "shell_command": [{"cmd": "rbenv local 2.0.0-p0"}], + }, + "shell_command": [ + {"cmd": "source .venv/bin/activate"}, + {"cmd": "rbenv local 2.0.0-p0"}, + {"cmd": 'cowsay "hey"'}, + ], + }, + ], + "layout": "main-vertical", + }, + { + "shell_command_before": { + "shell_command": [{"cmd": "rbenv local 2.0.0-p0"}], + }, + "start_directory": "/", + "window_name": "logging", + "panes": [ + { + "shell_command": [ + {"cmd": "rbenv local 2.0.0-p0"}, + {"cmd": "tail -F /var/log/syslog"}, + ], + }, + {"shell_command": [{"cmd": "rbenv local 2.0.0-p0"}]}, + ], + }, + { + "start_directory": "/", + "window_name": "shufu", + "panes": [ + { + "shell_command_before": { + "shell_command": [{"cmd": "rbenv local 2.0.0-p0"}], + }, + "shell_command": [ + {"cmd": "rbenv local 2.0.0-p0"}, + {"cmd": "htop"}, + ], + }, + ], + }, + { + "start_directory": "/", + "options": {"automatic-rename": True}, + "panes": [{"shell_command": [{"cmd": "htop"}]}], + }, + {"start_directory": "/", "panes": [{"shell_command": [{"cmd": "top"}]}]}, + ], + } diff --git a/tests/fixtures/workspace/shell_command_before_session-expected.yaml b/tests/fixtures/workspace/shell_command_before_session-expected.yaml new file mode 100644 index 00000000000..dd55247c886 --- /dev/null +++ b/tests/fixtures/workspace/shell_command_before_session-expected.yaml @@ -0,0 +1,24 @@ +shell_command_before: + shell_command: + - cmd: 'echo "hi"' +session_name: 'test' +windows: +- window_name: editor + panes: + - shell_command: + - cmd: 'echo "hi"' + - cmd: vim + - cmd: :Ex + - shell_command: + - cmd: 'echo "hi"' + - shell_command: + - cmd: 'echo "hi"' + - cmd: cd /usr +- window_name: logging + panes: + - shell_command: + - cmd: 'echo "hi"' + - shell_command: + - cmd: 'echo "hi"' + - cmd: top + - cmd: emacs diff --git a/tests/fixtures/workspace/shell_command_before_session.py b/tests/fixtures/workspace/shell_command_before_session.py new file mode 100644 index 00000000000..adfc86f1838 --- /dev/null +++ b/tests/fixtures/workspace/shell_command_before_session.py @@ -0,0 +1,10 @@ +"""Tests shell_command_before configuration.""" + +from __future__ import annotations + +from tests.fixtures import utils as test_utils + +before = test_utils.read_workspace_file("workspace/shell_command_before_session.yaml") +expected = test_utils.read_workspace_file( + "workspace/shell_command_before_session-expected.yaml", +) diff --git a/tests/fixtures/workspace/shell_command_before_session.yaml b/tests/fixtures/workspace/shell_command_before_session.yaml new file mode 100644 index 00000000000..09976f0edec --- /dev/null +++ b/tests/fixtures/workspace/shell_command_before_session.yaml @@ -0,0 +1,18 @@ +shell_command_before: + - 'echo "hi"' +session_name: 'test' +windows: +- window_name: editor + panes: + - shell_command: + - vim + - :Ex + - pane + - cd /usr +- window_name: logging + panes: + - shell_command: + - + - shell_command: + - top + - emacs diff --git a/tests/fixtures/workspace/trickle.py b/tests/fixtures/workspace/trickle.py new file mode 100644 index 00000000000..a1012be6473 --- /dev/null +++ b/tests/fixtures/workspace/trickle.py @@ -0,0 +1,51 @@ +"""Test data for tmuxp workspace fixture to demo object tree inheritance.""" + +from __future__ import annotations + +before = { # shell_command_before is string in some areas + "session_name": "sample workspace", + "start_directory": "/var", + "windows": [ + { + "window_name": "editor", + "start_directory": "log", + "panes": [ + {"shell_command": [{"cmd": "vim"}]}, + {"shell_command": [{"cmd": 'cowsay "hey"'}]}, + ], + "layout": "main-vertical", + }, + { + "window_name": "logging", + "start_directory": "~", + "panes": [ + {"shell_command": [{"cmd": "tail -F /var/log/syslog"}]}, + {"shell_command": []}, + ], + }, + ], +} + +expected = { # shell_command_before is string in some areas + "session_name": "sample workspace", + "start_directory": "/var", + "windows": [ + { + "window_name": "editor", + "start_directory": "/var/log", + "panes": [ + {"shell_command": [{"cmd": "vim"}]}, + {"shell_command": [{"cmd": 'cowsay "hey"'}]}, + ], + "layout": "main-vertical", + }, + { + "start_directory": "~", + "window_name": "logging", + "panes": [ + {"shell_command": [{"cmd": "tail -F /var/log/syslog"}]}, + {"shell_command": []}, + ], + }, + ], +} diff --git a/tests/test_plugin.py b/tests/test_plugin.py new file mode 100644 index 00000000000..cf7cfc63718 --- /dev/null +++ b/tests/test_plugin.py @@ -0,0 +1,97 @@ +"""Tests for tmuxp plugin API.""" + +from __future__ import annotations + +import pytest + +from tmuxp.exc import TmuxpPluginException + +from .fixtures.pluginsystem.partials.all_pass import AllVersionPassPlugin +from .fixtures.pluginsystem.partials.libtmux_version_fail import ( + LibtmuxVersionFailIncompatiblePlugin, + LibtmuxVersionFailMaxPlugin, + LibtmuxVersionFailMinPlugin, +) +from .fixtures.pluginsystem.partials.tmux_version_fail import ( + TmuxVersionFailIncompatiblePlugin, + TmuxVersionFailMaxPlugin, + TmuxVersionFailMinPlugin, +) +from .fixtures.pluginsystem.partials.tmuxp_version_fail import ( + TmuxpVersionFailIncompatiblePlugin, + TmuxpVersionFailMaxPlugin, + TmuxpVersionFailMinPlugin, +) + + +@pytest.fixture(autouse=True) +def autopatch_sitedir(monkeypatch_plugin_test_packages: None) -> None: + """Fixture automatically used that patches sitedir.""" + + +def test_all_pass() -> None: + """Plugin for tmuxp that loads successfully.""" + AllVersionPassPlugin() + + +def test_tmux_version_fail_min() -> None: + """Plugin raises if tmux version is below minimum constraint.""" + with pytest.raises(TmuxpPluginException, match=r"Incompatible.*") as exc_info: + TmuxVersionFailMinPlugin() + assert "tmux-min-version-fail" in str(exc_info.value) + + +def test_tmux_version_fail_max() -> None: + """Plugin raises if tmux version is above maximum constraint.""" + with pytest.raises(TmuxpPluginException, match=r"Incompatible.*") as exc_info: + TmuxVersionFailMaxPlugin() + assert "tmux-max-version-fail" in str(exc_info.value) + + +def test_tmux_version_fail_incompatible() -> None: + """Plugin raises if tmuxp version is incompatible.""" + with pytest.raises(TmuxpPluginException, match=r"Incompatible.*") as exc_info: + TmuxVersionFailIncompatiblePlugin() + assert "tmux-incompatible-version-fail" in str(exc_info.value) + + +def test_tmuxp_version_fail_min() -> None: + """Plugin raises if tmuxp version is below minimum constraint.""" + with pytest.raises(TmuxpPluginException, match=r"Incompatible.*") as exc_info: + TmuxpVersionFailMinPlugin() + assert "tmuxp-min-version-fail" in str(exc_info.value) + + +def test_tmuxp_version_fail_max() -> None: + """Plugin raises if tmuxp version is above max constraint.""" + with pytest.raises(TmuxpPluginException, match=r"Incompatible.*") as exc_info: + TmuxpVersionFailMaxPlugin() + assert "tmuxp-max-version-fail" in str(exc_info.value) + + +def test_tmuxp_version_fail_incompatible() -> None: + """Plugin raises if libtmux version is below minimum constraint.""" + with pytest.raises(TmuxpPluginException, match=r"Incompatible.*") as exc_info: + TmuxpVersionFailIncompatiblePlugin() + assert "tmuxp-incompatible-version-fail" in str(exc_info.value) + + +def test_libtmux_version_fail_min() -> None: + """Plugin raises if libtmux version is below minimum constraint.""" + with pytest.raises(TmuxpPluginException, match=r"Incompatible.*") as exc_info: + LibtmuxVersionFailMinPlugin() + assert "libtmux-min-version-fail" in str(exc_info.value) + + +def test_libtmux_version_fail_max() -> None: + """Plugin raises if libtmux version is above max constraint.""" + with pytest.raises(TmuxpPluginException, match=r"Incompatible.*") as exc_info: + LibtmuxVersionFailMaxPlugin() + assert "libtmux-max-version-fail" in str(exc_info.value) + + +def test_libtmux_version_fail_incompatible() -> None: + """Plugin raises if libtmux version is incompatible.""" + with pytest.raises(TmuxpPluginException, match=r"Incompatible.*") as exc_info: + LibtmuxVersionFailIncompatiblePlugin() + assert "libtmux-incompatible-version-fail" in str(exc_info.value) diff --git a/tests/test_shell.py b/tests/test_shell.py new file mode 100644 index 00000000000..d544439d606 --- /dev/null +++ b/tests/test_shell.py @@ -0,0 +1,19 @@ +"""Tests for tmuxp shell module.""" + +from __future__ import annotations + +from tmuxp import shell + + +def test_detect_best_shell() -> None: + """detect_best_shell() returns a a string of the best shell.""" + result = shell.detect_best_shell() + assert isinstance(result, str) + + +def test_shell_detect() -> None: + """Tests shell detection functions.""" + assert isinstance(shell.has_bpython(), bool) + assert isinstance(shell.has_ipython(), bool) + assert isinstance(shell.has_ptpython(), bool) + assert isinstance(shell.has_ptipython(), bool) diff --git a/tests/test_util.py b/tests/test_util.py new file mode 100644 index 00000000000..e5a4a513277 --- /dev/null +++ b/tests/test_util.py @@ -0,0 +1,163 @@ +"""Tests for tmuxp's utility functions.""" + +from __future__ import annotations + +import sys +import typing as t + +import pytest + +from tmuxp import exc +from tmuxp.exc import BeforeLoadScriptError, BeforeLoadScriptNotExists +from tmuxp.util import get_session, run_before_script + +from .constants import FIXTURE_PATH + +if t.TYPE_CHECKING: + import pathlib + + from libtmux.server import Server + + +def test_run_before_script_raise_BeforeLoadScriptNotExists_if_not_exists() -> None: + """run_before_script() raises BeforeLoadScriptNotExists if script not found.""" + script_file = FIXTURE_PATH / "script_noexists.sh" + + with pytest.raises(BeforeLoadScriptNotExists): + run_before_script(script_file) + + with pytest.raises(OSError): + run_before_script(script_file) + + +def test_run_before_script_raise_BeforeLoadScriptError_if_retcode() -> None: + """run_before_script() raises BeforeLoadScriptNotExists if script fails.""" + script_file = FIXTURE_PATH / "script_failed.sh" + + with pytest.raises(BeforeLoadScriptError): + run_before_script(script_file) + + +@pytest.fixture +def temp_script(tmp_path: pathlib.Path) -> pathlib.Path: + """Fixture of an example script that prints "Hello, world!".""" + script = tmp_path / "test_script.sh" + script.write_text( + """#!/bin/sh +echo "Hello, World!" +exit 0 +""", + ) + script.chmod(0o755) + return script + + +class TTYTestFixture(t.NamedTuple): + """Test fixture for isatty behavior verification.""" + + test_id: str + isatty_value: bool + expected_output: str + + +TTY_TEST_FIXTURES: list[TTYTestFixture] = [ + TTYTestFixture( + test_id="tty_enabled_shows_output", + isatty_value=True, + expected_output="Hello, World!", + ), + TTYTestFixture( + test_id="tty_disabled_suppresses_output", + isatty_value=False, + expected_output="", + ), +] + + +@pytest.mark.parametrize( + list(TTYTestFixture._fields), + TTY_TEST_FIXTURES, + ids=[test.test_id for test in TTY_TEST_FIXTURES], +) +def test_run_before_script_isatty( + temp_script: pathlib.Path, + monkeypatch: pytest.MonkeyPatch, + capsys: pytest.CaptureFixture[str], + test_id: str, + isatty_value: bool, + expected_output: str, +) -> None: + """Verify behavior of ``isatty()``, which we mock in `run_before_script()`.""" + # Mock sys.stdout.isatty() to return the desired value. + monkeypatch.setattr(sys.stdout, "isatty", lambda: isatty_value) + + # Run the script. + returncode = run_before_script(temp_script) + + # Assert that the script ran successfully. + assert returncode == 0 + + out, _err = capsys.readouterr() + + # In TTY mode, we expect the output; in non-TTY mode, we expect it to be suppressed. + assert expected_output in out + + +def test_return_stdout_if_ok( + capsys: pytest.CaptureFixture[str], + monkeypatch: pytest.MonkeyPatch, +) -> None: + """run_before_script() returns stdout if script succeeds.""" + # Simulate sys.stdout.isatty() + sys.stderr.isatty() + monkeypatch.setattr(sys.stdout, "isatty", lambda: True) + monkeypatch.setattr(sys.stderr, "isatty", lambda: True) + + script_file = FIXTURE_PATH / "script_complete.sh" + + run_before_script(script_file) + out, _err = capsys.readouterr() + assert "hello" in out + + +def test_beforeload_returncode() -> None: + """run_before_script() returns returncode if script fails.""" + script_file = FIXTURE_PATH / "script_failed.sh" + + with pytest.raises(exc.BeforeLoadScriptError) as excinfo: + run_before_script(script_file) + assert excinfo.match(r"113") + + +def test_beforeload_returns_stderr_messages() -> None: + """run_before_script() returns stderr messages if script fails.""" + script_file = FIXTURE_PATH / "script_failed.sh" + + with pytest.raises(exc.BeforeLoadScriptError) as excinfo: + run_before_script(script_file) + assert excinfo.match(r"failed with returncode") + + +def test_get_session_should_default_to_local_attached_session( + server: Server, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """get_session() should launch current terminal's tmux session, if inside one.""" + server.new_session(session_name="myfirstsession") + second_session = server.new_session(session_name="mysecondsession") + + # Assign an active pane to the session + first_pane_on_second_session_id = second_session.windows[0].panes[0].pane_id + assert first_pane_on_second_session_id is not None + monkeypatch.setenv("TMUX_PANE", first_pane_on_second_session_id) + + assert get_session(server) == second_session + + +def test_get_session_should_return_first_session_if_no_active_session( + server: Server, +) -> None: + """get_session() should return first session if no active session.""" + first_session = server.new_session(session_name="myfirstsession") + server.new_session(session_name="mysecondsession") + + assert get_session(server) == first_session diff --git a/tests/tests/__init__.py b/tests/tests/__init__.py new file mode 100644 index 00000000000..f887a3d06e1 --- /dev/null +++ b/tests/tests/__init__.py @@ -0,0 +1 @@ +"""Tests for tmuxp's pytest helpers.""" diff --git a/tests/tests/test_helpers.py b/tests/tests/test_helpers.py new file mode 100644 index 00000000000..aa04cee8c07 --- /dev/null +++ b/tests/tests/test_helpers.py @@ -0,0 +1,41 @@ +"""Tests for tmuxp's helper and utility functions.""" + +from __future__ import annotations + +import typing as t + +import pytest +from libtmux.test.random import get_test_session_name +from libtmux.test.temporary import temp_session + +if t.TYPE_CHECKING: + from libtmux.server import Server + + +def test_temp_session_kills_session_on_exit(server: Server) -> None: + """Test temp_session() context manager kills session on exit.""" + server = server + session_name = get_test_session_name(server=server) + + with temp_session(server=server, session_name=session_name): + result = server.has_session(session_name) + assert result + + assert not server.has_session(session_name) + + +@pytest.mark.flaky(reruns=5) +def test_temp_session_if_session_killed_before_exit(server: Server) -> None: + """Handles situation where session already closed within context.""" + server = server + session_name = get_test_session_name(server=server) + + with temp_session(server=server, session_name=session_name): + # an error or an exception within a temp_session kills the session + server.kill_session(session_name) + + result = server.has_session(session_name) + assert not result + + # really dead? + assert not server.has_session(session_name) diff --git a/tests/workspace/__init__.py b/tests/workspace/__init__.py new file mode 100644 index 00000000000..ec8ccc92b49 --- /dev/null +++ b/tests/workspace/__init__.py @@ -0,0 +1 @@ +"""Workspace tests for tmuxp.""" diff --git a/tests/workspace/conftest.py b/tests/workspace/conftest.py new file mode 100644 index 00000000000..403189710a3 --- /dev/null +++ b/tests/workspace/conftest.py @@ -0,0 +1,27 @@ +"""Pytest configuration for tmuxp workspace tests.""" + +from __future__ import annotations + +import types + +import pytest + +from tests.fixtures.structures import WorkspaceTestData + + +@pytest.fixture +def config_fixture() -> WorkspaceTestData: + """Deferred import of tmuxp.tests.fixtures.*. + + pytest setup (conftest.py) patches os.environ["HOME"], delay execution of + os.path.expanduser until here. + """ + from tests.fixtures import workspace as test_workspace_data + + return WorkspaceTestData( + **{ + k: v + for k, v in test_workspace_data.__dict__.items() + if isinstance(v, types.ModuleType) + }, + ) diff --git a/tests/workspace/test_builder.py b/tests/workspace/test_builder.py new file mode 100644 index 00000000000..c71c1b275a3 --- /dev/null +++ b/tests/workspace/test_builder.py @@ -0,0 +1,1583 @@ +"""Test for tmuxp workspace builder.""" + +from __future__ import annotations + +import functools +import os +import pathlib +import textwrap +import time +import typing as t + +import libtmux +import pytest +from libtmux._internal.query_list import ObjectDoesNotExist +from libtmux.common import has_gte_version, has_lt_version +from libtmux.exc import LibTmuxException +from libtmux.pane import Pane +from libtmux.session import Session +from libtmux.test.retry import retry_until +from libtmux.test.temporary import temp_session +from libtmux.window import Window + +from tests.constants import EXAMPLE_PATH, FIXTURE_PATH +from tests.fixtures import utils as test_utils +from tmuxp import exc +from tmuxp._internal.config_reader import ConfigReader +from tmuxp.cli.load import load_plugins +from tmuxp.workspace import loader +from tmuxp.workspace.builder import WorkspaceBuilder + +if t.TYPE_CHECKING: + from libtmux.server import Server + + class AssertCallbackProtocol(t.Protocol): + """Assertion callback type protocol.""" + + def __call__(self, cmd: str, hist: str) -> bool: + """Run function code for testing assertion.""" + ... + + +def test_split_windows(session: Session) -> None: + """Test workspace builder splits windows in a tmux session.""" + workspace = ConfigReader._from_file( + test_utils.get_workspace_file("workspace/builder/two_pane.yaml"), + ) + + builder = WorkspaceBuilder(session_config=workspace, server=session.server) + + window_count = len(session.windows) # current window count + assert len(session.windows) == window_count + for w, wconf in builder.iter_create_windows(session): + for p in builder.iter_create_panes(w, wconf): + w.select_layout("tiled") # fix glitch with pane size + p = p + assert len(session.windows) == window_count + assert isinstance(w, Window) + + assert len(session.windows) == window_count + window_count += 1 + + +def test_split_windows_three_pane(session: Session) -> None: + """Test workspace builder splits windows in a tmux session.""" + workspace = ConfigReader._from_file( + test_utils.get_workspace_file("workspace/builder/three_pane.yaml"), + ) + + builder = WorkspaceBuilder(session_config=workspace, server=session.server) + + window_count = len(session.windows) # current window count + assert len(session.windows) == window_count + for w, wconf in builder.iter_create_windows(session): + for p in builder.iter_create_panes(w, wconf): + w.select_layout("tiled") # fix glitch with pane size + p = p + assert len(session.windows) == window_count + assert isinstance(w, Window) + + assert len(session.windows) == window_count + window_count += 1 + w.set_window_option("main-pane-height", 50) + w.select_layout(wconf["layout"]) + + +def test_focus_pane_index(session: Session) -> None: + """Test focus of pane by index works correctly, including with pane-base-index.""" + workspace = ConfigReader._from_file( + test_utils.get_workspace_file("workspace/builder/focus_and_pane.yaml"), + ) + workspace = loader.expand(workspace) + workspace = loader.trickle(workspace) + + builder = WorkspaceBuilder(session_config=workspace, server=session.server) + + builder.build(session=session) + + assert session.active_window.name == "focused window" + + pane_base_index_ = session.active_window.show_window_option( + "pane-base-index", + g=True, + ) + assert isinstance(pane_base_index_, int) + pane_base_index = int(pane_base_index_) + + pane_base_index = 0 if not pane_base_index else int(pane_base_index) + + # get the pane index for each pane + pane_base_indexes = [ + int(pane.index) + for pane in session.active_window.panes + if pane is not None and pane.index is not None + ] + + pane_indexes_should_be = [pane_base_index + x for x in range(3)] + assert pane_indexes_should_be == pane_base_indexes + + w = session.active_window + + assert w.name != "man" + + pane_path = "/usr" + p = None + + def f_check() -> bool: + nonlocal p + p = w.active_pane + assert p is not None + return p.pane_current_path == pane_path + + assert retry_until(f_check) + + assert p is not None + assert p.pane_current_path == pane_path + + proc = session.cmd("show-option", "-gv", "base-index") + base_index = int(proc.stdout[0]) + + window3 = session.windows.get(window_index=str(base_index + 2)) + assert isinstance(window3, Window) + + p = None + pane_path = "/" + + def f_check_again() -> bool: + nonlocal p + p = window3.active_pane + assert p is not None + return p.pane_current_path == pane_path + + assert retry_until(f_check_again) + + assert p is not None + assert p.pane_current_path is not None + assert isinstance(p.pane_current_path, str) + assert p.pane_current_path == pane_path + + +@pytest.mark.skip( + reason=""" +Test needs to be rewritten, assertion not reliable across platforms +and CI. See https://github.com/tmux-python/tmuxp/issues/310. + """.strip(), +) +def test_suppress_history(session: Session) -> None: + """Test suppression of command history.""" + workspace = ConfigReader._from_file( + test_utils.get_workspace_file("workspace/builder/suppress_history.yaml"), + ) + workspace = loader.expand(workspace) + workspace = loader.trickle(workspace) + + builder = WorkspaceBuilder(session_config=workspace, server=session.server) + builder.build(session=session) + + inHistoryWindow = session.windows.get(window_name="inHistory") + assert inHistoryWindow is not None + isMissingWindow = session.windows.get(window_name="isMissing") + assert isMissingWindow is not None + + def assertHistory(cmd: str, hist: str) -> bool: + return "inHistory" in cmd and cmd.endswith(hist) + + def assertIsMissing(cmd: str, hist: str) -> bool: + return "isMissing" in cmd and not cmd.endswith(hist) + + for w, window_name, assertCase in [ + (inHistoryWindow, "inHistory", assertHistory), + (isMissingWindow, "isMissing", assertIsMissing), + ]: + assert w.name == window_name + w.select() + p = w.active_pane + assert p is not None + p.select() + + # Print the last-in-history command in the pane + p.cmd("send-keys", " fc -ln -1") + p.cmd("send-keys", "Enter") + + buffer_name = "test" + sent_cmd = None + + def f(p: Pane, buffer_name: str, assertCase: AssertCallbackProtocol) -> bool: + # from v0.7.4 libtmux session.cmd adds target -t self.id by default + # show-buffer doesn't accept -t, use global cmd. + + # Get the contents of the pane + p.cmd("capture-pane", "-b", buffer_name) + + captured_pane = session.server.cmd("show-buffer", "-b", buffer_name) + session.server.cmd("delete-buffer", "-b", buffer_name) + + # Parse the sent and last-in-history commands + sent_cmd = captured_pane.stdout[0].strip() + history_cmd = captured_pane.stdout[-2].strip() + + return assertCase(sent_cmd, history_cmd) + + f_ = functools.partial(f, p=p, buffer_name=buffer_name, assertCase=assertCase) + + assert retry_until(f_), f"Unknown sent command: [{sent_cmd}] in {assertCase}" + + +def test_session_options(session: Session) -> None: + """Test setting of options to session scope.""" + workspace = ConfigReader._from_file( + test_utils.get_workspace_file("workspace/builder/session_options.yaml"), + ) + workspace = loader.expand(workspace) + + builder = WorkspaceBuilder(session_config=workspace, server=session.server) + builder.build(session=session) + + default_shell = session.show_option("default-shell") + assert isinstance(default_shell, str) + assert "/bin/sh" in default_shell + + default_command = session.show_option("default-command") + assert isinstance(default_command, str) + assert "/bin/sh" in default_command + + +def test_global_options(session: Session) -> None: + """Test setting of global options.""" + workspace = ConfigReader._from_file( + test_utils.get_workspace_file("workspace/builder/global_options.yaml"), + ) + workspace = loader.expand(workspace) + + builder = WorkspaceBuilder(session_config=workspace, server=session.server) + builder.build(session=session) + + status_position = session.show_option("status-position", global_=True) + assert isinstance(status_position, str) + assert "top" in status_position + assert session.show_option("repeat-time", global_=True) == 493 + + +def test_global_session_env_options( + session: Session, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Test setting of global option variables.""" + visual_silence = "on" + monkeypatch.setenv("VISUAL_SILENCE", str(visual_silence)) + repeat_time = 738 + monkeypatch.setenv("REPEAT_TIME", str(repeat_time)) + main_pane_height = 8 + monkeypatch.setenv("MAIN_PANE_HEIGHT", str(main_pane_height)) + + workspace = ConfigReader._from_file( + test_utils.get_workspace_file("workspace/builder/env_var_options.yaml"), + ) + workspace = loader.expand(workspace) + + builder = WorkspaceBuilder(session_config=workspace, server=session.server) + builder.build(session=session) + + visual_silence_ = session.show_option("visual-silence", global_=True) + assert isinstance(visual_silence_, str) + assert visual_silence in visual_silence_ + assert repeat_time == session.show_option("repeat-time") + assert main_pane_height == session.active_window.show_window_option( + "main-pane-height", + ) + + +def test_window_options( + session: Session, +) -> None: + """Test setting of window options.""" + workspace = ConfigReader._from_file( + test_utils.get_workspace_file("workspace/builder/window_options.yaml"), + ) + workspace = loader.expand(workspace) + + if has_gte_version("2.3"): + workspace["windows"][0]["options"]["pane-border-format"] = " #P " + + builder = WorkspaceBuilder(session_config=workspace, server=session.server) + + window_count = len(session.windows) # current window count + assert len(session.windows) == window_count + for w, wconf in builder.iter_create_windows(session): + for p in builder.iter_create_panes(w, wconf): + w.select_layout("tiled") # fix glitch with pane size + p = p + assert len(session.windows) == window_count + assert isinstance(w, Window) + assert w.show_window_option("main-pane-height") == 5 + if has_gte_version("2.3"): + assert w.show_window_option("pane-border-format") == " #P " + + assert len(session.windows) == window_count + window_count += 1 + w.select_layout(wconf["layout"]) + + +@pytest.mark.flaky(reruns=5) +def test_window_options_after( + session: Session, +) -> None: + """Test setting window options via options_after (WorkspaceBuilder.after_window).""" + workspace = ConfigReader._from_file( + test_utils.get_workspace_file("workspace/builder/window_options_after.yaml"), + ) + workspace = loader.expand(workspace) + + builder = WorkspaceBuilder(session_config=workspace, server=session.server) + builder.build(session=session) + + def assert_last_line(p: Pane, s: str) -> bool: + def f() -> bool: + pane_out = p.cmd("capture-pane", "-p", "-J").stdout + while not pane_out[-1].strip(): # delete trailing lines tmux 1.8 + pane_out.pop() + return len(pane_out) > 1 and pane_out[-2].strip() == s + + # Print output for easier debugging if assertion fails + return retry_until(f, raises=False) + + for i, pane in enumerate(session.active_window.panes): + assert assert_last_line(pane, str(i)), ( + "Initial command did not execute properly/" + str(i) + ) + pane.cmd("send-keys", "Up") # Will repeat echo + pane.enter() # in each iteration + assert assert_last_line(pane, str(i)), ( + "Repeated command did not execute properly/" + str(i) + ) + + session.cmd("send-keys", " echo moo") + session.cmd("send-keys", "Enter") + + for pane in session.active_window.panes: + assert assert_last_line( + pane, + "moo", + ), "Synchronized command did not execute properly" + + +def test_window_shell( + session: Session, +) -> None: + """Test execution of commands via tmuxp configuration.""" + workspace = ConfigReader._from_file( + test_utils.get_workspace_file("workspace/builder/window_shell.yaml"), + ) + workspace = loader.expand(workspace) + + builder = WorkspaceBuilder(session_config=workspace, server=session.server) + + for w, wconf in builder.iter_create_windows(session): + if "window_shell" in wconf: + assert wconf["window_shell"] == "top" + + def f(w: Window) -> bool: + return w.window_name != "top" + + f_ = functools.partial(f, w=w) + + retry_until(f_) + + assert w.name != "top" + + +@pytest.mark.skipif( + has_lt_version("3.0"), + reason="needs -e flag for new-window and split-window introduced in tmux 3.0", +) +def test_environment_variables( + session: Session, +) -> None: + """Test setting of environmental variables in tmux via workspace builder.""" + workspace = ConfigReader._from_file( + test_utils.get_workspace_file("workspace/builder/environment_vars.yaml"), + ) + workspace = loader.expand(workspace) + + builder = WorkspaceBuilder(session_config=workspace, server=session.server) + builder.build(session) + # Give slow shells some time to settle as otherwise tests might fail. + time.sleep(0.3) + + assert session.getenv("FOO") == "SESSION" + assert session.getenv("PATH") == "/tmp" + + no_overrides_win = session.windows[0] + pane = no_overrides_win.panes[0] + pane.send_keys("echo $FOO") + assert pane.capture_pane()[1] == "SESSION" + + window_overrides_win = session.windows[1] + pane = window_overrides_win.panes[0] + pane.send_keys("echo $FOO") + assert pane.capture_pane()[1] == "WINDOW" + + pane_overrides_win = session.windows[2] + pane = pane_overrides_win.panes[0] + pane.send_keys("echo $FOO") + assert pane.capture_pane()[1] == "PANE" + + both_overrides_win = session.windows[3] + pane = both_overrides_win.panes[0] + pane.send_keys("echo $FOO") + assert pane.capture_pane()[1] == "WINDOW" + pane = both_overrides_win.panes[1] + pane.send_keys("echo $FOO") + assert pane.capture_pane()[1] == "PANE" + + +@pytest.mark.skipif( + has_gte_version("3.0"), + reason="warnings are not needed for tmux >= 3.0", +) +def test_environment_variables_warns_prior_to_tmux_3_0( + session: Session, + caplog: pytest.LogCaptureFixture, +) -> None: + """Warns when environmental variables cannot be set prior to tmux 3.0.""" + workspace = ConfigReader._from_file( + test_utils.get_workspace_file("workspace/builder/environment_vars.yaml"), + ) + workspace = loader.expand(workspace) + + builder = WorkspaceBuilder(session_config=workspace, server=session.server) + builder.build(session) + + # environment on sessions should work as this is done using set-environment + # on the session itself + assert session.getenv("FOO") == "SESSION" + assert session.getenv("PATH") == "/tmp" + + assert ( + sum( + 1 + for record in caplog.records + if "Cannot set environment for new windows." in record.msg + ) + # From window_overrides and both_overrides, but not + # both_overrides_in_first_pane. + == 2 + ), "Warning on creating windows missing" + assert ( + sum( + 1 + for record in caplog.records + if "Cannot set environment for new panes." in record.msg + ) + # From pane_overrides and both_overrides, but not both_overrides_in_first_pane. + == 2 + ), "Warning on creating panes missing" + assert ( + sum( + 1 + for record in caplog.records + if "Cannot set environment for new panes and windows." in record.msg + ) + # From both_overrides_in_first_pane. + == 1 + ) + + +def test_automatic_rename_option( + server: Server, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Test workspace builder with automatic renaming enabled.""" + monkeypatch.setenv("DISABLE_AUTO_TITLE", "true") + monkeypatch.setenv("ROWS", "36") + + workspace = ConfigReader._from_file( + test_utils.get_workspace_file("workspace/builder/window_automatic_rename.yaml"), + ) + + # This should be a command guaranteed to be terminal name across systems + portable_command = workspace["windows"][0]["panes"][0]["shell_command"][0]["cmd"] + # If a command is like "man ls", get the command base name, "ls" + if " " in portable_command: + portable_command = portable_command.split(" ")[0] + + builder = WorkspaceBuilder(session_config=workspace, server=server) + builder.build() + assert builder.session is not None + session: Session = builder.session + w: Window = session.windows[0] + assert len(session.windows) == 1 + + assert w.name != "renamed_window" + + def check_window_name_mismatch() -> bool: + return bool(w.name != portable_command) + + assert retry_until(check_window_name_mismatch, 5, interval=0.25) + + def check_window_name_match() -> bool: + assert w.show_window_option("automatic-rename") == "on" + return w.name in { + pathlib.Path(os.getenv("SHELL", "bash")).name, + portable_command, + } + + assert retry_until( + check_window_name_match, + 4, + interval=0.05, + ), f"Window name {w.name} should be {portable_command}" + + w.select_pane("-D") + + assert retry_until(check_window_name_mismatch, 2, interval=0.25) + + +def test_blank_pane_spawn( + session: Session, +) -> None: + """Test various ways of spawning blank panes from a tmuxp configuration. + + :todo: Verify blank panes of various types build into workspaces. + """ + yaml_workspace_file = EXAMPLE_PATH / "blank-panes.yaml" + test_config = ConfigReader._from_file(yaml_workspace_file) + + test_config = loader.expand(test_config) + builder = WorkspaceBuilder(session_config=test_config, server=session.server) + builder.build(session=session) + + assert session == builder.session + + window1 = session.windows.get(window_name="Blank pane test") + assert window1 is not None + assert len(window1.panes) == 3 + + window2 = session.windows.get(window_name="More blank panes") + assert window2 is not None + assert len(window2.panes) == 3 + + window3 = session.windows.get(window_name="Empty string (return)") + assert window3 is not None + assert len(window3.panes) == 3 + + window4 = session.windows.get(window_name="Blank with options") + assert window4 is not None + assert len(window4.panes) == 2 + + +def test_start_directory(session: Session, tmp_path: pathlib.Path) -> None: + """Test workspace builder setting start_directory relative to current directory.""" + test_dir = tmp_path / "foo bar" + test_dir.mkdir() + + yaml_workspace = test_utils.read_workspace_file( + "workspace/builder/start_directory.yaml", + ) + test_config = yaml_workspace.format(TEST_DIR=test_dir) + + workspace = ConfigReader._load(fmt="yaml", content=test_config) + workspace = loader.expand(workspace) + workspace = loader.trickle(workspace) + + builder = WorkspaceBuilder(session_config=workspace, server=session.server) + builder.build(session=session) + + assert session == builder.session + dirs = ["/usr/bin", "/dev", str(test_dir), "/usr", "/usr"] + + for path, window in zip(dirs, session.windows): + for p in window.panes: + + def f(path: str, p: Pane) -> bool: + pane_path = p.pane_current_path + return ( + pane_path is not None and path in pane_path + ) or pane_path == path + + f_ = functools.partial(f, path=path, p=p) + + # handle case with OS X adding /private/ to /tmp/ paths + assert retry_until(f_) + + +def test_start_directory_relative(session: Session, tmp_path: pathlib.Path) -> None: + """Test workspace builder setting start_directory relative to project file. + + Same as above test, but with relative start directory, mimicking + loading it from a location of project file. Like:: + + $ tmuxp load ~/workspace/myproject/.tmuxp.yaml + + instead of:: + + $ cd ~/workspace/myproject/.tmuxp.yaml + $ tmuxp load . + + """ + yaml_workspace = test_utils.read_workspace_file( + "workspace/builder/start_directory_relative.yaml", + ) + + test_dir = tmp_path / "foo bar" + test_dir.mkdir() + config_dir = tmp_path / "testRelConfigDir" + config_dir.mkdir() + + test_config = yaml_workspace.format(TEST_DIR=test_dir) + workspace = ConfigReader._load(fmt="yaml", content=test_config) + # the second argument of os.getcwd() mimics the behavior + # the CLI loader will do, but it passes in the workspace file's location. + workspace = loader.expand(workspace, config_dir) + + workspace = loader.trickle(workspace) + + assert config_dir.exists() + assert test_dir.exists() + builder = WorkspaceBuilder(session_config=workspace, server=session.server) + builder.build(session=session) + + assert session == builder.session + + dirs = ["/usr/bin", "/dev", str(test_dir), str(config_dir), str(config_dir)] + + for path, window in zip(dirs, session.windows): + for p in window.panes: + + def f(path: str, p: Pane) -> bool: + pane_path = p.pane_current_path + return ( + pane_path is not None and path in pane_path + ) or pane_path == path + + f_ = functools.partial(f, path=path, p=p) + + # handle case with OS X adding /private/ to /tmp/ paths + assert retry_until(f_) + + +@pytest.mark.skipif( + has_lt_version("3.2a"), + reason="needs format introduced in tmux >= 3.2a", +) +def test_start_directory_sets_session_path(server: Server) -> None: + """Test start_directory setting path in session_path.""" + workspace = ConfigReader._from_file( + test_utils.get_workspace_file( + "workspace/builder/start_directory_session_path.yaml", + ), + ) + workspace = loader.expand(workspace) + workspace = loader.trickle(workspace) + + builder = WorkspaceBuilder(session_config=workspace, server=server) + builder.build() + + session = builder.session + expected = f"{session.id}|/usr" + + cmd = server.cmd("list-sessions", "-F", "#{session_id}|#{session_path}") + assert expected in cmd.stdout + + +def test_pane_order(session: Session) -> None: + """Pane ordering based on position in config and ``pane_index``. + + Regression test for https://github.com/tmux-python/tmuxp/issues/15. + """ + yaml_workspace = test_utils.read_workspace_file( + "workspace/builder/pane_ordering.yaml", + ).format(HOME=str(pathlib.Path().home().resolve())) + + # test order of `panes` (and pane_index) above against pane_dirs + pane_paths = [ + "/usr/bin", + "/usr", + "/etc", + str(pathlib.Path().home().resolve()), + ] + + workspace = ConfigReader._load(fmt="yaml", content=yaml_workspace) + workspace = loader.expand(workspace) + workspace = loader.trickle(workspace) + + builder = WorkspaceBuilder(session_config=workspace, server=session.server) + + window_count = len(session.windows) # current window count + assert len(session.windows) == window_count + + for w, wconf in builder.iter_create_windows(session): + for _ in builder.iter_create_panes(w, wconf): + w.select_layout("tiled") # fix glitch with pane size + assert len(session.windows) == window_count + + assert isinstance(w, Window) + + assert len(session.windows) == window_count + window_count += 1 + + for w in session.windows: + pane_base_index = w.show_window_option("pane-base-index", g=True) + assert pane_base_index is not None + pane_base_index = int(pane_base_index) + for p_index, p in enumerate(w.panes, start=pane_base_index): + assert p.index is not None + assert int(p_index) == int(p.index) + + # pane-base-index start at base-index, pane_paths always start + # at 0 since python list. + pane_path = pane_paths[p_index - pane_base_index] + + def f(pane_path: str, p: Pane) -> bool: + p.refresh() + return p.pane_current_path == pane_path + + f_ = functools.partial(f, pane_path=pane_path, p=p) + + assert retry_until(f_) + + +def test_window_index( + session: Session, +) -> None: + """Test window_index respected by workspace builder.""" + proc = session.cmd("show-option", "-gv", "base-index") + base_index = int(proc.stdout[0]) + name_index_map = {"zero": 0 + base_index, "one": 1 + base_index, "five": 5} + + workspace = ConfigReader._from_file( + test_utils.get_workspace_file("workspace/builder/window_index.yaml"), + ) + workspace = loader.expand(workspace) + workspace = loader.trickle(workspace) + + builder = WorkspaceBuilder(session_config=workspace, server=session.server) + + for window, _ in builder.iter_create_windows(session): + expected_index = name_index_map[window.window_name] + assert int(window.window_index) == expected_index + + +def test_before_script_throw_error_if_retcode_error( + server: Server, +) -> None: + """Test tmuxp configuration before_script when command fails.""" + config_script_fails = test_utils.read_workspace_file( + "workspace/builder/config_script_fails.yaml", + ) + yaml_workspace = config_script_fails.format( + script_failed=FIXTURE_PATH / "script_failed.sh", + ) + + workspace = ConfigReader._load(fmt="yaml", content=yaml_workspace) + workspace = loader.expand(workspace) + workspace = loader.trickle(workspace) + + builder = WorkspaceBuilder(session_config=workspace, server=server) + + with temp_session(server) as sess: + session_name = sess.name + assert session_name is not None + + with pytest.raises(exc.BeforeLoadScriptError): + builder.build(session=sess) + + result = server.has_session(session_name) + assert not result, "Kills session if before_script exits with errcode" + + +def test_before_script_throw_error_if_file_not_exists( + server: Server, +) -> None: + """Test tmuxp configuration before_script when script does not exist.""" + config_script_not_exists = test_utils.read_workspace_file( + "workspace/builder/config_script_not_exists.yaml", + ) + yaml_workspace = config_script_not_exists.format( + script_not_exists=FIXTURE_PATH / "script_not_exists.sh", + ) + workspace = ConfigReader._load(fmt="yaml", content=yaml_workspace) + workspace = loader.expand(workspace) + workspace = loader.trickle(workspace) + + builder = WorkspaceBuilder(session_config=workspace, server=server) + + with temp_session(server) as session: + session_name = session.name + + assert session_name is not None + temp_session_exists = server.has_session(session_name) + assert temp_session_exists + with pytest.raises((exc.BeforeLoadScriptNotExists, OSError)) as excinfo: + builder.build(session=session) + excinfo.match(r"No such file or directory") + result = server.has_session(session_name) + assert not result, "Kills session if before_script doesn't exist" + + +def test_before_script_true_if_test_passes( + server: Server, +) -> None: + """Test tmuxp configuration before_script when command succeeds.""" + config_script_completes = test_utils.read_workspace_file( + "workspace/builder/config_script_completes.yaml", + ) + script_complete_sh = FIXTURE_PATH / "script_complete.sh" + assert script_complete_sh.exists() + + yaml_workspace = config_script_completes.format(script_complete=script_complete_sh) + workspace = ConfigReader._load(fmt="yaml", content=yaml_workspace) + workspace = loader.expand(workspace) + workspace = loader.trickle(workspace) + + builder = WorkspaceBuilder(session_config=workspace, server=server) + + with temp_session(server) as session: + builder.build(session=session) + + +def test_before_script_true_if_test_passes_with_args( + server: Server, +) -> None: + """Test tmuxp configuration before_script when command passes w/ args.""" + config_script_completes = test_utils.read_workspace_file( + "workspace/builder/config_script_completes.yaml", + ) + script_complete_sh = FIXTURE_PATH / "script_complete.sh" + assert script_complete_sh.exists() + + yaml_workspace = config_script_completes.format(script_complete=script_complete_sh) + + workspace = ConfigReader._load(fmt="yaml", content=yaml_workspace) + workspace = loader.expand(workspace) + workspace = loader.trickle(workspace) + + builder = WorkspaceBuilder(session_config=workspace, server=server) + + with temp_session(server) as session: + builder.build(session=session) + + +def test_plugin_system_before_workspace_builder( + monkeypatch_plugin_test_packages: None, + session: Session, +) -> None: + """Test tmuxp configuration plugin hook before workspace builder starts.""" + workspace = ConfigReader._from_file( + path=test_utils.get_workspace_file("workspace/builder/plugin_bwb.yaml"), + ) + workspace = loader.expand(workspace) + + builder = WorkspaceBuilder( + session_config=workspace, + plugins=load_plugins(workspace), + server=session.server, + ) + assert len(builder.plugins) > 0 + + builder.build(session=session) + + proc = session.cmd("display-message", "-p", "'#S'") + assert proc.stdout[0] == "'plugin_test_bwb'" + + +def test_plugin_system_on_window_create( + monkeypatch_plugin_test_packages: None, + session: Session, +) -> None: + """Test tmuxp configuration plugin hooks work on window creation.""" + workspace = ConfigReader._from_file( + path=test_utils.get_workspace_file("workspace/builder/plugin_owc.yaml"), + ) + workspace = loader.expand(workspace) + + builder = WorkspaceBuilder( + session_config=workspace, + plugins=load_plugins(workspace), + server=session.server, + ) + assert len(builder.plugins) > 0 + + builder.build(session=session) + + proc = session.cmd("display-message", "-p", "'#W'") + assert proc.stdout[0] == "'plugin_test_owc'" + + +def test_plugin_system_after_window_finished( + monkeypatch_plugin_test_packages: None, + session: Session, +) -> None: + """Test tmuxp configuration plugin hooks work after windows created.""" + workspace = ConfigReader._from_file( + path=test_utils.get_workspace_file("workspace/builder/plugin_awf.yaml"), + ) + workspace = loader.expand(workspace) + + builder = WorkspaceBuilder( + session_config=workspace, + plugins=load_plugins(workspace), + server=session.server, + ) + assert len(builder.plugins) > 0 + + builder.build(session=session) + + proc = session.cmd("display-message", "-p", "'#W'") + assert proc.stdout[0] == "'plugin_test_awf'" + + +def test_plugin_system_on_window_create_multiple_windows( + session: Session, +) -> None: + """Test tmuxp configuration plugin hooks work on windows creation.""" + workspace = ConfigReader._from_file( + path=test_utils.get_workspace_file( + "workspace/builder/plugin_owc_multiple_windows.yaml", + ), + ) + workspace = loader.expand(workspace) + + builder = WorkspaceBuilder( + session_config=workspace, + plugins=load_plugins(workspace), + server=session.server, + ) + assert len(builder.plugins) > 0 + + builder.build(session=session) + + proc = session.cmd("list-windows", "-F", "'#W'") + assert "'plugin_test_owc_mw'" in proc.stdout + assert "'plugin_test_owc_mw_2'" in proc.stdout + + +def test_plugin_system_after_window_finished_multiple_windows( + monkeypatch_plugin_test_packages: None, + session: Session, +) -> None: + """Test tmuxp configuration plugin hooks work after windows created.""" + workspace = ConfigReader._from_file( + path=test_utils.get_workspace_file( + "workspace/builder/plugin_awf_multiple_windows.yaml", + ), + ) + workspace = loader.expand(workspace) + + builder = WorkspaceBuilder( + session_config=workspace, + plugins=load_plugins(workspace), + server=session.server, + ) + assert len(builder.plugins) > 0 + + builder.build(session=session) + + proc = session.cmd("list-windows", "-F", "'#W'") + assert "'plugin_test_awf_mw'" in proc.stdout + assert "'plugin_test_awf_mw_2'" in proc.stdout + + +def test_plugin_system_multiple_plugins( + monkeypatch_plugin_test_packages: None, + session: Session, +) -> None: + """Test tmuxp plugin system works with multiple plugins.""" + workspace = ConfigReader._from_file( + path=test_utils.get_workspace_file( + "workspace/builder/plugin_multiple_plugins.yaml", + ), + ) + workspace = loader.expand(workspace) + + builder = WorkspaceBuilder( + session_config=workspace, + plugins=load_plugins(workspace), + server=session.server, + ) + assert len(builder.plugins) > 0 + + builder.build(session=session) + + # Drop through to the before_script plugin hook + proc = session.cmd("display-message", "-p", "'#S'") + assert proc.stdout[0] == "'plugin_test_bwb'" + + # Drop through to the after_window_finished. This won't succeed + # unless on_window_create succeeds because of how the test plugin + # override methods are currently written + proc = session.cmd("display-message", "-p", "'#W'") + assert proc.stdout[0] == "'mp_test_awf'" + + +def test_load_configs_same_session( + server: Server, +) -> None: + """Test tmuxp configuration can be loaded into same session.""" + workspace = ConfigReader._from_file( + path=test_utils.get_workspace_file("workspace/builder/three_windows.yaml"), + ) + + builder = WorkspaceBuilder(session_config=workspace, server=server) + builder.build() + + assert len(server.sessions) == 1 + assert len(server.sessions[0].windows) == 3 + + workspace = ConfigReader._from_file( + path=test_utils.get_workspace_file("workspace/builder/two_windows.yaml"), + ) + + builder = WorkspaceBuilder(session_config=workspace, server=server) + builder.build() + assert len(server.sessions) == 2 + assert len(server.sessions[1].windows) == 2 + + workspace = ConfigReader._from_file( + path=test_utils.get_workspace_file("workspace/builder/two_windows.yaml"), + ) + + builder = WorkspaceBuilder(session_config=workspace, server=server) + builder.build(server.sessions[1], True) + + assert len(server.sessions) == 2 + assert len(server.sessions[1].windows) == 4 + + +def test_load_configs_separate_sessions( + server: Server, +) -> None: + """Test workspace builder can load configuration in separate session.""" + workspace = ConfigReader._from_file( + path=test_utils.get_workspace_file("workspace/builder/three_windows.yaml"), + ) + + builder = WorkspaceBuilder(session_config=workspace, server=server) + builder.build() + + assert len(server.sessions) == 1 + assert len(server.sessions[0].windows) == 3 + + workspace = ConfigReader._from_file( + path=test_utils.get_workspace_file("workspace/builder/two_windows.yaml"), + ) + + builder = WorkspaceBuilder(session_config=workspace, server=server) + builder.build() + + assert len(server.sessions) == 2 + assert len(server.sessions[0].windows) == 3 + assert len(server.sessions[1].windows) == 2 + + +def test_find_current_active_pane( + server: Server, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Tests workspace builder can find the current active pane (and session).""" + workspace = ConfigReader._from_file( + path=test_utils.get_workspace_file("workspace/builder/three_windows.yaml"), + ) + + builder = WorkspaceBuilder(session_config=workspace, server=server) + builder.build() + + workspace = ConfigReader._from_file( + path=test_utils.get_workspace_file("workspace/builder/two_windows.yaml"), + ) + + builder = WorkspaceBuilder(session_config=workspace, server=server) + builder.build() + + assert len(server.sessions) == 2 + + # Assign an active pane to the session + second_session = server.sessions[1] + first_pane_on_second_session_id = second_session.windows[0].panes[0].pane_id + + assert first_pane_on_second_session_id is not None + monkeypatch.setenv("TMUX_PANE", first_pane_on_second_session_id) + + builder = WorkspaceBuilder(session_config=workspace, server=server) + + assert builder.find_current_attached_session() == second_session + + +class WorkspaceEnterFixture(t.NamedTuple): + """Test fixture for workspace enter behavior verification.""" + + test_id: str + yaml: str + output: str + should_see: bool + + +WORKSPACE_ENTER_FIXTURES: list[WorkspaceEnterFixture] = [ + WorkspaceEnterFixture( + test_id="pane_enter_false_shortform", + yaml=textwrap.dedent( + """ +session_name: Should not execute +windows: +- panes: + - shell_command: echo "___$((1 + 3))___" + enter: false + """, + ), + output="___4___", + should_see=False, + ), + WorkspaceEnterFixture( + test_id="pane_enter_false_longform", + yaml=textwrap.dedent( + """ +session_name: Should not execute +windows: +- panes: + - shell_command: + - echo "___$((1 + 3))___" + enter: false + """, + ), + output="___4___", + should_see=False, + ), + WorkspaceEnterFixture( + test_id="pane_enter_default_shortform", + yaml=textwrap.dedent( + """ +session_name: Should execute +windows: +- panes: + - shell_command: echo "___$((1 + 3))___" + """, + ), + output="___4___", + should_see=True, + ), + WorkspaceEnterFixture( + test_id="pane_enter_default_longform", + yaml=textwrap.dedent( + """ +session_name: Should execute +windows: +- panes: + - shell_command: + - echo "___$((1 + 3))___" + """, + ), + output="___4___", + should_see=True, + ), + WorkspaceEnterFixture( + test_id="pane_command_enter_false_shortform", + yaml=textwrap.dedent( + """ +session_name: Should not execute +windows: +- panes: + - shell_command: + - cmd: echo "___$((1 + 3))___" + enter: false + """, + ), + output="___4___", + should_see=False, + ), + WorkspaceEnterFixture( # NOQA: PT014 RUF100 + test_id="pane_command_enter_false_longform", + yaml=textwrap.dedent( + """ +session_name: Should not execute +windows: +- panes: + - shell_command: + - cmd: echo "___$((1 + 3))___" + enter: false + """, + ), + output="___4___", + should_see=False, + ), + WorkspaceEnterFixture( # NOQA: PT014 RUF100 + test_id="pane_command_enter_default_shortform", + yaml=textwrap.dedent( + """ +session_name: Should execute +windows: +- panes: + - shell_command: echo "___$((1 + 3))___" + """, + ), + output="___4___", + should_see=True, + ), + WorkspaceEnterFixture( + test_id="pane_command_enter_default_longform", + yaml=textwrap.dedent( + """ +session_name: Should execute +windows: +- panes: + - shell_command: + - cmd: echo "other command" + - cmd: echo "___$((1 + 3))___" + """, + ), + output="___4___", + should_see=True, + ), +] + + +@pytest.mark.parametrize( + list(WorkspaceEnterFixture._fields), + WORKSPACE_ENTER_FIXTURES, + ids=[test.test_id for test in WORKSPACE_ENTER_FIXTURES], +) +def test_load_workspace_enter( + tmp_path: pathlib.Path, + server: Server, + monkeypatch: pytest.MonkeyPatch, + test_id: str, + yaml: str, + output: str, + should_see: bool, +) -> None: + """Test workspace enters commands to panes in tmuxp configuration.""" + yaml_workspace = tmp_path / "simple.yaml" + yaml_workspace.write_text(yaml, encoding="utf-8") + workspace = ConfigReader._from_file(yaml_workspace) + workspace = loader.expand(workspace) + workspace = loader.trickle(workspace) + builder = WorkspaceBuilder(session_config=workspace, server=server) + builder.build() + + session = builder.session + assert isinstance(session, Session) + pane = session.active_pane + assert isinstance(pane, Pane) + + def fn() -> bool: + captured_pane = "\n".join(pane.capture_pane()) + + if should_see: + return output in captured_pane + return output not in captured_pane + + assert retry_until( + fn, + 1, + ), f"Should{' ' if should_see else 'not '} output in captured pane" + + +class WorkspaceSleepFixture(t.NamedTuple): + """Test fixture for workspace sleep behavior verification.""" + + test_id: str + yaml: str + sleep: float + output: str + + +WORKSPACE_SLEEP_FIXTURES: list[WorkspaceSleepFixture] = [ + WorkspaceSleepFixture( + test_id="command_level_sleep_shortform", + yaml=textwrap.dedent( + """ +session_name: Should not execute +windows: +- panes: + - shell_command: + - cmd: echo "___$((1 + 5))___" + sleep_before: .15 + - cmd: echo "___$((1 + 3))___" + sleep_before: .35 + """, + ), + sleep=0.5, + output="___4___", + ), + WorkspaceSleepFixture( + test_id="command_level_pane_sleep_longform", + yaml=textwrap.dedent( + """ +session_name: Should not execute +windows: +- panes: + - shell_command: + - cmd: echo "___$((1 + 5))___" + sleep_before: 1 + - cmd: echo "___$((1 + 3))___" + sleep_before: .25 + """, + ), + sleep=1.25, + output="___4___", + ), + WorkspaceSleepFixture( + test_id="pane_sleep_shortform", + yaml=textwrap.dedent( + """ +session_name: Should not execute +windows: +- panes: + - shell_command: + - cmd: echo "___$((1 + 3))___" + sleep_before: .5 + """, + ), + sleep=0.5, + output="___4___", + ), + WorkspaceSleepFixture( + test_id="pane_sleep_longform", + yaml=textwrap.dedent( + """ +session_name: Should not execute +windows: +- panes: + - shell_command: + - cmd: echo "___$((1 + 3))___" + sleep_before: 1 + """, + ), + sleep=1, + output="___4___", + ), + WorkspaceSleepFixture( + test_id="shell_before_before_command_level", + yaml=textwrap.dedent( + """ +session_name: Should not execute +shell_command_before: + - cmd: echo "sleeping before" + sleep_before: .5 +windows: +- panes: + - echo "___$((1 + 3))___" + """, + ), + sleep=0.5, + output="___4___", + ), +] + + +@pytest.mark.parametrize( + list(WorkspaceSleepFixture._fields), + WORKSPACE_SLEEP_FIXTURES, + ids=[test.test_id for test in WORKSPACE_SLEEP_FIXTURES], +) +@pytest.mark.flaky(reruns=3) +def test_load_workspace_sleep( + tmp_path: pathlib.Path, + server: Server, + monkeypatch: pytest.MonkeyPatch, + test_id: str, + yaml: str, + sleep: float, + output: str, +) -> None: + """Test sleep commands in tmuxp configuration.""" + yaml_workspace = tmp_path / "simple.yaml" + yaml_workspace.write_text(yaml, encoding="utf-8") + workspace = ConfigReader._from_file(yaml_workspace) + workspace = loader.expand(workspace) + workspace = loader.trickle(workspace) + builder = WorkspaceBuilder(session_config=workspace, server=server) + + start_time = time.process_time() + + builder.build() + time.sleep(0.5) + session = builder.session + assert isinstance(builder.session, Session) + assert session is not None + pane = session.active_pane + assert isinstance(pane, Pane) + + assert pane is not None + + assert not isinstance(pane.capture_pane, str) + assert callable(pane.capture_pane) + + while (time.process_time() - start_time) * 1000 < sleep: + captured_pane = "\n".join(pane.capture_pane()) + + assert output not in captured_pane + time.sleep(0.1) + + captured_pane = "\n".join(pane.capture_pane()) + assert output in captured_pane + + +def test_first_pane_start_directory(session: Session, tmp_path: pathlib.Path) -> None: + """Test the first pane start_directory sticks.""" + yaml_workspace = test_utils.get_workspace_file( + "workspace/builder/first_pane_start_directory.yaml", + ) + + workspace = ConfigReader._from_file(yaml_workspace) + workspace = loader.expand(workspace) + workspace = loader.trickle(workspace) + + builder = WorkspaceBuilder(session_config=workspace, server=session.server) + builder.build(session=session) + + assert session == builder.session + dirs = ["/usr", "/etc"] + + assert session.windows + window = session.windows[0] + for path, p in zip(dirs, window.panes): + + def f(path: str, p: Pane) -> bool: + pane_path = p.pane_current_path + return (pane_path is not None and path in pane_path) or pane_path == path + + f_ = functools.partial(f, path=path, p=p) + + # handle case with OS X adding /private/ to /tmp/ paths + assert retry_until(f_) + + +@pytest.mark.skipif( + has_lt_version("2.9"), + reason="needs option introduced in tmux >= 2.9", +) +def test_layout_main_horizontal(session: Session) -> None: + """Test that tmux's main-horizontal layout is used when specified.""" + yaml_workspace = test_utils.get_workspace_file("workspace/builder/three_pane.yaml") + workspace = ConfigReader._from_file(path=yaml_workspace) + + builder = WorkspaceBuilder(session_config=workspace, server=session.server) + builder.build(session=session) + + assert session.windows + window = session.windows[0] + + assert len(window.panes) == 3 + main_horizontal_pane, *panes = window.panes + + def height(p: Pane) -> int: + return int(p.pane_height) if p.pane_height is not None else 0 + + def width(p: Pane) -> int: + return int(p.pane_width) if p.pane_width is not None else 0 + + main_horizontal_pane_height = height(main_horizontal_pane) + pane_heights = [height(pane) for pane in panes] + # TODO: When libtmux has new pane formatters added, use that to detect top / bottom + assert all( + main_horizontal_pane_height != pane_height for pane_height in pane_heights + ), "The top row should not be the same size as the bottom row (even though it can)" + assert all(pane_heights[0] == pane_height for pane_height in pane_heights), ( + "The bottom row should be uniform height" + ) + assert width(main_horizontal_pane) > width(panes[0]) + + def is_almost_equal(x: int, y: int) -> bool: + return abs(x - y) <= 1 + + assert is_almost_equal(height(panes[0]), height(panes[1])) + assert is_almost_equal(width(panes[0]), width(panes[1])) + + +class DefaultSizeNamespaceFixture(t.NamedTuple): + """Pytest fixture default-size option in tmuxp workspace builder.""" + + # pytest parametrize needs a unique id for each fixture + test_id: str + + # test params + TMUXP_DEFAULT_SIZE: str | None + raises: bool + confoverrides: dict[str, t.Any] + + +DEFAULT_SIZE_FIXTURES = [ + DefaultSizeNamespaceFixture( + test_id="default-behavior", + TMUXP_DEFAULT_SIZE=None, + raises=False, + confoverrides={}, + ), + DefaultSizeNamespaceFixture( + test_id="v1.13.1 default-size-breaks", + TMUXP_DEFAULT_SIZE=None, + raises=True, + confoverrides={"options": {"default-size": "80x24"}}, + ), + DefaultSizeNamespaceFixture( + test_id="v1.13.1-option-workaround", + TMUXP_DEFAULT_SIZE=None, + raises=False, + confoverrides={"options": {"default-size": "800x600"}}, + ), +] + + +@pytest.mark.parametrize( + DefaultSizeNamespaceFixture._fields, + DEFAULT_SIZE_FIXTURES, + ids=[f.test_id for f in DEFAULT_SIZE_FIXTURES], +) +@pytest.mark.skipif(has_lt_version("2.9"), reason="default-size only applies there") +def test_issue_800_default_size_many_windows( + server: Server, + monkeypatch: pytest.MonkeyPatch, + test_id: str, + TMUXP_DEFAULT_SIZE: str | None, + raises: bool, + confoverrides: dict[str, t.Any], +) -> None: + """Recreate default-size issue. + + v1.13.1 added a default-size, but this can break building workspaces with + a lot of panes. + + See also: https://github.com/tmux-python/tmuxp/issues/800 + + 2024-04-07: This test isn't being used as of this date, as default-size is totally + unused in builder.py. + """ + monkeypatch.setenv("ROWS", "36") + + yaml_workspace = test_utils.get_workspace_file( + "regressions/issue_800_default_size_many_windows.yaml", + ) + + workspace = ConfigReader._from_file(yaml_workspace) + workspace = loader.expand(workspace) + workspace = loader.trickle(workspace) + + if isinstance(confoverrides, dict): + for k, v in confoverrides.items(): + workspace[k] = v + + builder = WorkspaceBuilder(session_config=workspace, server=server) + + if raises: + with pytest.raises( + ( + LibTmuxException, + exc.TmuxpException, + exc.EmptyWorkspaceException, + ObjectDoesNotExist, + ), + ): + builder.build() + + assert builder is not None + assert builder.session is not None + assert isinstance(builder.session, Session) + assert callable(builder.session.kill) + builder.session.kill() + + with pytest.raises(libtmux.exc.LibTmuxException, match="no space for new pane"): + builder.build() + return + + builder.build() + assert len(server.sessions) == 1 diff --git a/tests/workspace/test_config.py b/tests/workspace/test_config.py new file mode 100644 index 00000000000..02ebcf5ffa2 --- /dev/null +++ b/tests/workspace/test_config.py @@ -0,0 +1,332 @@ +"""Test for tmuxp configuration import, inlining, expanding and export.""" + +from __future__ import annotations + +import pathlib +import typing as t + +import pytest + +from tests.constants import EXAMPLE_PATH +from tmuxp import exc +from tmuxp._internal.config_reader import ConfigReader +from tmuxp.workspace import loader, validation + +if t.TYPE_CHECKING: + from tests.fixtures.structures import WorkspaceTestData + + +def load_workspace(path: str | pathlib.Path) -> dict[str, t.Any]: + """Load tmuxp workspace configuration from file.""" + return ConfigReader._from_file( + pathlib.Path(path) if isinstance(path, str) else path, + ) + + +def test_export_json( + tmp_path: pathlib.Path, + config_fixture: WorkspaceTestData, +) -> None: + """Test exporting configuration dictionary to JSON.""" + json_workspace_file = tmp_path / "config.json" + + configparser = ConfigReader(config_fixture.sample_workspace.sample_workspace_dict) + + json_workspace_data = configparser.dump("json", indent=2) + + json_workspace_file.write_text(json_workspace_data, encoding="utf-8") + + new_workspace_data = ConfigReader._from_file(path=json_workspace_file) + assert config_fixture.sample_workspace.sample_workspace_dict == new_workspace_data + + +def test_workspace_expand1(config_fixture: WorkspaceTestData) -> None: + """Expand shell commands from string to list.""" + test_workspace = loader.expand(config_fixture.expand1.before_workspace) + assert test_workspace == config_fixture.expand1.after_workspace() + + +def test_workspace_expand2(config_fixture: WorkspaceTestData) -> None: + """Expand shell commands from string to list.""" + unexpanded_dict = ConfigReader._load( + fmt="yaml", + content=config_fixture.expand2.unexpanded_yaml(), + ) + expanded_dict = ConfigReader._load( + fmt="yaml", + content=config_fixture.expand2.expanded_yaml(), + ) + assert loader.expand(unexpanded_dict) == expanded_dict + + +"""Test config inheritance for the nested 'start_command'.""" + +inheritance_workspace_before = { + "session_name": "sample workspace", + "start_directory": "/", + "windows": [ + { + "window_name": "editor", + "start_directory": "~", + "panes": [{"shell_command": ["vim"]}, {"shell_command": ['cowsay "hey"']}], + "layout": "main-vertical", + }, + { + "window_name": "logging", + "panes": [{"shell_command": ["tail -F /var/log/syslog"]}], + }, + {"window_name": "shufu", "panes": [{"shell_command": ["htop"]}]}, + {"options": {"automatic-rename": True}, "panes": [{"shell_command": ["htop"]}]}, + ], +} + +inheritance_workspace_after = { + "session_name": "sample workspace", + "start_directory": "/", + "windows": [ + { + "window_name": "editor", + "start_directory": "~", + "panes": [{"shell_command": ["vim"]}, {"shell_command": ['cowsay "hey"']}], + "layout": "main-vertical", + }, + { + "window_name": "logging", + "panes": [{"shell_command": ["tail -F /var/log/syslog"]}], + }, + {"window_name": "shufu", "panes": [{"shell_command": ["htop"]}]}, + {"options": {"automatic-rename": True}, "panes": [{"shell_command": ["htop"]}]}, + ], +} + + +def test_inheritance_workspace() -> None: + """TODO: Create a test to verify workspace config inheritance to object tree.""" + workspace = inheritance_workspace_before + + # TODO: Look at verifying window_start_directory + # if 'start_directory' in workspace: + # session_start_directory = workspace['start_directory'] + # else: + # session_start_directory = None + + # for windowconfitem in workspace['windows']: + # window_start_directory = None + # + # if 'start_directory' in windowconfitem: + # window_start_directory = windowconfitem['start_directory'] + # elif session_start_directory: + # window_start_directory = session_start_directory + # + # for paneconfitem in windowconfitem['panes']: + # if 'start_directory' in paneconfitem: + # pane_start_directory = paneconfitem['start_directory'] + # elif window_start_directory: + # paneconfitem['start_directory'] = window_start_directory + # elif session_start_directory: + # paneconfitem['start_directory'] = session_start_directory + + assert workspace == inheritance_workspace_after + + +def test_shell_command_before(config_fixture: WorkspaceTestData) -> None: + """Config inheritance for the nested 'start_command'.""" + test_workspace = config_fixture.shell_command_before.config_unexpanded + test_workspace = loader.expand(test_workspace) + + assert test_workspace == config_fixture.shell_command_before.config_expanded() + + test_workspace = loader.trickle(test_workspace) + assert test_workspace == config_fixture.shell_command_before.config_after() + + +def test_in_session_scope(config_fixture: WorkspaceTestData) -> None: + """Verify shell_command before_session is in session scope.""" + sconfig = ConfigReader._load( + fmt="yaml", + content=config_fixture.shell_command_before_session.before, + ) + + validation.validate_schema(sconfig) + + assert loader.expand(sconfig) == sconfig + assert loader.expand(loader.trickle(sconfig)) == ConfigReader._load( + fmt="yaml", + content=config_fixture.shell_command_before_session.expected, + ) + + +def test_trickle_relative_start_directory(config_fixture: WorkspaceTestData) -> None: + """Verify tmuxp config proliferates relative start directory to descendants.""" + test_workspace = loader.trickle(config_fixture.trickle.before) + assert test_workspace == config_fixture.trickle.expected + + +def test_trickle_window_with_no_pane_workspace() -> None: + """Verify tmuxp window config automatically infers a single pane.""" + test_yaml = """ + session_name: test_session + windows: + - window_name: test_1 + panes: + - shell_command: + - ls -l + - window_name: test_no_panes + """ + sconfig = ConfigReader._load(fmt="yaml", content=test_yaml) + validation.validate_schema(sconfig) + + assert loader.expand(loader.trickle(sconfig))["windows"][1]["panes"][0] == { + "shell_command": [], + } + + +def test_expands_blank_panes(config_fixture: WorkspaceTestData) -> None: + """Expand blank config into full form. + + Handle ``NoneType`` and 'blank':: + + # nothing, None, 'blank' + 'panes': [ + None, + 'blank' + ] + + # should be blank + 'panes': [ + 'shell_command': [] + ] + + Blank strings:: + + panes: [ + '' + ] + + # should output to: + panes: + 'shell_command': [''] + + """ + yaml_workspace_file = EXAMPLE_PATH / "blank-panes.yaml" + test_workspace = load_workspace(yaml_workspace_file) + assert loader.expand(test_workspace) == config_fixture.expand_blank.expected + + +def test_no_session_name() -> None: + """Verify exception raised when tmuxp configuration has no session name.""" + yaml_workspace = """ + - window_name: editor + panes: + shell_command: + - tail -F /var/log/syslog + start_directory: /var/log + - window_name: logging + automatic-rename: true + panes: + - shell_command: + - htop + """ + + sconfig = ConfigReader._load(fmt="yaml", content=yaml_workspace) + + with pytest.raises(exc.WorkspaceError) as excinfo: + validation.validate_schema(sconfig) + assert excinfo.match(r'requires "session_name"') + + +def test_no_windows() -> None: + """Verify exception raised when tmuxp configuration has no windows.""" + yaml_workspace = """ + session_name: test session + """ + + sconfig = ConfigReader._load(fmt="yaml", content=yaml_workspace) + + with pytest.raises(exc.WorkspaceError) as excinfo: + validation.validate_schema(sconfig) + assert excinfo.match(r'list of "windows"') + + +def test_no_window_name() -> None: + """Verify exception raised when tmuxp config missing window name.""" + yaml_workspace = """ + session_name: test session + windows: + - window_name: editor + panes: + shell_command: + - tail -F /var/log/syslog + start_directory: /var/log + - automatic-rename: true + panes: + - shell_command: + - htop + """ + + sconfig = ConfigReader._load(fmt="yaml", content=yaml_workspace) + + with pytest.raises(exc.WorkspaceError) as excinfo: + validation.validate_schema(sconfig) + assert excinfo.match('missing "window_name"') + + +def test_replaces_env_variables(monkeypatch: pytest.MonkeyPatch) -> None: + """Test loading configuration resolves environmental variables.""" + env_key = "TESTHEY92" + env_val = "HEYO1" + yaml_workspace = """ + start_directory: {TEST_VAR}/test + shell_command_before: {TEST_VAR}/test2 + before_script: {TEST_VAR}/test3 + session_name: hi - {TEST_VAR} + options: + default-command: {TEST_VAR}/lol + global_options: + default-shell: {TEST_VAR}/moo + windows: + - window_name: editor + panes: + - shell_command: + - tail -F /var/log/syslog + start_directory: /var/log + - window_name: logging @ {TEST_VAR} + automatic-rename: true + panes: + - shell_command: + - htop + """.format(TEST_VAR=f"${{{env_key}}}") + + sconfig = ConfigReader._load(fmt="yaml", content=yaml_workspace) + + monkeypatch.setenv(str(env_key), str(env_val)) + sconfig = loader.expand(sconfig) + assert f"{env_val}/test" == sconfig["start_directory"] + assert ( + f"{env_val}/test2" in sconfig["shell_command_before"]["shell_command"][0]["cmd"] + ) + assert f"{env_val}/test3" == sconfig["before_script"] + assert f"hi - {env_val}" == sconfig["session_name"] + assert f"{env_val}/moo" == sconfig["global_options"]["default-shell"] + assert f"{env_val}/lol" == sconfig["options"]["default-command"] + assert f"logging @ {env_val}" == sconfig["windows"][1]["window_name"] + + +def test_validate_plugins() -> None: + """Test validation of plugins loading via tmuxp configuration file.""" + yaml_workspace = """ + session_name: test session + plugins: tmuxp-plugin-one.plugin.TestPluginOne + windows: + - window_name: editor + panes: + shell_command: + - tail -F /var/log/syslog + start_directory: /var/log + """ + + sconfig = ConfigReader._load(fmt="yaml", content=yaml_workspace) + + with pytest.raises(exc.WorkspaceError) as excinfo: + validation.validate_schema(sconfig) + assert excinfo.match("only supports list type") diff --git a/tests/workspace/test_finder.py b/tests/workspace/test_finder.py new file mode 100644 index 00000000000..20aee8703a0 --- /dev/null +++ b/tests/workspace/test_finder.py @@ -0,0 +1,359 @@ +"""Test config file searching for tmuxp.""" + +from __future__ import annotations + +import argparse +import pathlib +import typing as t + +import pytest + +from tmuxp import cli +from tmuxp.cli.utils import tmuxp_echo +from tmuxp.workspace.finders import ( + find_workspace_file, + get_workspace_dir, + in_cwd, + in_dir, + is_pure_name, +) + +if t.TYPE_CHECKING: + import _pytest.capture + + +def test_in_dir_from_config_dir(tmp_path: pathlib.Path) -> None: + """config.in_dir() finds configs config dir.""" + cli.startup(tmp_path) + yaml_config = tmp_path / "myconfig.yaml" + yaml_config.touch() + json_config = tmp_path / "myconfig.json" + json_config.touch() + configs_found = in_dir(tmp_path) + + assert len(configs_found) == 2 + + +def test_ignore_non_configs_from_current_dir(tmp_path: pathlib.Path) -> None: + """cli.in_dir() ignore non-config from config dir.""" + cli.startup(tmp_path) + + junk_config = tmp_path / "myconfig.psd" + junk_config.touch() + conf = tmp_path / "watmyconfig.json" + conf.touch() + configs_found = in_dir(tmp_path) + assert len(configs_found) == 1 + + +def test_get_configs_cwd( + tmp_path: pathlib.Path, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """config.in_cwd() find config in shell current working directory.""" + confdir = tmp_path / "tmuxpconf2" + confdir.mkdir() + + monkeypatch.chdir(confdir) + with pathlib.Path(".tmuxp.json").open("w+b") as config1: + config1.close() + + configs_found = in_cwd() + assert len(configs_found) == 1 + assert ".tmuxp.json" in configs_found + + +class PureNameTestFixture(t.NamedTuple): + """Test fixture for verifying pure name path validation.""" + + test_id: str + path: str + expect: bool + + +PURE_NAME_TEST_FIXTURES: list[PureNameTestFixture] = [ + PureNameTestFixture( + test_id="current_dir", + path=".", + expect=False, + ), + PureNameTestFixture( + test_id="current_dir_slash", + path="./", + expect=False, + ), + PureNameTestFixture( + test_id="empty_path", + path="", + expect=False, + ), + PureNameTestFixture( + test_id="tmuxp_yaml", + path=".tmuxp.yaml", + expect=False, + ), + PureNameTestFixture( + test_id="parent_tmuxp_yaml", + path="../.tmuxp.yaml", + expect=False, + ), + PureNameTestFixture( + test_id="parent_dir", + path="../", + expect=False, + ), + PureNameTestFixture( + test_id="absolute_path", + path="/hello/world", + expect=False, + ), + PureNameTestFixture( + test_id="home_tmuxp_path", + path="~/.tmuxp/hey", + expect=False, + ), + PureNameTestFixture( + test_id="home_work_path", + path="~/work/c/tmux/", + expect=False, + ), + PureNameTestFixture( + test_id="home_work_tmuxp_yaml", + path="~/work/c/tmux/.tmuxp.yaml", + expect=False, + ), + PureNameTestFixture( + test_id="pure_name", + path="myproject", + expect=True, + ), +] + + +@pytest.mark.parametrize( + list(PureNameTestFixture._fields), + PURE_NAME_TEST_FIXTURES, + ids=[test.test_id for test in PURE_NAME_TEST_FIXTURES], +) +def test_is_pure_name( + test_id: str, + path: str, + expect: bool, +) -> None: + """Test is_pure_name() is truthy when file, not directory or config alias.""" + assert is_pure_name(path) == expect + + +def test_tmuxp_configdir_env_var( + tmp_path: pathlib.Path, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Tests get_workspace_dir() when TMUXP_CONFIGDIR is set.""" + monkeypatch.setenv("TMUXP_CONFIGDIR", str(tmp_path)) + + assert get_workspace_dir() == str(tmp_path) + + +def test_tmuxp_configdir_xdg_config_dir( + tmp_path: pathlib.Path, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Test get_workspace_dir() when XDG_CONFIG_HOME is set.""" + monkeypatch.setenv("XDG_CONFIG_HOME", str(tmp_path)) + tmux_dir = tmp_path / "tmuxp" + tmux_dir.mkdir() + + assert get_workspace_dir() == str(tmux_dir) + + +@pytest.fixture +def homedir(tmp_path: pathlib.Path) -> pathlib.Path: + """Fixture to ensure and return a home directory.""" + home = tmp_path / "home" + home.mkdir() + return home + + +@pytest.fixture +def configdir(homedir: pathlib.Path) -> pathlib.Path: + """Fixture to ensure user directory for tmuxp and return it, via homedir fixture.""" + conf = homedir / ".tmuxp" + conf.mkdir() + return conf + + +@pytest.fixture +def projectdir(homedir: pathlib.Path) -> pathlib.Path: + """Fixture to ensure and return an example project dir.""" + proj = homedir / "work" / "project" + proj.mkdir(parents=True) + return proj + + +def test_resolve_dot( + tmp_path: pathlib.Path, + homedir: pathlib.Path, + configdir: pathlib.Path, + projectdir: pathlib.Path, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Test find_workspace_file() resolves dots as relative / current directory.""" + monkeypatch.setenv("HOME", str(homedir)) + monkeypatch.setenv("XDG_CONFIG_HOME", str(homedir / ".config")) + + tmuxp_conf_path = projectdir / ".tmuxp.yaml" + tmuxp_conf_path.touch() + user_config_name = "myconfig" + user_config = configdir / f"{user_config_name}.yaml" + user_config.touch() + + project_config = tmuxp_conf_path + + monkeypatch.chdir(projectdir) + + expect = str(project_config) + assert find_workspace_file(".") == expect + assert find_workspace_file("./") == expect + assert find_workspace_file("") == expect + assert find_workspace_file("../project") == expect + assert find_workspace_file("../project/") == expect + assert find_workspace_file(".tmuxp.yaml") == expect + assert find_workspace_file(f"../../.tmuxp/{user_config_name}.yaml") == str( + user_config, + ) + assert find_workspace_file("myconfig") == str(user_config) + assert find_workspace_file("~/.tmuxp/myconfig.yaml") == str(user_config) + + with pytest.raises(FileNotFoundError): + find_workspace_file(".tmuxp.json") + with pytest.raises(FileNotFoundError): + find_workspace_file(".tmuxp.ini") + with pytest.raises(FileNotFoundError): + find_workspace_file("../") + with pytest.raises(FileNotFoundError): + find_workspace_file("mooooooo") + + monkeypatch.chdir(homedir) + + expect = str(project_config) + assert find_workspace_file("work/project") == expect + assert find_workspace_file("work/project/") == expect + assert find_workspace_file("./work/project") == expect + assert find_workspace_file("./work/project/") == expect + assert find_workspace_file(f".tmuxp/{user_config_name}.yaml") == str(user_config) + assert find_workspace_file(f"./.tmuxp/{user_config_name}.yaml") == str( + user_config, + ) + assert find_workspace_file("myconfig") == str(user_config) + assert find_workspace_file("~/.tmuxp/myconfig.yaml") == str(user_config) + + with pytest.raises(FileNotFoundError): + find_workspace_file("") + with pytest.raises(FileNotFoundError): + find_workspace_file(".") + with pytest.raises(FileNotFoundError): + find_workspace_file(".tmuxp.yaml") + with pytest.raises(FileNotFoundError): + find_workspace_file("../") + with pytest.raises(FileNotFoundError): + find_workspace_file("mooooooo") + + monkeypatch.chdir(configdir) + + expect = str(project_config) + assert find_workspace_file("../work/project") == expect + assert find_workspace_file("../../home/work/project") == expect + assert find_workspace_file("../work/project/") == expect + assert find_workspace_file(f"{user_config_name}.yaml") == str(user_config) + assert find_workspace_file(f"./{user_config_name}.yaml") == str(user_config) + assert find_workspace_file("myconfig") == str(user_config) + assert find_workspace_file("~/.tmuxp/myconfig.yaml") == str(user_config) + + with pytest.raises(FileNotFoundError): + find_workspace_file("") + with pytest.raises(FileNotFoundError): + find_workspace_file(".") + with pytest.raises(FileNotFoundError): + find_workspace_file(".tmuxp.yaml") + with pytest.raises(FileNotFoundError): + find_workspace_file("../") + with pytest.raises(FileNotFoundError): + find_workspace_file("mooooooo") + + monkeypatch.chdir(tmp_path) + + expect = str(project_config) + assert find_workspace_file("home/work/project") == expect + assert find_workspace_file("./home/work/project/") == expect + assert find_workspace_file(f"home/.tmuxp/{user_config_name}.yaml") == str( + user_config, + ) + assert find_workspace_file(f"./home/.tmuxp/{user_config_name}.yaml") == str( + user_config, + ) + assert find_workspace_file("myconfig") == str(user_config) + assert find_workspace_file("~/.tmuxp/myconfig.yaml") == str(user_config) + + with pytest.raises(FileNotFoundError): + find_workspace_file("") + with pytest.raises(FileNotFoundError): + find_workspace_file(".") + with pytest.raises(FileNotFoundError): + find_workspace_file(".tmuxp.yaml") + with pytest.raises(FileNotFoundError): + find_workspace_file("../") + with pytest.raises(FileNotFoundError): + find_workspace_file("mooooooo") + + +def test_find_workspace_file_arg( + homedir: pathlib.Path, + configdir: pathlib.Path, + projectdir: pathlib.Path, + monkeypatch: pytest.MonkeyPatch, + capsys: pytest.CaptureFixture[str], +) -> None: + """Test find_workspace_file() via file path.""" + parser = argparse.ArgumentParser() + parser.add_argument("workspace_file", type=str) + + def config_cmd(workspace_file: str) -> None: + tmuxp_echo(find_workspace_file(workspace_file, workspace_dir=configdir)) + + monkeypatch.setenv("HOME", str(homedir)) + tmuxp_config_path = projectdir / ".tmuxp.yaml" + tmuxp_config_path.touch() + user_config_name = "myconfig" + user_config = configdir / f"{user_config_name}.yaml" + user_config.touch() + + project_config = projectdir / ".tmuxp.yaml" + + def check_cmd(config_arg: str) -> _pytest.capture.CaptureResult[str]: + args = parser.parse_args([config_arg]) + config_cmd(workspace_file=args.workspace_file) + return capsys.readouterr() + + monkeypatch.chdir(projectdir) + expect = str(project_config) + assert expect in check_cmd(".").out + assert expect in check_cmd("./").out + assert expect in check_cmd("").out + assert expect in check_cmd("../project").out + assert expect in check_cmd("../project/").out + assert expect in check_cmd(".tmuxp.yaml").out + assert str(user_config) in check_cmd(f"../../.tmuxp/{user_config_name}.yaml").out + assert user_config.stem in check_cmd("myconfig").out + assert str(user_config) in check_cmd("~/.tmuxp/myconfig.yaml").out + + with pytest.raises(FileNotFoundError, match="file not found"): + assert "file not found" in check_cmd(".tmuxp.json").err + with pytest.raises(FileNotFoundError, match="file not found"): + assert "file not found" in check_cmd(".tmuxp.ini").err + with pytest.raises(FileNotFoundError, match="No tmuxp files found"): + assert "No tmuxp files found" in check_cmd("../").err + with pytest.raises( + FileNotFoundError, + match="workspace-file not found in workspace dir", + ): + assert "workspace-file not found in workspace dir" in check_cmd("moo").err diff --git a/tests/workspace/test_freezer.py b/tests/workspace/test_freezer.py new file mode 100644 index 00000000000..42fa6cc581c --- /dev/null +++ b/tests/workspace/test_freezer.py @@ -0,0 +1,108 @@ +"""Tests tmux session freezing functionality for tmuxp.""" + +from __future__ import annotations + +import time +import typing + +from tests.fixtures import utils as test_utils +from tmuxp._internal.config_reader import ConfigReader +from tmuxp.workspace import freezer, validation +from tmuxp.workspace.builder import WorkspaceBuilder + +if typing.TYPE_CHECKING: + import pathlib + + from libtmux.session import Session + + from tests.fixtures.structures import WorkspaceTestData + + +def test_freeze_config(session: Session) -> None: + """Test freezing a tmux session.""" + session_config = ConfigReader._from_file( + test_utils.get_workspace_file("workspace/freezer/sample_workspace.yaml"), + ) + + builder = WorkspaceBuilder(session_config=session_config, server=session.server) + builder.build(session=session) + assert session == builder.session + + time.sleep(0.50) + + session = session + new_config = freezer.freeze(session) + + validation.validate_schema(new_config) + + # These should dump without an error + ConfigReader._dump(fmt="json", content=new_config) + ConfigReader._dump(fmt="yaml", content=new_config) + + # Inline configs should also dump without an error + compact_config = freezer.inline(new_config) + + ConfigReader._dump(fmt="json", content=compact_config) + ConfigReader._dump(fmt="yaml", content=compact_config) + + +"""Tests for :meth:`freezer.inline()`.""" + +ibefore_workspace = { # inline config + "session_name": "sample workspace", + "start_directory": "~", + "windows": [ + { + "shell_command": ["top"], + "window_name": "editor", + "panes": [{"shell_command": ["vim"]}, {"shell_command": ['cowsay "hey"']}], + "layout": "main-vertical", + }, + { + "window_name": "logging", + "panes": [{"shell_command": ["tail -F /var/log/syslog"]}], + }, + {"options": {"automatic-rename": True}, "panes": [{"shell_command": ["htop"]}]}, + ], +} + +iafter_workspace = { + "session_name": "sample workspace", + "start_directory": "~", + "windows": [ + { + "shell_command": "top", + "window_name": "editor", + "panes": ["vim", 'cowsay "hey"'], + "layout": "main-vertical", + }, + {"window_name": "logging", "panes": ["tail -F /var/log/syslog"]}, + {"options": {"automatic-rename": True}, "panes": ["htop"]}, + ], +} + + +def test_inline_workspace() -> None: + """:meth:`freezer.inline()` shell commands list to string.""" + test_workspace = freezer.inline(ibefore_workspace) + assert test_workspace == iafter_workspace + + +def test_export_yaml( + tmp_path: pathlib.Path, + config_fixture: WorkspaceTestData, +) -> None: + """Test exporting a frozen tmux session to YAML.""" + yaml_workspace_file = tmp_path / "config.yaml" + + sample_workspace = freezer.inline( + config_fixture.sample_workspace.sample_workspace_dict, + ) + configparser = ConfigReader(sample_workspace) + + yaml_workspace_data = configparser.dump("yaml", indent=2, default_flow_style=False) + + yaml_workspace_file.write_text(yaml_workspace_data, encoding="utf-8") + + new_workspace_data = ConfigReader._from_file(yaml_workspace_file) + assert config_fixture.sample_workspace.sample_workspace_dict == new_workspace_data diff --git a/tests/workspace/test_import_teamocil.py b/tests/workspace/test_import_teamocil.py new file mode 100644 index 00000000000..7de727684be --- /dev/null +++ b/tests/workspace/test_import_teamocil.py @@ -0,0 +1,141 @@ +"""Test for tmuxp teamocil configuration.""" + +from __future__ import annotations + +import typing as t + +import pytest + +from tests.fixtures import import_teamocil as fixtures +from tmuxp._internal import config_reader +from tmuxp.workspace import importers, validation + + +class TeamocilConfigTestFixture(t.NamedTuple): + """Test fixture for teamocil config conversion tests.""" + + test_id: str + teamocil_yaml: str + teamocil_dict: dict[str, t.Any] + tmuxp_dict: dict[str, t.Any] + + +TEAMOCIL_CONFIG_TEST_FIXTURES: list[TeamocilConfigTestFixture] = [ + TeamocilConfigTestFixture( + test_id="test1", + teamocil_yaml=fixtures.test1.teamocil_yaml, + teamocil_dict=fixtures.test1.teamocil_conf, + tmuxp_dict=fixtures.test1.expected, + ), + TeamocilConfigTestFixture( + test_id="test2", + teamocil_yaml=fixtures.test2.teamocil_yaml, + teamocil_dict=fixtures.test2.teamocil_dict, + tmuxp_dict=fixtures.test2.expected, + ), + TeamocilConfigTestFixture( + test_id="test3", + teamocil_yaml=fixtures.test3.teamocil_yaml, + teamocil_dict=fixtures.test3.teamocil_dict, + tmuxp_dict=fixtures.test3.expected, + ), + TeamocilConfigTestFixture( + test_id="test4", + teamocil_yaml=fixtures.test4.teamocil_yaml, + teamocil_dict=fixtures.test4.teamocil_dict, + tmuxp_dict=fixtures.test4.expected, + ), +] + + +@pytest.mark.parametrize( + list(TeamocilConfigTestFixture._fields), + TEAMOCIL_CONFIG_TEST_FIXTURES, + ids=[test.test_id for test in TEAMOCIL_CONFIG_TEST_FIXTURES], +) +def test_config_to_dict( + test_id: str, + teamocil_yaml: str, + teamocil_dict: dict[str, t.Any], + tmuxp_dict: dict[str, t.Any], +) -> None: + """Test exporting teamocil configuration to dictionary.""" + yaml_to_dict = config_reader.ConfigReader._load( + fmt="yaml", + content=teamocil_yaml, + ) + assert yaml_to_dict == teamocil_dict + + assert importers.import_teamocil(teamocil_dict) == tmuxp_dict + + validation.validate_schema(importers.import_teamocil(teamocil_dict)) + + +@pytest.fixture(scope="module") +def multisession_config() -> dict[ + str, + dict[str, t.Any], +]: + """Return loaded multisession teamocil config as a dictionary. + + Also prevents re-running assertion the loads the yaml, since ordering of + deep list items like panes will be inconsistent. + """ + teamocil_yaml_file = fixtures.layouts.teamocil_yaml_file + test_config = config_reader.ConfigReader._from_file(teamocil_yaml_file) + teamocil_dict: dict[str, t.Any] = fixtures.layouts.teamocil_dict + + assert test_config == teamocil_dict + return teamocil_dict + + +class TeamocilMultiSessionTestFixture(t.NamedTuple): + """Test fixture for teamocil multisession config tests.""" + + test_id: str + session_name: str + expected: dict[str, t.Any] + + +TEAMOCIL_MULTISESSION_TEST_FIXTURES: list[TeamocilMultiSessionTestFixture] = [ + TeamocilMultiSessionTestFixture( + test_id="basic_two_windows", + session_name="two-windows", + expected=fixtures.layouts.two_windows, + ), + TeamocilMultiSessionTestFixture( + test_id="two_windows_with_filters", + session_name="two-windows-with-filters", + expected=fixtures.layouts.two_windows_with_filters, + ), + TeamocilMultiSessionTestFixture( + test_id="two_windows_with_custom_command_options", + session_name="two-windows-with-custom-command-options", + expected=fixtures.layouts.two_windows_with_custom_command_options, + ), + TeamocilMultiSessionTestFixture( + test_id="three_windows_within_session", + session_name="three-windows-within-a-session", + expected=fixtures.layouts.three_windows_within_a_session, + ), +] + + +@pytest.mark.parametrize( + list(TeamocilMultiSessionTestFixture._fields), + TEAMOCIL_MULTISESSION_TEST_FIXTURES, + ids=[test.test_id for test in TEAMOCIL_MULTISESSION_TEST_FIXTURES], +) +def test_multisession_config( + test_id: str, + session_name: str, + expected: dict[str, t.Any], + multisession_config: dict[str, t.Any], +) -> None: + """Test importing teamocil multisession configuration.""" + # teamocil can fit multiple sessions in a config + assert importers.import_teamocil(multisession_config[session_name]) == expected + + validation.validate_schema( + importers.import_teamocil(multisession_config[session_name]), + ) diff --git a/tests/workspace/test_import_tmuxinator.py b/tests/workspace/test_import_tmuxinator.py new file mode 100644 index 00000000000..23f567ae5d0 --- /dev/null +++ b/tests/workspace/test_import_tmuxinator.py @@ -0,0 +1,62 @@ +"""Test for tmuxp tmuxinator configuration.""" + +from __future__ import annotations + +import typing as t + +import pytest + +from tests.fixtures import import_tmuxinator as fixtures +from tmuxp._internal.config_reader import ConfigReader +from tmuxp.workspace import importers, validation + + +class TmuxinatorConfigTestFixture(t.NamedTuple): + """Test fixture for tmuxinator config conversion tests.""" + + test_id: str + tmuxinator_yaml: str + tmuxinator_dict: dict[str, t.Any] + tmuxp_dict: dict[str, t.Any] + + +TMUXINATOR_CONFIG_TEST_FIXTURES: list[TmuxinatorConfigTestFixture] = [ + TmuxinatorConfigTestFixture( + test_id="basic_config", + tmuxinator_yaml=fixtures.test1.tmuxinator_yaml, + tmuxinator_dict=fixtures.test1.tmuxinator_dict, + tmuxp_dict=fixtures.test1.expected, + ), + TmuxinatorConfigTestFixture( + test_id="legacy_tabs_config", # older vers use `tabs` instead of `windows` + tmuxinator_yaml=fixtures.test2.tmuxinator_yaml, + tmuxinator_dict=fixtures.test2.tmuxinator_dict, + tmuxp_dict=fixtures.test2.expected, + ), + TmuxinatorConfigTestFixture( + test_id="sample_config", # Test importing + tmuxinator_yaml=fixtures.test3.tmuxinator_yaml, + tmuxinator_dict=fixtures.test3.tmuxinator_dict, + tmuxp_dict=fixtures.test3.expected, + ), +] + + +@pytest.mark.parametrize( + list(TmuxinatorConfigTestFixture._fields), + TMUXINATOR_CONFIG_TEST_FIXTURES, + ids=[test.test_id for test in TMUXINATOR_CONFIG_TEST_FIXTURES], +) +def test_config_to_dict( + test_id: str, + tmuxinator_yaml: str, + tmuxinator_dict: dict[str, t.Any], + tmuxp_dict: dict[str, t.Any], +) -> None: + """Test exporting tmuxinator configuration to dictionary.""" + yaml_to_dict = ConfigReader._load(fmt="yaml", content=tmuxinator_yaml) + assert yaml_to_dict == tmuxinator_dict + + assert importers.import_tmuxinator(tmuxinator_dict) == tmuxp_dict + + validation.validate_schema(importers.import_tmuxinator(tmuxinator_dict)) diff --git a/tmuxp/__init__.py b/tmuxp/__init__.py deleted file mode 100644 index 997f0eb8016..00000000000 --- a/tmuxp/__init__.py +++ /dev/null @@ -1,23 +0,0 @@ -# -*- coding: utf8 - *- -""" - tmuxp - ~~~~~ - - :copyright: Copyright 2013 Tony Narlock. - :license: BSD, see LICENSE for details -""" - - -from __future__ import absolute_import, division, print_function, with_statement - -from .session import Session -from .server import Server -from .window import Window -from .pane import Pane -from .workspacebuilder import WorkspaceBuilder -from .cli import main -from . import config, util - -import logging - -__version__ = '0.0.21' diff --git a/tmuxp/__main__.py b/tmuxp/__main__.py deleted file mode 100755 index 52e085757d2..00000000000 --- a/tmuxp/__main__.py +++ /dev/null @@ -1,23 +0,0 @@ -# -*- coding: utf8 - *- -""" - tmuxp - ~~~~~ - - :copyright: Copyright 2013 Tony Narlock. - :license: BSD, see LICENSE for details -""" - -import sys -import os - - -def run(): - base = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - sys.path.insert(0, base) - import tmuxp.cli - tmuxp.cli.main() - -if __name__ == '__main__': - exit = run() - if exit: - sys.exit(exit) diff --git a/tmuxp/cli.py b/tmuxp/cli.py deleted file mode 100644 index 394498510c7..00000000000 --- a/tmuxp/cli.py +++ /dev/null @@ -1,748 +0,0 @@ -# -*- coding: utf8 - *- -"""Command line tool for managing tmux workspaces and tmuxp configurations. - -tmuxp.cli -~~~~~~~~~ - -:copyright: Copyright 2013 Tony Narlock. -:license: BSD, see LICENSE for details - - -prompt, prompt_bool, prompt_choices -LICENSE: https://github.com/techniq/flask-script/blob/master/LICENSE - -""" - -import os -import sys -import argparse -import argcomplete -import logging -import kaptan -from . import config -from distutils.util import strtobool -from . import log, util, exc, WorkspaceBuilder, Server -from .util import ascii_lowercase, input -import pkg_resources - -__version__ = pkg_resources.require("tmuxp")[0].version - -logger = logging.getLogger(__name__) - -config_dir = os.path.expanduser('~/.tmuxp/') -cwd_dir = os.getcwd() + '/' -tmuxinator_config_dir = os.path.expanduser('~/.tmuxinator/') -teamocil_config_dir = os.path.expanduser('~/.teamocil/') - - -def prompt(name, default=None): - """Return user input from command line. - - :param name: prompt text - :param default: default value if no input provided. - :rtype: string - - """ - - prompt = name + (default and ' [%s]' % default or '') - prompt += name.endswith('?') and ' ' or ': ' - while True: - rv = input(prompt) - if rv: - return rv - if default is not None: - return default - - -def prompt_bool(name, default=False, yes_choices=None, no_choices=None): - """ - Return user input from command line and converts to boolean value. - - :param name: prompt text - :param default: default value if no input provided. - :param yes_choices: default 'y', 'yes', '1', 'on', 'true', 't' - :param no_choices: default 'n', 'no', '0', 'off', 'false', 'f' - :rtype: bool - - """ - - yes_choices = yes_choices or ('y', 'yes', '1', 'on', 'true', 't') - no_choices = no_choices or ('n', 'no', '0', 'off', 'false', 'f') - - if default is None: - prompt_choice = 'y/n' - elif default is True: - prompt_choice = 'Y/n' - else: - prompt_choice = 'y/N' - - prompt = name + ' [%s]' % prompt_choice - prompt += name.endswith('?') and ' ' or ': ' - - while True: - rv = input(prompt) - if not rv: - return default - if rv.lower() in yes_choices: - return True - elif rv.lower() in no_choices: - return False - - -def prompt_yes_no(name, default=True): - return prompt_bool(name, default=default) - - -def prompt_choices(name, choices, default=None, resolve=ascii_lowercase, - no_choice=('none',)): - """ - Return user input from command line from set of provided choices. - - :param name: prompt text - :param choices: list or tuple of available choices. Choices may be - single strings or (key, value) tuples. - :param default: default value if no input provided. - :param no_choice: acceptable list of strings for "null choice" - :rtype: str - - """ - - _choices = [] - options = [] - - for choice in choices: - if isinstance(choice, basestring): - options.append(choice) - else: - options.append("%s [%s]" % (choice[1], choice[0])) - choice = choice[0] - _choices.append(choice) - - while True: - rv = prompt(name + ' - (%s)' % ', '.join(options), default) - if not rv: - return default - rv = resolve(rv) - if rv in no_choice: - return None - if rv in _choices: - return rv - - -class ConfigFileCompleter(argcomplete.completers.FilesCompleter): - - """ argcomplete completer for tmuxp files. """ - - def __call__(self, prefix, **kwargs): - completion = argcomplete.completers.FilesCompleter.__call__( - self, prefix, **kwargs - ) - - completion += [os.path.join(config_dir, c) - for c in config.in_dir(config_dir)] - - return completion - - -class TmuxinatorCompleter(argcomplete.completers.FilesCompleter): - - """ argcomplete completer for Tmuxinator files. """ - - def __call__(self, prefix, **kwargs): - completion = argcomplete.completers.FilesCompleter.__call__( - self, prefix, **kwargs - ) - - tmuxinator_configs = config.in_dir( - tmuxinator_config_dir, extensions='yml') - completion += [os.path.join(tmuxinator_config_dir, f) - for f in tmuxinator_configs] - - return completion - - -class TeamocilCompleter(argcomplete.completers.FilesCompleter): - - """ argcomplete completer for Teamocil files. """ - - def __call__(self, prefix, **kwargs): - completion = argcomplete.completers.FilesCompleter.__call__( - self, prefix, **kwargs - ) - - teamocil_configs = config.in_dir(teamocil_config_dir, extensions='yml') - completion += [os.path.join(teamocil_config_dir, f) - for f in teamocil_configs] - - return completion - - -def SessionCompleter(prefix, **kwargs): - """ Return list of session names for argcomplete completer. """ - t = Server() - return [s.get('session_name') for s in t._sessions - if s.get('session_name').startswith(prefix)] - - -def setup_logger(logger=None, level='INFO'): - """Setup logging for CLI use. - - :param logger: instance of logger - :type logger: :py:class:`Logger` - - """ - if not logger: - logger = logging.getLogger() - if not logger.handlers: - channel = logging.StreamHandler() - channel.setFormatter(log.LogFormatter()) - logger.setLevel(level) - logger.addHandler(channel) - - -def startup(config_dir): - """Initialize CLI. - - :param config_dir: Config directory to search - :type config_dir: string - - """ - - if not os.path.exists(config_dir): - os.makedirs(config_dir) - - -def load_workspace(config_file, args): - """ Build config workspace. - - :param config_file: full path to config file - :param type: string - - """ - logger.info('building %s.' % config_file) - - sconfig = kaptan.Kaptan() - sconfig = sconfig.import_config(config_file).get() - sconfig = config.expand(sconfig) - sconfig = config.trickle(sconfig) - - t = Server( - socket_name=args.socket_name, - socket_path=args.socket_path - ) - - try: - builder = WorkspaceBuilder(sconf=sconfig, server=t) - except exc.EmptyConfigException: - logger.error('%s is empty or parsed no config data' % config_file) - return - - tmux_bin = util.which('tmux') - - try: - builder.build() - - if 'TMUX' in os.environ: - if prompt_yes_no('Already inside TMUX, load session?'): - del os.environ['TMUX'] - os.execl(tmux_bin, 'tmux', 'switch-client', '-t', sconfig[ - 'session_name']) - - os.execl(tmux_bin, 'tmux', 'attach-session', '-t', sconfig[ - 'session_name']) - except exc.TmuxSessionExists as e: - attach_session = prompt_yes_no(e.message + ' Attach?') - - if 'TMUX' in os.environ: - del os.environ['TMUX'] - os.execl(tmux_bin, 'tmux', 'switch-client', '-t', - sconfig['session_name']) - - if attach_session: - print(sconfig['sesson_name']) - os.execl(tmux_bin, 'tmux', 'attach-session', '-t', - sconfig['session_name']) - return - - -def command_load(args): - """ Load a session from a tmuxp session file. """ - if args.list: - startup(config_dir) - configs_in_user = config.in_dir(config_dir) - configs_in_cwd = config.in_cwd() - - output = '' - - if not configs_in_user: - output += '# %s: \n\tNone found.\n' % config_dir - else: - output += '# %s: \n\t%s\n' % ( - config_dir, ', '.join(configs_in_user) - ) - - if configs_in_cwd: - output += '# current directory:\n\t%s' % ( - ', '.join(configs_in_cwd) - ) - - print(output) - - elif args.config: - if '.' == args.config: - if config.in_cwd(): - configfile = config.in_cwd()[0] - else: - print('No tmuxp configs found in current directory.') - else: - configfile = args.config - file_user = os.path.join(config_dir, configfile) - file_cwd = os.path.join(cwd_dir, configfile) - if os.path.exists(file_cwd) and os.path.isfile(file_cwd): - load_workspace(file_cwd, args) - elif os.path.exists(file_user) and os.path.isfile(file_user): - load_workspace(file_user, args) - else: - logger.error('%s not found.' % configfile) - - -def command_import_teamocil(args): - """ Import teamocil config to tmuxp format. """ - - if args.list: - try: - configs_in_user = config.in_dir( - teamocil_config_dir, extensions='yml') - except OSError: - configs_in_user = [] - configs_in_cwd = config.in_dir( - config_dir=cwd_dir, extensions='yml') - - output = '' - - if not os.path.exists(teamocil_config_dir): - output += '# %s: \n\tDirectory doesn\'t exist.\n' % teamocil_config_dir - elif not configs_in_user: - output += '# %s: \n\tNone found.\n' % teamocil_config_dir - else: - output += '# %s: \n\t%s\n' % ( - config_dir, ', '.join(configs_in_user) - ) - - if configs_in_cwd: - output += '# current directory:\n\t%s' % ( - ', '.join(configs_in_cwd) - ) - - print(output) - elif args.config: - configfile = os.path.abspath(os.path.relpath(args.config)) - configparser = kaptan.Kaptan(handler='yaml') - - if os.path.exists(configfile): - print(configfile) - configparser.import_config(configfile) - else: - sys.exit('File not found: %s' % configfile) - - newconfig = config.import_teamocil(configparser.get()) - - config_format = prompt_choices('Convert to', choices=[ - 'yaml', 'json'], default='yaml') - - if config_format == 'yaml': - newconfig = configparser.export( - 'yaml', indent=2, default_flow_style=False - ) - elif config_format == 'json': - newconfig = configparser.export('json', indent=2) - else: - sys.exit('Unknown config format.') - - print(newconfig) - print( - '---------------------------------------------------------------') - print( - 'Configuration import does its best to convert teamocil files.\n') - if prompt_yes_no( - 'The new config *WILL* require adjusting afterwards. Save config?' - ): - dest = None - while not dest: - dest_prompt = prompt('Save to: ', os.path.abspath( - os.path.join(config_dir, 'myimport.%s' % config_format))) - if os.path.exists(dest_prompt): - print('%s exists. Pick a new filename.' % dest_prompt) - continue - - dest = dest_prompt - - dest = os.path.abspath(os.path.relpath(dest)) - if prompt_yes_no('Write to %s?' % dest): - buf = open(dest, 'w') - buf.write(newconfig) - buf.close() - - print('Saved to %s.' % dest) - else: - print( - 'tmuxp has examples in JSON and YAML format at \n' - 'View tmuxp docs at ' - ) - sys.exit() - - -def command_import_tmuxinator(args): - """ Import tmuxinator config to tmuxp format. """ - if args.list: - try: - configs_in_user = config.in_dir( - tmuxinator_config_dir, extensions='yml') - except OSError: - configs_in_user = [] - configs_in_cwd = config.in_dir( - config_dir=cwd_dir, extensions='yml') - - output = '' - - if not os.path.exists(tmuxinator_config_dir): - output += '# %s: \n\tDirectory doesn\'t exist.\n' % tmuxinator_config_dir - elif not configs_in_user: - output += '# %s: \n\tNone found.\n' % tmuxinator_config_dir - else: - output += '# %s: \n\t%s\n' % ( - config_dir, ', '.join(configs_in_user) - ) - - if configs_in_cwd: - output += '# current directory:\n\t%s' % ( - ', '.join(configs_in_cwd) - ) - - print(output) - - if args.config: - configfile = os.path.abspath(os.path.relpath(args.config)) - configparser = kaptan.Kaptan(handler='yaml') - - if os.path.exists(configfile): - print(configfile) - configparser.import_config(configfile) - else: - sys.exit('File not found: %s' % configfile) - - newconfig = config.import_tmuxinator(configparser.get()) - - config_format = prompt_choices('Convert to', choices=[ - 'yaml', 'json'], default='yaml') - - if config_format == 'yaml': - newconfig = configparser.export( - 'yaml', indent=2, default_flow_style=False - ) - elif config_format == 'json': - newconfig = configparser.export('json', indent=2) - else: - sys.exit('Unknown config format.') - - print(newconfig) - print( - '---------------------------------------------------------------') - print( - 'Configuration import does its best to convert teamocil files.\n') - if prompt_yes_no( - 'The new config *WILL* require adjusting afterwards. Save config?' - ): - dest = None - while not dest: - dest_prompt = prompt('Save to: ', os.path.abspath( - os.path.join(config_dir, 'myimport.%s' % config_format))) - if os.path.exists(dest_prompt): - print('%s exists. Pick a new filename.' % dest_prompt) - continue - - dest = dest_prompt - - dest = os.path.abspath(os.path.relpath(dest)) - if prompt_yes_no('Write to %s?' % dest): - buf = open(dest, 'w') - buf.write(newconfig) - buf.close() - - print('Saved to %s.' % dest) - else: - print( - 'tmuxp has examples in JSON and YAML format at \n' - 'View tmuxp docs at ' - ) - sys.exit() - - -def command_convert(args): - """ Convert tmuxp config to and from JSON and YAML. """ - - try: - configfile = args.config - except Exception: - print('Please enter a config') - - file_user = os.path.join(config_dir, configfile) - file_cwd = os.path.join(cwd_dir, configfile) - if os.path.exists(file_cwd) and os.path.isfile(file_cwd): - fullfile = os.path.normpath(file_cwd) - filename, ext = os.path.splitext(file_cwd) - elif os.path.exists(file_user) and os.path.isfile(file_user): - - fullfile = os.path.normpath(file_user) - filename, ext = os.path.splitext(file_user) - else: - logger.error('%s not found.' % configfile) - return - - if 'json' in ext: - if prompt_yes_no('convert to <%s> to yaml?' % (fullfile)): - configparser = kaptan.Kaptan() - configparser.import_config(configfile) - newfile = fullfile.replace(ext, '.yaml') - newconfig = configparser.export( - 'yaml', indent=2, default_flow_style=False - ) - if prompt_yes_no('write config to %s?' % (newfile)): - buf = open(newfile, 'w') - buf.write(newconfig) - buf.close() - print('written new config to %s' % (newfile)) - elif 'yaml' in ext: - if prompt_yes_no('convert to <%s> to json?' % (fullfile)): - configparser = kaptan.Kaptan() - configparser.import_config(configfile) - newfile = fullfile.replace(ext, '.json') - newconfig = configparser.export('json', indent=2) - print(newconfig) - if prompt_yes_no('write config to <%s>?' % (newfile)): - buf = open(newfile, 'w') - buf.write(newconfig) - buf.close() - print('written new config to <%s>.' % (newfile)) - - -def command_attach_session(args): - """ Command to attach / switch client to a tmux session.""" - commands = [] - ctext = args.session_name - - t = Server( - socket_name=args.socket_name or None, - socket_path=args.socket_path or None - ) - try: - session = next((s for s in t.sessions if s.get( - 'session_name') == ctext), None) - if not session: - raise Exception('Session not found.') - except Exception as e: - print(e.message[0]) - return - - if 'TMUX' in os.environ: - del os.environ['TMUX'] - session.switch_client() - print('Inside tmux client, switching client.') - else: - session.attach_session() - print('Attaching client.') - - -def command_kill_session(args): - """ Command to kill a tmux session.""" - commands = [] - ctext = args.session_name - - t = Server( - socket_name=args.socket_name or None, - socket_path=args.socket_path or None - ) - - try: - session = next((s for s in t.sessions if s.get( - 'session_name') == ctext), None) - if not session: - raise Exception('Session not found.') - except Exception as e: - print(e.message[0]) - return - - try: - session.kill_session() - except Exception as e: - logger.error(e) - - -def get_parser(): - """ Return :py:class:`argparse.ArgumentParser` instance for CLI. """ - - parser = argparse.ArgumentParser( - description='''\ - Launch tmux workspace. Help documentation: . - ''', - ) - - subparsers = parser.add_subparsers(title='commands', - description='valid commands', - help='additional help') - - kill_session = subparsers.add_parser('kill-session') - kill_session.set_defaults(callback=command_kill_session) - - kill_session.add_argument( - dest='session_name', - type=str, - default=None, - ).completer = SessionCompleter - - attach_session = subparsers.add_parser('attach-session') - attach_session.set_defaults(callback=command_attach_session) - - attach_session.add_argument( - dest='session_name', - type=str, - ).completer = SessionCompleter - - load = subparsers.add_parser('load') - - loadgroup = load.add_mutually_exclusive_group(required=True) - loadgroup.add_argument( - '--list', dest='list', action='store_true', - help='List config files available', - ) - - loadgroup.add_argument( - dest='config', - type=str, - nargs='?', - help='''\ - List of config files to launch session from. - - Checks current working directory (%s) then $HOME/.tmuxp directory (%s). - - $ tmuxp . - - will check launch a ~/.pullv.yaml / ~/.pullv.json from the cwd. - will also check for any ./*.yaml and ./*.json. - ''' % (cwd_dir + '/', config_dir), - ).completer = ConfigFileCompleter(allowednames=('.yaml', '.json'), directories=False) - load.set_defaults(callback=command_load) - - convert = subparsers.add_parser('convert') - - convert.add_argument( - dest='config', - type=str, - default=None, - help='''\ - Checks current working directory (%s) then $HOME/.tmuxp directory (%s). - - $ tmuxp . - - will check launch a ~/.pullv.yaml / ~/.pullv.json from the cwd. - will also check for any ./*.yaml and ./*.json. - ''' % (cwd_dir + '/', config_dir) - ).completer = ConfigFileCompleter(allowednames=('.yaml', '.json'), directories=False) - - convert.set_defaults(callback=command_convert) - - importparser = subparsers.add_parser('import') - importsubparser = importparser.add_subparsers(title='commands', - description='valid commands', - help='additional help') - - import_teamocil = importsubparser.add_parser('teamocil') - - import_teamocilgroup = import_teamocil.add_mutually_exclusive_group( - required=True) - import_teamocilgroup.add_argument( - '--list', dest='list', action='store_true', - help='List yaml configs in ~/.teamocil and current working directory.' - ) - - import_teamocilgroup.add_argument( - dest='config', - type=str, - nargs='?', - help='''\ - Checks current ~/.teamocil and current directory for yaml files. - ''' - ).completer = TeamocilCompleter(allowednames=('.yml'), directories=False) - import_teamocil.set_defaults(callback=command_import_teamocil) - - import_tmuxinator = importsubparser.add_parser('tmuxinator') - - import_tmuxinatorgroup = import_tmuxinator.add_mutually_exclusive_group( - required=True) - import_tmuxinatorgroup.add_argument( - '--list', dest='list', action='store_true', - help='List yaml configs in ~/.tmuxinator and current working directory.' - ) - - import_tmuxinatorgroup.add_argument( - dest='config', - type=str, - nargs='?', - help='''\ - Checks current ~/.tmuxinator and current directory for yaml files. - ''' - ).completer = TmuxinatorCompleter(allowednames=('.yml'), directories=False) - - import_tmuxinator.set_defaults(callback=command_import_tmuxinator) - - parser.add_argument('--log-level', dest='log_level', default='INFO', - metavar='log-level', - help='Log level e.g. INFO, DEBUG, ERROR') - - parser.add_argument('-L', dest='socket_name', default=None, - metavar='socket-name') - - parser.add_argument('-S', dest='socket_path', default=None, - metavar='socket-path') - - # http://stackoverflow.com/questions/8521612/argparse-optional-subparser - parser.add_argument( - '-v', '--version', action='version', - version='tmuxp %s' % __version__, - help='Prints the tmuxp version') - - return parser - - -def main(): - - parser = get_parser() - - argcomplete.autocomplete(parser, always_complete_options=False) - - args = parser.parse_args() - - setup_logger(level=args.log_level.upper()) - - try: - util.version() - except Exception as e: - logger.error(e) - sys.exit() - - util.oh_my_zsh_auto_title() - - if args.callback is command_load: - command_load(args) - elif args.callback is command_convert: - command_convert(args) - elif args.callback is command_import_teamocil: - command_import_teamocil(args) - elif args.callback is command_import_tmuxinator: - command_import_tmuxinator(args) - elif args.callback is command_attach_session: - command_attach_session(args) - elif args.callback is command_kill_session: - command_kill_session(args) - else: - parser.print_help() diff --git a/tmuxp/config.py b/tmuxp/config.py deleted file mode 100644 index e5cbef3f934..00000000000 --- a/tmuxp/config.py +++ /dev/null @@ -1,354 +0,0 @@ -# -*- coding: utf8 - *- -""" - tmuxp.config - ~~~~~~~~~~~~ - - tmuxp helps you manage tmux workspaces. - - :copyright: Copyright 2013 Tony Narlock. - :license: BSD, see LICENSE for details -""" - -from __future__ import absolute_import, division, print_function, with_statement -import os -import logging -from . import exc -from .util import basestring - -logger = logging.getLogger(__name__) - - -def check_consistency(sconf): - '''Verify the consistency of the config file. - - Config files in tmuxp are met to import into :py:mod:`dict`. - ''' - - # verify session_name - if not 'session_name' in sconf: - raise exc.ConfigError('config requires "session_name"') - - if not 'windows' in sconf: - raise exc.ConfigError('config requires list of "windows"') - - for window in sconf['windows']: - if not 'window_name' in window: - raise exc.ConfigError('config window is missing "window_name"') - - if not 'panes' in window: - raise exc.ConfigError( - 'config window %s requires list of panes' % window['window_name']) - - return True - - -def is_config_file(filename, extensions=['.yaml', '.json', '.ini']): - '''Is config compatible extension. - - :param filename: filename to check (e.g. ``mysession.json``). - :type filename: string - :param extensions: filetypes to check (e.g. ``['.yaml', '.json']``). - :type extensions: list or string - :rtype: bool - ''' - - extensions = [extensions] if isinstance(extensions, basestring) else extensions - return any(filename.endswith(e) for e in extensions) - - -def in_dir(config_dir=os.path.expanduser('~/.tmuxp'), extensions=['.yaml', '.json', '.ini']): - '''Find configs in config_dir and current dir - - :param config_dir: directory to search - :type config_dir: string - :param extensions: filetypes to check (e.g. ``['.yaml', '.json']``). - :type extensions: list - :rtype: list - ''' - configs = [] - - for filename in os.listdir(config_dir): - if is_config_file(filename, extensions) and not filename.startswith('.'): - configs.append(filename) - - return configs - - -def in_cwd(): - '''Return list of configs in current working directory. - - If filename is ``.tmuxp.py``, ``.tmuxp.json``, ``.tmuxp.yaml`` or - ``.tmuxp.ini``. - - :rtype: list - ''' - configs = [] - - for filename in os.listdir(os.getcwd()): - if filename.startswith('.tmuxp') and is_config_file(filename): - configs.append(filename) - - return configs - - -def inline(config): - '''Opposite of :meth:`config.expand`. Where possible, inline. - - :param config: unexpanded config file - :type config: dict - :rtype: dict - ''' - - if ('shell_command' in config and isinstance(config['shell_command'], list) and len(config['shell_command']) == 1): - config['shell_command'] = config['shell_command'][0] - if ('shell_command_before' in config and isinstance(config['shell_command_before'], list) and len(config['shell_command_before']) == 1): - config['shell_command_before'] = config['shell_command_before'][0] - - # recurse into window and pane config items - if 'windows' in config: - config['windows'] = [inline(window) - for window in config['windows']] - if 'panes' in config: - config['panes'] = [inline(pane) for pane in config['panes']] - - return config - - -def expand(config): - '''Expand configuration into full form. Enables shorthand forms for tmuxp - config. - - This is necessary to keep the code in the :class:`Builder` clean and also - allow for neat, short-hand configurations. - - As a simple example, internally, tmuxp expects that config options - like ``shell_command`` are a list (array):: - - 'shell_command': ['htop'] - - tmuxp configs allow for it to be simply a string:: - - 'shell_command': 'htop' - - Kaptan will load JSON/YAML/INI files into python dicts for you. - :param config: the configuration for the session - :type config: dict - - iterate through session, windows, and panes for ``shell_command``, if - it is a string, turn to list. - - :param config: the session configuration - :type config: dict - :rtype: dict - ''' - - '''any config section, session, window, pane that can - contain the 'shell_command' value - ''' - if ('shell_command' in config and isinstance(config['shell_command'], basestring)): - config['shell_command'] = [config['shell_command']] - elif not 'windows' in config and not 'panes' in config and isinstance(config, basestring): - config = {'shell_command': [config]} - - if ('shell_command_before' in config and isinstance(config['shell_command_before'], basestring)): - config['shell_command_before'] = [config['shell_command_before']] - - # recurse into window and pane config items - if 'windows' in config: - config['windows'] = [expand(window) - for window in config['windows']] - if 'panes' in config: - config['panes'] = [expand(pane) for pane in config['panes']] - - return config - - -def trickle(config): - '''Trickle down / inherit config values - - This will only work if config has been expanded to full form with - :meth:`config.expand`. - - tmuxp allows certain commands to be default at the session, window - level. shell_command_before trickles down and prepends the - ``shell_command`` for the pane. - - :param config: the session configuration - :type config: dict - :rtype: dict - ''' - - ''' - prepends a pane's ``shell_command`` list with the window and sessions' - ``shell_command_before``. - ''' - - for windowconfig in config['windows']: - for paneconfig in windowconfig['panes']: - commands_before = config[ - 'shell_command_before'] if 'shell_command_before' in config else [] - commands_before.extend(windowconfig[ - 'shell_command_before']) if 'shell_command_before' in windowconfig else None - commands_before.extend(paneconfig[ - 'shell_command_before']) if 'shell_command_before' in paneconfig else None - - if 'shell_command' not in paneconfig: - paneconfig['shell_command'] = list() - - commands_before.extend(paneconfig[ - 'shell_command']) if paneconfig['shell_command'] else None - paneconfig['shell_command'] = commands_before - - return config - - -def import_tmuxinator(sconf): - '''Import yaml configs from `tmuxinator`_. - - .. _tmuxinator: https://github.com/aziz/tmuxinator - - :todo: handle 'root' with a cd shell_command_before - - :param sconf: python dict for session configuration - :type sconf: dict - ''' - - tmuxp_config = {} - - if 'project_name' in sconf: - tmuxp_config['session_name'] = sconf['project_name'] - elif 'name' in sconf: - tmuxp_config['session_name'] = sconf['name'] - else: - tmuxp_config['session_name'] = None - - if 'cli_args' in sconf: - tmuxp_config['config'] = sconf['cli_args'] - - if '-f' in tmuxp_config['config']: - tmuxp_config['config'] = tmuxp_config[ - 'config'].replace('-f', '').strip() - elif 'tmux_options' in sconf: - tmuxp_config['config'] = sconf['tmux_options'] - - if '-f' in tmuxp_config['config']: - tmuxp_config['config'] = tmuxp_config[ - 'config'].replace('-f', '').strip() - - if 'socket_name' in sconf: - tmuxp_config['socket_name'] = sconf['socket_name'] - - tmuxp_config['windows'] = [] - - if 'tabs' in sconf: - sconf['windows'] = sconf.pop('tabs') - - if 'pre' in sconf and 'pre_window' in sconf: - tmuxp_config['shell_command'] = sconf['pre'] - - if isinstance(sconf['pre'], basestring): - tmuxp_config['shell_command_before'] = [sconf['pre_window']] - else: - tmuxp_config['shell_command_before'] = sconf['pre_window'] - elif 'pre' in sconf: - if isinstance(sconf['pre'], basestring): - tmuxp_config['shell_command_before'] = [sconf['pre']] - else: - tmuxp_config['shell_command_before'] = sconf['pre'] - - if 'rbenv' in sconf: - if 'shell_command_before' not in tmuxp_config: - tmuxp_config['shell_command_before'] = [] - tmuxp_config['shell_command_before'].append( - 'rbenv shell %s' % sconf['rbenv'] - ) - - for w in sconf['windows']: - for k, v in w.items(): - - windowdict = {} - - windowdict['window_name'] = k - - if isinstance(v, basestring) or v is None: - windowdict['panes'] = [v] - tmuxp_config['windows'].append(windowdict) - continue - elif isinstance(v, list): - windowdict['panes'] = v - tmuxp_config['windows'].append(windowdict) - continue - - if 'pre' in v: - windowdict['shell_command_before'] = v['pre'] - if 'panes' in v: - windowdict['panes'] = v['panes'] - - if 'layout' in v: - windowdict['layout'] = v['layout'] - tmuxp_config['windows'].append(windowdict) - - return tmuxp_config - - -def import_teamocil(sconf): - '''Import yaml configs from `teamocil`_. - - .. _teamocil: https://github.com/remiprev/teamocil - - :todo: change 'root' to a cd or start_directory - :todo: width in pane -> main-pain-width - :todo: with_env_var - :todo: clear - :todo: cmd_separator - - :param sconf: python dict for session configuration - :type sconf: dict - ''' - - tmuxp_config = {} - - if 'session' in sconf: - sconf = sconf['session'] - - if 'name' in sconf: - tmuxp_config['session_name'] = sconf['name'] - else: - tmuxp_config['session_name'] = None - - tmuxp_config['windows'] = [] - - for w in sconf['windows']: - - windowdict = {} - - windowdict['window_name'] = w['name'] - if 'clear' in w: - windowdict['clear'] = w['clear'] - - if 'filters' in w: - if 'before' in w['filters']: - for b in w['filters']['before']: - windowdict['shell_command_before'] = w['filters']['before'] - if 'after' in w['filters']: - for b in w['filters']['after']: - windowdict['shell_command_after'] = w['filters']['after'] - - if 'splits' in w: - w['panes'] = w.pop('splits') - - if 'panes' in w: - for p in w['panes']: - if 'cmd' in p: - p['shell_command'] = p.pop('cmd') - if 'width' in p: - # todo support for height/width - p.pop('width') - windowdict['panes'] = w['panes'] - - if 'layout' in w: - windowdict['layout'] = w['layout'] - tmuxp_config['windows'].append(windowdict) - - return tmuxp_config - diff --git a/tmuxp/exc.py b/tmuxp/exc.py deleted file mode 100644 index 5aa6e153a9b..00000000000 --- a/tmuxp/exc.py +++ /dev/null @@ -1,19 +0,0 @@ -# -*- coding: utf8 - *- -""" - tmuxp.exc - ~~~~~~~~~ - :copyright: Copyright 2013 Tony Narlock. - :license: BSD, see LICENSE for details -""" - - -class TmuxSessionExists(Exception): - pass - - -class ConfigError(Exception): - pass - - -class EmptyConfigException(Exception): - pass diff --git a/tmuxp/formats.py b/tmuxp/formats.py deleted file mode 100644 index 68d670accd8..00000000000 --- a/tmuxp/formats.py +++ /dev/null @@ -1,101 +0,0 @@ -# -*- coding: utf8 - *- -""" - tmuxp.formats - ~~~~~~~~~~~~~ - - .. _For reference: - http://sourceforge.net/p/tmux/tmux-code/ci/master/tree/format.c - - :copyright: Copyright 2013 Tony Narlock. - :license: BSD, see LICENSE for details -""" - -SESSION_FORMATS = [ - 'session_name', - 'session_windows', - 'session_width', - 'session_height', - 'session_id', - 'session_created', - 'session_created_string', - 'session_attached', - 'session_grouped', - 'session_group', -] - -CLIENT_FORMATS = [ - 'client_cwd', - 'client_height', - 'client_width', - 'client_tty', - 'client_termname', - 'client_created', - 'client_created_string', - 'client_activity', - 'client_activity_string', - 'client_prefix', - 'client_utf8', - 'client_readonly', - 'client_session', - 'client_last_session', -] - -WINDOW_FORMATS = [ - # format_window() - 'window_id', - 'window_name', - 'window_width', - 'window_height', - 'window_layout', - 'window_panes', - # format_winlink() - 'window_index', - 'window_flags', - 'window_active', - 'window_bell_flag', - 'window_activity_flag', - 'window_silence_flag', -] - -PANE_FORMATS = [ - 'history_size', - 'history_limit', - 'history_bytes', - 'pane_index', - 'pane_width', - 'pane_height', - 'pane_title', - 'pane_id', - 'pane_active', - 'pane_dead', - 'pane_in_mode', - 'pane_synchronized', - 'pane_tty', - 'pane_pid', - 'pane_start_command', - 'pane_start_path', - 'pane_current_path', - 'pane_current_command', - - 'cursor_x', - 'cursor_y', - 'scroll_region_upper', - 'scroll_region_lower', - 'saved_cursor_x', - 'saved_cursor_y', - - 'alternate_on', - 'alternate_saved_x', - 'alternate_saved_y', - - 'cursor_flag', - 'insert_flag', - 'keypad_cursor_flag', - 'keypad_flag', - 'wrap_flag', - - 'mouse_standard_flag', - 'mouse_button_flag', - 'mouse_any_flag', - 'mouse_utf8_flag', -] diff --git a/tmuxp/log.py b/tmuxp/log.py deleted file mode 100644 index bf54ca1ef07..00000000000 --- a/tmuxp/log.py +++ /dev/null @@ -1,214 +0,0 @@ -#!/usr/bin/env python -# Pieces of this code are from Tornado, they are being phased out. -# -# Copyright 2012 Facebook -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -from __future__ import absolute_import, division, print_function, with_statement - -import logging -import logging.handlers -import sys -import time -from colorama import init -init() -from colorama import Fore, Back, Style -from .util import unicode, bytes, basestring - -try: - import curses -except ImportError: - curses = None - -# From tornado/util.py: -# Fake unicode literal support: Python 3.2 doesn't have the u'' marker for -# literal strings, and alternative solutions like "from __future__ import -# unicode_literals" have other problems (see PEP 414). u() can be applied -# to ascii strings that include \u escapes (but they must not contain -# literal non-ascii characters). -# todo _ can remove this, this next 10 lines is from -# http://www.rfk.id.au/blog/entry/preparing-pyenchant-for-python-3/ -_UTF8_TYPES = (bytes, type(None)) -_TO_UNICODE_TYPES = (unicode, type(None)) - - -def utf8(value): - """Converts a string argument to a byte string. - - If the argument is already a byte string or None, it is returned unchanged. - Otherwise it must be a unicode string and is encoded as utf8. - """ - if isinstance(value, _UTF8_TYPES): - return value - assert isinstance(value, unicode), \ - "Expected bytes, unicode, or None; got %r" % type(value) - return value.encode("utf-8") - - -def to_unicode(value): - """Converts a string argument to a unicode string. - - If the argument is already a unicode string or None, it is returned - unchanged. Otherwise it must be a byte string and is decoded as utf8. - """ - if isinstance(value, _TO_UNICODE_TYPES): - return value - assert isinstance(value, bytes), \ - "Expected bytes, unicode, or None; got %r" % type(value) - return value.decode("utf-8") - -# to_unicode was previously named _unicode not because it was private, -# but to avoid conflicts with the built-in unicode() function/type -_unicode = to_unicode - -# When dealing with the standard library across python 2 and 3 it is -# sometimes useful to have a direct conversion to the native string type -if str is unicode: - native_str = to_unicode -else: - native_str = utf8 - - -def _stderr_supports_color(): - color = False - if curses and sys.stderr.isatty(): - try: - curses.setupterm() - if curses.tigetnum("colors") > 0: - color = True - except Exception: - pass - return color - -# Encoding notes: The logging module prefers to work with character -# strings, but only enforces that log messages are instances of -# basestring. In python 2, non-ascii bytestrings will make -# their way through the logging framework until they blow up with -# an unhelpful decoding error (with this formatter it happens -# when we attach the prefix, but there are other opportunities for -# exceptions further along in the framework). -# -# If a byte string makes it this far, convert it to unicode to -# ensure it will make it out to the logs. Use repr() as a fallback -# to ensure that all byte strings can be converted successfully, -# but don't do it by default so we don't add extra quotes to ascii -# bytestrings. This is a bit of a hacky place to do this, but -# it's worth it since the encoding errors that would otherwise -# result are so useless (and tornado is fond of using utf8-encoded -# byte strings whereever possible). - - -def safe_unicode(s): - try: - return _unicode(s) - except UnicodeDecodeError: - return repr(s) - -NORMAL = Fore.RESET + Style.RESET_ALL + Back.RESET - -LEVEL_COLORS = { - 'DEBUG': Fore.BLUE, # Blue - 'INFO': Fore.GREEN, # Green - 'WARNING': Fore.YELLOW, - 'ERROR': Fore.RED, - 'CRITICAL': Fore.RED -} - - -class LogFormatter(logging.Formatter): - - """Log formatter used in Tornado. - - Key features of this formatter are: - - * Color support when logging to a terminal that supports it. - * Timestamps on every log line. - * Robust against str/bytes encoding problems. - - This formatter is enabled automatically by - `tornado.options.parse_command_line` (unless ``--logging=none`` is - used). - """ - - def prefix_template(self, record): - ''' this is available as a definition instead of a class - variable so it can access to instance. it also accepts the record - parameter. - - :param: record: :py:class:`logging.LogRecord` object. this is passed in - from inside the :py:meth:`logging.Formatter.format` record. - ''' - - prefix_template = '' - prefix_template += NORMAL - prefix_template += LEVEL_COLORS.get(record.levelname) + Style.BRIGHT + '(%(levelname)s)' + NORMAL + ' ' - prefix_template += '[' + Fore.BLACK + Style.DIM + Style.BRIGHT + '%(asctime)s' + Fore.RESET + Style.RESET_ALL + ']' - prefix_template += ' ' + Fore.WHITE + Style.DIM + Style.BRIGHT + '%(name)s' + Fore.RESET + Style.RESET_ALL + ' ' - prefix_template += NORMAL - - return prefix_template - - def __init__(self, color=True, *args, **kwargs): - logging.Formatter.__init__(self, *args, **kwargs) - self._color = color and _stderr_supports_color() - - def format(self, record): - try: - record.message = record.getMessage() - except Exception as e: - record.message = "Bad message (%r): %r" % (e, record.__dict__) - assert isinstance( - record.message, basestring) # guaranteed by logging - - date_format = '%H:%m:%S' - record.asctime = time.strftime(date_format, self.converter(record.created)) - - prefix = self.prefix_template(record) % record.__dict__ - - formatted = prefix + " " + safe_unicode(record.message) - if record.exc_info: - if not record.exc_text: - record.exc_text = self.formatException(record.exc_info) - if record.exc_text: - # exc_text contains multiple lines. We need to safe_unicode - # each line separately so that non-utf8 bytes don't cause - # all the newlines to turn into '\n'. - lines = [formatted.rstrip()] - lines.extend(safe_unicode(ln) - for ln in record.exc_text.split('\n')) - formatted = '\n'.join(lines) - return formatted.replace("\n", "\n ") - - -class DebugLogFormatter(LogFormatter): - - def prefix_template(self, record): - ''' this is available as a definition instead of a class - variable so it can access to instance. it also accepts the record - argument. - - :param: record: :py:class:`logging.LogRecord` object. this is passed in - from inside the :py:meth:`logging.Formatter.format` record. - ''' - - prefix_template = '' - prefix_template += NORMAL - prefix_template += LEVEL_COLORS.get(record.levelname) + Style.BRIGHT + '(%(levelname)1.1s)' + NORMAL + ' ' - prefix_template += '[' + Fore.BLACK + Style.DIM + Style.BRIGHT + '%(asctime)s' + Fore.RESET + Style.RESET_ALL + ']' - prefix_template += ' ' + Fore.WHITE + Style.DIM + Style.BRIGHT + '%(name)s' + Fore.RESET + Style.RESET_ALL + ' ' - prefix_template += Fore.GREEN + Style.BRIGHT + '%(module)s.%(funcName)s()' - prefix_template += Fore.BLACK + Style.DIM + Style.BRIGHT + ':' + NORMAL + Fore.CYAN + '%(lineno)d' - prefix_template += NORMAL - - return prefix_template - diff --git a/tmuxp/pane.py b/tmuxp/pane.py deleted file mode 100644 index 143f5de9a59..00000000000 --- a/tmuxp/pane.py +++ /dev/null @@ -1,138 +0,0 @@ -# -*- coding: utf8 - *- -""" - tmuxp.pane - ~~~~~~~~~~ - - tmuxp helps you manage tmux workspaces. - - :copyright: Copyright 2013 Tony Narlock. - :license: BSD, see LICENSE for details -""" -from __future__ import absolute_import, division, print_function, with_statement -from . import util, formats -import logging - -logger = logging.getLogger(__name__) - - -class Pane(util.TmuxMappingObject, util.TmuxRelationalObject): - - """:term:`tmux(1)` :ref:`pane`. - - :param window: :class:`Window` - - """ - - def __init__(self, window=None, **kwargs): - if not window: - raise ValueError('Pane must have \ - ``Window`` object') - - self.window = window - self.session = self.window.session - self.server = self.session.server - - self._pane_id = kwargs['pane_id'] - - self.server._update_panes() - - @property - def _TMUX(self, *args): - - attrs = { - 'pane_id': self._pane_id - } - - # from https://github.com/serkanyersen/underscore.py - def by(val, *args): - for key, value in attrs.items(): - try: - if attrs[key] != val[key]: - return False - except KeyError: - return False - return True - - return list(filter(by, self.server._panes))[0] - - def tmux(self, cmd, *args, **kwargs): - """Send command to tmux with :attr:`pane_id` as ``target-pane``. - - Specifying ``('-t', 'custom-target')`` or ``('-tcustom_target')`` in - ``args`` will override using the object's ``pane_id`` as target. - - """ - if not len([arg for arg in args if '-t' in arg]): - args = ('-t', self.get('pane_id')) + args - - return self.server.tmux(cmd, *args, **kwargs) - - def send_keys(self, cmd, enter=True): - ''' - ```tmux send-keys``` to the pane - - :param enter: bool. send enter after sending the key. - ''' - self.tmux('send-keys', cmd) - - if enter: - self.enter() - - def clear(self): - """Clear pane.""" - self.send_keys('reset') - - def reset(self): - """Reset and clear pane history. """ - - self.tmux('send-keys', '-R \; clear-history') - - def set_width(self, width): - """Set width of pane. - - :param width: pane width, in cells. - :type width: int - """ - self.resize_pane(width=width) - - def set_height(self, height): - """Set height of pane. - - :param height: pane height, in cells. - :type height: int - """ - self.resize_pane(height=height) - - def resize_pane(self, *args, **kwargs): - ''' - ``$ tmux resize-pane`` - - :param target_pane: ``target_pane``, or ``-U``,``-D``, ``-L``, ``-R``. - :type target_pane: string - :rtype: :class:`Pane` - - ''' - # if isinstance(target_pane, basestring) and not ':' not in target_pane or isinstance(target_pane, int): - # target_pane = "%s.%s" % (self.target, target_pane) - - if 'height' in kwargs: - proc = self.tmux('resize-pane', '-y%s' % int(kwargs['height'])) - elif 'width' in kwargs: - proc = self.tmux('resize-pane', '-x%s' % int(kwargs['width'])) - else: - proc = self.tmux('resize-pane', args[0]) - - if proc.stderr: - raise Exception(proc.stderr) - - self.server._update_panes() - return self - - def enter(self): - ''' - ``$ tmux send-keys`` send Enter to the pane. - ''' - self.tmux('send-keys', 'Enter') - - def __repr__(self): - return "%s(%s %s)" % (self.__class__.__name__, self.get('pane_id'), self.window) diff --git a/tmuxp/server.py b/tmuxp/server.py deleted file mode 100644 index 0af159cbdf9..00000000000 --- a/tmuxp/server.py +++ /dev/null @@ -1,500 +0,0 @@ -# -*- coding: utf8 - *- -""" - tmuxp.server - ~~~~~~~~~~~~ - - tmuxp helps you manage tmux workspaces. - - :copyright: Copyright 2013 Tony Narlock. - :license: BSD, see LICENSE for details -""" -from __future__ import absolute_import, division, print_function, with_statement - -import os -from .util import tmux, TmuxRelationalObject -from .session import Session -from . import formats -import logging - -logger = logging.getLogger(__name__) - - -class Server(TmuxRelationalObject): - - ''' - The :term:`tmux(1)` server. Container for: - - - :attr:`Server._sessions` [:class:`Session`, ...] - - - :attr:`Session._windows` [:class:`Window`, ...] - - - :attr:`Window._panes` [:class:`Pane`, ...] - - - :class:`Pane` - - When instantiated, provides the ``t`` global. stores information on live, - running tmux server. - ''' - - socket_name = None - socket_path = None - config_file = None - childIdAttribute = 'session_id' - - def __init__(self, socket_name=None, socket_path=None, config_file=None, - **kwargs): - self._windows = [] - self._panes = [] - - if socket_name: - self.socket_name = socket_name - - if socket_path: - self.socket_path = socket_path - - if config_file: - self.config_file = config_file - - def tmux(self, *args, **kwargs): - args = list(args) - if self.socket_name: - args.insert(0, '-L{}'.format(self.socket_name)) - if self.socket_path: - args.insert(0, '-S{}'.format(self.socket_path)) - if self.config_file: - args.insert(0, '-f{}'.format(self.config_file)) - - return tmux(*args, **kwargs) - - def __list_sessions(self): - ''' - compatibility wrapping for ``$ tmux list-sessions``. - - :rtype: stdout or stderr of tmux proc - ''' - sformats = formats.SESSION_FORMATS - tmux_formats = ['#{%s}' % f for f in sformats] - - tmux_args = ( - '-F%s' % '\t'.join(tmux_formats), # output - ) - - proc = self.tmux( - 'list-sessions', - *tmux_args - ) - - if proc.stderr: - if 'unknown option -- F' in proc.stderr[0]: - proc = self.tmux( - 'list-sessions', - ) - - if proc.stderr: - raise Exception(proc.stderr) - else: - return 'hi' - else: - raise Exception(proc.stderr) - else: - session_info = proc.stdout[0] - - if proc.stderr: - raise Exception(sessions.stderr) - - return proc.stdout - - def _list_sessions(self): - ''' - Return a list of session information ``tmux(1)`` for the sessions - - :rtype: :py:obj:`list` of :py:obj:`dict` - ''' - - sformats = formats.SESSION_FORMATS - tmux_formats = ['#{%s}' % format for format in sformats] - sessions = self.__list_sessions() - - # combine format keys with values returned from ``tmux list-windows`` - sessions = [dict(zip( - sformats, session.split('\t'))) for session in sessions] - - # clear up empty dict - sessions = [ - dict((k, v) for k, v in session.items() if v) for session in sessions - ] - - return sessions - - @property - def _sessions(self): - return self._list_sessions() - - def list_sessions(self): - ''' - Return a list of :class:`Session` from the ``tmux(1)`` session. - - :rtype: :py:obj:`list` of :class:`Session` - ''' - return [ - Session(server=self, **s) for s in self._sessions - ] - - @property - def sessions(self): - return self.list_sessions() - children = sessions - - def __list_windows(self): - ''' - Return dict of ``tmux(1) list-windows`` values. - ''' - - wformats = ['session_name', 'session_id'] + formats.WINDOW_FORMATS - tmux_formats = ['#{%s}' % format for format in wformats] - - windows = self.tmux( - 'list-windows', # ``tmux list-windows`` - '-a', - '-F%s' % '\t'.join(tmux_formats), # output - ) - - if windows.stderr: - raise Exception(windows.stderr) - - return windows.stdout - - def _list_windows(self): - ''' take the outpout of _list_windows from shell and put it into - a list of dicts''' - - wformats = ['session_name', 'session_id'] + formats.WINDOW_FORMATS - - windows = self.__list_windows() - - # combine format keys with values returned from ``tmux list-windows`` - windows = [dict(zip( - wformats, window.split('\t'))) for window in windows] - - # clear up empty dict - windows = [ - dict((k, v) for k, v in window.items() if v) for window in windows - ] - - # tmux < 1.8 doesn't have window_id, use window_name - for w in windows: - if not 'window_id' in w: - w['window_id'] = w['window_name'] - - if self._windows: - # http://stackoverflow.com/a/14465359 - self._windows[:] = [] - - self._windows.extend(windows) - - return self._windows - - def _update_windows(self): - self._list_windows() - return self - - def __list_panes(self): - '''Return list of :class:`Pane` for the window. - - :rtype: list of :class:`Pane` - ''' - pformats = ['session_name', 'session_id', - 'window_index', 'window_id', 'window_name'] + formats.PANE_FORMATS - tmux_formats = ['#{%s}\t' % f for f in pformats] - - panes = self.tmux( - 'list-panes', - #'-t%s:%s' % (self.get('session_name'), self.get('window_id')), - '-a', - '-F%s' % ''.join(tmux_formats), # output - ) - - if panes.stderr: - raise Exception(panes.stderr) - - return panes.stdout - - def _list_panes(self): - ''' take the outpout of _list_panes from shell and put it into - a list of dicts''' - - pformats = ['session_name', 'session_id', - 'window_index', 'window_id', 'window_name'] + formats.PANE_FORMATS - - panes = self.__list_panes() - - # combine format keys with values returned from ``tmux list-panes`` - panes = [dict(zip( - pformats, window.split('\t'))) for window in panes] - - # clear up empty dict - panes = [ - dict((k, v) for k, v in window.items() if v) for window in panes - ] - - if self._panes: - # http://stackoverflow.com/a/14465359 - self._panes[:] = [] - - self._panes.extend(panes) - - return self._panes - - def _update_panes(self): - self._list_panes() - return self - - def list_clients(self): - ''' - Return a list of :class:`client` from tmux server. - - ``$ tmux list-clients`` - ''' - cformats = formats.CLIENT_FORMATS - tmux_formats = ['#{%s}' % format for format in cformats] - # import ipdb - # ipdb.set_trace() - clients = self.tmux( - 'list-clients', - '-F%s' % '\t'.join(tmux_formats), # output - ).stdout - - # combine format keys with values returned from ``tmux list-windows`` - clients = [dict(zip( - cformats, client.split('\t'))) for client in clients] - - # clear up empty dict - new_clients = [ - dict((k, v) for k, v in client.items() if v) for client in clients - ] - - if not self._clients: - for client in new_clients: - logger.debug('adding client_tty %s' % (client['client_tty'])) - self._clients.append(client) - return self._clients - - new = {client['client_tty']: client for client in new_clients} - old = {client.get('client_tty'): client for client in self._clients} - - created = set(new.keys()) - set(old.keys()) - deleted = set(old.keys()) - set(new.keys()) - intersect = set(new.keys()).intersection(set(old.keys())) - - diff = {id: dict(set(new[id].items()) - set(old[id].items())) - for id in intersect} - - logger.debug( - "syncing clients" - "\n\tdiff: %s\n" - "\tcreated: %s\n" - "\tdeleted: %s\n" - "\tintersect: %s" % (diff, created, deleted, intersect) - ) - - for s in self._clients: - # remove client objects if deleted or out of client - if s.get('client_tty') in deleted: - logger.debug("removing %s" % s) - self._clients.remove(s) - - if s.get('client_tty') in intersect: - logger.debug('updating client_tty %s' % (s.get('client_tty'))) - s.update(diff[s.get('client_tty')]) - - # create client objects for non-existant client_tty's - for client in [new[client_tty] for client_tty in created]: - logger.debug('new client %s' % client['client_tty']) - self._clients.append(client) - - return self._clients - - def has_clients(self): - # are any clients connected to tmux - proc = self.tmux('list-clients') - - if proc.stderr: - raise Exception(proc.stderr) - - def attached_sessions(self): - ''' - Returns active :class:`Session` object - - This will not work where multiple tmux sessions are attached. - ''' - - sessions = self._sessions - attached_sessions = list() - - for session in sessions: - if 'session_attached' in session: - # for now session_active is a unicode - if session.get('session_attached') == '1': - logger.debug('session %s attached', session.get( - 'session_name')) - attached_sessions.append(session) - else: - continue - - return attached_sessions or None - - def has_session(self, target_session): - ''' - ``$ tmux has-session`` - - :param: target_session: str of session name. - - returns True if session exists. - ''' - - proc = self.tmux('has-session', '-t%s' % target_session) - - if 'failed to connect to server' in proc.stdout: - return False - elif 'session not found' in proc.stdout: - return False - else: - return True - - def kill_server(self): - ''' - ``$ tmux kill-server`` - ''' - self.tmux('kill-server') - - def kill_session(self, target_session=None): - ''' - ``$ tmux kill-session`` - - :param: target_session: str. note this accepts fnmatch(3). 'asdf' will - kill asdfasd - ''' - proc = self.tmux('kill-session', '-t%s' % target_session) - - if proc.stderr: - raise Exception(proc.stderr) - - return self - - def switch_client(self, target_session): - ''' - ``$ tmux switch-client`` - - :param: target_session: str. name of the session. fnmatch(3) works. - ''' - # tmux('switch-client', '-t', target_session) - proc = self.tmux('switch-client', '-t%s' % target_session) - - if proc.stderr: - raise Exception(proc.stderr) - - def attach_session(self, target_session=None): - ''' - ``$ tmux attach-session`` aka alias: ``$ tmux attach`` - - :param: target_session: str. name of the session. fnmatch(3) works. - ''' - # tmux('switch-client', '-t', target_session) - tmux_args = tuple() - if target_session: - tmux_args += ('-t%s' % target_session,) - - proc = self.tmux('attach-session', *tmux_args) - - if proc.stderr: - raise Exception(proc.stderr) - - def new_session(self, - session_name=None, - kill_session=False, - attach=False, - *args, - **kwargs): - ''' - ``$ tmux new-session`` - - Returns :class:`Session` - - Uses ``-P`` flag to print session info, ``-F`` for return formatting - returns new Session object. - - ``$ tmux new-session -d`` will create the session in the background - ``$ tmux new-session -Ad`` will move to the session name if it already - exists. todo: make an option to handle this. - - :param session_name: session name:: - - $ tmux new-session -s - :type session_name: string - - :param detach: create session background:: - - $ tmux new-session -d - :type detach: bool - - :param attach_if_exists: if the session_name exists, attach it. - if False, this method will raise a - :exc:`tmuxp.exc.TmuxSessionExists` exception - :type attach_if_exists: bool - - :param kill_session: Kill current session if ``$ tmux has-session`` - Useful for testing workspaces. - :type kill_session: bool - ''' - - # ToDo: Update below to work with attach_if_exists - if self.has_session(session_name): - if kill_session: - self.tmux('kill-session', '-t%s' % session_name) - logger.info('session %s exists. killed it.' % session_name) - else: - raise TmuxSessionExists( - 'Session named %s exists' % session_name) - - logger.debug('creating session %s' % session_name) - - sformats = formats.SESSION_FORMATS - tmux_formats = ['#{%s}' % f for f in sformats] - - env = os.environ.get('TMUX') - - if env: - del os.environ['TMUX'] - - tmux_args = ( - '-s%s' % session_name, - '-P', '-F%s' % '\t'.join(tmux_formats), # output - ) - - if not attach: - tmux_args += ('-d',) - - proc = self.tmux( - 'new-session', - *tmux_args - ) - - if proc.stderr: - raise Exception(proc.stderr) - - session = proc.stdout[0] - - if env: - os.environ['TMUX'] = env - - # combine format keys with values returned from ``tmux list-windows`` - session = dict(zip(sformats, session.split('\t'))) - - # clear up empty dict - session = dict((k, v) for k, v in session.items() if v) - - session = Session(server=self, **session) - - # self._sessions.append(session) - - return session diff --git a/tmuxp/session.py b/tmuxp/session.py deleted file mode 100644 index de5d42b3ff5..00000000000 --- a/tmuxp/session.py +++ /dev/null @@ -1,365 +0,0 @@ -# -*- coding: utf8 - *- -""" - tmuxp.session - ~~~~~~~~~~~~~ - - tmuxp helps you manage tmux workspaces. - - :copyright: Copyright 2013 Tony Narlock. - :license: BSD, see LICENSE for details -""" -from __future__ import absolute_import, division, print_function, with_statement - -import pipes -from .window import Window -from .exc import TmuxSessionExists -from . import util, formats -import logging -logger = logging.getLogger(__name__) - - -class Session(util.TmuxMappingObject, util.TmuxRelationalObject): - - ''' - ``tmux(1) session``. - - Holds :class:`Window` objects. - - ''' - - childIdAttribute = 'window_id' - - def __init__(self, server=None, **kwargs): - - self.server = server - - if not 'session_id' in kwargs: - raise ValueError('Session requires a `session_id`') - self._session_id = kwargs['session_id'] - self.server._update_windows() - - @property - def _TMUX(self, *args): - - attrs = { - 'session_id': str(self._session_id) - } - - # from https://github.com/serkanyersen/underscore.py - def by(val, *args): - for key, value in attrs.items(): - try: - if attrs[key] != val[key]: - return False - except KeyError: - return False - return True - - try: - return list(filter(by, self.server._sessions))[0] - except IndexError as e: - logger.error(e) - logger.error(self._session_name) - logger.error(self.server._sessions) - - def tmux(self, *args, **kwargs): - # if '-t' not in kwargs: - # kwargs['-t'] = self.get['session_id'] - return self.server.tmux(*args, **kwargs) - - def attach_session(self, target_session=None): - ''' - ``$ tmux attach-session`` aka alias: ``$ tmux attach`` - - :param: target_session: str. name of the session. fnmatch(3) works. - ''' - proc = self.tmux('attach-session', '-t%s' % self.get('session_id')) - - if proc.stderr: - raise Exception(proc.stderr) - - def kill_session(self, target_session=None): - ''' - ``$ tmux kill-session`` - - :param: target_session: str. note this accepts fnmatch(3). 'asdf' will - kill asdfasd - ''' - proc = self.tmux('kill-session', '-t%s' % self.get('session_id')) - - if proc.stderr: - raise Exception(proc.stderr) - - def switch_client(self, target_session=None): - ''' - ``$ tmux kill-session`` - - :param: target_session: str. note this accepts fnmatch(3). 'asdf' will - kill asdfasd - ''' - proc = self.tmux('switch-client', '-t%s' % self.get('session_id')) - - if proc.stderr: - raise Exception(proc.stderr) - - - def rename_session(self, new_name): - '''rename session and return new :class:`Session` object - - :param rename_session: new session name - :type rename_session: string - ''' - new_name = pipes.quote(new_name) - proc = self.tmux( - 'rename-session', - '-t%s' % self.get('session_id'), - new_name - ) - - if proc.stderr: - raise Exception(proc.stderr) - - return self - - def new_window(self, - window_name=None, - attach=True): - ''' - ``$ tmux new-window`` - - .. note:: - - By default, this will make the window active. For the new window - to be created and not set to current, pass in ``attach=False``. - - :param window_name: window name. - - .. code-block:: bash - - $ tmux new-window -n - - :type window_name: string - - :param attach: make new window the current window after creating it, - default True. - :param type: bool - ''' - - wformats = ['session_name', 'session_id'] + formats.WINDOW_FORMATS - tmux_formats = ['#{%s}' % f for f in wformats] - - window_args = ( - '-t%s' % self.get('session_id'), - '-P', - '-F%s' % '\t'.join(tmux_formats), # output - ) - - if window_name: - window_args += ('-n%s' % window_name,) - - if not attach: - window_args += ('-d',) - - proc = self.tmux('new-window', *window_args) - - if proc.stderr: - raise Exception(proc.stderr) - - window = proc.stdout[0] - - window = dict(zip(wformats, window.split('\t'))) - - # clear up empty dict - window = dict((k, v) for k, v in window.items() if v) - window = Window(session=self, **window) - - self.server._update_windows() - - return window - - def kill_window(self, target_window=None): - ''' - ``$ tmux kill-window`` - - Kill the current window or the window at ``target-window``. removing it - from any sessions to which it is linked. - - :param target_window: the ``target window``. - :type target_window: string - ''' - - tmux_args = list() - - if target_window: - if isinstance(target_window, int): - target = '-t%s:%d' % (self.get('session_name'), target_window) - else: - target = '-t%s' % target_window - - proc = self.tmux('kill-window', target) - - if proc.stderr: - raise Exception(proc.stderr) - - self.server._update_windows() - - def _list_windows(self): - windows = self.server._update_windows()._windows - - windows = [ - w for w in windows if w['session_id'] == self.get('session_id') - ] - - return windows - - @property - def _windows(self): - return self._list_windows() - - def list_windows(self): - ''' - Return a list of :class:`Window` from the ``tmux(1)`` session. - - :rtype: :class:`Window` - ''' - windows = [ - w for w in self._windows if w['session_id'] == self._session_id - ] - - return [Window(session=self, **window) for window in windows] - - @property - def windows(self): - return self.list_windows() - children = windows - - def attached_window(self): - ''' - Returns active :class:`Window` object. - ''' - active_windows = [] - for window in self._windows: - if 'window_active' in window: - # for now window_active is a unicode - if window.get('window_active') == '1': - active_windows.append(Window(session=self, **window)) - else: - continue - - if len(active_windows) == int(1): - return active_windows[0] - else: - raise Exception( - 'multiple active windows found. %s' % active_windows) - - if len(self._windows) == int(0): - raise Exception('No Windows') - - def select_window(self, target_window): - ''' - ``$ tmux select-window`` - - :param: window: ``target_window`` also 'last-window' (``-l``), - 'next-window' (``-n``), or 'previous-window' (``-p``) - :type window: integer - - Returns the attached :class:`Window`. - - Todo: assure ``-l``, ``-n``, ``-p`` work. - ''' - - target = '-t%s' % target_window - - proc = self.tmux('select-window', target) - - if proc.stderr: - raise Exception(proc.stderr) - - return self.attached_window() - - def attached_pane(self): - ''' - Returns active :class:`Pane` object - ''' - return self.attached_window().attached_pane() - - def set_option(self, option, value): - ''' - wrapper for ``tmux(1)``:: - - $ tmux set-option