Clear screen and preserve scroll buffer on Wezterm
Posted on 10 Dec, 2023
I have been using Wezterm for quite a while now. With built-in support for multiple tabs and panes, a flexible Lua config, and many features, my terminal experience has never been better. But it has its quirks.
One thing that has been bothering me for a while is Wezterm losing the contents of my screen when I hit Ctrl-L to clear the screen. I tried the same on the default macOS Terminal, and it preserves the contents in the scroll buffer but not on Wezterm.
Reading more into it, Ctrl-L
prints \33[H\33[2J
to the screen. Running a
simple printf '\33[H\33[2J'
verified it. Contents on my screen did vanish from
my scrollback buffer.
Solution
I wrote a simple function in my ~/.zshrc
to resolve this:
my-clear() {
for i in {3..$(tput lines)}
do
printf '\n'
done
printf '\33[H\33[2J'
}
The above code gets the number of lines for the current terminal, i.e., the height of the terminal, using tput lines and prints \n (new lines). In the end, it prints the magic string \33[H\33[2J to clear the contents of the screen, which, in this case, are empty lines that I don't need.
The loop runs from 3 to tput lines (inclusive). It does not start from 1 because my prompt is a two-liner, and printing \33[H\33[2J takes it into account (somehow).
Key binding
I will never use my-clear
as a command to clear my screen. To bind Ctrl-L
to
this command, I used bindkey
(since I use ZSH):
bindkey -s '^L' '^Qmy-clear\n'
bindkey -s
sends the signals directly, so here is how it works:
^Q
: Clears the line and restores once the current command executes.my-clear
: The function name.\n
: Hit return to run the function.
The only issue with this is that my history now has an extra command, so either I need to train my reflexes to check for the last command (rather than mindlessly executing the previous command), or I figure out how to clear the session history (which seems very unlikely from first impressions).
ZSH Widgets
There is another solution to this: using widgets instead of sending in the command directly.
my-clear() {
for i in {3..$(tput lines)}
do
printf '\n'
done
printf '\33[H\33[2J'
zle reset-prompt
}
zle -N my-clear
bindkey '^L' my-clear
zle -N
creates a new widget from the function my-clear
. We bind the key
directly to the widget (without the -s
flag). We also need to reset the prompt
at the end of executing the widget since ZSH doesn't redraw the prompt when
executing a widget.
And voila! We can now use Ctrl-L
to clear the screen and preserve everything.
A final refinement can be to combine the printf
and reset-prompt
statements with the clear-screen
widget.
Final code
my-clear() {
for i in {3..$(tput lines)}
do
printf '\n'
done
zle clear-screen
}
zle -N my-clear
bindkey '^L' my-clear