Progress bars for apt in shell

Intro

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 and display "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!