Autocomplete can';t使用'~';在zsh中自动完成
我使用zsh,我想使用我编写的函数来替换cd。 此功能使您能够移动到父目录:Autocomplete can';t使用'~';在zsh中自动完成,autocomplete,zsh,tab-completion,zsh-completion,Autocomplete,Zsh,Tab Completion,Zsh Completion,我使用zsh,我想使用我编写的函数来替换cd。 此功能使您能够移动到父目录: $ pwd /a/b/c/d $ cl b $ pwd /a/b $ pwd /a/b/c/d $ cl b/e $ pwd /a/b/e 您还可以移动到父目录的子目录中: $ pwd /a/b/c/d $ cl b $ pwd /a/b $ pwd /a/b/c/d $ cl b/e $ pwd /a/b/e 如果路径的第一部分不是父目录,它将像普通cd一样工作。我希望这是有道理的 总之,在/a/b/c/d中
$ pwd
/a/b/c/d
$ cl b
$ pwd
/a/b
$ pwd
/a/b/c/d
$ cl b/e
$ pwd
/a/b/e
您还可以移动到父目录的子目录中:
$ pwd
/a/b/c/d
$ cl b
$ pwd
/a/b
$ pwd
/a/b/c/d
$ cl b/e
$ pwd
/a/b/e
如果路径的第一部分不是父目录,它将像普通cd一样工作。我希望这是有道理的
总之,在/a/b/c/d中,我希望能够移动到/a、/a/b、/a/b/c、/a/b/d的所有子目录以及以/、~/或../(或./)开头的任何绝对路径。
我希望这是有道理的
这是我写的函数:
cl () {
local first=$( echo $1 | cut -d/ -f1 )
if [ $# -eq 0 ]; then
# cl without any arguments moves back to the previous directory
cd - > /dev/null
elif [ -d $first ]; then
# If the first argument is an existing normal directory, move there
cd $1
else
# Otherwise, move to a parent directory
cd ${PWD%/$first/*}/$1
fi
}
可能有更好的方法(欢迎提供提示),但到目前为止,我还没有遇到任何问题
现在我想添加自动完成。这就是我到目前为止所做的:
_cl() {
pth=${words[2]}
opts=""
new=${pth##*/}
[[ "$pth" != *"/"*"/"* ]] && middle="" || middle="${${pth%/*}#*/}/"
if [[ "$pth" != *"/"* ]]; then
# If this is the start of the path
# In this case we should also show the parent directories
opts+=" "
first=""
d="${${PWD#/}%/*}/"
opts+="${d//\/// }"
dir=$PWD
else
first=${pth%%/*}
if [[ "$first" == "" ]]; then
# path starts with "/"
dir="/$middle"
elif [[ "$first" == "~" ]]; then
# path starts with "~/"
dir="$HOME/$middle"
elif [ -d $first ]; then
# path starts with a directory in the current directory
dir="$PWD/$first/$middle"
else
# path starts with parent directory
dir=${PWD%/$first/*}/$first/$middle
fi
first=$first/
fi
# List al sub directories of the $dir directory
if [ -d "$dir" ]; then
for d in $(ls -a $dir); do
if [ -d $dir/$d ] && [[ "$d" != "." ]] && [[ "$d" != ".." ]]; then
opts+="$first$middle$d/ "
fi
done
fi
_multi_parts / "(${opts})"
return 0
}
compdef _cl cl
再说一次,这可能不是最好的方法,但它很有效。。。有点
其中一个问题是,我键入的cl~/,它将其替换为cl~/,并且不建议我的主文件夹中有任何目录。有没有办法让它发挥作用
编辑
cl () {
local first=$( echo $1 | cut -d/ -f1 )
if [ $# -eq 0 ]; then
# cl without any arguments moves back to the previous directory
local pwd_bu=$PWD
[[ $(dirs) == "~" ]] && return 1
while [[ $PWD == $pwd_bu ]]; do
popd >/dev/null
done
local pwd_nw=$PWD
[[ $(dirs) != "~" ]] && popd >/dev/null
pushd $pwd_bu >/dev/null
pushd $pwd_nw >/dev/null
elif [ -d $first ]; then
pushd $1 >/dev/null # If the first argument is an existing normal directory, move there
else
pushd ${PWD%/$first/*}/$1 >/dev/null # Otherwise, move to a parent directory or a child of that parent directory
fi
}
_cl() {
_cd
pth=${words[2]}
opts=""
new=${pth##*/}
local expl
# Generate the visual formatting and store it in `$expl`
_description -V ancestor-directories expl 'ancestor directories'
[[ "$pth" != *"/"*"/"* ]] && middle="" || middle="${${pth%/*}#*/}/"
if [[ "$pth" != *"/"* ]]; then
# If this is the start of the path
# In this case we should also show the parent directories
local ancestor=$PWD:h
while (( $#ancestor > 1 )); do
# -f: Treat this as a file (incl. dirs), so you get proper highlighting.
# -Q: Don't quote (escape) any of the characters.
# -W: Specify the parent of the dir we're adding.
# ${ancestor:h}: The parent ("head") of $ancestor.
# ${ancestor:t}: The short name ("tail") of $ancestor.
compadd "$expl[@]" -fQ -W "${ancestor:h}/" - "${ancestor:t}"
# Move on to the next parent.
ancestor=$ancestor:h
done
else
# $first is the first part of the path the user typed in.
# it it is part of the current direoctory, we know the user is trying to go back to a directory
first=${pth%%/*}
# $middle is the rest of the provided path
if [ ! -d $first ]; then
# path starts with parent directory
dir=${PWD%/$first/*}/$first
first=$first/
# List all sub directories of the $dir/$middle directory
if [ -d "$dir/$middle" ]; then
for d in $(ls -a $dir/$middle); do
if [ -d $dir/$middle/$d ] && [[ "$d" != "." ]] && [[ "$d" != ".." ]]; then
compadd "$expl[@]" -fQ -W $dir/ - $first$middle$d
fi
done
fi
fi
fi
}
compdef _cl cl
这是我一个人所能做到的。它确实有效(有点),但有几个问题:
- 当返回到父目录时,完成主要起作用。但是,当您转到paretn目录的子目录时,建议是错误的(它们显示您键入的完整路径,而不仅仅是子目录)。结果确实有效
- 我使用语法hightlighting,但我键入的路径仅为白色(当使用转到父目录时。普通cd函数为彩色)
- 在我的zshrc中,我有一行:
\u cl(){
#存储到目前为止生成的匹配数。
本地-i nmatches=$compstate[nmatches]
#调用“cd”的内置完成。无需重新发明轮子。
_光盘
#${PWD:h}:当前工作目录的父目录(“头”)。
本地祖先=$PWD:h expl
#生成可视化格式并将其存储在“$expl”中`
#-V:不要对这些项目进行排序;按我们添加它们的顺序显示它们。
_description-V祖先目录解释“祖先目录”
而($#祖先>1)),则
#-f:将其视为一个文件(包括dirs),以便获得适当的高亮显示。
#-W:指定要添加的目录的父级。
#${祖先:h}:$祖先的父项(“头”)。
#${祖先:t}:$祖先的短名称(“尾”)。
compadd“$expl[@]”-f-W${祖先:h}/-$祖先:t
#转到下一个父级。
祖先=$祖先:h
完成
#如果添加了任何匹配项,则返回true。
((compstate[nmatches]>nmatches))
}
#将上面的函数定义为生成'cl'的补全。
compdef_cl
#或者,代替上面的行:
# 1. 在“$fpath”中的目录中创建一个文件“\u cl”。
# 2. 将函数“\u cl”的内容粘贴到此文件中。
# 3. 添加“#compdef cl`添加文件的顶部。
#“u cl”现在将在运行“compinit”时自动加载。
另外,我会像这样重写您的cl
函数,这样它就不再依赖于cut
或其他外部命令:
cl(){
如果($#==0));那么
#没有任何参数的`cl`将移回上一个目录。
光盘-
elif[-d$1 | |-d$PWD/$1]];然后
#如果参数是现有的绝对路径或直接子级,请移到那里。
cd$1
其他的
#获取以参数结尾的最长前缀。
本地祖先=${(M)${PWD:h}##*$1}
如果[[-d$祖先]];则
#如果它是现有的目录,请移动到那里。
cd$祖先
其他的
#否则,打印到stderr并返回false。
打印-u2“$0:没有这样的祖先“$1”
返回1
fi
fi
}
替代解决方案 有一种更简单的方法来完成这一切,无需编写
cd
替换或任何完成代码:
cdpath(){
#`$PWD`始终等于当前工作目录。
本地目录=$PWD
#除了搜索“$PWD”的所有子项之外,“cd”还将搜索所有子项
#数组“$cdpath”中所有dir的子级。
cdpath=()
#将“$PWD”的所有祖先添加到“$cdpath”。
而($#dir>1)),则
#`:h`是直接父母。
dir=$dir:h
cdpath+=($dir)
完成
}
#只要我们更改目录,就运行上面的函数。
添加zsh hook chpwd cdpath
Zsh的cd
完成代码会自动考虑$cdpath
。甚至不需要配置它。:)
例如,假设您在/Users/marlon/.zsh/prezto/modules/history substring search/external/
- 您现在可以键入
并按Tab键,Zsh将在cd-pre
中完成此操作。之后,按Enter键将直接进入cd-prezto
/Users/marlon/.zsh/prezto/
- 或者说也存在
。当您在前一个目录中时,您可以执行/Users/marlon/.zsh/prezto/modules/prompt/external/agnoster/
直接转到后一个目录,Zsh将为您完成此路径的每一步cd-prompt/external/agnoster
cdpath
。该功能无法从该更改中识别。仅当用户决定在定义函数时设置默认cdpath,并且在定义后不更改它时