HyperJump - A Quicker Way to CD

HyperJump in Action

This spring I was working with a startup that wanted to use CentOS as their main development platform. Though this wasn’t my role exactly, as it is with startups, you pitch in where you can. So I also became the defacto server admin. Problem was, I am used to the Debian derivatives of Linux, so CentOS was a bit of a switch for me, especially in their default directory structure. Not a big deal, but working over a high latency connection, autocompletion wasn’t working so well. To avoid the headache of remembering new directory structure, and in general to speed up directory navigation, I decided to make a little BASH script to bookmark all the directories I commonly use on a system. Thus, HyperJump was borne.

I have used pushd and popd as well as the autojump utility, but for my needs I wanted something else. My goals were:

With that in mind I made HyperJump, a simple and quick bookmark tool for bash and zsh shells. Actually, it might work on other shells, but I only tested it on bash. A zsh patch was provided by daveFNbuck on GitHub. I tested it on OSX, Debian, CentOS, Ubuntu, and Slackware. I am sure it will run on basically anything with bash or zsh support.

In short, HyperJump is a bookmark tool and a kind of memory aid. You bookmark directories by using the jr command (for Jump Remember), delete bookmarks with the jf command (for Jump Forget), and jump to directories with jj command (for Jump Jump, I guess). If you have Dialog utility, a UNIX CURSES utility for presenting Text Based GUI, it will also show you a list of all bookmarks, and you can select the one you would like to jump to.

HyperJump is almost pure BASH. I originally intended it to be 3 separate bash scripts, but that proved to be problematic. There is no simple mechanism by which you can change the working directory of one bash session from another one. So, when you run jj, if it starts in a separate bash instance, it will not be able to change the working directory of the shell it was lunched from. The solution was to write the script as a function, add it to bashrc, and have it execute inside the active bash instance directly. In any case, having all 3 scripts and the supporting logic collected in one file actually turned out to be more elegant.

Installing HyperJump

Download hyperjump script and place it somewhere on your system, such as ~/bin/hyperjump. Add the following line to your .profile, .bashrc or .zshrc file:

source /location/of/hyperjump

Optional: To get the list of all the Bookmarks in a nice looking menu window, you need a unix utility called dialog. You can install it via yum, apt-get, homebrew, ports, and others like so:

# On CentOS or Another RedHat Derivative
sudo yum install dialog

# On Ubuntu or Another Debian Derivative
sudo apt-get install dialog

# On OS X
brew install dialog
sudo port install dialog

Using HyperJump

HyperJump consists of 3 command line commands (functions).

All of the commands have autocomplete. Both jj and jf will autocomplete with nicknames of bookmarked locations. The jr command will autocomplete with the basename of the current directory. After the first argument, jj will autocomplete with list of available system commands (programs).

Examples:

# Remember current directory
jr
jr MyDir

# Forget current directory
jf

# Forget another directory
jf AnotherDir

# Jump to a Directory
jj
jj MyDir
 
# Jump to a directory and open the directory in another program(s)
jj MyDir open
jj MyDir open subl tm 

Final Thoughts

I hope HyperJump can be as useful to others as it is to me. HyperJump is released under the MIT License. Use it, love it, fork it, make changes, send pull requests. Enjoy!

Source Code

# Set Up the Database if it does not exist yet
function _hyperjumpdatabase() {
    local dbdir="$HOME"/.local/lib
    local db="$dbdir"/hyperjumpdb
    local db_old="$HOME"/.hyperjumpdb
    if [[ ! -f "$db" ]]; then
        # Create ~/.local/lib directory if it fors not exists
        if [[ ! -d "$dbdir" ]]; then
            mkdir -p "$dbdir" >> /dev/null
        fi
        # If old DB exists, move it to the new location
        if [[ -f "$db_old" ]]; then
            mv "$db_old" "$db"
        else
            touch "$db"
            echo "home:$HOME" >> "$db"
        fi
    fi
    echo "$db"
}

# Jump Remamber - Adds a jump to the database
function jr() {
    local db=$(_hyperjumpdatabase)
    local wd=$(pwd)
    local nick=${wd##*/}
    local nick=${nick// /_}

    if grep -q "$wd$" "$db"; then
        echo "This directory is already added to the database. Run 'jf' to forget it."
    else
        if [[ -z "$1" ]]; then
            echo "We need a nickname for this directory. Use jr <name> or specify it now."
            read -p "[C]ancel, [U]se \"$nick\", Enter [N]ickname [C/U/N]: " -n 1 -e choice
            case "$choice" in
                "U" | "u" )
                    echo "We are going to use $nick as the nickname for this directory."
                    ;;
                "N" | "n" )
                    local nick=
                    while [[ "$nick" = "" ]]; do
                       read -p "Enter a Nickname for this Directory and Press [Enter]: " -e nick
                    done
                    ;;
                * )
                    echo "Nothing was added to the database. Quitting."
                    return
                    ;;
            esac
        else
            local nick=$1
        fi

        local nick=${nick// /_}

        if grep -q "^$nick:" "$db"; then
            echo "Oops, the nickname '$nick' is already in use :( Try again...."
        else
            echo "$nick:$wd" >> "$db"
            echo "Added $wd with Nickname \"$nick\" to the database."
        fi
    fi
}

# Jump Forget - Removes a jump location from the database
function jf() {
    local db=$(_hyperjumpdatabase)

    if [[ -z "$1" ]]; then
        local wd=$(pwd)
        if grep -q ":$wd$" "$db"; then
            local dbline=$(grep ":$wd$" "$db" | head -n 1)
            local nickname=${dbline%:*}
        else
            echo "This directory is not in the database!"
            return
        fi
    else
        local nickname=$1
        if grep -q "^$nickname:" "$db"; then
            local line=$(grep "^$nickname:" "$db" | head -n 1)
            local wd=${line#*:}
        else
            echo "This nickname is not in the database!"
            return
        fi
    fi

    echo "$nickname : $wd"
    read -p "Forget It? [Y/N]: " -n 1 -e choice
    case "$choice" in
        "Y" | "y" )
            local tempfile=$(mktemp -t "XXXjumpdb")
            grep -v ":$wd$" "$db" > "$tempfile"
            cat "$tempfile" > "$db"
            rm "$tempfile"
            echo "$nickname is forgotten!"
            ;;
        * )
            echo "Nothing was deleted from the database. Quitting."
            return
            ;;
    esac
}

# Jump to Nickname - Shows the Dialog Menu with all of the jumps in the db
function jj() {
    local db=$(_hyperjumpdatabase)
    local foundDialog=0

    # If no name on the prompt, than pop up the dialog, else use $1
    if [[ -z "$1" ]]; then
        if ! type dialog > /dev/null 2>&1; then
            # Dialog Utility NOT found, so show alternative
            echo Dialog utility NOT found. Install Dialog to get a nice menu of jump locations.
            echo List of Saved Locations:
            while read line
            do
                printf "    %-25s %s \n" "${line%:*}" "${line#*:}"
            done < <(cat "$db")
        else
            local foundDialog=1
            local list=""
            while read line
            do
                line="'${line%:*}' '${line#*:}' "
                list+=$line
            done < <(cat "$db")

            if [[ "$list" == "" ]]; then
                echo The HyperJump Database is Empty. Bookmark a directory with the jr command to get started.
            else
                local cmd="dialog --menu 'Where do you want to jump to?' 22 76 16 $list"
                local choice=$(eval "$cmd" 2>&1 >/dev/tty)
                clear
            fi
        fi
    else
        local choice=$1
    fi

    # Check if the Jump is legit, and jump
    if grep -q "^$choice:" "$db"; then
        local line=$(grep "^$choice:" "$db" | head -n 1)
        local target=${line#*:}
        echo Navigating to "$choice" at "$target"
        cd "$target"
        # Run Additional Commands If Specified
        local param
        for param in ${@:2}
        do
            if [[ ! -z "$param" && "$param" != "" ]]; then
                local cmd="$param ./"
                echo Running \"$cmd\" inside $choice
                eval "$cmd"
            fi
        done
    else
        if [[ -z "$choice" ]]; then
            # Do not show the message if Dialog was not found
            if [[ "$foundDialog" -eq 1 ]]; then
                echo "Jump Cancelled"
            fi
        else
            echo "Jump Nickname isn't in the Database"
        fi
    fi
}

# Autocomplete for Jump to Nickname
_jj() {
    local db=$(_hyperjumpdatabase)

    local list=""
    while read line
    do
        local list+=" ${line%:*}"
    done < <(cat "$db")

    local cur=${COMP_WORDS[COMP_CWORD]}
    if [[ "$COMP_CWORD" -eq 1 ]]; then
        COMPREPLY=( $(compgen -W "$list" -- "$cur") )
    else
        COMPREPLY=( $(compgen -c "$cur") )
    fi
}

_jr() {
    local wd=$(pwd)
    local nick=${wd##*/}
    local nick=${nick// /_}
    local cur=${COMP_WORDS[COMP_CWORD]}
    COMPREPLY=( $(compgen -W "$nick" -- "$cur") )
}

if [[ -n "${ZSH_VERSION-}" ]]; then
    autoload -U +X bashcompinit && bashcompinit
fi

complete -F _jj jj
complete -F _jj jf
complete -F _jr jr
Mounty for OSX      Color Themes in Swift on iOS


Comments