From 090516588918c1924698b09e660357aaf3f04a71 Mon Sep 17 00:00:00 2001 From: Ilya Grigoriev Date: Sat, 27 Aug 2022 21:12:16 -0700 Subject: [PATCH 1/3] Allow `z -b` to do fuzzy matching If `z -b foo bar` finds no exact match in the path to the current directory, it tries a "fuzzy" substitution with a frecent directory. For example, if we are in `~/github/jekyll/test` and run `z -b jek gh`, it will try to substitute the entire `jekyll` path component with `gh`. The result will be equivalent to running `z ~/github gh test`. --- README.md | 6 ++++++ z.lua | 17 ++++++++++++++--- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3e8a35a..e3b0bb0 100644 --- a/README.md +++ b/README.md @@ -319,6 +319,7 @@ New option `"-b"` can quickly go back to a specific parent directory in bash ins - **(No argument)**: `cd` into the project root, the project root the nearest parent directory with `.git`/`.hg`/`.svn` in it. - **(One argument)**: `cd` into the closest parent starting with keyword, if not find, go to the parent containing keyword. - **(Two arguments)**: replace the first value with the second one (in the current path). + If simple substitution does not work, falls back to fuzzily replacing path components. Let's start by aliasing `z -b` to `zb`: @@ -338,6 +339,11 @@ Let's start by aliasing `z -b` to `zb`: # 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` ``). diff --git a/z.lua b/z.lua index f47fc1d..e9dc4ff 100755 --- a/z.lua +++ b/z.lua @@ -1759,7 +1759,7 @@ function cd_backward(args, options, pwd) end return nil end - else + elseif nargs == 2 then local test = windows and pwd:gsub('\\', '/') or pwd local src = args[1] local dst = args[2] @@ -1770,10 +1770,21 @@ function cd_backward(args, options, pwd) if not start then return pwd end + local lhs = pwd:sub(1, start - 1) local rhs = pwd:sub(ends + 1) - return lhs .. dst .. rhs + local newpath = lhs .. dst .. rhs + if os.path.isdir(newpath) then + return newpath + end + + lhs = lhs:gsub("[^/]*$", "") + rhs = rhs:gsub("^[^/]*", "") + return z_cd({lhs, dst, rhs}) end + + io.stderr:write("Error: " .. Z_CMD .. " -b takes at most 2 arguments.\n") + return nil end @@ -1922,7 +1933,7 @@ function main(argv) if options['--cd'] or options['-e'] then local path = '' if options['-b'] then - if Z_INTERACTIVE == 0 then + if #args > 0 or Z_INTERACTIVE == 0 then path = cd_backward(args, options) else path = cd_breadcrumbs('', Z_INTERACTIVE) From 3894a6b936c08b266cc74370240ab9e546dacc2e Mon Sep 17 00:00:00 2001 From: Ilya Grigoriev Date: Sat, 27 Aug 2022 21:18:36 -0700 Subject: [PATCH 2/3] Reduce indentation and add comments --- z.lua | 76 ++++++++++++++++++++++++++++++----------------------------- 1 file changed, 39 insertions(+), 37 deletions(-) diff --git a/z.lua b/z.lua index e9dc4ff..256c80f 100755 --- a/z.lua +++ b/z.lua @@ -1719,46 +1719,44 @@ function cd_backward(args, options, pwd) local pwd = (pwd ~= nil) and pwd or os.pwd() if nargs == 0 then return find_vcs_root(pwd) - elseif nargs == 1 then - if args[1]:sub(1, 2) == '..' then - local size = args[1]:len() - 1 - if args[1]:match('^%.%.+$') then - size = args[1]:len() - 1 - elseif args[1]:match('^%.%.%d+$') then - size = tonumber(args[1]:sub(3)) - else - return nil - end - local path = pwd - for index = 1, size do - path = os.path.join(path, '..') - end - return os.path.normpath(path) + elseif nargs == 1 and args[1]:sub(1, 2) == '..' then + local size = args[1]:len() - 1 + if args[1]:match('^%.%.+$') then + size = args[1]:len() - 1 + elseif args[1]:match('^%.%.%d+$') then + size = tonumber(args[1]:sub(3)) else - pwd = os.path.split(pwd) - local test = windows and pwd:gsub('\\', '/') or pwd - local key = windows and args[1]:lower() or args[1] - if not key:match('%u') then - test = test:lower() - end - local pos, ends = test:rfind('/' .. key) - if pos then - ends = test:find('/', pos + key:len() + 1, true) - ends = ends and ends or test:len() - return os.path.normpath(pwd:sub(1, ends)) - elseif windows and test:startswith(key) then - ends = test:find('/', key:len(), true) - ends = ends and ends or test:len() - return os.path.normpath(pwd:sub(1, ends)) - end - pos = test:rfind(key) - if pos then - ends = test:find('/', pos + key:len(), true) - ends = ends and ends or test:len() - return os.path.normpath(pwd:sub(1, ends)) - end return nil end + local path = pwd + for index = 1, size do + path = os.path.join(path, '..') + end + return os.path.normpath(path) + elseif nargs == 1 then + pwd = os.path.split(pwd) + local test = windows and pwd:gsub('\\', '/') or pwd + local key = windows and args[1]:lower() or args[1] + if not key:match('%u') then + test = test:lower() + end + local pos, ends = test:rfind('/' .. key) + if pos then + ends = test:find('/', pos + key:len() + 1, true) + ends = ends and ends or test:len() + return os.path.normpath(pwd:sub(1, ends)) + elseif windows and test:startswith(key) then + ends = test:find('/', key:len(), true) + ends = ends and ends or test:len() + return os.path.normpath(pwd:sub(1, ends)) + end + pos = test:rfind(key) + if pos then + ends = test:find('/', pos + key:len(), true) + ends = ends and ends or test:len() + return os.path.normpath(pwd:sub(1, ends)) + end + return nil elseif nargs == 2 then local test = windows and pwd:gsub('\\', '/') or pwd local src = args[1] @@ -1778,9 +1776,13 @@ function cd_backward(args, options, pwd) return newpath end + -- Get rid of the entire path component that matched `src`. lhs = lhs:gsub("[^/]*$", "") rhs = rhs:gsub("^[^/]*", "") return z_cd({lhs, dst, rhs}) + -- In the future, it would make sense to have `z -b -c from to to2` + -- to z_cd({lhs, dst[1], dst[2]}). Without `-c`, we probably still + -- want to support only 2 argumets. end io.stderr:write("Error: " .. Z_CMD .. " -b takes at most 2 arguments.\n") From 3703c5a0eaa108728806746027a945f64846109f Mon Sep 17 00:00:00 2001 From: Ilya Grigoriev Date: Mon, 22 Aug 2022 18:24:10 -0700 Subject: [PATCH 3/3] Put `z -b foo bar` in command summary, add regex docs --- README.md | 36 +++++++++++++++++++++--------------- z.lua | 48 +++++++++++++++++++++++++----------------------- 2 files changed, 46 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index e3b0bb0..c4e7b6e 100644 --- a/README.md +++ b/README.md @@ -42,16 +42,17 @@ From people using z.lua: ## Examples ```bash -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 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 ``` @@ -176,16 +177,12 @@ To z.lua, a directory that has low ranking but has been accessed recently will q ## 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. +By default, `z.lua` uses default matching algorithm similar to the original `z.sh`. Paths must be match all of the regexes in order. - cd to a directory contains foo: z foo -- cd to a directory ends with foo: - - z foo$ - - use multiple arguments: Assuming the following database: @@ -195,6 +192,15 @@ By default, z.lua uses default matching algorithm similar to the original z.sh. `"z in"` would cd into `/home/user/mail/inbox` as the higher weighted entry. However you can pass multiple arguments to z.lua to prefer a different entry. In the above example, `"z w in"` would then change directory to `/home/user/work/inbox`. +- use regexes: + + ```bash + z foo$ # cd to a directory ends with foo + z %d # cd to a directory that contains a digit + ``` + + Unlike `z.sh`, `z.lua` uses the [Lua regular expression syntax](https://www.lua.org/pil/20.2.html). + ## Enhanced Matching Enhanced matching can be enabled by exporting the environment: diff --git a/z.lua b/z.lua index 256c80f..64429e7 100755 --- a/z.lua +++ b/z.lua @@ -12,17 +12,18 @@ -- * compatible with lua 5.1, 5.2 and 5.3+ -- -- USE: --- * 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 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 -x path # remove path from history --- * 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 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 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 -x path # remove path from history +-- * 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 -- -- Bash Install: -- * put something like this in your .bashrc: @@ -46,7 +47,7 @@ -- -- Fish Shell Install: -- * put something like this in your config file: --- source (lua /path/to/z.lua --init fish | psub) +-- lua /path/to/z.lua --init fish | source -- -- Power Shell Install: -- * put something like this in your config file: @@ -2714,17 +2715,18 @@ end ----------------------------------------------------------------------- function z_help() local cmd = Z_CMD .. ' ' - print(cmd .. 'foo # cd to most frecent dir matching foo') - print(cmd .. 'foo bar # cd to most frecent dir matching foo and bar') - print(cmd .. '-r foo # cd to highest ranked dir matching foo') - print(cmd .. '-t foo # cd to most recently accessed dir matching foo') - print(cmd .. '-l foo # list matches instead of cd') - print(cmd .. '-c foo # restrict matches to subdirs of $PWD') - print(cmd .. '-e foo # echo the best match, don\'t cd') - print(cmd .. '-x path # remove path from history') - print(cmd .. '-i foo # cd with interactive selection') - print(cmd .. '-I foo # cd with interactive selection using fzf') - print(cmd .. '-b foo # cd to the parent directory starting with foo') + print(cmd .. 'foo # cd to most frecent dir matching foo') + print(cmd .. 'foo bar # cd to most frecent dir matching foo and bar') + print(cmd .. '-r foo # cd to highest ranked dir matching foo') + print(cmd .. '-t foo # cd to most recently accessed dir matching foo') + print(cmd .. '-l foo # list matches instead of cd') + print(cmd .. '-c foo # restrict matches to subdirs of $PWD') + print(cmd .. '-e foo # echo the best match, don\'t cd') + print(cmd .. '-x path # remove path from history') + print(cmd .. '-i foo # cd with interactive selection') + print(cmd .. '-I foo # cd with interactive selection using fzf') + print(cmd .. '-b foo # cd to the parent directory starting with foo') + print(cmd .. '-b foo bar # replace foo with bar in cwd and cd there') end