We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
这是一个用lua来执行快速跳转的工具,来替代z.sh,执行速度快。
eval "$(lua /path/to/z.lua --init bash once enhanced)" # BASH 初始化
我们执行$(lua /path/to/z.lua --init bash once enanced) 输出的脚本是
$(lua /path/to/z.lua --init bash once enanced)
ZLUA_SCRIPT="/usr/local/bin/z.lua" ZLUA_LUAEXE="/usr/bin/lua" _zlua() { local arg_mode="" local arg_type="" local arg_subdir="" local arg_inter="" local arg_strip="" if [ "$1" = "--add" ]; then shift _ZL_RANDOM="$RANDOM" "$ZLUA_LUAEXE" "$ZLUA_SCRIPT" --add "$@" return elif [ "$1" = "--complete" ]; then shift "$ZLUA_LUAEXE" "$ZLUA_SCRIPT" --complete "$@" return fi while [ "$1" ]; do case "$1" in -l) local arg_mode="-l" ;; -e) local arg_mode="-e" ;; -x) local arg_mode="-x" ;; -t) local arg_type="-t" ;; -r) local arg_type="-r" ;; -c) local arg_subdir="-c" ;; -s) local arg_strip="-s" ;; -i) local arg_inter="-i" ;; -I) local arg_inter="-I" ;; -h|--help) local arg_mode="-h" ;; --purge) local arg_mode="--purge" ;; *) break ;; esac shift done if [ "$arg_mode" = "-h" ] || [ "$arg_mode" = "--purge" ]; then "$ZLUA_LUAEXE" "$ZLUA_SCRIPT" $arg_mode elif [ "$arg_mode" = "-l" ] || [ "$#" -eq 0 ]; then "$ZLUA_LUAEXE" "$ZLUA_SCRIPT" -l $arg_subdir $arg_type $arg_strip "$@" elif [ -n "$arg_mode" ]; then "$ZLUA_LUAEXE" "$ZLUA_SCRIPT" $arg_mode $arg_subdir $arg_type $arg_inter "$@" else local zdest=$("$ZLUA_LUAEXE" "$ZLUA_SCRIPT" --cd $arg_type $arg_subdir $arg_inter "$@") if [ -n "$zdest" ] && [ -d "$zdest" ]; then if [ -z "$_ZL_CD" ]; then builtin cd "$zdest" else $_ZL_CD "$zdest" fi if [ -n "$_ZL_ECHO" ]; then pwd; fi fi fi } # alias ${_ZL_CMD:-z}='_zlua 2>&1' alias ${_ZL_CMD:-z}='_zlua' _zlua_precmd() { [ "$_ZL_PREVIOUS_PWD" = "$PWD" ] && return _ZL_PREVIOUS_PWD="$PWD" (_zlua --add "$PWD" 2> /dev/null &) } case "$PROMPT_COMMAND" in *_zlua_precmd*) ;; *) PROMPT_COMMAND="_zlua_precmd${PROMPT_COMMAND:+;$PROMPT_COMMAND}" ;; esac if [ -n "$BASH_VERSION" ]; then complete -o filenames -C '_zlua --complete "$COMP_LINE"' ${_ZL_CMD:-z} fi
_zlua _zlua是一段胶水代码,其做了两件事情
PROMPT_COMMAND就是说每执行一个命令前,PROMPT_COMMAND 里面先执行,然后执行PROMPT
**_zlua --add **
首先定义了z.lua脚本的位置和解释器的位置。然后定义shell函数_z_lua。看下最关键的信息:
# 如果是使用bash if [ -n "$BASH_VERSION" ]; then #使用complete来不全文件列表 #-C用来产生候选项的命令,会调用一个sub shell来执行 complete -o filenames -C '_zlua --complete "$COMP_LINE"' ${_ZL_CMD:-z} fi
这里可以查看$COMP_LINE的含义,这里的概念programmable completion.这里我们可以理解成,当我们在终端打z xxx tab键的时候, 这个变量$COMP_LINE就是我们打入的字符串,接着, 叫做我们可以看_zlua,实际上直接跳到
"$ZLUA_LUAEXE" "$ZLUA_SCRIPT" --complete "$@"
然后开始执行z.lua的逻辑,
进入z.lua的逻辑是,在命令行传入了--complete的和 "$@",
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
这部分逻辑比较清晰:
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()中体现,这里由一个数据老化的概念
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
注意这里的frecent值默认时rank的值,也就是1,我们发现在切换工作目录时,也就是调用data_insert时,并没有frecent值进行更新,只是在不存在该路径的时候,给了一个默认值,那么什么时候更新这个frecent值呢,在match的时候。
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
match的逻辑比较简单
这里涉及到更新frecent的值
什么时候更新rank值呢?在我们改变我们的工作目录,就会将rank + 1,总结起来,如果我们输入的path pattern,匹配到相关路径,那么frecent值 将会改变,如果是我们切换到了该path,则rank值会增加。
The text was updated successfully, but these errors were encountered:
No branches or pull requests
参考资料
这是一个用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
这部分逻辑比较清晰:
注意这里有更新数据文件中path的逻辑,这部分逻辑在data_insert()中体现,这里由一个数据老化的概念
注意这里的frecent值默认时rank的值,也就是1,我们发现在切换工作目录时,也就是调用data_insert时,并没有frecent值进行更新,只是在不存在该路径的时候,给了一个默认值,那么什么时候更新这个frecent值呢,在match的时候。
z_match
match的逻辑比较简单
这里涉及到更新frecent的值
什么时候更新rank值呢?在我们改变我们的工作目录,就会将rank + 1,总结起来,如果我们输入的path pattern,匹配到相关路径,那么frecent值 将会改变,如果是我们切换到了该path,则rank值会增加。
The text was updated successfully, but these errors were encountered: