zsh rechtsbündig in ps1

21

Ich hätte gerne eine mehrzeilige zsh-Eingabeaufforderung mit einem rechtsseitigen Teil, die ungefähr so ​​aussieht:

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

Ich kenne RPROMPT in zsh, aber das hat eine rechtsbündige Eingabeaufforderung gegenüber Ihrer normalen Eingabeaufforderung, die sich in derselben Textzeile befindet wie Ihre Eingabe.

Gibt es eine Möglichkeit, einen rechtsbündigen Teil zur ersten Zeile einer mehrzeiligen Eingabeaufforderung zu setzen? Ich suche entweder eine Direktive in der PS1-Variablen, die 'Right Align Now' sagt, oder eine Variable, die PS1 entspricht, was RPROMPT PROMPT bedeutet.

Vielen Dank!

So8res
quelle

Antworten:

12

Eine ausführliche Antwort und ein Beispiel finden Sie hier . Die Idee ist, die Zeile vor PS1 mit precmdRückruf, Verwendung $COLUMNSund ein wenig Mathematik zu schreiben , um die Position des Texts auf der rechten Seite des Bildschirms zu berechnen. Die Kenntnis der Escape-Sequenzen hilft Ihnen auch beim Positionieren und Färben des Cursors.

Eine andere Lösung kann sein, ein Thema von Oh My ZSH zu verwenden .

Pablo Castellazzi
quelle
10

Ich habe auch danach gesucht. Für mich war die Tatsache, dass precmd()gezeichnete Linien bei der Größenänderung oder beim ^LLöschen des Bildschirms nicht neu gezeichnet werden, etwas, das mich immer wieder juckte. Ich verwende jetzt ANSI-Escape-Sequenzen , um den Cursor ein wenig zu bewegen. Obwohl ich vermute, dass es eine elegantere Möglichkeit gibt, sie auszustellen, funktioniert dies für mich:

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

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

Beachten Sie, dass im zsh-Handbuch angegeben ist , dass% {...%} für wörtliche Escape-Sequenzen steht , die den Cursor nicht bewegen . Trotzdem benutze ich sie, weil sie es erlauben, die Länge ihres Inhalts zu ignorieren (ich konnte jedoch nicht herausfinden, wie das Escape ausgegeben wird, das den Cursor mit ihnen bewegt).

ferhtgoldaraz
quelle
Nachdem Sie einige Zeit damit herumgespielt haben, kommt es gelegentlich zu einem Durcheinander, und der Cursor oder das Datum werden in die falsche Zeile gesetzt. Keine so große Sache; kann einfach die Eingabetaste drücken, um es zu korrigieren.
Mpen
3

Hier ist, wie ich dieses Ding gerade konfiguriert habe. Bei diesem Ansatz sind keine Manipulationen der Escape-Sequenz erforderlich, es stehen jedoch zwei verschiedene Variablen für die primäre Eingabeaufforderung zur Verfügung: PS1mit und NPS1ohne Farbgebung .

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

Beachten Sie die Verwendung von print -Pzur sofortigen Erweiterung, ${#variable}zum Abrufen der Länge der in Variablen gespeicherten Zeichenfolge und printf "%Nd"zum Auffüllen der linken Seite mit NLeerzeichen. Beide printund printfsind eingebaute Befehle, daher sollte es keine Leistungseinbußen geben.

firegurafiku
quelle
1

Definieren wir die Eingabeaufforderung mit diesem Layout:

top_left              top_right
bottom_left        bottom_right

Dazu benötigen wir eine Funktion, die angibt, wie viele Zeichen eine bestimmte Zeichenfolge beim Drucken benötigt.

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

Wir benötigen eine weitere Funktion, die zwei Argumente verwendet und mit diesen Argumenten eine vollständige Geldstrafe auf den gegenüberliegenden Seiten des Bildschirms ausgibt.

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

Schließlich können wir eine Funktion definieren, die setzt PROMPTund RPROMPT, ZSH anweist, sie vor jeder Eingabeaufforderung aufzurufen, und entsprechende Optionen für die Erweiterung der Eingabeaufforderung festlegen:

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

Dies erzeugt die folgende Eingabeaufforderung:

~/foo/bar                     master
%                             10:51
  • Oben links: Blaues aktuelles Verzeichnis.
  • Oben rechts: Grüner Git-Zweig.
  • Unten links: #wenn root, %wenn nicht; Grün bei Erfolg, Rot bei Fehler.
  • Unten rechts: Gelbe aktuelle Uhrzeit.

Weitere Details finden Sie in der mehrzeiligen Eingabeaufforderung: Die fehlende Zutat und der vollständige Code in dieser Übersicht .

Roman Perepelitsa
quelle
1
Willkommen bei Super User! Während dies theoretisch die Frage beantworten mag, wäre es vorzuziehen , die wesentlichen Teile der Antwort hier aufzunehmen und den Link als Referenz bereitzustellen.
CaldeiraG
1
@CaldeiraG Ich habe meine Antwort auf Ihren Vorschlag umgeschrieben. FWIW, die Form meiner ursprünglichen Antwort wurde durch die am höchsten bewertete und akzeptierte Antwort auf diese Frage bestimmt.
Roman Perepelitsa
Sieht viel besser aus! : p Genießen Sie Ihren Aufenthalt hier
CaldeiraG