Skip to content

homerours/jumper

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Jumper

Jumper is a command-line program that helps you jumping to the directories and files that you frequently visit. It relies on fzf for fuzzy-finding and is heavily inspired by z.

It differentiates itself from the plethora of similar tools on the following points:

  • Efficient ranking mechanism which combines the "frecency" of the match (as z does) and the accuracy of the match (as fzf or fzy do). This allows to find files/folders accurately in very few keystrokes. More details here.
  • It is not restricted to folders. It allows to quickly navigate files, or anything you want (you can easily create and query a new custom database).
  • It can be run in "interactive mode".
  • Written in C, for speed and portability.

Usage - Installation - Vim-Neovim

Usage

  • Use z <something> to jump to the most frequent/recent directories matching <something>.
  • Use zf <something> to open (in $EDITOR) the most frequent/recent file matching <something>.
  • Use Ctrl+Y to fuzzy-find directories matching a query interactively (fzf required).
  • Use Ctrl+U to fuzzy-find files matching a query interactively (fzf required).

All these mappings can be updated, see Configuration below.

Ranking mechanism

The paths that match a given query are ranked based on

  • the frecency of the path: how often / recently has this path been visited ?
  • the accuracy of the query: how well does the query match the path ?

The ranking of a path at time $t$ is based on the following score

$$\text{score}(\text{query}, \text{path}) = \text{frecency}(t, \text{path}) + \beta \times \text{accuracy}(\text{query}, \text{path})$$

where $\beta = 1.0$ by default, but can be updated with the flag -b <value>. More details about the scoring mechanism are given here.

Concept

jumper operates on files whose lines are in the format <path>|<number-of-visits>|<timestamp-of-last-visit>. Such files are typically used to record accesses to files/directories. Given such a file, the command

jumper -f <database-file> -n N <query>

returns the top N entries of the <database-file> (this will typically be ~/.jfolders or ~/.jfiles) that match <query>. Adding the -c flag colors the matched substring. The command

jumper -f <database-file> -a <path>

adds the <path> to the <database-file>, or updates its record (i.e. updates the visits count and timestamp) if already present. From these two main functions, the shell scripts shell/jumper.{bash,zsh,fish} define various functions/mappings (see Usage above) allowing to quickly jump around

  • Folders: Folders' visits are recorded in the file ${__JUMPER_FOLDERS} using a shell pre-command.
  • Files: Files open are recorded in the file ${__JUMPER_FILES} by making Vim run jumper -f ${__JUMPER_FILES} -a <current-file> each time a file is opened. This can be adapted to other editors.

Search syntax

By default jumper uses a simpler version of fzf's "extended search-mode". One can search for multiple tokens separated by spaces, which have to be found in the same order in order to have a match. The full fzf-syntax is not implemented yet, only the following token are implemented.

Token Match type Description
dotfvi fuzzy-match Items that match dotfvi
'wild exact-match (quoted) Items that include wild
^music prefix-exact-match Items that start with music
.lua$ suffix-exact-match Items that end with .lua

The syntax mode can be changed to fuzzy (use only fuzzy-matches, the characters ^, $ and ' are interpreted as standard characters) or exact (exact matches only), with the --syntax flag.

Case sensitivity

By default, matches are "case-semi-sensitive". This means that a lower case character a can match both a and A, but an upper case character A can only match A. Matches can be set to be case-sensitive or case-insensitive using the flags -S and -I.

Installation

Requirements

  • A C compiler for installation. The makefile uses gcc.
  • Bash (>=4.0), Zsh or Fish.
  • fzf. This is not mandatory, but you will need it for running queries interactively.

Installation process

Run

git clone https://github.com/homerours/jumper
cd jumper
make install

to compile and move the jumper binary to /usr/local/bin. Then add

source <path-to>/shell/jumper.{bash, zsh, or fish depending on the shell you use}

to your .bashrc, .zshrc or .config/fish/config.fish to get access to jumper's functions.

Tip

If you were already using z, you can cp ~/.z ~/.jfolders to export your database to Jumper.

In order to keep track of the visited files, the function jumper -f $__JUMPER_FILES -a <file> has to be called each time a file <file> is opened. This can be done automatically in Vim/Neovim, see next section. For other programs, you may want to use aliases (better solutions exist, using for instance "hooks" in emacs)

function myeditor() {
   jumper -f $__JUMPER_FILES -a "$1" 
   myeditor $1
}

Configuration

One typically only needs to add source <path-to>/shell/jumper.{bash, zsh, or fish} in ones .bashrc, .zshrc or .config/fish/config.fish. However, the default keybindings, previewers and "database-files" can still be configured if desired. Here is a sample configuration (for bash)

# Change default folders/files database-files (defaults are ~/.jfolders and ~/.jfiles):
export __JUMPER_FOLDERS='/path/to/custom/database_for_folders'
export __JUMPER_FILES='/path/to/custom/database_for_files'

# Update jumper's options
# Default: '-cH -n 500' (colors the matches, replace $HOME with ~/ and print only the top 500 entries, see all options by running 'jumper --help')
__JUMPER_FLAGS='-c -n 1000 --syntax=fuzzy --case-insensitive --beta=0.5'

# FZF options for interactive search
# Default: --height=70% --layout=reverse --keep-right --preview-window=hidden --ansi
__JUMPER_FZF_OPTS='--height=30 --keep-right --preview-window=hidden --ansi'

# Change the default binding (ctrl-p) to toggle preview:
__JUMPER_TOGGLE_PREVIEW='ctrl-o'

# Change default files' previewer (default: bat or cat):
__JUMPER_FZF_FILES_PREVIEW='head -n 30'

# Change default folders' previewer (default: 'ls -1UpC --color=always'):
__JUMPER_FZF_FOLDERS_PREVIEW='ls -lah --color=always'

# IMPORTANT: this has to be after the configuration above:
source /path/to/jumper/shell/jumper.bash

# Change default (ctrl-y and ctrl-u) bindings:
bind -x '"\C-d": jumper-find-dir'
bind -x '"\C-f": jumper-find-file'

Database maintenance

Use the function _jumper_clean to remove from the databases the files and directories that do not exist anymore. To clean the files' or folders' database only, use __jumper_clean_files_db or __jumper_clean_folders_db.

This cleaning can be done automatically by setting the variable __JUMPER_CLEAN_FREQ to some integer value N. In such case, the function _jumper_clean will be called on average every N command run in the terminal.

For more advanced/custom maintenance, the files ~/.jfolders and ~/.jfiles can be edited directly.

Performance

Querying and updating jumper's database is very fast and shouldn't cause any latency. On an old 2012 laptop, these operations (over a database with 1000 entries) run in about 4ms:

$ time for i in {1..100}; do jumper -f ~/.jfolders hello > /dev/null; done
real    0m0.432s
user    0m0.165s
sys     0m0.198s
$ time for i in {1..100}; do jumper -f ~/.jfolders -a test; done
real    0m0.383s
user    0m0.118s
sys     0m0.209s

For comparison:

$ time for i in {1..100}; do wc -l ~/.jfolders > /dev/null; done
real    0m0.357s
user    0m0.117s
sys     0m0.233s

Vim-Neovim

Jumper can be used in Vim and Neovim. Depending on your configuration, you can either use it

  • without any plugin, see below. However, you won't be able to do run queries interactively.
  • with the jumper.nvim plugin (prefered). This uses either Telescope or fzf-lua as backend for the UI.
  • with the jumper.vim plugin (works for both Vim/Neovim). This uses fzf "native" plugin as UI.

Without any plugins

We describe below how to use it without plugins. This only allows to use Z and Zf commands. First, you have to keep track of the files you open by adding to your .vimrc/init.lua

autocmd BufReadPre,BufNewFile *   silent execute '!jumper -f ${__JUMPER_FILES} -a ' .. expand('%:p')

or, if you are using Neovim's Lua api,

vim.api.nvim_create_autocmd({ "BufNewFile", "BufReadPre" }, {
    pattern = { "*" },
    callback = function(ev)
        local filename = vim.api.nvim_buf_get_name(ev.buf)
        -- do not log .git files, and buffers opened by plugins (which often contain some ':')
        if not (string.find(filename, "/.git") or string.find(filename, ":")) then
            vim.fn.system({ "jumper", "-f", os.getenv("__JUMPER_FILES"),  "-a", filename })
        end
    end
})

Then in order to quickly jumper to folders and files, add

command! -nargs=+ Z :cd `jumper -f ${__JUMPER_FOLDERS} -n 1 '<args>'`
command! -nargs=+ Zf :edit `jumper -f ${__JUMPER_FILES} -n 1 '<args>'`

to your .vimrc to then change directory with :Z <query> or open files with :Zf <query>.

Combine it with other tools

You can for instance define a function

fu() {
    RG_PREFIX="jumper -f ${__JUMPER_FILES} '' | xargs rg -i --column --line-number --color=always "
    fzf --ansi --disabled --query '' \
    --bind "start:reload:$RG_PREFIX {q}" \
    --bind "change:reload:sleep 0.1; $RG_PREFIX {q} || true" \
    --delimiter : \
    --preview 'bat --color=always {1} --highlight-line {2}' \
    --preview-window 'up,60%,border-bottom,+{2}+3/3,~3' \
    --bind 'enter:become(nvim {1} +{2})'
}

which allows to "live-grep" (using here ripgrep) the files of jumper's database.