summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.bashrc73
-rw-r--r--usr/local/bin/commacd.sh330
2 files changed, 332 insertions, 71 deletions
diff --git a/.bashrc b/.bashrc
index 2212fee..dff2018 100644
--- a/.bashrc
+++ b/.bashrc
@@ -32,77 +32,6 @@ local_pathmunge () {
}
local_pathmunge ~/usr/local/bin
-# Change to the correct sibling of a parent directory
-cd_parent_sibling ()
-{
- local sibling_name="$2";
- local curname="";
- local curtop="$PWD";
- local finaltop=""
- while [ ! "$curtop" = "/" ]; do
- if [ -d $(dirname $curtop)/$sibling_name ]; then
- finaltop=$(dirname $curtop)/$sibling_name;
- break;
- fi
- curname=$(basename $curtop)/$curname;
- curtop=$(dirname $curtop);
- done
- if [ ! -z "$finaltop" ]; then
- if [ ! -z "$3" ]; then
- if [ ! -d $finaltop/$curname ]; then
- echo "$finaltop found but $finaltop/$curname not found";
- fi
- finaltop=$finaltop/$curname;
- fi
- if [ -d $finaltop ]; then
- echo "$1 $finaltop"
- $1 $finaltop
- fi
- else
- echo "parent directory with sibling $sibling_name not found"
- fi
-}
-
-# Change to build directory
-cdb ()
-{
- if [ -z "$1" ]; then
- cd_parent_sibling cd build t;
- else
- cd_parent_sibling cd "$1" t;
- fi
-}
-
-# Change to source directory
-cds ()
-{
- if [ -z "$1" ]; then
- cd_parent_sibling cd source t;
- else
- cd_parent_sibling cd "$1" t;
- fi
-}
-
-# Push to top of build directory
-cdbt ()
-{
- if [ -z "$1" ]; then
- cd_parent_sibling pushd build;
- else
- cd_parent_sibling pushd "$1";
- fi
-}
-
-# Push to top of source directory
-cdst ()
-{
- if [ -z "$1" ]; then
- cd_parent_sibling pushd source;
- else
- cd_parent_sibling pushd "$1";
- fi
-}
-
# An emacs 'alias' with the ability to read from stdin; example:
# echo "hello world" | ef -
ef()
@@ -144,6 +73,8 @@ case "$-" in
source_first /usr/share/fzf/shell/key-bindings.bash ~/.fzf.bash
eval "$(~/usr/local/bin/fasd --init auto)"
complete -d j
+ eval "$(direnv hook bash)"
+ source ~/usr/local/bin/commacd.sh
# Emacs tramp wants a simple prompt that it can recognize
if [[ "$TERM" = "dumb" ]]; then
diff --git a/usr/local/bin/commacd.sh b/usr/local/bin/commacd.sh
new file mode 100644
index 0000000..7465516
--- /dev/null
+++ b/usr/local/bin/commacd.sh
@@ -0,0 +1,330 @@
+# commacd - a faster way to move around (Bash 3+/Zsh).
+# https://github.com/shyiko/commacd
+#
+# ENV variables that can be used to control commacd:
+# COMMACD_CD - function to change the directory
+# (by default 'builtin cd "$1" && pwd' is used)
+# COMMACD_NOTTY - set it to "on" when you want to suppress user input
+# (print multiple matches and exit)
+# COMMACD_NOFUZZYFALLBACK - set it to "on" if you don't want commacd to use
+# "fuzzy matching" as a fallback for "no matches by prefix"
+# (introduced in 0.2.0)
+# COMMACD_SEQSTART - set it to 1 if you want "multiple choices" to start
+# from 1 instead of 0
+# (introduced in 0.3.0)
+# COMMACD_IMPLICITENTER - set it to "on" to avoid pressing <ENTER> when
+# number of options (to select from) is less than 10
+# (introduced in 0.4.0)
+# COMMACD_MARKER - space-separated project root "marker"s (for ,, to stop at)
+# (".git/ .hg/ .svn/" by default)
+# (introduced in 1.0.0)
+#
+# @version 1.0.0
+# @author Stanley Shyiko <stanley.shyiko@gmail.com>
+# @license MIT
+
+# turn on case-insensitive search by default
+
+if [ -n "$ZSH_VERSION" ]; then
+ # CAUTION: if you name local variable "path" (when no_case_glob is set) it
+ # will override $PATH
+ setopt no_case_glob
+ unsetopt nomatch
+elif [ -n "$BASH_VERSION" ]; then
+ shopt -s nocaseglob
+else
+ echo "commacd: unsupported shell" >&2
+ return
+fi
+
+_commacd_split() {
+ # shellcheck disable=SC2001
+ echo "$1" | sed $'s|/|\\\n/|g'
+}
+_commacd_join() { local IFS="$1"; shift; echo "$*"; }
+
+_commacd_expand() (
+ if [ -n "$ZSH_VERSION" ]; then
+ setopt extended_glob null_glob
+ # shellcheck disable=SC2086
+ print -rl - ${~1}
+ else
+ shopt -s extglob nullglob
+ shopt -u failglob
+ local ex=($1)
+ printf "%s\n" "${ex[@]}"
+ fi
+)
+
+_command_cd() {
+ local dir=$1 IFS=$' \t\n'
+ if [[ -z "$COMMACD_CD" ]]; then
+ if [[ "$PWD" != "$dir" ]]; then
+ builtin cd "$dir" && pwd
+ else
+ echo "commacd: no matches found" >&2
+ return 1
+ fi
+ else
+ $COMMACD_CD "$dir"
+ fi
+}
+
+# show match selection menu
+_commacd_choose_match() {
+ local matches=("$@")
+ local i=${COMMACD_SEQSTART:-0}
+ for match in "${matches[@]}"; do
+ printf "%s\t%s\n" "$((i++))" "$match" >&2
+ done
+ local selection
+ local threshold=$((11-${COMMACD_SEQSTART:-0}))
+ if [[ "$COMMACD_IMPLICITENTER" == "on" && \
+ ${#matches[@]} -lt $threshold ]]; then
+ if [ -n "$ZSH_VERSION" ]; then
+ read -k1 "selection?: " >&2
+ else
+ read -n1 -e -p ': ' selection >&2
+ fi
+ else
+ if [ -n "$ZSH_VERSION" ]; then
+ read "selection?: " >&2
+ else
+ read -e -p ': ' selection >&2
+ fi
+ fi
+ if [[ "$selection" =~ ^[0-9]+$ ]]; then
+ local i=$((selection-${COMMACD_SEQSTART:-0}))
+ if [ -n "$ZSH_VERSION" ]; then
+ ((i++)) # zsh arrays are 1-based
+ fi
+ if [[ "${matches[i]}" != "" ]]; then
+ echo -n "${matches[i]}"
+ return
+ fi
+ fi
+ echo -n "$PWD"
+}
+
+_commacd_prefix_glob() (
+ set -f
+ local pth="${*%/}/" IFS=$'\n'
+ # shellcheck disable=SC2046
+ echo -n "$(_commacd_join \* $(_commacd_split "$pth"))"
+)
+
+_commacd_glob() (
+ set -f
+ local pth="${*%/}" IFS=$'\n'
+ if [[ "${pth/\/}" == "$pth" ]]; then
+ pth="*$pth*/"
+ else
+ # shellcheck disable=SC2046
+ pth="$(_commacd_join \* $(_commacd_split "$pth") | rev |
+ sed 's/\//*\//' | rev)*/"
+ fi
+ echo -n "$pth"
+)
+
+_commacd_forward_by_prefix() {
+ local matches=($(_commacd_expand "$(_commacd_prefix_glob "$*")"))
+ if [[ "$COMMACD_NOFUZZYFALLBACK" != "on" && ${#matches[@]} -eq 0 ]]; then
+ matches=($(_commacd_expand "$(_commacd_glob "$*")"))
+ fi
+ case ${#matches[@]} in
+ 0) echo -n "$PWD";;
+ *) printf "%s\n" "${matches[@]}"
+ esac
+}
+
+# jump forward (`,`)
+_commacd_forward() {
+ if [[ -z "$*" ]]; then return 1; fi
+ local IFS=$'\n'
+ local dir=($(_commacd_forward_by_prefix "$@"))
+ if [[ "$COMMACD_NOTTY" == "on" ]]; then
+ printf "%s\n" "${dir[@]}"
+ return
+ fi
+ if [[ ${#dir[@]} -gt 1 ]]; then
+ # https://github.com/shyiko/commacd/issues/12
+ trap 'trap - SIGINT; stty '"$(stty -g)" SIGINT
+
+ dir=$(_commacd_choose_match "${dir[@]}")
+
+ # make sure trap is removed regardless of whether read -e ... was
+ # interrupted or not
+ trap - SIGINT
+ if [[ -z "$dir" ]]; then return 1; fi
+ fi
+ _command_cd "$dir"
+}
+
+_commacd_marked() {
+ local dir="${*%/}"
+ local markers=(${COMMACD_MARKER:-.git/ .hg/ .svn/})
+ if [ -n "$ZSH_VERSION" ]; then
+ markers=("${=markers[1]}") # shwordsplit
+ fi
+ for marker in "${markers[@]}"; do
+ if [[ -e "$dir/$marker" ]]; then
+ return 0
+ fi
+ done
+ return 1
+}
+
+# search backward for the vcs root (`,,`)
+_commacd_backward_vcs_root() {
+ local dir="${PWD%/*}"
+ while ! _commacd_marked "$dir"; do
+ dir="${dir%/*}"
+ if [[ -z "$dir" ]]; then
+ echo -n "$PWD"
+ return
+ fi
+ done
+ echo -n "$dir"
+}
+
+# search backward for the directory whose name begins with $1 (`,, $1`)
+_commacd_backward_by_prefix() (
+ local prev_dir dir="${PWD%/*}" matches match IFS=$'\n'
+ while [[ -n "$dir" ]]; do
+ prev_dir="$dir"
+ dir="${dir%/*}"
+ matches=($(_commacd_expand "$dir/${1}*/"))
+ for match in "${matches[@]}"; do
+ if [ -n "$ZSH_VERSION" ]; then
+ if [[ "${match:l}" == "${prev_dir:l}/" ]]; then
+ echo -n "$prev_dir"
+ return
+ fi
+ else
+ # ${var,,}/${var^^} are not available in BASH 3.2 (macOS 10.14)
+ # hence nocasematch & ==
+ shopt -s nocasematch
+ if [[ "$match" == "$prev_dir/" ]]; then
+ echo -n "$prev_dir"
+ return
+ fi
+ fi
+ done
+ done
+ # at this point there is still a possibility that $1 is an actual path
+ # (e.g. passed by "complete"), so let's check that
+ if [[ -d "$1" ]]; then echo -n "$1"; return; fi
+ # otherwise fallback to pwd
+ echo -n "$PWD"
+)
+
+# replace $1 with $2 in $PWD (`,, $1 $2`)
+_commacd_backward_substitute() {
+ echo -n "${PWD/$1/$2}"
+}
+
+# choose `,,` strategy based on a number of arguments
+_commacd_backward() {
+ local dir=
+ case $# in
+ 0) dir=$(_commacd_backward_vcs_root);;
+ 1) dir=$(_commacd_backward_by_prefix "$*")
+ if [[ "$COMMACD_NOFUZZYFALLBACK" != "on" && "$dir" == "$PWD" ]]; then
+ dir=$(_commacd_backward_by_prefix "*$*")
+ fi;;
+ 2) dir=$(_commacd_backward_substitute "$@");;
+ *) return 1
+ esac
+ if [[ "$COMMACD_NOTTY" == "on" ]]; then
+ echo -n "${dir}"
+ return
+ fi
+ _command_cd "$dir"
+}
+
+_commacd_backward_forward_by_prefix() {
+ local dir="$PWD" pth="${*%/}/" matches match IFS=$'\n'
+ if [[ "${pth:0:1}" == "/" ]]; then
+ # assume that we've been brought here by the completion
+ dir=(${pth%/}*)
+ printf "%s\n" "${dir[@]}"
+ return
+ fi
+ while [[ -n "$dir" ]]; do
+ dir="${dir%/*}"
+ matches=($(_commacd_expand "$dir/$(_commacd_prefix_glob "$*")"))
+ if [[ "$COMMACD_NOFUZZYFALLBACK" != "on" && ${#matches[@]} -eq 0 ]]; then
+ matches=($(_commacd_expand "$dir/$(_commacd_glob "$*")"))
+ fi
+ case ${#matches[@]} in
+ 0) ;;
+ *) printf "%s\n" "${matches[@]}"
+ return;;
+ esac
+ done
+ echo -n "$PWD"
+}
+
+# combine backtracking with `, $1` (`,,, $1`)
+_commacd_backward_forward() {
+ if [[ -z "$*" ]]; then return 1; fi
+ local IFS=$'\n'
+ local dir=($(_commacd_backward_forward_by_prefix "$@"))
+ if [[ "$COMMACD_NOTTY" == "on" ]]; then
+ printf "%s\n" "${dir[@]}"
+ return
+ fi
+ if [[ ${#dir[@]} -gt 1 ]]; then
+ dir=$(_commacd_choose_match "${dir[@]}")
+ fi
+ _command_cd "$dir"
+}
+
+_commacd_completion_valid() {
+ if [[ "$2" == "$PWD" || "${2// /\\ }" == "$1" ]]; then return 1; fi
+}
+
+_commacd_completion() {
+ local pattern=${COMP_WORDS[COMP_CWORD]} IFS=$'\n'
+ # shellcheck disable=SC2088
+ if [[ "${pattern:0:2}" == "~/" ]]; then
+ # shellcheck disable=SC2116
+ pattern=$(echo ~/"${pattern:2}")
+ fi
+ local completion=($(COMMACD_NOTTY=on $1 "$pattern"))
+ if ! _commacd_completion_valid "$pattern" "$completion"; then
+ pattern="$pattern?"
+ # retry with ? matching
+ completion=($(COMMACD_NOTTY=on $1 "$pattern"))
+ if ! _commacd_completion_valid "$pattern" "$completion"; then
+ return
+ fi
+ fi
+ # remove trailing / (if any)
+ for i in "${!completion[@]}"; do
+ completion[$i]="${completion[$i]%/}";
+ done
+ COMPREPLY=($(compgen -W "$(printf "%s\n" "${completion[@]}")" -- ''))
+}
+
+_commacd_forward_completion() {
+ _commacd_completion _commacd_forward
+}
+
+_commacd_backward_completion() {
+ _commacd_completion _commacd_backward
+}
+
+_commacd_backward_forward_completion() {
+ _commacd_completion _commacd_backward_forward
+}
+
+alias ,=_commacd_forward
+alias ,,=_commacd_backward
+alias ,,,=_commacd_backward_forward
+
+if [ -n "$BASH_VERSION" ]; then
+ complete -o filenames -F _commacd_forward_completion ,
+ complete -o filenames -F _commacd_backward_completion ,,
+ complete -o filenames -F _commacd_backward_forward_completion ,,,
+fi