How to print colored text below the prompt in zsh widget?

576 Views Asked by At

I want to create a widget bound to a hotkey that prints the current command description in rich text below the prompt, then erases it after a keypress. Like so (simplified):

widget() {
  zle -R "ls - list files"
  read -k 1
}
zle -N widget
bindkey '\eg' widget

But zle -R can print only plain text, it doesn't even support line breaks. I want to print text with color and line breaks like ^[[31mls^[[00m - list files.

What approaches can I use to do that?

Just printing it to a regular stdout then initializing a new prompt would be a bad user interface for my use case. That would be an undesirable solutions.

I want it to appear below the prompt and work similarly to zsh-autocomplete, ctrl+R, or fzf. The output doesn't have any complex interactions, it only appears on hotkey and disappears on keypress after that.

The zsh-autocomplete repo does similar, but I don’t know how it is done.

1

There are 1 best solutions below

2
James Risner On

When you use zle -R, you end up calling this function inside zsh. The first argument after -R is stored in statusline. That is checked here and stripped of any characters that would modify the screen unexpectedly here. So the mailing list question from 2011 is still present today.

Upon review of the hooks and the code in zsh-autocomplete, I now understand this works by using built in support for color highlighting in zsh while actively using autocomplete.

Since you just wish to have a key binding to display the status line and await for a keystroke, you will need to write (in C) a module to display the current BUFFER and await a keystroke. This will require you to track the cursor position and maintaining the same state that zsh is expecting.

The example module is a start. You will need to compile it with zsh, as the build script builds all modules with tools needed to create the .mdh/.pro files from the .mdd of your module.

Here is an updated widget that demonstrates the various options:

widget() {
  autoload -Uz colors && colors
  PREDISPLAY="PRE:"
  POSTDISPLAY="${fg[red]}POST$reset_color"
  zle -R "$BUFFER" "${fg[red]}line below$reset_color"
  print "${fg[red]}print$reset_color"
  read -k 1
}
zle -N widget

Notice, you can't control the display of the status line, it has all special characters removed. The print properly allows special characters, but only on the input line (not on the status line). PRE/POST also only work on the input line, and also are stripped of special characters.

What approaches can I use to do that?

  • Write your own module. This is the best way.
  • you may be able to use print to color it as you wish, but you would need to still make sure the screen positions are maintained as zsh expects.