zsh justifica a la derecha en ps1

21

Me gustaría un indicador zsh de varias líneas con una parte alineada a la derecha, que se verá así:

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

Sé acerca de RPROMPT en zsh, pero tiene un mensaje alineado a la derecha opuesto a su mensaje normal, que está en la misma línea de texto que cuando escribe.

¿Hay alguna manera de tener una porción alineada a la derecha con la primera línea de un símbolo del sistema de varias líneas? Estoy buscando una directiva en la variable PS1 que dice "alinear ahora" o una variable que sea para PS1 lo que RPROMPT es PROMPT.

¡Gracias!

So8res
fuente

Respuestas:

12

Encontrará una respuesta detallada y un ejemplo aquí . La idea es escribir la línea antes de PS1 usando la precmddevolución de llamada, el uso $COLUMNSy un poco de matemática para calcular la posición del texto en el lado derecho de la pantalla. El conocimiento de las secuencias de escape también lo ayudará con el posicionamiento y la coloración del cursor.

Otra solución puede ser usar un tema de Oh My ZSH .

Pablo Castellazzi
fuente
10

He estado buscando esto también. Para mí, el hecho de que las precmd()líneas dibujadas no se vuelven a dibujar en el cambio de tamaño o cuando ^Lse usa para borrar la pantalla fue algo que me seguía picando. Lo que estoy haciendo ahora es usar secuencias de escape ANSI para mover el cursor un poco. Aunque sospecho que hay una forma más elegante de emitirlos, esto está funcionando para mí:

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

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

Tenga en cuenta que el manual de zsh establece que% {...%} es para secuencias de escape literales que no mueven el cursor . Aun así, los estoy usando porque permiten ignorar la longitud de su contenido (aunque no pude averiguar cómo emitir el escape que mueve el cursor usándolos)

ferhtgoldaraz
fuente
Después de jugar con esto durante un tiempo, se equivoca ocasionalmente y coloca el cursor o la fecha en la línea incorrecta. Sin embargo, no es un gran problema; puede presionar enter para corregirlo.
mpen
3

Así es como configuré esto ahora. Este enfoque no requiere ninguna manipulación de la secuencia de escape, pero hará que tenga dos variables diferentes para el indicador primario: PS1con color y NPS1sin él.

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

Tenga en cuenta el uso de print -Ppara la expansión rápida, ${#variable}para obtener la longitud de la cadena almacenada en variable y printf "%Nd"para el relleno izquierdo con Nespacios. Ambos printy printfson comandos integrados, por lo que no debería haber ningún impacto en el rendimiento.

firegurafiku
fuente
1

Definamos prompt con este diseño:

top_left              top_right
bottom_left        bottom_right

Para hacer esto, necesitaremos una función que nos diga cuántos caracteres toma una cadena dada cuando se imprime.

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

Necesitaremos otra función que tome dos argumentos e imprima una multa completa con estos argumentos en los lados opuestos de la pantalla.

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

Finalmente podemos definir una función que establece PROMPTyRPROMPT indique a ZSH que la llame antes de cada solicitud, y establezca las opciones de expansión de solicitud apropiadas:

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

Esto produce el siguiente mensaje:

~/foo/bar                     master
%                             10:51
  • Arriba a la izquierda: directorio actual azul.
  • Arriba a la derecha: rama Green Git.
  • Abajo a la izquierda: #si es raíz,% si no; verde en caso de éxito, rojo en caso de error.
  • Abajo a la derecha: hora actual amarilla.

Puede encontrar detalles adicionales en el mensaje de varias líneas: el ingrediente que falta y el código completo en este resumen .

Perepelitsa romana
fuente
1
¡Bienvenido a Super User! Si bien esto puede responder teóricamente la pregunta, sería preferible incluir aquí las partes esenciales de la respuesta y proporcionar el enlace para referencia.
CaldeiraG
1
@CaldeiraG Reescribí mi respuesta siguiendo su sugerencia. FWIW, la forma de mi respuesta original fue informada por la respuesta más votada y aceptada sobre esta pregunta.
Roman Perepelitsa
¡Se ve mucho mejor! : p Disfrute de su estadía aquí
CaldeiraG