Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ scrollback previews. When given root directories to search, it also lists any
git or jj repositories beneath them. Selecting a session attaches to it;
selecting a repository starts a numbered session inside it.

<img width="2400" height="1120" alt="Animated demo of the zp session picker" src="https://github.com/user-attachments/assets/c80ce837-25eb-4c1f-b394-d9f6f1daed54" />
<img width="2400" height="1120" alt="Animated demo of the zp session picker" src="https://github.com/user-attachments/assets/cdc4ca7a-b4d5-45b7-a1e4-7a956e212261" />

[zmx]: https://github.com/neurosnap/zmx
[fzf]: https://github.com/junegunn/fzf
Expand Down Expand Up @@ -85,6 +85,10 @@ Sessions created from a repository are named `<repo>.<n>` with the next free
number and start in the repository's directory; sessions created from a query
start in the current directory.

Any [zmx] session labels (`zmx set <name> key=value`) are shown alongside each
session and are fuzzy-searchable, so typing `key=value` narrows the picker to
the sessions that carry that label.

## License

zmx-picker is released under the [Zero Clause BSD License](LICENSE) (SPDX:
Expand Down
5 changes: 4 additions & 1 deletion tests/bin/zmx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ sessions() {
fi
printf '\342\206\222 name=alpha.1\tpid=11\tclients=1\tcreated=1\tstart_dir=/tmp\n'
printf ' name=alpha.2\tpid=12\tclients=0\tcreated=2\tstart_dir=/tmp\n'
printf ' name=beta\tpid=13\tclients=0\tcreated=3\tstart_dir=%s\n' \
# beta carries labels (trailing tab-separated key=value fields)
# and lives in a repo, so it also guards against those fields
# leaking into start_dir and breaking the repo-association count.
printf ' name=beta\tpid=13\tclients=0\tcreated=3\tstart_dir=%s\tproject=zp\tenv=work\n' \
"$ZP_TEST_DIR/root/plain"
printf ' name=d.gamma\tpid=14\tclients=0\tcreated=4\tstart_dir=/tmp\n'
printf ' name=d.omega.9\tpid=15\tclients=0\tcreated=5\tstart_dir=/tmp\n'
Expand Down
10 changes: 10 additions & 0 deletions tests/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,16 @@ else
not_ok 'candidates strips list markers and annotates session counts' "$out"
fi

reset_log
out=$("$zp" --candidates "$root")
beta_line=$(grep $'^session\tbeta\t' <<<"$out")
if [[ $beta_line == *'project=zp env=work'* ]]; then
ok 'session labels are surfaced in the searchable display column'
else
not_ok 'session labels are surfaced in the searchable display column' \
"$beta_line"
fi

reset_log
ZMX_SESSION_PREFIX=d. run '' 'gamma' "$enter" >/dev/null || true
assert_log 'attaching a listed session never re-applies ZMX_SESSION_PREFIX' \
Expand Down
25 changes: 19 additions & 6 deletions zp
Original file line number Diff line number Diff line change
Expand Up @@ -58,20 +58,26 @@ tilde() {
}

candidates() {
local name clients dir repo count note sdir width i
local name clients dir labels repo count note labelcol sdir width i
local session_dirs=()
local names=() notes=() session_clients=()
local names=() notes=() session_clients=() session_labels=()

# Existing zmx sessions. Lines carry a marker prefix before
# name= (e.g. "→ " on the attached session), so strip through
# the field labels rather than off the front. Rows are buffered
# so the name column can be padded to the widest name instead
# of a fixed width.
width=0
while IFS=$'\t' read -r name _ clients _ dir; do
while IFS=$'\t' read -r name _ clients _ dir labels; do
name=${name#*name=}
clients=${clients#*clients=}
dir=${dir#*start_dir=}
# Any session labels arrive as extra tab-separated key=value
# fields after start_dir; slurping them into their own field
# keeps a stray tab out of $dir, which would corrupt the path
# and the start_dir match behind repo session counts. Join
# them with spaces for display; absent labels leave it empty.
labels=${labels//$'\t'/ }

[[ -n $name ]] || continue

Expand All @@ -88,16 +94,23 @@ candidates() {
names+=("$name")
notes+=("$note")
session_clients+=("$clients")
session_labels+=("$labels")
if [[ ${#name} -gt $width ]]; then
width=${#name}
fi
done < <(zmx list 2>/dev/null || true)

for ((i = 0; i < ${#names[@]}; i++)); do
tilde "${session_dirs[i]}"
printf 'session\t%s\t%s%-*s clients:%-2s %s\n' \
# Trailing label column, shown only when the session has
# labels so unlabeled rows keep their current shape.
labelcol=''
if [[ -n ${session_labels[i]} ]]; then
labelcol=" ${session_labels[i]}"
fi
printf 'session\t%s\t%s%-*s clients:%-2s %s%s\n' \
"${names[i]}" "${notes[i]}" "$width" "${names[i]}" \
"${session_clients[i]}" "$tilded"
"${session_clients[i]}" "$tilded" "$labelcol"
done

# Repositories, when any search roots are configured: git
Expand Down Expand Up @@ -276,7 +289,7 @@ select_session() {
--tiebreak=index \
--ellipsis='…' \
--prompt='zmx> ' \
--header=$'Enter: select/create | Tab: mark\n^N: new from query | ^X: kill' \
--header=$'Enter: select/create | Tab: mark\n^N: new from query | ^X: kill' \
--bind='enter:clear-selection+accept' \
--bind="ctrl-x:execute-silent(printf '%s\n' {+} | $kill_cmd)+reload($reload_cmd)" \
--bind="focus:transform:test {1} = session && echo 'change-preview-window(follow)' || echo 'change-preview-window(nofollow)'" \
Expand Down
Loading