├── .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 |
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 |
--------------------------------------------------------------------------------