1
0
mirror of https://github.com/skywind3000/z.lua synced 2026-03-22 15:49:47 +00:00

47 Commits

Author SHA1 Message Date
skywind3000
5c36d55698 use lua-filesystem package if possible when $_ZL_USE_LFS is 1 2019-09-07 01:02:48 +08:00
skywind3000
bdab27db1b fixed wrong return value from os.path.exists 2019-09-06 01:21:28 +08:00
skywind3000
e08f5beca4 update doc 2019-08-01 20:06:05 +08:00
skywind3000
a245db0d93 update doc 2019-08-01 19:54:34 +08:00
skywind3000
dd721703c3 1.7.2: improve bash / zsh shell compatibility 2019-08-01 19:46:23 +08:00
Linwei
7920d56c89 Merge pull request #71 from barlik/prompt-command
Do not append semicolon to PROMPT_COMMAND
2019-08-01 17:45:54 +08:00
Linwei
645818ccc8 Merge pull request #72 from barlik/zsh-hooks 2019-08-01 02:41:19 +08:00
Rastislav Barlik
b98911a227 Setting zsh-hooks to be unique is not necessary 2019-07-31 14:46:28 +01:00
Rastislav Barlik
671830059b Do not append semicolon to PROMPT_COMMAND 2019-07-31 13:47:55 +01:00
skywind3000
0fad96124b commit new README.md 2019-06-07 22:17:42 +08:00
skywind3000
465f2b8e62 fixed linux $_ZL_DATA failure 2019-06-07 22:13:29 +08:00
skywind3000
ff9d874ef4 update doc 2019-03-09 17:24:53 +08:00
skywind3000
86120d206e fix: ReplaceFile issue in luajit (windows). 2019-03-09 16:56:46 +08:00
skywind3000
1e0e3523b0 add ranger plugin for z.lua 2019-03-08 16:23:40 +08:00
skywind3000
7eee4e4b13 optimize with ffi (luajit builtin module). 2019-03-04 20:29:43 +08:00
skywind3000
edd71f7a6e update doc 2019-03-02 12:16:50 +08:00
skywind3000
f63a832921 improve errno handling in os.path.exists 2019-03-02 12:06:02 +08:00
skywind3000
f92ad79e2c fixed: os.path.isdir not work for symbol links close #59. 2019-03-02 11:55:39 +08:00
skywind3000
38c1741f4f prevent writing file racing 2019-03-01 14:45:03 +08:00
skywind3000
fdff5c5e1e commit new README.md 2019-02-28 12:24:20 +08:00
skywind3000
b691d537f2 z -b should not match current directory, close #56 2019-02-25 23:20:15 +08:00
skywind3000
472925ac92 improve lua test 2019-02-25 11:01:00 +08:00
Linwei
5ba0e50894 Merge pull request #54 from aaronjensen/patch-1
Use \which in case user has it aliased to type -a
2019-02-24 13:12:20 +08:00
Aaron Jensen
c769dba4dd Use \which in case user has it aliased to type -a 2019-02-23 19:40:26 -08:00
skywind3000
bbca83c9a5 update doc 2019-02-22 22:54:22 +08:00
skywind3000
53c127a4a2 update doc 2019-02-22 22:46:23 +08:00
skywind3000
5f7fa928b6 new $_ZL_FZF_HEIGHT, set to 0 to use fullscreen 2019-02-21 12:12:58 +08:00
skywind3000
68de23ecef v1.5.7: rename $_ZL_FZF_SORT to $_ZL_INT_SORT it will affect both -i and -I. 2019-02-21 11:15:02 +08:00
skywind3000
5ea08c5dac update doc 2019-02-20 23:31:37 +08:00
skywind3000
9dd0fec7ed update doc 2019-02-20 23:24:24 +08:00
skywind3000
fe11dd30c5 improve fzf sorting 2019-02-20 23:23:15 +08:00
skywind3000
db92fa0a1c new "$_ZL_FZF_SORT" to sort directories by alphabet in fzf 2019-02-20 20:24:24 +08:00
skywind3000
b1702839d2 update doc 2019-02-20 15:58:11 +08:00
skywind3000
1a0428f54e update doc 2019-02-20 15:31:11 +08:00
skywind3000
31e9d42d08 update image 2019-02-20 15:04:53 +08:00
skywind3000
14b942634c fixed picture 2019-02-20 14:52:14 +08:00
skywind3000
6cfe2150e9 images 2019-02-20 14:48:00 +08:00
skywind3000
a4c589a724 _ZL_FZF_FLAG can be used to override fzf parameters, default to "+s -e" 2019-02-20 12:25:07 +08:00
skywind3000
cb8021599c update doc 2019-02-19 17:11:16 +08:00
skywind3000
91a4d75e62 update doc 2019-02-19 15:26:50 +08:00
skywind3000
4d4dc8802b update doc 2019-02-19 11:43:33 +08:00
skywind3000
761b3ebf31 update 2019-02-19 11:24:46 +08:00
skywind3000
4e7ecde314 update z_print to support fz.sh
update os.path.isdir to handle windows path better
2019-02-19 11:22:05 +08:00
Linwei
e5c78e3087 Merge pull request #50 from contrun/master
fix spurious file not exists
2019-02-19 11:16:04 +08:00
B YI
1fb4f25744 fix spurious file not exists
see https://github.com/skywind3000/z.lua/issues/49
2019-02-19 10:12:01 +08:00
skywind3000
6057644adb commit new files 2019-02-18 23:25:03 +08:00
skywind3000
dc02feb424 update doc 2019-02-17 21:29:13 +08:00
9 changed files with 361 additions and 69 deletions

View File

@@ -250,7 +250,9 @@ PS如果你使用 Fish shell需要 2.7.0 以上才支持该功能。
![](images/fzf.png)
当你使用 `"z -I vim"` 时12 条路径被筛选出来,并按照 frecent 排序,他们都包含 "vim" 关键字,在实际 cd 改变路径前z.lua 会调用 fzf 来让你更方便的选择你想去的地方,每条记录包含左边的 frecent 权重和右边的路径名,权重越高的排在越前面,你可以按回车选择,或者 ESC 退出
当你使用 `"z -I vim"` 时12 条路径被筛选出来,并按照 frecent 排序,他们都包含 "vim" 关键字,在实际 cd 改变路径前z.lua 会调用 fzf 来让你更方便的选择你想去的地方,每条记录包含左边的 frecent 权重和右边的路径名,权重越高的排在越前面。
你可以在 fzf 里输入一些**空格分隔**的关键字(不需要先后顺序),或者按 `CTRL+J` / `CTRL+K` (方向键的上下也可以)进行选择,`ESC` 或者 `CTRL`+`D`/`G` 放弃。
你仍然可以用老方法,通过在 `z` 命令后面添加更多关键词来精确的匹配你想去的地方,这个特性给了你一个可视化的方式来做这件事情。为了方便起见,通常把 `z -I` alias 成 `zf` (z + fuzzy finder)。如果搜索结果只有一项,那么 z.lua 会直接跳转过去,不需要启动 fzf 再选择一遍,只有多项结果要选择时,才会启动 fzf。
@@ -267,7 +269,7 @@ PS你可以使用 `$_ZL_FZF` 环境变量来精确指明 fzf 的可执行路
- **(单个参数)**`cd` 到离当前目录最近的以关键字开头的父目录,如果找不到就尝试跳到包含关键字的父目录。
- **(两个参数)**:将当前路径中的第一个关键词替换为第二个关键词。
为了使用简便,我们继续将 `z -b` 取个别名成 `zb`
将 `z -b` 别名成 `zb`
```bash
# 一直向上退到项目根目录(就是里面有一个 .git 目录的地方)
@@ -348,23 +350,22 @@ $ z -
=> cd -
```
这个 `dirstack` 是根据 z.lua 的路径历史数据库计算的出来的,所以和具体的 shell 或者操作系统都没有关系。你退出再登陆不会丢失这些路径历史,不同的 shell 之间也可以共享同一份历史数据
这个 `dirstack` 是根据 z.lua 的路径历史数据库计算的出来的,和具体的 shell 或者操作系统无关。你退出再登陆不会丢失这些记录,不同的 shell 之间也可以共享同一份记录
此外,还有另外一种方法,通过使用前面提到的 `-I` 和 `-t` 参数组和,可以使用 fzf 进行交互式选择:
此外,还能通过前面提到的 `-I` 和 `-t` 参数组和,使用 fzf 选择最近去过的目录
```bash
alias zh='z -I -t .'
```
首先定义个新的别名 `zh`(回到历史路径的意思),我们使用了 `-t` 参数来告诉 `z.lua` 按时间戳为权重进行排序,同时 `-I` 启用 fzf 进行搜索,最后句号代表任意路径。
方便起见,定义个新的别名 `zh`(回到历史路径的意思),我们 `-t` 参数来告诉 `z.lua` 按时间戳为权重排序,同时 `-I` 启用 fzf 搜索,最后句号代表任意路径。
那么当我们在命令行敲入 zh 时,就可以用 fzf 选择最近呆过的路径了:
那么当我们在命令行敲入 zh 时,就可以用 fzf 进行历史路径操作了:
![](images/mru.png)
第一列数据代表该路径多少秒以前访问过,第二列是路径名称。此时,你可以试着敲几个字母,用 fzf 的字符串模糊匹配进行定位或者用光标键的上和下CTRL+J/K 也可以)来上下移动,最后按回车 cd 过去,或者 ESC 放弃。
第一列上次访问距今多少秒,第二列是目录名。你可以试着敲几个字母,用 fzf 的字符串模糊匹配进行定位或者用光标键的上和下CTRL+J/K 也可以)来上下移动,最后按回车 cd 过去,或者 ESC 放弃。
注意:最好把增强匹配模式打开,这样查看历史路径时可以把当前路径过滤掉。
## Tips

View File

@@ -24,7 +24,6 @@ For example, `z foo bar` would match `/foo/bar` but not `/bar/foo`.
- Interactive selection enables you to choose where to go before cd.
- Intergrated with FZF (optional) for interactive selection and completion.
- Quickly go back to a parent directory instead of typing "cd ../../..".
- Cooperate with [fz](https://github.com/changyuheng/fz) to provide better completion, see [FAQ](https://github.com/skywind3000/z.lua/wiki/FAQ#fzsh-for-better-completion).
- Corresponding experience in different shells and operating systems.
- Compatible with Lua 5.1, 5.2 and 5.3+
- Self contained, distributed as a single `z.lua` script, no other dependence.
@@ -35,7 +34,7 @@ For example, `z foo bar` would match `/foo/bar` but not `/bar/foo`.
```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 highest ranked dir matching foo
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
@@ -220,14 +219,14 @@ If no match is found, it will fall back to default matching method.
- Skip the current directory:
when you are calling `z xxx` but the best match is the current directory, z.lua will choose the 2nd best match result for you. Assuming the database:
When you are calling `z xxx` but the best match is the current directory, z.lua will choose the 2nd best match result for you. Assuming the database:
10 /Users/Great_Wall/.rbenv/versions/2.4.1/lib/ruby/gems
20 /Library/Ruby/Gems/2.0.0/gems
When I use `z gems` by default, it will take me to `/Library/Ruby/Gems/2.0.0/gems`, but it's not what I want, so I press up arrow and execute `z gems` again, it will take me to `/Users/Great_Wall/.rbenv/versions/2.4.1/lib/ruby/gems` and this what I want.
Of course I can always use `z env gems` to indicate what I want precisely. Skip the current directory means when you use `z xxx` you always want to change directory instead of stay in the same directory and do nothing if current directory is the best match.
Of course, I can always use `z env gems` to indicate what I want precisely. Skip the current directory means when you use `z xxx` you always want to change directory instead of stay in the same directory and do nothing if current directory is the best match.
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.
@@ -236,7 +235,7 @@ The default matching method is designed to be compatible with original z.sh, but
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 init z.lua on linux like this:
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:
````bash
eval "$(lua /path/to/z.lua --init bash once)"
@@ -270,13 +269,15 @@ From version 1.1.0, a new option `"-I"` will allow you to use fzf to select when
![](images/fzf.png)
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. And you can use fzf to select where you want to go, or ESC to quit.
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.
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.
`"z -I ."` or `"zf ."` can be used to use fzf select from entire database. Environment variable `_ZL_FZF_FLAG` is for passing additional arguments to fzf.
For more information about this, please visit [wiki - effective with fzf](https://github.com/skywind3000/z.lua/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.
@@ -285,8 +286,8 @@ NOTE: For fish shell, this feature requires fish 2.7.0 or above. You can specify
New option `"-b"` can quickly go back to a specific parent directory in bash instead of typing "cd ../../.." redundantly.
- **(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, goto the parent containing keyword.
- **(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).
Let's start by aliasing `z -b` to `zb`:
@@ -419,7 +420,7 @@ Don't forget to read the [Frequently Asked Questions](https://github.com/skywind
## 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:
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:
```bash
$ time autojump --add /tmp
@@ -457,6 +458,19 @@ As you see, z.lua is the fastest one and requires less resource.
## History
- 1.7.3 (2019-09-07): use [lua-filesystem](http://keplerproject.github.io/luafilesystem/) package if possible when `$_ZL_USE_LFS` is `1`.
- 1.7.2 (2019-08-01): Improve bash/zsh shell compatibility by [@barlik](https://github.com/barlik).
- 1.7.1 (2019-06-07): Fixed: `$_ZL_DATA` failure on Linux sometimes.
- 1.7.0 (2019-03-09): Support [ranger](https://github.com/skywind3000/z.lua/wiki/FAQ#how-to-integrate-zlua-to-ranger-), fix ReplaceFile issue in luajit (windows).
- 1.6.0 (2019-03-04): optimize with ffi module (luajit builtin module).
- 1.5.11 (2019-03-02): fixed: os.path.isdir doesn't work for symbol link folders.
- 1.5.10 (2019-03-01): Prevent writing file racing.
- 1.5.9 (2019-02-25): `z -b` should not match current directory (close #56).
- 1.5.8 (2019-02-21): new `$_ZL_FZF_HEIGHT` to control `--height` parameter in fzf.
- 1.5.7 (2019-02-21): rename `$_ZL_FZF_SORT` to `$_ZL_INT_SORT` it will affect both `-i` and `-I`.
- 1.5.6 (2019-02-20): set `$_ZL_FZF_SORT` to 1 to sort directories by alphabet in fzf.
- 1.5.5 (2019-02-20): `$_ZL_FZF_FLAG` can be used to override fzf flags, default to "+s -e".
- 1.5.4 (2019-02-19): fixed: file/path existence detection fails on read-only fs (closed [#49](https://github.com/skywind3000/z.lua/issues/49) by [@contrun](https://github.com/contrun)).
- 1.5.3 (2019-02-17): new `$_ZL_FZF_FLAG` for passing additional flags to fzf, add `-e` argument to fzf.
- 1.5.2 (2019-02-16): be aware of all arguments in fzf completion.
- 1.5.1 (2019-02-15): new: simulated dir stack by `z -`, `z --` and `z -{num}`.
@@ -485,6 +499,7 @@ As you see, z.lua is the fastest one and requires less resource.
This project needs help for the tasks below:
- [ ] Support csh/tcsh.
- [ ] Completion: Actually I got little knowledge in completion, and need help to improve it.
- [ ] Completion: Implement completion for Power Shell.
- [ ] Completion: Implement completion for different arguments.
@@ -498,6 +513,7 @@ This project needs help for the tasks below:
- Thanks to [@TeddyDD](https://github.com/TeddyDD) for Fish Shell porting.
- 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.
And many others.

BIN
images/step1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
images/step2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
images/step3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

BIN
images/step4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

91
ranger_zlua.py Normal file
View File

@@ -0,0 +1,91 @@
import time, sys, os
import ranger.api
import subprocess
old_hook_init = ranger.api.hook_init
PATH_LUA = os.environ.get('RANGER_LUA')
PATH_ZLUA = os.environ.get('RANGER_ZLUA')
if not PATH_LUA:
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
break
if not PATH_LUA:
sys.stderr.write('Please install lua or set $RANGER_LUA.\n')
sys.exit()
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()
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.wait()
if PATH_ZLUA and PATH_LUA and os.path.exists(PATH_ZLUA):
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
args = self.args[1:]
if args:
mode = ''
for arg in args:
if arg in ('-l', '-e', '-x', '-h', '--help', '--'):
mode = arg
break
elif arg in ('-I', '-i'):
mode = arg
elif arg[:1] != '-':
break
if mode:
cmd = '"%s" "%s" '%(PATH_LUA, PATH_ZLUA)
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 = path.decode("utf-8", "ignore")
path = path.rstrip('\n')
self.fm.notify(path)
elif mode in ('-h', '-l', '--help'):
p = self.fm.execute_command(cmd + '| less +G', universal_newlines=True)
stdout, stderr = p.communicate()
elif mode == '--':
p = self.fm.execute_command(cmd + ' 2>&1 | less +G', universal_newlines=True)
stdout, stderr = p.communicate()
else:
if mode == '-I':
os.environ['_ZL_FZF_HEIGHT'] = '0'
path = subprocess.check_output([PATH_LUA, PATH_ZLUA, '--cd'] + args)
self.fm.execute_console('redraw_window')
else:
p = self.fm.execute_command(cmd, universal_newlines=True, stdout=subprocess.PIPE)
stdout, stderr = p.communicate()
path = stdout.rstrip('\n')
if path and os.path.exists(path):
self.fm.cd(path)
else:
path = subprocess.check_output([PATH_LUA, PATH_ZLUA, '--cd'] + args)
path = path.decode("utf-8", "ignore")
path = path.rstrip('\n')
if path and os.path.exists(path):
self.fm.cd(path)
else:
self.fm.notify('No matching found', bad = True)
return True

268
z.lua
View File

@@ -4,7 +4,7 @@
-- z.lua - a cd command that learns, by skywind 2018, 2019
-- Licensed under MIT license.
--
-- Version 1.5.3, Last Modified: 2019/02/17 16:22
-- Version 1.7.3, Last Modified: 2019/09/06 17:27
--
-- * 10x faster than fasd and autojump, 3x faster than z.sh
-- * available for posix shells: bash, zsh, sh, ash, dash, busybox
@@ -74,6 +74,7 @@
-- set $_ZL_MAXAGE to define a aging threshold (default is 5000).
-- set $_ZL_MATCH_MODE to 1 to enable enhanced matching mode.
-- set $_ZL_NO_CHECK to 1 to disable path validation. z --purge to clear.
-- set $_ZL_USE_LFS to 1 to use lua-filesystem package
--
--=====================================================================
@@ -158,6 +159,10 @@ function string:startswith(text)
return false
end
function string:endswith(text)
return text == "" or self:sub(-#text) == text
end
function string:lstrip()
if self == nil then return nil end
local s = self:gsub('^%s+', '')
@@ -298,10 +303,107 @@ function os.log(text)
end
-----------------------------------------------------------------------
-- ffi optimize (luajit has builtin ffi module)
-----------------------------------------------------------------------
os.native = {}
os.native.status, os.native.ffi = pcall(require, "ffi")
if os.native.status then
local ffi = os.native.ffi
if windows then
ffi.cdef[[
int GetFullPathNameA(const char *name, uint32_t size, char *out, char **name);
int ReplaceFileA(const char *dstname, const char *srcname, void *, uint32_t, void *, void *);
uint32_t GetTickCount(void);
uint32_t GetFileAttributesA(const char *name);
uint32_t GetCurrentDirectoryA(uint32_t size, char *ptr);
]]
local kernel32 = ffi.load('kernel32.dll')
local buffer = ffi.new('char[?]', 300)
local INVALID_FILE_ATTRIBUTES = 0xffffffff
local FILE_ATTRIBUTE_DIRECTORY = 0x10
os.native.kernel32 = kernel32
function os.native.GetFullPathName(name)
local hr = kernel32.GetFullPathNameA(name, 290, buffer, nil)
return (hr > 0) and ffi.string(buffer, hr) or nil
end
function os.native.ReplaceFile(replaced, replacement)
local hr = kernel32.ReplaceFileA(replaced, replacement, nil, 2, nil, nil)
return (hr ~= 0) and true or false
end
function os.native.GetTickCount()
return kernel32.GetTickCount()
end
function os.native.GetFileAttributes(name)
return kernel32.GetFileAttributesA(name)
end
function os.native.exists(name)
local attr = os.native.GetFileAttributes(name)
return attr ~= INVALID_FILE_ATTRIBUTES
end
function os.native.isdir(name)
local attr = os.native.GetFileAttributes(name)
local isdir = FILE_ATTRIBUTE_DIRECTORY
if attr == INVALID_FILE_ATTRIBUTES then
return false
end
return (attr % (2 * isdir)) >= isdir
end
function os.native.getcwd()
local hr = kernel32.GetCurrentDirectoryA(299, buffer)
if hr <= 0 then return nil end
return ffi.string(buffer, hr)
end
else
ffi.cdef[[
typedef struct { long tv_sec; long tv_usec; } timeval;
int gettimeofday(timeval *tv, void *tz);
int access(const char *name, int mode);
char *realpath(const char *path, char *resolve);
char *getcwd(char *buf, size_t size);
]]
local timeval = ffi.new('timeval[?]', 1)
local buffer = ffi.new('char[?]', 4100)
function os.native.gettimeofday()
local hr = ffi.C.gettimeofday(timeval, nil)
local sec = tonumber(timeval[0].tv_sec)
local usec = tonumber(timeval[0].tv_usec)
return sec + (usec * 0.000001)
end
function os.native.access(name, mode)
return ffi.C.access(name, mode)
end
function os.native.realpath(name)
local path = ffi.C.realpath(name, buffer)
return (path ~= nil) and ffi.string(buffer) or nil
end
function os.native.getcwd()
local hr = ffi.C.getcwd(buffer, 4099)
return hr ~= nil and ffi.string(buffer) or nil
end
end
function os.native.tickcount()
if windows then
return os.native.GetTickCount()
else
return math.floor(os.native.gettimeofday() * 1000)
end
end
os.native.init = true
end
-----------------------------------------------------------------------
-- get current path
-----------------------------------------------------------------------
function os.pwd()
if os.native and os.native.getcwd then
local hr = os.native.getcwd()
if hr then return hr end
end
if os.getcwd then
return os.getcwd()
end
if windows then
local fp = io.popen('cd')
if fp == nil then
@@ -368,6 +470,10 @@ end
-----------------------------------------------------------------------
function os.path.abspath(path)
if path == '' then path = '.' end
if os.native and os.native.GetFullPathName then
local test = os.native.GetFullPathName(path)
if test then return test end
end
if windows then
local script = 'FOR /f "delims=" %%i IN ("%s") DO @echo %%~fi'
local script = string.format(script, path)
@@ -418,22 +524,24 @@ end
function os.path.isdir(pathname)
if pathname == '/' then
return true
elseif pathname == '' then
return false
elseif windows then
if pathname == '\\' then
return true
elseif pathname:match('^%a:[/\\]$') then
return true
end
end
local name = pathname .. '/'
local ok, err, code = os.rename(name, name)
if not ok then
if code == 13 then
return true
end
return false
if os.native and os.native.isdir then
return os.native.isdir(pathname)
end
return true
if clink and os.isdir then
return os.isdir(pathname)
end
local name = pathname
if (not name:endswith('/')) and (not name:endswith('\\')) then
name = name .. os.path.sep
end
return os.path.exists(name)
end
@@ -441,10 +549,25 @@ end
-- file or path exists
-----------------------------------------------------------------------
function os.path.exists(name)
if name == '/' then
return true
end
local ok, err, code = os.rename(name, name)
if not ok then
if code == 13 then
return true
elseif code == 30 then
local f = io.open(name,"r")
if f ~= nil then
io.close(f)
return true
end
elseif name:sub(-1) == '/' and code == 20 and (not windows) then
local test = name .. '.'
ok, err, code = os.rename(test, test)
if code == 16 or code == 13 or code == 22 then
return true
end
end
return false
end
@@ -762,6 +885,10 @@ end
-----------------------------------------------------------------------
function os.environ(name, default)
local value = os.getenv(name)
if os.envmap ~= nil and type(os.envmap) == 'table' then
local t = os.envmap[name]
value = (t ~= nil and type(t) == 'string') and t or value
end
if value == nil then
return default
elseif type(default) == 'boolean' then
@@ -855,6 +982,9 @@ function math.random_init()
seed = seed .. rnd
end
seed = seed .. tostring(os.clock() * 10000000)
if os.native and os.native.tickcount then
seed = seed .. tostring(os.native.tickcount())
end
local number = 0
for i = 1, seed:len() do
local k = string.byte(seed:sub(i, i))
@@ -944,15 +1074,29 @@ function data_save(filename, M)
local tmpname = nil
local i
filename = os.path.expand(filename)
if windows then
fp = io.open(filename, 'w')
else
math.random_init()
math.random_init()
while true do
tmpname = filename .. '.' .. tostring(os.time())
tmpname = tmpname .. math.random_string(8)
local rnd = os.getenv('_ZL_RANDOM')
tmpname = tmpname .. '' .. (rnd and rnd or '')
-- print('tmpname: '..tmpname)
if os.native and os.native.tickcount then
local key = os.native.tickcount() % 1000
tmpname = tmpname .. string.format('%03d', key)
tmpname = tmpname .. math.random_string(5)
else
tmpname = tmpname .. math.random_string(8)
end
if not os.path.exists(tmpname) then
-- print('tmpname: '..tmpname)
break
end
end
if windows then
if os.native and os.native.ReplaceFile then
fp = io.open(tmpname, 'w')
else
fp = io.open(filename, 'w')
tmpname = nil
end
else
fp = io.open(tmpname, 'w')
end
if fp == nil then
@@ -965,7 +1109,14 @@ function data_save(filename, M)
end
fp:close()
if tmpname ~= nil then
os.rename(tmpname, filename)
if windows then
local ok, err, code = os.rename(tmpname, filename)
if not ok then
os.native.ReplaceFile(filename, tmpname)
end
else
os.rename(tmpname, filename)
end
os.remove(tmpname)
end
return true
@@ -1266,13 +1417,14 @@ function z_match(patterns, method, subdir)
subdir = subdir ~= nil and subdir or false
local M = data_load(DATA_FILE)
M = data_select(M, patterns, false)
M = data_filter(M)
if Z_MATCHNAME then
local N = data_select(M, patterns, true)
N = data_filter(N)
if #N > 0 then
M = N
end
end
M = data_filter(M)
M = data_update_frecent(M)
if method == 'time' then
current = os.time()
@@ -1325,7 +1477,7 @@ end
-----------------------------------------------------------------------
function z_print(M, weight, number)
local N = {}
local maxsize = 10
local maxsize = 9
local numsize = string.len(tostring(#M))
for _, item in pairs(M) do
local record = {}
@@ -1418,6 +1570,9 @@ function z_cd(patterns)
elseif Z_INTERACTIVE == 0 then
return M[1].name
end
if os.environ('_ZL_INT_SORT', false) then
table.sort(M, function (a, b) return a.name < b.name end)
end
local retval = nil
if Z_INTERACTIVE == 1 then
PRINT_MODE = '<stderr>'
@@ -1439,13 +1594,15 @@ 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 +s --tac -e'
cmd = ((fzf == '') and 'fzf' or fzf) .. ' ' .. cmd
cmd = cmd .. ' ' .. os.environ('_ZL_FZF_FLAG', '') .. ' '
local cmd = '--nth 2.. --reverse --inline-info --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
if not windows then
tmpname = os.tmpname()
if not os.environ('_ZL_FZF_FULLSCR', false) then
cmd = cmd .. ' --height 35%'
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
@@ -1537,6 +1694,7 @@ function cd_backward(args, options, pwd)
end
return os.path.normpath(path)
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
@@ -1715,11 +1873,8 @@ function z_init()
DATA_FILE = _zl_data
else
-- avoid windows environments affect cygwin & msys
if _zl_data:sub(2, 2) ~= ':' then
local t = _zl_data:sub(3, 3)
if t ~= '/' and t ~= "\\" then
DATA_FILE = _zl_data
end
if not string.match(_zl_data, '^%a:[/\\]') then
DATA_FILE = _zl_data
end
end
end
@@ -1859,14 +2014,14 @@ alias ${_ZL_CMD:-z}='_zlua'
local script_init_bash = [[
case "$PROMPT_COMMAND" in
*_zlua?--add*) ;;
*) PROMPT_COMMAND="(_zlua --add \"\$(command pwd 2>/dev/null)\" &);$PROMPT_COMMAND" ;;
*) PROMPT_COMMAND="(_zlua --add \"\$(command pwd 2>/dev/null)\" &)${PROMPT_COMMAND:+;$PROMPT_COMMAND}" ;;
esac
]]
local script_init_bash_fast = [[
case "$PROMPT_COMMAND" in
*_zlua?--add*) ;;
*) PROMPT_COMMAND="(_zlua --add \"\$PWD\" &);$PROMPT_COMMAND" ;;
*) PROMPT_COMMAND="(_zlua --add \"\$PWD\" &)${PROMPT_COMMAND:+;$PROMPT_COMMAND}" ;;
esac
]]
@@ -1878,7 +2033,7 @@ _zlua_precmd() {
}
case "$PROMPT_COMMAND" in
*_zlua_precmd*) ;;
*) PROMPT_COMMAND="_zlua_precmd;$PROMPT_COMMAND" ;;
*) PROMPT_COMMAND="_zlua_precmd${PROMPT_COMMAND:+;$PROMPT_COMMAND}" ;;
esac
]]
@@ -1905,7 +2060,7 @@ local script_init_zsh = [[
_zlua_precmd() {
(_zlua --add "${PWD:a}" &)
}
typeset -gaU precmd_functions
typeset -ga precmd_functions
[ -n "${precmd_functions[(r)_zlua_precmd]}" ] || {
precmd_functions[$(($#precmd_functions+1))]=_zlua_precmd
}
@@ -1915,7 +2070,7 @@ local script_init_zsh_once = [[
_zlua_precmd() {
(_zlua --add "${PWD:a}" &)
}
typeset -gaU chpwd_functions
typeset -ga chpwd_functions
[ -n "${chpwd_functions[(r)_zlua_precmd]}" ] || {
chpwd_functions[$(($#chpwd_functions+1))]=_zlua_precmd
}
@@ -1987,11 +2142,14 @@ function z_shell_init(opts)
end
print(script_complete_bash)
if opts.fzf ~= nil then
fzf_cmd = "fzf --nth 2 --reverse --inline-info +s --tac -e"
if not os.environ('_ZL_FZF_FULLSCR', false) then
fzf_cmd = fzf_cmd .. ' --height 35%'
fzf_cmd = "fzf --nth 2.. --reverse --inline-info --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
fzf_cmd = fzf_cmd .. ' ' .. os.environ('_ZL_FZF_FLAG', '') .. ' '
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_bash)
end
@@ -2385,7 +2543,33 @@ end
-----------------------------------------------------------------------
-- testing case
-- LFS optimize
-----------------------------------------------------------------------
os.lfs = {}
os.lfs.enable = os.getenv('_ZL_USE_LFS')
if os.lfs.enable ~= nil then
local m = string.lower(os.lfs.enable)
if (m == '1' or m == 'yes' or m == 'true' or m == 't') then
os.lfs.status, os.lfs.pkg = pcall(require, 'lfs')
if os.lfs.status then
local lfs = os.lfs.pkg
os.path.exists = function (name)
return lfs.attributes(name) and true or false
end
os.path.isdir = function (name)
local mode = lfs.attributes(name)
if not mode then
return false
end
return (mode.mode == 'directory') and true or false
end
end
end
end
-----------------------------------------------------------------------
-- program entry
-----------------------------------------------------------------------
if not pcall(debug.getlocal, 4, 1) then
-- main script

View File

@@ -6,14 +6,14 @@ ZLUA_SCRIPT="${0:A:h}/z.lua"
# search lua executable
if [[ -z "$ZLUA_EXEC" ]]; then
if [[ -x "$(which lua)" ]]; then
ZLUA_EXEC="$(which lua)"
elif [[ -x "$(which lua5.3)" ]]; then
ZLUA_EXEC="$(which lua5.3)"
elif [[ -x "$(which lua5.2)" ]]; then
ZLUA_EXEC="$(which lua5.2)"
elif [[ -x "$(which lua5.1)" ]]; then
ZLUA_EXEC="$(which lua5.1)"
if [[ -x "$(command which lua)" ]]; then
ZLUA_EXEC="$(command which lua)"
elif [[ -x "$(command which lua5.3)" ]]; then
ZLUA_EXEC="$(command which lua5.3)"
elif [[ -x "$(command which lua5.2)" ]]; then
ZLUA_EXEC="$(command which lua5.2)"
elif [[ -x "$(command which lua5.1)" ]]; then
ZLUA_EXEC="$(command which lua5.1)"
else
echo "Not find lua in your $PATH, please install it."
return