]> git.eshelyaron.com Git - emacs.git/commitdiff
Make :align-to account for display-line-numbers
authorEli Zaretskii <eliz@gnu.org>
Fri, 20 Oct 2017 09:36:12 +0000 (12:36 +0300)
committerEli Zaretskii <eliz@gnu.org>
Fri, 20 Oct 2017 09:36:12 +0000 (12:36 +0300)
These changes also update the various bundled packages to use
new feature, and better support customizations of the line-number
face.
* src/xdisp.c (calc_pixel_width_or_height): Improve commentary.
Make :align-to count from the end of the line-number display when
the offset or the width form reference that of the text area.
(Bug#28855)

* src/indent.c (Fline_number_display_width): Implement support for
the PIXELWISE argument being 'columns'.  Update the doc string.
(syms_of_indent): New symbol 'columns'.

* lisp/ruler-mode.el (ruler-mode-window-col, ruler-mode-ruler):
Call line-number-display-width with last argument 'columns'.

* lisp/proced.el (proced-header-line): Call
line-number-display-width with 2nd arg 'columns', which also fixes
a problem when display-line-numbers is nil.

* lisp/emacs-lisp/tabulated-list.el
(tabulated-list-line-number-width): Call line-number-display-width
with 2nd arg 'columns.
(tabulated-list-entry-lnum-width): Remove unneeded defvar.
(tabulated-list-print, tabulated-list-print-entry): No need to
account for the value of tabulated-list-entry-lnum-width.
(tabulated-list--current-lnum-width): New defvar.
(tabulated-list-watch-line-number-width): New function.
(tabulated-list-mode): Bind tabulated-list--current-lnum-width
locally, and set up tabulated-list-watch-line-number-width as
pre-redisplay-functions hook.

* doc/lispref/display.texi (Size of Displayed Text): Document the
'columns' value of the PIXELWISE argument.
(Pixel Specification): Update and improve the documentation of the
supported forms.

doc/lispref/display.texi
lisp/emacs-lisp/tabulated-list.el
lisp/proced.el
lisp/ruler-mode.el
src/indent.c
src/xdisp.c

index fbdd810247d18cb070b26bbea970d8df94cd0e37..17126ce72b97e40d0317b9297c79e285dbe97ea9 100644 (file)
@@ -2050,14 +2050,17 @@ calculations.
 
 @defun line-number-display-width &optional pixelwise
 This function returns the width used for displaying the line numbers
-in the selected window.  Optional argument @var{pixelwise}, if
-non-@code{nil}, means return the value in pixels; otherwise the value
-is returned in column units of the font defined for the
+in the selected window.  If the optional argument @var{pixelwise} is
+the symbol @code{columns}, the return value is a float number of the
+frame's canonical columns; if @var{pixelwise} is @code{t} or any other
+non-@code{nil} value, the value is an integer and is measured in
+pixels.  If @var{pixelwise} is omitted or @code{nil}, the value is the
+integer number of columns of the font defined for the
 @code{line-number} face, and doesn't include the 2 columns used to pad
-the numbers.  If line numbers are not displayed in the selected
-window, the value is zero.  Use @code{with-selected-window}
-(@pxref{Selecting Windows}) if you need this information about another
-window.
+the numbers on display.  If line numbers are not displayed in the
+selected window, the value is zero regardless of the value of
+@var{pixelwise}.  Use @code{with-selected-window} (@pxref{Selecting
+Windows}) if you need this information about another window.
 @end defun
 
 
@@ -4636,7 +4639,7 @@ as an absolute number of pixels.
 
 @smallexample
 @group
-  @var{expr} ::= @var{num} | (@var{num}) | @var{unit} | @var{elem} | @var{pos} | @var{image} | @var{form}
+  @var{expr} ::= @var{num} | (@var{num}) | @var{unit} | @var{elem} | @var{pos} | @var{image} | @var{xwidget} | @var{form}
   @var{num}  ::= @var{integer} | @var{float} | @var{symbol}
   @var{unit} ::= in | mm | cm | width | height
 @end group
@@ -4652,22 +4655,34 @@ as an absolute number of pixels.
   The form @var{num} specifies a fraction of the default frame font
 height or width.  The form @code{(@var{num})} specifies an absolute
 number of pixels.  If @var{num} is a symbol, @var{symbol}, its
-buffer-local variable binding is used.
+buffer-local variable binding is used; that binding can be either a
+number or a cons cell of the forms shown above (including yet another
+cons cell whose @code{car} is a symbol that has a buffer-local
+binding).
 
   The @code{in}, @code{mm}, and @code{cm} units specify the number of
 pixels per inch, millimeter, and centimeter, respectively.  The
 @code{width} and @code{height} units correspond to the default width
-and height of the current face.  An image specification @code{image}
-corresponds to the width or height of the image.
+and height of the current face.  An image specification of the form
+@w{@code{(image . @var{props})}} (@pxref{Image Descriptors})
+corresponds to the width or height of the specified image.  Similarly,
+an xwidget specification of the form @w{@code{(xwidget . @var{props})}}
+stands for the width or height of the specified xwidget.
+@xref{Xwidgets}.
 
   The elements @code{left-fringe}, @code{right-fringe},
 @code{left-margin}, @code{right-margin}, @code{scroll-bar}, and
-@code{text} specify to the width of the corresponding area of the
-window.
+@code{text} specify the width of the corresponding area of the window.
+When the window displays line numbers (@pxref{Size of Displayed
+Text}), the width of the @code{text} area is decreased by the screen
+space taken by the line-number display.
 
   The @code{left}, @code{center}, and @code{right} positions can be
 used with @code{:align-to} to specify a position relative to the left
-edge, center, or right edge of the text area.
+edge, center, or right edge of the text area.  When the window
+displays line numbers, the @code{left} and the @code{center} positions
+are offset to account for the screen space taken by the line-number
+display.
 
   Any of the above window elements (except @code{text}) can also be
 used with @code{:align-to} to specify that the position is relative to
@@ -4683,13 +4698,15 @@ the left-margin, use
 
   If no specific base offset is set for alignment, it is always relative
 to the left edge of the text area.  For example, @samp{:align-to 0} in a
-header-line aligns with the first text column in the text area.
+header-line aligns with the first text column in the text area.  When
+the window displays line numbers, the text is considered to start where
+the space used for line-number display ends.
 
   A value of the form @code{(@var{num} . @var{expr})} stands for the
 product of the values of @var{num} and @var{expr}.  For example,
 @code{(2 . in)} specifies a width of 2 inches, while @code{(0.5 .
 @var{image})} specifies half the width (or height) of the specified
-image.
+@var{image} (which should be given by its image spec).
 
   The form @code{(+ @var{expr} ...)} adds up the value of the
 expressions.  The form @code{(- @var{expr} ...)} negates or subtracts
index d1d7c0a80427b6b42f69db06d7dd7d9e558e7dd8..73ddadfb80582b0aefbc6d98c2daca06aff8495e 100644 (file)
@@ -193,10 +193,10 @@ Populated by `tabulated-list-init-header'.")
   ;; is displayed.
   (if (not display-line-numbers)
            0
-    (let ((cbuf-window (get-buffer-window (current-buffer))))
+    (let ((cbuf-window (get-buffer-window (current-buffer) t)))
       (if (window-live-p cbuf-window)
           (with-selected-window cbuf-window
-            (+ (line-number-display-width) 2))
+            (line-number-display-width 'columns))
         4))))
 
 (defun tabulated-list-init-header ()
@@ -329,8 +329,6 @@ Check the current row, the previous one and the next row."
                            (string-width (if (stringp nt) nt (car nt)))))
                        tabulated-list--near-rows)))
 
-(defvar tabulated-list-entry-lnum-width nil)
-
 (defun tabulated-list-print (&optional remember-pos update)
   "Populate the current Tabulated List mode buffer.
 This sorts the `tabulated-list-entries' list if sorting is
@@ -373,7 +371,6 @@ changing `tabulated-list-sort-key'."
       (unless tabulated-list-use-header-line
         (tabulated-list-print-fake-header)))
     ;; Finally, print the resulting list.
-    (setq tabulated-list-entry-lnum-width (tabulated-list-line-number-width))
     (while entries
       (let* ((elt (car entries))
              (tabulated-list--near-rows
@@ -428,9 +425,8 @@ of column descriptors."
        (x     (max tabulated-list-padding 0))
        (ncols (length tabulated-list-format))
        (inhibit-read-only t))
-    (setq x (+ x tabulated-list-entry-lnum-width))
     (if (> tabulated-list-padding 0)
-       (insert (make-string (- x tabulated-list-entry-lnum-width) ?\s)))
+       (insert (make-string x ?\s)))
     (let ((tabulated-list--near-rows ; Bind it if not bound yet (Bug#25506).
            (or (bound-and-true-p tabulated-list--near-rows)
                (list (or (tabulated-list-get-entry (point-at-bol 0))
@@ -601,6 +597,14 @@ With a numeric prefix argument N, sort the Nth column."
     (tabulated-list-init-header)
     (tabulated-list-print t)))
 
+(defvar tabulated-list--current-lnum-width nil)
+(defun tabulated-list-watch-line-number-width (_window)
+  (if display-line-numbers
+      (let ((lnum-width (tabulated-list-line-number-width)))
+        (when (not (= tabulated-list--current-lnum-width lnum-width))
+          (setq-local tabulated-list--current-lnum-width lnum-width)
+          (tabulated-list-revert)))))
+
 ;;; The mode definition:
 
 (define-derived-mode tabulated-list-mode special-mode "Tabulated"
@@ -645,7 +649,12 @@ as the ewoc pretty-printer."
   ;; column of the first entry happens to begin with a R2L letter.
   (setq bidi-paragraph-direction 'left-to-right)
   ;; This is for if/when they turn on display-line-numbers
-  (add-hook 'display-line-numbers-mode-hook #'tabulated-list-revert nil t))
+  (add-hook 'display-line-numbers-mode-hook #'tabulated-list-revert nil t)
+  ;; This is for if/when they customize the line-number face or when
+  ;; the line-number width needs to change due to scrolling.
+  (setq-local tabulated-list--current-lnum-width 0)
+  (add-hook 'pre-redisplay-functions
+            #'tabulated-list-watch-line-number-width nil t))
 
 (put 'tabulated-list-mode 'mode-class 'special)
 
index c9e851b7e05e9ef803b073fafde8e0a77d48193a..aec54b59211bb9375d0b16745bfeae425b75e6b4 100644 (file)
@@ -604,7 +604,8 @@ Important: the match ends just after the marker.")
   "Return header line for Proced buffer."
   (list (propertize " "
                     'display
-                    (list 'space :align-to (+ 2 (line-number-display-width))))
+                    (list 'space :align-to
+                          (line-number-display-width 'columns)))
         (if (<= (window-hscroll) (length proced-header-line))
             (replace-regexp-in-string ;; preserve text properties
              "\\(%\\)" "\\1\\1"
index 7a23f4d03c091e51e0b2da5c752115175b1b50ed..3d27858d0fe46816aee1c1190191b4d2378821f8 100644 (file)
@@ -307,7 +307,12 @@ or remove a tab stop.  \\[ruler-mode-toggle-show-tab-stops] or
 N is a column number relative to selected frame.
 If required, account for screen estate taken by `display-line-numbers'."
   (if display-line-numbers
-      (setq n (- n (line-number-display-width) 2)))
+      ;; FIXME: ruler-mode relies on N being an integer, so if the
+      ;; 'line-number' face is customized to use a font that is larger
+      ;; or smaller than that of the default face, the alignment might
+      ;; be off by up to half a column, unless the font width is an
+      ;; integral multiple or divisor of the default face's font.
+      (setq n (- n (round (line-number-display-width 'columns)))))
   (- n
      (or (car (window-margins)) 0)
      (fringe-columns 'left)
@@ -668,7 +673,12 @@ Optional argument PROPS specifies other text properties to apply."
   (let* ((w (ruler-mode-text-scaled-window-width))
          (m (window-margins))
          (f (window-fringes))
-         (i (if display-line-numbers (+ (line-number-display-width) 2) 0))
+         (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))
          (j (ruler-mode-text-scaled-window-hscroll))
          ;; Setup the scrollbar, fringes, and margins areas.
          (lf (ruler-mode-space
@@ -708,7 +718,7 @@ Optional argument PROPS specifies other text properties to apply."
             ;; line-number display be blank, not filled with
             ;; ruler-mode-basic-graduation-char.
             (if display-line-numbers
-                (let* ((lndw (+ (line-number-display-width) 2))
+                (let* ((lndw (round (line-number-display-width 'columns)))
                        (s (make-string lndw ?\s)))
                   (concat s (make-string (- w lndw)
                                          ruler-mode-basic-graduation-char)))
index a3e9b5b0b9a924cc017903d014d0bc1b57788f17..192eec72efe1b0e8050f578b6fcd817c8312f497 100644 (file)
@@ -1991,15 +1991,26 @@ line_number_display_width (struct window *w, int *width, int *pixel_width)
 DEFUN ("line-number-display-width", Fline_number_display_width,
        Sline_number_display_width, 0, 1, 0,
        doc: /* Return the width used for displaying line numbers in the selected window.
-If optional argument PIXELWISE is non-nil, return the width in pixels,
-otherwise return the width in columns of the face used to display
-line numbers, `line-number'.  Note that in the latter case, the value
-doesn't include the 2 columns used for padding the numbers.  */)
+If optional argument PIXELWISE is the symbol `columns', return the width
+in units of the frame's canonical character width.  In this case, the
+value is a float.
+If optional argument PIXELWISE is t or any other non-nil value, return
+the width as an integer number of pixels.
+Otherwise return the value as an integer number of columns of the face
+used to display line numbers, `line-number'.  Note that in the latter
+case, the value doesn't include the 2 columns used for padding the
+numbers on display.  */)
   (Lisp_Object pixelwise)
 {
   int width, pixel_width;
+  struct window *w = XWINDOW (selected_window);
   line_number_display_width (XWINDOW (selected_window), &width, &pixel_width);
-  if (!NILP (pixelwise))
+  if (EQ (pixelwise, Qcolumns))
+    {
+      struct frame *f = XFRAME (w->frame);
+      return make_float ((double) pixel_width / FRAME_COLUMN_WIDTH (f));
+    }
+  else if (!NILP (pixelwise))
     return make_number (pixel_width);
   return make_number (width);
 }
@@ -2361,6 +2372,8 @@ syms_of_indent (void)
               doc: /* Indentation can insert tabs if this is non-nil.  */);
   indent_tabs_mode = 1;
 
+  DEFSYM (Qcolumns, "columns");
+
   defsubr (&Scurrent_indentation);
   defsubr (&Sindent_to);
   defsubr (&Scurrent_column);
index 6d9acecb424c58f221c6db47f758a5efd5b9ef2b..dc23959aadbc8a891cfd60782af0bf284f759e8e 100644 (file)
@@ -25123,7 +25123,20 @@ else if the text is replaced by an ellipsis.  */)
      '(space :width (+ left-fringe left-margin (- (1))))
      '(space :width (+ left-fringe left-margin (-1)))
 
-*/
+   If ALIGN_TO is NULL, returns the result in *RES.  If ALIGN_TO is
+   non-NULL, the value of *ALIGN_TO is a window-relative pixel
+   coordinate, and *RES is the additional pixel width from that point
+   till the end of the stretch glyph.
+
+   WIDTH_P non-zero means take the width dimension or X coordinate of
+   the object specified by PROP, WIDTH_P zero means take the height
+   dimension or the Y coordinate.  (Therefore, if ALIGN_TO is
+   non-NULL, WIDTH_P should be non-zero.)
+
+   FONT is the font of the face of the surrounding text.
+
+   The return value is non-zero if width or height were successfully
+   calculated, i.e. if PROP is a valid spec.  */
 
 static bool
 calc_pixel_width_or_height (double *res, struct it *it, Lisp_Object prop,
@@ -25145,6 +25158,7 @@ calc_pixel_width_or_height (double *res, struct it *it, Lisp_Object prop,
        {
          char *unit = SSDATA (SYMBOL_NAME (prop));
 
+         /* The UNIT expression, e.g. as part of (NUM . UNIT).  */
          if (unit[0] == 'i' && unit[1] == 'n')
            pixels = 1.0;
          else if (unit[0] == 'm' && unit[1] == 'm')
@@ -25165,10 +25179,12 @@ calc_pixel_width_or_height (double *res, struct it *it, Lisp_Object prop,
        }
 
 #ifdef HAVE_WINDOW_SYSTEM
+      /* 'height': the height of FONT.  */
       if (EQ (prop, Qheight))
        return OK_PIXELS (font
                          ? normal_char_height (font, -1)
                          : FRAME_LINE_HEIGHT (it->f));
+      /* 'width': the width of FONT.  */
       if (EQ (prop, Qwidth))
        return OK_PIXELS (font
                          ? FONT_WIDTH (font)
@@ -25178,33 +25194,48 @@ calc_pixel_width_or_height (double *res, struct it *it, Lisp_Object prop,
        return OK_PIXELS (1);
 #endif
 
+      /* 'text': the width or height of the text area.  */
       if (EQ (prop, Qtext))
          return OK_PIXELS (width_p
-                           ? window_box_width (it->w, TEXT_AREA)
+                           ? (window_box_width (it->w, TEXT_AREA)
+                              - it->lnum_pixel_width)
                            : WINDOW_BOX_HEIGHT_NO_MODE_LINE (it->w));
 
+      /* ':align_to'.  First time we compute the value, window
+        elements are interpreted as the position of the element's
+        left edge.  */
       if (align_to && *align_to < 0)
        {
          *res = 0;
+         /* 'left': left edge of the text area.  */
          if (EQ (prop, Qleft))
-           return OK_ALIGN_TO (window_box_left_offset (it->w, TEXT_AREA));
+           return OK_ALIGN_TO (window_box_left_offset (it->w, TEXT_AREA)
+                               + it->lnum_pixel_width);
+         /* 'right': right edge of the text area.  */
          if (EQ (prop, Qright))
            return OK_ALIGN_TO (window_box_right_offset (it->w, TEXT_AREA));
+         /* 'center': the center of the text area.  */
          if (EQ (prop, Qcenter))
            return OK_ALIGN_TO (window_box_left_offset (it->w, TEXT_AREA)
+                               + it->lnum_pixel_width
                                + window_box_width (it->w, TEXT_AREA) / 2);
+         /* 'left-fringe': left edge of the left fringe.  */
          if (EQ (prop, Qleft_fringe))
            return OK_ALIGN_TO (WINDOW_HAS_FRINGES_OUTSIDE_MARGINS (it->w)
                                ? WINDOW_LEFT_SCROLL_BAR_AREA_WIDTH (it->w)
                                : window_box_right_offset (it->w, LEFT_MARGIN_AREA));
+         /* 'right-fringe': left edge of the right fringe.  */
          if (EQ (prop, Qright_fringe))
            return OK_ALIGN_TO (WINDOW_HAS_FRINGES_OUTSIDE_MARGINS (it->w)
                                ? window_box_right_offset (it->w, RIGHT_MARGIN_AREA)
                                : window_box_right_offset (it->w, TEXT_AREA));
+         /* 'left-margin': left edge of the left display margin.  */
          if (EQ (prop, Qleft_margin))
            return OK_ALIGN_TO (window_box_left_offset (it->w, LEFT_MARGIN_AREA));
+         /* 'right-margin': left edge of the right display margin.  */
          if (EQ (prop, Qright_margin))
            return OK_ALIGN_TO (window_box_left_offset (it->w, RIGHT_MARGIN_AREA));
+         /* 'scroll-bar': left edge of the vertical scroll bar.  */
          if (EQ (prop, Qscroll_bar))
            return OK_ALIGN_TO (WINDOW_HAS_VERTICAL_SCROLL_BAR_ON_LEFT (it->w)
                                ? 0
@@ -25215,6 +25246,7 @@ calc_pixel_width_or_height (double *res, struct it *it, Lisp_Object prop,
        }
       else
        {
+         /* Otherwise, the elements stand for their width.  */
          if (EQ (prop, Qleft_fringe))
            return OK_PIXELS (WINDOW_LEFT_FRINGE_WIDTH (it->w));
          if (EQ (prop, Qright_fringe))
@@ -25237,6 +25269,8 @@ calc_pixel_width_or_height (double *res, struct it *it, Lisp_Object prop,
       int base_unit = (width_p
                       ? FRAME_COLUMN_WIDTH (it->f)
                       : FRAME_LINE_HEIGHT (it->f));
+      if (width_p && align_to && *align_to < 0)
+       return OK_PIXELS (XFLOATINT (prop) * base_unit + it->lnum_pixel_width);
       return OK_PIXELS (XFLOATINT (prop) * base_unit);
     }
 
@@ -25248,6 +25282,7 @@ calc_pixel_width_or_height (double *res, struct it *it, Lisp_Object prop,
       if (SYMBOLP (car))
        {
 #ifdef HAVE_WINDOW_SYSTEM
+         /* '(image PROPS...)': width or height of the specified image.  */
          if (FRAME_WINDOW_P (it->f)
              && valid_image_p (prop))
            {
@@ -25256,12 +25291,15 @@ calc_pixel_width_or_height (double *res, struct it *it, Lisp_Object prop,
 
              return OK_PIXELS (width_p ? img->width : img->height);
            }
+         /* '(xwidget PROPS...)': dimensions of the specified xwidget.  */
          if (FRAME_WINDOW_P (it->f) && valid_xwidget_spec_p (prop))
            {
               /* TODO: Don't return dummy size.  */
               return OK_PIXELS (100);
             }
 #endif
+         /* '(+ EXPR...)' or '(- EXPR...)' add or subtract
+            recursively calculated values.  */
          if (EQ (car, Qplus) || EQ (car, Qminus))
            {
              bool first = true;
@@ -25289,15 +25327,18 @@ calc_pixel_width_or_height (double *res, struct it *it, Lisp_Object prop,
            car = Qnil;
        }
 
+      /* '(NUM)': absolute number of pixels.  */
       if (NUMBERP (car))
        {
          double fact;
+         int offset =
+           width_p && align_to && *align_to < 0 ? it->lnum_pixel_width : 0;
          pixels = XFLOATINT (car);
          if (NILP (cdr))
-           return OK_PIXELS (pixels);
+           return OK_PIXELS (pixels + offset);
          if (calc_pixel_width_or_height (&fact, it, cdr,
                                          font, width_p, align_to))
-           return OK_PIXELS (pixels * fact);
+           return OK_PIXELS (pixels * fact + offset);
          return false;
        }