zsh在ps1中右对齐


21

我想要带有右斜线部分的多行zsh提示符,它看起来像这样:

2.nate@host:/current/dir                                               16:00
->

我知道zsh中的RPROMPT,但是它的右对齐提示与您的普通提示相反,普通提示与您键入的文字在同一行。

有没有办法使多行命令提示符的第一行右对齐?我正在寻找PS1变量中的指令“现在立即对齐”或PS1就像RPROMPT对PROMPT一样的变量。

谢谢!

Answers:



10

我也一直在寻找。对我而言,precmd()绘制的线条不会在调整大小时或在^L用于清除屏幕时重新绘制,这一事实一直困扰着我。我现在正在做的是使用ANSI转义序列将光标移动一点。尽管我怀疑有一种更优雅的发行方式,但是这对我有用:

_newline=$'\n'
_lineup=$'\e[1A'
_linedown=$'\e[1B'

PROMPT=...whatever...${_newline}...whatever...
RPROMPT=%{${_lineup}%}...whatever...%{${_linedown}%}

请记住,zsh手册指出%{...%}用于不移动光标的原义转义序列。即便如此,我仍在使用它们,因为它们允许忽略其内容的长度(虽然无法弄清楚如何发出使用它们移动光标的转义符)


在玩了一段时间后,它偶尔会弄乱,并将光标或日期放在错误的行上。不过没什么大不了的。只需按Enter即可对其进行更正。
mpen 2015年

3

这就是我刚才配置此东西的方式。这种方法不需要任何转义序列操作,但是会使您在主提示中具有两个不同的变量:PS1带颜色和NPS1不带颜色。

# Here NPS1 stands for "naked PS1" and isn't a built-in shell variable. I've
# defined it myself for PS1-PS2 alignment to operate properly.
PS1='%S%F{red}[%l]%f%s %F{green}%n@%m%f %B%#%b '
NPS1='[%l] %n@%m # '
RPS1='%B%F{green}(%~)%f%b'

# Hook function which gets executed right before shell prints prompt.
function precmd() {
    local expandedPrompt="$(print -P "$NPS1")"
    local promptLength="${#expandedPrompt}"
    PS2="> "
    PS2="$(printf "%${promptLength}s" "$PS2")"
}

请注意print -P用于快速扩展,${#variable}用于获取存储在变量中的字符串的长度以及printf "%Nd"用于N空格的左侧填充的用法。这两个printprintf是内置命令,所以应该没有性能损失。


1

让我们用这种布局定义提示:

top_left              top_right
bottom_left        bottom_right

为此,我们需要一个函数来告诉我们给定字符串在打印时需要多少个字符。

# 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 '❎'          => 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
}

我们需要另一个函数,该函数需要两个参数,并在屏幕的相对侧上完全打印这些参数。

# Usage: fill-line LEFT RIGHT
#
# Prints LEFT<spaces>RIGHT with enough spaces in the middle
# to fill a terminal line.
function fill-line() {
  emulate -L zsh
  local left_len=$(prompt-length $1)
  local right_len=$(prompt-length $2 9999)
  local pad_len=$((COLUMNS - left_len - right_len - ${ZLE_RPROMPT_INDENT:-1}))
  if (( pad_len < 1 )); then
    # Not enough space for the right part. Drop it.
    echo -E - ${1}
  else
    local pad=${(pl.$pad_len.. .)}  # pad_len spaces
    echo -E - ${1}${pad}${2}
  fi
}

最后,我们可以定义一个函数来设置PROMPTRPROMPT,指示ZSH在每个提示之前调用它,并设置适当的提示扩展选项:

# Sets PROMPT and RPROMPT.
#
# Requires: prompt_percent and no_prompt_subst.
function set-prompt() {
  emulate -L zsh
  local git_branch="$(git rev-parse --abbrev-ref HEAD 2>/dev/null)"
  git_branch=${${git_branch//\%/%%}/\\/\\\\\\}  # escape '%' and '\'

  local top_left='%F{blue}%~%f'
  local top_right="%F{green}${git_branch}%f"
  local bottom_left='%B%F{%(?.green.red)}%#%f%b '
  local bottom_right='%F{yellow}%T%f'

  PROMPT="$(fill-line "$top_left" "$top_right")"$'\n'$bottom_left
  RPROMPT=$bottom_right
}

autoload -Uz add-zsh-hook
add-zsh-hook precmd set-prompt
setopt noprompt{bang,subst} prompt{cr,percent,sp}

这将产生以下提示:

~/foo/bar                     master
%                             10:51
  • 左上方:蓝色当前目录。
  • 右上方:Green Git分支。
  • 左下:#如果是root,%则不是;绿色表示成功,红色表示错误。
  • 右下:黄色当前时间。

您可以在“ 多行提示”中找到更多详细信息本要点中缺少的成分和完整的代码。


1
欢迎来到超级用户!虽然从理论上讲这可以回答问题,但最好在此处包括答案的基本部分,并提供链接以供参考。
CaldeiraG

1
@CaldeiraG我根据你的建议改写了我的答案。FWIW,我对这个问题的最高投票和接受的答案告诉了我最初的答案。
Roman Perepelitsa

看起来更好!:p在这里
过得
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.