The telnet protocol, described in RFC 854, includes a way to send in-band commands, consisting of the IAC character, '\255'
, followed by several more bytes. These commands can do things like send an interrupt to the remote, but typically they're used to send options.
A detailed look at an exchange that sends the terminal type option can be found in Microsoft Q231866.
The window size option is described in RFC 1073. The client first sends its willingness to send an NAWS
option. If the server replies DO NAWS
, the client can then send the NAWS
option data, which is comprised of two 16-bit values.
Example session, on a 47 row 80 column terminal:
telnet> set options
Will show option processing.
telnet> open localhost
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
SENT WILL NAWS
RCVD DO NAWS
SENT IAC SB NAWS 0 80 (80) 0 47 (47)
The ssh protocol is described in RFC 4254. It consists of a stream of messages. One such message is "pty-req"
, which requests a pseudo-terminal, and its parameters include the terminal height and width.
byte SSH_MSG_CHANNEL_REQUEST
uint32 recipient channel
string "pty-req"
boolean want_reply
string TERM environment variable value (e.g., vt100)
uint32 terminal width, characters (e.g., 80)
uint32 terminal height, rows (e.g., 24)
uint32 terminal width, pixels (e.g., 640)
uint32 terminal height, pixels (e.g., 480)
string encoded terminal modes
The telnet and ssh clients will catch the SIGWINCH
signal, so if you resize a terminal window during a session, they will send an appropriate message to the server with the new size. Ssh sends the Window Dimension Change Message:
byte SSH_MSG_CHANNEL_REQUEST
uint32 recipient channel
string "window-change"
boolean FALSE
uint32 terminal width, columns
uint32 terminal height, rows
uint32 terminal width, pixels
uint32 terminal height, pixels
You could do something like:
branch=master mode=insert
setopt promptsubst
left='%m | %~'
PS1='%K{green}$left${(l,COLUMNS-${#${(%)left}},)${${:-$branch | $mode}//[%]/%%}}%k$ '
We use the ${(l,length,)...}
left-pad operator to pad the right hand side of the prompt with $COLUMNS
minus the length of what was displayed on the left hand side.
For $mode
to be updated when you press Insert, you'd do:
update-mode() {
case $KEYMAP in
(main)
case $ZLE_STATE in
(*insert*) mode=insert;;
(*) mode=overwrite
esac;;
(*) mode=$KEYMAP
esac
[[ $mode = $oldmode ]] || zle reset-prompt
}
overwrite-mode() {
zle ".$WIDGET"
update-mode
}
zle -N overwrite-mode
That causes the prompt to be redrawn when you switch mode.
For vi
mode, you'd also need to consider all the cases where the insert mode is entered (in insert mode (a
/A
, i
/I
, c
/C
, s
/S
) or overwrite (R
)), which you can do with the zle-keymap-select
special hook widget (as you found out):
zle -N zle-keymap-select update-mode
I'd rewrite your final solution as:
bindkey -v
# Bind the <Insert> key.
bindkey $terminfo[kich1] overwrite-mode
setopt promptsubst
(){ # local scope
local left right invisible leftcontent
# User name.
left+='%B%F{black}%K{green} %n '
# Current working directory.
left+='%K{yellow} %~ '
# Version control branch.
right='${vcs_info_msg_0_:+${vcs_info_msg_0_//[%]/%%} }'
# Virtualenv.
export VIRTUAL_ENV_DISABLE_PROMP=1
right+='${VIRTUAL_ENV:+venv }'
# Editing mode. $ZLE_MODE shouldn't contain %, no need to escape
ZLE_MODE=insert
right+='%K{green} $ZLE_MODE'
# closing
right+=$' %k%f%b'
# Combine left and right prompt with spacing in between.
invisible='%([BSUbfksu]|([FBK]|){*})'
leftcontent=${(S)left//$~invisible}
rightcontent=${(S)right//$~invisible}
PS1="$left\${(l,COLUMNS-\${#\${(%):-$leftcontent$rightcontent}},)}$right%{"$'\n%}$ '
}
autoload vcs_info
precmd() vcs_info
update-mode() {
case $KEYMAP in
(main)
case $ZLE_STATE in
(*insert*) ZLE_MODE=insert;;
(*) ZLE_MODE=overwrite
esac;;
(*) ZLE_MODE=$KEYMAP
esac
[[ $ZLE_MODE = $oldmode ]] || zle reset-prompt
}
overwrite-mode() {
zle ".$WIDGET"
update-mode
}
zle -N overwrite-mode
zle -N zle-keymap-select update-mode
That would minimise the number of unnecessary forks and prompt redraws, and allow the shell to draw the prompt correctly in all circumstances including when the windows is being resized.
Screenshot:
Best Answer
Zsh's prompt expansion behaviour is defined in
man zshmisc
. Regarding setting custom values for prompt length it says:From that you can infer
PROMPT="%/ "
will give you a working directoryPROMPT='%10<..<%/ '
will give you a working directory - if the working directory string is longer than 10 chars it will be truncated on the left edge, and where the truncation occurs, the..
pattern will be displayed (to indicate trimmed value)The terminal width can be obtained with
$COLUMNS
, so if you want a prompt constrained at 25% of the terminal width replace the10
with a variable:i.e.