master 5e48f106852: Fix :align-to when a number precedes a window element

Eli Zaretskii <[email protected]>
Newsgroups gmane.emacs.diffs
Message-ID <[email protected]>
branch: master
commit 5e48f106852d36359ad1bee9f54e9fa94d568132
Author: Andrea Alberti <[email protected]>
Commit: Eli Zaretskii <[email protected]>

    Fix :align-to when a number precedes a window element
    
    * src/xdisp.c (calc_pixel_width_or_height): Track the line-number
    width in a new LNUM_ADDED argument instead of overloading *ALIGN_TO,
    so it is counted once and a window element following a numeric term is
    still recognized.  (Bug#81253)
    (produce_stretch_glyph, display_min_width): Pass the new argument.
---
 src/xdisp.c | 91 +++++++++++++++++++++++++++++++++++--------------------------
 1 file changed, 53 insertions(+), 38 deletions(-)

diff --git a/src/xdisp.c b/src/xdisp.c
index 4af9bcca428..295a13b99ed 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -857,7 +857,7 @@ bool help_echo_showing_p;
 enum { REDISPLAY_SOME = 2};	/* Arbitrary choice.  */
 
 static bool calc_pixel_width_or_height (double *, struct it *, Lisp_Object,
-					struct font *, bool, int *);
+					struct font *, bool, int *, bool *);
 
 void
 redisplay_other_windows (void)
@@ -5807,7 +5807,7 @@ display_min_width (struct it *it, ptrdiff_t charpos,
 	      font = face->font ? face->font : FRAME_FONT (it->f);
 	      calc_pixel_width_or_height (&width, it,
 					  XCAR (it->min_width_property),
-					  font, true, NULL);
+					  font, true, NULL, NULL);
 	      width -= it->current_x - it->min_width_start;
 	      /* It makes no sense to try to obey min-width which yields
                  a stretch that ends beyond the visible portion of the
@@ -5823,7 +5823,7 @@ display_min_width (struct it *it, ptrdiff_t charpos,
 	    {
 	      calc_pixel_width_or_height (&width, it,
 					  XCAR (it->min_width_property),
-					  NULL, true, NULL);
+					  NULL, true, NULL, NULL);
 	      width -= (it->current_x - it->min_width_start) /
 		FRAME_COLUMN_WIDTH (it->f);
 	      if (width > 0
@@ -30391,15 +30391,26 @@ display may depend on `buffer-invisibility-spec', which see.  */)
    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.  If *ALIGN_TO is negative, it
-   means no element contributed to alignment (yet).  The value starts at
-   -1, and is set to either -2 or a positive number once we've processed
-   the first element that contributes to the alignment.
+   till the end of the stretch glyph.  *ALIGN_TO starts at -1 and stays
+   negative until a window element (e.g. 'left' or 'center') contributes
+   an anchor position, at which point it holds that non-negative
+   coordinate.
+
+   LNUM_ADDED, if non-NULL, must point to a flag initialized to false.
+   The default text-area base, which includes the line-number width,
+   must contribute to the alignment exactly once; whichever term first
+   establishes that base adds the line-number width and sets
+   *LNUM_ADDED, so it is never counted more than once per alignment.
+   Because of the recursive nature of the function, the state needs to
+   be managed with a pointer: if the code flow branches into subcalls of
+   the same function and one of the subcalls applies the line-number
+   width offset, the change of state needs to propagate to the sibling
+   subcalls to avoid applying the offset once again.
 
    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.)
+   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.
 
@@ -30407,8 +30418,9 @@ display may depend on `buffer-invisibility-spec', which see.  */)
    calculated, i.e. if PROP is a valid spec.  */
 
 static bool
-calc_pixel_width_or_height (double *res, struct it *it, Lisp_Object prop,
-			    struct font *font, bool width_p, int *align_to)
+calc_pixel_width_or_height (double *res, struct it *it,
+			    Lisp_Object prop, struct font *font,
+			    bool width_p, int *align_to, bool *lnum_added)
 {
   /* Don't adjust for line number if we didn't yet produce it for this
      screen line.  This is for when this function is called from
@@ -30419,6 +30431,11 @@ calc_pixel_width_or_height (double *res, struct it *it, Lisp_Object prop,
 
 # define OK_PIXELS(val) (*res = (val), true)
 # define OK_ALIGN_TO(val) (*align_to = (val), true)
+  /* Add the line-number width to an alignment base only once;
+     LNUM_ONCE yields it the first time and 0 after.  */
+# define LNUM_ONCE (lnum_added && !*lnum_added			\
+		    ? (*lnum_added = true, lnum_pixel_width)	\
+		    : 0)
 
   if (NILP (prop))
     return OK_PIXELS (0);
@@ -30479,20 +30496,20 @@ calc_pixel_width_or_height (double *res, struct it *it, Lisp_Object prop,
       /* ':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 == -1)
+      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)
-				+ lnum_pixel_width);
+				+ LNUM_ONCE);
 	  /* '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)
-				+ lnum_pixel_width
+				+ LNUM_ONCE
 				+ window_box_width (it->w, TEXT_AREA) / 2);
 	  /* 'left-fringe': left edge of the left fringe.  */
 	  if (EQ (prop, Qleft_fringe))
@@ -30544,15 +30561,12 @@ 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));
-      /* `align_to' starts at -1.  A numeric value without an explicit
-	 base is relative to the text area's left edge, so account for
-	 line numbers once and mark the default base as consumed, so we
-	 don't account for the line-number width more than once.  */
-      if (width_p && align_to && *align_to == -1)
-	{
-	  *align_to = -2;
-	  return OK_PIXELS (XFLOATINT (prop) * base_unit + lnum_pixel_width);
-	}
+      /* A numeric value is measured from the text area's left edge
+	 until a window element sets an anchor.  While *align_to < 0,
+	 include the line-number width, but only once per alignment via
+	 the macro LNUM_ONCE.  */
+      if (width_p && align_to && *align_to < 0)
+	return OK_PIXELS (XFLOATINT (prop) * base_unit + LNUM_ONCE);
       return OK_PIXELS (XFLOATINT (prop) * base_unit);
     }
 
@@ -30591,7 +30605,8 @@ calc_pixel_width_or_height (double *res, struct it *it, Lisp_Object prop,
 	      while (CONSP (cdr))
 		{
 		  if (!calc_pixel_width_or_height (&px, it, XCAR (cdr),
-						   font, width_p, align_to))
+						   font, width_p, align_to,
+						   lnum_added))
 		    return false;
 		  if (first)
 		    pixels = (EQ (car, Qplus) ? px : -px), first = false;
@@ -30613,20 +30628,16 @@ calc_pixel_width_or_height (double *res, struct it *it, Lisp_Object prop,
       if (NUMBERP (car))
 	{
 	  double fact;
-	  int offset = 0;
-	  /* See the NUMBERP case above: the default text-area base
-	     should apply only once to the whole pixel expression, not
-	     once for each numeric subexpression.  */
-	  if (width_p && align_to && *align_to == -1)
-	    {
-	      offset = lnum_pixel_width;
-	      *align_to = -2;
-	    }
+	  /* As in the NUMBERP case above: while *align_to < 0, add the
+	     line-number width, but only once per alignment via the macro
+	     LNUM_ONCE.  */
+	  int offset = (width_p && align_to && *align_to < 0) ? LNUM_ONCE : 0;
 	  pixels = XFLOATINT (car);
 	  if (NILP (cdr))
 	    return OK_PIXELS (pixels + offset);
 	  if (calc_pixel_width_or_height (&fact, it, cdr,
-					  font, width_p, align_to))
+					  font, width_p, align_to,
+					  lnum_added))
 	    return OK_PIXELS (pixels * fact + offset);
 	  return false;
 	}
@@ -32858,9 +32869,13 @@ produce_stretch_glyph (struct it *it)
   eassert (CONSP (it->object) && EQ (XCAR (it->object), Qspace));
   plist = XCDR (it->object);
 
+  /* Seeds calc_pixel_width_or_height's recursion; must start false.
+     Its value on return is ignored here, hence the name.  */
+  bool lnum_added_ignored = false;
+
   /* Compute the width of the stretch.  */
   if ((prop = plist_get (plist, QCwidth), !NILP (prop))
-      && calc_pixel_width_or_height (&tem, it, prop, font, true, NULL))
+      && calc_pixel_width_or_height (&tem, it, prop, font, true, NULL, NULL))
     {
       /* Absolute width `:width WIDTH' specified and valid.  */
       zero_width_ok_p = true;
@@ -32905,7 +32920,7 @@ produce_stretch_glyph (struct it *it)
     }
   else if ((prop = plist_get (plist, QCalign_to), !NILP (prop))
 	   && calc_pixel_width_or_height (&tem, it, prop, font, true,
-					  &align_to))
+					  &align_to, &lnum_added_ignored))
     {
       int x = it->current_x + (it->align_visually_p
 			       ? 0
@@ -32959,7 +32974,7 @@ produce_stretch_glyph (struct it *it)
 	font ? normal_char_height (font, ' ') : FRAME_LINE_HEIGHT (it->f);
 
       if ((prop = plist_get (plist, QCheight), !NILP (prop))
-	  && calc_pixel_width_or_height (&tem, it, prop, font, false, NULL))
+	  && calc_pixel_width_or_height (&tem, it, prop, font, false, NULL, NULL))
 	{
 	  height = (int)tem;
 	  zero_height_ok_p = true;
@@ -32980,7 +32995,7 @@ produce_stretch_glyph (struct it *it)
           NUMVAL (prop) > 0 && NUMVAL (prop) <= 100)
 	ascent = height * NUMVAL (prop) / 100.0;
       else if (!NILP (prop)
-	       && calc_pixel_width_or_height (&tem, it, prop, font, false, 0))
+	       && calc_pixel_width_or_height (&tem, it, prop, font, false, NULL, NULL))
 	ascent = min (max (0, (int)tem), height);
       else
 	ascent = (height * FONT_BASE (font)) / FONT_HEIGHT (font);
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.