From e8f26d554b64ed63fe2b7f110d5247648b7322ed Mon Sep 17 00:00:00 2001 From: Daniel Mendler Date: Wed, 7 Jan 2026 17:39:16 +0100 Subject: [PATCH] Support cons cell for 'line-spacing' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * etc/NEWS: Announce the change. * src/dispextern.h (struct glyph_row): Add 'extra_line_spacing_above' member. (struct it): Add 'extra_line_spacing_above' member. * src/frame.h (struct frame): Add 'extra_line_spacing_above' member. Update comment for 'extra_line_spacing.' * src/buffer.c (syms_of_buffer): Update the docstring of 'line-spacing' to describe the cons cell usage. * src/buffer.h (struct buffer): Update comment for 'extra_line_spacing'. * src/frame.c (gui_set_line_spacing): Handle cons cell value for 'line-spacing'. Calculate and set 'extra_line_spacing_above' for both integer and float pairs. * src/xdisp.c (init_iterator): Initialize 'extra_line_spacing_above' from buffer or frame 'line-spacing', handling cons cells for both integer and float values. (gui_produce_glyphs): Use 'extra_line_spacing_above' to distribute spacing between ascent and descent. Update 'max_extra_line_spacing' calculation. (resize_mini_window): Take line spacing into account when resizing the mini window. Pass height of a single line to 'grow_mini_window' and 'shrink_mini_window'. * src/window.c (grow_mini_window, shrink_mini_window): Add unit argument which defines height of a single line. * src/window.h (grow_mini_window, shrink_mini_window): Adjust function prototypes accordingly with unit argument. * lisp/subr.el (total-line-spacing): New function to calculate total spacing from a number or cons cell. (posn-col-row): Use total-line-spacing. * lisp/simple.el (default-line-height): Use 'total-line-spacing'. * lisp/textmodes/picture.el (picture-mouse-set-point): Use 'total-line-spacing'. * lisp/window.el (window-default-line-height): Use 'total-line-spacing'. (window--resize-mini-window): Take 'line-spacing' into account. * test/lisp/subr-tests.el (total-line-spacing): New test. * test/src/buffer-tests.el (test-line-spacing): New test. * doc/emacs/display.texi (Display Custom): Document that 'line-spacing' can be a cons cell. (Line Height): Document the new cons cell format for 'line-spacing' to allow vertical centering. Co-authored-by: Przemysław Alexander Kamiński Co-authored-by: Daniel Mendler --- doc/emacs/display.texi | 10 ++++++ doc/lispref/display.texi | 11 +++--- etc/NEWS | 6 ++++ lisp/simple.el | 8 ++--- lisp/subr.el | 13 +++++-- lisp/textmodes/picture.el | 4 +-- lisp/window.el | 16 ++++++--- src/buffer.c | 7 ++-- src/buffer.h | 5 ++- src/dispextern.h | 7 ++++ src/frame.c | 50 ++++++++++++++++++++++++--- src/frame.h | 9 ++++- src/window.c | 15 ++++---- src/window.h | 4 +-- src/xdisp.c | 73 ++++++++++++++++++++++++++++++++------- test/lisp/subr-tests.el | 15 ++++++++ test/src/buffer-tests.el | 18 ++++++++++ 17 files changed, 224 insertions(+), 47 deletions(-) diff --git a/doc/emacs/display.texi b/doc/emacs/display.texi index 0cda594d5b1..dde6cc4f1b6 100644 --- a/doc/emacs/display.texi +++ b/doc/emacs/display.texi @@ -2351,6 +2351,16 @@ of lines which are a multiple of certain numbers. Customize @code{display-line-numbers-minor-tick} respectively to set those numbers. +@vindex line-spacing + The variable @code{line-spacing} controls the vertical spacing between +lines. It can be set to an integer (specifying pixels) or a float +(specifying spacing relative to the default frame font height). You can +also set this variable to a cons cell of integers or floats, such as +@code{(@var{top} . @var{bottom})}. When set to a cons cell, the spacing +is distributed above and below the line, allowing for text to be +vertically centered within the line height. See also @ref{Line Height,,, +elisp, The Emacs Lisp Reference Manual}. + @vindex visible-bell If the variable @code{visible-bell} is non-@code{nil}, Emacs attempts to make the whole screen blink when it would normally make an audible bell diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi index b74e4b9632f..1d037807070 100644 --- a/doc/lispref/display.texi +++ b/doc/lispref/display.texi @@ -2582,10 +2582,13 @@ the spacing relative to the frame's default line height. @vindex line-spacing You can specify the line spacing for all lines in a buffer via the -buffer-local @code{line-spacing} variable. An integer specifies -the number of pixels put below lines. A floating-point number -specifies the spacing relative to the default frame line height. This -overrides line spacings specified for the frame. +buffer-local @code{line-spacing} variable. An integer specifies the +number of pixels put below lines. A floating-point number specifies the +spacing relative to the default frame line height. A cons cell of +integers or floating-point numbers specifies the spacing put above and +below the line, allowing for vertically centering text. This overrides +line spacings specified for the frame. + @kindex line-spacing @r{(text property)} Finally, a newline can have a @code{line-spacing} text or overlay diff --git a/etc/NEWS b/etc/NEWS index 594c0d45322..ceaf682e2ab 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -82,6 +82,12 @@ other directory on your system. You can also invoke the * Changes in Emacs 31.1 +** 'line-spacing' now supports specifying spacing above the line. +Previously, only spacing below the line could be specified. The variable +can now be set to a cons cell to specify spacing both above and below +the line, which allows for vertically centering text. + ++++ ** 'prettify-symbols-mode' attempts to ignore undisplayable characters. Previously, such characters would be rendered as, e.g., white boxes. diff --git a/lisp/simple.el b/lisp/simple.el index 53f11e4eeee..99930c3090c 100644 --- a/lisp/simple.el +++ b/lisp/simple.el @@ -7875,10 +7875,10 @@ This function uses the definition of the default face for the currently selected frame." (let ((dfh (default-font-height)) (lsp (if (display-graphic-p) - (or line-spacing - (default-value 'line-spacing) - (frame-parameter nil 'line-spacing) - 0) + (total-line-spacing (or line-spacing + (default-value 'line-spacing) + (frame-parameter nil 'line-spacing) + 0)) 0))) (if (floatp lsp) (setq lsp (truncate (* (frame-char-height) lsp)))) diff --git a/lisp/subr.el b/lisp/subr.el index d307a07f05b..40325c30326 100644 --- a/lisp/subr.el +++ b/lisp/subr.el @@ -2027,8 +2027,9 @@ and `event-end' functions." (let* ((spacing (when (display-graphic-p frame) (or (with-current-buffer (window-buffer (frame-selected-window frame)) - line-spacing) - (frame-parameter frame 'line-spacing))))) + (total-line-spacing)) + (total-line-spacing + (frame-parameter frame 'line-spacing)))))) (cond ((floatp spacing) (setq spacing (truncate (* spacing (frame-char-height frame))))) @@ -7936,4 +7937,12 @@ and return the value found in PLACE instead." ,(funcall setter val) ,val))))) +(defun total-line-spacing (&optional line-spacing-param) + "Return numeric value of line-spacing, summing it if it's a cons. + When LINE-SPACING-PARAM is provided, calculate from it instead." + (let ((v (or line-spacing-param line-spacing))) + (pcase v + ((pred numberp) v) + (`(,above . ,below) (+ above below))))) + ;;; subr.el ends here diff --git a/lisp/textmodes/picture.el b/lisp/textmodes/picture.el index 8b75b7c52a8..b23b1a1d0ed 100644 --- a/lisp/textmodes/picture.el +++ b/lisp/textmodes/picture.el @@ -235,8 +235,8 @@ Use \"\\[command-apropos] picture-movement\" to see commands which control motio (char-ht (frame-char-height frame)) (spacing (when (display-graphic-p frame) (or (with-current-buffer (window-buffer window) - line-spacing) - (frame-parameter frame 'line-spacing))))) + (total-line-spacing)) + (total-line-spacing (frame-parameter frame 'line-spacing)))))) (cond ((floatp spacing) (setq spacing (truncate (* spacing char-ht)))) ((null spacing) diff --git a/lisp/window.el b/lisp/window.el index 98722e909a6..7d866d6475d 100644 --- a/lisp/window.el +++ b/lisp/window.el @@ -2850,9 +2850,15 @@ as small) as possible, but don't signal an error." (let* ((frame (window-frame window)) (root (frame-root-window frame)) (height (window-pixel-height window)) - (min-height (+ (frame-char-height frame) - (- (window-pixel-height window) - (window-body-height window t)))) + ;; Take line-spacing into account if the line-spacing is + ;; configured as a cons cell with above > 0 to prevent + ;; mini-window jiggling. + (ls (or (buffer-local-value 'line-spacing (window-buffer window)) + (frame-parameter frame 'line-spacing))) + (min-height (+ (if (and (consp ls) (> (car ls) 0)) + (window-default-line-height window) + (frame-char-height frame)) + (- height (window-body-height window t)))) (max-delta (- (window-pixel-height root) (window-min-size root nil nil t)))) ;; Don't make mini window too small. @@ -9906,8 +9912,8 @@ face on WINDOW's frame." (buffer (window-buffer window)) (space-height (or (and (display-graphic-p frame) - (or (buffer-local-value 'line-spacing buffer) - (frame-parameter frame 'line-spacing))) + (total-line-spacing (or (buffer-local-value 'line-spacing buffer) + (frame-parameter frame 'line-spacing)))) 0))) (+ font-height (if (floatp space-height) diff --git a/src/buffer.c b/src/buffer.c index fec30d4f0e9..3d85d784f1c 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -5875,12 +5875,15 @@ cursor's appearance is instead controlled by the variable `cursor-in-non-selected-windows'. */); DEFVAR_PER_BUFFER ("line-spacing", - &BVAR (current_buffer, extra_line_spacing), Qnumberp, + &BVAR (current_buffer, extra_line_spacing), Qnil, doc: /* Additional space to put between lines when displaying a buffer. The space is measured in pixels, and put below lines on graphic displays, see `display-graphic-p'. If value is a floating point number, it specifies the spacing relative -to the default frame line height. A value of nil means add no extra space. */); +to the default frame line height. +If value is a cons cell containing a pair of floats or integers, +it is interpreted as space above and below the line, respectively. +A value of nil means add no extra space. */); DEFVAR_PER_BUFFER ("cursor-in-non-selected-windows", &BVAR (current_buffer, cursor_in_non_selected_windows), Qnil, diff --git a/src/buffer.h b/src/buffer.h index 34fe308b671..403b249df6f 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -575,7 +575,10 @@ struct buffer Lisp_Object cursor_type_; /* An integer > 0 means put that number of pixels below text lines - in the display of this buffer. */ + in the display of this buffer. + A float ~ 1.0 means add extra number of pixels below text lines + relative to the line height. + A cons means put car spacing above and cdr spacing below the line. */ Lisp_Object extra_line_spacing_; #ifdef HAVE_TREE_SITTER diff --git a/src/dispextern.h b/src/dispextern.h index d325e185c5a..30785b9ccdf 100644 --- a/src/dispextern.h +++ b/src/dispextern.h @@ -960,6 +960,9 @@ struct glyph_row in last row when checking if row is fully visible. */ int extra_line_spacing; + /* Part of extra_line_spacing that should go above the line. */ + int extra_line_spacing_above; + /* First position in this row. This is the text position, including overlay position information etc, where the display of this row started, and can thus be less than the position of the first @@ -2772,6 +2775,10 @@ struct it window systems only.) */ int extra_line_spacing; + /* Default amount of additional space in pixels above lines (for + window systems only). */ + int extra_line_spacing_above; + /* Max extra line spacing added in this row. */ int max_extra_line_spacing; diff --git a/src/frame.c b/src/frame.c index 033215a76ec..ba342b15723 100644 --- a/src/frame.c +++ b/src/frame.c @@ -5454,18 +5454,60 @@ void gui_set_line_spacing (struct frame *f, Lisp_Object new_value, Lisp_Object old_value) { if (NILP (new_value)) - f->extra_line_spacing = 0; + { + f->extra_line_spacing = 0; + f->extra_line_spacing_above = 0; + } else if (RANGED_FIXNUMP (0, new_value, INT_MAX)) - f->extra_line_spacing = XFIXNAT (new_value); + { + f->extra_line_spacing = XFIXNAT (new_value); + f->extra_line_spacing_above = 0; + } else if (FLOATP (new_value)) { - int new_spacing = XFLOAT_DATA (new_value) * FRAME_LINE_HEIGHT (f) + 0.5; + int new_spacing = XFLOAT_DATA (new_value) * FRAME_LINE_HEIGHT (f); - if (new_spacing >= 0) + if (new_spacing >= 0) { f->extra_line_spacing = new_spacing; + f->extra_line_spacing_above = 0; + } else signal_error ("Invalid line-spacing", new_value); } + else if (CONSP (new_value)) + { + Lisp_Object above = XCAR (new_value); + Lisp_Object below = XCDR (new_value); + + /* Integer pair case. */ + if (RANGED_FIXNUMP (0, above, INT_MAX) + && RANGED_FIXNUMP (0, below, INT_MAX)) + { + f->extra_line_spacing = XFIXNAT (above) + XFIXNAT (below); + f->extra_line_spacing_above = XFIXNAT (above); + } + + /* Float pair case. */ + else if (FLOATP (XCAR (new_value)) + && FLOATP (XCDR (new_value))) + { + int new_spacing = (XFLOAT_DATA (above) + XFLOAT_DATA (below)) * FRAME_LINE_HEIGHT (f); + int spacing_above = XFLOAT_DATA (above) * FRAME_LINE_HEIGHT (f); + if(new_spacing >= 0 && spacing_above >= 0) + { + f->extra_line_spacing = new_spacing; + f->extra_line_spacing_above = spacing_above; + } + else + signal_error ("Invalid line-spacing", new_value); + } + + /* Unmatched pair case. */ + else + { + signal_error ("Invalid line-spacing", new_value); + } + } else signal_error ("Invalid line-spacing", new_value); if (FRAME_VISIBLE_P (f)) diff --git a/src/frame.h b/src/frame.h index c369a848b7c..091b112e8b9 100644 --- a/src/frame.h +++ b/src/frame.h @@ -718,9 +718,16 @@ struct frame frame parameter. 0 means don't do gamma correction. */ double gamma; - /* Additional space to put between text lines on this frame. */ + /* Additional space to put below text lines on this frame. + Also takes part in line height calculation. */ int extra_line_spacing; + /* Amount of space (included in extra_line_spacing) that goes ABOVE + line line. + IMPORTANT: Don't use this for line height calculations. + (5 . 20) means that extra_line_spacing is 25 with 5 above. */ + int extra_line_spacing_above; + /* All display backends seem to need these two pixel values. */ unsigned long background_pixel; unsigned long foreground_pixel; diff --git a/src/window.c b/src/window.c index 497c587b167..c4f2e4e491f 100644 --- a/src/window.c +++ b/src/window.c @@ -5894,11 +5894,11 @@ resize_mini_window_apply (struct window *w, int delta) * line of text. */ void -grow_mini_window (struct window *w, int delta) +grow_mini_window (struct window *w, int delta, int unit) { struct frame *f = XFRAME (w->frame); int old_height = window_body_height (w, WINDOW_BODY_IN_PIXELS); - int min_height = FRAME_LINE_HEIGHT (f); + int min_height = unit; eassert (MINI_WINDOW_P (w)); @@ -5926,7 +5926,7 @@ grow_mini_window (struct window *w, int delta) resize_mini_window_apply (w, -XFIXNUM (grow)); } FRAME_WINDOWS_FROZEN (f) - = window_body_height (w, WINDOW_BODY_IN_PIXELS) > FRAME_LINE_HEIGHT (f); + = window_body_height (w, WINDOW_BODY_IN_PIXELS) > unit; } /** @@ -5936,11 +5936,10 @@ grow_mini_window (struct window *w, int delta) * line of text. */ void -shrink_mini_window (struct window *w) +shrink_mini_window (struct window *w, int unit) { struct frame *f = XFRAME (w->frame); - int delta = (window_body_height (w, WINDOW_BODY_IN_PIXELS) - - FRAME_LINE_HEIGHT (f)); + int delta = (window_body_height (w, WINDOW_BODY_IN_PIXELS) - unit); eassert (MINI_WINDOW_P (w)); @@ -5959,10 +5958,10 @@ shrink_mini_window (struct window *w) else if (delta < 0) /* delta can be less than zero after adding horizontal scroll bar. */ - grow_mini_window (w, -delta); + grow_mini_window (w, -delta, unit); FRAME_WINDOWS_FROZEN (f) - = window_body_height (w, WINDOW_BODY_IN_PIXELS) > FRAME_LINE_HEIGHT (f); + = window_body_height (w, WINDOW_BODY_IN_PIXELS) > unit; } DEFUN ("resize-mini-window-internal", Fresize_mini_window_internal, diff --git a/src/window.h b/src/window.h index 5a75f62cc6e..1b4939b40a6 100644 --- a/src/window.h +++ b/src/window.h @@ -1126,8 +1126,8 @@ extern Lisp_Object window_from_coordinates (struct frame *, int, int, extern void resize_frame_windows (struct frame *, int, bool); extern void restore_window_configuration (Lisp_Object); extern void delete_all_child_windows (Lisp_Object); -extern void grow_mini_window (struct window *, int); -extern void shrink_mini_window (struct window *); +extern void grow_mini_window (struct window *, int, int); +extern void shrink_mini_window (struct window *, int); extern int window_relative_x_coord (struct window *, enum window_part, int); void run_window_change_functions (void); diff --git a/src/xdisp.c b/src/xdisp.c index a295131a311..fa826c366dd 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -3316,13 +3316,50 @@ init_iterator (struct it *it, struct window *w, if (base_face_id == DEFAULT_FACE_ID && FRAME_WINDOW_P (it->f)) { + Lisp_Object line_space_above; + Lisp_Object line_space_below; + if (FIXNATP (BVAR (current_buffer, extra_line_spacing))) - it->extra_line_spacing = XFIXNAT (BVAR (current_buffer, extra_line_spacing)); + { + it->extra_line_spacing = XFIXNAT (BVAR (current_buffer, extra_line_spacing)); + it->extra_line_spacing_above = 0; + } else if (FLOATP (BVAR (current_buffer, extra_line_spacing))) - it->extra_line_spacing = (XFLOAT_DATA (BVAR (current_buffer, extra_line_spacing)) - * FRAME_LINE_HEIGHT (it->f)); + { + it->extra_line_spacing = (XFLOAT_DATA (BVAR (current_buffer, extra_line_spacing)) + * FRAME_LINE_HEIGHT (it->f)); + it->extra_line_spacing_above = 0; + } + else if (CONSP (BVAR (current_buffer, extra_line_spacing))) + { + line_space_above = XCAR (BVAR (current_buffer, extra_line_spacing)); + line_space_below = XCDR (BVAR (current_buffer, extra_line_spacing)); + /* Integer pair case. */ + if (FIXNATP (line_space_above) && FIXNATP (line_space_below)) + { + int line_space_total = XFIXNAT (line_space_below) + XFIXNAT (line_space_above); + it->extra_line_spacing = line_space_total; + it->extra_line_spacing_above = XFIXNAT (line_space_above); + } + /* Float pair case. */ + else if (FLOATP (line_space_above) && FLOATP (line_space_below)) + { + double line_space_total = XFLOAT_DATA (line_space_above) + XFLOAT_DATA (line_space_below); + it->extra_line_spacing = (line_space_total * FRAME_LINE_HEIGHT (it->f)); + it->extra_line_spacing_above = (XFLOAT_DATA (line_space_above) * FRAME_LINE_HEIGHT (it->f)); + } + /* Invalid cons. */ + else + { + it->extra_line_spacing = 0; + it->extra_line_spacing_above = 0; + } + } else if (it->f->extra_line_spacing > 0) - it->extra_line_spacing = it->f->extra_line_spacing; + { + it->extra_line_spacing = it->f->extra_line_spacing; + it->extra_line_spacing_above = it->f->extra_line_spacing_above; + } } /* If realized faces have been removed, e.g. because of face @@ -13157,7 +13194,7 @@ resize_mini_window (struct window *w, bool exact_p) else { struct it it; - int unit = FRAME_LINE_HEIGHT (f); + int unit; int height, max_height; struct text_pos start; struct buffer *old_current_buffer = NULL; @@ -13171,6 +13208,10 @@ resize_mini_window (struct window *w, bool exact_p) init_iterator (&it, w, BEGV, BEGV_BYTE, NULL, DEFAULT_FACE_ID); + /* Unit includes line spacing if line spacing is added above */ + unit = FRAME_LINE_HEIGHT (f) + + (it.extra_line_spacing_above ? it.extra_line_spacing : 0); + /* Compute the max. number of lines specified by the user. */ if (FLOATP (Vmax_mini_window_height)) max_height = XFLOAT_DATA (Vmax_mini_window_height) * windows_height; @@ -13203,7 +13244,10 @@ resize_mini_window (struct window *w, bool exact_p) } else height = it.current_y + it.max_ascent + it.max_descent; - height -= min (it.extra_line_spacing, it.max_extra_line_spacing); + + /* Remove final line spacing in the mini-window */ + if (!it.extra_line_spacing_above) + height -= min (it.extra_line_spacing, it.max_extra_line_spacing); /* Compute a suitable window start. */ if (height > max_height) @@ -13241,13 +13285,13 @@ resize_mini_window (struct window *w, bool exact_p) /* Let it grow only, until we display an empty message, in which case the window shrinks again. */ if (height > old_height) - grow_mini_window (w, height - old_height); + grow_mini_window (w, height - old_height, unit); else if (height < old_height && (exact_p || BEGV == ZV)) - shrink_mini_window (w); + shrink_mini_window (w, unit); } else if (height != old_height) /* Always resize to exact size needed. */ - grow_mini_window (w, height - old_height); + grow_mini_window (w, height - old_height, unit); if (old_current_buffer) set_buffer_internal (old_current_buffer); @@ -24068,6 +24112,7 @@ append_space_for_newline (struct it *it, bool default_face_p) { Lisp_Object height, total_height; int extra_line_spacing = it->extra_line_spacing; + int extra_line_spacing_above = it->extra_line_spacing_above; int boff = font->baseline_offset; if (font->vertical_centering) @@ -24109,7 +24154,7 @@ append_space_for_newline (struct it *it, bool default_face_p) if (!NILP (total_height)) spacing = calc_line_height_property (it, total_height, font, - boff, false); + boff, false); else { spacing = get_it_property (it, Qline_spacing); @@ -24121,11 +24166,13 @@ append_space_for_newline (struct it *it, bool default_face_p) extra_line_spacing = XFIXNUM (spacing); if (!NILP (total_height)) extra_line_spacing -= (it->phys_ascent + it->phys_descent); + } } if (extra_line_spacing > 0) { - it->descent += extra_line_spacing; + it->descent += (extra_line_spacing - extra_line_spacing_above); + it->ascent += extra_line_spacing_above; if (extra_line_spacing > it->max_extra_line_spacing) it->max_extra_line_spacing = extra_line_spacing; } @@ -33138,6 +33185,7 @@ void gui_produce_glyphs (struct it *it) { int extra_line_spacing = it->extra_line_spacing; + int extra_line_spacing_above = it->extra_line_spacing_above; it->glyph_not_available_p = false; @@ -33891,7 +33939,8 @@ gui_produce_glyphs (struct it *it) if (extra_line_spacing > 0) { - it->descent += extra_line_spacing; + it->descent += extra_line_spacing - extra_line_spacing_above; + it->ascent += extra_line_spacing_above; if (extra_line_spacing > it->max_extra_line_spacing) it->max_extra_line_spacing = extra_line_spacing; } diff --git a/test/lisp/subr-tests.el b/test/lisp/subr-tests.el index 1a64cbff0a1..4d9237f08b6 100644 --- a/test/lisp/subr-tests.el +++ b/test/lisp/subr-tests.el @@ -1694,5 +1694,20 @@ final or penultimate step during initialization.")) (should (equal (funcall (subr--identity #'any) #'minusp ls) '(-1 -2 -3))) (should (equal (funcall (subr--identity #'any) #'stringp ls) nil)))) +(ert-deftest total-line-spacing () + (progn + (let ((line-spacing 10)) + (should (equal (total-line-spacing) line-spacing) )) + (let ((line-spacing 0.8)) + (should (equal (total-line-spacing) 0.8))) + (let ((line-spacing '(10 . 5))) + (should (equal (total-line-spacing) 15))) + (let ((line-spacing '(0.3 . 0.4))) + (should (equal (total-line-spacing) 0.7))) + (should (equal (total-line-spacing 10) 10)) + (should (equal (total-line-spacing 0.3) 0.3)) + (should (equal (total-line-spacing '(1 . 3)) 4)) + (should (equal (total-line-spacing '(0.1 . 0.1 )) 0.2)))) + (provide 'subr-tests) ;;; subr-tests.el ends here diff --git a/test/src/buffer-tests.el b/test/src/buffer-tests.el index 9ed76a42603..5f534ed513a 100644 --- a/test/src/buffer-tests.el +++ b/test/src/buffer-tests.el @@ -8650,4 +8650,22 @@ Finally, kill the buffer and its temporary file." (should (= (point-min) 1)) (should (= (point-max) 5001)))) +(ert-deftest test-line-spacing () + "Test `line-spacing' impact on text size" + (skip-unless (display-graphic-p)) + (let* + ((size-with-text (lambda (ls) + (with-temp-buffer + (setq-local line-spacing ls) + (insert "X\nX") + (cdr (buffer-text-pixel-size)))))) + (cl-loop for x from 0 to 50 + for y from 0 to 50 + do + (ert-info ((format "((linespacing '(%d . %d)) == (linespacing %d)" x y (+ x y)) + :prefix "Linespace check: ") + (should (= + (funcall size-with-text (+ x y)) + (funcall size-with-text (cons x y)))))))) + ;;; buffer-tests.el ends here