zsh just-just in ps1

21

Je voudrais une invite zsh sur plusieurs lignes avec une partie alignée à droite, qui ressemblera à ceci:

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

Je connais RPROMPT dans zsh, mais cela a une invite alignée à droite en face de votre invite normale, qui se trouve sur la même ligne de texte que votre saisie.

Existe-t-il un moyen d'avoir une partie alignée à droite sur la première ligne d'une invite de commande multiligne? Je recherche soit une directive dans la variable PS1 qui dit «aligner à droite maintenant» ou une variable qui est à PS1 ce que RPROMPT est à PROMPT.

Merci!

So8res
la source

Réponses:

12

Vous trouverez une réponse détaillée et un exemple ici . L'idée est d'écrire la ligne avant PS1 en utilisant le precmdrappel, l'utilisation $COLUMNSet un peu de calcul pour calculer la position du texte sur le côté droit de l'écran. La connaissance des séquences d'échappement vous aidera également à positionner et à colorier le curseur.

Une autre solution peut être d'utiliser un thème d' Oh My ZSH .

Pablo Castellazzi
la source
10

Je le cherchais aussi. Pour moi, le fait que les precmd()lignes tracées ne se redessinent pas lors du redimensionnement ou quand ^Lest utilisé pour effacer l'écran était quelque chose qui me démangeait. Ce que je fais maintenant, c'est utiliser des séquences d'échappement ANSI pour déplacer un peu le curseur. Bien que je soupçonne qu'il existe une façon plus élégante de les émettre, cela fonctionne pour moi:

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

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

Gardez à l'esprit que le manuel zsh indique que% {...%} est destiné aux séquences d'échappement littérales qui ne déplacent pas le curseur . Malgré tout, je les utilise parce qu'ils permettent d'ignorer la longueur de son contenu (je ne pouvais pas comprendre comment émettre l'échappement qui déplace le curseur en les utilisant cependant)

ferhtgoldaraz
la source
Après avoir joué avec cela pendant un certain temps, il gâche parfois et place le curseur ou la date sur la mauvaise ligne. Pas si grave cependant; peut simplement appuyer sur entrée pour le corriger.
mpen
3

Voici comment j'ai configuré cette chose tout à l'heure. Cette approche ne nécessite aucune manipulation de séquence d'échappement, mais vous aurez deux variables différentes pour l'invite principale: PS1avec coloration et NPS1sans.

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

Notez l'utilisation de print -Ppour l'expansion rapide, ${#variable}pour obtenir la longueur de la chaîne stockée dans une variable et printf "%Nd"pour le remplissage à gauche avec des Nespaces. Les deux printet printfsont des commandes intégrées, donc il ne devrait y avoir aucun impact sur les performances.

firegurafiku
la source
1

Définissons l'invite avec cette disposition:

top_left              top_right
bottom_left        bottom_right

Pour ce faire, nous aurons besoin d'une fonction qui nous indique combien de caractères une chaîne donnée prend lors de l'impression.

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

Nous aurons besoin d'une autre fonction qui prend deux arguments et affiche une amende complète avec ces arguments sur les côtés opposés de l'écran.

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

Enfin, nous pouvons définir une fonction qui définit PROMPTet RPROMPT, demander à ZSH de l'appeler avant chaque invite, et définir les options d'extension d'invite appropriées:

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

Cela produit l'invite suivante:

~/foo/bar                     master
%                             10:51
  • En haut à gauche: répertoire actuel bleu.
  • En haut à droite: branche Green Git.
  • En bas à gauche: #si root, %sinon; vert en cas de succès, rouge en cas d'erreur.
  • En bas à droite: heure actuelle jaune.

Vous pouvez trouver des détails supplémentaires dans l' invite multiligne: l'ingrédient manquant et le code complet dans cet essentiel .

Roman Perepelitsa
la source
1
Bienvenue sur Super User! Bien que cela puisse théoriquement répondre à la question, il serait préférable d'inclure ici les parties essentielles de la réponse et de fournir le lien de référence.
CaldeiraG
1
@CaldeiraG J'ai réécrit ma réponse suite à votre suggestion. FWIW, la forme de ma réponse originale a été éclairée par la réponse la plus votée et acceptée sur cette question.
Roman Perepelitsa
Semble beaucoup mieux! : p Profitez de votre séjour ici
CaldeiraG