Progress bars for apt in shell24 Mar 2019
For a couple years now, I use M-x
shell as my main shell. Recently, I have fixed one of
the minor annoyances that go along with using
shell in Emacs. At least since Ubuntu 18.04, the
terminal "progress bar" feature, displayed below is non-optional:
It uses terminal escape codes to display the progress bar, and
shell-mode can't handle them well,
so they clobber a lot of the output.
Initial work around
Previously, I was using this work around, since
apt-get doesn't display the progress bar:
# sudo apt upgrade sudo apt-get upgrade
Progress bar in the mode line
But typing 4 extra chars is hard. And
apt-get will likely get these progress bars at some point as
well. So I spent around an hour of my weekend hacking an Elisp solution. Here is the code:
(advice-add 'ansi-color-apply-on-region :before 'ora-ansi-color-apply-on-region) (defun ora-ansi-color-apply-on-region (begin end) "Fix progress bars for e.g. apt(8). Display progress in the mode line instead." (let ((end-marker (copy-marker end)) mb) (save-excursion (goto-char (copy-marker begin)) (while (re-search-forward "\0337" end-marker t) (setq mb (match-beginning 0)) (when (re-search-forward "\0338" end-marker t) (ora-apt-progress-message (substring-no-properties (delete-and-extract-region mb (point)) 2 -2))))))) (defun ora-apt-progress-message (progress) (setq mode-line-process (if (string-match "Progress: \\[ *\\([0-9]+\\)%\\]" progress) (list (concat ":%s " (match-string 1 progress) "%%%% ")) '(":%s"))) (force-mode-line-update))
The solution will detect e.g.
"\0337...Progress: [ 25%]...\0338", remove it from the shell buffer
"25%" in the mode line instead.
Use the Echo Area instead of the mode line
The above is a good enough solution specifically for
apt(8), but not for the generic case. Let's
try to emulate how e.g.
gnome-terminal handles these escape sequences. It takes sequences like
"\0337.*\0338" and displays them in the bottom of the window. Kind of like the Emacs Echo
Area. That's easy enough to do:
(defun ora-apt-progress-message (progress) (message (replace-regexp-in-string "%" "%%" (ansi-color-apply progress))))
Above, we use
ansi-color-apply to get rid of any extra terminal escape codes. I decided to stay
with the Echo Area version instead of the mode line version. Here's how it looks like:
You can find all of the above code in my config. Happy hacking!