如何防止我的 Python 脚本中的 ANSI 转义序列干扰我的 zsh RPROMPT 和光标位置?
How can I prevent ANSI escape sequences in my Python script from messing with my zsh RPROMPT and cursor positions?
我一直在编写一个 Python 脚本,该脚本为 zsh 生成一个关于当前工作目录中 git 存储库状态的 RPROMPT。它在我的 .zshrc 文件中调用:
RPROMPT='$(python3 ~/.git_zsh_rprompt.py)'
为了将它与我终端中的其他文本区分开来,我使用 ANSI 转义码将其设为粗体,并根据存储库是干净还是脏来为其着色。无论出于何种原因,添加这些转义码都会导致我的 RPROMPT 向左移动,并且还会将我的光标移到左侧 PROMPT 的顶部。这是一个表示,块代表我的光标:
jared@Jareds-Mac⌷ook-Pro:foobar% master( +2 ~4 )
除了一些没有根据的猜测,我不完全确定为什么会这样。我希望这里有人知道并且知道可以将所有内容恢复原状的解决方案或变通方法。作为参考,这里是有问题的脚本:
from collections import Counter
from os import devnull
from subprocess import call, check_output, STDOUT
def git_is_repo():
command = ["git", "branch"]
return not call(command, stderr = STDOUT, stdout = open(devnull, "w"))
def git_current_branch():
command = ["git", "branch", "--list"]
lines = check_output(command).decode("utf-8").strip().splitlines()
return next((line.split()[1] for line in lines if line.startswith("* ")),
"unknown branch")
def git_status_counter():
command = ["git", "status", "--porcelain"]
lines = check_output(command).decode("utf-8").strip().splitlines()
return Counter(line.split()[0] for line in lines)
if __name__ == "__main__":
if git_is_repo():
counter = git_status_counter()
# Print bold green if the repo is clean or bold red if it is dirty.
if counter.elements() == []:
print("3[1;32m", end="")
else:
print("3[1;31m", end="")
print(git_current_branch(), end="")
# Only print status counters if the repo is dirty.
if counter.elements() != []:
print("(", end="")
if counter["??"] != 0:
print(" +{}".format(counter["??"]), end="")
if counter["M"] != 0:
print(" ~{}".format(counter["M"]), end="")
if counter["D"] != 0:
print(" -{}".format(counter["D"]), end="")
print(" )", end="")
# Reset text attributes.
print("3[0m", end="")
这是 zsh
的常见问题,与所有其他 shells 一样,除了 strlen
ing 字符串之外,不知道你的提示有多长,除非你标记非- 以某种方式为其打印字符。
每个 shell、* 都有解决此问题的方法,但在 zsh
中,您真的不必这样做;这就是它的提示格式首先具有 visual effects commands 的一半原因。**
所以,例如,而不是这个:
print("3[1;32m", end="")
……你可以这样做:
print("%B%F{green}", end="")
zsh
知道如何查找粗体和绿色并为您的终端插入适当的转义序列,现在它知道根据您的提示长度计算这些序列。
* IIRC,zsh
支持 bash
风格的转义方括号,以及它自己的 %{
…%}
标记。
** 另一半是视觉效果将限制为正确的转义序列,或者如果不可能则退化为无,而不是假设一切都是 ANSI。
您想要在提示中围绕您的 ANSI 转义使用 %{ %}
ZSH 转义序列,如记录的那样 here
%{...%}
Include a string as a literal escape sequence. The string
within the braces should not change the cursor position. Brace pairs
can nest.
A positive numeric argument between the % and the { is treated as
described for %G below.
我一直在编写一个 Python 脚本,该脚本为 zsh 生成一个关于当前工作目录中 git 存储库状态的 RPROMPT。它在我的 .zshrc 文件中调用:
RPROMPT='$(python3 ~/.git_zsh_rprompt.py)'
为了将它与我终端中的其他文本区分开来,我使用 ANSI 转义码将其设为粗体,并根据存储库是干净还是脏来为其着色。无论出于何种原因,添加这些转义码都会导致我的 RPROMPT 向左移动,并且还会将我的光标移到左侧 PROMPT 的顶部。这是一个表示,块代表我的光标:
jared@Jareds-Mac⌷ook-Pro:foobar% master( +2 ~4 )
除了一些没有根据的猜测,我不完全确定为什么会这样。我希望这里有人知道并且知道可以将所有内容恢复原状的解决方案或变通方法。作为参考,这里是有问题的脚本:
from collections import Counter
from os import devnull
from subprocess import call, check_output, STDOUT
def git_is_repo():
command = ["git", "branch"]
return not call(command, stderr = STDOUT, stdout = open(devnull, "w"))
def git_current_branch():
command = ["git", "branch", "--list"]
lines = check_output(command).decode("utf-8").strip().splitlines()
return next((line.split()[1] for line in lines if line.startswith("* ")),
"unknown branch")
def git_status_counter():
command = ["git", "status", "--porcelain"]
lines = check_output(command).decode("utf-8").strip().splitlines()
return Counter(line.split()[0] for line in lines)
if __name__ == "__main__":
if git_is_repo():
counter = git_status_counter()
# Print bold green if the repo is clean or bold red if it is dirty.
if counter.elements() == []:
print("3[1;32m", end="")
else:
print("3[1;31m", end="")
print(git_current_branch(), end="")
# Only print status counters if the repo is dirty.
if counter.elements() != []:
print("(", end="")
if counter["??"] != 0:
print(" +{}".format(counter["??"]), end="")
if counter["M"] != 0:
print(" ~{}".format(counter["M"]), end="")
if counter["D"] != 0:
print(" -{}".format(counter["D"]), end="")
print(" )", end="")
# Reset text attributes.
print("3[0m", end="")
这是 zsh
的常见问题,与所有其他 shells 一样,除了 strlen
ing 字符串之外,不知道你的提示有多长,除非你标记非- 以某种方式为其打印字符。
每个 shell、* 都有解决此问题的方法,但在 zsh
中,您真的不必这样做;这就是它的提示格式首先具有 visual effects commands 的一半原因。**
所以,例如,而不是这个:
print("3[1;32m", end="")
……你可以这样做:
print("%B%F{green}", end="")
zsh
知道如何查找粗体和绿色并为您的终端插入适当的转义序列,现在它知道根据您的提示长度计算这些序列。
* IIRC,zsh
支持 bash
风格的转义方括号,以及它自己的 %{
…%}
标记。
** 另一半是视觉效果将限制为正确的转义序列,或者如果不可能则退化为无,而不是假设一切都是 ANSI。
您想要在提示中围绕您的 ANSI 转义使用 %{ %}
ZSH 转义序列,如记录的那样 here
%{...%}
Include a string as a literal escape sequence. The string within the braces should not change the cursor position. Brace pairs can nest.
A positive numeric argument between the % and the { is treated as described for %G below.