ZSH правого выравнивания в PS1


21

Мне бы хотелось многострочное приглашение zsh с выровненной справа частью, которое будет выглядеть примерно так:

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

Я знаю о RPROMPT в zsh, но он выровнен по правому краю напротив вашего обычного приглашения, которое находится на той же строке текста, что и вы печатаете.

Есть ли способ получить выравнивание по правому краю в первой строке многострочной командной строки? Я ищу либо директиву в переменной PS1, которая говорит «выравнивание по правому краю сейчас», либо переменную, которая для PS1, что RPROMPT для PROMPT.

Благодарность!

Ответы:


12

Подробный ответ и пример вы найдете здесь . Идея состоит в том, чтобы написать строку перед PS1, используя precmdфункцию обратного вызова, $COLUMNSнемного математики, чтобы вычислить позицию текста в правой части экрана. Знание escape-последовательностей также поможет вам с позиционированием и раскраской курсора.

Другим решением может быть использование темы от Oh My ZSH .


10

Я тоже искал это. Для меня тот факт, что precmd()нарисованные линии не перерисовываются при изменении размера или когда ^Lиспользуется для очистки экрана, был чем-то, что вызывало у меня зуд. Сейчас я использую escape-последовательности ANSI, чтобы немного переместить курсор. Хотя я подозреваю, что есть более элегантный способ их выдачи, это работает для меня:

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

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

Помните, что руководство zsh утверждает, что% {...%} предназначено для буквенных escape-последовательностей, которые не перемещают курсор . Тем не менее, я использую их, потому что они позволяют игнорировать длину его содержимого (хотя я не мог понять, как создать escape, который перемещает курсор, используя их)


После того, как вы поэкспериментируете с этим в течение некоторого времени, он иногда портится и помещает курсор или дату в неправильную строку. Не такое уж большое дело, хотя; можно просто нажать ввод, чтобы исправить это.
mpen

3

Вот как я настроил эту вещь только сейчас. Этот подход не требует каких-либо манипуляций с escape-последовательностями, но заставит вас иметь две разные переменные для основного приглашения: 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пробелами. Оба printи printfявляются встроенными командами, поэтому не должно быть никакого снижения производительности.


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
}

Наконец, мы можем определить функцию, которая устанавливает PROMPTи RPROMPTинструктирует 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.
  • Внизу слева: #если корень, %если нет; зеленый при успехе, красный при ошибке.
  • Справа внизу: желтое текущее время.

Вы можете найти дополнительные подробности в многострочном запросе: отсутствующий ингредиент и полный код в этой сущности .


1
Добро пожаловать в Супер пользователя! Хотя это может теоретически ответить на вопрос, было бы предпочтительным включить здесь основные части ответа и предоставить ссылку для справки.
CaldeiraG

1
@CaldeiraG Я переписал свой ответ после вашего предложения. FWIW, форма моего первоначального ответа была подтверждена самым высоко оцененным и принятым ответом на этот вопрос.
Роман Перепелица

Выглядит намного лучше! : p Приятного пребывания здесь
CaldeiraG
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.