Subject: bug#75168: 31.0.50; text-scale confuses ruler-mode when display-line-number-mode is active

From: Arsen Arsenović <[email-masked]>
Date: Sun, 29 Dec 2024 13:06:03 +0100

Eli Zaretskii <[email-masked]> writes:

>> Date: Sat, 28 Dec 2024 22:15:28 +0100
>> From:  Arsen Arsenović via "Bug reports for GNU Emacs,
>>  the Swiss army knife of text editors" <[email-masked]>
>> 
>> Reproduction steps:
>> 1. emacs -Q
>> 2. M-x display-line-numbers-mode RET
>> 3. M-x ruler-mode RET
>> 4. C-x C-+ C-+ C-+ C-+
>> 
>> You should notice that the ruler is misaligned.
>
> Ruler mode doesn't work well with text-scale, as the comment there says.
>
>> I've hotfixed this in my running Emacs by applying:
>> 
>> modified   lisp/ruler-mode.el
>> @@ -636,7 +636,7 @@ ruler-mode-ruler
>>                  ;; FIXME: ruler-mode relies on I being an integer, so
>>                  ;; the column numbers might be slightly off if the
>>                  ;; line-number face is customized.
>> -                (round (line-number-display-width 'columns))
>> +                (+ (round (line-number-display-width)) 2)
>>                0))
>>           (j (ruler-mode-text-scaled-window-hscroll))
>>           ;; Setup the scrollbar, fringes, and margins areas.
>> 
>> ... and re-evaling the ruler-mode-ruler defun.
>> 
>> Per the line-number-display-width doc, when it is called with 'columns,
>> it returns the number of columns in the frames canonical font size, but
>> that's not the font size that is used for the fringes: the fringes seem
>> to use the font the buffer is using, at least after text-scale-mode does
>> its thing.
>
> I'm not sure I understand what you mean by "fringes seem to use the
> font the buffer is using".  When I type "C-x C-+ C-+...", the fringes
> stay at their original width.  Do you see something different?

Hm, yes, fringe might be the wrong word.  I am referring to the column
where the ruler and line number are, which I've outlined in red:

Sorry about the confusion.

That part of the display appears to use the same font size as the
buffer after text-scale has been enabled.

> Anyway, the change you suggest is basically going back to what we had
> before fixing bug#28855, so I don't think we can make such a change,
> at least not literally.  We need something more complex to account for
> the use cases described in that bug.

Yes, I had a feeling what I posted was not correct, because it seemed
like the "obvious" thing, and so, would've been there already if
correct.

I've just dug through the Elisp manual a little and I think (elisp)
Specified Space is precisely what's needed here.  I've tried the
following:

modified   lisp/ruler-mode.el
@@ -632,12 +632,7 @@ ruler-mode-ruler
   (let* ((w (ruler-mode-text-scaled-window-width))
          (m (window-margins))
          (f (window-fringes))
-         (i (if display-line-numbers
-                ;; FIXME: ruler-mode relies on I being an integer, so
-                ;; the column numbers might be slightly off if the
-                ;; line-number face is customized.
-                (round (line-number-display-width 'columns))
-              0))
+         (i 0)
          (j (ruler-mode-text-scaled-window-hscroll))
          ;; Setup the scrollbar, fringes, and margins areas.
          (lf (ruler-mode-space
@@ -739,7 +734,13 @@ ruler-mode-ruler
       (setq i (1+ i)
             j (1+ j)))
 
-    (let ((ruler-str (concat ruler))
+    (let ((ruler-indent
+           (if (not display-line-numbers)
+               ""
+             (propertize " " 'display
+                         `(space :width
+                                 (,(- (line-number-display-width t) 1))))))
+          (ruler-str (concat ruler))
           (len (length ruler)))
       (add-text-properties 0 len ruler-wide-props ruler-str)
       (dolist (p (nreverse props))
@@ -747,13 +748,14 @@ ruler-mode-ruler
 
       ;; Return the ruler propertized string.  Using list here,
       ;; instead of concat visually separate the different areas.
-      (if (nth 2 (window-fringes))
-          ;; fringes outside margins.
-          (list "" (and (eq 'left sbvt) sb) lf lm
-                ruler-str rm rf (and (eq 'right sbvt) sb))
-        ;; fringes inside margins.
-        (list "" (and (eq 'left sbvt) sb) lm lf
-              ruler-str rf rm (and (eq 'right sbvt) sb))))))
+      (let ((ruler-str-ind (concat ruler-indent ruler-str)))
+            (if (nth 2 (window-fringes))
+            ;; fringes outside margins.
+            (list "" (and (eq 'left sbvt) sb) lf lm
+                  ruler-str-ind rm rf (and (eq 'right sbvt) sb))
+          ;; fringes inside margins.
+          (list "" (and (eq 'left sbvt) sb) lm lf
+                ruler-str-ind rf rm (and (eq 'right sbvt) sb)))))))
 
 (provide 'ruler-mode)
 
This is almost perfect. The (- ... 1) in the :width pixel spec is
obviously a hack (and breaks this patch in emacs -nw).  I have no idea
why an extra pixel is inserted in GUI mode.  If you apply the above but
remove (- ... 1), you'll see the following:

The above is a screenshot of Emacs with the patch applied opened in GIMP
and zoomed in very far.  I've placed the crosshair on the problematic
pixel column.

If you know why that happens, this could constitute a fix with that
tweak applied.

Just using :align-to header-line-indent-width appears to have even worse
results, because ISTM that it gets rounded after scaling is applied, so
it is of the wrong width by different amounts for each text-scale.  Even
without any text-scale applied, it has the same extra-pixel problem.

I think the above is close to correct, though, it perhaps implies that
header-line-indent-mode should get some love in order to be
pixel-perfect in pixel specifications too.

>> Interestingly, changing text-scale-remap-header-line appears to have no
>> effect, but that variable ought to be accounted for somehow also, I
>> think?  Though, if the line-numbers and header line always use the same
>> face, then the above should be correct, I think?
>
> If you take a look at ruler-mode.el, you will see that it overrides
> header-line-format by its own code.

Yes, but the name and logic in text-scale seemed to imply to me that
text-scale would take the header-line-format set up by ruler-mode and
"alter" it somehow.
-- 
Arsen Arsenović