zsh justifica à direita no ps1

21

Eu gostaria de um prompt zsh de várias linhas com uma parte alinhada à direita, que será algo como isto:

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

Eu sei sobre o RPROMPT no zsh, mas que possui um prompt alinhado à direita, oposto ao prompt normal, que está na mesma linha de texto da sua digitação.

Existe uma maneira de ter uma parte alinhada à direita na primeira linha de um prompt de comando com várias linhas? Estou procurando por uma diretiva na variável PS1 que diga 'alinhar agora' ou uma variável que seja para PS1 o que RPROMPT é PROMPT.

Obrigado!

So8res
fonte

Respostas:

12

Você encontrará uma resposta detalhada e um exemplo aqui . A idéia é escrever a linha antes do PS1 usando o precmdretorno de chamada, o uso $COLUMNSe um pouco de matemática para calcular a posição do texto no lado direito da tela. O conhecimento das seqüências de escape também ajudará no posicionamento e coloração do cursor.

Outra solução pode ser usar um tema de Oh My ZSH .

Pablo Castellazzi
fonte
10

Eu também estava procurando por isso. Para mim, o fato de que as precmd()linhas desenhadas não são redesenhadas ao redimensionar ou quando ^Lé usado para limpar a tela foi algo que me incomodou. O que estou fazendo agora é usar seqüências de escape ANSI para mover o cursor um pouco. Embora eu suspeite que exista uma maneira mais elegante de publicá-los, isso está funcionando para mim:

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

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

Lembre-se de que o manual zsh afirma que% {...%} é para seqüências de escape literais que não movem o cursor . Mesmo assim, estou usando-os porque eles permitem ignorar o tamanho de seu conteúdo (não foi possível descobrir como emitir a fuga que move o cursor usando-os)

ferhtgoldaraz
fonte
Depois de brincar com isso por algum tempo, ele erra ocasionalmente e coloca o cursor ou a data na linha errada. Não é um grande negócio; basta pressionar enter para corrigi-lo.
MPEN
3

Aqui está como eu configurei essa coisa agora. Essa abordagem não requer nenhuma manipulação de sequência de escape, mas fará com que você tenha duas variáveis ​​diferentes para o prompt primário: PS1com coloração e NPS1sem.

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

Observe o uso de print -Ppara expansão rápida, ${#variable}para obter o comprimento da string armazenada em variável e printf "%Nd"para preenchimento à esquerda com Nespaços. Ambos printe printfsão comandos internos, portanto, não deve haver desempenho atingido.

firegurafiku
fonte
1

Vamos definir prompt com este layout:

top_left              top_right
bottom_left        bottom_right

Para fazer isso, precisaremos de uma função que nos diga quantos caracteres uma determinada string leva quando impressa.

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

Precisamos de outra função que use dois argumentos e imprima uma multa completa com esses argumentos nos lados opostos da tela.

# 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 uma função que define PROMPTe RPROMPT, instruir o ZSH para chamá-la antes de cada prompt, e definir opções de expansão de prompt apropriadas:

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

Isso produz o seguinte prompt:

~/foo/bar                     master
%                             10:51
  • Superior esquerdo: diretório atual azul.
  • Superior direito: Ramo verde do Git.
  • Parte inferior esquerda: #se raiz, %se não; verde em caso de sucesso, vermelho em caso de erro.
  • Em baixo à direita: hora atual amarela.

Você pode encontrar detalhes adicionais no prompt de várias linhas: O ingrediente que falta e o código completo nesta lista .

Roman Perepelitsa
fonte
1
Bem-vindo ao Super Usuário! Embora isso possa teoricamente responder à pergunta, seria preferível incluir aqui as partes essenciais da resposta e fornecer o link para referência.
CaldeiraG
1
@CaldeiraG Reescrevi minha resposta seguindo sua sugestão. FWIW, o formato da minha resposta original foi informado pela resposta mais votada e aceita nesta questão.
Roman Perepelitsa
Parece muito melhor! : p Aproveite a sua estadia aqui
CaldeiraG