Add tmux package and common dotfiles deployment

This commit is contained in:
Fabio Scotto di Santolo
2026-03-17 10:17:42 +01:00
parent 046e69a2b6
commit cf86fcdc81
229 changed files with 10575 additions and 0 deletions

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019-2021 Seong Yong-ju
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,79 @@
# tmux-project
Search projects and open them in a new session.
## Prequisites
- find
- [fzf](https://github.com/junegunn/fzf) (needs `fzf-tmux` to be installed)
## Install
### Installation with [tpm](https://github.com/tmux-plugins/tpm) (recommended)
Add the following line to your `.tmux.conf`.
```tmux
set -g @plugin 'sei40kr/tmux-project'
```
---
It is also recommended to add the following line to your `.tmux.conf` to prevent
a client from being detached when the active session is destroyed.
```tmux
set detach-on-destroy no-detached
```
## Customization
| Variable | Default value | Description |
| :------------------------------ | :-------------------- | :-------------------------------------------------------------------------------- |
| `@project-key` | `"g"` | The key to invoke the project search. If you set it to `""`, the key is disabled. |
| `@project-base-dirs` | `""` | A comma-separated list of directories and their depths to search for projects. |
| `@project-rooters` | `".git"` | A comma-separated list of rooters. |
| `@project-fzf-tmux-layout-opts` | `""` | The layout options for fzf-tmux. See `fzf-tmux(1)` for details. |
| `@project-fzf-opts` | `"--preview 'ls {}'"` | The options for fzf. See `fzf(1)` for details. |
### Setting `@project-base-dirs`
`@project-base-dirs` is a comma-separated list of directories and their depths to search for projects.
Each element of the list is in the following format:
```
/path/to/dir[:<min depth>[:<max depth>]]
```
- If you omit `<min depth>` and `<max depth>`, they are set to `0` and `0` respectively.
- If you omit `<max depth>`, it is set to `<min depth>`. (means `<min depth>` is the exact depth)
If you omit the depth or explicitly set it to `0`, the directory itself will be
added as a project. In that case, you can add the directory as a project even if
it contains no rooter.
---
For example, if you want to search for ghq repositories as projects:
```tmux
set -ag @project-base-dirs ,"${GHQ_ROOT}:3"
```
For example, if you want to add `~/.vim` itself as a project:
```tmux
set -ag @project-base-dirs ,"${HOME}/.vim"
```
## tmuxinator Integration
tmux-project can be integrated with [tmuxinator](https://github.com/tmuxinator/tmuxinator).
When you have tmuxinator installed and the selected project has
`.tmuxinator.yml` at the root, tmux-project will start a new session with
tmuxinator.
## License
MIT

View File

@@ -0,0 +1,61 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1681202837,
"narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "cfacdce06f30d2b68473a46042957675eebb3401",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1683014792,
"narHash": "sha256-6Va9iVtmmsw4raBc3QKvQT2KT/NGRWlvUlJj46zN8B8=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "1a411f23ba299db155a5b45d5e145b85a7aafc42",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

View File

@@ -0,0 +1,30 @@
{
description = "A very basic flake";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, flake-utils, nixpkgs }: flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs { inherit system; };
in
{
packages.default = pkgs.callPackage ./packages/default.nix { };
devShells.default =
let
tmux_conf = pkgs.writeText "tmux.conf" ''
run-shell ${self.packages.${system}.default.rtp}/project.tmux
'';
in
pkgs.mkShell {
buildInputs = with pkgs; [ tmux fzf ];
shellHook = ''
TMUX= TMUX_TMPDIR= exec tmux -f ${tmux_conf}
'';
};
}
);
}

View File

@@ -0,0 +1,8 @@
get_tmux_option() {
local option=$1
local default_value=$2
local option_value
option_value=$(tmux show -gqv "$option")
echo "${option_value:-$default_value}"
}

View File

@@ -0,0 +1,98 @@
#!/usr/bin/env bash
basedir="$(cd "$(dirname "$0")/.." && pwd)"
# shellcheck source=../lib/tmux.bash
. "$basedir/lib/tmux.bash"
find_projects() {
local -a base_dirs rooters rooter_opts
IFS=',' read -ra base_dirs < <(get_tmux_option '@project-base-dirs')
IFS=',' read -ra rooters < <(get_tmux_option '@project-rooters' '.git')
for rooter in "${rooters[@]}"; do
rooter_opts+=("-o" "-name" "$rooter")
done
rooter_opts=('(' "${rooter_opts[@]:1}" ')')
for base_dir in "${base_dirs[@]}"; do
# If the base_dir is empty, skip it
if [[ -z "$base_dir" ]]; then
continue
fi
local -a tmp
IFS=':' read -ra tmp <<<"$base_dir"
path="${tmp[0]}"
min_depth="${tmp[1]:-0}"
max_depth="${tmp[2]:-${min_depth}}"
if [[ min_depth -eq 0 && max_depth -eq 0 ]]; then
# If min_depth and max_depth are both 0, that means we
# want to add the base_dir itself as a project.
# In that case, add the base_dir as a project even if it
# contains no rooter.
if [[ -d "$path" || -L "$path" ]]; then
echo "$path"
fi
else
find "$path" -mindepth "$((min_depth + 1))" \
-maxdepth "$((max_depth + 1))" \
"${rooter_opts[@]}" \
-printf '%h\n'
fi
done
}
# Convert a path to a session name. The session name is the last component of
# the path. Dots are replaced with underscores. If the path starts with a dot,
# the dot is removed.
# e.g. /home/username/.emacs.d -> emacs_d
to_session_name() {
local session_name="$1"
session_name="${session_name##*/}"
# Dots are not allowed in a tmux session name
# e.g. .emacs.d -> _emacs_d
session_name="${session_name//./_}"
# If the path starts with a slash (a dot), remove it
# e.g. .emacs.d -> _emacs_d -> emacs_d
session_name="${session_name#_}"
echo "$session_name"
}
main() {
local fzf_opts fzf_tmux_layout_opts selected_path session_name
# shellcheck disable=SC2207
fzf_opts="$(get_tmux_option "@project-fzf-opts" "--preview 'ls {}'")"
# shellcheck disable=SC2207
fzf_tmux_layout_opts="$(get_tmux_option "@project-fzf-tmux-layout-opts")"
selected_path="$(find_projects | eval "fzf-tmux ${fzf_tmux_layout_opts} -- ${fzf_opts}")"
if [[ -z "$selected_path" ]]; then
return 0
fi
# If the selected project has .tmuxinator.yml file, use tmuxinator to
# start the session.
if command -v tmuxinator &>/dev/null && [[ -f "${selected_path}/.tmuxinator.yml" ]]; then
tmuxinator start -p "${selected_path}/.tmuxinator.yml"
return 0
fi
session_name="$(to_session_name "$selected_path")"
# If the session already exists, attach to it. Otherwise, create a new
# session and attach to it.
if ! tmux has-session -t "$session_name" 2>/dev/null; then
# Return 0 even if creating the session fails.
tmux new-session -d -s "$session_name" -c "$selected_path" \; \
set -t "$session_name" destroy-unattached off || :
fi
tmux switch-client -t "$session_name"
}
main "$@"

View File

@@ -0,0 +1,11 @@
{ lib, tmuxPlugins }:
tmuxPlugins.mkTmuxPlugin {
pluginName = "project";
version = "unstable-2023-05-04";
src = ../.;
meta = with lib; {
license = licenses.mit;
platforms = platforms.unix;
};
}

View File

@@ -0,0 +1,10 @@
#!/usr/bin/env bash
basedir="$(cd "$(dirname "$0")" && pwd)"
# shellcheck source=lib/tmux.bash
. "${basedir}/lib/tmux.bash"
key="$(get_tmux_option '@project-key' 'g')"
if [[ -n "$key" ]]; then
tmux bind-key "$key" run-shell -b "${basedir}/libexec/switch-project.bash"
fi