上下文:我想对我提示的部分内容进行右对齐。在这样做时,我目前的方法是计算它的左侧和右侧组件的长度,并用空格填充中间组件。
问题:当字符串可能包含unicode时(例如git status),请处理%G
(请参阅prompt expansion)。可能实际问题是我没有正确掌握它。在how to signal zsh that there are characters to be output的另一个帖子回答中建议使用%G
,这可能是我混淆的根源。以下代码段说明了问题:
strlen() {
FOO=$1
local invisible='%([BSUbfksu]|([FB]|){*})' # (1)
LEN=${#${(S%%)FOO//$~invisible/}}
echo $LEN
}
local blob="%{↓%G%}"
echo $blob $(strlen $blob) # (2) Unexpectedly gives 0
local blob="↓"
echo $blob $(strlen $blob) # (3) Gives the wanted output of 1
# but then this result would tell us to not use %G for unicode
strlen
函数来自this tentative explanation of counting user-visible string。遗憾的是,对于invisible
部分#(1)没有明确的完整解释,任何额外的参考/解释都会受到欢迎。
问题:我应该何时使用%G
?或者我应该按照上面的片段建议放弃它?
答案 0 :(得分:2)
使用Unicode字符而不是纯ASCII字符时,不必执行任何其他步骤。当前版本的zsh
完全支持Unicode字符,并且可以正确处理它们。因此,即使字符由多个字节编码,zsh
仍将知道它只是一个字符。
%{...%}
和%G
%{...%}
用于向zsh
指示内部的字符串不会更改光标位置。例如,如果要添加用于设置颜色的转义序列,这很有用:
print -P '%{\e[31m%}terminal red%{\e[0m%}'
print -P '%{\e[38;2;0;127;255m%}#007FFF%{\e[0m%}'
如果没有%{...%}
zsh
,则必须假设转义序列的每个字符都将光标向右移动一个位置。
在%G
(或%{...%}
)中使用%1{...%}
告诉zsh
假设将输出单个字符。这仅用于计数目的,它不会自动移动光标。
根据ZSH Manual:
当输出shell无法正确处理的字符时,这很有用,例如某些终端上的备用字符集。
由于zsh
能够处理Unicode字符,因此没有必要(尽管不一定是错误的)。
strlen "%{↓%G%}"
的意外结果的原因:这是因为strlen
实际上只是尝试删除任何空值长度的提示序列(如%B
或%F{red}
),而不是实际测量结果的打印长度字符串(无论如何这可能是不可能的)。在许多情况下,这种方法运行得很好,但在"%{↓%G%}"
的情况下它实际上失败了,在"↓"
提示的上下文中实际上相当于zsh
。
为了找到这些空长提示序列,strlen
将其输入与此模式匹配
invisible=%([BSUbfksu]|([FB]|){*})'
这也包含子模式%{*}
,它将在%{…%}
上匹配。然后
LEN=${#${(S%%)FOO//$~invisible/}}
在计算字符之前,只删除FOO
中任何匹配的子字符串。
除此之外,它实际上并不以任何方式处理%G
,只是将其与周围的%{...%}
一起删除。
当整个字符串"%{↓%G%}"
与模式匹配时,它将被完全删除,从而导致0
的意外字符数。
顺便说一句:这并不意味着你不应该使用strlen
(在我的提示中我已经使用了很长一段时间的东西)。但是你应该意识到一些限制:
%G
(显然)。%{...%}
的数字参数,例如%3{...%}
。%
之后的数字参数,用于前景色和背景色,例如%1F
(而不是%F{1}
或%F{red}
)%{...%}
,或}
内的任何%{...%}
。 (例如,当打算使用%D{string}
进行日期格式化时,这很重要,因为格式字符串string
的长度必须与结果日期的长度匹配,而不使用'%{...%在它周围。)最后,原始定义中存在一个错误,它应该是:
local invisible='%([BSUbfksu]|([FK]|){*})'
第二个B
应该是K
,因为它旨在匹配背景颜色的提示转义。 (%B
启动粗体模式)
答案 1 :(得分:0)
以下函数以与快速扩展期间相同的方式计算字符串的长度。与其他解决方案不同,它可以正确处理所有输入。
# Usage: prompt-length TEXT [COLUMNS]
#
# If you run `print -P TEXT`, how many characters will
# be printed on the last line?
#
# Or, equivalently, if you set `PROMPT=TEXT` with `prompt_subst`
# option unset, on which column will the cursor be?
#
# The second argument specifies terminal width. Defaults to the
# real terminal width.
#
# Assumes that `%{%}` and `%G` don't lie.
#
# Examples:
#
# prompt-length '' => 0
# prompt-length 'abc' => 3
# prompt-length $'abc\nxy' => 2
# prompt-length $'\t' => 8
# prompt-length $'\u274E' => 2
# prompt-length '%F{red}abc' => 3
# prompt-length $'%{a\b%Gb%}' => 1
# prompt-length '%D' => 8
# prompt-length '%1(l..ab)' => 2
# prompt-length '%(!.a.)' => 1 if root, 0 if not
function prompt-length() {
emulate -L zsh
local COLUMNS=${2:-$COLUMNS}
local -i x y=$#1 m
if (( y )); then
while (( ${${(%):-$1%$y(l.1.0)}[-1]} )); do
x=y
(( y *= 2 ));
done
local xy
while (( y > x + 1 )); do
m=$(( x + (y - x) / 2 ))
typeset ${${(%):-$1%$m(l.x.y)}[-1]}=$m
done
fi
echo $x
}
此功能来自Powerlevel10k ZSH主题,用于实现多行右提示和响应的当前目录截断(demo)。更多信息:Multi-line prompt: The missing ingredient。