emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

master a13b437 2/2: Add support for the min-width display property


From: Lars Ingebrigtsen
Subject: master a13b437 2/2: Add support for the min-width display property
Date: Wed, 24 Nov 2021 05:56:04 -0500 (EST)

branch: master
commit a13b437c81f1f2e54555e7281480ea7e8eee8753
Author: Lars Ingebrigtsen <larsi@gnus.org>
Commit: Lars Ingebrigtsen <larsi@gnus.org>

    Add support for the min-width display property
    
    * doc/lispref/display.texi (Display Property): Document
    get-display-property.
    (Other Display Specs): Document min-width property.
    
    * src/dispextern.h (struct it): Add fields for min-width handling.
    
    * src/xdisp.c (find_display_property, get_display_property): New
    helper functions.
    (display_min_width): Insert stretch glyphs based on the min width.
    (Fget_display_property): New defun.
    (handle_display_prop): Handle min-width ends.
    (handle_single_display_spec): Handle min-width starts.
---
 doc/lispref/display.texi |  42 +++++++++++-
 etc/NEWS                 |   7 ++
 src/dispextern.h         |   6 ++
 src/xdisp.c              | 172 ++++++++++++++++++++++++++++++++++++++++++++++-
 test/src/xdisp-tests.el  |  16 +++++
 5 files changed, 237 insertions(+), 6 deletions(-)

diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi
index 6b1c52b..dc53eef 100644
--- a/doc/lispref/display.texi
+++ b/doc/lispref/display.texi
@@ -4874,9 +4874,7 @@ window on a minibuffer-less frame.
 
   The @code{display} text property (or overlay property) is used to
 insert images into text, and to control other aspects of how text
-displays.  The value of the @code{display} property should be a
-display specification, or a list or vector containing several display
-specifications.  Display specifications in the same @code{display}
+displays.  Display specifications in the same @code{display}
 property value generally apply in parallel to the text they cover.
 
   If several sources (overlays and/or a text property) specify values
@@ -4884,6 +4882,28 @@ for the @code{display} property, only one of the values 
takes effect,
 following the rules of @code{get-char-property}.  @xref{Examining
 Properties}.
 
+  The value of the @code{display} property should be a display
+specification, or a list or vector containing several display
+specifications.
+
+@defun get-display-property position prop &optional object properties
+This convenience function can be used to get a specific display
+property, no matter whether the @code{display} property is a vector, a
+list or a simple property.  This is like @code{get-text-property}
+(@pxref{Examining Properties}), but works on the @code{display}
+property only.
+
+@var{position} is the position in the buffer or string to examine, and
+@var{prop} is the @code{display} property to return.  The optional
+@var{object} argument should be either a string or a buffer, and
+defaults to the current buffer.  If the optional @var{properties}
+argument is non-@code{nil}, it should be a @code{display} property,
+and in that case, @var{position} and @var{object} are ignored.  (This
+can be useful if you've already gotten the @code{display} property
+with @code{get-char-property}, for instance (@pxref{Examining
+Properties}).
+@end defun
+
 @cindex display property, unsafe evaluation
 @cindex security, and display specifications
   Some of the display specifications allow inclusion of Lisp forms,
@@ -5159,6 +5179,22 @@ text that has the specification.  It displays all of 
these spaces
 be an integer or float.  Characters other than spaces are not affected
 at all; in particular, this has no effect on tab characters.
 
+@item (min-width (@var{width}))
+This display specification adds padding to the end of the text if the
+text is shorter than @var{width}.  The text is partitioned using the
+identity of the parameter, which is why the parameter is a list with
+one element.  For instance:
+
+@lisp
+(insert (propertize "foo" '(display (min-width (6.0)))))
+@end lisp
+
+This will add padding after @samp{foo} bringing the total width up to
+the width of six normal characters.  Note that the ``range'' is
+identified by the @code{(6.0)} list, compared with @code{eq}.  The
+width can be either a character width or a pixel specification
+(@pxref{Pixel Specification}).
+
 @item (height @var{height})
 This display specification makes the text taller or shorter.
 Here are the possibilities for @var{height}:
diff --git a/etc/NEWS b/etc/NEWS
index 0bf3d93..1cd49c5 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -668,6 +668,13 @@ Use 'exif-parse-file' and 'exif-field' instead.
 
 * Lisp Changes in Emacs 29.1
 
+** New function 'get-display-property'.
+This is like 'get-text-property', but works on the 'display' text
+property.
+
+** New 'min-width' 'display' property.
+This allows setting a minimum width for a region.
+
 ** Keymaps and key definitions
 
 +++
diff --git a/src/dispextern.h b/src/dispextern.h
index a698f65..0882971 100644
--- a/src/dispextern.h
+++ b/src/dispextern.h
@@ -2746,6 +2746,12 @@ struct it
   /* For iterating over bidirectional text.  */
   struct bidi_it bidi_it;
   bidi_dir_t paragraph_embedding;
+
+  /* For handling the :min-width property.  The object is the text
+     property we're testing the `eq' of (nil if none), and the integer
+     is the x position of the start of the run of glyphs. */
+  Lisp_Object min_width_property;
+  int min_width_start;
 };
 
 
diff --git a/src/xdisp.c b/src/xdisp.c
index 11ea836..4d3b487 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -822,6 +822,9 @@ bool help_echo_showing_p;
 /* Functions to mark elements as needing redisplay.  */
 enum { REDISPLAY_SOME = 2};    /* Arbitrary choice.  */
 
+static bool calc_pixel_width_or_height (double *, struct it *, Lisp_Object,
+                                       struct font *, bool, int *);
+
 void
 redisplay_other_windows (void)
 {
@@ -5141,6 +5144,149 @@ setup_for_ellipsis (struct it *it, int len)
   it->ellipsis_p = true;
 }
 
+
+static Lisp_Object
+find_display_property (Lisp_Object disp, Lisp_Object prop)
+{
+  if (NILP (disp))
+    return Qnil;
+  /* We have a vector of display specs. */
+  if (VECTORP (disp))
+    {
+      for (ptrdiff_t i = 0; i < ASIZE (disp); i++)
+       {
+         Lisp_Object elem = AREF (disp, i);
+         if (CONSP (elem)
+             && CONSP (XCDR (elem))
+             && EQ (XCAR (elem), prop))
+           return XCAR (XCDR (elem));
+       }
+      return Qnil;
+    }
+  /* We have a list of display specs. */
+  else if (CONSP (disp)
+          && CONSP (XCAR (disp)))
+    {
+      while (!NILP (disp))
+       {
+         Lisp_Object elem = XCAR (disp);
+         if (CONSP (elem)
+             && CONSP (XCDR (elem))
+             && EQ (XCAR (elem), prop))
+           return XCAR (XCDR (elem));
+
+         /* Check that we have a proper list before going to the next
+            element. */
+         if (CONSP (XCDR (disp)))
+           disp = XCDR (disp);
+         else
+           disp = Qnil;
+       }
+      return Qnil;
+    }
+  /* A simple display spec. */
+  else if (CONSP (disp)
+          && CONSP (XCDR (disp))
+          && EQ (XCAR (disp), prop))
+    return XCAR (XCDR (disp));
+  else
+    return Qnil;
+}
+
+static Lisp_Object get_display_property (ptrdiff_t bufpos, Lisp_Object prop,
+                                        Lisp_Object object)
+{
+  return find_display_property (Fget_text_property (make_fixnum (bufpos),
+
+                                                   Qdisplay, object),
+                               prop);
+}
+
+static void
+display_min_width (struct it *it, ptrdiff_t bufpos,
+                  Lisp_Object object, Lisp_Object width_spec)
+{
+  /* We're being called at the end of the `min-width' sequence,
+     probably. */
+  if (!NILP (it->min_width_property)
+      && !EQ (width_spec, it->min_width_property))
+    {
+      if (!it->glyph_row)
+       return;
+
+      /* Check that we're really right after the sequence of
+        characters covered by this `min-width'.  */
+      if (bufpos > BEGV
+         && EQ (it->min_width_property,
+                get_display_property (bufpos - 1, Qmin_width, object)))
+       {
+         Lisp_Object w = Qnil;
+         double width;
+#ifdef HAVE_WINDOW_SYSTEM
+         if (FRAME_WINDOW_P (it->f))
+           {
+             struct font *font = NULL;
+             struct face *face = FACE_FROM_ID (it->f, it->face_id);
+             font = face->font ? face->font : FRAME_FONT (it->f);
+             calc_pixel_width_or_height (&width, it,
+                                         XCAR (it->min_width_property),
+                                         font, true, NULL);
+             width -= it->current_x - it->min_width_start;
+             w = list1 (make_int (width));
+           }
+         else
+#endif
+           {
+             calc_pixel_width_or_height (&width, it,
+                                         XCAR (it->min_width_property),
+                                         NULL, true, NULL);
+             width -= (it->current_x - it->min_width_start) /
+               FRAME_COLUMN_WIDTH (it->f);
+             w = make_int (width);
+           }
+
+         /* Insert the stretch glyph.  */
+         it->object = list3 (Qspace, QCwidth, w);
+         produce_stretch_glyph (it);
+         it->min_width_property = Qnil;
+       }
+    }
+
+  /* We're at the start of a `min-width' sequence -- record the
+     position and the property, so that we can later see if we're at
+     the end.  */
+  if (CONSP (width_spec))
+    {
+      if (bufpos == BEGV
+         || (bufpos > BEGV
+             && !EQ (width_spec,
+                     get_display_property (bufpos - 1, Qmin_width, object))))
+       {
+         it->min_width_property = width_spec;
+         it->min_width_start = it->current_x;
+       }
+    }
+}
+
+DEFUN ("get-display-property", Fget_display_property,
+       Sget_display_property, 2, 4, 0,
+       doc: /* Get the `display' property PROP at POSITION.
+If OBJECT, this should be a buffer or string where the property is
+fetched from.  This defaults to the current buffer.
+
+If PROPERTIES, use those properties instead of the properties at
+POSITION.  */)
+  (Lisp_Object position, Lisp_Object prop, Lisp_Object object,
+   Lisp_Object properties)
+{
+  if (NILP (properties))
+    properties = Fget_text_property (position, Qdisplay, object);
+  else
+    CHECK_LIST (properties);
+
+  return find_display_property (properties, prop);
+}
+
 
 
 /***********************************************************************
@@ -5187,16 +5333,22 @@ handle_display_prop (struct it *it)
   if (!it->string_from_display_prop_p)
     it->area = TEXT_AREA;
 
+  if (!STRINGP (it->string))
+    object = it->w->contents;
+
   propval = get_char_property_and_overlay (make_fixnum (position->charpos),
                                           Qdisplay, object, &overlay);
+
+  /* Handle min-width ends. */
+  if (! NILP (it->min_width_property)
+      && NILP (find_display_property (propval, Qmin_width)))
+    display_min_width (it, bufpos, object, Qnil);
+
   if (NILP (propval))
     return HANDLED_NORMALLY;
   /* Now OVERLAY is the overlay that gave us this property, or nil
      if it was a text property.  */
 
-  if (!STRINGP (it->string))
-    object = it->w->contents;
-
   display_replaced = handle_display_spec (it, propval, object, overlay,
                                          position, bufpos,
                                          FRAME_WINDOW_P (it->f));
@@ -5250,6 +5402,7 @@ handle_display_spec (struct it *it, Lisp_Object spec, 
Lisp_Object object,
       && !(CONSP (XCAR (spec)) && EQ (XCAR (XCAR (spec)), Qmargin))
       && !EQ (XCAR (spec), Qleft_fringe)
       && !EQ (XCAR (spec), Qright_fringe)
+      && !EQ (XCAR (spec), Qmin_width)
       && !NILP (XCAR (spec)))
     {
       for (; CONSP (spec); spec = XCDR (spec))
@@ -5483,6 +5636,17 @@ handle_single_display_spec (struct it *it, Lisp_Object 
spec, Lisp_Object object,
       return 0;
     }
 
+  /* Handle `(min-width (WIDTH))'.  */
+  if (CONSP (spec)
+      && EQ (XCAR (spec), Qmin_width)
+      && CONSP (XCDR (spec))
+      && CONSP (XCAR (XCDR (spec))))
+    {
+      if (it)
+       display_min_width (it, bufpos, object, XCAR (XCDR (spec)));
+      return 0;
+    }
+
   /* Handle `(slice X Y WIDTH HEIGHT)'.  */
   if (CONSP (spec)
       && EQ (XCAR (spec), Qslice))
@@ -7186,6 +7350,7 @@ reseat_1 (struct it *it, struct text_pos pos, bool 
set_stop_p)
     }
   /* This make the information stored in it->cmp_it invalidate.  */
   it->cmp_it.id = -1;
+  it->min_width_property = Qnil;
 }
 
 
@@ -35121,6 +35286,7 @@ be let-bound around code that needs to disable messages 
temporarily. */);
   defsubr (&Smove_point_visually);
   defsubr (&Sbidi_find_overridden_directionality);
   defsubr (&Sdisplay__line_is_continued_p);
+  defsubr (&Sget_display_property);
 
   DEFSYM (Qmenu_bar_update_hook, "menu-bar-update-hook");
   DEFSYM (Qoverriding_terminal_local_map, "overriding-terminal-local-map");
diff --git a/test/src/xdisp-tests.el b/test/src/xdisp-tests.el
index cc67aef..ae4aacd 100644
--- a/test/src/xdisp-tests.el
+++ b/test/src/xdisp-tests.el
@@ -154,4 +154,20 @@ int main () {
                                                      nil)
                 138))))
 
+(ert-deftest test-get-display-property ()
+  (with-temp-buffer
+    (insert (propertize "foo" 'face 'bold 'display '(height 2.0)))
+    (should (equal (get-display-property 2 'height) 2.0)))
+  (with-temp-buffer
+    (insert (propertize "foo" 'face 'bold 'display '((height 2.0)
+                                                     (space-width 2.0))))
+    (should (equal (get-display-property 2 'height) 2.0))
+    (should (equal (get-display-property 2 'space-width) 2.0)))
+  (with-temp-buffer
+    (insert (propertize "foo bar" 'face 'bold
+                        'display '[(height 2.0)
+                                   (space-width 20)]))
+    (should (equal (get-display-property 2 'height) 2.0))
+    (should (equal (get-display-property 2 'space-width) 20))))
+
 ;;; xdisp-tests.el ends here



reply via email to

[Prev in Thread] Current Thread [Next in Thread]