function main(argv):
...
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)
-- 打印所有的item
for _, item in pairs(M) do
print(item.name)
end
....
end
z.lua添加path
这部分逻辑比较清晰:
首先查看当前路径是否是HOME目录,如果是,skip
是否是指定忽略的目录(比如.git等),忽略
然后看是否已经存在了该路径,如果存在了路径,将会更新路径的rank
最后将新的数据保存到data_file
function z_add(path)
local paths = {}
local count = 0
if type(path) == 'table' then
paths = path
elseif type(path) == 'string' then
paths[1] = path -- 第一次执行放入第一个位置
end
if table.length(paths) == 0 then
return false
end
-- 获取HOME目录
local H = os.getenv('HOME')
local M = data_load(DATA_FILE)
local nc = os.getenv('_ZL_NO_CHECK')
if nc == nil or nc == '' or nc == '0' then
-- 执行数据检查
M = data_filter(M)
end
-- 插入当前工作目录, insert paths
for _, path in pairs(paths) do
if os.path.isdir(path) and os.path.isabs(path) then
local skip = false
local test = path
path = os.path.norm(path)
-- check ignore
if windows then
if path:len() == 3 and path:sub(2, 2) == ':' then
local tail = path:sub(3, 3)
if tail == '/' or tail == '\\' then
skip = true
end
end
test = os.path.norm(path:lower())
else
--如果是HOME目录,那么直接忽略
if H == path then
skip = true
end
end
-- check exclude
if not skip then
for _, exclude in ipairs(Z_EXCLUDE) do
-- 目录是以排除目录开头,那么直接忽略
if test:startswith(exclude) then
skip = true
break
end
end
end
if not skip then
--更新了数据
M = data_insert(M, path)
count = count + 1
end
end
end
end
注意这里有更新数据文件中path的逻辑,这部分逻辑在data_insert()中体现,这里由一个数据老化的概念
* 总的访问次数超过了5000次(默认),那么 * 0.9后清楚不足1,清除这个path
```lua
function data_insert(M, filename)
local i = 1
local sumscore = 0
for i = 1, #M do
local item = M[i]
sumscore = sumscore + item.rank
end
if sumscore >= MAX_AGE then
local X = {}
for i = 1, #M do
local item = M[i]
item.rank = item.rank * 0.9
-- 清除不足1的rank
if item.rank >= 1.0 then
table.insert(X, item)
end
end
M = X
end
local nocase = path_case_insensitive()
local name = filename
local key = nocase and string.lower(name) or name
local find = false
local current = os.time()
for i = 1, #M do
local item = M[i]
if not nocase then
if name == item.name then
item.rank = item.rank + 1
item.time = current
find = true
break
end
else
-- 存在,则rank + 1
-- 更新访问时间戳
if key == string.lower(item.name) then
item.rank = item.rank + 1
item.time = current
find = true
break
end
end
end
--初始化时frecent为rank
if not find then
local item = {}
item.name = name
item.rank = 1
item.time = current
item.frecent = item.rank
table.insert(M, item)
end
return M
function z_match(patterns, method, subdir)
patterns = patterns ~= nil and patterns or {}
-- 默认支持几种匹配的pattern
method = method ~= nil and method or 'frecent'
subdir = subdir ~= nil and subdir or false
local M = data_load(DATA_FILE)
-- 从数据集中选择匹配的path
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
-- 在匹配时,进行对frecent更新
M = data_update_frecent(M)
-- 匹配算法如果时按照时间
if method == 'time' then
current = os.time()
for _, item in pairs(M) do
item.score = item.time - current
end
elseif method == 'rank' then
--根据分值来排名
for _, item in pairs(M) do
item.score = item.rank
end
else
for _, item in pairs(M) do
-- 默认使用frecent值
item.score = item.frecent
end
end
--按照分值排序
table.sort(M, function (a, b) return a.score > b.score end)
-- 获取当前的工作目录
local pwd = (PWD == nil or PWD == '') and os.getenv('PWD') or PWD
if pwd == nil or pwd == '' then
pwd = os.pwd()
end
if pwd ~= '' and pwd ~= nil then
--如果是子目录模式
if subdir then
local N = {}
--获取满足当前目录中的子目录,并过滤
for _, item in pairs(M) do
if os.path.subdir(pwd, item.name) then
table.insert(N, item)
end
end
M = N
end
if Z_SKIPPWD then
local N = {}
local key = windows and string.lower(pwd) or pwd
for _, item in pairs(M) do
local match = false
local name = windows and string.lower(item.name) or item.name
if name ~= key then
table.insert(N, item)
end
end
M = N
end
end
return M
end
参考资料
这是一个用lua来执行快速跳转的工具,来替代z.sh,执行速度快。
分析
我们执行
$(lua /path/to/z.lua --init bash once enanced)
输出的脚本是_zlua _zlua是一段胶水代码,其做了两件事情
PROMPT_COMMAND就是说每执行一个命令前,PROMPT_COMMAND 里面先执行,然后执行PROMPT
_zlua --add
首先定义了z.lua脚本的位置和解释器的位置。然后定义shell函数_z_lua。看下最关键的信息:
这里可以查看$COMP_LINE的含义,这里的概念programmable completion.这里我们可以理解成,当我们在终端打z xxx tab键的时候, 这个变量\$COMP_LINE就是我们打入的字符串,接着, 叫做我们可以看_zlua,实际上直接跳到
然后开始执行z.lua的逻辑,
z.lua的complete逻辑
进入z.lua的逻辑是,在命令行传入了--complete的和 "$@",
z.lua添加path
这部分逻辑比较清晰:
最后将新的数据保存到data_file
注意这里的frecent值默认时rank的值,也就是1,我们发现在切换工作目录时,也就是调用data_insert时,并没有frecent值进行更新,只是在不存在该路径的时候,给了一个默认值,那么什么时候更新这个frecent值呢,在match的时候。
z_match
match的逻辑比较简单
根据匹配的算法,来计算score
这里涉及到更新frecent的值
什么时候更新rank值呢?在我们改变我们的工作目录,就会将rank + 1,总结起来,如果我们输入的path pattern,匹配到相关路径,那么frecent值 将会改变,如果是我们切换到了该path,则rank值会增加。