zsh justowanie w ps1


21

Chciałbym wieloliniowego monitu zsh z odpowiednio wyprostowaną częścią, który będzie wyglądał mniej więcej tak:

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

Wiem o RPROMPT w zsh, ale ma on wyrównany do prawej znak zachęty przeciwny do zwykłego znaku zachęty, który znajduje się w tym samym wierszu tekstu co pisanie.

Czy istnieje sposób na wyrównanie do prawej części pierwszego wiersza wiersza polecenia zawierającego wiele wierszy? Szukam albo dyrektywy w zmiennej PS1, która mówi „wyrównaj teraz”, albo zmiennej, która jest dla PS1 tym, czym RPROMPT jest dla PROMPT.

Dzięki!

Odpowiedzi:


12

Znajdziesz tu szczegółowe odpowiedzi i przykład tutaj . Chodzi o to, aby napisać wiersz przed PS1 za pomocą precmdwywołania zwrotnego, użyć $COLUMNSi trochę matematyki, aby obliczyć pozycję tekstu po prawej stronie ekranu. Znajomość sekwencji ucieczki pomoże ci również w pozycjonowaniu kursora i kolorowaniu.

Innym rozwiązaniem może być użycie motywu z Oh My ZSH .


10

Też tego szukałem. Dla mnie fakt, że precmd()narysowane linie nie przerysowują przy zmianie rozmiaru lub kiedy ^Ljest używany do wyczyszczenia ekranu, sprawiał, że ciągle mnie swędziło. Teraz robię sekwencje specjalne ANSI, aby nieco przesunąć kursor. Chociaż podejrzewam, że istnieje bardziej elegancki sposób ich wydawania, działa to dla mnie:

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

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

Należy pamiętać, że instrukcja zsh stwierdza, że% {...%} dotyczy dosłownych sekwencji ucieczki , które nie poruszają kursorem . Mimo to używam ich, ponieważ pozwalają zignorować długość jego zawartości (chociaż nie mogłem wymyślić, jak wydać ucieczkę, która porusza kursorem za ich pomocą)


Po zabawie z tym przez jakiś czas, od czasu do czasu psuje i ustawia kursor lub datę w niewłaściwym wierszu. Nie jest to jednak wielka sprawa; wystarczy nacisnąć Enter, aby to poprawić.
mpen

3

Oto jak właśnie skonfigurowałem tę rzecz. To podejście nie wymaga żadnych manipulacji sekwencjami ucieczki, ale sprawi, że będziesz mieć dwie różne zmienne dla głównego monitu: PS1z kolorowaniem i NPS1bez.

# 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")"
}

Zwróć uwagę na użycie print -Pdo szybkiego rozwijania, ${#variable}do uzyskania długości łańcucha przechowywanego w zmiennej oraz printf "%Nd"do wypełniania lewej strony Nspacjami. Zarówno printi printfsą wbudowane w poleceniach, więc nie powinno być hitem wydajność.


1

Zdefiniujmy monit za pomocą tego układu:

top_left              top_right
bottom_left        bottom_right

Aby to zrobić, potrzebujemy funkcji, która mówi nam, ile znaków dany ciąg przyjmuje podczas drukowania.

# 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
}

Będziemy potrzebować innej funkcji, która pobierze dwa argumenty i wypisuje pełną grzywnę z tymi argumentami po przeciwnych stronach ekranu.

# 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
}

Wreszcie możemy zdefiniować funkcję, która ustawia PROMPTi RPROMPTpoinstruować ZSH, aby wywoływał ją przed każdym monitem, oraz ustawić odpowiednie opcje rozszerzania monitu:

# 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}

Powoduje to wyświetlenie następującego monitu:

~/foo/bar                     master
%                             10:51
  • Lewy górny róg: niebieski bieżący katalog.
  • U góry po prawej: oddział Green Git.
  • U dołu po lewej: #jeśli root, %jeśli nie; zielony w przypadku sukcesu, czerwony w przypadku błędu.
  • Na dole po prawej: Żółty aktualny czas.

Możesz znaleźć dodatkowe szczegóły w wierszu Wieloliniowym: brakujący składnik i pełny kod w tej liście .


1
Witamy w Super User! Chociaż teoretycznie może to odpowiedzieć na pytanie, lepiej byłoby zawrzeć tutaj istotne części odpowiedzi i podać odnośnik.
CaldeiraG

1
@CaldeiraG Przepisałem swoją odpowiedź zgodnie z twoją sugestią. FWIW, kształt mojej oryginalnej odpowiedzi został podany przez osobę, która uzyskała najwyższą głosowanie i przyjęła odpowiedź na to pytanie.
Roman Perepelitsa

Wygląda o wiele lepiej! : p Życzymy miłego pobytu tutaj
CaldeiraG
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.