Subject: bug#73032: 31.0.50; vtable header is not aligned

From: Eli Zaretskii <[email-masked]>
Date: Thu, 05 Sep 2024 10:36:10 +0300

> Date: Thu, 05 Sep 2024 01:45:14 +0300
> From:  Aleksandr Vityazev via "Bug reports for GNU Emacs,
>  the Swiss army knife of text editors" <[email-masked]>
> 
> There is a problem in vtable.el when an emoji is specified as a delimiter; the
> header and row delimiters are not aligned.
> 
> Minimal reproducer for emacs -Q:
> 
> (require 'vtable)
> (with-current-buffer (get-buffer-create "*test*")
>   (make-vtable
>    :columns '((:name "Name" :width 20) "Size" "File")
>    :objects (buffer-list)
>    :actions '("k" kill-buffer
>               "RET" display-buffer)
>    :divider " 🍉 "
>    :getter (lambda (object column vtable)
>              (pcase (vtable-column vtable column)
>                ("Name" (buffer-name object))
>                ("Size" (buffer-size object))
>                ("File" (or (buffer-file-name object) "")))))
>   (switch-to-buffer "*test*"))
> 
> Screenshot is attached.

I cannot get them aligned even if I replace the Emoji character with
an ASCII character, like 'x'.  Can you?

AFAICT, there's inconsistency in whitespace calculation between the
header line and the body of the table, due to the desire to display
the sorting indicator not quite right-aligned.  The patch below
attempts to fix that; does it give good results?

diff --git a/lisp/emacs-lisp/vtable.el b/lisp/emacs-lisp/vtable.el
index cb7ea39..dd26ccd 100644
--- a/lisp/emacs-lisp/vtable.el
+++ b/lisp/emacs-lisp/vtable.el
@@ -722,15 +722,17 @@ vtable--insert-header-line
                    (vtable--limit-string
                     name (- (elt widths index) indicator-width))
                  name))
-         (let ((fill-width
-                (+ (- (elt widths index)
-                      (string-pixel-width displayed)
-                      indicator-width
-                      (vtable-separator-width table)
-                      ;; We want the indicator to not be quite flush
-                      ;; right.
-                      (/ (vtable--char-width table) 2.0))
-                   (if last 0 spacer))))
+         (let* ((indicator-lead-width
+                 ;; We want the indicator to not be quite flush right.
+                 (/ (vtable--char-width table) 2.0))
+                (indicator-pad-width (- (vtable--char-width table)
+                                        indicator-lead-width))
+                (fill-width
+                 (+ (- (elt widths index)
+                       (string-pixel-width displayed)
+                       indicator-width
+                       indicator-lead-width)
+                    (if last 0 spacer))))
            (if (or (not last)
                    (zerop indicator-width)
                    (< (seq-reduce #'+ widths 0) (window-width nil t)))
@@ -739,7 +741,9 @@ vtable--insert-header-line
                 displayed
                 (propertize " " 'display
                             (list 'space :width (list fill-width)))
-                indicator)
+                indicator
+                (propertize " " 'display
+                            (list 'space :width (list indicator-pad-width))))
              ;; This is the final column, and we have a sorting
              ;; indicator, and the table is too wide for the window.
              (let* ((pre-indicator (string-pixel-width