#!/usr/bin/env rc2 flag e + # Preserve spaces in paths from command output. ifs=' ' path_prefix=() if(! ~ $#* 0 && ~ $1 --path) path_prefix=1 args { about 'Navigate to and manage bookmarked directories' flag full -f --full 'Show full paths' flag show_ls -l --ls 'Show directory contents' flag path_mode --path 'Print the selected path' option name -n --name 'Name for a new bookmark' positional action 'Bookmark name or command' positional value 'Bookmark name or directory' } fn fail { echo bm: $"* >[1=2] exit 1 } fn bookmark_path { cat $store/$1 } fn validate_name { if(~ $1 add rm list -*) fail $1 is reserved if(~ $1 .* */* *' '*) fail invalid bookmark name: $1 return 0 } fn list_bookmarks { for(file in $store/*) { if(test -f $file) { name=`{basename $file} dir=`{cat $file} if(~ $#full 1) printf '%s\t%s\n' $name $dir if(! ~ $#full 1) printf '%s\n' $name } } return 0 } fn choose_bookmark { chosen_name=() if(! whatis fzf >/dev/null >[2=1]) fail fzf is required candidates=`{mktemp -t bm.XXXXXXXX} for(file in $store/*) { if(test -f $file) { name=`{basename $file} dir=`{cat $file} contents=() if(~ $#show_ls 1) contents=`{ls -A -1 $dir | tr '\n' ' '} printf '%s\t%s\t%s\n' $name $dir $"contents >>$candidates } } nth=1 if(~ $#full 1) nth=1,2 selection_file=`{mktemp -t bm.XXXXXXXX} flag e - fzf --delimiter '\t' --nth $nth --with-nth 1,2,3 <$candidates >$selection_file fzf_status=$status flag e + rm -f $candidates selection=`{cat $selection_file} rm -f $selection_file if(~ $fzf_status 130 1) return if(! ~ $fzf_status '') exit $fzf_status chosen_name=`{printf '%s\n' $selection | cut -f1} return 0 } if(~ $#XDG_DATA_HOME 0) data_home=$HOME/.local/share if(! ~ $#XDG_DATA_HOME 0) data_home=$XDG_DATA_HOME store=$data_home/bm/bookmarks mkdir -p $store command=go bookmark=$action if(! ~ $#action 0 && ~ $action add rm list) { command=$action bookmark=$value } if(! ~ $#path_mode 0 && ~ $#path_prefix 0) fail unknown option: --path if(! ~ $#name 0 && ! ~ $command add) fail unknown option: --name switch($command) { case add if(! ~ $#full 0) fail unknown option: --full if(! ~ $#show_ls 0) fail unknown option: --ls dir=$value if(~ $#dir 0) dir=$PWD if(! test -d $dir) fail not a directory: $dir dir=`{cd $dir && pwd -P} if(~ $#name 0) name=`{basename $dir} validate_name $name tmp=`{mktemp $store/.tmp.XXXXXXXX} printf '%s\n' $dir >$tmp mv -f $tmp $store/$name echo $dir >[1=2] case rm name=$bookmark if(~ $#name 0) { choose_bookmark if(~ $#chosen_name 0) exit name=$chosen_name } if(! test -f $store/$name) fail no such bookmark: $name rm -f $store/$name case list if(! ~ $#show_ls 0) fail unknown option: --ls if(! ~ $#bookmark 0) fail unknown option: $bookmark list_bookmarks case go if(! ~ $#value 0) fail too many bookmark names name=$bookmark if(~ $#name 0) { choose_bookmark if(~ $#chosen_name 0) exit name=$chosen_name } if(! test -f $store/$name) fail no such bookmark: $name dir=`{bookmark_path $name} if(! test -d $dir) fail bookmark is not a directory: $dir printf '%s\n' $dir }