Make :align-to account for display-line-numbers

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.
This commit is contained in:
Eli Zaretskii 2017-10-20 12:36:12 +03:00
parent 831eafc8ae
commit fd3d8610b2
6 changed files with 129 additions and 38 deletions

View 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

View 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)

View 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"

View 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)))

View 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);

View 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;
}