skywind3000 / z.lua

:zap: A new cd command that helps you navigate faster by learning your habits.
MIT License
2.94k stars 137 forks source link
autojump bash cd fasd fish fishshell fuzzy fzf j jump plugin powershell shell z zsh zsh-plugin

z.lua

A command line tool which helps you navigate faster by learning your habits :zap:

An alternative to z.sh with windows and posix shells support and various improvements.

README in Chinese | 中文文档

Description

z.lua is a faster way to navigate your filesystem. It tracks your most used directories, based on 'frecency'. After a short learning phase, z will take you to the most 'frecent' directory that matches ALL of the regexes given on the command line, in order.

For example, z foo bar would match /foo/bar but not /bar/foo.

Reputation

From people using z.lua:

Features

Examples

z foo        # cd to most frecent dir matching foo
z foo bar    # cd to most frecent dir matching foo and bar
z -r foo     # cd to the highest ranked dir matching foo
z -t foo     # cd to most recently accessed dir matching foo
z -l foo     # list matches instead of cd
z -c foo     # restrict matches to subdirs of $PWD
z -e foo     # echo the best match, don't cd
z -i foo     # cd with interactive selection
z -I foo     # cd with interactive selection using fzf
z -b foo     # cd to the parent directory starting with foo
z -b foo bar # replace foo with bar in cwd and cd there

Install

Options

Aging

The rank of directories maintained by z.lua undergoes aging based on a simple formula. The rank of each entry is incremented every time it is accessed. When the sum of ranks is over 5000 ($_ZL_MAXAGE), all ranks are multiplied by 0.9. Entries with a rank lower than 1 are forgotten.

Frecency

Frecency is a portmanteau of 'recent' and 'frequency'. It is a weighted rank that depends on how often and how recently something occurred. As far as I know, Mozilla came up with the term.

To z.lua, a directory that has low ranking but has been accessed recently will quickly have higher rank than a directory accessed frequently a long time ago. Frecency is determined at runtime.

Default Matching

By default, z.lua uses default matching algorithm similar to the original z.sh. Paths must be match all of the regexes in order.

Enhanced Matching

Enhanced matching can be enabled by exporting the environment:

export _ZL_MATCH_MODE=1

Or, append a enhanced after --init xxx:

eval "$(lua /path/to/z.lua --init bash enhanced)"

For a given set of queries (the set of command-line arguments passed to z.lua), a path is a match if and only if:

  1. Queries match the path in order (same as default method).
  2. The last query matches the last segment of the path.

If no match is found, it will fall back to default matching method.

The default matching method is designed to be compatible with original z.sh, but the enhanced matching method is much more handy and exclusive to z.lua.

Add Once

By default, z.lua will add current directory to database each time before display command prompt (correspond with z.sh). But there is an option to allow z.lua add path only if current working directory changed.

To enable this, you can set $_ZL_ADD_ONCE to 1 before init z.lua. Or you can initialize z.lua on linux like this:

eval "$(lua /path/to/z.lua --init bash once)"
eval "$(lua /path/to/z.lua --init zsh once)"
lua /path/to/z.lua --init fish once | source

With add once mode off (default), z.lua will consider the time you spent in the directory (like z.sh). When this mode is on, consider the times you accessed the directory (like autojump), and that could be much faster on slow hardware.

Interactive Selection

When there are multiple matches found, using z -i will display a list:

$ z -i soft
3:  0.25        /home/data/software
2:  3.75        /home/skywind/tmp/comma/software
1:  21          /home/skywind/software
> {CURSOR}

And then you can input the number and choose where to go before actual cd. eg. input 3 to cd to /home/data/software. And if you just press ENTER and input nothing, it will just quit and stay where you were.

NOTE: for fish shell, this feature requires fish 2.7.0 or above.

FZF Supports

From version 1.1.0, a new option "-I" will allow you to use fzf to select when there are multiple matches.

When we use "z -I vim",12 paths contains keyword "vim" has been matched and ordered by their frecent value, the higher frecent comes with the higher rank. Then without cd to the highest ranked path, z.lua passes all the candidates to fzf.

Now you can input some space separated keywords (no order required) or use CTRL+J/CTRL+K (same as UP/DOWN) to select where you want to go, or ESC / CTRL+D/G to give up.

Of course, you can always give more keywords to z command to match your destination precisely. "z -I" is similar to "z -i", but use fzf. Both "-i" and "-I" provide you another way for path navigation.

Usually, z -I can be aliased to zf (z + fuzzy finder) for convenience. If there are only one path matched, z -I will jump to it directly, fzf will only be invoked for multiple matches. "z -I ." or "zf ." can be used to use fzf select from entire database.

For more information about this, please visit wiki - effective with fzf.

NOTE: For fish shell, this feature requires fish 2.7.0 or above. You can specify fzf executable in $_ZL_FZF environment variable, "fzf" will be called by default.

Jump Backwards

New option "-b" can quickly go back to a specific parent directory in bash instead of typing "cd ../../.." redundantly.

Let's start by aliasing z -b to zb:

# go all the way up to the project root (in this case, the one that has .git in it)
~/github/lorem/src/public$ zb
  => cd ~/github/lorem

# cd into to the first parent directory named g*
~/github/vimium/src/public$ zb g
  => cd ~/github

# goto the site directory quickly
~/github/demo/src/org/main/site/utils/file/reader/whatever$ zb si
  => cd ~/github/demo/src/org/main/site

# substitute jekyll with ghost
~/github/jekyll/test$ zb jekyll ghost
  => cd ~/github/ghost/test

# same as above, but fuzzy
~/github/jekyll/test$ zb jek gh
  => z ~/github/ gh /test
    => cd ~/github/ghost/test  # Assuming that's the most frecent match

Backward jumping can also be used with $_ZL_ECHO option (echo $PWD after cd), which makes it possible to combine them with other tools without actually changing the working directory (eg. ls `zb git`).

Environment variable $_ZL_ROOT_MARKERS is a comma separated list for project root locating, and can be redefined as:

export _ZL_ROOT_MARKERS=".git,.svn,.hg,.root,package.json"

If you want zb jump back to a parent directory contains a .root or package.json in it.

Bonus: zb .. equals to cd .., zb ... equals to cd ../.. and zb .... equals to cd ../../.., and so on. Finally, zb ..20 equals to cd (..)x20.

Bonus: try z -b -i and z -b -I and you can alias them to zbi and zbf.

Completion

For zsh/fish, completion can be triggered by z foo<tab>. and a list of candidates will display in zsh / fish:

Press <tab> again, you can select your destination in a visualized way.

Bash is not as powerful as zsh/fish, so we introduced fzf-completion for bash, initialize your z.lua and append fzf keyword after --init:

eval "$(lua /path/to/z.lua --init bash enhanced once echo fzf)"

If you want use fzf completion in zsh, initalize your z.lua and append fzf keyword after --init:

eval "$(lua /path/to/z.lua --init zsh enhanced once echo fzf)"

Then press <tab> after z xxx:

With the help of fzf, completion in bash is much easier now.

z.lua can cooperate with fz for better completion result in both bash and zsh, for more information see FAQ.

NOTE: To enable this, command fzf must be found in $PATH before initialization.

Most Recently Accessed Path

z.lua provides a fast way to visit MRU directories without typing any keyword. That is dirstack, which records recently visited paths and can be manipulated by z -, z -- and z -{num}:

# display current dir stack
$ z --    
 0  /home/skywind/work/match/memory-match
 1  /home/skywind/.local/etc
 2  /home/skywind/software/vifm-0.9.1
 3  /home/skywind/work
 4  /home/skywind/work/match

# cd to the 2nd directory in the stack
$ z -2
  => cd /home/skywind/software/vifm-0.9.1

# popup stacktop (cd to previous directory), same as "z -0"
$ z -
  => cd -

The dirstack is calculated from z.lua's database, and has no dependency on shells or systems. You will not lost records after re-login, and history can be shared across shells and sessions.

There is another way to access MRU directories interactively by utilizing parameter -I (fzf) and -t (sort by time):

alias zh='z -I -t .'

The new alias zh (jump to history) is very easy to input:

The first column indicates how many seconds ago you have visited, and the second column is the path name. With zh, you can type some character to use string matching in fzf, or use <Up>/<Down> (as well as CTRL+j/k) to move the selector (red >) up and down.

At last, press <enter> to accept or <ESC> to give up.

Remember to enable the enhanced matching algorithm, the current working directory can be skipped with it.

Ranger integration

To add a :z command to the ranger file manager, copy the ranger_zlua.py file to ~/.config/ranger/plugins/. You can then use :z foo, :z -b foo, etc. from ranger. Use :z -h for help.

To define additional commands (:zb for example) in ranger, you can put alias zb z -b into ~/.config/ranger/rc.conf.

Tips

Recommended aliases you may find useful:

alias zz='z -c'      # restrict matches to subdirs of $PWD
alias zi='z -i'      # cd with interactive selection
alias zf='z -I'      # use fzf to select in multiple matches
alias zb='z -b'      # quickly cd to the parent directory

Import data from z.sh:

cat ~/.z >> ~/.zlua

Import data from autojump:

FN="$HOME/.local/share/autojump/autojump.txt"
awk -F '\t' '{print $2 "|" $1 "|" 0}' $FN >> ~/.zlua

Don't forget to read the Frequently Asked Questions.

Benchmark

The slowest part is adding path to history data file. It will run every time when you press enter (installed in $PROMPT_COMMAND). So I profile it on my NAS:

$ time autojump --add /tmp
real    0m0.352s
user    0m0.077s
sys     0m0.185s

$ time fasd -A /tmp
real    0m0.618s
user    0m0.076s
sys     0m0.242s

$ time _z --add /tmp
real    0m0.194s
user    0m0.046s
sys     0m0.154s

$ time _zlua --add /tmp
real    0m0.052s
user    0m0.015s
sys     0m0.030s

As you see, z.lua is the fastest one and requires less resource.

Native Module

z.lua is fast enough for most case, the path tracking action will be triggered each time when you change your current directory.

So I still recommend the pure lua script for portability and flexibility, but for someone who really care about 10ms or 1ms things, this module can help them to gain the ultimate speed.

Average performance:

Name czmod z.lua
Update Time 1.6ms 13.2ms
Query Time 1.5ms 9.8ms

History

Help

This project needs help for the tasks below:

Thanks

And many others.

License

Licensed under MIT license.