Mode line region size indicator, revisited

Published 2025-09-18

tag(s): #emacs

I wrote before about my custom mode line.
To briefly recap: it is text-only, based on mood-line which is itself based on the very popular doom-modeline.

Region size indicator

From my time using doom-modeline, I came to appreciate the convenience of having the region size displayed. Yes, I can use M-= (default binding for count-words-region)...but turns out that it presents the same problem that my mode line indicator had!

A partial screenshot of an Emacs mode line.
(direct link to image)

You can see the region size displayed above. The way this information was calculated until this morning:


(setq-default mode-line-position
     (list "%l:%c"
           '(:propertize " %p%  %I" face shadow)
           '(:eval
             (when (use-region-p)
               (propertize (format " (%sL:%sC)"
                                   (count-lines (region-beginning)
                                                (region-end))
                                   (- (region-end) (region-beginning)))
                           'face 'shadow)))
           " "))
    

Counting the lines in the region was provided by (count-lines (region-beginning) (region-end)), and then the number of characters was simply the difference between the positions of the beginning and end of the region.
Except...

rectangle-mark-mode

The problem is, when the region is a rectangle, the positions of the beginning and end of the region do not reflect accurately the character count, because obviously, a number of chars are excluded in each line.

I don't use rectangles that often, and I could live with the count being off the few times I do. But...you know.
Once I realized it was wrong, I had to fix it. 👀

Updated code

My first reaction was to look at how count-words-region does this. To my surprise, it doesn't account for rectangle selections. Maybe I should report this as a bug?
Then I took a look at how to figure out that a rectangle selection is taking place, and from there, if any of the code in rect.el was counting the number of characters. I didn't find anything, but figured that extract-rectangle (used internally in a number of operations) could help me, since it returns the rectangle as a list of strings.


(setq-default mode-line-position
  (list "%l:%c"
        '(:propertize " %p%  %I" face shadow)
        '(:eval
          (when (use-region-p)
            (propertize (apply #'format
                               " (%sL:%sC)"
                               (hoagie--mode-line-region-size))
                        'face 'shadow))
           " ")))

(defun hoagie--mode-line-region-size ()
  "Helper to calculate the region size to display in the mode line.
Returns a list with the number of lines, and the number of characters.
For the latter, it uses a more involved calculation for rectangle selections."
  (let ((lines (count-lines (region-beginning) (region-end))))
    (list lines
          (if rectangle-mark-mode
              ;; first line or last line, both have the same amount of
              ;; characters.
              (* (length (car (extract-rectangle (region-beginning)
                                                 (region-end))))
                 lines)
            ;; regular region
            (- (region-end) (region-beginning))))))
    

A first approach used seq-reduce to sum the length of all lines in the list, then I realized I only need the length of the first line, the rest are the same. Because rectangles. 🙃

After testing this version, I found extract-rectangle-bounds, which returns the positions in each line as a list of conses. I could have used that too, getting the difference in the positions of the first line instead.

I find the code is getting too complicated for something that runs constantly (the mode line is updated all the time). But I guess unless I run into performance problems, it should be fine?
You can see in the screenshot the end result, with the output of M-= in the echo area.

A partial screenshot of an Emacs mode line and the echo area.
(direct link to image)

Share your thoughts (via email)

Back to top

Back to homepage