Skip to content

Commit

Permalink
Merge pull request #9 from mandos/develop
Browse files Browse the repository at this point in the history
Customize session names + tests
  • Loading branch information
27medkamal authored May 6, 2024
2 parents e13c4c4 + 61cde1a commit d4041f8
Show file tree
Hide file tree
Showing 11 changed files with 458 additions and 85 deletions.
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
# IDE
.idea

# Nix
.envrc
.direnv
result
34 changes: 34 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# syntax = docker/dockerfile:1.4
FROM nixos/nix:2.22.0 AS builder

WORKDIR /tmp/build
RUN mkdir /tmp/nix-store-closure
COPY . .

RUN \
--mount=type=cache,target=/nix,from=nixos/nix:2.22.0,source=/nix \
--mount=type=cache,target=/root/.cache \
--mount=type=bind,target=/tmp/build \
<<EOF
nix \
--extra-experimental-features "nix-command flakes" \
--option filter-syscalls false \
--show-trace \
--log-format raw \
build .#dev --out-link /tmp/output/result
cp -R $(nix-store -qR /tmp/output/result) /tmp/nix-store-closure
EOF


FROM scratch

WORKDIR /workspace

COPY --from=builder /tmp/nix-store-closure /nix/store
COPY --from=builder /tmp/output/ /workspace/
ENV PATH=/workspace/result/bin:$PATH
RUN ["ln","-s", "/workspace/result/bin", "/bin"]
RUN ["mkdir","-p", "/usr/bin"]
RUN ["ln","-s", "/workspace/result/bin/env", "/usr/bin/env"]
# For bats
RUN ["mkdir","--mode", "1777", "/tmp"]
78 changes: 71 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@

<img width="500" alt="tmux-session-wizard" src="https://user-images.githubusercontent.com/14043848/195257556-bc2cfe0a-a1c7-4e29-9741-776eaf0caa06.png">


One prefix key to rule them all (with [fzf](https://github.com/junegunn/fzf) & [zoxide](https://github.com/ajeetdsouza/zoxide)):

- Creating a new session from a list of recently accessed directories
- Naming a session after a folder/project
- Naming a session after a directory/project
- Switching sessions
- Viewing current or creating new sessions in one popup

### Elevator Pitch

Tmux is powerful, yes, but why is creating/switching sessions (arguably its main feature) is so damn hard to do? To create a new session for a project you have to run `tmux new-session -s <session-name> -c <project-folder>`. What if you're inside tmux? Oh, wait you have to use `-d` followed by `tmux switch-client -t <session-name>`. Oh, wait again! What if you're outside tmux and you want to attach to an existing session? now you have to run `tmux attach -t <session-name>` instead. What if you can't remember whether you have a session for that project or not. Guess what? Now you have to run `tmux has-session -t <session-name>`. What if your project folder contains characters not accepted by tmux as a session name? What if you want to show a list of existing sessions? You run `tmux list-sessions`. What if you want to create a session for a project you've recently navigated to? What if, what if, what if.... HOW IS THAT BETTER THAN HAVING 20 TERMINAL WINDOWS OPEN?
Tmux is powerful, yes, but why is creating/switching sessions (arguably its main feature) is so damn hard to do? To create a new session for a project you have to run `tmux new-session -s <session-name> -c <project-directory>`. What if you're inside tmux? Oh, wait you have to use `-d` followed by `tmux switch-client -t <session-name>`. Oh, wait again! What if you're outside tmux and you want to attach to an existing session? now you have to run `tmux attach -t <session-name>` instead. What if you can't remember whether you have a session for that project or not. Guess what? Now you have to run `tmux has-session -t <session-name>`. What if your project folder contains characters not accepted by tmux as a session name? What if you want to show a list of existing sessions? You run `tmux list-sessions`. What if you want to create a session for a project you've recently navigated to? What if, what if, what if.... HOW IS THAT BETTER THAN HAVING 20 TERMINAL WINDOWS OPEN?

What if you could use 1 prefix key to do all of this? Read on!

Expand All @@ -20,6 +20,7 @@ What if you could use 1 prefix key to do all of this? Read on!
`prefix + T` (customisable) - displays a pop-up with [fzf](https://github.com/junegunn/fzf) which displays the existing sessions followed by recently accessed directories (using [zoxide](https://github.com/ajeetdsouza/zoxide)). Choose the session or the directory and voila! You're in that session. If the session doesn't exist, it will be created.

### Required

You must have [fzf](https://github.com/junegunn/fzf), [zoxide](https://github.com/ajeetdsouza/zoxide) installed and available in your path.

### Installation with [Tmux Plugin Manager](https://github.com/tmux-plugins/tpm) (recommended)
Expand Down Expand Up @@ -62,19 +63,82 @@ set -g @session-wizard-height 40
set -g @session-wizard-width 80
```

To customise the way session names are created, use `@session-wizard-mode` option. Allowed values are:

- `directory` (default)
- `full-path`
- `short-path`

```tmux
set -g @session-wizard-mode "full-path"
```

### (Optional) Using the script outside of tmux

Run the following to download the script and add it to your path.
```bash
curl https://raw.githubusercontent.com/27medkamal/tmux-session-wizard/master/session-wizard.sh > /usr/local/bin/t && chmod u+x /usr/local/bin/t
**Note:** you'll need to check the path of your tpm plugins. It may be `~/.tmux/plugins` or `~/.config/tmux/plugins` depending on where your `tmux.conf` is located.

<details>
<summary>bash</summary>

Add the following line to `~/.bashrc`

```sh
# ~/.tmux/plugins
export PATH=$HOME/.tmux/plugins/tmux-session-wizard/bin:$PATH
# ~/.config/tmux/plugins
export PATH=$HOME/.config/tmux/plugins/tmux-session-wizard/bin:$PATH
```

</details>

<details>
<summary>zsh</summary>

Add the following line to `~/.zprofile`

```sh
# ~/.tmux/plugins
export PATH=$HOME/.tmux/plugins/tmux-session-wizard/bin:$PATH
# ~/.config/tmux/plugins
export PATH=$HOME/.config/tmux/plugins/tmux-session-wizard/bin:$PATH
```

</details>

<details>
<summary>fish</summary>

Add the following line to `~/.config/fish/config.fish`

```fish
# ~/.tmux/plugins
fish_add_path $HOME/.tmux/plugins/tmux-session-wizard/bin
# ~/.config/tmux/plugins
fish_add_path $HOME/.config/tmux/plugins/tmux-session-wizard/bin
```
You can then run `t` from anywhere to use the script.

</details>

You can then run `t` from anywhere to use the script.

You can also run `t` with a relative or absolute path to a directory (similar to [zoxide](https://github.com/ajeetdsouza/zoxide)) to create a session for that directory. For example, `t ~/projects/my-project` will create a session named `my-project` and cd into that directory.

Also, depending on the terminal emulator you use, you can make it always start what that script.

### Development

The development environment is built with Nix and Nix's Flakes, if you have it on your system then just run `nix develop` and you are ready to go. Other method is to build the Docker image based on provided Dockerfile:

```bash
docker build --tag tmux-session-wizard:dev --file ./Dockerfile .
```

To run the tests, just run `bats ./tests` for local development environment or `docker run --rm -it -u $(id -u):$(id -g) -v $PWD:$PWD -w $PWD tmux-session-wizard:dev bats ./tests` if you are using Docker.

There is also the helper script for it _./scripts/run-tests.sh_, run `./scripts/run-tests.sh -h` to get more information about usage.

### Inspiration

- ThePrimeagen's [tmux-sessionizer](https://github.com/ThePrimeagen/.dotfiles/blob/master/bin/.local/scripts/tmux-sessionizer)
- Josh Medeski's [t-smart-tmux-session-manager](https://github.com/joshmedeski/t-smart-tmux-session-manager)

Expand Down
75 changes: 75 additions & 0 deletions bin/t
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#!/bin/bash

CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$CURRENT_DIR/../src/helpers.sh"

# Usage: t <optional zoxide-like dir, relative or absolute path>
# If no argument is given, a combination of existing sessions and a zoxide query will be displayed in a FZF

# Parse optional argument
if [ "$1" ]; then
# Argument is given
eval "$(zoxide init bash)"
RESULT=$(z $@ && pwd)
else
# No argument is given. Use FZF
RESULT=$( (
tmux list-sessions -F "#{session_last_attached} #{session_name}: #{session_windows} window(s)\
#{?session_grouped, (group ,}#{session_group}#{?session_grouped,),}#{?session_attached, (attached),}" |
sort -r | (if [ -n "$TMUX" ]; then grep -v " $(tmux display-message -p '#S'):"; else cat; fi) | cut -d' ' -f2-
zoxide query -l | sed -e "$HOME_REPLACER"
) | $(__fzfcmd) --reverse --print-query | tail -n 1)
if [ -z "$RESULT" ]; then
exit 0
fi
fi

# Makes sure tmux is running in order to get all the correct tmux options below. Gets cleaned at the bottom
if ! tmux info &>/dev/null; then
TMP_SESSION_DIR=$(mktemp -d)
TMP_SESSION_NAME=$(session_name --full-path "$TMP_SESSION_DIR")
tmux new-session -d -s "$TMP_SESSION_NAME" -c "$TMP_SESSION_DIR"
fi

# Get or create session
if [[ $RESULT == *":"* ]]; then
# RESULT comes from list-sessions
SESSION=$(echo $RESULT | awk '{print $1}')
SESSION=${SESSION//:/}
else
# RESULT is a path

DIR_FULL=$(echo "$RESULT" | sed -e "s|^~/|$HOME/|")
DIR_WITH_TILDE=$(echo "$RESULT" | sed -e "$HOME_REPLACER") # in case it came from a direct usage of `t <path>`

# Quit if directory does not exists
if [ ! -d "$DIR_FULL" ]; then
exit 0
fi

# Promote rank in zoxide.
zoxide add "$DIR_FULL"

MODE=$(get_tmux_option "@session-wizard-mode" "directory")
SESSION=$(session_name --"$MODE" "$DIR_WITH_TILDE")

if ! tmux has-session -t="$SESSION" 2>/dev/null; then
tmux new-session -d -s "$SESSION" -c "$DIR_FULL"
fi
fi

# Clean up tmp session
if [[ -n "$TMP_SESSION_NAME" ]]; then
tmux kill-session -t "$TMP_SESSION_NAME" 2>/dev/null
rm -rf "$TMP_SESSION_DIR"
fi

# Attach to session
# Escape tilde which if appears by itself, tmux will interpret as a marked target
# https://github.com/tmux/tmux/blob/master/cmd-find.c#L1024C51-L1024C57
SESSION=$(echo "$SESSION" | sed 's/^~$/\\~/')
if [ -z "$TMUX" ]; then
tmux attach -t "$SESSION"
else
tmux switch-client -t "$SESSION"
fi
61 changes: 61 additions & 0 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

48 changes: 48 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
flake-utils.url = "github:numtide/flake-utils";
};

outputs = { self, nixpkgs, flake-utils, ... }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
devPackages = [
(pkgs.bats.withLibraries (p: [ p.bats-support p.bats-assert ]))
pkgs.watchexec
];
plugin = pkgs.tmuxPlugins.mkTmuxPlugin {
pluginName = "session-wizard";
rtpFilePath = "session-wizard.tmux";
version = "unstable";
src = self;
nativeBuildInputs = [ pkgs.makeWrapper ];
postInstall = ''
ls -al $target && \
substituteInPlace $target/session-wizard.tmux \
--replace \$CURRENT_DIR/bin/t $target/bin/t
wrapProgram $target/bin/t \
--prefix PATH : ${with pkgs; lib.makeBinPath ([ fzf zoxide coreutils gnugrep gnused ])}
'';
};
in
{
packages.dev = (pkgs.symlinkJoin
{
name = "dev-environment";
paths = [
plugin
plugin.buildInputs
pkgs.tmux
pkgs.bashInteractive
pkgs.busybox
] ++ devPackages;
});

devShell = pkgs.mkShell {
buildInputs = devPackages;
};
}
);
}
Loading

0 comments on commit d4041f8

Please sign in to comment.