├── .gitignore ├── .travis.yml ├── Makefile ├── README.md ├── animation.gif ├── bd.plugin.zsh ├── bd.zsh └── tests ├── .zshenv └── bd.t /.gitignore: -------------------------------------------------------------------------------- 1 | tests/.zcompdump 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "3.3" 4 | before_script: 5 | - "sudo apt-get install zsh" 6 | install: "sudo pip install cram" 7 | script: "make tests" 8 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: itests tests 2 | 3 | itests: 4 | ZDOTDIR="${PWD}/tests" cram -i --shell=zsh tests 5 | 6 | tests: 7 | ZDOTDIR="${PWD}/tests" cram --shell=zsh tests 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/Tarrasch/zsh-bd.png)](https://travis-ci.org/Tarrasch/zsh-bd) 2 | 3 | # zsh-bd 4 | 5 | Quickly go back to a specific parent directory instead of typing `cd ../../..` redundantly. 6 | 7 | Update August 2017: While there have not been commits lately, it's still fully functional. 8 | 9 | --- 10 | 11 | This is a reimplementation of 12 | [vigneshwaranr/bd](https://github.com/vigneshwaranr/bd) that utilizes the power 13 | of the zsh shell. 14 | 15 | ## Install 16 | 17 | All you need to do is to source `bd.zsh`. Here's the manual installation 18 | 19 | mkdir -p $HOME/.zsh/plugins/bd 20 | curl https://raw.githubusercontent.com/Tarrasch/zsh-bd/master/bd.zsh > $HOME/.zsh/plugins/bd/bd.zsh 21 | print -- "\n# zsh-bd\n. \$HOME/.zsh/plugins/bd/bd.zsh" >> $HOME/.zshrc 22 | 23 | Restart your shell (or run `zsh`) 24 | 25 | ### [Antigen](https://github.com/zsh-users/antigen) 26 | 27 | If you prefer antigen over manual installation 28 | 29 | antigen-bundle Tarrasch/zsh-bd 30 | 31 | ## Usage 32 | 33 | $ mkdir -p a/b/c/d 34 | $ cd a/b/c/d 35 | $ bd b 36 | $ ls 37 | c 38 | $ cd c/d 39 | $ bd 2 40 | $ ls 41 | c 42 | 43 | Here's an animation also showing the completion functionality 44 | 45 | ![Animated gif](animation.gif "Animation that shows auto completion") 46 | 47 | ## Thanks 48 | 49 | Thanks to 50 | [@vigneshwaranr](https://github.com/vigneshwaranr) 51 | for inspiring me to do a zsh version. 52 | 53 | Thanks to [@voria](https://github.com/voria) for many useful 54 | patches! (see commit log) 55 | -------------------------------------------------------------------------------- /animation.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tarrasch/zsh-bd/646f06d9b6a840926a671fdc2da196d4eecfc305/animation.gif -------------------------------------------------------------------------------- /bd.plugin.zsh: -------------------------------------------------------------------------------- 1 | bd.zsh -------------------------------------------------------------------------------- /bd.zsh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | bd () { 3 | (($#<1)) && { 4 | printf -- 'usage: %s \n' "${0}" 5 | printf -- ' %s \n' "${0}" 6 | 7 | return 1 8 | } >&2 9 | 10 | local requestedDestination="${1}" 11 | local -a parents=(${(ps:/:)"${PWD}"}) 12 | local numParents 13 | local dest 14 | local i 15 | local parent 16 | 17 | # prepend root to the parents array 18 | parents=('/' "${parents[@]}") 19 | 20 | # Remove the current directory since it isn't a parent 21 | shift -p parents 22 | 23 | # Get the number of parent directories 24 | numParents="$(( ${#parents[@]}))" 25 | 26 | # Build dest and 'cd' to it by looping over the parents array in reverse 27 | dest='./' 28 | for i in $(seq "${numParents}" -1 1); do 29 | parent="${parents[${i}]}" 30 | dest+='../' 31 | 32 | if [[ "${requestedDestination}" == "${parent}" ]]; then 33 | cd $dest 34 | 35 | return $? 36 | fi 37 | done 38 | 39 | # If the user provided an integer, go up as many times as asked 40 | dest='./' 41 | if [[ "${requestedDestination}" == <-> ]]; then 42 | if [[ "${requestedDestination}" -gt "${numParents}" ]]; then 43 | printf -- '%s: Error: Can not go up %s times (not enough parent directories)\n' "${0}" "${requestedDestination}" 44 | return 1 45 | fi 46 | 47 | for i in {1.."${requestedDestination}"}; do 48 | dest+='../' 49 | done 50 | 51 | cd "${dest}" 52 | 53 | return $? 54 | fi 55 | 56 | # If the above methods fail 57 | printf -- '%s: Error: No parent directory named "%s"\n' "${0}" "${requestedDestination}" 58 | return 1 59 | } 60 | 61 | _bd () { 62 | # Get parents (in reverse order) 63 | local localMatcherList 64 | local -a parents=(${(ps:/:)"${PWD}"}) 65 | local numParents 66 | local i 67 | local -a parentsReverse 68 | 69 | zstyle -s ':completion:*' 'matcher-list' 'localMatcherList' 70 | 71 | # prepend root to the parents array 72 | parents=('/' "${parents[@]}") 73 | 74 | # Remove the current directory since it isn't a parent 75 | shift -p parents 76 | 77 | # Get the number of parent directories 78 | numParents="$(( ${#parents[@]}))" 79 | 80 | parentsReverse=() 81 | for i in $(seq "${numParents}" -1 1); do 82 | parentsReverse+=("${parents[${i}]}") 83 | done 84 | 85 | local expl 86 | _wanted -V directories expl 'parent directories' \ 87 | compadd -M "${localMatcherList}" "$@" -- "${parentsReverse[@]}" 88 | } 89 | 90 | compdef _bd bd 91 | 92 | () { 93 | local -a colors 94 | local dir_color='1;31' # hard-coded default in zsh/complist 95 | 96 | # check for defined zstyle 97 | if zstyle -a ':completion:*' list-colors colors && [[ "$#colors" -ne 0 ]]; then 98 | local zstyle_color="${colors[(r)di=*]#di=}" 99 | if [[ -n "$zstyle_color" ]]; then 100 | dir_color="$zstyle_color" 101 | fi 102 | else 103 | return 104 | fi 105 | 106 | zstyle ':completion:*:*:bd:*:directories' list-colors "=*=${dir_color}" 107 | } 108 | -------------------------------------------------------------------------------- /tests/.zshenv: -------------------------------------------------------------------------------- 1 | test -f "$TESTDIR/.zcompdump" && rm "$TESTDIR/.zcompdump" 2 | 3 | autoload -Uz compinit && compinit 4 | 5 | source "$TESTDIR/../bd.zsh" 6 | export ENV_AUTHORIZATION_FILE="$PWD/.env_auth" 7 | -------------------------------------------------------------------------------- /tests/bd.t: -------------------------------------------------------------------------------- 1 | Move into some folders 2 | 3 | $ mkdir -p a/b/c/d 4 | $ cd a/b/c/d 5 | 6 | Move down to b 7 | 8 | $ bd b 9 | 10 | Check that it went OK 11 | 12 | $ ls 13 | c 14 | 15 | Now lets try to jump to folder with spaces in it 16 | 17 | $ mkdir -p 'a/space dir/f/g/h' 18 | $ cd 'a/space dir/f/g/h' 19 | $ bd 'space dir' 20 | $ ls 21 | f 22 | 23 | Issue [#6] in github, `Fix error "not an identifier"`. I think the actual error 24 | was due to OS X vs gnuutils differences. But it wont hurt to have another test 25 | case. 26 | 27 | $ cd 'f/g/h' 28 | $ bd f 29 | $ ls 30 | g 31 | 32 | Lets also make sure we can jump beyond a 'space dir' 33 | 34 | $ cd g/h 35 | $ bd a 36 | $ ls 37 | space dir 38 | 39 | If the provided argument is an integer, go up as many times as required. 40 | 41 | $ cd 'space dir/f/g/h' 42 | $ bd 1 43 | $ ls 44 | h 45 | $ bd 2 46 | $ ls 47 | f 48 | 49 | Make sure folders can be only numbers, and they are understood 50 | 51 | $ mkdir -p ncpc/2015/whistles 52 | $ cd ncpc/2015/whistles 53 | $ bd 2015 54 | $ ls 55 | whistles 56 | 57 | Also document (with this test) the ambigious case 58 | 59 | $ mkdir -p 1/2/3 60 | $ cd 1/2/3 61 | $ bd 1 62 | $ ls 63 | 2 64 | 65 | Exclude current working directory from parent list 66 | 67 | $ mkdir -p aaa/bbb/aaa/ccc 68 | $ cd aaa/bbb/aaa 69 | $ bd aaa 70 | $ ls 71 | bbb 72 | --------------------------------------------------------------------------------