Your config file should be located at $XDG_CONFIG_HOME/twm/twm.yaml (default: ~/.config/twm/twm.yaml).
twm
has sensible defaults if you don't want to deal with a config file just yet, but it will definitely not suffice for everybody's directory structure.
search_paths
: a list of strings representing directories to search for workspaces in. Defaults to~
. Shell expansion is supported.exclude_path_components
: a list of strings representing folders that will not be searched when searching for workspacesmax_search_depth
: integer, how many directories deep to search yoursearch_paths
for workspacessession_name_path_components
: integer, how many directories deep to use by default when generating the name of your tmux session. e.g. if your workspace is/home/vinny/dev/rust/twm
andsession_name_path_components
is 2, the tmux session will be namedrust/twm
. In the case of name conflicts (e.g. I also have a workspace in/home/vinny/cache/rust/twm
), the new session would be calledcache/rust/twm
.workspace_definitions
: optional, a list of workspace definitions. if no workspaces are defined, git repositories and directories with a.twm.yaml
config are considered a workspace by default. other thanname
anddefault_layout
, the other properties all configure workspace match conditions. any number of conditions can be used in combination with each other, e.g.has_all_files: [ ".git", "requirements.txt" ]
andmissing_all_files: [ "pyproject.toml", "Pipfile", "poetry.lock"]
can be used together. each workspace definition has the following properties:name
: string, the name describing the workspace type. must be unique.has_any_file
: optional list of strings, tells twm to only consider a directory to be a workspace of this type if at least one filename in this list is presenthas_all_files
: optional list of strings, tells twm to only consider a directory to be a workspace of this type if all filenames in this list are presentmissing_any_file
: optional list of strings, tells twm to only consider a directory to be a workspace of this type if at least one of the filenames in this list is missingmissing_all_files
: optional list of strings, tells twm to only consider a directory to be a workspace of this type if all the filenames in this list are missingdefault_layout
: optional string, the name of the layout to open this workspace with if the user does not select a layout manually. must match a defined layout name
layouts
: optional, a list of layout definitions. each layout definition has the following properties:name
: string, the name of the layout. must be uniqueinherits
: optional list of strings, the names of layouts to "inherit" from, i.e. run the commands listed in that layout before thecommands
defined in this layout. useful for setting up base layouts to be used in specific development environments. e.g. it might set up one big pane on the left, with two horizontally split panes off to the right, with nothing running inside them. then thecommands
of layouts that inherit from the base can start environment-specific processes.commands
: list of strings, the commands to run to set up the layout. these are sent directly to tmux withtmux send-keys
. generally one will use tmux commands to configure the panes and windows, and then open programs in specific places.
Here is an example configuration with all configuration options set:
# ~/.config/twm/twm.yaml
search_paths: # directories we should begin searching for workspaces in. i just use home. shell expansion is supported
- "~" # default: ["~"]
exclude_path_components: # search branches will be pruned the path being explored contains any of these components
- .git
- .direnv
- node_modules
- venv
- target
max_search_depth: 5 # how deep we should search for workspaces (default: 3)
session_name_path_components: 3 # how many parts of the workspace path to use in generating the session name by default
# if you attempt to open two separate workspaces that would generate the same session name,
# this value will be incremented until a unique session name is found
workspace_definitions: # our list of workspaces, each with different properties
- name: python # they all have to be named
has_any_file: # if any file matches this list, we consider it a match, since its "has_any_file"
- requirements.txt # more complex matching isn't implemented currently
- setup.py
- pyproject.toml
- Pipfile
default_layout: python-dev # the hierarchy for how a layout gets chosen is user opts to select manually > local layout > default for workspace type
- name: go
has_all_files: # if all files match this list, we consider it a match, since its "has_all_files"
- go.mod
- go.sum
- name: docker-compose # you can also combine conditions, as in this example, a docker-compose workspace is matched only if we have *any* of the docker-compose files and both `.git` folder and a `Dockerfile`
has_any_file:
- docker-compose.yaml
- docker-compose.yml
has_all_files:
- Dockerfile
- .git
- name: node # the order of these definitions matters - if a directory matches multiple, the first one wins
has_any_file:
- package.json
- yarn.lock
- .nvmrc
default_layout: node-dev
- name: catchall # without any conditions, all directories will match this wworkspace
default_layout: catchall-dev # this is the default layout for this workspace type
- name: rust
has_any_file:
- Cargo.toml
- Cargo.lock
default_layout: rust-dev
- name: other
has_any_file:
- .git
- flake.nix
- .twm.yaml
layouts: # our list of layouts just have names and a list of commands. the command get sent directly with tmux send-keys
- name: python-dev # i chose not to use any custom configuration becuase that would be a lot of work to basically maintain a subset of possible functionality
commands:
- tmux split-window -h
- tmux resize-pane -x 80
- tmux split-window -v
- tmux send-keys -t 0 'nvim .' C-m
- name: rust-dev
commands:
- tmux split-window -h
- tmux resize-pane -x 80
- tmux select-pane -t 0
- tmux send-keys -t 1 'cargo watch -x test -x run' C-m
- nvim .
- name: catchall-dev
commands:
- nvim .
- name: split-bottom-panes
commands:
- tmux split-window -v
- tmux resize-pane -y 20
- tmux split-window -h
- tmux select-pane -t 0
- name: syslog-monitor
inherits:
- split-bottom-panes # you can also inherit layouts, which will just run the commands from the inherited layout before running your specified commands. these can be nested arbitrarily. this is useful when you have many layouts that should look similar but, for example, have different commands they should run
commands:
- tmux send-keys -t 1 'tail -f /var/log/syslog' C-m
- tmux send-keys -t 2 'journalctl -f' C-m
Note: twm
will search up the directory tree for a .twm.yaml
file. If it finds one, it will be used instead of the default for your workspace type. This is useful for worktrees, where you may not want to check in the layout to source control, but have the same layout apply to all branches. You can simply put .twm.yaml
in the worktree root to achieve this.
Local configuration files can also inherit from globally-defined layouts.
# ~/dev/random/project/dir/.twm.yaml
layout:
name: layout-for-this-project
commands:
- tmux split-window -h
- tmux split-window -h
- tmux split-window -h
- tmux split-window -h