1
0
mirror of https://github.com/skywind3000/z.lua synced 2026-03-22 07:39:48 +00:00

81 Commits

Author SHA1 Message Date
skywind3000
3482460bd2 update version number 2025-05-24 23:05:00 +08:00
Linwei
bcb5599c12 Merge pull request #221 from brglng/feature/nushell
feat(nushell)!: improve nushell support for nushell v0.96+
2025-05-21 23:37:24 +08:00
Zhaosheng Pan
ef8767c5f0 feat(nushell)!: improve nushell support for nushell v0.96+ 2025-05-21 22:45:12 +08:00
skywind3000
23aeabedd9 update version number 2025-05-18 00:18:33 +08:00
Linwei
27dceb9899 Merge pull request #219 from chrisant996/allow_interactive_with_no_args
Fix when _ZL_MATCH_MODE=1.
2025-05-18 00:16:39 +08:00
Chris Antos
19b2d1747e Fix when _ZL_MATCH_MODE=1. 2025-05-17 04:29:40 -07:00
skywind3000
7c1c73af33 1. upgrade internal batch script
2. made _ZL_FZF_HEIGHT available on windows.
2025-05-17 13:57:37 +08:00
Linwei
b63a38b11a Merge pull request #218 from chrisant996/allow_interactive_with_no_args
Make `z -i` and `z -I` with no arguments be interactive.
2025-05-16 15:12:21 +08:00
Chris Antos
c524e28851 Make z -i and z -I with no arguments be interactive.
In skywind3000/z.lua#122 there's a note that z.lua intentionally doesn't
support `z -i` being interactive to maintain consistency with how z.sh
behaves with `z -i`.  But z.sh doesn't have a `-i` option, so I'm not
sure what is the intent.

This commit makes `z -i` and `z -I` go interactive even with no args.
2025-05-15 09:47:19 -07:00
Linwei
c3a182c6c9 Merge pull request #216 from chrisant996/fix_invalid_path_on_window
Fix invalid pathname in `z -I xyz` on Windows.
2025-05-15 22:14:44 +08:00
Chris Antos
771c72de5f Fix invalid pathname in z -I xyz on Windows.
The fzf interactive mode (`-I`) constructs a `tmpname` pathname and
tries to sanitize it by stripping all `\` characters.  But it missed
stripping all `:` characters, and the command printed this error output:

The filename, directory name, or volume label syntax is incorrect.

The `tmpname` looked like "c:\tmp\Temp\zlua_c:tmpTempsh2k0.txt", and
the embedded `:` caused the error output.

This commit adds `:` to the list of characters to be stripped (only on
Windows).
2025-05-14 16:49:50 -07:00
Linwei
4bbd0f1e58 Merge pull request #214 from russell7/patch-1
fix(readme): fix typo
2025-02-21 00:55:49 +08:00
洪运
db1fb1f256 fix(readme): fix typo 2025-02-20 22:46:25 +08:00
Linwei
19cb43d1ac Merge pull request #212 from ShalokShalom/patch-1
Remove Fig
2025-01-14 17:28:48 +08:00
ShalokShalom
787da9512c Remove Fig
Fig is no more (see https://app.fig.io/plugins/z.lua)
2025-01-14 10:23:57 +01:00
skywind3000
3875d774cb fixed nushell completion issue 2024-10-18 10:35:14 +08:00
skywind3000
dff590adc1 update docs 2024-10-18 03:09:39 +08:00
skywind3000
20c0494d44 update docs 2024-10-17 08:04:13 +08:00
skywind3000
67405d57e8 update gitignore 2024-10-16 00:31:50 +08:00
skywind3000
6e67a32520 fix: indentation consistency 2024-10-14 15:42:46 +08:00
Linwei
c334bc1ae5 Merge pull request #210 from brglng/feature/nushell
feat: add initial support for nushell (wip)
2024-10-10 15:17:31 +08:00
Zhaosheng Pan
0f6318ce4c fix(nushell): chdir failure 2024-10-09 14:59:05 +08:00
Zhaosheng Pan
3b55089ad8 improve README 2024-10-09 12:44:15 +08:00
Zhaosheng Pan
c58d31ec1d feat: add initial support for nushell 2024-10-08 18:11:32 +08:00
skywind3000
7c890c3645 Improve handling of the '--help' option in _zlua() function to improve
zsh compatibility.
2024-04-30 17:16:36 +08:00
skywind3000
7af012cc35 fixed: interpret _ZL_ZSH_NO_FZF in a wrong way. 2024-04-20 20:38:20 +08:00
skywind3000
019b2af475 add a return statement for nil case in os.interpreter() function. 2024-04-07 11:26:35 +08:00
Linwei
ef9a49d73d Merge pull request #158 from ibayramli/fig
Add Fig as an installation method to the README
2024-03-20 22:19:26 +08:00
skywind3000
7cd399b30d bloat version 2024-03-20 22:17:58 +08:00
Linwei
9112f0e9cc Merge pull request #189 from Arkaeriit/fix_pipes_in_filenames
Support '|' in path
2024-03-20 22:13:57 +08:00
Maxime Bouillot
cb4c9d3c8f Revert back to the old separator and improve db reading
A smarter reading function lets us support '|' in
filenames whilst still using it as the database
separator.
2024-03-20 14:38:04 +01:00
Maxime Bouillot
924f61a48b Change separator in data file
Using '|' as the data separator caused crashes if
a path with a '|' char in it was in the data file.
Changing that separator to a null byte ensures
that this kind of issue can never happen again as
the null byte can't be in a path.
2024-03-06 14:52:16 +01:00
skywind3000
30220996ca fix typo 2024-02-20 12:40:23 +08:00
skywind3000
d3ba781d4d Refactor the z.lua.plugin.zsh script to ensure proper initialization with or without FZF support 2024-02-20 11:43:00 +08:00
skywind3000
9a24fef334 Refactor logic to initialize fzf in zsh plugin to support a new condition where fzf can be disabled. 2024-02-19 11:29:04 +08:00
skywind3000
a9e034cb5c Update z.lua zsh plugin to allow customization of the fzf command used for fuzzy finding.
- Introduced new environment variable _ZL_ZSH_FZF to specify the fzf command
- Updated the eval statement to use the custom fzf command if provided by the user
2024-02-19 11:24:30 +08:00
Linwei
992134061a Merge pull request #196 from ilyagr/hyphens
Make z.lua try treating `-` as a normal character if there are no results
2023-12-11 01:11:14 +08:00
Ilya Grigoriev
b62784b66d Make z.lua try treating - as a normal character if there are no results
Previously, you needed to set `_ZL_HYPHEN=1` to treat `-` as a normal
character.  Otherwise, it was treated as a Lua regexp special character, see
https://www.lua.org/pil/20.2.html. Note that it is not super-useful to treat
`-` as a special character; it is almost the same as `*` and the difference
is not very useful in the context of fuzzy matching.

Now, if `_ZL_HYPHEN` is not set, z.lua first tries to treat it as a regexp
character. If there are no results (which is likely if the user does not know
it's a special character), z.lua tries again, treating `-` as a normal
character this time.

If `_ZL_HYPHEN=0` or `_ZL_HYPHEN=1`, z.lua will always treat `-` as
either a regex symbol or as a normal character (respectively).

Hopefully, this will make the FAQ at
https://github.com/skywind3000/z.lua/wiki/FAQ#how-to-input-a-hyphen---in-the-keyword-
unnecessary.  It took me weeks to look into the question of why `z
home-manager` refused to work and to find that FAQ. 

I only tested this briefly, but it seems to work.
2023-12-08 22:21:38 -08:00
Linwei
7f2bfcbfc1 Merge pull request #185 from kang8/feature/support-fzf-for-zsh-tab
ZSH: TAB complete using fzf
2023-07-13 11:02:56 +08:00
kang
86cb43af67 ZSH: TAB complete using fzf
Tested in Linux(ArchLinux) and MacOS

Close: #99, #77
2023-07-13 10:29:48 +08:00
skywind3000
71bae7fc0b refine window batch script initializing 2023-05-19 10:51:41 +08:00
Linwei
4d89b55363 Merge pull request #181 from chrisant996/clink_changes
Clink changes
2023-05-18 05:49:33 +08:00
Chris Antos
db1a863d40 Update z completions for Clink.
- Added description strings for the flags.
- Added dir completions for the `-x` flag.
2023-05-17 10:48:42 -07:00
Chris Antos
dff016528c z.lua was missing some lines for z.cmd.
The auto-init for z.cmd was missing a couple of lines.  The HomeDir
line, in particular, is important because it enables Lua to find the
z.lua script.
2023-05-17 10:48:05 -07:00
Linwei
6fe677aae4 Merge pull request #179 from Kosette/master
fix potential compatibilty issue for some fish shell versions and add…
2023-05-04 18:17:06 +08:00
Simsum
ba83bf67b2 fix potential compatibilty issue for some fish shell versions and add autocompletion for cmd options
1. `alias` command not available in some fishshell versions like 3.3.1. use fish-native `function` instead;
2. add autocompletion for cmd options benefitting from magic power of fish. just type `z -` and press tab. enjoy yourself.
2023-05-04 17:49:20 +08:00
skywind3000
0992ebf9f1 update z.lua 2022-09-29 15:48:52 +08:00
Linwei
83dfec0843 Merge pull request #170 from ilyagr/fuzzy-zb
Allow `z -b` to do fuzzy matching
2022-09-06 21:13:00 +08:00
Ilya Grigoriev
3703c5a0ea Put z -b foo bar in command summary, add regex docs 2022-08-27 22:00:52 -07:00
Ilya Grigoriev
3894a6b936 Reduce indentation and add comments 2022-08-27 22:00:52 -07:00
Ilya Grigoriev
0905165889 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`.
2022-08-27 22:00:52 -07:00
Linwei
3009e33176 Merge pull request #167 from FlTr/convert-string-to-float
Substitute comma with dot before conversion
2022-08-15 17:17:25 +08:00
FlTr
32786a0592 Substitute comma with dot before conversion 2022-08-15 09:37:36 +02:00
Linwei
47468a8588 Merge pull request #165 from ilyagr/ilya
Fixes and docs for the ranger plugin
2022-08-14 15:52:11 +08:00
Ilya Grigoriev
f347eaf1c8 Slight stylistic edits to ranger_zlua.py 2022-08-13 18:10:20 -07:00
Ilya Grigoriev
e11d2e2017 Fixes and docs for the ranger plugin
- Makes Ranger plugin use the standard `$ZLUA_LUAEXE` and `$ZLUA_SCRIPT`
  environment variables that `z.lua` itself uses.

- Better error handling; errors loading plugin no longer cause
  `ranger` to quit entirely.

- Briefly document the plugin
2022-08-13 16:14:50 -07:00
skywind3000
3ecc14747f prevent loading 2022-08-10 16:58:47 +08:00
skywind3000
632f722ee4 bloat version number 2022-07-21 22:12:50 +08:00
Linwei
9b9c0b82e6 Merge pull request #163 from jpcirrus/patch-1
Update fzf --inline-info option to --info=inline
2022-07-21 22:11:56 +08:00
John Purnell
08510fe6c6 Update fzf --inline-info option to --info=inline
fzf changed the option `--inline-info` to the equivalent `--info=inline` in release 0.19.0 with commit junegunn/fzf@d2fa470165 on 2019-11-14.
2022-07-21 15:30:21 +02:00
Ilkin Bayramli
4c05e0d911 Add Fig as an installation method to the README 2022-06-15 10:12:16 -07:00
Linwei
1ead694395 Merge pull request #157 from wtfzambo/patch-1
docs: added warning for powershell users
2022-06-14 20:35:45 +08:00
wtfzambo
ee6aa2c5ce docs: added warning for powershell users
`.zlua` file doesn't get created for Powershell users that use [Starship Prompt](https://starship.rs/) and place z.lua init command before starship init command in their `profile.ps1`.

Couldn't reproduce the bug in zsh. Didn't test other shells.
2022-06-14 06:16:58 -04:00
skywind3000
a8e92e0216 fix Monterey initialization issues 2022-03-27 21:51:29 +08:00
skywind3000
e2cce39ee4 bloat version to 1.8.14 2022-01-30 21:54:08 +08:00
skywind3000
3c88f8d8fa recognize %_ZL_CD% in cmd.exe 2022-01-30 10:55:40 +08:00
Linwei
786225005c Merge pull request #148 from phanirithvij/imgbot
[ImgBot] Optimize images
2022-01-29 18:37:34 +08:00
ImgBotApp
481c1774ed [ImgBot] Optimize images
*Total -- 161.14kb -> 53.31kb (66.92%)

/images/step1.png -- 27.95kb -> 7.53kb (73.06%)
/images/mru.png -- 38.12kb -> 10.42kb (72.68%)
/images/fzf.png -- 29.02kb -> 8.47kb (70.81%)
/images/complete-2.png -- 28.69kb -> 9.05kb (68.46%)
/images/complete-1.png -- 12.35kb -> 5.12kb (58.53%)
/images/step2.png -- 12.63kb -> 5.66kb (55.16%)
/images/step3.png -- 7.56kb -> 3.63kb (52.03%)
/images/step4.png -- 4.83kb -> 3.44kb (28.77%)

Signed-off-by: ImgBotApp <ImgBotHelp@gmail.com>
2022-01-29 07:03:13 +00:00
Linwei
35e1199eff Merge pull request #145 from doubleloop/zluaexec
Try luajit in zsh and fish, fix ZLUA_EXEC check
2021-11-14 01:40:55 +08:00
Jakub Łuczyński
99dbe7b677 Mention support for luajit in README 2021-11-13 11:29:00 +01:00
Jakub Łuczyński
c88746629b zsh plugin: fix ZLUA_EXEC check
Previous check required full path (even if value was available in PATH)
2021-11-13 10:33:41 +01:00
Jakub Łuczyński
336e95b05a Try luajit in zsh and fish 2021-11-13 10:32:59 +01:00
Linwei
a3d4f5db68 Merge pull request #138 from FlTr/path-exists-17
path valid if error code 17 by os.rename
2021-05-05 16:41:06 +08:00
TRF2SGM
a991162428 #136 path valid if error code 17 by os.rename 2021-05-03 12:13:34 +02:00
Linwei
8210c56414 Merge pull request #132 from ilyagr/patch-1
Improve Fish installation instructions
2021-03-08 23:16:40 +08:00
Ilya Grigoriev
a01b28a8d3 Fixup #2 2021-03-08 01:26:22 -08:00
Ilya Grigoriev
695533cee1 Fixup 2021-03-08 01:17:50 -08:00
Ilya Grigoriev
58425dad65 Improve Fish installation instructions
Most notably, this adds instructions of changing $_Z_CD.  I think many
`fish` users would find them useful.
See https://github.com/skywind3000/z.lua/pull/131 for more background.

This makes the installation instructions more conventional (`|source` 
instead of `psub`). This also deletes the second (equivalent) set of instructions.
If the warning in that sentence is important, we could add it back, but
it would apply to all the shells, wouldn't it?

Finally, some wording is improved.
2021-03-08 01:15:26 -08:00
Linwei
44a2489ba3 Merge pull request #131 from ilyagr/add_builtin_cd_doc
Add default to docs for $_ZL_CD
2021-03-04 22:28:18 +08:00
Ilya Grigoriev
6e01d4ad52 Add default to docs for $_ZL_CD
I don't know if the default of `builtin cd` is a good one, as opposed to simple `cd`. It took me quite a bit of debugging before I figured out why, on my `fish` shell, changing directories with `z.lua` messed up the `cdh` (cd history) command.

I think the best thing would be to change the default. However, if you have a good reason to stick with it, I thought that documenting this potentially unexpected behavior might help.
2021-03-04 00:56:51 -08:00
skywind3000
c3c15a3ca9 remove redundant comments 2021-02-15 05:13:24 +08:00
17 changed files with 497 additions and 178 deletions

1
.gitignore vendored
View File

@@ -39,3 +39,4 @@ luac.out
*.x86_64
*.hex
/.vscode/*

View File

@@ -84,6 +84,17 @@ z -b foo # 跳转到父目录中名称以 foo 开头的那一级
但是第二种方法需要记得在 z.lua 位置改变或者 lua 版本升级后需要重新生成。
- Nushell:
`env.nu` 中加入如下代码:
lua /path/to/z.lua --init nushell | save -f ~/.cache/zlua.nu
然后在 `config.nu` 中加入如下代码:
source ~/.cache/zlua.nu
alias z = _zlua
- Power Shell:
在你 Power Shell 的配置文件 `profile.ps1` 中放入下面语句:
@@ -112,7 +123,7 @@ z -b foo # 跳转到父目录中名称以 foo 开头的那一级
- 设置 `$_ZL_NO_PROMPT_COMMAND` 为 1 来跳过钩子函数初始化(方便自己处理)。
- 设置 `$_ZL_EXCLUDE_DIRS` 逗号分隔的路径列表,列表内的路径不会被收集。
- 设置 `$_ZL_ADD_ONCE` 为 '1' 时,仅在当前路径 `$PWD` 改变时才更新数据库。
- 设置 `$_ZL_MAXAGE` 来确定一个数据老化的值 (默认为 5000)。
- 设置 `$_ZL_MAXAGE` 来确定一个数据老化的值 (默认为 5000)。
- 设置 `$_ZL_CD` 用来指定你想用的 cd 命令,比如有人用 cd_func 。
- 设置 `$_ZL_ECHO` 为 1 可以在跳转后显示目标路径名称。
- 设置 `$_ZL_MATCH_MODE` 为 1 可以打开 “增强匹配模式”。
@@ -201,7 +212,7 @@ Frecency 是一个由 'recent' 和 'frequency' 组成的合成词,这个术语
cd foo
因此,在增强匹配算法中,你总可以 cd 命令一样使用 z 命令,而不必当心目标路径是否被记录过。
因此,在增强匹配算法中,你总可以 cd 命令一样使用 z 命令,而不必当心目标路径是否被记录过。
- 忽略当前路径:
@@ -214,7 +225,7 @@ Frecency 是一个由 'recent' 和 'frequency' 组成的合成词,这个术语
我当然可以每次使用`z env gems` 来精确指明,但是每当我输入 `z xxx` 我必然是想进行路径跳转的,而不是呆在原地,所以使用增强匹配模式,即便当前目录是最佳匹配,它也能懂得你想跳转的心思。
我最初实现 z.lua 时,只有一个和 z.sh 类似的默认匹配算法,在网友的建议下,我陆续学习了来自 fasd / autojump 中的优秀理念,并加以完善改进,成为如今集三家之长的 “增强匹配算法” ,给它取个昵称,叫做 “更懂你的匹配算法”。
我最初实现 z.lua 时,只有一个和 z.sh 类似的默认匹配算法,在网友的建议下,我陆续学习了来自 fasd / autojump 中的优秀理念,并加以完善改进,成为如今集三家之长的 “增强匹配算法” ,给它取个昵称,叫做 “更懂你的匹配算法”。
## Add once
@@ -325,7 +336,13 @@ zsh/fish 的补全系统是比较完善的,使用 `z foo<tab>` 就能触发补
eval "$(lua /path/to/z.lua --init bash enhanced once echo fzf)"
```
然后你在 bash 中,输入部分关键字后按 tab就能把匹配的路径列出来
如果你想在 zsh 中使用 fzf 补全,初始化时在 `--init` 后追加 `fzf` 关键字
```zsh
eval "$(lua /path/to/z.lua --init zsh enhanced once echo fzf)"
```
然后你在 bash/zsh 中,输入部分关键字后按 tab就能把匹配的路径列出来
![](images/complete-2.png)
@@ -438,15 +455,13 @@ sys 0m0.030s
描述力强,可以更好的实现核心功能,同时速度更快,纯 shell 开发的话,太多语句是通过子进程 shell 的模式运行,所以性能很差,而 Python 开发的话启动速度又太慢,我在 Cygwin/msys 下用 z.sh 都觉得很卡autojump/fasd 卡到不能用。
最关键的一点Lua 速度很快 200 KB 的可执行程序,启动速度是 python 的 3倍perl 的 2 倍,很多命令行工具 go/rust 写成,动不动就 2MB / 3MB他们都还没有完成加载lua 脚本可能都运行完了。
最关键的一点Lua 速度很快 200 KB 的可执行程序,启动速度是 python 的 3 perl 的 2 倍,很多命令行工具 go/rust 写成,动不动就 2MB / 3MB他们都还没有完成加载lua 脚本可能都运行完了。
## Credit
Releated projects:
- [rupa/z](https://github.com/rupa/z): origin z.sh implementation
- [JannesMeyer/z.ps](https://github.com/JannesMeyer/z.ps): z for powershell
我的推特https://x.com/skywind3000
个人博客: https://skywind.me/blog
## License

View File

@@ -35,23 +35,24 @@ From people using z.lua:
- Integrated with FZF (optional) for interactive selection and completion.
- Quickly go back to a parent directory instead of typing "cd ../../..".
- Corresponding experience in different shells and operating systems.
- Compatible with Lua 5.1, 5.2 and 5.3+
- Compatible with Lua (5.1, 5.2, 5.3+) and luajit.
- Self contained, distributed as a single `z.lua` script, no other dependence.
## 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
```
@@ -89,7 +90,7 @@ z -b foo # cd to the parent directory starting with foo
eval "$(lua /path/to/z.lua --init zsh)"
Options like "enhanced" and "once" can be used after `--init` too. It can also be initialized from "skywind3000/z.lua" with your zsh plugin managers (antigen / oh-my-zsh).
Options like "enhanced", "once" and "fzf" can be used after `--init` too. It can also be initialized from "skywind3000/z.lua" with your zsh plugin managers (antigen / oh-my-zsh).
**NOTE**: for wsl-1 users, `lua-filesystem` must be installed.
@@ -105,20 +106,31 @@ z -b foo # cd to the parent directory starting with foo
To generate old posix compatible script.
- Fish Shell:
- Fish Shell (version `2.4.0` or above):
Create `~/.config/fish/conf.d/z.fish` with following code
source (lua /path/to/z.lua --init fish | psub)
lua /path/to/z.lua --init fish | source
Fish version `2.4.0` or above is required.
If you'd like `z.lua` to cooperate with fish's own [directory history](https://fishshell.com/docs/3.2/index.html#id34), you can put
lua /path/to/z.lua --init fish > ~/.config/fish/conf.d/z.fish
set -gx _ZL_CD cd
This is another way to initialize z.lua in fish shell, but remember to regenerate z.fish if z.lua has been updated or moved.
into the same file.
- Nushell
Put something like this in your `env.nu`:
mkdir ($nu.data-dir | path join "vendor/autoload")
lua /path/to/z.lua --init nushell | save -f ($nu.data-dir | path join "vendor/autoload/zlua.nu")
Note: Only Nushell v0.96+ is supported
- Power Shell:
> ⚠️ **WARNING**: users of [Starship Prompt](https://starship.rs/) should add the following command *after* `starship init`.
put something like this in your `profile.ps1`:
Invoke-Expression (& { (lua /path/to/z.lua --init powershell) -join "`n" })
@@ -153,11 +165,16 @@ z -b foo # cd to the parent directory starting with foo
- set `$_ZL_EXCLUDE_DIRS` to a comma separated list of dirs to exclude.
- set `$_ZL_ADD_ONCE` to '1' to update database only if `$PWD` changed.
- set `$_ZL_MAXAGE` to define a aging threshold (default is 5000).
- set `$_ZL_CD` to specify your own cd command.
- set `$_ZL_CD` to specify your own cd command (default is `builtin cd` in Unix shells).
- set `$_ZL_ECHO` to 1 to display new directory name after cd.
- set `$_ZL_MATCH_MODE` to 1 to enable enhanced matching.
- set `$_ZL_NO_CHECK` to 1 to disable path validation, use `z --purge` to clean
- set `$_ZL_HYPHEN` to 1 to treat hyphon (-) as a normal character not a lua regexp keyword.
- set `$_ZL_HYPHEN` to 0 to treat a hyphen (`-`) as a
[lua regexp special character](https://www.lua.org/pil/20.2.html),
set `$_ZL_HYPHEN` to 1 to treat a hyphen as a normal character.
If `$_ZL_HYPHEN` is not set or if it is set to `auto`, z.lua tries to treat `-`
as a lua regexp special character first. If there are no matches, z.lua tries
again, this time treating `-` as a normal character.
- set `$_ZL_CLINK_PROMPT_PRIORITY` change clink prompt register priority (default 99).
## Aging
@@ -174,16 +191,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:
@@ -193,6 +206,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:
@@ -268,7 +290,7 @@ To enable this, you can set `$_ZL_ADD_ONCE` to `1` before init z.lua. Or you can
````bash
eval "$(lua /path/to/z.lua --init bash once)"
eval "$(lua /path/to/z.lua --init zsh once)"
source (lua /path/to/z.lua --init fish once | psub)
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.
@@ -317,6 +339,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`:
@@ -336,6 +359,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` ``).
@@ -366,6 +394,12 @@ Bash is not as powerful as zsh/fish, so we introduced fzf-completion for bash, i
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`:
```zsh
eval "$(lua /path/to/z.lua --init zsh enhanced once echo fzf)"
```
Then press `<tab>` after `z xxx`:
![](images/complete-2.png)
@@ -418,6 +452,14 @@ At last, press `<enter>` to accept or `<ESC>` to give up.
Remember to enable the [enhanced matching](#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.
[`ranger` file manager]: https://github.com/ranger/ranger
To define additional commands (`:zb` for example) in ranger, you can put `alias zb z -b` into `~/.config/ranger/rc.conf`.
## Tips
@@ -554,6 +596,7 @@ This project needs help for the tasks below:
- Thanks to [@manhong2112](https://github.com/manhong2112) for Power Shell porting.
- Thanks to [@BarbUk](https://github.com/BarbUk) for fzf completion in Bash.
- Thanks to [@barlik](https://github.com/barlik) for many improvements.
- Thanks to [@brglng](https://github.com/brglng) for nushell porting.
And many others.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@@ -27,6 +27,8 @@ end
if test -e $_zlua_dir/z.lua
if type -q lua
lua $_zlua_dir/z.lua --init fish enhanced once echo | source
else if type -q luajit
luajit $_zlua_dir/z.lua --init fish enhanced once echo | source
else if type -q lua5.3
lua5.3 $_zlua_dir/z.lua --init fish enhanced once echo | source
else if type -q lua5.2

View File

@@ -2,41 +2,45 @@ import time, sys, os
import ranger.api
import subprocess
old_hook_init = ranger.api.hook_init
# $RANGER_LUA and $RANGER_ZLUA variables are deprecated, do not use them.
ZLUA_LUAEXE = os.environ.get('RANGER_LUA') or os.environ.get('ZLUA_LUAEXE')
ZLUA_SCRIPT = os.environ.get('RANGER_ZLUA') or os.environ.get('ZLUA_SCRIPT')
PATH_LUA = os.environ.get('RANGER_LUA')
PATH_ZLUA = os.environ.get('RANGER_ZLUA')
if not PATH_LUA:
if not ZLUA_LUAEXE:
for path in os.environ.get('PATH', '').split(os.path.pathsep):
for name in ('lua', 'luajit', 'lua5.3', 'lua5.2', 'lua5.1'):
test = os.path.join(path, name)
test = test + (sys.platform[:3] == 'win' and ".exe" or "")
if os.path.exists(test):
PATH_LUA = test
ZLUA_LUAEXE = test
break
if not PATH_LUA:
sys.stderr.write('Please install lua or set $RANGER_LUA.\n')
sys.exit()
def _report_error(msg):
sys.stderr.write('ranger_zlua: ' + msg)
raise RuntimeError(msg)
if (not PATH_ZLUA) or (not os.path.exists(PATH_ZLUA)):
sys.stderr.write('Not find z.lua, please set $RANGER_ZLUA to absolute path of z.lua.\n')
sys.exit()
if not ZLUA_LUAEXE:
_report_error('Please install lua in $PATH or make sure $ZLUA_LUAEXE points to a lua executable.\n')
if (not ZLUA_SCRIPT) or (not os.path.exists(ZLUA_SCRIPT)):
_report_error('Could not find z.lua, please make sure $ZLUA_SCRIPT is set to absolute path of z.lua.\n')
# Inform z.lua about directories the user browses to inside ranger
old_hook_init = ranger.api.hook_init
def hook_init(fm):
def update_zlua(signal):
import os, random
os.environ['_ZL_RANDOM'] = str(random.randint(0, 0x7fffffff))
p = subprocess.Popen([PATH_LUA, PATH_ZLUA, "--add", signal.new.path])
p = subprocess.Popen([ZLUA_LUAEXE, ZLUA_SCRIPT, "--add", signal.new.path])
p.wait()
if PATH_ZLUA and PATH_LUA and os.path.exists(PATH_ZLUA):
if ZLUA_SCRIPT and ZLUA_LUAEXE and os.path.exists(ZLUA_SCRIPT):
fm.signal_bind('cd', update_zlua)
return old_hook_init(fm)
ranger.api.hook_init = hook_init
class z(ranger.api.commands.Command):
def execute (self):
import sys, os, time
@@ -52,13 +56,13 @@ class z(ranger.api.commands.Command):
elif arg[:1] != '-':
break
if mode:
cmd = '"%s" "%s" '%(PATH_LUA, PATH_ZLUA)
cmd = '"%s" "%s" '%(ZLUA_LUAEXE, ZLUA_SCRIPT)
if mode in ('-I', '-i', '--'):
cmd += ' --cd'
for arg in args:
cmd += ' "%s"'%arg
if mode in ('-e', '-x'):
path = subprocess.check_output([PATH_LUA, PATH_ZLUA, '--cd'] + args)
path = subprocess.check_output([ZLUA_LUAEXE, ZLUA_SCRIPT, '--cd'] + args)
path = path.decode("utf-8", "ignore")
path = path.rstrip('\n')
self.fm.notify(path)
@@ -76,7 +80,7 @@ class z(ranger.api.commands.Command):
if path and os.path.exists(path):
self.fm.cd(path)
else:
path = subprocess.check_output([PATH_LUA, PATH_ZLUA, '--cd'] + args)
path = subprocess.check_output([ZLUA_LUAEXE, ZLUA_SCRIPT, '--cd'] + args)
path = path.decode("utf-8", "ignore")
path = path.rstrip('\n')
if path and os.path.exists(path):

16
z.cmd
View File

@@ -1,10 +1,8 @@
@echo off
setlocal EnableDelayedExpansion
set "HomeDir=%~dp0"
set "PathSave=%PATH%"
set "LuaExe=lua"
set "LuaScript=%HomeDir%z.lua"
set "LuaScript=%~dp0z.lua"
set "MatchType=-n"
set "StrictSub=-n"
set "RunMode=-n"
@@ -91,7 +89,9 @@ if /i "%1"=="--purge" (
:check
if /i "%1"=="" (
set "RunMode=-l"
if /i "%InterMode%"=="" (
set "RunMode=-l"
)
)
for /f "delims=" %%i in ('cd') do set "PWD=%%i"
@@ -120,8 +120,12 @@ rem -- Exploits variable expansion and the pushd stack to set the current
rem -- directory without leaking a pushd.
popd
setlocal
set NewPath=%CD%
endlocal & popd & cd /d "%NewPath%"
set "NewPath=%CD%"
set "CDCmd=cd /d"
if /i not "%_ZL_CD%"=="" (
set "CDCmd=%_ZL_CD%"
)
endlocal & popd & %CDCmd% "%NewPath%"
:end
echo.

477
z.lua
View File

@@ -1,10 +1,10 @@
#! /usr/bin/env lua
--=====================================================================
--
-- z.lua - a cd command that learns, by skywind 2018, 2019, 2020, 2021
-- z.lua - a cd command that learns, by skywind 2018-2022
-- Licensed under MIT license.
--
-- Version 1.8.12, Last Modified: 2021/02/15 00:05
-- Version 1.8.24, Last Modified: 2025/05/24 23:04:42
--
-- * 10x faster than fasd and autojump, 3x faster than z.sh
-- * available for posix shells: bash, zsh, sh, ash, dash, busybox
@@ -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,12 +47,11 @@
--
-- 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:
-- Invoke-Expression (& {
-- Invoke-Expression (& {
-- (lua /path/to/z.lua --init powershell) -join "`n" })
--
-- Windows Install (with Clink):
@@ -127,7 +127,8 @@ Z_CMD = 'z'
Z_MATCHMODE = 0
Z_MATCHNAME = false
Z_SKIPPWD = false
Z_HYPHEN = false
Z_HYPHEN = "auto"
Z_DATA_SEPARATOR = "|"
os.LOG_NAME = os.getenv('_ZL_LOG_NAME')
@@ -406,7 +407,7 @@ if os.native.status then
return hr ~= nil and ffi.string(buffer) or nil
end
end
function os.native.tickcount()
function os.native.tickcount()
if windows then
return os.native.GetTickCount()
else
@@ -528,13 +529,16 @@ function os.path.abspath(path)
return test
end
end
for _, python in pairs({'python', 'python2', 'python3'}) do
for _, python in pairs({'python3', 'python2', 'python'}) do
local s = 'sys.stdout.write(os.path.abspath(sys.argv[1]))'
local s = '-c "import os, sys;' .. s .. '" \'' .. path .. '\''
local s = python .. ' ' .. s
local test = os.path.which(python)
if test ~= nil and test ~= '' then
return os.call(s)
test = os.call(s)
if test ~= nil and test ~= '' then
return test
end
end
end
end
@@ -581,7 +585,7 @@ function os.path.exists(name)
end
local ok, err, code = os.rename(name, name)
if not ok then
if code == 13 then
if code == 13 or code == 17 then
return true
elseif code == 30 then
local f = io.open(name,"r")
@@ -879,6 +883,7 @@ function os.interpreter()
local lua = os.argv[-1]
if lua == nil then
io.stderr:write("cannot get executable name, recompiled your lua\n")
return nil
end
if os.path.single(lua) then
local path = os.path.which(lua)
@@ -929,8 +934,8 @@ function os.environ(name, default)
end
elseif type(default) == 'number' then
value = tonumber(value)
if value == nil then
return default
if value == nil then
return default
else
return value
end
@@ -1058,6 +1063,26 @@ function path_case_insensitive()
end
-----------------------------------------------------------------------
-- Read a line of the database and return a list of the 3 fields in it
-----------------------------------------------------------------------
function read_data_line(line)
local part = string.split(line, Z_DATA_SEPARATOR)
if #part <= 3 then
return part
end
-- If the part is made of more than 3 elements, it's probably because the
-- path element contains '|' that have been split. Thus, we want to
-- reconstruct it and keep the 2 last elements of part intact as the end
-- of the returned part.
local path = part[1]
for i=2,#part-2 do
path = path .. Z_DATA_SEPARATOR .. part[i]
end
return {path, part[#part-1], part[#part]}
end
-----------------------------------------------------------------------
-- load and split data
-----------------------------------------------------------------------
@@ -1070,10 +1095,11 @@ function data_load(filename)
return {}
end
for line in fp:lines() do
local part = string.split(line, '|')
local part = read_data_line(line)
local item = {}
if part and part[1] and part[2] and part[3] then
local key = insensitive and part[1]:lower() or part[1]
part[2] = part[2]:gsub(",", ".")
item.name = part[1]
item.rank = tonumber(part[2])
item.time = tonumber(part[3]) + 0
@@ -1131,7 +1157,7 @@ function data_save(filename, M)
end
for i = 1, #M do
local item = M[i]
local text = item.name .. '|' .. item.rank .. '|' .. item.time
local text = item.name .. Z_DATA_SEPARATOR .. item.rank .. Z_DATA_SEPARATOR .. item.time
fp:write(text .. '\n')
end
fp:close()
@@ -1288,13 +1314,16 @@ end
-----------------------------------------------------------------------
-- select matched pathnames
-----------------------------------------------------------------------
function data_select(M, patterns, matchlast)
-- z_hyphen must be `true`, `false``, or `"auto"`.
function data_select(M, patterns, matchlast, z_hyphen)
local N = {}
local i = 1
local pats = {}
local hyphens = false
for i = 1, #patterns do
local p = patterns[i]
if Z_HYPHEN then
hyphens = hyphens or string.match(p, "%-")
if z_hyphen == true then
p = p:gsub('-', '%%-')
end
table.insert(pats, case_insensitive_pattern(p))
@@ -1305,6 +1334,9 @@ function data_select(M, patterns, matchlast)
table.insert(N, item)
end
end
if (hyphens and z_hyphen == "auto" and #N == 0) then
N = data_select(M, patterns, matchlast, true)
end
return N
end
@@ -1454,10 +1486,10 @@ function z_match(patterns, method, subdir)
method = method ~= nil and method or 'frecent'
subdir = subdir ~= nil and subdir or false
local M = data_load(DATA_FILE)
M = data_select(M, patterns, false)
M = data_select(M, patterns, false, Z_HYPHEN)
M = data_filter(M)
if Z_MATCHNAME then
local N = data_select(M, patterns, true)
local N = data_select(M, patterns, true, Z_HYPHEN)
N = data_filter(N)
if #N > 0 then
M = N
@@ -1580,21 +1612,23 @@ function z_cd(patterns)
if patterns == nil then
return nil
end
if #patterns == 0 then
return nil
end
local last = patterns[#patterns]
if last == '~' or last == '~/' then
return os.path.expand('~')
elseif windows and last == '~\\' then
return os.path.expand('~')
end
if os.path.isabs(last) and os.path.isdir(last) then
local size = #patterns
if size <= 1 then
return os.path.norm(last)
elseif last ~= '/' and last ~= '\\' then
return os.path.norm(last)
if Z_INTERACTIVE == 0 then
if #patterns == 0 then
return nil
end
local last = patterns[#patterns]
if last == '~' or last == '~/' then
return os.path.expand('~')
elseif windows and last == '~\\' then
return os.path.expand('~')
end
if os.path.isabs(last) and os.path.isdir(last) then
local size = #patterns
if size <= 1 then
return os.path.norm(last)
elseif last ~= '/' and last ~= '\\' then
return os.path.norm(last)
end
end
end
local M = z_match(patterns, Z_METHOD, Z_SUBDIR)
@@ -1632,19 +1666,19 @@ function z_cd(patterns)
elseif Z_INTERACTIVE == 2 then
local fzf = os.environ('_ZL_FZF', 'fzf')
local tmpname = '/tmp/zlua.txt'
local cmd = '--nth 2.. --reverse --inline-info --tac '
local cmd = '--nth 2.. --reverse --info=inline --tac '
local flag = os.environ('_ZL_FZF_FLAG', '')
flag = (flag == '' or flag == nil) and '+s -e' or flag
cmd = ((fzf == '') and 'fzf' or fzf) .. ' ' .. cmd .. ' ' .. flag
local height = os.environ('_ZL_FZF_HEIGHT', '35%')
if height ~= nil and height ~= '' and height ~= '0' then
cmd = cmd .. ' --height ' .. height
end
if not windows then
tmpname = os.tmpname()
local height = os.environ('_ZL_FZF_HEIGHT', '35%')
if height ~= nil and height ~= '' and height ~= '0' then
cmd = cmd .. ' --height ' .. height
end
cmd = cmd .. ' < "' .. tmpname .. '"'
else
tmpname = os.tmpname():gsub('\\', ''):gsub('%.', '')
tmpname = os.tmpname():gsub('[\\:]', ''):gsub('%.', '')
tmpname = os.environ('TMP', '') .. '\\zlua_' .. tmpname .. '.txt'
cmd = 'type "' .. tmpname .. '" | ' .. cmd
end
@@ -1716,47 +1750,45 @@ 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
else
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]
local dst = args[2]
@@ -1767,10 +1799,25 @@ 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
-- 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")
return nil
end
@@ -1828,7 +1875,7 @@ function cd_breadcrumbs(pwd, interactive)
if not windows then
tmpname = os.tmpname()
else
tmpname = os.tmpname():gsub('\\', ''):gsub('%.', '')
tmpname = os.tmpname():gsub('[\\:]', ''):gsub('%.', '')
tmpname = os.environ('TMP', '') .. '\\zlua_' .. tmpname .. '.txt'
end
fp = io.open(tmpname, 'w')
@@ -1853,7 +1900,7 @@ function cd_breadcrumbs(pwd, interactive)
retval = io.read('*l')
elseif interactive == 2 then
local fzf = os.environ('_ZL_FZF', 'fzf')
local cmd = '--reverse --inline-info --tac '
local cmd = '--reverse --info=inline --tac '
local flag = os.environ('_ZL_FZF_FLAG', '')
flag = (flag == '' or flag == nil) and '+s -e' or flag
cmd = ((fzf == '') and 'fzf' or fzf) .. ' ' .. cmd .. ' ' .. flag
@@ -1919,20 +1966,20 @@ 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)
end
elseif options['-'] then
path = cd_minus(args, options)
elseif #args == 0 then
elseif #args == 0 and Z_INTERACTIVE == 0 then
path = nil
else
path = z_cd(args)
if path == nil and Z_MATCHMODE ~= 0 then
local last = args[#args]
if os.path.isdir(last) then
if last and os.path.isdir(last) then
path = os.path.abspath(last)
path = os.path.norm(path)
end
@@ -1956,12 +2003,14 @@ function main(argv)
for _, key in ipairs(args) do
opts[key] = 1
end
if windows then
if opts.nushell then
z_nushell_init(opts)
elseif windows then
z_windows_init(opts)
elseif opts.fish then
z_fish_init(opts)
elseif opts.powershell then
z_windows_init(opts)
z_windows_init(opts)
else
z_shell_init(opts)
end
@@ -1973,9 +2022,14 @@ function main(argv)
z_print(M, true, false)
end
elseif options['--complete'] then
local line = args[1] and args[1] or ''
local head = line:sub(Z_CMD:len()+1):gsub('^%s+', '')
local M = z_match({head}, Z_METHOD, Z_SUBDIR)
local M = {}
if options['-m1'] then
M = z_match(args and args or {}, Z_METHOD, Z_SUBDIR)
else
local line = args[1] and args[1] or ''
local head = line:sub(Z_CMD:len()+1):gsub('^%s+', '')
M = z_match({head}, Z_METHOD, Z_SUBDIR)
end
for _, item in pairs(M) do
print(item.name)
end
@@ -2050,10 +2104,13 @@ function z_init()
Z_SKIPPWD = true
end
end
assert(Z_HYPHEN == "auto", "Z_HYPHEN initialized to an unexpected value")
if _zl_hyphen ~= nil then
local m = string.lower(_zl_hyphen)
if (m == '1' or m == 'yes' or m == 'true' or m == 't') then
Z_HYPHEN = true
elseif (m == '0' or m == 'no' or m == 'false' or m == 'f') then
Z_HYPHEN = false
end
end
end
@@ -2084,10 +2141,28 @@ function z_clink_init()
end
return {}
end
local dirmatchfunc = function (word)
clink.matches_are_files(1)
return clink.match_files(word..'*', true, clink.find_dirs)
end
local dirmatchparser = clink.arg.new_parser():set_arguments({ dirmatchfunc })
local z_parser = clink.arg.new_parser()
z_parser:set_arguments({ z_match_completion })
z_parser:set_flags("-c", "-r", "-i", "--cd", "-e", "-b", "--add", "-x", "--purge",
"--init", "-l", "-s", "--complete", "--help", "-h")
z_parser:set_flags("-r", "-i", "-I", "-t", "-l", "-c", "-e", "-b", "-x"..dirmatchparser, "-h")
if z_parser.adddescriptions then
z_parser:adddescriptions({
['-r'] = "cd to highest ranked dir matching",
['-i'] = "cd with interactive selection",
['-I'] = "cd with interactive selection using fzf",
['-t'] = "cd to most recently accessed dir matching",
['-l'] = "list matches instead of cd",
['-c'] = "restrict matches to subdirs of cwd (or %PWD% if set)",
['-e'] = "echo the best match, don't cd",
['-b'] = "jump backwards to given dir or to project root",
['-x'] = { " dir", "remove path from history" },
['-h'] = "show help",
})
end
clink.arg.register_parser("z", z_parser)
end
@@ -2122,7 +2197,8 @@ _zlua() {
-s) local arg_strip="-s" ;;
-i) local arg_inter="-i" ;;
-I) local arg_inter="-I" ;;
-h|--help) local arg_mode="-h" ;;
-h) local arg_mode="-h" ;;
--help) local arg_mode="-h" ;;
--purge) local arg_mode="--purge" ;;
*) break ;;
esac
@@ -2245,6 +2321,31 @@ _zlua_zsh_tab_completion() {
}
if [ "${+functions[compdef]}" -ne 0 ]; then
compdef _zlua_zsh_tab_completion _zlua 2> /dev/null
compdef ${_ZL_CMD:-z}=_zlua
fi
]]
local script_fzf_complete_zsh = [[
if command -v fzf >/dev/null 2>&1; then
# To redraw line after fzf closes (printf '\e[5n')
bindkey '\e[0n' kill-whole-line
_zlua_zsh_fzf_complete() {
local list=$(_zlua -l ${words[2,-1]})
if [ -n "$list" ]; then
local selected=$(print $list | ${=zlua_fzf} | sed 's/^[0-9,.]* *//')
if [ -n "$selected" ]; then
cd ${selected}
printf '\e[5n'
fi
fi
}
if [ "${+functions[compdef]}" -ne 0 ]; then
compdef _zlua_zsh_fzf_complete _zlua > /dev/null 2>&1
compdef ${_ZL_CMD:-z}=_zlua
fi
fi
]]
@@ -2285,7 +2386,7 @@ function z_shell_init(opts)
end
print(script_complete_bash)
if opts.fzf ~= nil then
fzf_cmd = "fzf --nth 2.. --reverse --inline-info --tac "
local fzf_cmd = "fzf --nth 2.. --reverse --info=inline --tac "
local height = os.environ('_ZL_FZF_HEIGHT', '35%')
if height ~= nil and height ~= '' and height ~= '0' then
fzf_cmd = fzf_cmd .. ' --height ' .. height .. ' '
@@ -2301,6 +2402,18 @@ function z_shell_init(opts)
print(once and script_init_zsh_once or script_init_zsh)
end
print(script_complete_zsh)
if opts.fzf ~= nil then
local fzf_cmd = "fzf --nth 2.. --reverse --info=inline --tac "
local height = os.environ('_ZL_FZF_HEIGHT', '35%')
if height ~= nil and height ~= '' and height ~= '0' then
fzf_cmd = fzf_cmd .. ' --height ' .. height .. ' '
end
local flag = os.environ('_ZL_FZF_FLAG', '')
flag = (flag == '' or flag == nil) and '+s -e' or flag
fzf_cmd = fzf_cmd .. ' ' .. flag .. ' '
print('zlua_fzf="' .. fzf_cmd .. '"')
print(script_fzf_complete_zsh)
end
elseif opts.posix ~= nil then
if prompt_hook then
local script = script_init_posix
@@ -2405,7 +2518,9 @@ function _zlua
end
if test -z "$_ZL_CMD"; set -x _ZL_CMD z; end
alias "$_ZL_CMD"=_zlua
function $_ZL_CMD -w _zlua -d "alias $_ZL_CMD=_zlua"
_zlua $argv
end
]]
script_init_fish = [[
@@ -2564,7 +2679,11 @@ goto end
popd
setlocal
set "NewPath=%CD%"
endlocal & popd & cd /d "%NewPath%"
set "CDCmd=cd /d"
if /i not "%_ZL_CD%"=="" (
set "CDCmd=%_ZL_CD%"
)
endlocal & popd & %CDCmd% "%NewPath%"
:end
]]
@@ -2689,22 +2808,146 @@ function z_windows_init(opts)
end
-----------------------------------------------------------------------
-- nushell
-----------------------------------------------------------------------
local script_zlua_nushell = [[
export def _zlua --env --wrapped [...args: string] {
if ($args | length) != 0 and $args.0 == "--add" {
with-env { _ZL_RANDOM: (random int) } { ^$env.ZLUA_LUAEXE $env.ZLUA_SCRIPT --add ...($args | skip 1) }
} else if ($args | length) != 0 and $args.0 == "--complete" {
^$env.ZLUA_LUAEXE $env.ZLUA_SCRIPT --complete ...($args | skip 1)
} else {
mut arg_mode = ''
mut arg_type = ''
mut arg_subdir = ''
mut arg_inter = ''
mut arg_strip = ''
mut count = 0
for arg in $args {
match $arg {
'-l' => { $arg_mode = '-l' },
'-e' => { $arg_mode = '-e' },
'-x' => { $arg_mode = '-x' },
'-t' => { $arg_type = '-t' },
'-r' => { $arg_type = '-r' },
'-c' => { $arg_subdir = '-c' },
'-s' => { $arg_strip = '-s' },
'-i' => { $arg_inter = '-i' },
'-I' => { $arg_inter = '-I' },
'-h' => { $arg_mode = '-h' },
'--help' => { $arg_mode = '-h' },
'--purge' => { $arg_mode = '--purge' },
_ => break
}
$count += 1
}
let args = $args | skip $count
if $arg_mode == '-h' or $arg_mode == '--purge' {
^$env.ZLUA_LUAEXE $env.ZLUA_SCRIPT $arg_mode
} else if $arg_mode == '-l' or ($args | length) == 0 {
^$env.ZLUA_LUAEXE $env.ZLUA_SCRIPT -l $arg_subdir $arg_type $arg_strip ...$args
} else if $arg_mode != '' {
^$env.ZLUA_LUAEXE $env.ZLUA_SCRIPT $arg_mode $arg_subdir $arg_type $arg_inter ...$args
} else {
let zdest = (^$env.ZLUA_LUAEXE $env.ZLUA_SCRIPT --cd $arg_type $arg_subdir $arg_inter ...$args)
if $zdest != '' and ($zdest | path exists) {
cd $zdest
if _ZL_ECHO in $env and $env._ZL_ECHO != '' {
pwd
}
}
}
}
}
]]
local script_init_nushell = [[
$env.config = ($env | default {} config).config
$env.config = ($env.config | default {} hooks)
$env.config = ($env.config | update hooks ($env.config.hooks | default {} env_change))
$env.config = ($env.config | update hooks.env_change ($env.config.hooks.env_change | default [] PWD))
$env.config = ($env.config | update hooks.env_change.PWD ($env.config.hooks.env_change.PWD | append {|_, dir| _zlua --add $dir }))
]]
local script_complete_nushell = [[
let zlua_completer = {|spans| $spans | skip 1 | _zlua --complete -m1 ...$in | lines | where {|x| $x != $env.PWD}}
$env.config = ($env.config | default {} completions)
$env.config = ($env.config | update completions ($env.config.completions | default {} external))
$env.config = ($env.config | update completions.external ($env.config.completions.external | default true enable))
if completer in $env.config.completions.external {
let orig_completer = $env.config.completions.external.completer
$env.config = ($env.config | update completions.external.completer {
{|spans|
match $spans.0 {
z => $zlua_completer,
_zlua => $zlua_completer,
_ => $orig_completer
} | do $in $spans
}
})
} else {
$env.config = ($env.config | update completions.external.completer {
{|spans|
match $spans.0 {
z => $zlua_completer,
_zlua => $zlua_completer,
} | do $in $spans
}
})
}
export alias z = _zlua
]]
-----------------------------------------------------------------------
-- initialize nushell
-----------------------------------------------------------------------
function z_nushell_init(opts)
print('$env.ZLUA_LUAEXE = \'' .. os.interpreter() .. '\'')
print('$env.ZLUA_SCRIPT = \'' .. os.scriptname() .. '\'')
local prompt_hook = (not os.environ("_ZL_NO_PROMPT_COMMAND", false))
if opts.clean ~= nil then
prompt_hook = false
end
print(script_zlua_nushell)
if prompt_hook then
print(script_init_nushell)
end
print(script_complete_nushell)
if opts.enhanced ~= nil then
print('$env._ZL_MATCH_MODE = 1')
end
if opts.once ~= nil then
print('$env._ZL_ADD_ONCE = 1')
end
if opts.echo ~= nil then
print('$env._ZL_ECHO = 1')
end
if opts.nc ~= nil then
print('$env._ZL_NO_CHECK = 1')
end
end
-----------------------------------------------------------------------
-- help
-----------------------------------------------------------------------
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
@@ -2725,7 +2968,7 @@ if os.lfs.enable ~= nil then
end
os.path.isdir = function (name)
local mode = lfs.attributes(name)
if not mode then
if not mode then
return false
end
return (mode.mode == 'directory') and true or false

View File

@@ -2,11 +2,14 @@
ZLUA_SCRIPT="${0:A:h}/z.lua"
[[ -n "$ZLUA_EXEC" ]] && [[ ! -x "$ZLUA_EXEC" ]] && ZLUA_EXEC=""
if [[ -n "$ZLUA_EXEC" ]] && ! which "$ZLUA_EXEC" &>/dev/null; then
echo "$ZLUA_EXEC not found"
ZLUA_EXEC=""
fi
# search lua executable
if [[ -z "$ZLUA_EXEC" ]]; then
for lua in lua lua5.3 lua5.2 lua5.1; do
for lua in lua luajit lua5.4 lua5.3 lua5.2 lua5.1; do
ZLUA_EXEC="$(command -v "$lua")"
[[ -n "$ZLUA_EXEC" ]] && break
done
@@ -18,7 +21,11 @@ fi
export _ZL_FZF_FLAG=${_ZL_FZF_FLAG:-"-e"}
eval "$($ZLUA_EXEC $ZLUA_SCRIPT --init zsh once enhanced)"
if [[ -z "$_ZL_ZSH_NO_FZF" ]]; then
eval "$($ZLUA_EXEC $ZLUA_SCRIPT --init zsh once enhanced fzf)"
else
eval "$($ZLUA_EXEC $ZLUA_SCRIPT --init zsh once enhanced)"
fi
if [[ -z "$_ZL_NO_ALIASES" ]]; then
alias zz='z -i'