├── .codecov.yml ├── .cursor └── rules │ ├── avoid-debug-loops.mdc │ ├── dev-loop.mdc │ ├── git-commits.mdc │ ├── notes-llms-txt.mdc │ └── tmuxp-pytest.mdc ├── .github ├── contributing.md ├── dependabot.yml ├── issue_template.md ├── stale.yml └── workflows │ ├── docs.yml │ └── tests.yml ├── .gitignore ├── .prettierrc ├── .python-version ├── .tmuxp.json ├── .tmuxp.yaml ├── .tool-versions ├── .vim └── coc-settings.json ├── .windsurfrules ├── CHANGES ├── CITATION.cff ├── LICENSE ├── MIGRATION ├── Makefile ├── README.md ├── conftest.py ├── docs ├── Makefile ├── _ext │ ├── __init__.py │ └── aafig.py ├── _static │ ├── css │ │ └── custom.css │ ├── favicon.ico │ ├── img │ │ ├── books │ │ │ ├── amazon-logo.png │ │ │ └── ibooks-logo.png │ │ ├── icons │ │ │ ├── icon-128x128.png │ │ │ ├── icon-144x144.png │ │ │ ├── icon-152x152.png │ │ │ ├── icon-192x192.png │ │ │ ├── icon-384x384.png │ │ │ ├── icon-512x512.png │ │ │ ├── icon-72x72.png │ │ │ └── icon-96x96.png │ │ └── tmuxp.svg │ ├── tao-tmux-screenshot.png │ ├── tmuxp-demo.gif │ ├── tmuxp-dev-screenshot.png │ └── tmuxp-shell.gif ├── _templates │ ├── book.html │ ├── layout.html │ └── sidebar │ │ └── projects.html ├── about.md ├── about_tmux.md ├── api │ ├── cli │ │ ├── convert.md │ │ ├── debug_info.md │ │ ├── edit.md │ │ ├── freeze.md │ │ ├── import_config.md │ │ ├── index.md │ │ ├── load.md │ │ ├── ls.md │ │ ├── shell.md │ │ └── utils.md │ ├── exc.md │ ├── index.md │ ├── internals │ │ ├── config_reader.md │ │ ├── index.md │ │ └── types.md │ ├── log.md │ ├── plugin.md │ ├── shell.md │ ├── types.md │ ├── util.md │ └── workspace │ │ ├── builder.md │ │ ├── constants.md │ │ ├── finders.md │ │ ├── freezer.md │ │ ├── importers.md │ │ ├── index.md │ │ ├── loader.md │ │ └── validation.md ├── cli │ ├── completion.md │ ├── convert.md │ ├── debug-info.md │ ├── edit.md │ ├── freeze.md │ ├── import.md │ ├── index.md │ ├── load.md │ ├── ls.md │ └── shell.md ├── conf.py ├── configuration │ ├── environmental-variables.md │ ├── examples.md │ ├── index.md │ └── top-level.md ├── developing.md ├── glossary.md ├── history.md ├── index.md ├── manifest.json ├── migration.md ├── plugins │ └── index.md ├── quickstart.md └── redirects.txt ├── examples ├── 2-pane-synchronized.json ├── 2-pane-synchronized.yaml ├── 2-pane-vertical.json ├── 2-pane-vertical.yaml ├── 3-pane.json ├── 3-pane.yaml ├── 4-pane.json ├── 4-pane.yaml ├── blank-panes.json ├── blank-panes.yaml ├── env-variables.json ├── env-variables.yaml ├── focus-window-and-panes.json ├── focus-window-and-panes.yaml ├── main-pane-height-percentage.json ├── main-pane-height-percentage.yaml ├── main-pane-height.json ├── main-pane-height.yaml ├── minimal.yaml ├── options.json ├── options.yaml ├── pane-shell.json ├── pane-shell.yaml ├── plugin-system.json ├── plugin-system.yaml ├── session-environment.json ├── session-environment.yaml ├── shorthands.json ├── shorthands.yaml ├── skip-send-pane-level.json ├── skip-send-pane-level.yaml ├── skip-send.json ├── skip-send.yaml ├── sleep-pane-level.json ├── sleep-pane-level.yaml ├── sleep-virtualenv.yaml ├── sleep.json ├── sleep.yaml ├── start-directory.json ├── start-directory.yaml ├── suppress-history.json ├── suppress-history.yaml ├── window-index.json └── window-index.yaml ├── pyproject.toml ├── src └── tmuxp │ ├── __about__.py │ ├── __init__.py │ ├── _compat.py │ ├── _internal │ ├── __init__.py │ ├── config_reader.py │ └── types.py │ ├── cli │ ├── __init__.py │ ├── convert.py │ ├── debug_info.py │ ├── edit.py │ ├── freeze.py │ ├── import_config.py │ ├── load.py │ ├── ls.py │ ├── shell.py │ └── utils.py │ ├── exc.py │ ├── log.py │ ├── plugin.py │ ├── shell.py │ ├── types.py │ ├── util.py │ └── workspace │ ├── __init__.py │ ├── builder.py │ ├── constants.py │ ├── finders.py │ ├── freezer.py │ ├── importers.py │ ├── loader.py │ └── validation.py ├── tests ├── __init__.py ├── cli │ ├── __init__.py │ ├── test_cli.py │ ├── test_convert.py │ ├── test_debug_info.py │ ├── test_freeze.py │ ├── test_import.py │ ├── test_load.py │ ├── test_ls.py │ └── test_shell.py ├── constants.py ├── fixtures │ ├── __init__.py │ ├── import_teamocil │ │ ├── __init__.py │ │ ├── layouts.py │ │ ├── layouts.yaml │ │ ├── test1.py │ │ ├── test1.yaml │ │ ├── test2.py │ │ ├── test2.yaml │ │ ├── test3.py │ │ ├── test3.yaml │ │ ├── test4.py │ │ └── test4.yaml │ ├── import_tmuxinator │ │ ├── __init__.py │ │ ├── test1.py │ │ ├── test1.yaml │ │ ├── test2.py │ │ ├── test2.yaml │ │ ├── test3.py │ │ └── test3.yaml │ ├── pluginsystem │ │ ├── __init__.py │ │ ├── partials │ │ │ ├── __init__.py │ │ │ ├── _types.py │ │ │ ├── all_pass.py │ │ │ ├── libtmux_version_fail.py │ │ │ ├── test_plugin_helpers.py │ │ │ ├── tmux_version_fail.py │ │ │ └── tmuxp_version_fail.py │ │ └── plugins │ │ │ ├── tmuxp_test_plugin_awf │ │ │ ├── pyproject.toml │ │ │ └── tmuxp_test_plugin_awf │ │ │ │ ├── __init__.py │ │ │ │ └── plugin.py │ │ │ ├── tmuxp_test_plugin_bs │ │ │ ├── pyproject.toml │ │ │ └── tmuxp_test_plugin_bs │ │ │ │ ├── __init__.py │ │ │ │ └── plugin.py │ │ │ ├── tmuxp_test_plugin_bwb │ │ │ ├── pyproject.toml │ │ │ └── tmuxp_test_plugin_bwb │ │ │ │ ├── __init__.py │ │ │ │ └── plugin.py │ │ │ ├── tmuxp_test_plugin_fail │ │ │ ├── pyproject.toml │ │ │ └── tmuxp_test_plugin_fail │ │ │ │ ├── __init__.py │ │ │ │ └── plugin.py │ │ │ ├── tmuxp_test_plugin_owc │ │ │ ├── pyproject.toml │ │ │ └── tmuxp_test_plugin_owc │ │ │ │ ├── __init__.py │ │ │ │ └── plugin.py │ │ │ └── tmuxp_test_plugin_r │ │ │ ├── pyproject.toml │ │ │ └── tmuxp_test_plugin_r │ │ │ ├── __init__.py │ │ │ └── plugin.py │ ├── regressions │ │ └── issue_800_default_size_many_windows.yaml │ ├── script_complete.sh │ ├── script_failed.sh │ ├── structures.py │ ├── tmux │ │ └── tmux.conf │ ├── utils.py │ └── workspace │ │ ├── __init__.py │ │ ├── builder │ │ ├── config_script_completes.yaml │ │ ├── config_script_fails.yaml │ │ ├── config_script_not_exists.yaml │ │ ├── env_var_options.yaml │ │ ├── environment_vars.yaml │ │ ├── first_pane_start_directory.yaml │ │ ├── focus_and_pane.yaml │ │ ├── global_options.yaml │ │ ├── pane_ordering.yaml │ │ ├── plugin_awf.yaml │ │ ├── plugin_awf_multiple_windows.yaml │ │ ├── plugin_bs.yaml │ │ ├── plugin_bwb.yaml │ │ ├── plugin_missing_fail.yaml │ │ ├── plugin_multiple_plugins.yaml │ │ ├── plugin_owc.yaml │ │ ├── plugin_owc_multiple_windows.yaml │ │ ├── plugin_r.yaml │ │ ├── plugin_versions_fail.yaml │ │ ├── regression_00132_dots.yaml │ │ ├── session_options.yaml │ │ ├── start_directory.yaml │ │ ├── start_directory_relative.yaml │ │ ├── start_directory_session_path.yaml │ │ ├── suppress_history.yaml │ │ ├── three_pane.yaml │ │ ├── three_windows.yaml │ │ ├── two_pane.yaml │ │ ├── two_windows.yaml │ │ ├── window_automatic_rename.yaml │ │ ├── window_index.yaml │ │ ├── window_options.yaml │ │ ├── window_options_after.yaml │ │ └── window_shell.yaml │ │ ├── expand1.py │ │ ├── expand2-expanded.yaml │ │ ├── expand2-unexpanded.yaml │ │ ├── expand2.py │ │ ├── expand_blank.py │ │ ├── freezer │ │ └── sample_workspace.yaml │ │ ├── sample_workspace.py │ │ ├── shell_command_before.py │ │ ├── shell_command_before_session-expected.yaml │ │ ├── shell_command_before_session.py │ │ ├── shell_command_before_session.yaml │ │ └── trickle.py ├── test_plugin.py ├── test_shell.py ├── test_util.py ├── tests │ ├── __init__.py │ └── test_helpers.py └── workspace │ ├── __init__.py │ ├── conftest.py │ ├── test_builder.py │ ├── test_config.py │ ├── test_finder.py │ ├── test_freezer.py │ ├── test_import_teamocil.py │ └── test_import_tmuxinator.py └── uv.lock /.codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | notify: 3 | require_ci_to_pass: no 4 | 5 | coverage: 6 | precision: 2 7 | round: down 8 | range: "70...100" 9 | status: 10 | project: 11 | default: 12 | target: auto 13 | threshold: 1% 14 | base: auto 15 | patch: off 16 | -------------------------------------------------------------------------------- /.cursor/rules/avoid-debug-loops.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | 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 3 | globs: *.py 4 | alwaysApply: true 5 | --- 6 | # Avoid Debug Loops 7 | 8 | When debugging becomes circular and unproductive, follow these steps: 9 | 10 | ## Detection 11 | - You have made multiple unsuccessful attempts to fix the same issue 12 | - You are adding increasingly complex code to address errors 13 | - Each fix creates new errors in a cascading pattern 14 | - You are uncertain about the root cause after 2-3 iterations 15 | 16 | ## Action Plan 17 | 18 | 1. **Pause and acknowledge the loop** 19 | - Explicitly state that you are in a potential debug loop 20 | - Review what approaches have been tried and failed 21 | 22 | 2. **Minimize to MVP** 23 | - Remove all debugging cruft and experimental code 24 | - Revert to the simplest version that demonstrates the issue 25 | - Focus on isolating the core problem without added complexity 26 | 27 | 3. **Comprehensive Documentation** 28 | - Provide a clear summary of the issue 29 | - Include minimal but complete code examples that reproduce the problem 30 | - Document exact error messages and unexpected behaviors 31 | - Explain your current understanding of potential causes 32 | 33 | 4. **Format for Portability** 34 | - Present the problem in quadruple backticks for easy copying: 35 | 36 | ```` 37 | # Problem Summary 38 | [Concise explanation of the issue] 39 | 40 | ## Minimal Reproduction Code 41 | ```python 42 | # Minimal code example that reproduces the issue 43 | ``` 44 | 45 | ## Error/Unexpected Output 46 | ``` 47 | [Exact error messages or unexpected output] 48 | ``` 49 | 50 | ## Failed Approaches 51 | [Brief summary of approaches already tried] 52 | 53 | ## Suspected Cause 54 | [Your current hypothesis about what might be causing the issue] 55 | ```` 56 | 57 | This format enables the user to easily copy the entire problem statement into a fresh conversation for a clean-slate approach. 58 | -------------------------------------------------------------------------------- /.cursor/rules/git-commits.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: git-commits: Git commit message standards and AI assistance 3 | globs: git-commits: Git commit message standards and AI assistance | *.git/* .gitignore .github/* CHANGELOG.md CHANGES.md 4 | alwaysApply: true 5 | --- 6 | # Optimized Git Commit Standards 7 | 8 | ## Commit Message Format 9 | ``` 10 | Component/File(commit-type[Subcomponent/method]): Concise description 11 | 12 | why: Explanation of necessity or impact. 13 | what: 14 | - Specific technical changes made 15 | - Focused on a single topic 16 | 17 | refs: #issue-number, breaking changes, or relevant links 18 | ``` 19 | 20 | ## Component Patterns 21 | ### General Code Changes 22 | ``` 23 | Component/File(feat[method]): Add feature 24 | Component/File(fix[method]): Fix bug 25 | Component/File(refactor[method]): Code restructure 26 | ``` 27 | 28 | ### Packages and Dependencies 29 | | Language | Standard Packages | Dev Packages | Extras / Sub-packages | 30 | |------------|------------------------------------|-------------------------------|-----------------------------------------------| 31 | | General | `lang(deps):` | `lang(deps[dev]):` | | 32 | | Python | `py(deps):` | `py(deps[dev]):` | `py(deps[extra]):` | 33 | | JavaScript | `js(deps):` | `js(deps[dev]):` | `js(deps[subpackage]):`, `js(deps[dev{subpackage}]):` | 34 | 35 | #### Examples 36 | - `py(deps[dev]): Update pytest to v8.1` 37 | - `js(deps[ui-components]): Upgrade Button component package` 38 | - `js(deps[dev{linting}]): Add ESLint plugin` 39 | 40 | ### Documentation Changes 41 | Prefix with `docs:` 42 | ``` 43 | docs(Component/File[Subcomponent/method]): Update API usage guide 44 | ``` 45 | 46 | ### Test Changes 47 | Prefix with `tests:` 48 | ``` 49 | tests(Component/File[Subcomponent/method]): Add edge case tests 50 | ``` 51 | 52 | ## Commit Types Summary 53 | - **feat**: New features or enhancements 54 | - **fix**: Bug fixes 55 | - **refactor**: Code restructuring without functional change 56 | - **docs**: Documentation updates 57 | - **chore**: Maintenance (dependencies, tooling, config) 58 | - **test**: Test-related updates 59 | - **style**: Code style and formatting 60 | 61 | ## General Guidelines 62 | - Subject line: Maximum 50 characters 63 | - Body lines: Maximum 72 characters 64 | - Use imperative mood (e.g., "Add", "Fix", not "Added", "Fixed") 65 | - Limit to one topic per commit 66 | - Separate subject from body with a blank line 67 | - Mark breaking changes clearly: `BREAKING:` 68 | - Use `See also:` to provide external references 69 | 70 | ## AI Assistance Workflow in Cursor 71 | - Stage changes with `git add` 72 | - Use `@commit` to generate initial commit message 73 | - Review and refine generated message 74 | - Ensure adherence to these standards 75 | 76 | ## Good Commit Example 77 | ``` 78 | Pane(feat[capture_pane]): Add screenshot capture support 79 | 80 | why: Provide visual debugging capability 81 | what: 82 | - Implement capturePane method with image export 83 | - Integrate with existing Pane component logic 84 | - Document usage in Pane README 85 | 86 | refs: #485 87 | See also: https://example.com/docs/pane-capture 88 | ``` 89 | 90 | ## Bad Commit Example 91 | ``` 92 | fixed stuff and improved some functions 93 | ``` 94 | 95 | These guidelines ensure clear, consistent commit histories, facilitating easier code review and maintenance. -------------------------------------------------------------------------------- /.cursor/rules/notes-llms-txt.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: LLM-friendly markdown format for notes directories 3 | globs: notes/**/*.md,**/notes/**/*.md 4 | alwaysApply: true 5 | --- 6 | 7 | # Instructions for Generating LLM-Optimized Markdown Content 8 | 9 | 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: 10 | 11 | 1. **Conciseness and Clarity**: 12 | - **Be Brief**: Present information succinctly, avoiding unnecessary elaboration. 13 | - **Use Clear Language**: Employ straightforward language to convey ideas effectively. 14 | 15 | 2. **Structured Formatting**: 16 | - **Headings**: Utilize markdown headings (`#`, `##`, `###`, etc.) to organize content hierarchically. 17 | - **Lists**: Use bullet points (`-`) or numbered lists (`1.`, `2.`, etc.) to enumerate items clearly. 18 | - **Code Blocks**: Enclose code snippets within triple backticks (```) to distinguish them from regular text. 19 | 20 | 3. **Semantic Elements**: 21 | - **Emphasis**: Use asterisks (`*`) or underscores (`_`) for italicizing text to denote emphasis. 22 | - **Strong Emphasis**: Use double asterisks (`**`) or double underscores (`__`) for bold text to highlight critical points. 23 | - **Inline Code**: Use single backticks (`) for inline code references. 24 | 25 | 4. **Linking and References**: 26 | - **Hyperlinks**: Format links using `[Link Text](mdc:URL)` to provide direct access to external resources. 27 | - **References**: When citing sources, use footnotes or inline citations to maintain readability. 28 | 29 | 5. **Avoid Redundancy**: 30 | - **Eliminate Repetition**: Ensure that information is not unnecessarily repeated within the document. 31 | - **Use Summaries**: Provide brief summaries where detailed explanations are not essential. 32 | 33 | 6. **Standard Compliance**: 34 | - **llms.txt Conformance**: Structure the document in alignment with the `llms.txt` standard, which includes: 35 | - An H1 heading with the project or site name. 36 | - A blockquote summarizing the project's purpose. 37 | - Additional markdown sections providing detailed information. 38 | - H2-delimited sections containing lists of URLs for further details. 39 | 40 | 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. 41 | 42 | For more information on the `llms.txt` standard, refer to the official documentation: https://llmstxt.org/ 43 | -------------------------------------------------------------------------------- /.cursor/rules/tmuxp-pytest.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: Guidelines for using pytest with tmuxp, including libtmux fixtures 3 | globs: tests/**/test_*.py 4 | alwaysApply: true 5 | --- 6 | 7 | # tmuxp Pytest Guidelines 8 | 9 | ## Leveraging libtmux fixtures 10 | 11 | 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. 12 | 13 | ### Available fixtures 14 | 15 | The following fixtures are available through libtmux's pytest plugin: 16 | 17 | - `server`: Creates a temporary tmux server with isolated socket 18 | - `session`: Creates a temporary tmux session in the server 19 | - `window`: Creates a temporary tmux window in the session 20 | - `pane`: Creates a temporary tmux pane in the pane 21 | - `TestServer`: Factory for creating multiple independent servers with unique socket names 22 | 23 | ### Usage in tests 24 | 25 | ```python 26 | def test_something_with_server(server): 27 | # server is already running with proper configuration 28 | my_session = server.new_session("test-session") 29 | assert server.is_alive() 30 | assert my_session in server.sessions 31 | 32 | def test_something_with_session(session): 33 | # session is already created and configured 34 | new_window = session.new_window("test-window") 35 | assert new_window in session.windows 36 | 37 | def test_with_multiple_servers(TestServer): 38 | # Create independent servers for comparison tests 39 | Server1 = TestServer() 40 | Server2 = TestServer() 41 | 42 | server1 = Server1() 43 | server2 = Server2() 44 | 45 | session1 = server1.new_session() 46 | session2 = server2.new_session() 47 | 48 | assert server1.socket_path != server2.socket_path 49 | ``` 50 | 51 | ### Customizing session parameters 52 | 53 | You can override the `session_params` fixture to customize session creation: 54 | 55 | ```python 56 | import pytest 57 | 58 | @pytest.fixture 59 | def session_params(): 60 | return { 61 | 'x': 800, 62 | 'y': 600, 63 | 'window_name': 'custom-window' 64 | } 65 | ``` 66 | 67 | ### Benefits 68 | 69 | - No need to manually set up and tear down tmux infrastructure 70 | - Tests run in isolated tmux environments 71 | - Faster test execution 72 | - Reliable test environment with predictable configuration 73 | - Multiple tests can run in parallel without tmux session conflicts 74 | 75 | For more details, see: 76 | - [libtmux pytest plugin documentation](https://libtmux.git-pull.com/pytest-plugin/index.html) 77 | - [libtmux pytest plugin code](https://github.com/tmux-python/libtmux/blob/master/src/libtmux/pytest_plugin.py) 78 | -------------------------------------------------------------------------------- /.github/contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | When contributing to this repository, please first discuss the change you wish to make via issue, 4 | email, or any other method with the maintainers of this repository before making a change. 5 | 6 | Please note we have a code of conduct, please follow it in all your interactions with the project. 7 | 8 | ## Pull Request Process 9 | 10 | 1. Ensure any install or build dependencies are removed before the end of the layer when doing a 11 | build. 12 | 2. This project uses flake8 to conform with common Python standards. Make sure 13 | to run your code through linter using latest version of flake8, before pull request. 14 | 3. Bad documnentation is a Bug. If your change demands documentation update, please do so. If you 15 | find an issue with documentation, take the time to improve or fix it. 16 | 4. pytest is used for automated testing. Please make sure to update tests that are needed, and to run 17 | `make test` before submitting your pull request. This should prevent issues with TravisCI and 18 | make the review and merging process easier and faster. 19 | 5. Update the README.md with details of changes to the interface, this includes new environment 20 | variables, exposed ports, useful file locations and container parameters. 21 | 6. Increase the version numbers in any examples files and the README.md to the new version that this 22 | Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/). 23 | 7. You may merge the Pull Request in once you have the sign-off of one other developer. If you 24 | do not have permission to do that, you may request reviewer to merge it for you. 25 | 26 | ## Decorum 27 | 28 | - Participants will be tolerant of opposing views. 29 | - Participants must ensure that their language and actions are free of personal 30 | attacks and disparaging personal remarks. 31 | - When interpreting the words and actions of others, participants should always 32 | assume good intentions. 33 | - Behaviour which can be reasonably considered harassment will not be tolerated. 34 | 35 | Based on [Ruby's Community Conduct Guideline](https://www.ruby-lang.org/en/conduct/) 36 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | # Check for updates to GitHub Actions every week 7 | interval: "weekly" 8 | -------------------------------------------------------------------------------- /.github/issue_template.md: -------------------------------------------------------------------------------- 1 | Are you using the latest version of tmuxp? Check `tmuxp -V` against 2 | https://pypi.org/project/tmuxp/. If it's not the latest, consider updating, e.g. 3 | `pip install --user tmuxp`. 4 | 5 | ### Step 1: Provide a summary of your problem 6 | 7 | - For general technical questions, problems or feature requests related to the code **in this repository** file an issue. 8 | 9 | ### Step 2: Provide tmuxp details 10 | 11 | - Include output of `tmuxp debug-info` 12 | - Include any other pertinant system info not captured 13 | 14 | ### Step 3: Describe the problem: 15 | 16 | #### Steps to reproduce: 17 | 18 | 1. ... 19 | 2. ... 20 | 3. ... 21 | 22 | #### Observed Results: 23 | 24 | - What happened? This could be a description, log output, etc. 25 | 26 | #### Expected Results: 27 | 28 | - What did you expect to happen? 29 | 30 | #### Relevant Code: 31 | 32 | ``` 33 | // TODO(you): paste here tmuxp configuration (YAML / JSON) you are having issues with. 34 | ``` 35 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 60 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 7 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - pinned 8 | - security 9 | - enhancement 10 | # Label to use when marking an issue as stale 11 | staleLabel: stale 12 | # Comment to post when marking an issue as stale. Set to `false` to disable 13 | markComment: | 14 | This issue has been automatically marked as stale because it has not had 15 | recent activity. It will be closed if no further activity occurs. Thank you 16 | for your contributions. 17 | 18 | This bot is used to handle issues where the issue hasn't been discussed or 19 | has gone out of date. If an issue isn't resolved and handled in a certain 20 | period of time, it may be closed. If you would like your issue re-opened, 21 | please create a fresh issue with the latest, up to date information and 22 | mention this issue in it. 23 | unmarkComment: | 24 | Thank you for updating this issue. It is no longer marked as stale. 25 | 26 | # Comment to post when closing a stale issue. Set to `false` to disable 27 | closeComment: false 28 | 29 | # limit to only 'issues' or 'pulls' 30 | only: issues 31 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: docs 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | strategy: 12 | matrix: 13 | python-version: ['3.13'] 14 | steps: 15 | - uses: actions/checkout@v4 16 | - name: Filter changed file paths to outputs 17 | uses: dorny/paths-filter@v3.0.2 18 | id: changes 19 | with: 20 | filters: | 21 | root_docs: 22 | - CHANGES 23 | - README.* 24 | docs: 25 | - 'docs/**' 26 | - 'examples/**' 27 | python_files: 28 | - 'src/tmuxp/**' 29 | - pyproject.toml 30 | - uv.lock 31 | 32 | - name: Should publish 33 | if: steps.changes.outputs.docs == 'true' || steps.changes.outputs.root_docs == 'true' || steps.changes.outputs.python_files == 'true' 34 | run: echo "PUBLISH=$(echo true)" >> $GITHUB_ENV 35 | 36 | - name: Install uv 37 | uses: astral-sh/setup-uv@v5 38 | if: env.PUBLISH == 'true' 39 | with: 40 | enable-cache: true 41 | 42 | - name: Set up Python ${{ matrix.python-version }} 43 | if: env.PUBLISH == 'true' 44 | run: uv python install ${{ matrix.python-version }} 45 | 46 | - name: Install dependencies 47 | if: env.PUBLISH == 'true' 48 | run: uv sync --all-extras --dev 49 | 50 | - name: Print python versions 51 | if: env.PUBLISH == 'true' 52 | run: | 53 | python -V 54 | uv run python -V 55 | 56 | - name: Build documentation 57 | if: env.PUBLISH == 'true' 58 | run: | 59 | pushd docs; make SPHINXBUILD='uv run sphinx-build' html; popd 60 | 61 | - name: Push documentation to S3 62 | if: env.PUBLISH == 'true' 63 | uses: jakejarvis/s3-sync-action@master 64 | with: 65 | args: --acl public-read --follow-symlinks --delete 66 | env: 67 | AWS_S3_BUCKET: ${{ secrets.AWS_S3_BUCKET }} 68 | AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} 69 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 70 | AWS_REGION: 'us-west-1' # optional: defaults to us-east-1 71 | SOURCE_DIR: 'docs/_build/html' # optional: defaults to entire repository 72 | 73 | - name: Purge cache on Cloudflare 74 | if: env.PUBLISH == 'true' 75 | uses: jakejarvis/cloudflare-purge-action@v0.3.0 76 | env: 77 | CLOUDFLARE_TOKEN: ${{ secrets.CLOUDFLARE_TOKEN }} 78 | CLOUDFLARE_ZONE: ${{ secrets.CLOUDFLARE_ZONE }} 79 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # macOS 2 | .DS_Store 3 | 4 | # Byte-compiled / optimized / DLL files 5 | __pycache__/ 6 | *.py[cod] 7 | *$py.class 8 | 9 | # C extensions 10 | *.so 11 | 12 | # Distribution / packaging 13 | .Python 14 | *env*/ 15 | build/ 16 | develop-eggs/ 17 | dist/ 18 | downloads/ 19 | eggs/ 20 | .eggs/ 21 | lib/ 22 | lib64/ 23 | parts/ 24 | sdist/ 25 | var/ 26 | *.egg-info/ 27 | .installed.cfg 28 | *.egg 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | .hypothesis/ 50 | .pytest_cache/ 51 | 52 | # Translations 53 | *.mo 54 | *.pot 55 | 56 | # Django stuff: 57 | *.log 58 | 59 | # Sphinx documentation 60 | docs/_build/ 61 | 62 | # PyBuilder 63 | target/ 64 | 65 | # Ipython Notebook 66 | .ipynb_checkpoints 67 | 68 | # mypy 69 | .mypy_cache/ 70 | 71 | # editors 72 | .idea 73 | .ropeproject 74 | *.swp 75 | .vscode 76 | 77 | # docs 78 | doc/_build/ 79 | 80 | # MonkeyType 81 | monkeytype.sqlite3 82 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 100 3 | } 4 | -------------------------------------------------------------------------------- /.python-version: -------------------------------------------------------------------------------- 1 | 3.13.0 2 | -------------------------------------------------------------------------------- /.tmuxp.json: -------------------------------------------------------------------------------- 1 | { 2 | "session_name": "tmuxp", 3 | "start_directory": "./", 4 | "shell_command_before": [ 5 | "uv virtualenv --quiet > /dev/null 2>&1 && clear" 6 | ], 7 | "windows": [ 8 | { 9 | "window_name": "tmuxp", 10 | "focus": true, 11 | "layout": "main-horizontal", 12 | "options": { 13 | "main-pane-height": "67%" 14 | }, 15 | "panes": [ 16 | { 17 | "focus": true 18 | }, 19 | "pane", 20 | "make watch_mypy", 21 | "make watch_test" 22 | ] 23 | }, 24 | { 25 | "window_name": "docs", 26 | "layout": "main-horizontal", 27 | "options": { 28 | "main-pane-height": "67%" 29 | }, 30 | "start_directory": "docs/", 31 | "panes": [ 32 | { 33 | "focus": true 34 | }, 35 | "pane", 36 | "pane", 37 | "make start" 38 | ] 39 | } 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /.tmuxp.yaml: -------------------------------------------------------------------------------- 1 | session_name: tmuxp 2 | start_directory: ./ # load session relative to config location (project root). 3 | shell_command_before: 4 | - uv virtualenv --quiet > /dev/null 2>&1 && clear 5 | windows: 6 | - window_name: tmuxp 7 | focus: True 8 | layout: main-horizontal 9 | options: 10 | main-pane-height: 67% 11 | panes: 12 | - focus: true 13 | - pane 14 | - make watch_mypy 15 | - make watch_test 16 | - window_name: docs 17 | layout: main-horizontal 18 | options: 19 | main-pane-height: 67% 20 | start_directory: docs/ 21 | panes: 22 | - focus: true 23 | - pane 24 | - pane 25 | - make start 26 | -------------------------------------------------------------------------------- /.tool-versions: -------------------------------------------------------------------------------- 1 | uv 0.7.9 2 | python 3.13.3 3.12.10 3.11.12 3.10.17 3.9.22 3.8.20 3.7.17 3 | -------------------------------------------------------------------------------- /.vim/coc-settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "[markdown][python]": { 3 | "coc.preferences.formatOnSave": true 4 | }, 5 | "python.analysis.autoSearchPaths": true, 6 | "python.analysis.typeCheckingMode": "basic", 7 | "python.analysis.useLibraryCodeForTypes": true, 8 | "python.formatting.provider": "ruff", 9 | "python.linting.ruffEnabled": true, 10 | "python.linting.mypyEnabled": true, 11 | "python.linting.flake8Enabled": false, 12 | "python.linting.pyflakesEnabled": false, 13 | "python.linting.pycodestyleEnabled": false, 14 | "python.linting.banditEnabled": false, 15 | "python.linting.pylamaEnabled": false, 16 | "python.linting.pylintEnabled": false, 17 | "pyright.organizeimports.provider": "ruff", 18 | "pyright.testing.provider": "pytest", 19 | } 20 | -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | cff-version: 1.2.0 2 | message: >- 3 | If you use this software, please cite it as below. 4 | NOTE: Change "x.y" by the version you use. If you are unsure about which version 5 | you are using run: `pip show tmuxp`." 6 | authors: 7 | - family-names: "Narlock" 8 | given-names: "Tony" 9 | orcid: "https://orcid.org/0000-0002-2568-415X" 10 | title: "tmuxp" 11 | type: software 12 | version: x.y 13 | url: "https://tmuxp.git-pull.com" 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2013- tmuxp contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MIGRATION: -------------------------------------------------------------------------------- 1 | # Migration notes 2 | 3 | Migration and deprecation notes for tmuxp are here, see {ref}`changelog` as 4 | well. 5 | 6 | ```{admonition} Welcome on board! 👋 7 | 1. 📌 For safety, **always** pin the package 8 | 2. 📖 Check the migration notes _(You are here)_ 9 | 3. 📣 If you feel something got deprecated and it interrupted you - past, present, or future - voice your opinion on the [tracker]. 10 | 11 | We want to make tmuxp fun, reliable, and useful for users. 12 | 13 | API changes can be painful. 14 | 15 | 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! 16 | 17 | (Please pin the package. 🙏) 18 | 19 | [tracker]: https://github.com/tmux-python/tmuxp/discussions 20 | ``` 21 | 22 | ## Next release 23 | 24 | _Notes on the upcoming release will be added here_ 25 | 26 | 27 | 28 | ## tmuxp 1.18.0 (2022-10-30) 29 | 30 | **Restructuring** (#840) 31 | 32 | "Config files" and "configs" are now referred to as workspace files. 33 | 34 | Additionally, there's been a major file structure update: 35 | 36 | - `cli/utils.py` functions moved to `workspace/finders.py` 37 | - `config.py` split between: 38 | 39 | - `workspace/finders.py` 40 | - `workspace/freezer.py` 41 | - `workspace/importers.py` 42 | - `workspace/validation.py` 43 | 44 | - `workspacebuilder.py` split into: 45 | 46 | - `workspace/builder.py` 47 | - `workspace/freezer.py` 48 | 49 | `config.inline` moved to freezer 50 | 51 | Tests: 52 | 53 | - `tests/fixtures/{workspacebuilder,workspacefreezer}` -> `tests/fixtures/workspace/{builder,freezer}` 54 | - `tests/test_import_{teamocil,tmuxinator}.py` -> `tests/workspace/test_import_{teamocil,tmuxinator}.py` 55 | 56 | ## tmuxp 1.17.0 (2022-10-09) 57 | 58 | **Completions have changed** (#830) 59 | 60 | Completions now use a different tool: [shtab]. See the [completions page] for more information. 61 | 62 | If you were using earlier versions of tmuxp (earlier than 1.17.0), you may need to uninstall the old completions, first. 63 | 64 | [completions page]: https://tmuxp.git-pull.com/cli/completion.html 65 | [shtab]: https://docs.iterative.ai/shtab/ 66 | 67 | 70 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PY_FILES= find . -type f -not -path '*/\.*' | grep -i '.*[.]py$$' 2> /dev/null 2 | TEST_FILES= find . -type f -not -path '*/\.*' | grep -i '.*[.]\(yaml\|py\)$$' 2> /dev/null 3 | DOC_FILES= find . -type f -not -path '*/\.*' | grep -i '.*[.]rst\$\|.*[.]md\$\|.*[.]css\$\|.*[.]py\$\|mkdocs\.yml\|CHANGES\|TODO\|.*conf\.py' 2> /dev/null 4 | SHELL := /bin/bash 5 | 6 | entr_warn: 7 | @echo "----------------------------------------------------------" 8 | @echo " ! File watching functionality non-operational ! " 9 | @echo " " 10 | @echo "Install entr(1) to automatically run tasks on file change." 11 | @echo "See https://eradman.com/entrproject/ " 12 | @echo "----------------------------------------------------------" 13 | 14 | test: 15 | uv run py.test $(test) 16 | 17 | start: 18 | $(MAKE) test; uv run ptw . 19 | 20 | watch_test: 21 | if command -v entr > /dev/null; then ${TEST_FILES} | entr -c $(MAKE) test; else $(MAKE) test entr_warn; fi 22 | 23 | build_docs: 24 | $(MAKE) -C docs html 25 | 26 | watch_docs: 27 | if command -v entr > /dev/null; then ${DOC_FILES} | entr -c $(MAKE) build_docs; else $(MAKE) build_docs entr_warn; fi 28 | 29 | serve_docs: 30 | $(MAKE) -C docs serve 31 | 32 | dev_docs: 33 | $(MAKE) -j watch_docs serve_docs 34 | 35 | start_docs: 36 | $(MAKE) -C docs start 37 | 38 | design_docs: 39 | $(MAKE) -C docs design 40 | 41 | ruff_format: 42 | uv run ruff format . 43 | 44 | ruff: 45 | uv run ruff check . 46 | 47 | watch_ruff: 48 | if command -v entr > /dev/null; then ${PY_FILES} | entr -c $(MAKE) ruff; else $(MAKE) ruff entr_warn; fi 49 | 50 | mypy: 51 | uv run mypy `${PY_FILES}` 52 | 53 | watch_mypy: 54 | if command -v entr > /dev/null; then ${PY_FILES} | entr -c $(MAKE) mypy; else $(MAKE) mypy entr_warn; fi 55 | 56 | format_markdown: 57 | npx prettier --parser=markdown -w *.md docs/*.md docs/**/*.md CHANGES 58 | 59 | monkeytype_create: 60 | uv run monkeytype run `uv run which py.test` 61 | 62 | monkeytype_apply: 63 | uv run monkeytype list-modules | xargs -n1 -I{} sh -c 'uv run monkeytype apply {}' 64 | -------------------------------------------------------------------------------- /docs/_ext/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmux-python/tmuxp/d9d4038baa7ecee9623fd770703b544ecec78ad0/docs/_ext/__init__.py -------------------------------------------------------------------------------- /docs/_static/css/custom.css: -------------------------------------------------------------------------------- 1 | .sidebar-tree p.indented-block { 2 | padding: var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal) 0 3 | var(--sidebar-item-spacing-horizontal); 4 | margin-bottom: 0; 5 | } 6 | 7 | .sidebar-tree p.indented-block span.indent { 8 | margin-left: var(--sidebar-item-spacing-horizontal); 9 | display: block; 10 | } 11 | 12 | .sidebar-tree p.indented-block .project-name { 13 | font-size: var(--sidebar-item-font-size); 14 | font-weight: bold; 15 | margin-right: calc(var(--sidebar-item-spacing-horizontal) / 2.5); 16 | } 17 | 18 | .sidebar-tree .active { 19 | font-weight: bold; 20 | } 21 | -------------------------------------------------------------------------------- /docs/_static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmux-python/tmuxp/d9d4038baa7ecee9623fd770703b544ecec78ad0/docs/_static/favicon.ico -------------------------------------------------------------------------------- /docs/_static/img/books/amazon-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmux-python/tmuxp/d9d4038baa7ecee9623fd770703b544ecec78ad0/docs/_static/img/books/amazon-logo.png -------------------------------------------------------------------------------- /docs/_static/img/books/ibooks-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmux-python/tmuxp/d9d4038baa7ecee9623fd770703b544ecec78ad0/docs/_static/img/books/ibooks-logo.png -------------------------------------------------------------------------------- /docs/_static/img/icons/icon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmux-python/tmuxp/d9d4038baa7ecee9623fd770703b544ecec78ad0/docs/_static/img/icons/icon-128x128.png -------------------------------------------------------------------------------- /docs/_static/img/icons/icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmux-python/tmuxp/d9d4038baa7ecee9623fd770703b544ecec78ad0/docs/_static/img/icons/icon-144x144.png -------------------------------------------------------------------------------- /docs/_static/img/icons/icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmux-python/tmuxp/d9d4038baa7ecee9623fd770703b544ecec78ad0/docs/_static/img/icons/icon-152x152.png -------------------------------------------------------------------------------- /docs/_static/img/icons/icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmux-python/tmuxp/d9d4038baa7ecee9623fd770703b544ecec78ad0/docs/_static/img/icons/icon-192x192.png -------------------------------------------------------------------------------- /docs/_static/img/icons/icon-384x384.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmux-python/tmuxp/d9d4038baa7ecee9623fd770703b544ecec78ad0/docs/_static/img/icons/icon-384x384.png -------------------------------------------------------------------------------- /docs/_static/img/icons/icon-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmux-python/tmuxp/d9d4038baa7ecee9623fd770703b544ecec78ad0/docs/_static/img/icons/icon-512x512.png -------------------------------------------------------------------------------- /docs/_static/img/icons/icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmux-python/tmuxp/d9d4038baa7ecee9623fd770703b544ecec78ad0/docs/_static/img/icons/icon-72x72.png -------------------------------------------------------------------------------- /docs/_static/img/icons/icon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmux-python/tmuxp/d9d4038baa7ecee9623fd770703b544ecec78ad0/docs/_static/img/icons/icon-96x96.png -------------------------------------------------------------------------------- /docs/_static/tao-tmux-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmux-python/tmuxp/d9d4038baa7ecee9623fd770703b544ecec78ad0/docs/_static/tao-tmux-screenshot.png -------------------------------------------------------------------------------- /docs/_static/tmuxp-demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmux-python/tmuxp/d9d4038baa7ecee9623fd770703b544ecec78ad0/docs/_static/tmuxp-demo.gif -------------------------------------------------------------------------------- /docs/_static/tmuxp-dev-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmux-python/tmuxp/d9d4038baa7ecee9623fd770703b544ecec78ad0/docs/_static/tmuxp-dev-screenshot.png -------------------------------------------------------------------------------- /docs/_static/tmuxp-shell.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmux-python/tmuxp/d9d4038baa7ecee9623fd770703b544ecec78ad0/docs/_static/tmuxp-shell.gif -------------------------------------------------------------------------------- /docs/_templates/book.html: -------------------------------------------------------------------------------- 1 |

The book!

2 | 3 |

4 | 5 |

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

6 |

Read and browse the book for free on the web.

7 | Amazon Kindle 8 | -------------------------------------------------------------------------------- /docs/_templates/layout.html: -------------------------------------------------------------------------------- 1 | {% extends "!layout.html" %} 2 | {%- block extrahead %} 3 | {{ super() }} 4 | {%- if theme_show_meta_manifest_tag == true %} 5 | 6 | {% endif -%} 7 | {%- if theme_show_meta_og_tags == true %} 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | {% endif -%} 24 | {%- if theme_show_meta_app_icon_tags == true %} 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | {% endif -%} 45 | {% endblock %} 46 | -------------------------------------------------------------------------------- /docs/_templates/sidebar/projects.html: -------------------------------------------------------------------------------- 1 | 56 | 70 | -------------------------------------------------------------------------------- /docs/about.md: -------------------------------------------------------------------------------- 1 | ```{module} tmuxp 2 | 3 | ``` 4 | 5 | (about)= 6 | 7 | # About 8 | 9 | tmuxp helps you manage tmux workspaces. 10 | 11 | Built on an object relational mapper for tmux. tmux users can reload common 12 | workspaces from YAML, JSON and {py:obj}`dict` workspace files like 13 | [tmuxinator] and [teamocil]. 14 | 15 | tmuxp is used by developers for tmux automation at great companies like 16 | [Bugsnag], [Pragmatic Coders] and many others. 17 | 18 | To jump right in, see {ref}`quickstart` and {ref}`examples`. 19 | 20 | Interested in some kung-fu or joining the effort? {ref}`api` and 21 | {ref}`developing`. 22 | 23 | [MIT-licensed]. Code on [github](http://github.com/tmux-python/tmuxp). 24 | 25 | [bugsnag]: https://blog.bugsnag.com/benefits-of-using-tmux/ 26 | [pragmatic coders]: http://pragmaticcoders.com/blog/tmuxp-preconfigured-sessions/ 27 | 28 | ## Compared to tmuxinator / teamocil 29 | 30 | ### Similarities 31 | 32 | **Load sessions** Loads tmux sessions from config 33 | 34 | **YAML** Supports YAML format 35 | 36 | **Inlining / shorthand configuration** All three support short-hand and 37 | simplified markup for panes that have one command. 38 | 39 | **Maturity and stability** As of 2016, all three are considered stable, 40 | well tested and adopted. 41 | 42 | ### Missing 43 | 44 | **Version support** tmuxp only supports `tmux >= 1.8`. Teamocil and 45 | tmuxinator may have support for earlier versions. 46 | 47 | ### Differences 48 | 49 | **Programming Language** python. teamocil and tmuxinator use ruby. 50 | 51 | **Workspace building process** teamocil and tmuxinator process configs 52 | directly shell commands. tmuxp processes configuration via ORM layer. 53 | 54 | ## Additional Features 55 | 56 | **CLI** tmuxp's CLI can attach and kill sessions with tab-completion 57 | support. See {ref}`commands`. 58 | 59 | **Import config** import configs from Teamocil / Tmuxinator [^id4]. See 60 | {ref}`cli-import`. 61 | 62 | **Session freezing** Supports session freezing into YAML and JSON 63 | format [^id4]. See {ref}`cli-freeze`. 64 | 65 | **JSON config** JSON config support. See {ref}`Examples`. 66 | 67 | **ORM-based API** via [libtmux] - Utilizes tmux >= 1.8's unique ID's for 68 | panes, windows and sessions to create an object relational view of the tmux 69 | {class}`~libtmux.Server`, its {class}`~libtmux.Session`, 70 | {class}`~libtmux.Window`, and {class}`~libtmux.Pane`. 71 | See {ref}`libtmux's internals `. 72 | 73 | **Conversion** `$ tmuxp convert ` can convert files to and 74 | from JSON and YAML. 75 | 76 | [^id4]: On freezing 77 | 78 | While freezing and importing sessions is a great way to save time, 79 | tweaking will probably be required - There is no substitute to a 80 | config made with love. 81 | 82 | [libtmux]: https://libtmux.git-pull.com 83 | 84 | ## Minor tweaks 85 | 86 | - Unit tests against live tmux version to test statefulness of tmux 87 | sessions, windows and panes. See {ref}`gh-actions`. 88 | - Load + switch to new session from inside tmux. 89 | - Resume session if config loaded. 90 | - Pre-commands virtualenv / rvm / any other commands. 91 | - Load config from anywhere `$ tmuxp load /full/file/path.json`. 92 | - Load config `.tmuxp.yaml` or `.tmuxp.json` from current working 93 | directory with `$ tmuxp load .`. 94 | - `$ tmuxp -2`, `$ tmuxp -8` for forcing term colors a la 95 | {term}`tmux(1)`. 96 | - `$ tmuxp -L`, `$ tmuxp -S` for sockets and 97 | `$ tmuxp -f ` for config file. 98 | 99 | [attempt at 1.7 test]: https://travis-ci.org/tmux-python/tmuxp/jobs/12348263 100 | [mit-licensed]: http://opensource.org/licenses/MIT 101 | [tmuxinator]: https://github.com/aziz/tmuxinator 102 | [teamocil]: https://github.com/remiprev/teamocil 103 | [erb]: http://ruby-doc.org/stdlib-2.0.0/libdoc/erb/rdoc/ERB.html 104 | [edit this page]: https://github.com/tmux-python/tmuxp/edit/master/doc/about.rst 105 | -------------------------------------------------------------------------------- /docs/api/cli/convert.md: -------------------------------------------------------------------------------- 1 | # tmuxp convert - `tmuxp.cli.convert` 2 | 3 | ```{eval-rst} 4 | .. automodule:: tmuxp.cli.convert 5 | :members: 6 | :show-inheritance: 7 | :undoc-members: 8 | ``` 9 | -------------------------------------------------------------------------------- /docs/api/cli/debug_info.md: -------------------------------------------------------------------------------- 1 | # tmuxp debug-info - `tmuxp.cli.debug_info` 2 | 3 | ```{eval-rst} 4 | .. automodule:: tmuxp.cli.debug_info 5 | :members: 6 | :show-inheritance: 7 | :undoc-members: 8 | ``` 9 | -------------------------------------------------------------------------------- /docs/api/cli/edit.md: -------------------------------------------------------------------------------- 1 | # tmuxp edit - `tmuxp.cli.edit` 2 | 3 | ```{eval-rst} 4 | .. automodule:: tmuxp.cli.edit 5 | :members: 6 | :show-inheritance: 7 | :undoc-members: 8 | ``` 9 | -------------------------------------------------------------------------------- /docs/api/cli/freeze.md: -------------------------------------------------------------------------------- 1 | # tmuxp freeze - `tmuxp.cli.freeze` 2 | 3 | ```{eval-rst} 4 | .. automodule:: tmuxp.cli.freeze 5 | :members: 6 | :show-inheritance: 7 | :undoc-members: 8 | ``` 9 | -------------------------------------------------------------------------------- /docs/api/cli/import_config.md: -------------------------------------------------------------------------------- 1 | # tmuxp import - `tmuxp.cli.import_config` 2 | 3 | ```{eval-rst} 4 | .. automodule:: tmuxp.cli.import_config 5 | :members: 6 | :show-inheritance: 7 | :undoc-members: 8 | ``` 9 | -------------------------------------------------------------------------------- /docs/api/cli/index.md: -------------------------------------------------------------------------------- 1 | (api_cli)= 2 | 3 | # CLI 4 | 5 | :::{warning} 6 | Be careful with these! Internal APIs are **not** covered by version policies. They can break or be removed between minor versions! 7 | 8 | If you need an internal API stabilized please [file an issue](https://github.com/tmux-python/tmuxp/issues). 9 | ::: 10 | 11 | ```{toctree} 12 | convert 13 | debug_info 14 | edit 15 | freeze 16 | import_config 17 | load 18 | ls 19 | shell 20 | utils 21 | ``` 22 | 23 | ## `tmuxp.cli` 24 | 25 | ```{eval-rst} 26 | .. automodule:: tmuxp.cli 27 | :members: 28 | :show-inheritance: 29 | :undoc-members: 30 | ``` 31 | -------------------------------------------------------------------------------- /docs/api/cli/load.md: -------------------------------------------------------------------------------- 1 | # tmuxp load - `tmuxp.cli.load` 2 | 3 | ```{eval-rst} 4 | .. automodule:: tmuxp.cli.load 5 | :members: 6 | :show-inheritance: 7 | :undoc-members: 8 | ``` 9 | -------------------------------------------------------------------------------- /docs/api/cli/ls.md: -------------------------------------------------------------------------------- 1 | # tmuxp ls - `tmuxp.cli.ls` 2 | 3 | ```{eval-rst} 4 | .. automodule:: tmuxp.cli.ls 5 | :members: 6 | :show-inheritance: 7 | :undoc-members: 8 | ``` 9 | -------------------------------------------------------------------------------- /docs/api/cli/shell.md: -------------------------------------------------------------------------------- 1 | # tmuxp shell - `tmuxp.cli.shell` 2 | 3 | ```{eval-rst} 4 | .. automodule:: tmuxp.cli.shell 5 | :members: 6 | :show-inheritance: 7 | :undoc-members: 8 | ``` 9 | -------------------------------------------------------------------------------- /docs/api/cli/utils.md: -------------------------------------------------------------------------------- 1 | # CLI utilities - `tmuxp.cli.utils` 2 | 3 | ```{eval-rst} 4 | .. automodule:: tmuxp.cli.utils 5 | :members: 6 | :show-inheritance: 7 | :undoc-members: 8 | ``` 9 | -------------------------------------------------------------------------------- /docs/api/exc.md: -------------------------------------------------------------------------------- 1 | # Exceptions - `tmuxp.exc` 2 | 3 | ```{eval-rst} 4 | .. automodule:: tmuxp.exc 5 | :members: 6 | :show-inheritance: 7 | :undoc-members: 8 | ``` 9 | -------------------------------------------------------------------------------- /docs/api/index.md: -------------------------------------------------------------------------------- 1 | (api)= 2 | 3 | # API Reference 4 | 5 | :::{seealso} 6 | See {ref}`libtmux's API ` and {ref}`Quickstart ` to see how you can control 7 | tmux via python API calls. 8 | ::: 9 | 10 | ```{toctree} 11 | internals/index 12 | cli/index 13 | workspace/index 14 | exc 15 | log 16 | plugin 17 | shell 18 | util 19 | types 20 | ``` 21 | -------------------------------------------------------------------------------- /docs/api/internals/config_reader.md: -------------------------------------------------------------------------------- 1 | # Config reader - `tmuxp._internal.config_reader` 2 | 3 | :::{warning} 4 | Be careful with these! Internal APIs are **not** covered by version policies. They can break or be removed between minor versions! 5 | 6 | If you need an internal API stabilized please [file an issue](https://github.com/tmux-python/tmuxp/issues). 7 | ::: 8 | 9 | ```{eval-rst} 10 | .. automodule:: tmuxp._internal.config_reader 11 | :members: 12 | :show-inheritance: 13 | :undoc-members: 14 | ``` 15 | -------------------------------------------------------------------------------- /docs/api/internals/index.md: -------------------------------------------------------------------------------- 1 | (internals)= 2 | 3 | # Internals 4 | 5 | :::{warning} 6 | Be careful with these! Internal APIs are **not** covered by version policies. They can break or be removed between minor versions! 7 | 8 | If you need an internal API stabilized please [file an issue](https://github.com/tmux-python/tmuxp/issues). 9 | ::: 10 | 11 | ```{toctree} 12 | config_reader 13 | types 14 | ``` 15 | -------------------------------------------------------------------------------- /docs/api/internals/types.md: -------------------------------------------------------------------------------- 1 | # Typings - `tmuxp._internal.types` 2 | 3 | ```{eval-rst} 4 | .. automodule:: tmuxp._internal.types 5 | :members: 6 | :show-inheritance: 7 | :undoc-members: 8 | ``` 9 | -------------------------------------------------------------------------------- /docs/api/log.md: -------------------------------------------------------------------------------- 1 | # Logging - `tmuxp.log` 2 | 3 | ```{eval-rst} 4 | .. automodule:: tmuxp.log 5 | :members: 6 | :show-inheritance: 7 | :undoc-members: 8 | ``` 9 | -------------------------------------------------------------------------------- /docs/api/plugin.md: -------------------------------------------------------------------------------- 1 | # Plugin - `tmuxp.plugin` 2 | 3 | ```{eval-rst} 4 | .. automodule:: tmuxp.plugin 5 | :members: 6 | :show-inheritance: 7 | :undoc-members: 8 | ``` 9 | -------------------------------------------------------------------------------- /docs/api/shell.md: -------------------------------------------------------------------------------- 1 | # Shell - `tmuxp.shell` 2 | 3 | ```{eval-rst} 4 | .. automodule:: tmuxp.shell 5 | :members: 6 | :show-inheritance: 7 | :undoc-members: 8 | ``` 9 | -------------------------------------------------------------------------------- /docs/api/types.md: -------------------------------------------------------------------------------- 1 | # Typings - `tmuxp.types` 2 | 3 | ```{eval-rst} 4 | .. automodule:: tmuxp.types 5 | :members: 6 | :show-inheritance: 7 | :undoc-members: 8 | ``` 9 | -------------------------------------------------------------------------------- /docs/api/util.md: -------------------------------------------------------------------------------- 1 | # Utilities - `tmuxp.util` 2 | 3 | ```{eval-rst} 4 | .. automodule:: tmuxp.util 5 | :members: 6 | :show-inheritance: 7 | :undoc-members: 8 | ``` 9 | -------------------------------------------------------------------------------- /docs/api/workspace/builder.md: -------------------------------------------------------------------------------- 1 | # Builder - `tmuxp.workspace.builder` 2 | 3 | ```{eval-rst} 4 | .. automodule:: tmuxp.workspace.builder 5 | :members: 6 | :show-inheritance: 7 | :undoc-members: 8 | ``` 9 | -------------------------------------------------------------------------------- /docs/api/workspace/constants.md: -------------------------------------------------------------------------------- 1 | # Constants - `tmuxp.workspace.constants` 2 | 3 | ```{eval-rst} 4 | .. automodule:: tmuxp.workspace.constants 5 | :members: 6 | :show-inheritance: 7 | :undoc-members: 8 | ``` 9 | -------------------------------------------------------------------------------- /docs/api/workspace/finders.md: -------------------------------------------------------------------------------- 1 | # Finders - `tmuxp.workspace.finders` 2 | 3 | ```{eval-rst} 4 | .. automodule:: tmuxp.workspace.finders 5 | :members: 6 | :show-inheritance: 7 | :undoc-members: 8 | ``` 9 | -------------------------------------------------------------------------------- /docs/api/workspace/freezer.md: -------------------------------------------------------------------------------- 1 | # Freezer - `tmuxp.workspace.freezer` 2 | 3 | ```{eval-rst} 4 | .. automodule:: tmuxp.workspace.freezer 5 | :members: 6 | :show-inheritance: 7 | :undoc-members: 8 | ``` 9 | -------------------------------------------------------------------------------- /docs/api/workspace/importers.md: -------------------------------------------------------------------------------- 1 | # Importers - `tmuxp.workspace.importers` 2 | 3 | ```{eval-rst} 4 | .. automodule:: tmuxp.workspace.importers 5 | :members: 6 | :show-inheritance: 7 | :undoc-members: 8 | ``` 9 | -------------------------------------------------------------------------------- /docs/api/workspace/index.md: -------------------------------------------------------------------------------- 1 | (workspace)= 2 | 3 | # Workspace 4 | 5 | :::{warning} 6 | Be careful with these! Internal APIs are **not** covered by version policies. They can break or be removed between minor versions! 7 | 8 | If you need an internal API stabilized please [file an issue](https://github.com/tmux-python/tmuxp/issues). 9 | ::: 10 | 11 | ```{toctree} 12 | builder 13 | constants 14 | finders 15 | freezer 16 | importers 17 | loader 18 | validation 19 | ``` 20 | -------------------------------------------------------------------------------- /docs/api/workspace/loader.md: -------------------------------------------------------------------------------- 1 | # Loader - `tmuxp.workspace.loader` 2 | 3 | ```{eval-rst} 4 | .. automodule:: tmuxp.workspace.loader 5 | :members: 6 | :show-inheritance: 7 | :undoc-members: 8 | ``` 9 | -------------------------------------------------------------------------------- /docs/api/workspace/validation.md: -------------------------------------------------------------------------------- 1 | # Validation - `tmuxp.workspace.validation` 2 | 3 | ```{eval-rst} 4 | .. automodule:: tmuxp.workspace.validation 5 | :members: 6 | :show-inheritance: 7 | :undoc-members: 8 | ``` 9 | -------------------------------------------------------------------------------- /docs/cli/completion.md: -------------------------------------------------------------------------------- 1 | (completion)= 2 | 3 | (completions)= 4 | 5 | (cli-completions)= 6 | 7 | # Completions 8 | 9 | ## tmuxp 1.17+ (experimental) 10 | 11 | ```{note} 12 | 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. 13 | ``` 14 | 15 | 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**. 16 | 17 | ```console 18 | $ pip install shtab --user 19 | ``` 20 | 21 | :::{tab} bash 22 | 23 | ```bash 24 | shtab --shell=bash -u tmuxp.cli.create_parser \ 25 | | sudo tee "$BASH_COMPLETION_COMPAT_DIR"/TMUXP 26 | ``` 27 | 28 | ::: 29 | 30 | :::{tab} zsh 31 | 32 | ```zsh 33 | shtab --shell=zsh -u tmuxp.cli.create_parser \ 34 | | sudo tee /usr/local/share/zsh/site-functions/_TMUXP 35 | ``` 36 | 37 | ::: 38 | 39 | :::{tab} tcsh 40 | 41 | ```zsh 42 | shtab --shell=tcsh -u tmuxp.cli.create_parser \ 43 | | sudo tee /etc/profile.d/TMUXP.completion.csh 44 | ``` 45 | 46 | ::: 47 | 48 | ## tmuxp 1.1 to 1.16 49 | 50 | ```{note} 51 | 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. 52 | ``` 53 | 54 | tmuxp 1.1 to 1.16 use [click](https://click.palletsprojects.com)'s completion: 55 | 56 | :::{tab} Bash 57 | 58 | _~/.bashrc_: 59 | 60 | ```bash 61 | 62 | eval "$(_TMUXP_COMPLETE=bash_source tmuxp)" 63 | 64 | ``` 65 | 66 | ::: 67 | 68 | :::{tab} Zsh 69 | 70 | _~/.zshrc_: 71 | 72 | ```zsh 73 | 74 | eval "$(_TMUXP_COMPLETE=zsh_source tmuxp)" 75 | 76 | ``` 77 | 78 | ::: 79 | -------------------------------------------------------------------------------- /docs/cli/convert.md: -------------------------------------------------------------------------------- 1 | (cli-convert)= 2 | 3 | # tmuxp convert 4 | 5 | Convert between YAML and JSON 6 | 7 | ```{eval-rst} 8 | .. argparse:: 9 | :module: tmuxp.cli 10 | :func: create_parser 11 | :prog: tmuxp 12 | :path: convert 13 | ``` 14 | 15 | ## Usage 16 | 17 | ````{tab} YAML -> JSON 18 | 19 | ```console 20 | $ tmuxp convert /path/to/file.yaml 21 | ``` 22 | 23 | ```` 24 | 25 | ````{tab} JSON -> YAML 26 | 27 | ```console 28 | $ tmuxp convert /path/to/file.json 29 | ``` 30 | 31 | ```` 32 | 33 | tmuxp automatically will prompt to convert `.yaml` to `.json` and 34 | `.json` to `.yaml`. 35 | -------------------------------------------------------------------------------- /docs/cli/debug-info.md: -------------------------------------------------------------------------------- 1 | (cli-debug-info)= 2 | 3 | (tmuxp-debug-info)= 4 | 5 | # tmuxp debug-info 6 | 7 | Use to collect all relevant information for submitting an issue to 8 | the project. 9 | 10 | ```{eval-rst} 11 | .. argparse:: 12 | :module: tmuxp.cli 13 | :func: create_parser 14 | :prog: tmuxp 15 | :path: debug-info 16 | ``` 17 | 18 | ## Usage 19 | 20 | ```console 21 | 22 | $ tmuxp debug-info 23 | -------------------------- 24 | environment: 25 | system: Linux 26 | arch: x86_64 27 | ... 28 | 29 | ``` 30 | -------------------------------------------------------------------------------- /docs/cli/edit.md: -------------------------------------------------------------------------------- 1 | (edit-config)= 2 | 3 | (cli-edit)= 4 | 5 | # tmuxp edit 6 | 7 | ```{eval-rst} 8 | .. argparse:: 9 | :module: tmuxp.cli 10 | :func: create_parser 11 | :prog: tmuxp 12 | :path: edit 13 | ``` 14 | -------------------------------------------------------------------------------- /docs/cli/freeze.md: -------------------------------------------------------------------------------- 1 | (cli-freeze)= 2 | 3 | (cli-freeze-reference)= 4 | 5 | # tmuxp freeze 6 | 7 | ```{eval-rst} 8 | .. argparse:: 9 | :module: tmuxp.cli 10 | :func: create_parser 11 | :prog: tmuxp 12 | :path: freeze 13 | ``` 14 | 15 | ## Usage 16 | 17 | Freeze sessions 18 | 19 | ```console 20 | $ tmuxp freeze 21 | ``` 22 | 23 | ```console 24 | $ tmuxp freeze [session_name] 25 | ``` 26 | 27 | ```console 28 | $ tmuxp freeze --force [session_name] 29 | ``` 30 | 31 | You can save the state of your tmux session by freezing it. 32 | 33 | Tmuxp will offer to save your session state to `.json` or `.yaml`. 34 | 35 | If no session is specified, it will default to the attached session. 36 | 37 | If the `--force` argument is passed, it will overwrite any existing workspace file with the same name. 38 | -------------------------------------------------------------------------------- /docs/cli/import.md: -------------------------------------------------------------------------------- 1 | (cli-import)= 2 | 3 | # tmuxp import 4 | 5 | (import-teamocil)= 6 | 7 | ## From teamocil 8 | 9 | ```{eval-rst} 10 | .. argparse:: 11 | :module: tmuxp.cli 12 | :func: create_parser 13 | :prog: tmuxp 14 | :path: import teamocil 15 | ``` 16 | 17 | ````{tab} YAML 18 | 19 | ```console 20 | $ tmuxp import teamocil /path/to/file.yaml 21 | ``` 22 | 23 | ```` 24 | 25 | ````{tab} JSON 26 | 27 | ```console 28 | $ tmuxp import teamocil /path/to/file.json 29 | ``` 30 | 31 | ```` 32 | 33 | (import-tmuxinator)= 34 | 35 | ## From tmuxinator 36 | 37 | ```{eval-rst} 38 | .. argparse:: 39 | :module: tmuxp.cli 40 | :func: create_parser 41 | :prog: tmuxp 42 | :path: import tmuxinator 43 | ``` 44 | 45 | ````{tab} YAML 46 | 47 | ```console 48 | $ tmuxp import tmuxinator /path/to/file.yaml 49 | ``` 50 | 51 | ```` 52 | 53 | ````{tab} JSON 54 | 55 | ```console 56 | $ tmuxp import tmuxinator /path/to/file.json 57 | ``` 58 | 59 | ```` 60 | -------------------------------------------------------------------------------- /docs/cli/index.md: -------------------------------------------------------------------------------- 1 | (cli)= 2 | 3 | (commands)= 4 | 5 | # Commands 6 | 7 | ```{toctree} 8 | :caption: General commands 9 | :maxdepth: 1 10 | 11 | load 12 | shell 13 | ls 14 | ``` 15 | 16 | ```{toctree} 17 | :caption: Configuration 18 | :maxdepth: 1 19 | 20 | edit 21 | import 22 | convert 23 | freeze 24 | ``` 25 | 26 | ```{toctree} 27 | :caption: Diagnostic 28 | :maxdepth: 1 29 | 30 | debug-info 31 | ``` 32 | 33 | ```{toctree} 34 | :caption: Completion 35 | :maxdepth: 1 36 | 37 | completion 38 | ``` 39 | 40 | (cli-main)= 41 | 42 | (tmuxp-main)= 43 | 44 | ## Command: `tmuxp` 45 | 46 | ```{eval-rst} 47 | .. argparse:: 48 | :module: tmuxp.cli 49 | :func: create_parser 50 | :prog: tmuxp 51 | :nosubcommands: 52 | 53 | subparser_name : @replace 54 | See :ref:`cli-ls` 55 | ``` 56 | -------------------------------------------------------------------------------- /docs/cli/load.md: -------------------------------------------------------------------------------- 1 | (cli-load)= 2 | 3 | (tmuxp-load)= 4 | 5 | (tmuxp-load-reference)= 6 | 7 | # tmuxp load 8 | 9 | ```{eval-rst} 10 | .. argparse:: 11 | :module: tmuxp.cli 12 | :func: create_parser 13 | :prog: tmuxp 14 | :path: load 15 | ``` 16 | 17 | ## Usage 18 | 19 | You can load your tmuxp file and attach the tmux session via a few 20 | shorthands: 21 | 22 | 1. The directory with a `.tmuxp.{yaml,yml,json}` file in it 23 | 2. The name of the project file in your `$HOME/.tmuxp` folder 24 | 3. The direct path of the tmuxp file you want to load 25 | 26 | Path to folder with `.tmuxp.yaml`, `.tmuxp.yml`, `.tmuxp.json`: 27 | 28 | ````{tab} Project based 29 | 30 | Projects with a file named `.tmuxp.yaml` or `.tmuxp.json` can be loaded: 31 | 32 | ```console 33 | // current directory 34 | $ tmuxp load . 35 | ``` 36 | 37 | ```console 38 | $ tmuxp load ../ 39 | ``` 40 | 41 | ```console 42 | $ tmuxp load path/to/folder/ 43 | ``` 44 | 45 | ```console 46 | $ tmuxp load /path/to/folder/ 47 | ``` 48 | 49 | ```` 50 | 51 | ````{tab} User based 52 | 53 | Name of the config, assume `$HOME/.tmuxp/myconfig.yaml`: 54 | 55 | ```console 56 | $ tmuxp load myconfig 57 | ``` 58 | 59 | Direct path to json/yaml file: 60 | 61 | ```console 62 | $ tmuxp load ./myfile.yaml 63 | ``` 64 | 65 | ```console 66 | $ tmuxp load /abs/path/to/myfile.yaml 67 | ``` 68 | 69 | ```console 70 | $ tmuxp load ~/myfile.yaml 71 | ``` 72 | 73 | ```` 74 | 75 | ````{tab} Direct 76 | 77 | Absolute and relative directory paths are supported. 78 | 79 | ```console 80 | $ tmuxp load [filename] 81 | ``` 82 | 83 | ```` 84 | 85 | ## Inside sessions 86 | 87 | If you try to load a workspace file from within a tmux session, it will ask you 88 | if you want to load and attach to the new session, or just load detached. 89 | You can also load a workspace file and append the windows to the current active session. 90 | 91 | ``` 92 | Already inside TMUX, switch to session? yes/no 93 | Or (a)ppend windows in the current active session? 94 | [y/n/a]: 95 | ``` 96 | 97 | ## Options 98 | 99 | All of these options can be preselected to skip the prompt: 100 | 101 | - Attach / open the client after load: 102 | 103 | ```console 104 | $ tmuxp load -y config 105 | ``` 106 | 107 | - Detached / open in background: 108 | 109 | ```console 110 | $ tmuxp load -d config 111 | ``` 112 | 113 | - Append windows to existing session 114 | 115 | ```console 116 | $ tmuxp load -a config 117 | ``` 118 | 119 | ## Loading multiple sessions 120 | 121 | Multiple sessions can be loaded at once. The first ones will be created 122 | without being attached. The last one will be attached if there is no 123 | `-d` flag on the command line. 124 | 125 | ```console 126 | $ tmuxp load [filename1] [filename2] ... 127 | ``` 128 | 129 | ## Custom session name 130 | 131 | A session name can be provided at the terminal. If multiple sessions 132 | are created, the last session is named from the terminal. 133 | 134 | ```console 135 | $ tmuxp load -s [new_session_name] [filename1] ... 136 | ``` 137 | 138 | ## Logging 139 | 140 | The output of the `load` command can be logged to a file for 141 | debugging purposes. the log level can be controlled with the global 142 | `--log-level` option (defaults to INFO). 143 | 144 | ```console 145 | $ tmuxp load [filename] --log-file [log_filename] 146 | ``` 147 | 148 | ```console 149 | $ tmuxp --log-level [LEVEL] load [filename] --log-file [log_filename] 150 | ``` 151 | -------------------------------------------------------------------------------- /docs/cli/ls.md: -------------------------------------------------------------------------------- 1 | (cli-ls)= 2 | 3 | (ls-config)= 4 | 5 | # tmuxp ls 6 | 7 | List sessions. 8 | 9 | ```{eval-rst} 10 | .. argparse:: 11 | :module: tmuxp.cli 12 | :func: create_parser 13 | :prog: tmuxp 14 | :path: ls 15 | ``` 16 | -------------------------------------------------------------------------------- /docs/cli/shell.md: -------------------------------------------------------------------------------- 1 | (cli-shell)= 2 | 3 | (tmuxp-shell)= 4 | 5 | # tmuxp shell 6 | 7 | ```{eval-rst} 8 | .. argparse:: 9 | :module: tmuxp.cli 10 | :func: create_parser 11 | :prog: tmuxp 12 | :path: shell 13 | ``` 14 | 15 | ## Directly enter commands 16 | 17 | ```console 18 | $ tmuxp shell -c 'python code' 19 | ``` 20 | 21 | ```{image} ../_static/tmuxp-shell.gif 22 | :width: 100% 23 | ``` 24 | 25 | ## Guide 26 | 27 | Launch into a Python console with [libtmux] objects. Compare to django's shell. 28 | 29 | Automatically preloads current tmux {class}`server `, 30 | {class}`session `, {class}`window ` 31 | {class}`pane `. Pass additional arguments to select a 32 | specific one of your choice: 33 | 34 | ```console 35 | (Pdb) server 36 | 37 | (Pdb) server.sessions 38 | [Session($1 your_project)] 39 | (Pdb) session 40 | Session($1 your_project) 41 | (Pdb) session.name 42 | 'your_project' 43 | (Pdb) window 44 | Window(@3 1:your_window, Session($1 your_project)) 45 | (Pdb) window.name 46 | 'your_window' 47 | (Pdb) window.panes 48 | [Pane(%6 Window(@3 1:your_window, Session($1 your_project))) 49 | (Pdb) pane 50 | Pane(%6 Window(@3 1:your_window, Session($1 your_project))) 51 | ``` 52 | 53 | Supports [PEP 553][pep 553]'s `PYTHONBREAKPOINT` and 54 | compatible debuggers, for instance [ipdb][ipdb]: 55 | 56 | ```console 57 | $ pip install --user ipdb 58 | ``` 59 | 60 | ```console 61 | $ env PYTHONBREAKPOINT=ipdb.set_trace tmuxp shell 62 | ``` 63 | 64 | You can also pass in python code directly, similar to `python -c`, do 65 | this via `tmuxp -c`: 66 | 67 | ```console 68 | $ tmuxp shell -c 'print(session.name); print(window.name)' 69 | my_server 70 | my_window 71 | ``` 72 | 73 | ```console 74 | $ tmuxp shell my_server -c 'print(session.name); print(window.name)' 75 | my_server 76 | my_window 77 | ``` 78 | 79 | ```console 80 | $ tmuxp shell my_server my_window -c 'print(session.name); print(window.name)' 81 | my_server 82 | my_window 83 | ``` 84 | 85 | ```console 86 | $ tmuxp shell my_server my_window -c 'print(window.name.upper())' 87 | MY_WINDOW 88 | ``` 89 | 90 | Assuming inside a tmux pane or one is attached on default server: 91 | 92 | ```console 93 | $ tmuxp shell -c 'print(pane.id); print(pane.window.name)' 94 | %2 95 | my_window 96 | ``` 97 | 98 | [pep 553]: https://www.python.org/dev/peps/pep-0553/ 99 | [ipdb]: https://pypi.org/project/ipdb/ 100 | [libtmux]: https://libtmux.git-pull.com 101 | 102 | ## Shell detection 103 | 104 | `tmuxp shell` detects the richest shell available in your _site packages_, you can also pick your shell via args: 105 | 106 | - `--pdb`: Use plain old `breakpoint()` (python 3.7+) or 107 | `pdb.set_trace` 108 | - `--code`: Drop into `code.interact`, accepts `--use-pythonrc` 109 | - `--bpython`: Drop into bpython 110 | - `--ipython`: Drop into ipython 111 | - `--ptpython`: Drop into ptpython, accepts `--use-vi-mode` 112 | - `--ptipython`: Drop into ipython + ptpython, accepts 113 | `--use-vi-mode` 114 | -------------------------------------------------------------------------------- /docs/configuration/environmental-variables.md: -------------------------------------------------------------------------------- 1 | (environmental-variables)= 2 | 3 | # Environmental variables 4 | 5 | (TMUXP_CONFIGDIR)= 6 | 7 | ## `TMUXP_CONFIGDIR` 8 | 9 | Example: `TMUXP_CONFIGDIR=$HOME/.mytmuxpconfigdir tmuxp load cpython` 10 | 11 | (LIBTMUX_TMUX_FORMAT_SEPARATOR)= 12 | 13 | ## `LIBTMUX_TMUX_FORMAT_SEPARATOR` 14 | 15 | :::{seealso} 16 | 17 | [`LIBTMUX_TMUX_FORMAT_SEPARATOR`](https://libtmux.git-pull.com/api.html#tmux-format-separator) in libtmux API. 18 | 19 | ::: 20 | 21 | In rare circumstances the `tmux -F` separator under the hood may cause issues 22 | building sessions. For this case you can override it here. 23 | 24 | ```console 25 | $ env LIBTMUX_TMUX_FORMAT_SEPARATOR='__SEP__' tmuxp load [session] 26 | ``` 27 | -------------------------------------------------------------------------------- /docs/configuration/index.md: -------------------------------------------------------------------------------- 1 | (config)= 2 | 3 | (configuration)= 4 | 5 | (workspace)= 6 | 7 | # Workspace files 8 | 9 | tmuxp loads your terminal workspace into tmux using workspace files. 10 | 11 | The workspace file can be JSON or YAML. It's declarative style resembles tmux's object hierarchy: session, window and panes. 12 | 13 | ## Launching your session 14 | 15 | Once you have `tmuxp` installed alongside tmux, you can load a workspace with: 16 | 17 | ```console 18 | $ tmuxp load ./path/to/file 19 | ``` 20 | 21 | tmuxp will offer to assist when: 22 | 23 | - _Session already exists_: tmuxp will prompt you to re-attach. It does this 24 | by checking if the workspace's `session_name` matches a session already 25 | running on the same server. 26 | - _When inside a tmux client_, `tmuxp` will let you create a new session and switch to it, or append the windows to your existing 27 | session. 28 | 29 | ## What's in a workspace file? 30 | 31 | 1. A session name 32 | 2. A list of _windows_ 33 | 3. A list of _panes_ for each window 34 | 4. A list of _commands_ for each pane 35 | 36 | ````{tab} Basics 37 | 38 | ```yaml 39 | session_name: My session 40 | windows: 41 | - window_name: Window 1 42 | panes: 43 | - shell_command: 44 | - cmd: echo "pane 1" 45 | - shell_command: 46 | - cmd: echo "pane 2" 47 | ``` 48 | 49 | ```` 50 | 51 | ````{tab} Smallest possible 52 | 53 | ```{literalinclude} ../../examples/minimal.yaml 54 | :language: yaml 55 | 56 | ``` 57 | 58 | As of 1.11.x. 59 | 60 | ```` 61 | 62 | Breaking down the basic workspace into sections: 63 | 64 | 1. A session name 65 | 66 | ```yaml 67 | session_name: My session 68 | ``` 69 | 70 | 2. A list of _windows_ 71 | 72 | ```yaml 73 | windows: 74 | - window_name: Window 1 75 | panes: ... 76 | # window settings 77 | - window_name: Window 2 78 | panes: ... 79 | # window settings 80 | ``` 81 | 82 | 3. A list of _panes_ for each window 83 | 84 | ```yaml 85 | windows: 86 | panes: 87 | - # pane settings 88 | - # pane settings 89 | ``` 90 | 91 | 4. A list of _commands_ for each pane 92 | 93 | ```yaml 94 | windows: 95 | panes: 96 | - shell_command: 97 | - cmd: echo "pane 1 - cmd 1" 98 | # command options 99 | - cmd: echo "pane 1 - cmd 2" 100 | # command options 101 | ``` 102 | 103 | ## Where do I store workspace files? 104 | 105 | ### Direct 106 | 107 | You can create a workspace and load it from anywhere in your file system. 108 | 109 | ```console 110 | $ tmuxp load [workspace-file] 111 | ``` 112 | 113 | ````{tab} Relative 114 | ```console 115 | $ tmuxp load ./favorites.yaml 116 | ``` 117 | ```` 118 | 119 | ````{tab} Absolute 120 | ```console 121 | $ tmuxp load /opt/myapp/favorites.yaml 122 | ``` 123 | ```` 124 | 125 | ### User-based workspaces 126 | 127 | tmuxp uses the [XDG Base Directory] specification. 128 | 129 | Often on POSIX machines, you will store them in `~/.config/tmuxp`. 130 | 131 | Assume you store `apple.yaml` in `$XDG_CONFIG_HOME/tmuxp/apple.yaml`, you can 132 | then use: 133 | 134 | ```console 135 | $ tmuxp load apple 136 | ``` 137 | 138 | :::{seealso} 139 | 140 | This path can be overridden by {ref}`TMUXP_CONFIGDIR` 141 | 142 | ::: 143 | 144 | [xdg base directory]: https://specifications.freedesktop.org/basedir-spec/latest/ 145 | 146 | ### Project-specific 147 | 148 | You can store a workspace in your project's root directory as `.tmuxp.yaml` or `.tmuxp.json`, then: 149 | 150 | Assume `.tmuxp.yaml` inside `/opt/myapp` 151 | 152 | ```console 153 | $ tmuxp load [workspace-file] 154 | ``` 155 | 156 | ````{tab} In project root 157 | ```console 158 | $ tmuxp load ./ 159 | ``` 160 | ```` 161 | 162 | ````{tab} Absolute 163 | ```console 164 | $ tmuxp load /opt/myapp 165 | ``` 166 | ```` 167 | 168 | ## Reference and usage 169 | 170 | ```{toctree} 171 | 172 | top-level 173 | environmental-variables 174 | examples 175 | 176 | ``` 177 | -------------------------------------------------------------------------------- /docs/configuration/top-level.md: -------------------------------------------------------------------------------- 1 | (top-level)= 2 | (top-level-config)= 3 | 4 | # Top-level configuration 5 | 6 | ## `session_name` 7 | 8 | Used for: 9 | 10 | - tmux session name 11 | - checking for existing sessions 12 | 13 | Notes: 14 | 15 | - Session names may differ from workspace filename. 16 | 17 | e.g. _apple.yaml_: 18 | 19 | ```yaml 20 | session_name: banana 21 | windows: 22 | - panes: 23 | - 24 | ``` 25 | 26 | Load detached: 27 | 28 | ```console 29 | $ tmuxp load ./apple.yaml -d 30 | ``` 31 | 32 | Above: 33 | 34 | - tmuxp loads a file named _apple.yaml_ from the current directory. 35 | - tmuxp built a tmux session called _banana_. 36 | - `-d` means _detached_, loading in background. 37 | 38 | ```console 39 | $ tmux attach -t banana 40 | ``` 41 | 42 | Above: Use `tmux` directly to attach _banana_. 43 | -------------------------------------------------------------------------------- /docs/glossary.md: -------------------------------------------------------------------------------- 1 | (glossary)= 2 | 3 | # Glossary 4 | 5 | ```{glossary} 6 | 7 | tmuxp 8 | A tool to manage workspaces with tmux. A pythonic abstraction of 9 | tmux. 10 | 11 | tmux 12 | tmux(1) 13 | The tmux binary. Used internally to distinguish tmuxp is only a 14 | layer on top of tmux. 15 | 16 | ConfigReader 17 | configuration management class, for parsing YAML / JSON / etc. files 18 | to and from python data (dictionaries, in the future, potentially 19 | dataclasses) 20 | 21 | Server 22 | Tmux runs in the background of your system as a process. 23 | 24 | The server holds multiple {term}`Session`. By default, tmux 25 | automatically starts the server the first time ``$ tmux`` is ran. 26 | 27 | A server contains {term}`session`'s. 28 | 29 | tmux starts the server automatically if it's not running. 30 | 31 | Advanced cases: multiple can be run by specifying 32 | ``[-L socket-name]`` and ``[-S socket-path]``. 33 | 34 | Client 35 | Attaches to a tmux {term}`server`. When you use tmux through CLI, 36 | you are using tmux as a client. 37 | 38 | Session 39 | Inside a tmux {term}`server`. 40 | 41 | The session has 1 or more {term}`Window`. The bottom bar in tmux 42 | show a list of windows. Normally they can be navigated with 43 | ``Ctrl-a [0-9]``, ``Ctrl-a n`` and ``Ctrl-a p``. 44 | 45 | Sessions can have a ``session_name``. 46 | 47 | Uniquely identified by ``session_id``. 48 | 49 | Window 50 | Entity of a {term}`session`. 51 | 52 | Can have 1 or more {term}`pane`. 53 | 54 | Panes can be organized with a layouts. 55 | 56 | Windows can have names. 57 | 58 | Pane 59 | Linked to a {term}`Window`. 60 | 61 | a pseudoterminal. 62 | 63 | Target 64 | A target, cited in the manual as ``[-t target]`` can be a session, 65 | window or pane. 66 | ``` 67 | -------------------------------------------------------------------------------- /docs/history.md: -------------------------------------------------------------------------------- 1 | (changelog)= 2 | 3 | (history)= 4 | 5 | ```{include} ../CHANGES 6 | 7 | ``` 8 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | (index)= 2 | 3 | ```{include} ../README.md 4 | :end-before: 41 | ``` 42 | -------------------------------------------------------------------------------- /docs/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tmuxp", 3 | "short_name": "tmuxp", 4 | "description": "tmux session manager", 5 | "theme_color": "#2196f3", 6 | "background_color": "#fff", 7 | "display": "browser", 8 | "Scope": "https://tmuxp.git-pull.com/", 9 | "start_url": "https://tmuxp.git-pull.com/", 10 | "icons": [ 11 | { 12 | "src": "_static/img/icons/icon-72x72.png", 13 | "sizes": "72x72", 14 | "type": "image/png" 15 | }, 16 | { 17 | "src": "_static/img/icons/icon-96x96.png", 18 | "sizes": "96x96", 19 | "type": "image/png" 20 | }, 21 | { 22 | "src": "_static/img/icons/icon-128x128.png", 23 | "sizes": "128x128", 24 | "type": "image/png" 25 | }, 26 | { 27 | "src": "_static/img/icons/icon-144x144.png", 28 | "sizes": "144x144", 29 | "type": "image/png" 30 | }, 31 | { 32 | "src": "_static/img/icons/icon-152x152.png", 33 | "sizes": "152x152", 34 | "type": "image/png" 35 | }, 36 | { 37 | "src": "_static/img/icons/icon-192x192.png", 38 | "sizes": "192x192", 39 | "type": "image/png" 40 | }, 41 | { 42 | "src": "_static/img/icons/icon-384x384.png", 43 | "sizes": "384x384", 44 | "type": "image/png" 45 | }, 46 | { 47 | "src": "_static/img/icons/icon-512x512.png", 48 | "sizes": "512x512", 49 | "type": "image/png" 50 | } 51 | ], 52 | "splash_pages": null 53 | } 54 | -------------------------------------------------------------------------------- /docs/migration.md: -------------------------------------------------------------------------------- 1 | (migration)= 2 | 3 | ```{currentmodule} libtmux 4 | 5 | ``` 6 | 7 | ```{include} ../MIGRATION 8 | 9 | ``` 10 | -------------------------------------------------------------------------------- /docs/plugins/index.md: -------------------------------------------------------------------------------- 1 | (plugins)= 2 | 3 | # Plugins 4 | 5 | The plugin system allows users to customize and extend different aspects of 6 | tmuxp without the need to change tmuxp itself. 7 | 8 | ## Using a Plugin 9 | 10 | To use a plugin, install it in your local python environment and add it to 11 | your tmuxp workspace file. 12 | 13 | ### Example Workspace files 14 | 15 | ````{tab} YAML 16 | 17 | ```{literalinclude} ../../examples/plugin-system.yaml 18 | :language: yaml 19 | 20 | ``` 21 | 22 | ```` 23 | 24 | ````{tab} JSON 25 | 26 | ```{literalinclude} ../../examples/plugin-system.json 27 | :language: json 28 | 29 | ``` 30 | 31 | ```` 32 | 33 | ## Developing a Plugin 34 | 35 | tmuxp expects all plugins to be a class within a python submodule named 36 | `plugin` that is within a python module that is installed in the local 37 | python environment. A plugin interface is provided by tmuxp to inherit. 38 | 39 | [uv] is the chosen python package manager for tmuxp. It is highly 40 | suggested to use it when developing plugins; however, `pip` will work 41 | just as well. Only one of the configuration files is needed for the packaging 42 | tool that the package developer decides to use. 43 | 44 | ```console 45 | 46 | python_module 47 | ├── tmuxp_plugin_my_plugin_module 48 | │   ├── __init__.py 49 | │   └── plugin.py 50 | └── pyproject.toml # Python project configuration file 51 | 52 | ``` 53 | 54 | When publishing plugins to pypi, tmuxp advocates for standardized naming: 55 | `tmuxp-plugin-{your-plugin-name}` to allow for easier searching. To create a 56 | module configuration file with uv, run `uv virtualenv` in the module 57 | directory. The resulting file looks something like this: 58 | 59 | ```toml 60 | 61 | [project] 62 | name = "tmuxp-plugin-my-tmuxp-plugin" 63 | version = "0.0.2" 64 | description = "An example tmuxp plugin." 65 | authors = ["Author Name .com>"] 66 | requires-python = ">=3.8,<4.0" 67 | dependencies = [ 68 | "tmuxp^=1.7.0" 69 | ] 70 | 71 | [build-system] 72 | requires = ["hatchling"] 73 | build-backend = "hatchling.build" 74 | ``` 75 | 76 | The `plugin.py` file could contain something like the following: 77 | 78 | ```python 79 | 80 | from tmuxp.plugin import TmuxpPlugin 81 | import datetime 82 | 83 | class MyTmuxpPlugin(TmuxpPlugin): 84 | def __init__(self): 85 | """ 86 | Initialize my custom plugin. 87 | """ 88 | # Optional version dependency configuration. See Plugin API docs 89 | # for all supported config parameters 90 | config = { 91 | 'tmuxp_min_version' = '1.6.2' 92 | } 93 | 94 | TmuxpPlugin.__init__( 95 | self, 96 | plugin_name='tmuxp-plugin-my-tmuxp-plugin', 97 | **config 98 | ) 99 | 100 | def before_workspace_builder(self, session): 101 | session.rename_session('my-new-session-name') 102 | 103 | def reattach(self, session): 104 | now = datetime.datetime.now().strftime('%Y-%m-%d') 105 | session.rename_session('session_{}'.format(now)) 106 | 107 | ``` 108 | 109 | Once this plugin is installed in the local python environment, it can be used 110 | in a configuration file like the following: 111 | 112 | ```yaml 113 | session_name: plugin example 114 | plugins: 115 | - my_plugin_module.plugin.MyTmuxpPlugin 116 | # ... the rest of your config 117 | ``` 118 | 119 | ## Plugin API 120 | 121 | ```{eval-rst} 122 | .. automethod:: tmuxp.plugin.TmuxpPlugin.__init__ 123 | ``` 124 | 125 | ```{eval-rst} 126 | .. automethod:: tmuxp.plugin.TmuxpPlugin.before_workspace_builder 127 | ``` 128 | 129 | ```{eval-rst} 130 | .. automethod:: tmuxp.plugin.TmuxpPlugin.on_window_create 131 | ``` 132 | 133 | ```{eval-rst} 134 | .. automethod:: tmuxp.plugin.TmuxpPlugin.after_window_finished 135 | ``` 136 | 137 | ```{eval-rst} 138 | .. automethod:: tmuxp.plugin.TmuxpPlugin.before_script 139 | ``` 140 | 141 | ```{eval-rst} 142 | .. automethod:: tmuxp.plugin.TmuxpPlugin.reattach 143 | ``` 144 | 145 | [uv]: https://github.com/astral-sh/uv 146 | -------------------------------------------------------------------------------- /docs/redirects.txt: -------------------------------------------------------------------------------- 1 | "cli.md" "commands/index.md" 2 | "api.md" "api/index.md" 3 | "examples.md" "configuration/examples.md" 4 | "plugin_system.md" "plugins/index.md" 5 | "commands/index.md" "cli/index.md" 6 | -------------------------------------------------------------------------------- /examples/2-pane-synchronized.json: -------------------------------------------------------------------------------- 1 | { 2 | "session_name": "2-pane-synchronized", 3 | "windows": [ 4 | { 5 | "window_name": "Two synchronized panes", 6 | "panes": [ 7 | "ssh server1", 8 | "ssh server2" 9 | ], 10 | "options_after": { 11 | "synchronize-panes": true 12 | } 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /examples/2-pane-synchronized.yaml: -------------------------------------------------------------------------------- 1 | session_name: 2-pane-synchronized 2 | windows: 3 | - window_name: Two synchronized panes 4 | panes: 5 | - ssh server1 6 | - ssh server2 7 | options_after: 8 | synchronize-panes: on 9 | -------------------------------------------------------------------------------- /examples/2-pane-vertical.json: -------------------------------------------------------------------------------- 1 | { 2 | "windows": [ 3 | { 4 | "panes": [ 5 | "echo hello", 6 | "echo hello" 7 | ], 8 | "window_name": "my test window" 9 | } 10 | ], 11 | "session_name": "2-pane-vertical" 12 | } 13 | -------------------------------------------------------------------------------- /examples/2-pane-vertical.yaml: -------------------------------------------------------------------------------- 1 | session_name: 2-pane-vertical 2 | windows: 3 | - window_name: my test window 4 | panes: 5 | - echo hello 6 | - echo hello 7 | -------------------------------------------------------------------------------- /examples/3-pane.json: -------------------------------------------------------------------------------- 1 | { 2 | "windows": [ 3 | { 4 | "panes": [ 5 | { 6 | "shell_command": [ 7 | "cd /var/log", 8 | "ls -al | grep \\.log" 9 | ] 10 | }, 11 | "echo hello", 12 | "echo hello" 13 | ], 14 | "shell_command_before": [ 15 | "cd ~/" 16 | ], 17 | "layout": "main-vertical", 18 | "window_name": "dev window" 19 | } 20 | ], 21 | "session_name": "3-panes" 22 | } 23 | -------------------------------------------------------------------------------- /examples/3-pane.yaml: -------------------------------------------------------------------------------- 1 | session_name: 3-panes 2 | windows: 3 | - window_name: dev window 4 | layout: main-vertical 5 | shell_command_before: 6 | - cd ~/ 7 | panes: 8 | - shell_command: 9 | - cd /var/log 10 | - ls -al | grep \.log 11 | - echo hello 12 | - echo hello 13 | -------------------------------------------------------------------------------- /examples/4-pane.json: -------------------------------------------------------------------------------- 1 | { 2 | "windows": [ 3 | { 4 | "panes": [ 5 | { 6 | "shell_command": [ 7 | "cd /var/log", 8 | "ls -al | grep \\.log" 9 | ] 10 | }, 11 | "echo hello", 12 | "echo hello", 13 | "echo hello" 14 | ], 15 | "shell_command_before": [ 16 | "cd ~/" 17 | ], 18 | "layout": "tiled", 19 | "window_name": "dev window" 20 | } 21 | ], 22 | "session_name": "4-pane-split" 23 | } 24 | -------------------------------------------------------------------------------- /examples/4-pane.yaml: -------------------------------------------------------------------------------- 1 | session_name: 4-pane-split 2 | windows: 3 | - window_name: dev window 4 | layout: tiled 5 | shell_command_before: 6 | - cd ~/ 7 | panes: 8 | - shell_command: 9 | - cd /var/log 10 | - ls -al | grep \.log 11 | - echo hello 12 | - echo hello 13 | - echo hello 14 | -------------------------------------------------------------------------------- /examples/blank-panes.json: -------------------------------------------------------------------------------- 1 | { 2 | "windows": [ 3 | { 4 | "panes": [ 5 | null, 6 | "pane", 7 | "blank" 8 | ], 9 | "window_name": "Blank pane test" 10 | }, 11 | { 12 | "panes": [ 13 | null, 14 | { 15 | "shell_command": null 16 | }, 17 | { 18 | "shell_command": [ 19 | null 20 | ] 21 | } 22 | ], 23 | "window_name": "More blank panes" 24 | }, 25 | { 26 | "panes": [ 27 | "", 28 | { 29 | "shell_command": "" 30 | }, 31 | { 32 | "shell_command": [ 33 | "" 34 | ] 35 | } 36 | ], 37 | "window_name": "Empty string (return)" 38 | }, 39 | { 40 | "panes": [ 41 | { 42 | "focus": true 43 | }, 44 | { 45 | "start_directory": "/tmp" 46 | } 47 | ], 48 | "window_name": "Blank with options" 49 | } 50 | ], 51 | "session_name": "Blank pane test" 52 | } -------------------------------------------------------------------------------- /examples/blank-panes.yaml: -------------------------------------------------------------------------------- 1 | session_name: Blank pane test 2 | windows: 3 | # Emptiness will simply open a blank pane, if no shell_command_before. 4 | # All these are equivalent 5 | - window_name: Blank pane test 6 | panes: 7 | - 8 | - pane 9 | - blank 10 | - window_name: More blank panes 11 | panes: 12 | - null 13 | - shell_command: 14 | - shell_command: 15 | - 16 | # an empty string will be treated as a carriage return 17 | - window_name: Empty string (return) 18 | panes: 19 | - "" 20 | - shell_command: "" 21 | - shell_command: 22 | - "" 23 | # a pane can have other options but still be blank 24 | - window_name: Blank with options 25 | panes: 26 | - focus: true 27 | - start_directory: /tmp 28 | -------------------------------------------------------------------------------- /examples/env-variables.json: -------------------------------------------------------------------------------- 1 | { 2 | "before_script": "${MY_ENV_VAR}/test3.sh", 3 | "windows": [ 4 | { 5 | "panes": [ 6 | { 7 | "shell_command": [ 8 | "tail -F /var/log/syslog" 9 | ] 10 | } 11 | ], 12 | "start_directory": "/var/log", 13 | "window_name": "editor" 14 | }, 15 | { 16 | "panes": [ 17 | { 18 | "shell_command": [ 19 | "htop", 20 | "ls $PWD" 21 | ] 22 | } 23 | ], 24 | "window_name": "logging for ${USER}", 25 | "options": { 26 | "automatic-rename": true 27 | } 28 | } 29 | ], 30 | "shell_command_before": "echo ${PWD}", 31 | "start_directory": "${PWD}/test", 32 | "session_name": "session - ${USER} (${MY_ENV_VAR})" 33 | } -------------------------------------------------------------------------------- /examples/env-variables.yaml: -------------------------------------------------------------------------------- 1 | start_directory: "${PWD}/test" 2 | shell_command_before: "echo ${PWD}" 3 | before_script: "${MY_ENV_VAR}/test3.sh" 4 | session_name: session - ${USER} (${MY_ENV_VAR}) 5 | windows: 6 | - window_name: editor 7 | panes: 8 | - shell_command: 9 | - tail -F /var/log/syslog 10 | start_directory: /var/log 11 | - window_name: logging for ${USER} 12 | options: 13 | automatic-rename: true 14 | panes: 15 | - shell_command: 16 | - htop 17 | - ls $PWD 18 | -------------------------------------------------------------------------------- /examples/focus-window-and-panes.json: -------------------------------------------------------------------------------- 1 | { 2 | "windows": [ 3 | { 4 | "panes": [ 5 | { 6 | "shell_command": [ 7 | "echo hello", 8 | "echo 'this pane should be selected on load'" 9 | ], 10 | "focus": true 11 | }, 12 | { 13 | "shell_command": [ 14 | "cd /var/log", 15 | "echo hello" 16 | ] 17 | } 18 | ], 19 | "window_name": "attached window on load", 20 | "focus": true 21 | }, 22 | { 23 | "panes": [ 24 | "pane", 25 | { 26 | "shell_command": [ 27 | "echo 'this pane should be focused, when window switched to first time'" 28 | ], 29 | "focus": true 30 | }, 31 | "pane" 32 | ], 33 | "shell_command_before": "cd /var/www", 34 | "window_name": "second window" 35 | } 36 | ], 37 | "session_name": "focus window and pane when loading sessions" 38 | } 39 | -------------------------------------------------------------------------------- /examples/focus-window-and-panes.yaml: -------------------------------------------------------------------------------- 1 | session_name: focus 2 | windows: 3 | - window_name: attached window 4 | focus: true 5 | panes: 6 | - shell_command: 7 | - echo hello 8 | - echo 'this pane should be selected on load' 9 | focus: true 10 | - shell_command: 11 | - cd /var/log 12 | - echo hello 13 | - window_name: second window 14 | shell_command_before: cd /var/log 15 | panes: 16 | - pane 17 | - shell_command: 18 | - echo 'this pane should be focused, when window switched to first time' 19 | focus: true 20 | - pane 21 | -------------------------------------------------------------------------------- /examples/main-pane-height-percentage.json: -------------------------------------------------------------------------------- 1 | { 2 | "windows": [ 3 | { 4 | "panes": [ 5 | { 6 | "shell_command": [ 7 | "top" 8 | ], 9 | "start_directory": "~" 10 | }, 11 | { 12 | "shell_command": [ 13 | "echo \"hey\"" 14 | ] 15 | }, 16 | { 17 | "shell_command": [ 18 | "echo \"moo\"" 19 | ] 20 | } 21 | ], 22 | "layout": "main-horizontal", 23 | "options": { 24 | "main-pane-height": "67%" 25 | }, 26 | "window_name": "editor" 27 | } 28 | ], 29 | "session_name": "main pane height", 30 | "start_directory": "~" 31 | } 32 | -------------------------------------------------------------------------------- /examples/main-pane-height-percentage.yaml: -------------------------------------------------------------------------------- 1 | session_name: main-pane-height 2 | start_directory: "~" 3 | windows: 4 | - layout: main-horizontal 5 | options: 6 | main-pane-height: 67% 7 | panes: 8 | - shell_command: 9 | - top 10 | start_directory: "~" 11 | - shell_command: 12 | - echo "hey" 13 | - shell_command: 14 | - echo "moo" 15 | window_name: my window name 16 | -------------------------------------------------------------------------------- /examples/main-pane-height.json: -------------------------------------------------------------------------------- 1 | { 2 | "windows": [ 3 | { 4 | "panes": [ 5 | { 6 | "shell_command": [ 7 | "top" 8 | ], 9 | "start_directory": "~" 10 | }, 11 | { 12 | "shell_command": [ 13 | "echo \"hey\"" 14 | ] 15 | }, 16 | { 17 | "shell_command": [ 18 | "echo \"moo\"" 19 | ] 20 | } 21 | ], 22 | "layout": "main-horizontal", 23 | "options": { 24 | "main-pane-height": 30 25 | }, 26 | "window_name": "editor" 27 | } 28 | ], 29 | "session_name": "main pane height", 30 | "start_directory": "~" 31 | } 32 | -------------------------------------------------------------------------------- /examples/main-pane-height.yaml: -------------------------------------------------------------------------------- 1 | session_name: main-pane-height 2 | start_directory: "~" 3 | windows: 4 | - layout: main-horizontal 5 | options: 6 | main-pane-height: 30 7 | panes: 8 | - shell_command: 9 | - top 10 | start_directory: "~" 11 | - shell_command: 12 | - echo "hey" 13 | - shell_command: 14 | - echo "moo" 15 | window_name: my window name 16 | -------------------------------------------------------------------------------- /examples/minimal.yaml: -------------------------------------------------------------------------------- 1 | session_name: My tmux session 2 | windows: 3 | - panes: 4 | - 5 | -------------------------------------------------------------------------------- /examples/options.json: -------------------------------------------------------------------------------- 1 | { 2 | "windows": [ 3 | { 4 | "panes": [ 5 | { 6 | "shell_command": [ 7 | "man echo" 8 | ], 9 | "start_directory": "~" 10 | }, 11 | { 12 | "shell_command": [ 13 | "echo \"hey\"" 14 | ] 15 | }, 16 | { 17 | "shell_command": [ 18 | "echo \"moo\"" 19 | ] 20 | } 21 | ], 22 | "layout": "main-horizontal", 23 | "options": { 24 | "automatic-rename": true 25 | } 26 | } 27 | ], 28 | "session_name": "test window options", 29 | "start_directory": "~", 30 | "global_options": { 31 | "default-shell": "/bin/sh", 32 | "default-command": "/bin/sh" 33 | }, 34 | "options": { 35 | "main-pane-height": "${MAIN_PANE_HEIGHT}" 36 | } 37 | } -------------------------------------------------------------------------------- /examples/options.yaml: -------------------------------------------------------------------------------- 1 | session_name: test window options 2 | start_directory: "~" 3 | global_options: 4 | default-shell: /bin/sh 5 | default-command: /bin/sh 6 | options: 7 | main-pane-height: ${MAIN_PANE_HEIGHT} # works with env variables 8 | windows: 9 | - layout: main-horizontal 10 | options: 11 | automatic-rename: on 12 | panes: 13 | - shell_command: 14 | - man echo 15 | start_directory: "~" 16 | - shell_command: 17 | - echo "hey" 18 | - shell_command: 19 | - echo "moo" 20 | -------------------------------------------------------------------------------- /examples/pane-shell.json: -------------------------------------------------------------------------------- 1 | { 2 | "session_name": "Pane shell example", 3 | "windows": [ 4 | { 5 | "window_name": "first", 6 | "window_shell": "/usr/bin/python2", 7 | "layout": "even-vertical", 8 | "suppress_history": false, 9 | "options": { 10 | "remain-on-exit": true 11 | }, 12 | "panes": [ 13 | { 14 | "shell": "/usr/bin/python3", 15 | "shell_command": [ 16 | "print('This is python 3')" 17 | ] 18 | }, 19 | { 20 | "shell": "/usr/bin/vim -u none", 21 | "shell_command": [ 22 | "iAll panes have the `remain-on-exit` setting on.", 23 | "When you exit out of the shell or application, the panes will remain.", 24 | "Use tmux command `:kill-pane` to remove the pane.", 25 | "Use tmux command `:respawn-pane` to restart the shell in the pane.", 26 | "Use and then `:q!` to get out of this vim window. :-)" 27 | ] 28 | }, 29 | { 30 | "shell_command": [ 31 | "print('Hello World 2')" 32 | ] 33 | }, 34 | { 35 | "shell": "/usr/bin/top" 36 | } 37 | ] 38 | } 39 | ] 40 | } -------------------------------------------------------------------------------- /examples/pane-shell.yaml: -------------------------------------------------------------------------------- 1 | session_name: Pane shell example 2 | windows: 3 | - window_name: first 4 | window_shell: /usr/bin/python2 5 | layout: even-vertical 6 | suppress_history: false 7 | options: 8 | remain-on-exit: true 9 | panes: 10 | - shell: /usr/bin/python3 11 | shell_command: 12 | - print('This is python 3') 13 | - shell: /usr/bin/vim -u none 14 | shell_command: 15 | - iAll panes have the `remain-on-exit` setting on. 16 | - When you exit out of the shell or application, the panes will remain. 17 | - Use tmux command `:kill-pane` to remove the pane. 18 | - Use tmux command `:respawn-pane` to restart the shell in the pane. 19 | - Use and then `:q!` to get out of this vim window. :-) 20 | - shell_command: 21 | - print('Hello World 2') 22 | - shell: /usr/bin/top 23 | -------------------------------------------------------------------------------- /examples/plugin-system.json: -------------------------------------------------------------------------------- 1 | { 2 | "session_name": "plugin-system", 3 | "plugins": [ 4 | "tmuxp_plugin_extended_build.plugin.PluginExtendedBuild" 5 | ], 6 | "windows": [ 7 | { 8 | "window_name": "editor", 9 | "layout": "tiled", 10 | "shell_command_before": [ 11 | "cd ~/" 12 | ], 13 | "panes": [ 14 | { 15 | "shell_command": [ 16 | "cd /var/log", 17 | "ls -al | grep *.log" 18 | ] 19 | }, 20 | "echo \"hello world\"" 21 | ] 22 | } 23 | ] 24 | } -------------------------------------------------------------------------------- /examples/plugin-system.yaml: -------------------------------------------------------------------------------- 1 | session_name: plugin-system 2 | plugins: 3 | - "tmuxp_plugin_extended_build.plugin.PluginExtendedBuild" 4 | windows: 5 | - window_name: editor 6 | layout: tiled 7 | shell_command_before: 8 | - cd ~/ 9 | panes: 10 | - shell_command: 11 | - cd /var/log 12 | - ls -al | grep *.log 13 | - echo "hello world" 14 | -------------------------------------------------------------------------------- /examples/session-environment.json: -------------------------------------------------------------------------------- 1 | { 2 | "environment": { 3 | "EDITOR": "/usr/bin/vim", 4 | "DJANGO_SETTINGS_MODULE": "my_app.settings.local", 5 | "SERVER_PORT": "8009" 6 | }, 7 | "windows": [ 8 | { 9 | "panes": [ 10 | "./manage.py runserver 0.0.0.0:${SERVER_PORT}" 11 | ], 12 | "window_name": "Django project" 13 | }, 14 | { 15 | "environment": { 16 | "DJANGO_SETTINGS_MODULE": "my_app.settings.local", 17 | "SERVER_PORT": "8010" 18 | }, 19 | "panes": [ 20 | "./manage.py runserver 0.0.0.0:${SERVER_PORT}", 21 | { 22 | "environment": { 23 | "DJANGO_SETTINGS_MODULE": "my_app.settings.local-testing", 24 | "SERVER_PORT": "8011" 25 | }, 26 | "shell_command": "./manage.py runserver 0.0.0.0:${SERVER_PORT}" 27 | } 28 | ], 29 | "window_name": "Another Django project" 30 | } 31 | ], 32 | "session_name": "Environment variables test" 33 | } -------------------------------------------------------------------------------- /examples/session-environment.yaml: -------------------------------------------------------------------------------- 1 | session_name: Environment variables test 2 | environment: 3 | EDITOR: /usr/bin/vim 4 | DJANGO_SETTINGS_MODULE: my_app.settings.local 5 | SERVER_PORT: "8009" 6 | windows: 7 | - window_name: Django project 8 | panes: 9 | - ./manage.py runserver 0.0.0.0:${SERVER_PORT} 10 | - window_name: Another Django project 11 | environment: 12 | DJANGO_SETTINGS_MODULE: my_app.settings.local 13 | SERVER_PORT: "8010" 14 | panes: 15 | - ./manage.py runserver 0.0.0.0:${SERVER_PORT} 16 | - environment: 17 | DJANGO_SETTINGS_MODULE: my_app.settings.local-testing 18 | SERVER_PORT: "8011" 19 | shell_command: ./manage.py runserver 0.0.0.0:${SERVER_PORT} 20 | -------------------------------------------------------------------------------- /examples/shorthands.json: -------------------------------------------------------------------------------- 1 | { 2 | "windows": [ 3 | { 4 | "panes": [ 5 | { 6 | "shell_command": [ 7 | "echo 'did you know'", 8 | "echo 'you can inline'" 9 | ] 10 | }, 11 | { 12 | "shell_command": "echo 'single commands'" 13 | }, 14 | "echo 'for panes'" 15 | ], 16 | "window_name": "long form" 17 | } 18 | ], 19 | "session_name": "shorthands" 20 | } -------------------------------------------------------------------------------- /examples/shorthands.yaml: -------------------------------------------------------------------------------- 1 | session_name: shorthands 2 | windows: 3 | - window_name: long form 4 | panes: 5 | - shell_command: 6 | - echo 'did you know' 7 | - echo 'you can inline' 8 | - shell_command: echo 'single commands' 9 | - echo 'for panes' 10 | -------------------------------------------------------------------------------- /examples/skip-send-pane-level.json: -------------------------------------------------------------------------------- 1 | { 2 | "session_name": "Skip command execution (pane-level)", 3 | "windows": [ 4 | { 5 | "panes": [ 6 | { 7 | "shell_command": "echo \"___$((1 + 3))___\"", 8 | "enter": false 9 | }, 10 | { 11 | "shell_command": [ 12 | "echo \"___$((1 + 3))___\"\\;", 13 | "echo \"___$((1 + 3))___\"" 14 | ], 15 | "enter": false 16 | } 17 | ] 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /examples/skip-send-pane-level.yaml: -------------------------------------------------------------------------------- 1 | session_name: Skip command execution (pane-level) 2 | windows: 3 | - panes: 4 | - shell_command: echo "___$((1 + 3))___" 5 | enter: false 6 | - shell_command: 7 | - echo "___$((1 + 3))___"\; 8 | - echo "___$((1 + 3))___" 9 | enter: false 10 | -------------------------------------------------------------------------------- /examples/skip-send.json: -------------------------------------------------------------------------------- 1 | { 2 | "session_name": "Skip command execution (command-level)", 3 | "windows": [ 4 | { 5 | "panes": [ 6 | { 7 | "shell_command": [ 8 | "echo \"___$((11 + 1))___\"", 9 | { 10 | "cmd": "echo \"___$((1 + 3))___\"", 11 | "enter": false 12 | } 13 | ] 14 | } 15 | ] 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /examples/skip-send.yaml: -------------------------------------------------------------------------------- 1 | session_name: Skip command execution (command-level) 2 | windows: 3 | - panes: 4 | - shell_command: 5 | # You can see this 6 | - echo "___$((11 + 1))___" 7 | # This is skipped 8 | - cmd: echo "___$((1 + 3))___" 9 | enter: false 10 | -------------------------------------------------------------------------------- /examples/sleep-pane-level.json: -------------------------------------------------------------------------------- 1 | { 2 | "session_name": "Pause / skip command execution (pane-level)", 3 | "windows": [ 4 | { 5 | "panes": [ 6 | { 7 | "sleep_before": 2, 8 | "shell_command": [ 9 | "echo \"___$((11 + 1))___\"", 10 | { 11 | "cmd": "echo \"___$((1 + 3))___\"" 12 | }, 13 | { 14 | "cmd": "echo \"___$((1 + 3))___\"" 15 | }, 16 | { 17 | "cmd": "echo \"Stuff rendering here!\"" 18 | }, 19 | { 20 | "cmd": "echo \"2 seconds later\"" 21 | } 22 | ] 23 | } 24 | ] 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /examples/sleep-pane-level.yaml: -------------------------------------------------------------------------------- 1 | session_name: Pause / skip command execution (pane-level) 2 | windows: 3 | - panes: 4 | - # Wait 2 seconds before sending all commands in this pane 5 | sleep_before: 2 6 | shell_command: 7 | - echo "___$((11 + 1))___" 8 | - cmd: echo "___$((1 + 3))___" 9 | - cmd: echo "___$((1 + 3))___" 10 | - cmd: echo "Stuff rendering here!" 11 | - cmd: echo "2 seconds later" 12 | -------------------------------------------------------------------------------- /examples/sleep-virtualenv.yaml: -------------------------------------------------------------------------------- 1 | session_name: virtualenv 2 | shell_command_before: 3 | # - cmd: source $(poetry env info --path)/bin/activate 4 | # - cmd: source `pipenv --venv`/bin/activate 5 | - cmd: source .venv/bin/activate 6 | sleep_before: 1 7 | sleep_after: 1 8 | windows: 9 | - panes: 10 | - shell_command: 11 | - ./manage.py runserver 12 | -------------------------------------------------------------------------------- /examples/sleep.json: -------------------------------------------------------------------------------- 1 | { 2 | "session_name": "Pause / skip command execution (command-level)", 3 | "windows": [ 4 | { 5 | "panes": [ 6 | { 7 | "shell_command": [ 8 | "echo \"___$((11 + 1))___\"", 9 | { 10 | "cmd": "echo \"___$((1 + 3))___\"", 11 | "sleep_before": 2 12 | }, 13 | { 14 | "cmd": "echo \"___$((1 + 3))___\"" 15 | }, 16 | { 17 | "cmd": "echo \"Stuff rendering here!\"", 18 | "sleep_after": 2 19 | }, 20 | { 21 | "cmd": "echo \"2 seconds later\"" 22 | } 23 | ] 24 | } 25 | ] 26 | } 27 | ] 28 | } -------------------------------------------------------------------------------- /examples/sleep.yaml: -------------------------------------------------------------------------------- 1 | session_name: Pause / skip command execution (command-level) 2 | windows: 3 | - panes: 4 | - shell_command: 5 | # Executes immediately 6 | - echo "___$((11 + 1))___" 7 | # Delays before sending 2 seconds 8 | - cmd: echo "___$((1 + 3))___" 9 | sleep_before: 2 10 | # Executes immediately 11 | - cmd: echo "___$((1 + 3))___" 12 | # Pauses 2 seconds after 13 | - cmd: echo "Stuff rendering here!" 14 | sleep_after: 2 15 | # Executes after earlier commands (after 2 sec) 16 | - cmd: echo "2 seconds later" 17 | -------------------------------------------------------------------------------- /examples/start-directory.json: -------------------------------------------------------------------------------- 1 | { 2 | "windows": [ 3 | { 4 | "panes": [ 5 | { 6 | "shell_command": [ 7 | "echo \"\\033c", 8 | "it trickles down from session-level\"" 9 | ] 10 | }, 11 | "echo hello" 12 | ], 13 | "window_name": "should be /var/" 14 | }, 15 | { 16 | "panes": [ 17 | { 18 | "shell_command": [ 19 | "echo '\\033c", 20 | "window start_directory concatenates to session start_directory", 21 | "if it is not absolute'" 22 | ] 23 | }, 24 | "echo hello" 25 | ], 26 | "start_directory": "log", 27 | "window_name": "should be /var/log" 28 | }, 29 | { 30 | "panes": [ 31 | { 32 | "shell_command": [ 33 | "echo \\\\033c ~ has precedence. note: remember to quote ~ in YAML" 34 | ] 35 | }, 36 | "echo hello" 37 | ], 38 | "start_directory": "~", 39 | "window_name": "should be ~" 40 | }, 41 | { 42 | "panes": [ 43 | "echo '\\033c absolute paths also have precedence.'", 44 | "echo hello" 45 | ], 46 | "start_directory": "/bin", 47 | "window_name": "should be /bin" 48 | }, 49 | { 50 | "panes": [ 51 | { 52 | "shell_command": [ 53 | "echo '\\033c", 54 | "./ is relative to workspace file location", 55 | "../ will be parent of workspace file", 56 | "./test will be \\\"test\\\" dir inside dir of workspace file'" 57 | ] 58 | }, 59 | { 60 | "shell_command": [ 61 | "echo '\\033c", 62 | "This way you can load up workspaces from projects and maintain", 63 | "relative paths.'" 64 | ] 65 | } 66 | ], 67 | "start_directory": "./", 68 | "window_name": "should be config's dir" 69 | } 70 | ], 71 | "session_name": "start directory", 72 | "start_directory": "/var/" 73 | } 74 | -------------------------------------------------------------------------------- /examples/start-directory.yaml: -------------------------------------------------------------------------------- 1 | session_name: start directory 2 | start_directory: /var/ 3 | windows: 4 | - window_name: should be /var/ 5 | panes: 6 | - shell_command: 7 | - echo "\033c 8 | - it trickles down from session-level" 9 | - echo hello 10 | - window_name: should be /var/log 11 | start_directory: log 12 | panes: 13 | - shell_command: 14 | - echo '\033c 15 | - window start_directory concatenates to session start_directory 16 | - if it is not absolute' 17 | - echo hello 18 | - window_name: should be ~ 19 | start_directory: "~" 20 | panes: 21 | - shell_command: 22 | - 'echo \\033c ~ has precedence. note: remember to quote ~ in YAML' 23 | - echo hello 24 | - window_name: should be /bin 25 | start_directory: /bin 26 | panes: 27 | - echo '\033c absolute paths also have precedence.' 28 | - echo hello 29 | - window_name: should be workspace file's dir 30 | 31 | start_directory: ./ 32 | panes: 33 | - shell_command: 34 | - echo '\033c 35 | - ./ is relative to workspace file location 36 | - ../ will be parent of workspace file 37 | - ./test will be \"test\" dir inside dir of workspace file' 38 | - shell_command: 39 | - echo '\033c 40 | - This way you can load up workspaces from projects and maintain 41 | - relative paths.' 42 | -------------------------------------------------------------------------------- /examples/suppress-history.json: -------------------------------------------------------------------------------- 1 | { 2 | "windows": [ 3 | { 4 | "panes": [ 5 | "echo 'window in the history!'" 6 | ], 7 | "focus": true, 8 | "suppress_history": false, 9 | "window_name": "appended" 10 | }, 11 | { 12 | "panes": [ 13 | "echo 'window not in the history!'" 14 | ], 15 | "suppress_history": true, 16 | "window_name": "suppressed" 17 | }, 18 | { 19 | "panes": [ 20 | "echo 'session in the history!'" 21 | ], 22 | "window_name": "default" 23 | }, 24 | { 25 | "panes": [ 26 | { 27 | "shell_command": "echo 'command in the history!'", 28 | "suppress_history": false 29 | }, 30 | { 31 | "shell_command": "echo 'command not in the history!'", 32 | "suppress_history": true 33 | }, 34 | { 35 | "shell_command": "echo 'window not in the history!'" 36 | } 37 | ], 38 | "suppress_history": true, 39 | "window_name": "mixed" 40 | } 41 | ], 42 | "suppress_history": false, 43 | "session_name": "suppress" 44 | } 45 | -------------------------------------------------------------------------------- /examples/suppress-history.yaml: -------------------------------------------------------------------------------- 1 | session_name: suppress 2 | suppress_history: false 3 | windows: 4 | - window_name: appended 5 | focus: true 6 | suppress_history: false 7 | panes: 8 | - echo "window in the history!" 9 | 10 | - window_name: suppressed 11 | suppress_history: true 12 | panes: 13 | - echo "window not in the history!" 14 | 15 | - window_name: default 16 | panes: 17 | - echo "session in the history!" 18 | 19 | - window_name: mixed 20 | suppress_history: false 21 | panes: 22 | - shell_command: 23 | - echo "command in the history!" 24 | suppress_history: false 25 | - shell_command: 26 | - echo "command not in the history!" 27 | suppress_history: true 28 | - shell_command: 29 | - echo "window in the history!" 30 | -------------------------------------------------------------------------------- /examples/window-index.json: -------------------------------------------------------------------------------- 1 | { 2 | "windows": [ 3 | { 4 | "panes": [ 5 | "echo \"this window's index will be zero\"" 6 | ], 7 | "window_name": "zero" 8 | }, 9 | { 10 | "panes": [ 11 | "echo \"this window's index will be five\"" 12 | ], 13 | "window_index": 5, 14 | "window_name": "five" 15 | }, 16 | { 17 | "panes": [ 18 | "echo \"this window's index will be one\"" 19 | ], 20 | "window_name": "one" 21 | } 22 | ], 23 | "session_name": "Window index example" 24 | } 25 | -------------------------------------------------------------------------------- /examples/window-index.yaml: -------------------------------------------------------------------------------- 1 | session_name: Window index example 2 | windows: 3 | - window_name: zero 4 | panes: 5 | - echo "this window's index will be zero" 6 | - window_name: five 7 | panes: 8 | - echo "this window's index will be five" 9 | window_index: 5 10 | - window_name: one 11 | panes: 12 | - echo "this window's index will be one" 13 | -------------------------------------------------------------------------------- /src/tmuxp/__about__.py: -------------------------------------------------------------------------------- 1 | """Metadata for tmuxp package.""" 2 | 3 | from __future__ import annotations 4 | 5 | __title__ = "tmuxp" 6 | __package_name__ = "tmuxp" 7 | __version__ = "1.55.0" 8 | __description__ = "tmux session manager" 9 | __email__ = "tony@git-pull.com" 10 | __author__ = "Tony Narlock" 11 | __github__ = "https://github.com/tmux-python/tmuxp" 12 | __docs__ = "https://tmuxp.git-pull.com" 13 | __tracker__ = "https://github.com/tmux-python/tmuxp/issues" 14 | __changes__ = "https://github.com/tmux-python/tmuxp/blob/master/CHANGES" 15 | __pypi__ = "https://pypi.org/project/tmuxp/" 16 | __license__ = "MIT" 17 | __copyright__ = "Copyright 2013- Tony Narlock" 18 | -------------------------------------------------------------------------------- /src/tmuxp/__init__.py: -------------------------------------------------------------------------------- 1 | # flake8: NOQA: F401 2 | """tmux session manager. 3 | 4 | :copyright: Copyright 2013- Tony Narlock. 5 | :license: MIT, see LICENSE for details 6 | """ 7 | 8 | from __future__ import annotations 9 | 10 | from . import cli, util 11 | from .__about__ import ( 12 | __author__, 13 | __copyright__, 14 | __description__, 15 | __email__, 16 | __license__, 17 | __package_name__, 18 | __title__, 19 | __version__, 20 | ) 21 | -------------------------------------------------------------------------------- /src/tmuxp/_compat.py: -------------------------------------------------------------------------------- 1 | # flake8: NOQA 2 | import sys 3 | 4 | PY3 = sys.version_info[0] == 3 5 | PYMINOR = sys.version_info[1] 6 | PYPATCH = sys.version_info[2] 7 | 8 | _identity = lambda x: x 9 | 10 | if PY3 and PYMINOR >= 7: 11 | breakpoint = breakpoint 12 | else: 13 | import pdb 14 | 15 | breakpoint = pdb.set_trace 16 | 17 | 18 | implements_to_string = _identity 19 | -------------------------------------------------------------------------------- /src/tmuxp/_internal/__init__.py: -------------------------------------------------------------------------------- 1 | """Internal APIs for tmuxp.""" 2 | -------------------------------------------------------------------------------- /src/tmuxp/_internal/types.py: -------------------------------------------------------------------------------- 1 | """Internal, :const:`typing.TYPE_CHECKING` guarded :term:`typings `. 2 | 3 | These are _not_ to be imported at runtime as `typing_extensions` is not 4 | bundled with tmuxp. Usage example: 5 | 6 | >>> import typing as t 7 | 8 | >>> if t.TYPE_CHECKING: 9 | ... from tmuxp._internal.types import PluginConfigSchema 10 | ... 11 | """ 12 | 13 | from __future__ import annotations 14 | 15 | import typing as t 16 | from typing import TypedDict 17 | 18 | if t.TYPE_CHECKING: 19 | import sys 20 | 21 | if sys.version_info >= (3, 11): 22 | from typing import NotRequired 23 | else: 24 | from typing_extensions import NotRequired 25 | 26 | 27 | class PluginConfigSchema(TypedDict): 28 | plugin_name: NotRequired[str] 29 | tmux_min_version: NotRequired[str] 30 | tmux_max_version: NotRequired[str] 31 | tmux_version_incompatible: NotRequired[list[str]] 32 | libtmux_min_version: NotRequired[str] 33 | libtmux_max_version: NotRequired[str] 34 | libtmux_version_incompatible: NotRequired[list[str]] 35 | tmuxp_min_version: NotRequired[str] 36 | tmuxp_max_version: NotRequired[str] 37 | tmuxp_version_incompatible: NotRequired[list[str]] 38 | -------------------------------------------------------------------------------- /src/tmuxp/cli/convert.py: -------------------------------------------------------------------------------- 1 | """CLI for ``tmuxp convert`` subcommand.""" 2 | 3 | from __future__ import annotations 4 | 5 | import locale 6 | import os 7 | import pathlib 8 | import typing as t 9 | 10 | from tmuxp import exc 11 | from tmuxp._internal.config_reader import ConfigReader 12 | from tmuxp.workspace.finders import find_workspace_file, get_workspace_dir 13 | 14 | from .utils import prompt_yes_no 15 | 16 | if t.TYPE_CHECKING: 17 | import argparse 18 | 19 | AllowedFileTypes = t.Literal["json", "yaml"] 20 | 21 | 22 | def create_convert_subparser( 23 | parser: argparse.ArgumentParser, 24 | ) -> argparse.ArgumentParser: 25 | """Augment :class:`argparse.ArgumentParser` with ``convert`` subcommand.""" 26 | workspace_file = parser.add_argument( 27 | dest="workspace_file", 28 | type=str, 29 | metavar="workspace-file", 30 | help="checks tmuxp and current directory for workspace files.", 31 | ) 32 | try: 33 | import shtab 34 | 35 | workspace_file.complete = shtab.FILE # type: ignore 36 | except ImportError: 37 | pass 38 | 39 | parser.add_argument( 40 | "--yes", 41 | "-y", 42 | dest="answer_yes", 43 | action="store_true", 44 | help="always answer yes", 45 | ) 46 | return parser 47 | 48 | 49 | class ConvertUnknownFileType(exc.TmuxpException): 50 | """Raise if tmuxp convert encounters an unknown filetype.""" 51 | 52 | def __init__(self, ext: str, *args: object, **kwargs: object) -> None: 53 | return super().__init__( 54 | f"Unknown filetype: {ext} (valid: [.json, .yaml, .yml])", 55 | ) 56 | 57 | 58 | def command_convert( 59 | workspace_file: str | pathlib.Path, 60 | answer_yes: bool, 61 | parser: argparse.ArgumentParser | None = None, 62 | ) -> None: 63 | """Entrypoint for ``tmuxp convert`` convert a tmuxp config between JSON and YAML.""" 64 | workspace_file = find_workspace_file( 65 | workspace_file, 66 | workspace_dir=get_workspace_dir(), 67 | ) 68 | 69 | if isinstance(workspace_file, str): 70 | workspace_file = pathlib.Path(workspace_file) 71 | 72 | _, ext = os.path.splitext(workspace_file) 73 | ext = ext.lower() 74 | to_filetype: AllowedFileTypes 75 | if ext == ".json": 76 | to_filetype = "yaml" 77 | elif ext in {".yaml", ".yml"}: 78 | to_filetype = "json" 79 | else: 80 | raise ConvertUnknownFileType(ext) 81 | 82 | configparser = ConfigReader.from_file(workspace_file) 83 | newfile = workspace_file.parent / (str(workspace_file.stem) + f".{to_filetype}") 84 | 85 | new_workspace = configparser.dump( 86 | fmt=to_filetype, 87 | indent=2, 88 | **{"default_flow_style": False} if to_filetype == "yaml" else {}, 89 | ) 90 | 91 | if ( 92 | not answer_yes 93 | and prompt_yes_no(f"Convert to <{workspace_file}> to {to_filetype}?") 94 | and prompt_yes_no(f"Save workspace to {newfile}?") 95 | ): 96 | answer_yes = True 97 | 98 | if answer_yes: 99 | with open(newfile, "w", encoding=locale.getpreferredencoding(False)) as buf: 100 | buf.write(new_workspace) 101 | print(f"New workspace file saved to <{newfile}>.") # NOQA: T201 RUF100 102 | -------------------------------------------------------------------------------- /src/tmuxp/cli/debug_info.py: -------------------------------------------------------------------------------- 1 | """CLI for ``tmuxp debug-info`` subcommand.""" 2 | 3 | from __future__ import annotations 4 | 5 | import os 6 | import pathlib 7 | import platform 8 | import shutil 9 | import sys 10 | import typing as t 11 | 12 | from colorama import Fore 13 | from libtmux.__about__ import __version__ as libtmux_version 14 | from libtmux.common import get_version, tmux_cmd 15 | 16 | from tmuxp.__about__ import __version__ 17 | 18 | from .utils import tmuxp_echo 19 | 20 | if t.TYPE_CHECKING: 21 | import argparse 22 | 23 | tmuxp_path = pathlib.Path(__file__).parent.parent 24 | 25 | 26 | def create_debug_info_subparser( 27 | parser: argparse.ArgumentParser, 28 | ) -> argparse.ArgumentParser: 29 | """Augment :class:`argparse.ArgumentParser` with ``debug-info`` subcommand.""" 30 | return parser 31 | 32 | 33 | def command_debug_info( 34 | parser: argparse.ArgumentParser | None = None, 35 | ) -> None: 36 | """Entrypoint for ``tmuxp debug-info`` to print debug info to submit with issues.""" 37 | 38 | def prepend_tab(strings: list[str]) -> list[str]: 39 | """Prepend tab to strings in list.""" 40 | return [f"\t{x}" for x in strings] 41 | 42 | def output_break() -> str: 43 | """Generate output break.""" 44 | return "-" * 25 45 | 46 | def format_tmux_resp(std_resp: tmux_cmd) -> str: 47 | """Format tmux command response for tmuxp stdout.""" 48 | return "\n".join( 49 | [ 50 | "\n".join(prepend_tab(std_resp.stdout)), 51 | Fore.RED, 52 | "\n".join(prepend_tab(std_resp.stderr)), 53 | Fore.RESET, 54 | ], 55 | ) 56 | 57 | output = [ 58 | output_break(), 59 | "environment:\n{}".format( 60 | "\n".join( 61 | prepend_tab( 62 | [ 63 | f"dist: {platform.platform()}", 64 | f"arch: {platform.machine()}", 65 | "uname: {}".format("; ".join(platform.uname()[:3])), 66 | f"version: {platform.version()}", 67 | ], 68 | ), 69 | ), 70 | ), 71 | output_break(), 72 | "python version: {}".format(" ".join(sys.version.split("\n"))), 73 | "system PATH: {}".format(os.environ["PATH"]), 74 | f"tmux version: {get_version()}", 75 | f"libtmux version: {libtmux_version}", 76 | f"tmuxp version: {__version__}", 77 | "tmux path: {}".format(shutil.which("tmux")), 78 | f"tmuxp path: {tmuxp_path}", 79 | "shell: {}".format(os.environ["SHELL"]), 80 | output_break(), 81 | "tmux sessions:\n{}".format(format_tmux_resp(tmux_cmd("list-sessions"))), 82 | "tmux windows:\n{}".format(format_tmux_resp(tmux_cmd("list-windows"))), 83 | "tmux panes:\n{}".format(format_tmux_resp(tmux_cmd("list-panes"))), 84 | "tmux global options:\n{}".format( 85 | format_tmux_resp(tmux_cmd("show-options", "-g")), 86 | ), 87 | "tmux window options:\n{}".format( 88 | format_tmux_resp(tmux_cmd("show-window-options", "-g")), 89 | ), 90 | ] 91 | 92 | tmuxp_echo("\n".join(output)) 93 | -------------------------------------------------------------------------------- /src/tmuxp/cli/edit.py: -------------------------------------------------------------------------------- 1 | """CLI for ``tmuxp edit`` subcommand.""" 2 | 3 | from __future__ import annotations 4 | 5 | import os 6 | import subprocess 7 | import typing as t 8 | 9 | from tmuxp.workspace.finders import find_workspace_file 10 | 11 | if t.TYPE_CHECKING: 12 | import argparse 13 | import pathlib 14 | 15 | 16 | def create_edit_subparser( 17 | parser: argparse.ArgumentParser, 18 | ) -> argparse.ArgumentParser: 19 | """Augment :class:`argparse.ArgumentParser` with ``edit`` subcommand.""" 20 | parser.add_argument( 21 | dest="workspace_file", 22 | metavar="workspace-file", 23 | type=str, 24 | help="checks current tmuxp and current directory for workspace files.", 25 | ) 26 | return parser 27 | 28 | 29 | def command_edit( 30 | workspace_file: str | pathlib.Path, 31 | parser: argparse.ArgumentParser | None = None, 32 | ) -> None: 33 | """Entrypoint for ``tmuxp edit``, open tmuxp workspace file in system editor.""" 34 | workspace_file = find_workspace_file(workspace_file) 35 | 36 | sys_editor = os.environ.get("EDITOR", "vim") 37 | subprocess.call([sys_editor, workspace_file]) 38 | -------------------------------------------------------------------------------- /src/tmuxp/cli/ls.py: -------------------------------------------------------------------------------- 1 | """CLI for ``tmuxp ls`` subcommand.""" 2 | 3 | from __future__ import annotations 4 | 5 | import os 6 | import typing as t 7 | 8 | from tmuxp.workspace.constants import VALID_WORKSPACE_DIR_FILE_EXTENSIONS 9 | from tmuxp.workspace.finders import get_workspace_dir 10 | 11 | if t.TYPE_CHECKING: 12 | import argparse 13 | 14 | 15 | def create_ls_subparser( 16 | parser: argparse.ArgumentParser, 17 | ) -> argparse.ArgumentParser: 18 | """Augment :class:`argparse.ArgumentParser` with ``ls`` subcommand.""" 19 | return parser 20 | 21 | 22 | def command_ls( 23 | parser: argparse.ArgumentParser | None = None, 24 | ) -> None: 25 | """Entrypoint for ``tmuxp ls`` subcommand.""" 26 | tmuxp_dir = get_workspace_dir() 27 | if os.path.exists(tmuxp_dir) and os.path.isdir(tmuxp_dir): 28 | for f in sorted(os.listdir(tmuxp_dir)): 29 | stem, ext = os.path.splitext(f) 30 | if os.path.isdir(f) or ext not in VALID_WORKSPACE_DIR_FILE_EXTENSIONS: 31 | continue 32 | print(stem) # NOQA: T201 RUF100 33 | -------------------------------------------------------------------------------- /src/tmuxp/types.py: -------------------------------------------------------------------------------- 1 | """Internal :term:`type annotations `. 2 | 3 | Notes 4 | ----- 5 | :class:`StrPath` and :class:`StrOrBytesPath` is based on `typeshed's`_. 6 | 7 | .. _typeshed's: https://github.com/python/typeshed/blob/9687d5/stdlib/_typeshed/__init__.pyi#L98 8 | """ # E501 9 | 10 | from __future__ import annotations 11 | 12 | import typing as t 13 | 14 | if t.TYPE_CHECKING: 15 | from os import PathLike 16 | 17 | StrPath = t.Union[str, "PathLike[str]"] 18 | """:class:`os.PathLike` or :class:`str`""" 19 | -------------------------------------------------------------------------------- /src/tmuxp/workspace/__init__.py: -------------------------------------------------------------------------------- 1 | """tmuxp workspace functionality.""" 2 | -------------------------------------------------------------------------------- /src/tmuxp/workspace/constants.py: -------------------------------------------------------------------------------- 1 | """Constant variables for tmuxp workspace functionality.""" 2 | 3 | from __future__ import annotations 4 | 5 | VALID_WORKSPACE_DIR_FILE_EXTENSIONS = [".yaml", ".yml", ".json"] 6 | -------------------------------------------------------------------------------- /src/tmuxp/workspace/validation.py: -------------------------------------------------------------------------------- 1 | """Validation errors for tmuxp configuration files.""" 2 | 3 | from __future__ import annotations 4 | 5 | import typing as t 6 | 7 | from tmuxp import exc 8 | 9 | 10 | class SchemaValidationError(exc.WorkspaceError): 11 | """Tmuxp configuration validation base error.""" 12 | 13 | 14 | class SessionNameMissingValidationError(SchemaValidationError): 15 | """Tmuxp configuration error for session name missing.""" 16 | 17 | def __init__(self, *args: object, **kwargs: object) -> None: 18 | return super().__init__( 19 | 'workspace requires "session_name"', 20 | *args, 21 | **kwargs, 22 | ) 23 | 24 | 25 | class WindowListMissingValidationError(SchemaValidationError): 26 | """Tmuxp configuration error for window list missing.""" 27 | 28 | def __init__(self, *args: object, **kwargs: object) -> None: 29 | return super().__init__( 30 | 'workspace requires list of "windows"', 31 | *args, 32 | **kwargs, 33 | ) 34 | 35 | 36 | class WindowNameMissingValidationError(SchemaValidationError): 37 | """Tmuxp configuration error for missing window_name.""" 38 | 39 | def __init__(self, *args: object, **kwargs: object) -> None: 40 | return super().__init__( 41 | 'workspace window is missing "window_name"', 42 | *args, 43 | **kwargs, 44 | ) 45 | 46 | 47 | class InvalidPluginsValidationError(SchemaValidationError): 48 | """Tmuxp configuration error for invalid plugins.""" 49 | 50 | def __init__(self, plugins: t.Any, *args: object, **kwargs: object) -> None: 51 | return super().__init__( 52 | '"plugins" only supports list type. ' 53 | + f" Received {type(plugins)}, " 54 | + f"value: {plugins}", 55 | *args, 56 | **kwargs, 57 | ) 58 | 59 | 60 | def validate_schema(workspace_dict: t.Any) -> bool: 61 | """ 62 | Return True if workspace schema is correct. 63 | 64 | Parameters 65 | ---------- 66 | workspace_dict : dict 67 | tmuxp workspace data 68 | 69 | Returns 70 | ------- 71 | bool 72 | """ 73 | # verify session_name 74 | if "session_name" not in workspace_dict: 75 | raise SessionNameMissingValidationError 76 | 77 | if "windows" not in workspace_dict: 78 | raise WindowListMissingValidationError 79 | 80 | for window in workspace_dict["windows"]: 81 | if "window_name" not in window: 82 | raise WindowNameMissingValidationError 83 | 84 | if "plugins" in workspace_dict and not isinstance(workspace_dict["plugins"], list): 85 | raise InvalidPluginsValidationError(plugins=workspace_dict.get("plugins")) 86 | 87 | return True 88 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | """Tests for tmuxp.""" 2 | -------------------------------------------------------------------------------- /tests/cli/__init__.py: -------------------------------------------------------------------------------- 1 | """CLI tests for tmuxp.""" 2 | -------------------------------------------------------------------------------- /tests/cli/test_debug_info.py: -------------------------------------------------------------------------------- 1 | """CLI tests for tmuxp debuginfo.""" 2 | 3 | from __future__ import annotations 4 | 5 | import typing as t 6 | 7 | from tmuxp import cli 8 | 9 | if t.TYPE_CHECKING: 10 | import pathlib 11 | 12 | import pytest 13 | 14 | 15 | def test_debug_info_cli( 16 | monkeypatch: pytest.MonkeyPatch, 17 | tmp_path: pathlib.Path, 18 | capsys: pytest.CaptureFixture[str], 19 | ) -> None: 20 | """Basic CLI test for tmuxp debug-info.""" 21 | monkeypatch.setenv("SHELL", "/bin/bash") 22 | 23 | cli.cli(["debug-info"]) 24 | cli_output = capsys.readouterr().out 25 | assert "environment" in cli_output 26 | assert "python version" in cli_output 27 | assert "system PATH" in cli_output 28 | assert "tmux version" in cli_output 29 | assert "libtmux version" in cli_output 30 | assert "tmuxp version" in cli_output 31 | assert "tmux path" in cli_output 32 | assert "tmuxp path" in cli_output 33 | assert "shell" in cli_output 34 | assert "tmux session" in cli_output 35 | assert "tmux windows" in cli_output 36 | assert "tmux panes" in cli_output 37 | assert "tmux global options" in cli_output 38 | assert "tmux window options" in cli_output 39 | -------------------------------------------------------------------------------- /tests/cli/test_ls.py: -------------------------------------------------------------------------------- 1 | """CLI tests for tmuxp ls command.""" 2 | 3 | from __future__ import annotations 4 | 5 | import contextlib 6 | import pathlib 7 | import typing as t 8 | 9 | from tmuxp import cli 10 | 11 | if t.TYPE_CHECKING: 12 | import pytest 13 | 14 | 15 | def test_ls_cli( 16 | monkeypatch: pytest.MonkeyPatch, 17 | tmp_path: pathlib.Path, 18 | capsys: pytest.CaptureFixture[str], 19 | ) -> None: 20 | """CLI test for tmuxp ls.""" 21 | monkeypatch.setenv("HOME", str(tmp_path)) 22 | monkeypatch.setenv("XDG_CONFIG_HOME", str(tmp_path / ".config")) 23 | 24 | filenames = [ 25 | ".git/", 26 | ".gitignore/", 27 | "session_1.yaml", 28 | "session_2.yaml", 29 | "session_3.json", 30 | "session_4.txt", 31 | ] 32 | 33 | # should ignore: 34 | # - directories should be ignored 35 | # - extensions not covered in VALID_WORKSPACE_DIR_FILE_EXTENSIONS 36 | ignored_filenames = [".git/", ".gitignore/", "session_4.txt"] 37 | stems = [pathlib.Path(f).stem for f in filenames if f not in ignored_filenames] 38 | 39 | for filename in filenames: 40 | location = tmp_path / f".tmuxp/{filename}" 41 | if filename.endswith("/"): 42 | location.mkdir(parents=True) 43 | else: 44 | location.touch() 45 | 46 | with contextlib.suppress(SystemExit): 47 | cli.cli(["ls"]) 48 | 49 | cli_output = capsys.readouterr().out 50 | 51 | assert cli_output == "\n".join(stems) + "\n" 52 | -------------------------------------------------------------------------------- /tests/constants.py: -------------------------------------------------------------------------------- 1 | """Constant variables for tmuxp tests.""" 2 | 3 | from __future__ import annotations 4 | 5 | import pathlib 6 | 7 | TESTS_PATH = pathlib.Path(__file__).parent 8 | EXAMPLE_PATH = TESTS_PATH.parent / "examples" 9 | FIXTURE_PATH = TESTS_PATH / "fixtures" 10 | -------------------------------------------------------------------------------- /tests/fixtures/__init__.py: -------------------------------------------------------------------------------- 1 | """Fixture test data for tmuxp.""" 2 | 3 | from __future__ import annotations 4 | 5 | from . import utils 6 | -------------------------------------------------------------------------------- /tests/fixtures/import_teamocil/__init__.py: -------------------------------------------------------------------------------- 1 | """Teamocil data fixtures for import_teamocil tests.""" 2 | 3 | from __future__ import annotations 4 | 5 | from . import layouts, test1, test2, test3, test4 6 | -------------------------------------------------------------------------------- /tests/fixtures/import_teamocil/layouts.yaml: -------------------------------------------------------------------------------- 1 | # Simple two windows layout 2 | two-windows: 3 | windows: 4 | - name: "foo" 5 | clear: true 6 | root: "/foo" 7 | layout: "tiled" 8 | panes: 9 | - cmd: "echo 'foo'" 10 | - cmd: "echo 'foo again'" 11 | - name: "bar" 12 | root: "/bar" 13 | splits: 14 | - cmd: 15 | - "echo 'bar'" 16 | - "echo 'bar in an array'" 17 | target: bottom-right 18 | - cmd: "echo 'bar again'" 19 | focus: true 20 | width: 50 21 | 22 | # Simple two windows layout with filters 23 | two-windows-with-filters: 24 | windows: 25 | - name: "foo" 26 | root: "/foo" 27 | filters: 28 | before: 29 | - "echo first before filter" 30 | - "echo second before filter" 31 | after: 32 | - "echo first after filter" 33 | - "echo second after filter" 34 | panes: 35 | - cmd: "echo 'foo'" 36 | - cmd: "echo 'foo again'" 37 | width: 50 38 | 39 | two-windows-with-custom-command-options: 40 | windows: 41 | - name: "foo" 42 | cmd_separator: "\n" 43 | with_env_var: false 44 | clear: true 45 | root: "/foo" 46 | layout: "tiled" 47 | panes: 48 | - cmd: "echo 'foo'" 49 | - cmd: "echo 'foo again'" 50 | - name: "bar" 51 | cmd_separator: " && " 52 | with_env_var: true 53 | root: "/bar" 54 | splits: 55 | - cmd: 56 | - "echo 'bar'" 57 | - "echo 'bar in an array'" 58 | target: bottom-right 59 | - cmd: "echo 'bar again'" 60 | focus: true 61 | width: 50 62 | 63 | three-windows-within-a-session: 64 | session: 65 | name: "my awesome session" 66 | windows: 67 | - name: "first window" 68 | panes: 69 | - cmd: "echo 'foo'" 70 | - name: "second window" 71 | panes: 72 | - cmd: "echo 'foo'" 73 | - name: "third window" 74 | panes: 75 | - cmd: "echo 'foo'" 76 | -------------------------------------------------------------------------------- /tests/fixtures/import_teamocil/test1.py: -------------------------------------------------------------------------------- 1 | """Teamocil data fixtures for import_teamocil tests, 1st test.""" 2 | 3 | from __future__ import annotations 4 | 5 | from tests.fixtures import utils as test_utils 6 | 7 | teamocil_yaml = test_utils.read_workspace_file("import_teamocil/test1.yaml") 8 | teamocil_conf = { 9 | "windows": [ 10 | { 11 | "name": "sample-two-panes", 12 | "root": "~/Code/sample/www", 13 | "layout": "even-horizontal", 14 | "panes": [{"cmd": ["pwd", "ls -la"]}, {"cmd": "rails server --port 3000"}], 15 | }, 16 | ], 17 | } 18 | 19 | expected = { 20 | "session_name": None, 21 | "windows": [ 22 | { 23 | "window_name": "sample-two-panes", 24 | "layout": "even-horizontal", 25 | "start_directory": "~/Code/sample/www", 26 | "panes": [ 27 | {"shell_command": ["pwd", "ls -la"]}, 28 | {"shell_command": "rails server --port 3000"}, 29 | ], 30 | }, 31 | ], 32 | } 33 | -------------------------------------------------------------------------------- /tests/fixtures/import_teamocil/test1.yaml: -------------------------------------------------------------------------------- 1 | windows: 2 | - name: "sample-two-panes" 3 | root: "~/Code/sample/www" 4 | layout: even-horizontal 5 | panes: 6 | - cmd: ["pwd", "ls -la"] 7 | - cmd: "rails server --port 3000" 8 | -------------------------------------------------------------------------------- /tests/fixtures/import_teamocil/test2.py: -------------------------------------------------------------------------------- 1 | """Teamocil data fixtures for import_teamocil tests, 2nd test.""" 2 | 3 | from __future__ import annotations 4 | 5 | from tests.fixtures import utils as test_utils 6 | 7 | teamocil_yaml = test_utils.read_workspace_file("import_teamocil/test2.yaml") 8 | teamocil_dict = { 9 | "windows": [ 10 | { 11 | "name": "sample-four-panes", 12 | "root": "~/Code/sample/www", 13 | "layout": "tiled", 14 | "panes": [{"cmd": "pwd"}, {"cmd": "pwd"}, {"cmd": "pwd"}, {"cmd": "pwd"}], 15 | }, 16 | ], 17 | } 18 | 19 | expected = { 20 | "session_name": None, 21 | "windows": [ 22 | { 23 | "window_name": "sample-four-panes", 24 | "layout": "tiled", 25 | "start_directory": "~/Code/sample/www", 26 | "panes": [ 27 | {"shell_command": "pwd"}, 28 | {"shell_command": "pwd"}, 29 | {"shell_command": "pwd"}, 30 | {"shell_command": "pwd"}, 31 | ], 32 | }, 33 | ], 34 | } 35 | -------------------------------------------------------------------------------- /tests/fixtures/import_teamocil/test2.yaml: -------------------------------------------------------------------------------- 1 | windows: 2 | - name: "sample-four-panes" 3 | root: "~/Code/sample/www" 4 | layout: tiled 5 | panes: 6 | - cmd: "pwd" 7 | - cmd: "pwd" 8 | - cmd: "pwd" 9 | - cmd: "pwd" 10 | -------------------------------------------------------------------------------- /tests/fixtures/import_teamocil/test3.py: -------------------------------------------------------------------------------- 1 | """Teamocil data fixtures for import_teamocil tests, 3rd test.""" 2 | 3 | from __future__ import annotations 4 | 5 | from tests.fixtures import utils as test_utils 6 | 7 | teamocil_yaml = test_utils.read_workspace_file("import_teamocil/test3.yaml") 8 | 9 | teamocil_dict = { 10 | "windows": [ 11 | { 12 | "name": "my-first-window", 13 | "root": "~/Projects/foo-www", 14 | "layout": "even-vertical", 15 | "filters": { 16 | "before": "rbenv local 2.0.0-p0", 17 | "after": "echo 'I am done initializing this pane.'", 18 | }, 19 | "panes": [ 20 | {"cmd": "git status"}, 21 | {"cmd": "bundle exec rails server --port 40", "focus": True}, 22 | {"cmd": ["sudo service memcached start", "sudo service mongodb start"]}, 23 | ], 24 | }, 25 | ], 26 | } 27 | 28 | expected = { 29 | "session_name": None, 30 | "windows": [ 31 | { 32 | "window_name": "my-first-window", 33 | "layout": "even-vertical", 34 | "start_directory": "~/Projects/foo-www", 35 | "shell_command_before": "rbenv local 2.0.0-p0", 36 | "shell_command_after": ("echo 'I am done initializing this pane.'"), 37 | "panes": [ 38 | {"shell_command": "git status"}, 39 | {"shell_command": "bundle exec rails server --port 40", "focus": True}, 40 | { 41 | "shell_command": [ 42 | "sudo service memcached start", 43 | "sudo service mongodb start", 44 | ], 45 | }, 46 | ], 47 | }, 48 | ], 49 | } 50 | -------------------------------------------------------------------------------- /tests/fixtures/import_teamocil/test3.yaml: -------------------------------------------------------------------------------- 1 | windows: 2 | - name: "my-first-window" 3 | root: "~/Projects/foo-www" 4 | layout: even-vertical 5 | filters: 6 | before: "rbenv local 2.0.0-p0" 7 | after: "echo 'I am done initializing this pane.'" 8 | panes: 9 | - cmd: "git status" 10 | - cmd: "bundle exec rails server --port 40" 11 | focus: true 12 | - cmd: 13 | - "sudo service memcached start" 14 | - "sudo service mongodb start" 15 | -------------------------------------------------------------------------------- /tests/fixtures/import_teamocil/test4.py: -------------------------------------------------------------------------------- 1 | """Teamocil data fixtures for import_teamocil tests, 4th test.""" 2 | 3 | from __future__ import annotations 4 | 5 | from tests.fixtures import utils as test_utils 6 | 7 | teamocil_yaml = test_utils.read_workspace_file("import_teamocil/test4.yaml") 8 | 9 | teamocil_dict = { 10 | "windows": [ 11 | { 12 | "name": "erb-example", 13 | "root": "<%= ENV['MY_PROJECT_ROOT'] %>", 14 | "panes": [{"cmd": "pwd"}], 15 | }, 16 | ], 17 | } 18 | 19 | expected = { 20 | "session_name": None, 21 | "windows": [ 22 | { 23 | "window_name": "erb-example", 24 | "start_directory": "<%= ENV['MY_PROJECT_ROOT'] %>", 25 | "panes": [{"shell_command": "pwd"}], 26 | }, 27 | ], 28 | } 29 | -------------------------------------------------------------------------------- /tests/fixtures/import_teamocil/test4.yaml: -------------------------------------------------------------------------------- 1 | windows: 2 | - name: "erb-example" 3 | root: <%= ENV['MY_PROJECT_ROOT'] %> 4 | panes: 5 | - cmd: "pwd" 6 | -------------------------------------------------------------------------------- /tests/fixtures/import_tmuxinator/__init__.py: -------------------------------------------------------------------------------- 1 | """Tmuxinator data fixtures for import_tmuxinator tests.""" 2 | 3 | from __future__ import annotations 4 | 5 | from . import test1, test2, test3 6 | -------------------------------------------------------------------------------- /tests/fixtures/import_tmuxinator/test1.py: -------------------------------------------------------------------------------- 1 | """Tmuxinator data fixtures for import_tmuxinator tests, 1st dataset.""" 2 | 3 | from __future__ import annotations 4 | 5 | from tests.fixtures import utils as test_utils 6 | 7 | tmuxinator_yaml = test_utils.read_workspace_file("import_tmuxinator/test1.yaml") 8 | tmuxinator_dict = { 9 | "windows": [ 10 | {"editor": {"layout": "main-vertical", "panes": ["vim", "guard"]}}, 11 | {"server": "bundle exec rails s"}, 12 | {"logs": "tail -f logs/development.log"}, 13 | ], 14 | } 15 | 16 | expected = { 17 | "session_name": None, 18 | "windows": [ 19 | {"window_name": "editor", "layout": "main-vertical", "panes": ["vim", "guard"]}, 20 | {"window_name": "server", "panes": ["bundle exec rails s"]}, 21 | {"window_name": "logs", "panes": ["tail -f logs/development.log"]}, 22 | ], 23 | } 24 | -------------------------------------------------------------------------------- /tests/fixtures/import_tmuxinator/test1.yaml: -------------------------------------------------------------------------------- 1 | windows: 2 | - editor: 3 | layout: main-vertical 4 | panes: 5 | - vim 6 | - guard 7 | - server: bundle exec rails s 8 | - logs: tail -f logs/development.log 9 | -------------------------------------------------------------------------------- /tests/fixtures/import_tmuxinator/test2.py: -------------------------------------------------------------------------------- 1 | """Tmuxinator data fixtures for import_tmuxinator tests, 2nd dataset.""" 2 | 3 | from __future__ import annotations 4 | 5 | from tests.fixtures import utils as test_utils 6 | 7 | tmuxinator_yaml = test_utils.read_workspace_file("import_tmuxinator/test2.yaml") 8 | 9 | tmuxinator_dict = { 10 | "project_name": "sample", 11 | "project_root": "~/test", 12 | "socket_name": "foo", 13 | "pre": "sudo /etc/rc.d/mysqld start", 14 | "rbenv": "2.0.0-p247", 15 | "cli_args": "-f ~/.tmux.mac.conf", 16 | "tabs": [ 17 | { 18 | "editor": { 19 | "pre": [ 20 | 'echo "I get run in each pane, before each pane command!"', 21 | None, 22 | ], 23 | "layout": "main-vertical", 24 | "panes": ["vim", None, "top"], 25 | }, 26 | }, 27 | {"shell": "git pull"}, 28 | { 29 | "guard": { 30 | "layout": "tiled", 31 | "pre": [ 32 | 'echo "I get run in each pane."', 33 | 'echo "Before each pane command!"', 34 | ], 35 | "panes": [None, None, None], 36 | }, 37 | }, 38 | {"database": "bundle exec rails db"}, 39 | {"server": "bundle exec rails s"}, 40 | {"logs": "tail -f log/development.log"}, 41 | {"console": "bundle exec rails c"}, 42 | {"capistrano": None}, 43 | {"server": "ssh user@example.com"}, 44 | ], 45 | } 46 | 47 | expected = { 48 | "session_name": "sample", 49 | "socket_name": "foo", 50 | "config": "~/.tmux.mac.conf", 51 | "start_directory": "~/test", 52 | "shell_command_before": ["sudo /etc/rc.d/mysqld start", "rbenv shell 2.0.0-p247"], 53 | "windows": [ 54 | { 55 | "window_name": "editor", 56 | "shell_command_before": [ 57 | 'echo "I get run in each pane, before each pane command!"', 58 | None, 59 | ], 60 | "layout": "main-vertical", 61 | "panes": ["vim", None, "top"], 62 | }, 63 | {"window_name": "shell", "panes": ["git pull"]}, 64 | { 65 | "window_name": "guard", 66 | "layout": "tiled", 67 | "shell_command_before": [ 68 | 'echo "I get run in each pane."', 69 | 'echo "Before each pane command!"', 70 | ], 71 | "panes": [None, None, None], 72 | }, 73 | {"window_name": "database", "panes": ["bundle exec rails db"]}, 74 | {"window_name": "server", "panes": ["bundle exec rails s"]}, 75 | {"window_name": "logs", "panes": ["tail -f log/development.log"]}, 76 | {"window_name": "console", "panes": ["bundle exec rails c"]}, 77 | {"window_name": "capistrano", "panes": [None]}, 78 | {"window_name": "server", "panes": ["ssh user@example.com"]}, 79 | ], 80 | } 81 | -------------------------------------------------------------------------------- /tests/fixtures/import_tmuxinator/test2.yaml: -------------------------------------------------------------------------------- 1 | project_name: sample 2 | project_root: ~/test 3 | socket_name: foo # Remove to use default socket 4 | pre: sudo /etc/rc.d/mysqld start # Runs before everything 5 | rbenv: 2.0.0-p247 6 | cli_args: -f ~/.tmux.mac.conf # Pass arguments to tmux 7 | tabs: 8 | - editor: 9 | pre: 10 | - echo "I get run in each pane, before each pane command!" 11 | - 12 | layout: main-vertical 13 | panes: 14 | - vim 15 | - #empty, will just run plain bash 16 | - top 17 | - shell: git pull 18 | - guard: 19 | layout: tiled 20 | pre: 21 | - echo "I get run in each pane." 22 | - echo "Before each pane command!" 23 | panes: 24 | - 25 | - #empty, will just run plain bash 26 | - 27 | - database: bundle exec rails db 28 | - server: bundle exec rails s 29 | - logs: tail -f log/development.log 30 | - console: bundle exec rails c 31 | - capistrano: 32 | - server: ssh user@example.com 33 | -------------------------------------------------------------------------------- /tests/fixtures/import_tmuxinator/test3.py: -------------------------------------------------------------------------------- 1 | """Tmuxinator data fixtures for import_tmuxinator tests, 3rd dataset.""" 2 | 3 | from __future__ import annotations 4 | 5 | from tests.fixtures import utils as test_utils 6 | 7 | tmuxinator_yaml = test_utils.read_workspace_file("import_tmuxinator/test3.yaml") 8 | 9 | tmuxinator_dict = { 10 | "name": "sample", 11 | "root": "~/test", 12 | "socket_name": "foo", 13 | "tmux_options": "-f ~/.tmux.mac.conf", 14 | "pre": "sudo /etc/rc.d/mysqld start", 15 | "pre_window": "rbenv shell 2.0.0-p247", 16 | "windows": [ 17 | { 18 | "editor": { 19 | "pre": [ 20 | 'echo "I get run in each pane, before each pane command!"', 21 | None, 22 | ], 23 | "layout": "main-vertical", 24 | "root": "~/test/editor", 25 | "panes": ["vim", None, "top"], 26 | }, 27 | }, 28 | {"shell": ["git pull", "git merge"]}, 29 | { 30 | "guard": { 31 | "layout": "tiled", 32 | "pre": [ 33 | 'echo "I get run in each pane."', 34 | 'echo "Before each pane command!"', 35 | ], 36 | "panes": [None, None, None], 37 | }, 38 | }, 39 | {"database": "bundle exec rails db"}, 40 | {"server": "bundle exec rails s"}, 41 | {"logs": "tail -f log/development.log"}, 42 | {"console": "bundle exec rails c"}, 43 | {"capistrano": None}, 44 | {"server": "ssh user@example.com"}, 45 | ], 46 | } 47 | 48 | expected = { 49 | "session_name": "sample", 50 | "socket_name": "foo", 51 | "start_directory": "~/test", 52 | "config": "~/.tmux.mac.conf", 53 | "shell_command": "sudo /etc/rc.d/mysqld start", 54 | "shell_command_before": ["rbenv shell 2.0.0-p247"], 55 | "windows": [ 56 | { 57 | "window_name": "editor", 58 | "shell_command_before": [ 59 | 'echo "I get run in each pane, before each pane command!"', 60 | None, 61 | ], 62 | "layout": "main-vertical", 63 | "start_directory": "~/test/editor", 64 | "panes": ["vim", None, "top"], 65 | }, 66 | {"window_name": "shell", "panes": ["git pull", "git merge"]}, 67 | { 68 | "window_name": "guard", 69 | "layout": "tiled", 70 | "shell_command_before": [ 71 | 'echo "I get run in each pane."', 72 | 'echo "Before each pane command!"', 73 | ], 74 | "panes": [None, None, None], 75 | }, 76 | {"window_name": "database", "panes": ["bundle exec rails db"]}, 77 | {"window_name": "server", "panes": ["bundle exec rails s"]}, 78 | {"window_name": "logs", "panes": ["tail -f log/development.log"]}, 79 | {"window_name": "console", "panes": ["bundle exec rails c"]}, 80 | {"window_name": "capistrano", "panes": [None]}, 81 | {"window_name": "server", "panes": ["ssh user@example.com"]}, 82 | ], 83 | } 84 | -------------------------------------------------------------------------------- /tests/fixtures/import_tmuxinator/test3.yaml: -------------------------------------------------------------------------------- 1 | # ~/.tmuxinator/sample.yml 2 | # you can make as many tabs as you wish... 3 | 4 | name: sample 5 | root: ~/test 6 | socket_name: foo # Remove to use default socket 7 | pre: sudo /etc/rc.d/mysqld start # Runs before everything 8 | pre_window: rbenv shell 2.0.0-p247 # Runs in each tab and pane 9 | tmux_options: -f ~/.tmux.mac.conf # Pass arguments to tmux 10 | windows: 11 | - editor: 12 | pre: 13 | - echo "I get run in each pane, before each pane command!" 14 | - 15 | layout: main-vertical 16 | root: ~/test/editor 17 | panes: 18 | - vim 19 | - #empty, will just run plain bash 20 | - top 21 | - shell: 22 | - git pull 23 | - git merge 24 | - guard: 25 | layout: tiled 26 | pre: 27 | - echo "I get run in each pane." 28 | - echo "Before each pane command!" 29 | panes: 30 | - 31 | - #empty, will just run plain bash 32 | - 33 | - database: bundle exec rails db 34 | - server: bundle exec rails s 35 | - logs: tail -f log/development.log 36 | - console: bundle exec rails c 37 | - capistrano: 38 | - server: ssh user@example.com 39 | -------------------------------------------------------------------------------- /tests/fixtures/pluginsystem/__init__.py: -------------------------------------------------------------------------------- 1 | """Test data for tmuxp plugin system.""" 2 | -------------------------------------------------------------------------------- /tests/fixtures/pluginsystem/partials/__init__.py: -------------------------------------------------------------------------------- 1 | """Tmuxp tests for plugins.""" 2 | -------------------------------------------------------------------------------- /tests/fixtures/pluginsystem/partials/_types.py: -------------------------------------------------------------------------------- 1 | """Internal, :const:`typing.TYPE_CHECKING` scoped :term:`type annotations `. 2 | 3 | These are _not_ to be imported at runtime as `typing_extensions` is not 4 | bundled with tmuxp. Usage example: 5 | 6 | >>> import typing as t 7 | 8 | >>> if t.TYPE_CHECKING: 9 | ... from tmuxp.fixtures.pluginsystem.partials._types import PluginConfigSchema 10 | ... 11 | """ 12 | 13 | from __future__ import annotations 14 | 15 | from typing_extensions import NotRequired, TypedDict 16 | 17 | 18 | class PluginTestConfigSchema(TypedDict): 19 | """Same as PluginConfigSchema, but with tmux, libtmux, and tmuxp version.""" 20 | 21 | tmux_version: NotRequired[str] 22 | libtmux_version: NotRequired[str] 23 | tmuxp_version: NotRequired[str] 24 | 25 | # Normal keys 26 | plugin_name: NotRequired[str] 27 | tmux_min_version: NotRequired[str] 28 | tmux_max_version: NotRequired[str] 29 | tmux_version_incompatible: NotRequired[list[str]] 30 | libtmux_min_version: NotRequired[str] 31 | libtmux_max_version: NotRequired[str] 32 | libtmux_version_incompatible: NotRequired[list[str]] 33 | tmuxp_min_version: NotRequired[str] 34 | tmuxp_max_version: NotRequired[str] 35 | tmuxp_version_incompatible: NotRequired[list[str]] 36 | -------------------------------------------------------------------------------- /tests/fixtures/pluginsystem/partials/all_pass.py: -------------------------------------------------------------------------------- 1 | """Tmuxp test plugin with version constraints guaranteed to pass.""" 2 | 3 | from __future__ import annotations 4 | 5 | import typing as t 6 | 7 | from .test_plugin_helpers import MyTestTmuxpPlugin 8 | 9 | if t.TYPE_CHECKING: 10 | from ._types import PluginTestConfigSchema 11 | 12 | 13 | class AllVersionPassPlugin(MyTestTmuxpPlugin): 14 | """Tmuxp plugin with config constraints guaranteed to validate.""" 15 | 16 | def __init__(self) -> None: 17 | config: PluginTestConfigSchema = { 18 | "plugin_name": "tmuxp-plugin-my-tmuxp-plugin", 19 | "tmux_min_version": "1.8", 20 | "tmux_max_version": "100.0", 21 | "tmux_version_incompatible": ["2.3"], 22 | "libtmux_min_version": "0.8.3", 23 | "libtmux_max_version": "100.0", 24 | "libtmux_version_incompatible": ["0.7.1"], 25 | "tmuxp_min_version": "1.7.0", 26 | "tmuxp_max_version": "100.0.0", 27 | "tmuxp_version_incompatible": ["1.5.6"], 28 | "tmux_version": "3.0", 29 | "tmuxp_version": "1.7.0", 30 | } 31 | MyTestTmuxpPlugin.__init__(self, **config) 32 | -------------------------------------------------------------------------------- /tests/fixtures/pluginsystem/partials/libtmux_version_fail.py: -------------------------------------------------------------------------------- 1 | """Fixtures for tmuxp plugins for libtmux version exceptions.""" 2 | 3 | from __future__ import annotations 4 | 5 | import typing as t 6 | 7 | from .test_plugin_helpers import MyTestTmuxpPlugin 8 | 9 | if t.TYPE_CHECKING: 10 | from ._types import PluginTestConfigSchema 11 | 12 | 13 | class LibtmuxVersionFailMinPlugin(MyTestTmuxpPlugin): 14 | """Tmuxp plugin that fails when libtmux below minimum version constraint.""" 15 | 16 | def __init__(self) -> None: 17 | config: PluginTestConfigSchema = { 18 | "plugin_name": "libtmux-min-version-fail", 19 | "libtmux_min_version": "0.8.3", 20 | "libtmux_version": "0.7.0", 21 | } 22 | MyTestTmuxpPlugin.__init__(self, **config) 23 | 24 | 25 | class LibtmuxVersionFailMaxPlugin(MyTestTmuxpPlugin): 26 | """Tmuxp plugin that fails when libtmux above maximum version constraint.""" 27 | 28 | def __init__(self) -> None: 29 | config: PluginTestConfigSchema = { 30 | "plugin_name": "libtmux-max-version-fail", 31 | "libtmux_max_version": "3.0", 32 | "libtmux_version": "3.5", 33 | } 34 | MyTestTmuxpPlugin.__init__(self, **config) 35 | 36 | 37 | class LibtmuxVersionFailIncompatiblePlugin(MyTestTmuxpPlugin): 38 | """Tmuxp plugin that fails when libtmux version constraint is invalid.""" 39 | 40 | def __init__(self) -> None: 41 | config: PluginTestConfigSchema = { 42 | "plugin_name": "libtmux-incompatible-version-fail", 43 | "libtmux_version_incompatible": ["0.7.1"], 44 | "libtmux_version": "0.7.1", 45 | } 46 | MyTestTmuxpPlugin.__init__(self, **config) 47 | -------------------------------------------------------------------------------- /tests/fixtures/pluginsystem/partials/test_plugin_helpers.py: -------------------------------------------------------------------------------- 1 | """Tmuxp test plugin for asserting version constraints.""" 2 | 3 | from __future__ import annotations 4 | 5 | import typing as t 6 | 7 | from tmuxp.plugin import TmuxpPlugin 8 | 9 | if t.TYPE_CHECKING: 10 | from typing_extensions import Unpack 11 | 12 | from tmuxp._internal.types import PluginConfigSchema 13 | 14 | from ._types import PluginTestConfigSchema 15 | 16 | 17 | class MyTestTmuxpPlugin(TmuxpPlugin): 18 | """Base class for testing tmuxp plugins with version constraints.""" 19 | 20 | def __init__(self, **config: Unpack[PluginTestConfigSchema]) -> None: 21 | assert isinstance(config, dict) 22 | tmux_version = config.pop("tmux_version", None) 23 | libtmux_version = config.pop("libtmux_version", None) 24 | tmuxp_version = config.pop("tmuxp_version", None) 25 | 26 | t.cast("PluginConfigSchema", config) 27 | 28 | assert "tmux_version" not in config 29 | 30 | # tests/fixtures/pluginsystem/partials/test_plugin_helpers.py:24: error: Extra 31 | # argument "tmux_version" from **args for "__init__" of "TmuxpPlugin" [misc] 32 | super().__init__(**config) # type:ignore 33 | 34 | # WARNING! This should not be done in anything but a test 35 | if tmux_version: 36 | self.version_constraints["tmux"]["version"] = tmux_version 37 | if libtmux_version: 38 | self.version_constraints["libtmux"]["version"] = libtmux_version 39 | if tmuxp_version: 40 | self.version_constraints["tmuxp"]["version"] = tmuxp_version 41 | 42 | self._version_check() 43 | -------------------------------------------------------------------------------- /tests/fixtures/pluginsystem/partials/tmux_version_fail.py: -------------------------------------------------------------------------------- 1 | """Fixtures for tmuxp plugins for tmux version exceptions.""" 2 | 3 | from __future__ import annotations 4 | 5 | import typing as t 6 | 7 | from .test_plugin_helpers import MyTestTmuxpPlugin 8 | 9 | if t.TYPE_CHECKING: 10 | from ._types import PluginTestConfigSchema 11 | 12 | 13 | class TmuxVersionFailMinPlugin(MyTestTmuxpPlugin): 14 | """Tmuxp plugin that fails when tmux below minimum version constraint.""" 15 | 16 | def __init__(self) -> None: 17 | config: PluginTestConfigSchema = { 18 | "plugin_name": "tmux-min-version-fail", 19 | "tmux_min_version": "1.8", 20 | "tmux_version": "1.7", 21 | } 22 | MyTestTmuxpPlugin.__init__(self, **config) 23 | 24 | 25 | class TmuxVersionFailMaxPlugin(MyTestTmuxpPlugin): 26 | """Tmuxp plugin that fails when tmux above maximum version constraint.""" 27 | 28 | def __init__(self) -> None: 29 | config: PluginTestConfigSchema = { 30 | "plugin_name": "tmux-max-version-fail", 31 | "tmux_max_version": "3.0", 32 | "tmux_version": "3.5", 33 | } 34 | MyTestTmuxpPlugin.__init__(self, **config) 35 | 36 | 37 | class TmuxVersionFailIncompatiblePlugin(MyTestTmuxpPlugin): 38 | """Tmuxp plugin that fails when tmux version constraint is invalid.""" 39 | 40 | def __init__(self) -> None: 41 | config: PluginTestConfigSchema = { 42 | "plugin_name": "tmux-incompatible-version-fail", 43 | "tmux_version_incompatible": ["2.3"], 44 | "tmux_version": "2.3", 45 | } 46 | 47 | MyTestTmuxpPlugin.__init__(self, **config) 48 | -------------------------------------------------------------------------------- /tests/fixtures/pluginsystem/partials/tmuxp_version_fail.py: -------------------------------------------------------------------------------- 1 | """Fixtures for tmuxp plugins for tmuxp version exceptions.""" 2 | 3 | from __future__ import annotations 4 | 5 | import typing as t 6 | 7 | from .test_plugin_helpers import MyTestTmuxpPlugin 8 | 9 | if t.TYPE_CHECKING: 10 | from ._types import PluginTestConfigSchema 11 | 12 | 13 | class TmuxpVersionFailMinPlugin(MyTestTmuxpPlugin): 14 | """Tmuxp plugin that fails when tmuxp below minimum version constraint.""" 15 | 16 | def __init__(self) -> None: 17 | config: PluginTestConfigSchema = { 18 | "plugin_name": "tmuxp-min-version-fail", 19 | "tmuxp_min_version": "1.7.0", 20 | "tmuxp_version": "1.6.3", 21 | } 22 | MyTestTmuxpPlugin.__init__(self, **config) 23 | 24 | 25 | class TmuxpVersionFailMaxPlugin(MyTestTmuxpPlugin): 26 | """Tmuxp plugin that fails when tmuxp above maximum version constraint.""" 27 | 28 | def __init__(self) -> None: 29 | config: PluginTestConfigSchema = { 30 | "plugin_name": "tmuxp-max-version-fail", 31 | "tmuxp_max_version": "2.0.0", 32 | "tmuxp_version": "2.5", 33 | } 34 | MyTestTmuxpPlugin.__init__(self, **config) 35 | 36 | 37 | class TmuxpVersionFailIncompatiblePlugin(MyTestTmuxpPlugin): 38 | """Tmuxp plugin that fails when tmuxp version constraint is invalid.""" 39 | 40 | def __init__(self) -> None: 41 | config: PluginTestConfigSchema = { 42 | "plugin_name": "tmuxp-incompatible-version-fail", 43 | "tmuxp_version_incompatible": ["1.5.0"], 44 | "tmuxp_version": "1.5.0", 45 | } 46 | MyTestTmuxpPlugin.__init__(self, **config) 47 | -------------------------------------------------------------------------------- /tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_awf/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "tmuxp_test_plugin_awf" 3 | version = "0.0.2" 4 | description = "A tmuxp plugin to test after_window_finished part of the tmuxp plugin system" 5 | authors = [ 6 | {name = "Joseph Flinn", email = "joseph.s.flinn@gmail.com"} 7 | ] 8 | requires-python = ">=3.8,<4.0" 9 | dependencies = [ 10 | "tmuxp>=1.7.0" 11 | ] 12 | 13 | [build-system] 14 | requires = ["hatchling"] 15 | build-backend = "hatchling.build" 16 | -------------------------------------------------------------------------------- /tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_awf/tmuxp_test_plugin_awf/__init__.py: -------------------------------------------------------------------------------- 1 | """Example tmuxp plugin that runs after window creation completions.""" 2 | -------------------------------------------------------------------------------- /tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_awf/tmuxp_test_plugin_awf/plugin.py: -------------------------------------------------------------------------------- 1 | """Tmuxp example plugin for after_window_finished.""" 2 | 3 | from __future__ import annotations 4 | 5 | import typing as t 6 | 7 | from tmuxp.plugin import TmuxpPlugin 8 | 9 | if t.TYPE_CHECKING: 10 | from libtmux.window import Window 11 | 12 | 13 | class PluginAfterWindowFinished(TmuxpPlugin): 14 | """Tmuxp plugin that runs after window creation completes.""" 15 | 16 | def __init__(self) -> None: 17 | self.message: str = "[+] This is the Tmuxp Test Plugin" 18 | 19 | def after_window_finished(self, window: Window) -> None: 20 | """Run hook after window creation completed.""" 21 | if window.name == "editor": 22 | window.rename_window("plugin_test_awf") 23 | elif window.name == "awf_mw_test": 24 | window.rename_window("plugin_test_awf_mw") 25 | elif window.name == "awf_mw_test_2": 26 | window.rename_window("plugin_test_awf_mw_2") 27 | elif window.name == "mp_test_owc": 28 | window.rename_window("mp_test_awf") 29 | -------------------------------------------------------------------------------- /tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_bs/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "tmuxp_test_plugin_bs" 3 | version = "0.0.2" 4 | description = "A tmuxp plugin to test before_script part of the tmuxp plugin system" 5 | authors = [ 6 | {name = "Joseph Flinn", email = "joseph.s.flinn@gmail.com"} 7 | ] 8 | requires-python = ">=3.8,<4.0" 9 | dependencies = [ 10 | "tmuxp>=1.7.0" 11 | ] 12 | 13 | [build-system] 14 | requires = ["hatchling"] 15 | build-backend = "hatchling.build" 16 | -------------------------------------------------------------------------------- /tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_bs/tmuxp_test_plugin_bs/__init__.py: -------------------------------------------------------------------------------- 1 | """Example tmuxp plugin module that hooks in before_script, if declared.""" 2 | -------------------------------------------------------------------------------- /tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_bs/tmuxp_test_plugin_bs/plugin.py: -------------------------------------------------------------------------------- 1 | """Tmux plugin that runs before_script, if it is declared in configuration.""" 2 | 3 | from __future__ import annotations 4 | 5 | import typing as t 6 | 7 | from tmuxp.plugin import TmuxpPlugin 8 | 9 | if t.TYPE_CHECKING: 10 | from libtmux.session import Session 11 | 12 | 13 | class PluginBeforeScript(TmuxpPlugin): 14 | """Tmuxp plugin that runs before_script.""" 15 | 16 | def __init__(self) -> None: 17 | self.message: str = "[+] This is the Tmuxp Test Plugin" 18 | 19 | def before_script(self, session: Session) -> None: 20 | """Run hook during before_script, if it is declared.""" 21 | session.rename_session("plugin_test_bs") 22 | -------------------------------------------------------------------------------- /tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_bwb/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "tmuxp_test_plugin_bwb" 3 | version = "0.0.2" 4 | description = "A tmuxp plugin to test before_workspace_build part of the tmuxp plugin system" 5 | authors = [ 6 | {name = "Joseph Flinn", email = "joseph.s.flinn@gmail.com"} 7 | ] 8 | requires-python = ">=3.8,<4.0" 9 | dependencies = [ 10 | "tmuxp>=1.7.0" 11 | ] 12 | 13 | [build-system] 14 | requires = ["hatchling"] 15 | build-backend = "hatchling.build" 16 | -------------------------------------------------------------------------------- /tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_bwb/tmuxp_test_plugin_bwb/__init__.py: -------------------------------------------------------------------------------- 1 | """Example tmuxp plugin that runs before workspace builder inits.""" 2 | -------------------------------------------------------------------------------- /tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_bwb/tmuxp_test_plugin_bwb/plugin.py: -------------------------------------------------------------------------------- 1 | """Tmuxp example plugin for before_worksplace_builder.""" 2 | 3 | from __future__ import annotations 4 | 5 | import typing as t 6 | 7 | from tmuxp.plugin import TmuxpPlugin 8 | 9 | if t.TYPE_CHECKING: 10 | from libtmux.session import Session 11 | 12 | 13 | class PluginBeforeWorkspaceBuilder(TmuxpPlugin): 14 | """Tmuxp plugin that runs before worksplace builder starts.""" 15 | 16 | def __init__(self) -> None: 17 | self.message: str = "[+] This is the Tmuxp Test Plugin" 18 | 19 | def before_workspace_builder(self, session: Session) -> None: 20 | """Run hook before workspace builder begins.""" 21 | session.rename_session("plugin_test_bwb") 22 | -------------------------------------------------------------------------------- /tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_fail/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "tmuxp_test_plugin_fail" 3 | version = "0.1.0" 4 | description = "A test plugin designed to fail to test the cli" 5 | authors = [ 6 | {name = "Joseph Flinn", email = "joseph.s.flinn@gmail.com"} 7 | ] 8 | requires-python = ">=3.8,<4.0" 9 | dependencies = [ 10 | "tmuxp>=1.7.0" 11 | ] 12 | 13 | [build-system] 14 | requires = ["hatchling"] 15 | build-backend = "hatchling.build" 16 | -------------------------------------------------------------------------------- /tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_fail/tmuxp_test_plugin_fail/__init__.py: -------------------------------------------------------------------------------- 1 | """Tmuxp plugin test, that is destined for failure.""" 2 | -------------------------------------------------------------------------------- /tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_fail/tmuxp_test_plugin_fail/plugin.py: -------------------------------------------------------------------------------- 1 | """Tmuxp example plugin that fails on initialization.""" 2 | 3 | from __future__ import annotations 4 | 5 | import typing as t 6 | 7 | from tmuxp.plugin import TmuxpPlugin 8 | 9 | if t.TYPE_CHECKING: 10 | from tmuxp._internal.types import PluginConfigSchema 11 | 12 | 13 | class PluginFailVersion(TmuxpPlugin): 14 | """A tmuxp plugin that is doomed to fail. DOOMED.""" 15 | 16 | def __init__(self) -> None: 17 | config: PluginConfigSchema = { 18 | "plugin_name": "tmuxp-plugin-fail-version", 19 | "tmuxp_max_version": "0.0.0", 20 | } 21 | TmuxpPlugin.__init__(self, **config) 22 | -------------------------------------------------------------------------------- /tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_owc/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "tmuxp_test_plugin_owc" 3 | version = "0.0.2" 4 | description = "A tmuxp plugin to test on_window_create part of the tmuxp plugin system" 5 | authors = [ 6 | {name = "Joseph Flinn", email = "joseph.s.flinn@gmail.com"} 7 | ] 8 | requires-python = ">=3.8,<4.0" 9 | dependencies = [ 10 | "tmuxp>=1.7.0" 11 | ] 12 | 13 | [build-system] 14 | requires = ["hatchling"] 15 | build-backend = "hatchling.build" 16 | -------------------------------------------------------------------------------- /tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_owc/tmuxp_test_plugin_owc/__init__.py: -------------------------------------------------------------------------------- 1 | """Example tmuxp plugin module for hooks on window creation.""" 2 | -------------------------------------------------------------------------------- /tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_owc/tmuxp_test_plugin_owc/plugin.py: -------------------------------------------------------------------------------- 1 | """Tmuxp example plugin for on_window_create.""" 2 | 3 | from __future__ import annotations 4 | 5 | import typing as t 6 | 7 | from tmuxp.plugin import TmuxpPlugin 8 | 9 | if t.TYPE_CHECKING: 10 | from libtmux.window import Window 11 | 12 | 13 | class PluginOnWindowCreate(TmuxpPlugin): 14 | """Tmuxp plugin to test custom functionality on window creation.""" 15 | 16 | def __init__(self) -> None: 17 | self.message: str = "[+] This is the Tmuxp Test Plugin" 18 | 19 | def on_window_create(self, window: Window) -> None: 20 | """Apply hook that runs for tmux on session reattach.""" 21 | if window.name == "editor": 22 | window.rename_window("plugin_test_owc") 23 | elif window.name == "owc_mw_test": 24 | window.rename_window("plugin_test_owc_mw") 25 | elif window.name == "owc_mw_test_2": 26 | window.rename_window("plugin_test_owc_mw_2") 27 | elif window.name == "mp_test": 28 | window.rename_window("mp_test_owc") 29 | -------------------------------------------------------------------------------- /tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_r/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "tmuxp_test_plugin_r" 3 | version = "0.0.2" 4 | description = "A tmuxp plugin to test reattach part of the tmuxp plugin system" 5 | authors = [ 6 | {name = "Joseph Flinn", email = "joseph.s.flinn@gmail.com"} 7 | ] 8 | requires-python = ">=3.8,<4.0" 9 | dependencies = [ 10 | "tmuxp>=1.7.0" 11 | ] 12 | 13 | [build-system] 14 | requires = ["hatchling"] 15 | build-backend = "hatchling.build" 16 | -------------------------------------------------------------------------------- /tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_r/tmuxp_test_plugin_r/__init__.py: -------------------------------------------------------------------------------- 1 | """Example tmuxp plugin module for reattaching sessions.""" 2 | -------------------------------------------------------------------------------- /tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_r/tmuxp_test_plugin_r/plugin.py: -------------------------------------------------------------------------------- 1 | """Tmuxp example plugin for reattaching session.""" 2 | 3 | from __future__ import annotations 4 | 5 | import typing as t 6 | 7 | from tmuxp.plugin import TmuxpPlugin 8 | 9 | if t.TYPE_CHECKING: 10 | from libtmux.session import Session 11 | 12 | 13 | class PluginReattach(TmuxpPlugin): 14 | """Tmuxp plugin to test renaming session on reattach.""" 15 | 16 | def __init__(self) -> None: 17 | self.message: str = "[+] This is the Tmuxp Test Plugin" 18 | 19 | def reattach(self, session: Session) -> None: 20 | """Apply hook that runs for tmux on session reattach.""" 21 | session.rename_session("plugin_test_r") 22 | -------------------------------------------------------------------------------- /tests/fixtures/regressions/issue_800_default_size_many_windows.yaml: -------------------------------------------------------------------------------- 1 | session_name: many-windows-issue 2 | windows: 3 | - window_name: moo 4 | layout: main-horizontal 5 | panes: 6 | - echo hello 7 | - echo hello 8 | - echo hello 9 | - echo hello 10 | - echo hello 11 | - echo hello 12 | - echo hello 13 | -------------------------------------------------------------------------------- /tests/fixtures/script_complete.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo hello 4 | echo $? # Exit status 0 returned because command executed successfully. 5 | -------------------------------------------------------------------------------- /tests/fixtures/script_failed.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | 4 | echoerr() { echo "$@" 1>&2; } 5 | echoerr An error has occurred 6 | 7 | exit 113 # Will return 113 to shell. 8 | # To verify this, type "echo $?" after script terminates. 9 | -------------------------------------------------------------------------------- /tests/fixtures/structures.py: -------------------------------------------------------------------------------- 1 | """Typings / structures for tmuxp fixtures.""" 2 | 3 | from __future__ import annotations 4 | 5 | import dataclasses 6 | import typing as t 7 | 8 | 9 | @dataclasses.dataclass 10 | class WorkspaceTestData: 11 | """Workspace data fixtures for tmuxp tests.""" 12 | 13 | expand1: t.Any 14 | expand2: t.Any 15 | expand_blank: t.Any 16 | sample_workspace: t.Any 17 | shell_command_before: t.Any 18 | shell_command_before_session: t.Any 19 | trickle: t.Any 20 | -------------------------------------------------------------------------------- /tests/fixtures/tmux/tmux.conf: -------------------------------------------------------------------------------- 1 | display-message "Hello World" 2 | -------------------------------------------------------------------------------- /tests/fixtures/utils.py: -------------------------------------------------------------------------------- 1 | """Utility functions for tmuxp fixtures.""" 2 | 3 | from __future__ import annotations 4 | 5 | import pathlib 6 | 7 | from tests.constants import FIXTURE_PATH 8 | 9 | 10 | def get_workspace_file( 11 | file: str | pathlib.Path, 12 | ) -> pathlib.Path: 13 | """Return fixture data, relative to __file__.""" 14 | if isinstance(file, str): 15 | file = pathlib.Path(file) 16 | 17 | return FIXTURE_PATH / file 18 | 19 | 20 | def read_workspace_file( 21 | file: pathlib.Path | str, 22 | ) -> str: 23 | """Return fixture data, relative to __file__.""" 24 | if isinstance(file, str): 25 | file = pathlib.Path(file) 26 | 27 | return get_workspace_file(file).open().read() 28 | 29 | 30 | def write_config( 31 | config_path: pathlib.Path, 32 | filename: str, 33 | content: str, 34 | ) -> pathlib.Path: 35 | """Write configuration content to file.""" 36 | config = config_path / filename 37 | config.write_text(content, encoding="utf-8") 38 | return config 39 | -------------------------------------------------------------------------------- /tests/fixtures/workspace/__init__.py: -------------------------------------------------------------------------------- 1 | """Workspace data fixtures for tmuxp tests.""" 2 | 3 | from __future__ import annotations 4 | 5 | from . import ( 6 | expand1, 7 | expand2, 8 | expand_blank, 9 | sample_workspace, 10 | shell_command_before, 11 | shell_command_before_session, 12 | trickle, 13 | ) 14 | -------------------------------------------------------------------------------- /tests/fixtures/workspace/builder/config_script_completes.yaml: -------------------------------------------------------------------------------- 1 | session_name: sample workspace 2 | before_script: {script_complete} 3 | windows: 4 | - panes: 5 | - pane 6 | -------------------------------------------------------------------------------- /tests/fixtures/workspace/builder/config_script_fails.yaml: -------------------------------------------------------------------------------- 1 | session_name: sample workspace 2 | before_script: {script_failed} 3 | windows: 4 | - panes: 5 | - pane 6 | -------------------------------------------------------------------------------- /tests/fixtures/workspace/builder/config_script_not_exists.yaml: -------------------------------------------------------------------------------- 1 | session_name: sample workspace 2 | before_script: {script_not_exists} 3 | windows: 4 | - panes: 5 | - pane 6 | -------------------------------------------------------------------------------- /tests/fixtures/workspace/builder/env_var_options.yaml: -------------------------------------------------------------------------------- 1 | session_name: test env vars for options 2 | start_directory: '~' 3 | global_options: 4 | visual-silence: ${VISUAL_SILENCE} 5 | options: 6 | repeat-time: 738 7 | windows: 8 | - window_name: moo 9 | layout: main-horizontal 10 | options: 11 | main-pane-height: ${MAIN_PANE_HEIGHT} 12 | panes: 13 | - pane 14 | -------------------------------------------------------------------------------- /tests/fixtures/workspace/builder/environment_vars.yaml: -------------------------------------------------------------------------------- 1 | session_name: test env vars 2 | start_directory: "~" 3 | environment: 4 | FOO: SESSION 5 | PATH: /tmp 6 | windows: 7 | - window_name: no_overrides 8 | panes: 9 | - pane 10 | - window_name: window_overrides 11 | environment: 12 | FOO: WINDOW 13 | panes: 14 | - pane 15 | - window_name: pane_overrides 16 | panes: 17 | - environment: 18 | FOO: PANE 19 | - window_name: both_overrides 20 | environment: 21 | FOO: WINDOW 22 | panes: 23 | - pane 24 | - environment: 25 | FOO: PANE 26 | # This test case it just needed for warnings issued in old versions of tmux. 27 | - window_name: both_overrides_on_first_pane 28 | environment: 29 | FOO: WINDOW 30 | panes: 31 | - environment: 32 | FOO: PANE 33 | -------------------------------------------------------------------------------- /tests/fixtures/workspace/builder/first_pane_start_directory.yaml: -------------------------------------------------------------------------------- 1 | session_name: sample workspace 2 | windows: 3 | - panes: 4 | - start_directory: /usr 5 | - start_directory: /etc 6 | -------------------------------------------------------------------------------- /tests/fixtures/workspace/builder/focus_and_pane.yaml: -------------------------------------------------------------------------------- 1 | session_name: sample workspace 2 | start_directory: '~' 3 | windows: 4 | - window_name: focused window 5 | focus: true 6 | panes: 7 | - shell_command: 8 | - cmd: cd ~ 9 | - shell_command: 10 | - cmd: cd /usr 11 | focus: true 12 | - shell_command: 13 | - cmd: cd ~ 14 | - cmd: echo "moo" 15 | - cmd: top 16 | - window_name: window 2 17 | panes: 18 | - shell_command: 19 | - cmd: top 20 | focus: true 21 | - shell_command: 22 | - cmd: echo "hey" 23 | - shell_command: 24 | - cmd: echo "moo" 25 | - window_name: window 3 26 | panes: 27 | - shell_command: 28 | - cmd: cd / 29 | focus: true 30 | - pane 31 | - pane 32 | -------------------------------------------------------------------------------- /tests/fixtures/workspace/builder/global_options.yaml: -------------------------------------------------------------------------------- 1 | session_name: test global options 2 | start_directory: '~' 3 | global_options: 4 | repeat-time: 493 5 | status-position: 'top' 6 | windows: 7 | - window_name: moo 8 | panes: 9 | - pane 10 | - pane 11 | - pane 12 | -------------------------------------------------------------------------------- /tests/fixtures/workspace/builder/pane_ordering.yaml: -------------------------------------------------------------------------------- 1 | session_name: sample workspace 2 | start_directory: {HOME} 3 | windows: 4 | - options: 5 | automatic-rename: on 6 | layout: tiled 7 | panes: 8 | - cd /usr/bin 9 | - cd /usr 10 | - cd /etc 11 | - cd {HOME} 12 | -------------------------------------------------------------------------------- /tests/fixtures/workspace/builder/plugin_awf.yaml: -------------------------------------------------------------------------------- 1 | session_name: plugin-test-awf 2 | plugins: 3 | - 'tmuxp_test_plugin_awf.plugin.PluginAfterWindowFinished' 4 | windows: 5 | - window_name: editor 6 | layout: tiled 7 | shell_command_before: 8 | - cd ~/ 9 | panes: 10 | - shell_command: 11 | - cd /var/log 12 | - ls -al | grep \.log 13 | - echo hello 14 | - echo hello 15 | - echo hello 16 | -------------------------------------------------------------------------------- /tests/fixtures/workspace/builder/plugin_awf_multiple_windows.yaml: -------------------------------------------------------------------------------- 1 | session_name: plugin-test-awf-mw 2 | plugins: 3 | - 'tmuxp_test_plugin_awf.plugin.PluginAfterWindowFinished' 4 | windows: 5 | - window_name: awf_mw_test 6 | layout: tiled 7 | shell_command_before: 8 | - cd ~/ 9 | panes: 10 | - shell_command: 11 | - cd /var/log 12 | - ls -al | grep \.log 13 | - window_name: awf_mw_test_2 14 | layout: tiled 15 | shell_command_before: 16 | - cd ~/ 17 | panes: 18 | - echo hello -------------------------------------------------------------------------------- /tests/fixtures/workspace/builder/plugin_bs.yaml: -------------------------------------------------------------------------------- 1 | session_name: plugin-test-bs 2 | plugins: 3 | - 'tmuxp_test_plugin_bs.plugin.PluginBeforeScript' 4 | windows: 5 | - window_name: editor 6 | layout: tiled 7 | shell_command_before: 8 | - cmd: cd ~/ 9 | panes: 10 | - shell_command: 11 | - cmd: cd /var/log 12 | - cmd: ls -al | grep \.log 13 | - cmd: echo hello 14 | - cmd: echo hello 15 | - cmd: echo hello 16 | -------------------------------------------------------------------------------- /tests/fixtures/workspace/builder/plugin_bwb.yaml: -------------------------------------------------------------------------------- 1 | session_name: plugin-test-bwb 2 | plugins: 3 | - 'tmuxp_test_plugin_bwb.plugin.PluginBeforeWorkspaceBuilder' 4 | windows: 5 | - window_name: editor 6 | layout: tiled 7 | shell_command_before: 8 | - cd ~/ 9 | panes: 10 | - shell_command: 11 | - cd /var/log 12 | - ls -al | grep \.log 13 | - echo hello 14 | - echo hello 15 | - echo hello 16 | -------------------------------------------------------------------------------- /tests/fixtures/workspace/builder/plugin_missing_fail.yaml: -------------------------------------------------------------------------------- 1 | session_name: plugin-test-missing-fail 2 | plugins: 3 | - 'tmuxp_test_plugin_fail.plugin.PluginFailMissing' 4 | windows: 5 | - panes: 6 | - echo "hey" -------------------------------------------------------------------------------- /tests/fixtures/workspace/builder/plugin_multiple_plugins.yaml: -------------------------------------------------------------------------------- 1 | session_name: plugin-test-multiple-plugins 2 | plugins: 3 | - 'tmuxp_test_plugin_bwb.plugin.PluginBeforeWorkspaceBuilder' 4 | - 'tmuxp_test_plugin_owc.plugin.PluginOnWindowCreate' 5 | - 'tmuxp_test_plugin_awf.plugin.PluginAfterWindowFinished' 6 | windows: 7 | - window_name: mp_test 8 | layout: tiled 9 | shell_command_before: 10 | - cd ~/ 11 | panes: 12 | - shell_command: 13 | - cd /var/log 14 | - ls -al | grep \.log -------------------------------------------------------------------------------- /tests/fixtures/workspace/builder/plugin_owc.yaml: -------------------------------------------------------------------------------- 1 | session_name: plugin-test-owc 2 | plugins: 3 | - 'tmuxp_test_plugin_owc.plugin.PluginOnWindowCreate' 4 | windows: 5 | - window_name: editor 6 | layout: tiled 7 | shell_command_before: 8 | - cd ~/ 9 | panes: 10 | - shell_command: 11 | - cd /var/log 12 | - ls -al | grep \.log 13 | - echo hello 14 | - echo hello 15 | - echo hello 16 | -------------------------------------------------------------------------------- /tests/fixtures/workspace/builder/plugin_owc_multiple_windows.yaml: -------------------------------------------------------------------------------- 1 | session_name: plugin-test-owc-mw 2 | plugins: 3 | - 'tmuxp_test_plugin_owc.plugin.PluginOnWindowCreate' 4 | windows: 5 | - window_name: owc_mw_test 6 | shell_command_before: 7 | - cd ~/ 8 | panes: 9 | - shell_command: 10 | - cd /var/log 11 | - ls -al | grep \.log 12 | - window_name: owc_mw_test_2 13 | layout: tiled 14 | shell_command_before: 15 | - cd ~/ 16 | panes: 17 | - echo hello -------------------------------------------------------------------------------- /tests/fixtures/workspace/builder/plugin_r.yaml: -------------------------------------------------------------------------------- 1 | session_name: plugin-test-r 2 | plugins: 3 | - 'tmuxp_test_plugin_r.plugin.PluginReattach' 4 | windows: 5 | - window_name: editor 6 | layout: tiled 7 | shell_command_before: 8 | - cd ~/ 9 | panes: 10 | - shell_command: 11 | - cd /var/log 12 | - ls -al | grep \.log 13 | - echo hello 14 | - echo hello 15 | - echo hello 16 | -------------------------------------------------------------------------------- /tests/fixtures/workspace/builder/plugin_versions_fail.yaml: -------------------------------------------------------------------------------- 1 | session_name: plugin-test-version-fail 2 | plugins: 3 | - 'tmuxp_test_plugin_fail.plugin.PluginFailVersion' 4 | windows: 5 | - panes: 6 | - echo "hey" -------------------------------------------------------------------------------- /tests/fixtures/workspace/builder/regression_00132_dots.yaml: -------------------------------------------------------------------------------- 1 | # regression for https://github.com/tmux-python/tmuxp/issues/132 2 | session_name: dot.session-name 3 | windows: 4 | - layout: main-vertical 5 | panes: 6 | - shell_command: 7 | - echo 'hi' 8 | -------------------------------------------------------------------------------- /tests/fixtures/workspace/builder/session_options.yaml: -------------------------------------------------------------------------------- 1 | session_name: test session options 2 | start_directory: '~' 3 | options: 4 | default-shell: /bin/sh 5 | default-command: /bin/sh 6 | windows: 7 | - window_name: moo 8 | panes: 9 | - pane 10 | - pane 11 | - pane 12 | -------------------------------------------------------------------------------- /tests/fixtures/workspace/builder/start_directory.yaml: -------------------------------------------------------------------------------- 1 | session_name: sample workspace 2 | start_directory: '/usr' 3 | windows: 4 | - window_name: supposed to be /usr/bin 5 | window_index: 1 6 | start_directory: /usr/bin 7 | options: 8 | main-pane-height: 50 9 | panes: 10 | - shell_command: 11 | - echo "hey" 12 | - shell_command: 13 | - echo "moo" 14 | - window_name: support to be /dev 15 | window_index: 2 16 | start_directory: /dev 17 | panes: 18 | - shell_command: 19 | - echo hello 20 | - shell_command: 21 | - echo "hey" 22 | - shell_command: 23 | - echo "moo" 24 | - window_name: cwd containing a space 25 | window_index: 3 26 | start_directory: {TEST_DIR} 27 | panes: 28 | - shell_command: 29 | - echo hello 30 | - shell_command: 31 | - echo "hey" 32 | - shell_command: 33 | - echo "moo" 34 | - window_name: testsa3 35 | window_index: 4 36 | panes: 37 | - shell_command: 38 | - echo hello 39 | - shell_command: 40 | - echo "hey" 41 | - shell_command: 42 | - echo "moo3" 43 | - window_name: cwd relative to start_directory since no rel dir entered 44 | window_index: 5 45 | start_directory: ./ 46 | panes: 47 | - shell_command: 48 | - echo hello 49 | - shell_command: 50 | - echo "hey" 51 | - shell_command: 52 | - echo "moo3" 53 | -------------------------------------------------------------------------------- /tests/fixtures/workspace/builder/start_directory_relative.yaml: -------------------------------------------------------------------------------- 1 | session_name: sample workspace 2 | start_directory: ./ 3 | windows: 4 | - window_name: supposed to be /usr/bin 5 | start_directory: '/usr/bin' 6 | options: 7 | main-pane-height: 50 8 | panes: 9 | - shell_command: 10 | - echo "hey" 11 | - shell_command: 12 | - echo "moo" 13 | - window_name: support to be /dev 14 | start_directory: '/dev' 15 | panes: 16 | - shell_command: 17 | - echo hello 18 | - shell_command: 19 | - echo "hey" 20 | - shell_command: 21 | - echo "moo" 22 | - window_name: cwd containing a space 23 | start_directory: {TEST_DIR} 24 | panes: 25 | - shell_command: 26 | - echo hello 27 | - shell_command: 28 | - echo "hey" 29 | - shell_command: 30 | - echo "moo" 31 | - window_name: inherit start_directory which is rel to workspace file 32 | panes: 33 | - shell_command: 34 | - echo hello 35 | - shell_command: 36 | - echo "hey" 37 | - shell_command: 38 | - echo "moo3" 39 | - window_name: cwd relative to workspace file 40 | start_directory: ./ 41 | panes: 42 | - shell_command: 43 | - echo hello 44 | - shell_command: 45 | - echo "hey" 46 | - shell_command: 47 | - echo "moo3" 48 | -------------------------------------------------------------------------------- /tests/fixtures/workspace/builder/start_directory_session_path.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | session_name: sample_start_dir_session_path 3 | start_directory: '/usr' 4 | windows: 5 | - panes: 6 | - 7 | -------------------------------------------------------------------------------- /tests/fixtures/workspace/builder/suppress_history.yaml: -------------------------------------------------------------------------------- 1 | session_name: sample workspace 2 | start_directory: '~' 3 | suppress_history: false 4 | windows: 5 | - window_name: inHistory 6 | panes: 7 | - echo inHistory 8 | - window_name: isMissing 9 | suppress_history: true 10 | panes: 11 | - echo isMissing 12 | -------------------------------------------------------------------------------- /tests/fixtures/workspace/builder/three_pane.yaml: -------------------------------------------------------------------------------- 1 | session_name: sample workspace 2 | start_directory: '~' 3 | windows: 4 | - window_name: test 5 | layout: main-horizontal 6 | panes: 7 | - shell_command: 8 | - cmd: vim 9 | - shell_command: 10 | - cmd: echo "hey" 11 | - shell_command: 12 | - cmd: echo "moo" 13 | -------------------------------------------------------------------------------- /tests/fixtures/workspace/builder/three_windows.yaml: -------------------------------------------------------------------------------- 1 | session_name: sample_three_windows 2 | windows: 3 | - window_name: first 4 | panes: 5 | - shell_command: 6 | - cmd: echo 'first window' 7 | - window_name: second 8 | panes: 9 | - shell_command: 10 | - cmd: echo 'second window' 11 | - window_name: third 12 | panes: 13 | - shell_command: 14 | - cmd: echo 'third window' 15 | -------------------------------------------------------------------------------- /tests/fixtures/workspace/builder/two_pane.yaml: -------------------------------------------------------------------------------- 1 | session_name: sample workspace 2 | start_directory: '~' 3 | windows: 4 | - layout: main-vertical 5 | panes: 6 | - shell_command: 7 | - cmd: vim 8 | - shell_command: 9 | - cmd: echo "hey" 10 | window_name: editor 11 | - panes: 12 | - shell_command: 13 | - cmd: tail | echo 'hi' 14 | window_name: logging 15 | - window_name: test 16 | panes: 17 | - shell_command: 18 | - cmd: htop 19 | -------------------------------------------------------------------------------- /tests/fixtures/workspace/builder/two_windows.yaml: -------------------------------------------------------------------------------- 1 | session_name: sample_two_windows 2 | windows: 3 | - window_name: first 4 | panes: 5 | - shell_command: 6 | - cmd: echo 'first window' 7 | - window_name: second 8 | panes: 9 | - shell_command: 10 | - cmd: echo 'second window' 11 | -------------------------------------------------------------------------------- /tests/fixtures/workspace/builder/window_automatic_rename.yaml: -------------------------------------------------------------------------------- 1 | session_name: test window options 2 | start_directory: '~' 3 | windows: 4 | - window_name: renamed_window 5 | layout: main-horizontal 6 | options: 7 | automatic-rename: on 8 | panes: 9 | - shell_command: 10 | - cmd: man ls 11 | start_directory: '~' 12 | focus: true 13 | - shell_command: 14 | - cmd: echo "hey" 15 | - shell_command: 16 | - cmd: echo "moo" 17 | -------------------------------------------------------------------------------- /tests/fixtures/workspace/builder/window_index.yaml: -------------------------------------------------------------------------------- 1 | session_name: sample workspace 2 | windows: 3 | - window_name: zero 4 | panes: 5 | - echo 'zero' 6 | - window_name: five 7 | panes: 8 | - echo 'five' 9 | window_index: 5 10 | - window_name: one 11 | panes: 12 | - echo 'one' 13 | -------------------------------------------------------------------------------- /tests/fixtures/workspace/builder/window_options.yaml: -------------------------------------------------------------------------------- 1 | session_name: test window options 2 | start_directory: '~' 3 | windows: 4 | - layout: main-horizontal 5 | options: 6 | main-pane-height: 5 7 | panes: 8 | - pane 9 | - pane 10 | - pane 11 | window_name: editor 12 | -------------------------------------------------------------------------------- /tests/fixtures/workspace/builder/window_options_after.yaml: -------------------------------------------------------------------------------- 1 | session_name: tmuxp test window_options_after 2 | options: 3 | default-shell: /bin/bash 4 | windows: 5 | - window_name: test 6 | suppress_history: false 7 | panes: 8 | - echo 0 9 | - echo 1 10 | options_after: 11 | synchronize-panes: on 12 | -------------------------------------------------------------------------------- /tests/fixtures/workspace/builder/window_shell.yaml: -------------------------------------------------------------------------------- 1 | session_name: test window options 2 | start_directory: '~' 3 | windows: 4 | - layout: main-horizontal 5 | options: 6 | main-pane-height: 5 7 | panes: 8 | - pane 9 | - pane 10 | - pane 11 | window_name: editor 12 | window_shell: top 13 | -------------------------------------------------------------------------------- /tests/fixtures/workspace/expand1.py: -------------------------------------------------------------------------------- 1 | """Examples of expansion of tmuxp configurations from shorthand style.""" 2 | 3 | from __future__ import annotations 4 | 5 | import pathlib 6 | import typing as t 7 | 8 | before_workspace = { 9 | "session_name": "sample workspace", 10 | "start_directory": "~", 11 | "windows": [ 12 | { 13 | "window_name": "editor", 14 | "panes": [ 15 | {"shell_command": ["vim", "top"]}, 16 | {"shell_command": ["vim"]}, 17 | {"shell_command": 'cowsay "hey"'}, 18 | ], 19 | "layout": "main-vertical", 20 | }, 21 | { 22 | "window_name": "logging", 23 | "panes": [{"shell_command": ["tail -F /var/log/syslog"]}], 24 | }, 25 | { 26 | "start_directory": "/var/log", 27 | "options": {"automatic-rename": True}, 28 | "panes": [{"shell_command": "htop"}, "vim"], 29 | }, 30 | {"start_directory": "./", "panes": ["pwd"]}, 31 | {"start_directory": "./asdf/", "panes": ["pwd"]}, 32 | {"start_directory": "../", "panes": ["pwd"]}, 33 | {"panes": ["top"]}, 34 | ], 35 | } 36 | 37 | 38 | def after_workspace() -> dict[str, t.Any]: 39 | """After expansion of shorthand style.""" 40 | return { 41 | "session_name": "sample workspace", 42 | "start_directory": str(pathlib.Path().home()), 43 | "windows": [ 44 | { 45 | "window_name": "editor", 46 | "panes": [ 47 | {"shell_command": [{"cmd": "vim"}, {"cmd": "top"}]}, 48 | {"shell_command": [{"cmd": "vim"}]}, 49 | {"shell_command": [{"cmd": 'cowsay "hey"'}]}, 50 | ], 51 | "layout": "main-vertical", 52 | }, 53 | { 54 | "window_name": "logging", 55 | "panes": [{"shell_command": [{"cmd": "tail -F /var/log/syslog"}]}], 56 | }, 57 | { 58 | "start_directory": "/var/log", 59 | "options": {"automatic-rename": True}, 60 | "panes": [ 61 | {"shell_command": [{"cmd": "htop"}]}, 62 | {"shell_command": [{"cmd": "vim"}]}, 63 | ], 64 | }, 65 | { 66 | "start_directory": str(pathlib.Path().home()), 67 | "panes": [{"shell_command": [{"cmd": "pwd"}]}], 68 | }, 69 | { 70 | "start_directory": str(pathlib.Path().home() / "asdf"), 71 | "panes": [{"shell_command": [{"cmd": "pwd"}]}], 72 | }, 73 | { 74 | "start_directory": str(pathlib.Path().home().parent.resolve()), 75 | "panes": [{"shell_command": [{"cmd": "pwd"}]}], 76 | }, 77 | {"panes": [{"shell_command": [{"cmd": "top"}]}]}, 78 | ], 79 | } 80 | -------------------------------------------------------------------------------- /tests/fixtures/workspace/expand2-expanded.yaml: -------------------------------------------------------------------------------- 1 | session_name: sample workspace 2 | start_directory: {HOME} 3 | windows: 4 | - window_name: focused window 5 | layout: main-horizontal 6 | focus: true 7 | panes: 8 | - shell_command: 9 | - cmd: cd ~ 10 | - shell_command: 11 | - cmd: cd /usr 12 | focus: true 13 | - shell_command: 14 | - cmd: cd ~ 15 | - cmd: echo "moo" 16 | - cmd: top 17 | - window_name: window 2 18 | panes: 19 | - shell_command: 20 | - cmd: top 21 | focus: true 22 | - shell_command: 23 | - cmd: echo "hey" 24 | - shell_command: 25 | - cmd: echo "moo" 26 | - window_name: window 3 27 | panes: 28 | - shell_command: 29 | - cmd: cd / 30 | focus: true 31 | - shell_command: [] 32 | - shell_command: [] 33 | -------------------------------------------------------------------------------- /tests/fixtures/workspace/expand2-unexpanded.yaml: -------------------------------------------------------------------------------- 1 | session_name: sample workspace 2 | start_directory: '~' 3 | windows: 4 | - window_name: focused window 5 | layout: main-horizontal 6 | focus: true 7 | panes: 8 | - shell_command: 9 | - cd ~ 10 | - shell_command: 11 | - cd /usr 12 | focus: true 13 | - shell_command: 14 | - cd ~ 15 | - echo "moo" 16 | - top 17 | - window_name: window 2 18 | panes: 19 | - shell_command: 20 | - top 21 | focus: true 22 | - shell_command: 23 | - echo "hey" 24 | - shell_command: 25 | - echo "moo" 26 | - window_name: window 3 27 | panes: 28 | - shell_command: cd / 29 | focus: true 30 | - pane 31 | - pane 32 | 33 | -------------------------------------------------------------------------------- /tests/fixtures/workspace/expand2.py: -------------------------------------------------------------------------------- 1 | """YAML examples of expansion of tmuxp configurations from shorthand style.""" 2 | 3 | from __future__ import annotations 4 | 5 | import pathlib 6 | 7 | from tests.fixtures import utils as test_utils 8 | 9 | 10 | def unexpanded_yaml() -> str: 11 | """Return unexpanded, shorthand YAML tmuxp configuration.""" 12 | return test_utils.read_workspace_file("workspace/expand2-unexpanded.yaml") 13 | 14 | 15 | def expanded_yaml() -> str: 16 | """Return expanded, verbose YAML tmuxp configuration.""" 17 | return test_utils.read_workspace_file("workspace/expand2-expanded.yaml").format( 18 | HOME=str(pathlib.Path().home()), 19 | ) 20 | -------------------------------------------------------------------------------- /tests/fixtures/workspace/expand_blank.py: -------------------------------------------------------------------------------- 1 | """Expected expanded configuration for empty workspace panes.""" 2 | 3 | from __future__ import annotations 4 | 5 | expected = { 6 | "session_name": "Blank pane test", 7 | "windows": [ 8 | { 9 | "window_name": "Blank pane test", 10 | "panes": [ 11 | {"shell_command": []}, 12 | {"shell_command": []}, 13 | {"shell_command": []}, 14 | ], 15 | }, 16 | { 17 | "window_name": "More blank panes", 18 | "panes": [ 19 | {"shell_command": []}, 20 | {"shell_command": []}, 21 | {"shell_command": []}, 22 | ], 23 | }, 24 | { 25 | "window_name": "Empty string (return)", 26 | "panes": [ 27 | {"shell_command": [{"cmd": ""}]}, 28 | {"shell_command": [{"cmd": ""}]}, 29 | {"shell_command": [{"cmd": ""}]}, 30 | ], 31 | }, 32 | { 33 | "window_name": "Blank with options", 34 | "panes": [ 35 | {"shell_command": [], "focus": True}, 36 | {"shell_command": [], "start_directory": "/tmp"}, 37 | ], 38 | }, 39 | ], 40 | } 41 | -------------------------------------------------------------------------------- /tests/fixtures/workspace/freezer/sample_workspace.yaml: -------------------------------------------------------------------------------- 1 | session_name: sample workspace 2 | start_directory: "~" 3 | windows: 4 | - layout: main-vertical 5 | panes: 6 | - shell_command: 7 | - cmd: vim 8 | start_directory: "~" 9 | - shell_command: 10 | - cmd: echo "hey" 11 | - cmd: cd ../ 12 | window_name: editor 13 | - panes: 14 | - shell_command: 15 | - cmd: pane 16 | start_directory: /usr/bin 17 | window_name: logging 18 | - window_name: test 19 | panes: 20 | - shell_command: 21 | - cmd: top 22 | -------------------------------------------------------------------------------- /tests/fixtures/workspace/sample_workspace.py: -------------------------------------------------------------------------------- 1 | """Example workspace fixture for tmuxp WorkspaceBuilder.""" 2 | 3 | from __future__ import annotations 4 | 5 | sample_workspace_dict = { 6 | "session_name": "sample workspace", 7 | "start_directory": "~", 8 | "windows": [ 9 | { 10 | "window_name": "editor", 11 | "panes": [ 12 | {"start_directory": "~", "shell_command": ["vim"]}, 13 | {"shell_command": ['cowsay "hey"']}, 14 | ], 15 | "layout": "main-vertical", 16 | }, 17 | { 18 | "window_name": "logging", 19 | "panes": [ 20 | { 21 | "shell_command": ["tail -F /var/log/syslog"], 22 | "start_directory": "/var/log", 23 | }, 24 | ], 25 | }, 26 | {"options": {"automatic_rename": True}, "panes": [{"shell_command": ["htop"]}]}, 27 | ], 28 | } 29 | -------------------------------------------------------------------------------- /tests/fixtures/workspace/shell_command_before_session-expected.yaml: -------------------------------------------------------------------------------- 1 | shell_command_before: 2 | shell_command: 3 | - cmd: 'echo "hi"' 4 | session_name: 'test' 5 | windows: 6 | - window_name: editor 7 | panes: 8 | - shell_command: 9 | - cmd: 'echo "hi"' 10 | - cmd: vim 11 | - cmd: :Ex 12 | - shell_command: 13 | - cmd: 'echo "hi"' 14 | - shell_command: 15 | - cmd: 'echo "hi"' 16 | - cmd: cd /usr 17 | - window_name: logging 18 | panes: 19 | - shell_command: 20 | - cmd: 'echo "hi"' 21 | - shell_command: 22 | - cmd: 'echo "hi"' 23 | - cmd: top 24 | - cmd: emacs 25 | -------------------------------------------------------------------------------- /tests/fixtures/workspace/shell_command_before_session.py: -------------------------------------------------------------------------------- 1 | """Tests shell_command_before configuration.""" 2 | 3 | from __future__ import annotations 4 | 5 | from tests.fixtures import utils as test_utils 6 | 7 | before = test_utils.read_workspace_file("workspace/shell_command_before_session.yaml") 8 | expected = test_utils.read_workspace_file( 9 | "workspace/shell_command_before_session-expected.yaml", 10 | ) 11 | -------------------------------------------------------------------------------- /tests/fixtures/workspace/shell_command_before_session.yaml: -------------------------------------------------------------------------------- 1 | shell_command_before: 2 | - 'echo "hi"' 3 | session_name: 'test' 4 | windows: 5 | - window_name: editor 6 | panes: 7 | - shell_command: 8 | - vim 9 | - :Ex 10 | - pane 11 | - cd /usr 12 | - window_name: logging 13 | panes: 14 | - shell_command: 15 | - 16 | - shell_command: 17 | - top 18 | - emacs 19 | -------------------------------------------------------------------------------- /tests/fixtures/workspace/trickle.py: -------------------------------------------------------------------------------- 1 | """Test data for tmuxp workspace fixture to demo object tree inheritance.""" 2 | 3 | from __future__ import annotations 4 | 5 | before = { # shell_command_before is string in some areas 6 | "session_name": "sample workspace", 7 | "start_directory": "/var", 8 | "windows": [ 9 | { 10 | "window_name": "editor", 11 | "start_directory": "log", 12 | "panes": [ 13 | {"shell_command": [{"cmd": "vim"}]}, 14 | {"shell_command": [{"cmd": 'cowsay "hey"'}]}, 15 | ], 16 | "layout": "main-vertical", 17 | }, 18 | { 19 | "window_name": "logging", 20 | "start_directory": "~", 21 | "panes": [ 22 | {"shell_command": [{"cmd": "tail -F /var/log/syslog"}]}, 23 | {"shell_command": []}, 24 | ], 25 | }, 26 | ], 27 | } 28 | 29 | expected = { # shell_command_before is string in some areas 30 | "session_name": "sample workspace", 31 | "start_directory": "/var", 32 | "windows": [ 33 | { 34 | "window_name": "editor", 35 | "start_directory": "/var/log", 36 | "panes": [ 37 | {"shell_command": [{"cmd": "vim"}]}, 38 | {"shell_command": [{"cmd": 'cowsay "hey"'}]}, 39 | ], 40 | "layout": "main-vertical", 41 | }, 42 | { 43 | "start_directory": "~", 44 | "window_name": "logging", 45 | "panes": [ 46 | {"shell_command": [{"cmd": "tail -F /var/log/syslog"}]}, 47 | {"shell_command": []}, 48 | ], 49 | }, 50 | ], 51 | } 52 | -------------------------------------------------------------------------------- /tests/test_plugin.py: -------------------------------------------------------------------------------- 1 | """Tests for tmuxp plugin API.""" 2 | 3 | from __future__ import annotations 4 | 5 | import pytest 6 | 7 | from tmuxp.exc import TmuxpPluginException 8 | 9 | from .fixtures.pluginsystem.partials.all_pass import AllVersionPassPlugin 10 | from .fixtures.pluginsystem.partials.libtmux_version_fail import ( 11 | LibtmuxVersionFailIncompatiblePlugin, 12 | LibtmuxVersionFailMaxPlugin, 13 | LibtmuxVersionFailMinPlugin, 14 | ) 15 | from .fixtures.pluginsystem.partials.tmux_version_fail import ( 16 | TmuxVersionFailIncompatiblePlugin, 17 | TmuxVersionFailMaxPlugin, 18 | TmuxVersionFailMinPlugin, 19 | ) 20 | from .fixtures.pluginsystem.partials.tmuxp_version_fail import ( 21 | TmuxpVersionFailIncompatiblePlugin, 22 | TmuxpVersionFailMaxPlugin, 23 | TmuxpVersionFailMinPlugin, 24 | ) 25 | 26 | 27 | @pytest.fixture(autouse=True) 28 | def autopatch_sitedir(monkeypatch_plugin_test_packages: None) -> None: 29 | """Fixture automatically used that patches sitedir.""" 30 | 31 | 32 | def test_all_pass() -> None: 33 | """Plugin for tmuxp that loads successfully.""" 34 | AllVersionPassPlugin() 35 | 36 | 37 | def test_tmux_version_fail_min() -> None: 38 | """Plugin raises if tmux version is below minimum constraint.""" 39 | with pytest.raises(TmuxpPluginException, match=r"Incompatible.*") as exc_info: 40 | TmuxVersionFailMinPlugin() 41 | assert "tmux-min-version-fail" in str(exc_info.value) 42 | 43 | 44 | def test_tmux_version_fail_max() -> None: 45 | """Plugin raises if tmux version is above maximum constraint.""" 46 | with pytest.raises(TmuxpPluginException, match=r"Incompatible.*") as exc_info: 47 | TmuxVersionFailMaxPlugin() 48 | assert "tmux-max-version-fail" in str(exc_info.value) 49 | 50 | 51 | def test_tmux_version_fail_incompatible() -> None: 52 | """Plugin raises if tmuxp version is incompatible.""" 53 | with pytest.raises(TmuxpPluginException, match=r"Incompatible.*") as exc_info: 54 | TmuxVersionFailIncompatiblePlugin() 55 | assert "tmux-incompatible-version-fail" in str(exc_info.value) 56 | 57 | 58 | def test_tmuxp_version_fail_min() -> None: 59 | """Plugin raises if tmuxp version is below minimum constraint.""" 60 | with pytest.raises(TmuxpPluginException, match=r"Incompatible.*") as exc_info: 61 | TmuxpVersionFailMinPlugin() 62 | assert "tmuxp-min-version-fail" in str(exc_info.value) 63 | 64 | 65 | def test_tmuxp_version_fail_max() -> None: 66 | """Plugin raises if tmuxp version is above max constraint.""" 67 | with pytest.raises(TmuxpPluginException, match=r"Incompatible.*") as exc_info: 68 | TmuxpVersionFailMaxPlugin() 69 | assert "tmuxp-max-version-fail" in str(exc_info.value) 70 | 71 | 72 | def test_tmuxp_version_fail_incompatible() -> None: 73 | """Plugin raises if libtmux version is below minimum constraint.""" 74 | with pytest.raises(TmuxpPluginException, match=r"Incompatible.*") as exc_info: 75 | TmuxpVersionFailIncompatiblePlugin() 76 | assert "tmuxp-incompatible-version-fail" in str(exc_info.value) 77 | 78 | 79 | def test_libtmux_version_fail_min() -> None: 80 | """Plugin raises if libtmux version is below minimum constraint.""" 81 | with pytest.raises(TmuxpPluginException, match=r"Incompatible.*") as exc_info: 82 | LibtmuxVersionFailMinPlugin() 83 | assert "libtmux-min-version-fail" in str(exc_info.value) 84 | 85 | 86 | def test_libtmux_version_fail_max() -> None: 87 | """Plugin raises if libtmux version is above max constraint.""" 88 | with pytest.raises(TmuxpPluginException, match=r"Incompatible.*") as exc_info: 89 | LibtmuxVersionFailMaxPlugin() 90 | assert "libtmux-max-version-fail" in str(exc_info.value) 91 | 92 | 93 | def test_libtmux_version_fail_incompatible() -> None: 94 | """Plugin raises if libtmux version is incompatible.""" 95 | with pytest.raises(TmuxpPluginException, match=r"Incompatible.*") as exc_info: 96 | LibtmuxVersionFailIncompatiblePlugin() 97 | assert "libtmux-incompatible-version-fail" in str(exc_info.value) 98 | -------------------------------------------------------------------------------- /tests/test_shell.py: -------------------------------------------------------------------------------- 1 | """Tests for tmuxp shell module.""" 2 | 3 | from __future__ import annotations 4 | 5 | from tmuxp import shell 6 | 7 | 8 | def test_detect_best_shell() -> None: 9 | """detect_best_shell() returns a a string of the best shell.""" 10 | result = shell.detect_best_shell() 11 | assert isinstance(result, str) 12 | 13 | 14 | def test_shell_detect() -> None: 15 | """Tests shell detection functions.""" 16 | assert isinstance(shell.has_bpython(), bool) 17 | assert isinstance(shell.has_ipython(), bool) 18 | assert isinstance(shell.has_ptpython(), bool) 19 | assert isinstance(shell.has_ptipython(), bool) 20 | -------------------------------------------------------------------------------- /tests/tests/__init__.py: -------------------------------------------------------------------------------- 1 | """Tests for tmuxp's pytest helpers.""" 2 | -------------------------------------------------------------------------------- /tests/tests/test_helpers.py: -------------------------------------------------------------------------------- 1 | """Tests for tmuxp's helper and utility functions.""" 2 | 3 | from __future__ import annotations 4 | 5 | import typing as t 6 | 7 | import pytest 8 | from libtmux.test.random import get_test_session_name 9 | from libtmux.test.temporary import temp_session 10 | 11 | if t.TYPE_CHECKING: 12 | from libtmux.server import Server 13 | 14 | 15 | def test_temp_session_kills_session_on_exit(server: Server) -> None: 16 | """Test temp_session() context manager kills session on exit.""" 17 | server = server 18 | session_name = get_test_session_name(server=server) 19 | 20 | with temp_session(server=server, session_name=session_name): 21 | result = server.has_session(session_name) 22 | assert result 23 | 24 | assert not server.has_session(session_name) 25 | 26 | 27 | @pytest.mark.flaky(reruns=5) 28 | def test_temp_session_if_session_killed_before_exit(server: Server) -> None: 29 | """Handles situation where session already closed within context.""" 30 | server = server 31 | session_name = get_test_session_name(server=server) 32 | 33 | with temp_session(server=server, session_name=session_name): 34 | # an error or an exception within a temp_session kills the session 35 | server.kill_session(session_name) 36 | 37 | result = server.has_session(session_name) 38 | assert not result 39 | 40 | # really dead? 41 | assert not server.has_session(session_name) 42 | -------------------------------------------------------------------------------- /tests/workspace/__init__.py: -------------------------------------------------------------------------------- 1 | """Workspace tests for tmuxp.""" 2 | -------------------------------------------------------------------------------- /tests/workspace/conftest.py: -------------------------------------------------------------------------------- 1 | """Pytest configuration for tmuxp workspace tests.""" 2 | 3 | from __future__ import annotations 4 | 5 | import types 6 | 7 | import pytest 8 | 9 | from tests.fixtures.structures import WorkspaceTestData 10 | 11 | 12 | @pytest.fixture 13 | def config_fixture() -> WorkspaceTestData: 14 | """Deferred import of tmuxp.tests.fixtures.*. 15 | 16 | pytest setup (conftest.py) patches os.environ["HOME"], delay execution of 17 | os.path.expanduser until here. 18 | """ 19 | from tests.fixtures import workspace as test_workspace_data 20 | 21 | return WorkspaceTestData( 22 | **{ 23 | k: v 24 | for k, v in test_workspace_data.__dict__.items() 25 | if isinstance(v, types.ModuleType) 26 | }, 27 | ) 28 | -------------------------------------------------------------------------------- /tests/workspace/test_freezer.py: -------------------------------------------------------------------------------- 1 | """Tests tmux session freezing functionality for tmuxp.""" 2 | 3 | from __future__ import annotations 4 | 5 | import time 6 | import typing 7 | 8 | from tests.fixtures import utils as test_utils 9 | from tmuxp._internal.config_reader import ConfigReader 10 | from tmuxp.workspace import freezer, validation 11 | from tmuxp.workspace.builder import WorkspaceBuilder 12 | 13 | if typing.TYPE_CHECKING: 14 | import pathlib 15 | 16 | from libtmux.session import Session 17 | 18 | from tests.fixtures.structures import WorkspaceTestData 19 | 20 | 21 | def test_freeze_config(session: Session) -> None: 22 | """Test freezing a tmux session.""" 23 | session_config = ConfigReader._from_file( 24 | test_utils.get_workspace_file("workspace/freezer/sample_workspace.yaml"), 25 | ) 26 | 27 | builder = WorkspaceBuilder(session_config=session_config, server=session.server) 28 | builder.build(session=session) 29 | assert session == builder.session 30 | 31 | time.sleep(0.50) 32 | 33 | session = session 34 | new_config = freezer.freeze(session) 35 | 36 | validation.validate_schema(new_config) 37 | 38 | # These should dump without an error 39 | ConfigReader._dump(fmt="json", content=new_config) 40 | ConfigReader._dump(fmt="yaml", content=new_config) 41 | 42 | # Inline configs should also dump without an error 43 | compact_config = freezer.inline(new_config) 44 | 45 | ConfigReader._dump(fmt="json", content=compact_config) 46 | ConfigReader._dump(fmt="yaml", content=compact_config) 47 | 48 | 49 | """Tests for :meth:`freezer.inline()`.""" 50 | 51 | ibefore_workspace = { # inline config 52 | "session_name": "sample workspace", 53 | "start_directory": "~", 54 | "windows": [ 55 | { 56 | "shell_command": ["top"], 57 | "window_name": "editor", 58 | "panes": [{"shell_command": ["vim"]}, {"shell_command": ['cowsay "hey"']}], 59 | "layout": "main-vertical", 60 | }, 61 | { 62 | "window_name": "logging", 63 | "panes": [{"shell_command": ["tail -F /var/log/syslog"]}], 64 | }, 65 | {"options": {"automatic-rename": True}, "panes": [{"shell_command": ["htop"]}]}, 66 | ], 67 | } 68 | 69 | iafter_workspace = { 70 | "session_name": "sample workspace", 71 | "start_directory": "~", 72 | "windows": [ 73 | { 74 | "shell_command": "top", 75 | "window_name": "editor", 76 | "panes": ["vim", 'cowsay "hey"'], 77 | "layout": "main-vertical", 78 | }, 79 | {"window_name": "logging", "panes": ["tail -F /var/log/syslog"]}, 80 | {"options": {"automatic-rename": True}, "panes": ["htop"]}, 81 | ], 82 | } 83 | 84 | 85 | def test_inline_workspace() -> None: 86 | """:meth:`freezer.inline()` shell commands list to string.""" 87 | test_workspace = freezer.inline(ibefore_workspace) 88 | assert test_workspace == iafter_workspace 89 | 90 | 91 | def test_export_yaml( 92 | tmp_path: pathlib.Path, 93 | config_fixture: WorkspaceTestData, 94 | ) -> None: 95 | """Test exporting a frozen tmux session to YAML.""" 96 | yaml_workspace_file = tmp_path / "config.yaml" 97 | 98 | sample_workspace = freezer.inline( 99 | config_fixture.sample_workspace.sample_workspace_dict, 100 | ) 101 | configparser = ConfigReader(sample_workspace) 102 | 103 | yaml_workspace_data = configparser.dump("yaml", indent=2, default_flow_style=False) 104 | 105 | yaml_workspace_file.write_text(yaml_workspace_data, encoding="utf-8") 106 | 107 | new_workspace_data = ConfigReader._from_file(yaml_workspace_file) 108 | assert config_fixture.sample_workspace.sample_workspace_dict == new_workspace_data 109 | -------------------------------------------------------------------------------- /tests/workspace/test_import_tmuxinator.py: -------------------------------------------------------------------------------- 1 | """Test for tmuxp tmuxinator configuration.""" 2 | 3 | from __future__ import annotations 4 | 5 | import typing as t 6 | 7 | import pytest 8 | 9 | from tests.fixtures import import_tmuxinator as fixtures 10 | from tmuxp._internal.config_reader import ConfigReader 11 | from tmuxp.workspace import importers, validation 12 | 13 | 14 | class TmuxinatorConfigTestFixture(t.NamedTuple): 15 | """Test fixture for tmuxinator config conversion tests.""" 16 | 17 | test_id: str 18 | tmuxinator_yaml: str 19 | tmuxinator_dict: dict[str, t.Any] 20 | tmuxp_dict: dict[str, t.Any] 21 | 22 | 23 | TMUXINATOR_CONFIG_TEST_FIXTURES: list[TmuxinatorConfigTestFixture] = [ 24 | TmuxinatorConfigTestFixture( 25 | test_id="basic_config", 26 | tmuxinator_yaml=fixtures.test1.tmuxinator_yaml, 27 | tmuxinator_dict=fixtures.test1.tmuxinator_dict, 28 | tmuxp_dict=fixtures.test1.expected, 29 | ), 30 | TmuxinatorConfigTestFixture( 31 | test_id="legacy_tabs_config", # older vers use `tabs` instead of `windows` 32 | tmuxinator_yaml=fixtures.test2.tmuxinator_yaml, 33 | tmuxinator_dict=fixtures.test2.tmuxinator_dict, 34 | tmuxp_dict=fixtures.test2.expected, 35 | ), 36 | TmuxinatorConfigTestFixture( 37 | test_id="sample_config", # Test importing 38 | tmuxinator_yaml=fixtures.test3.tmuxinator_yaml, 39 | tmuxinator_dict=fixtures.test3.tmuxinator_dict, 40 | tmuxp_dict=fixtures.test3.expected, 41 | ), 42 | ] 43 | 44 | 45 | @pytest.mark.parametrize( 46 | list(TmuxinatorConfigTestFixture._fields), 47 | TMUXINATOR_CONFIG_TEST_FIXTURES, 48 | ids=[test.test_id for test in TMUXINATOR_CONFIG_TEST_FIXTURES], 49 | ) 50 | def test_config_to_dict( 51 | test_id: str, 52 | tmuxinator_yaml: str, 53 | tmuxinator_dict: dict[str, t.Any], 54 | tmuxp_dict: dict[str, t.Any], 55 | ) -> None: 56 | """Test exporting tmuxinator configuration to dictionary.""" 57 | yaml_to_dict = ConfigReader._load(fmt="yaml", content=tmuxinator_yaml) 58 | assert yaml_to_dict == tmuxinator_dict 59 | 60 | assert importers.import_tmuxinator(tmuxinator_dict) == tmuxp_dict 61 | 62 | validation.validate_schema(importers.import_tmuxinator(tmuxinator_dict)) 63 | --------------------------------------------------------------------------------