An open API service indexing awesome lists of open source software.

https://github.com/vinnymeller/twm

Tmux Workspace Manager
https://github.com/vinnymeller/twm

Last synced: 6 days ago
JSON representation

Tmux Workspace Manager

Awesome Lists containing this project

README

        

# twm

Tmux Workspace Manager

`twm` is a highly performant, customizable tool to manage workspaces as `tmux` sessions. Sensible defaults can get you up and running with zero configuration, or you can set it up to fit any workflow you like.

![twm](https://raw.githubusercontent.com/vinnymeller/twm-assets/e1a390f8aaa58994059d10271452f8fea5f94a26/twm.gif)

### Table of Contents
#### [CLI Usage](#usage)
#### [Installation Instructions](#installation)
#### [Exposed Environment Variables](#environment-variables)
#### [Configuring twm](#configuration)
#### [Configuration Recipes](#recipes)
#### [Contribution Guidelines](#contributing)

## Usage

```
twm (tmux workspace manager) is a customizable tool for managing workspaces in tmux sessions.

Workspaces are defined as a directory matching any workspace pattern from your configuration. If no configuration is set, any directory containing a `.git` file/folder or a `.twm.yaml` file is considered a workspace.

Usage: twm [OPTIONS]

Options:
-e, --existing
Prompt user to select an existing tmux session to attach to.

This shouldn't be used with other options.

-g, --group
Prompt user to start a new session in the same group as an existing session.

Setting this option will cause `-l/--layout` and `-p/--path` to be ignored.

-G, --group-with
Group with an existing session by name

-d, --dont-attach
Don't attach to the workspace session after opening it

-l, --layout
Prompt user to select a globally-defined layout to open the workspace with.

Using this option will override any other layout definitions that would otherwise automatically be used when opening the workspace.

-p, --path
Open the given path as a workspace.

Using this option does not require that the path be a valid workspace according to your configuration.

-n, --name
Force the workspace to be opened with the given name.

When setting this option, you should be aware that twm will not "see" this session when performing other automatic actions. For example, if you have a workspace at ~/foobar and run `twm -n jimbob -p ~/foobar`, and then run `twm` and select `~/foobar` from the picker, a new session `foobar` will be created. If you then run `twm -g` and select `foobar`, `foobar-1` will be created in the `foobar` group.

-N, --print-workspace-name
Print the name of the workspace generated for the given path to stdout.

This can be used with other options.

-c, --command
Override any layouts and open the workspace with the given command instead

--make-default-config
Make default configuration file.

By default will attempt to write a default configuration file and configuration schema in `$XDG_CONFIG_HOME/twm/` Using `-p/--path` with this flag will attempt to write the files to the folder specified. twm will not overwrite existing files. You will be prompted to rename/move the existing files before retrying.

--make-default-layout-config
Make default local layout configuration file.

Will attempt to create `.twm.yaml` in the current directory. Will not overwrite existing files. You can use `-p/--path ` to specify a different directory to write the file to.

--print-config-schema
Print the configuration file (twm.yaml) schema.

This can be used with tools (e.g. language servers) to provide autocompletion and validation when editing your configuration.

--print-layout-config-schema
Print the local layout configuration file (.twm.yaml) schema.

This can be used with tools (e.g. language servers) to provide autocompletion and validation when editing your configuration.

--print-bash-completion
Print bash completions to stdout

--print-zsh-completion
Print zsh completions to stdout

--print-fish-completion
Print fish completions to stdout

--print-man
Print man(1) page to stdout

-h, --help
Print help (see a summary with '-h')

-V, --version
Print version
```

### Environment Variables

`twm` will set several environment variables within all sessions generated by it. They're there to help with scripts or keybinds you want to interact with `twm`. They are:
- `TWM` - set to `1` if the current shell is in a `twm` session
- `TWM_ROOT` - the root directory of the new workspace
- `TWM_TYPE` - the type of workspace. empty string if there was no workspace type defined.
- `TWM_NAME` - the name of the tmux session created by `twm`.

These can be used in many possible ways:
- Instead of defining all your setup commands in a workspace-type-specific layout, you could have a 1 shared setup script defined globally that runs on workspace entry that checks `TWM_TYPE` for type-specific setup
- You can use `TWM_ROOT` to perform actions if the workspace is in within a specific directory
- You can check `TWM` to ensure you handle manually-created sessions differently than `twm`-created sessions in some automation task

Additionally, setting the `TWM_CONFIG_FILE` env var will override the default config search path. If your config file is in a non-standard location, you can test twm with the default configuration with `TWM_CONFIG_FILE= twm`, or if your configuration is in the standard location, `TWM_CONFIG_FILE=/dev/null twm` will do.

## Installation
Contributions are more than welcome! If there are workflows you think would be useful to add, or if you find a bug, please open an issue or PR. For style and linting, I simply use `cargo fmt` and `clippy::all`.

### Cargo

The easiest way to install is to use Cargo:
```bash
cargo install twm
```

### Nix

If you use Nix, it is available in [nixpkgs](https://search.nixos.org/packages?channel=unstable&show=twm&from=0&size=50&sort=relevance&type=packages&query=twm) as well as being packaged in this repo's flake.

### Completion

If you're using Nix, completion scripts are automatically installed with `twm`, both from this flake and nixpkgs.

I don't believe it is packaged on any other OS. You can manually set up shell completions by putting the following in your shell config:

#### Bash

```bash
# ~/.bashrc
eval "$(twm --print-bash-completion)"
```

#### Zsh
```bash
# ~/.zshrc
eval "$(twm --print-zsh-completion)"
```

#### Fish
```fish
# ~/.config/fish/config.fish
twm --print-fish-completion | source
```

## Configuration

`twm` doesn't need any configuration to run. You can just install it and run `twm`, and the defaults should work for some.

To get a decent default configuration file, you can run `twm --make-default-config`, which will attempt to write two files: `$XDG_CONFIG_HOME/twm/{twm.yaml,twm.schema.json}`.

You can pass `-p/--path ` with `--make-default-config` to write the files to a different folder.

If you use `yaml-language-server`, the default configuration file will automatically be set up to use the `twm.schema.json` file for validation and completion.

When you update `twm`, you can run `twm --print-config-schema > $XDG_CONFIG_HOME/twm/twm.schema.json` to ensure you have the latest schema file.

For a full list of configuration options with examples, see [CONFIGURATION.md](./doc/CONFIGURATION.md)

### Configuration Validation Examples

Here's what the in-editor config validation looks like in neovim with using `yaml-language-server`:

![twm configuration completion](https://raw.githubusercontent.com/vinnymeller/twm-assets/master/twm-config-completion.png)

![twm configuration validation](https://raw.githubusercontent.com/vinnymeller/twm-assets/master/twm-config-errors.png)

## Recipes

### tmux keybindings

Here are the basic twm bindings I personally use:

```tmux
# ~/.tmux.conf
bind f run-shell "tmux neww twm"
bind F run-shell "tmux neww twm -l"
bind s run-shell "tmux neww twm -e" # i rebind the original `s` to `S` so I can still use it
bind g run-shell "tmux neww twm -g"
bind e run-shell "tmux switch -t $TWM_DEFAULT" # i set TWM_DEFAULT in my shellrc, just a session that is always available as a scratch area
```

### Scripting

#### Switching to a workspace from neovim

One thing I wanted to do was to be able to switch to a new workspace from my [snacks.nvim](https://github.com/folke/snacks.nvim) dashboard's recent project selector.

```lua
-- ~/.config/nvim/lua/snacks/init.lua
-- ...
action = function(project_path)
local tmux = vim.fn.getenv("TMUX")
if tmux == nil then
-- if not in tmux already, do something else, maybe just set cwd or something
return
end
-- -d to not attach, -N to print the session name to stdout, -p to use the path we selected in neovim, sub(1, -2) to remove the trailing newline
local twm_workspace_name = vim.system({"twm", "-d", "-N", "-p", project_path}, {text = true}):wait().stdout:sub(1, -2)
vim.system({"tmux", "switch-client"})
end
-- ...
```

#### Start new shells in a "default" workspace

I like to make sure I'm always in tmux when I'm in the terminal, so I have something like this in my `.zshrc`

```bash
# ~/.zshrc
export TWM_DEFAULT="default"
tmux has-session -t $TWM_DEFAULT >/dev/null || twm -d -p $HOME -n $TWM_DEFAULT
if [ -z "$TWM" ]; then
tmux switch -t $TWM_DEFAULT
fi
```

#### Useful shell aliases / functions I've used

```bash
# ~/.zshrc
alias twm-fork='twmg() { gh repo fork --clone --default-branch-only --remote "$1" "$2"; twm -p "$2"; }; twmg' # fork repo $1 at path $2 and open it in twm

# kill current session and switch to last/next/previous session
# i bind this to K in tmux
kt() {
ORIG_SESS="$TWM_NAME"
tmux switch -l || tmux switch -n || tmux switch -p
tmux kill-session -t "$ORIG_SESS"
}

```

## Contributing

Contributions are of course welcome! It's not a huge project so I don't have a lot of guidelines. Make sure the tests (test*, for now :\\) pass. `cargo fmt`. `cargo clippy -- -D clippy::all`. That's all I can think of.

### Feature Philosophy

I avoid adding anything that doesn't have obvious value being *inside* `twm`. If something that can be done *well* with a simple shell script or similar, it probably should be.

#### Examples of things that won't be added:

- Opening a fuzzy finder to choose a session to kill. Simple shell script / easy enough to do with built-in tmux features.
- Opening git worktree branches in windows. Use layouts for this. Detect when you're in a workspace with worktrees and run a script to open them how you want. This will always be more flexible than anything that would be built in.
- Cloning a git repository into a new workspace. This and the above are pretty specific to `tmux-sessionizer` but useful to highlight the differences in our goals. `twm` offers `-p` to open a workspace at a specific path, so such a functionality could be done with something as simple as an alias `alias twm-clone='git clone $1 $2 && twm -p $2'`. You can do the same with mercurial, svn, whatever you want to do.

#### Examples of things that could be added:

- Support for different multiplexers (e.g. Zellij). Abstracting the tmux-specific parts of `twm` into something that could be set up with different multiplexer backends could be a great addition. But I only use tmux/nobody has asked, so I haven't bothered.

#### Features that snuck their way in

There are some features I originally didn't want to add but have since been added anyways.
- Fuzzy finding existing sessions to attach to. Originally I figured it just makes more sense to leave that to built in tmux features, but when the request was made to add a feature to open a session in a group (`twm -g`), I had to add everything needed for `twm -e` anyways. Additionally, if you want to *fuzzy find* existing sessions, having that functionality outside `twm` would rely on some other fuzzy finder. Part of the point of `twm` coming with its own builtin finder is avoid having to rely on other tools in the first place. Thus `twm -g` and `twm -e` were born. I think `-e` is the least useful thing that will ever be added to `twm`.

## License
[MIT](./LICENSE)

## Similar Projects

- [dmux](https://github.com/zdcthomas/dmux)
- [jrmoulton/tmux-sessionizer](https://github.com/jrmoulton/tmux-sessionizer)
- [ThePrimeagen/tmux-sessionizer](https://github.com/ThePrimeagen/.dotfiles/blob/602019e902634188ab06ea31251c01c1a43d1621/bin/.local/scripts/tmux-sessionizer)
- [tmux-sessionx](https://github.com/omerxx/tmux-sessionx)
- [tmuxinator](https://github.com/tmuxinator/tmuxinator)
- [tmuxp](https://github.com/tmux-python/tmuxp)