aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLars Ingebrigtsen2021-11-24 11:55:53 +0100
committerLars Ingebrigtsen2021-11-24 11:55:58 +0100
commita13b437c81f1f2e54555e7281480ea7e8eee8753 (patch)
tree602c0751912e81fef3113bd47720bdad4d649e9e
parent8efee422e1915a000f7220e680e3165407171388 (diff)
downloademacs-a13b437c81f1f2e54555e7281480ea7e8eee8753.tar.gz
emacs-a13b437c81f1f2e54555e7281480ea7e8eee8753.zip
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.
-rw-r--r--doc/lispref/display.texi42
-rw-r--r--etc/NEWS7
-rw-r--r--src/dispextern.h6
-rw-r--r--src/xdisp.c172
-rw-r--r--test/src/xdisp-tests.el16
5 files changed, 237 insertions, 6 deletions
diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi
index 6b1c52b4859..dc53eeff9bf 100644
--- a/doc/lispref/display.texi
+++ b/doc/lispref/display.texi
@@ -4874,9 +4874,7 @@ window on a minibuffer-less frame.
4874 4874
4875 The @code{display} text property (or overlay property) is used to 4875 The @code{display} text property (or overlay property) is used to
4876insert images into text, and to control other aspects of how text 4876insert images into text, and to control other aspects of how text
4877displays. The value of the @code{display} property should be a 4877displays. Display specifications in the same @code{display}
4878display specification, or a list or vector containing several display
4879specifications. Display specifications in the same @code{display}
4880property value generally apply in parallel to the text they cover. 4878property value generally apply in parallel to the text they cover.
4881 4879
4882 If several sources (overlays and/or a text property) specify values 4880 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,
4884following the rules of @code{get-char-property}. @xref{Examining 4882following the rules of @code{get-char-property}. @xref{Examining
4885Properties}. 4883Properties}.
4886 4884
4885 The value of the @code{display} property should be a display
4886specification, or a list or vector containing several display
4887specifications.
4888
4889@defun get-display-property position prop &optional object properties
4890This convenience function can be used to get a specific display
4891property, no matter whether the @code{display} property is a vector, a
4892list or a simple property. This is like @code{get-text-property}
4893(@pxref{Examining Properties}), but works on the @code{display}
4894property only.
4895
4896@var{position} is the position in the buffer or string to examine, and
4897@var{prop} is the @code{display} property to return. The optional
4898@var{object} argument should be either a string or a buffer, and
4899defaults to the current buffer. If the optional @var{properties}
4900argument is non-@code{nil}, it should be a @code{display} property,
4901and in that case, @var{position} and @var{object} are ignored. (This
4902can be useful if you've already gotten the @code{display} property
4903with @code{get-char-property}, for instance (@pxref{Examining
4904Properties}).
4905@end defun
4906
4887@cindex display property, unsafe evaluation 4907@cindex display property, unsafe evaluation
4888@cindex security, and display specifications 4908@cindex security, and display specifications
4889 Some of the display specifications allow inclusion of Lisp forms, 4909 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
5159be an integer or float. Characters other than spaces are not affected 5179be an integer or float. Characters other than spaces are not affected
5160at all; in particular, this has no effect on tab characters. 5180at all; in particular, this has no effect on tab characters.
5161 5181
5182@item (min-width (@var{width}))
5183This display specification adds padding to the end of the text if the
5184text is shorter than @var{width}. The text is partitioned using the
5185identity of the parameter, which is why the parameter is a list with
5186one element. For instance:
5187
5188@lisp
5189(insert (propertize "foo" '(display (min-width (6.0)))))
5190@end lisp
5191
5192This will add padding after @samp{foo} bringing the total width up to
5193the width of six normal characters. Note that the ``range'' is
5194identified by the @code{(6.0)} list, compared with @code{eq}. The
5195width can be either a character width or a pixel specification
5196(@pxref{Pixel Specification}).
5197
5162@item (height @var{height}) 5198@item (height @var{height})
5163This display specification makes the text taller or shorter. 5199This display specification makes the text taller or shorter.
5164Here are the possibilities for @var{height}: 5200Here are the possibilities for @var{height}:
diff --git a/etc/NEWS b/etc/NEWS
index 0bf3d9368b6..1cd49c5289c 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -668,6 +668,13 @@ Use 'exif-parse-file' and 'exif-field' instead.
668 668
669* Lisp Changes in Emacs 29.1 669* Lisp Changes in Emacs 29.1
670 670
671** New function 'get-display-property'.
672This is like 'get-text-property', but works on the 'display' text
673property.
674
675** New 'min-width' 'display' property.
676This allows setting a minimum width for a region.
677
671** Keymaps and key definitions 678** Keymaps and key definitions
672 679
673+++ 680+++
diff --git a/src/dispextern.h b/src/dispextern.h
index a698f6546b1..088297157ac 100644
--- a/src/dispextern.h
+++ b/src/dispextern.h
@@ -2746,6 +2746,12 @@ struct it
2746 /* For iterating over bidirectional text. */ 2746 /* For iterating over bidirectional text. */
2747 struct bidi_it bidi_it; 2747 struct bidi_it bidi_it;
2748 bidi_dir_t paragraph_embedding; 2748 bidi_dir_t paragraph_embedding;
2749
2750 /* For handling the :min-width property. The object is the text
2751 property we're testing the `eq' of (nil if none), and the integer
2752 is the x position of the start of the run of glyphs. */
2753 Lisp_Object min_width_property;
2754 int min_width_start;
2749}; 2755};
2750 2756
2751 2757
diff --git a/src/xdisp.c b/src/xdisp.c
index 11ea8360343..4d3b4878058 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -822,6 +822,9 @@ bool help_echo_showing_p;
822/* Functions to mark elements as needing redisplay. */ 822/* Functions to mark elements as needing redisplay. */
823enum { REDISPLAY_SOME = 2}; /* Arbitrary choice. */ 823enum { REDISPLAY_SOME = 2}; /* Arbitrary choice. */
824 824
825static bool calc_pixel_width_or_height (double *, struct it *, Lisp_Object,
826 struct font *, bool, int *);
827
825void 828void
826redisplay_other_windows (void) 829redisplay_other_windows (void)
827{ 830{
@@ -5141,6 +5144,149 @@ setup_for_ellipsis (struct it *it, int len)
5141 it->ellipsis_p = true; 5144 it->ellipsis_p = true;
5142} 5145}
5143 5146
5147
5148static Lisp_Object
5149find_display_property (Lisp_Object disp, Lisp_Object prop)
5150{
5151 if (NILP (disp))
5152 return Qnil;
5153 /* We have a vector of display specs. */
5154 if (VECTORP (disp))
5155 {
5156 for (ptrdiff_t i = 0; i < ASIZE (disp); i++)
5157 {
5158 Lisp_Object elem = AREF (disp, i);
5159 if (CONSP (elem)
5160 && CONSP (XCDR (elem))
5161 && EQ (XCAR (elem), prop))
5162 return XCAR (XCDR (elem));
5163 }
5164 return Qnil;
5165 }
5166 /* We have a list of display specs. */
5167 else if (CONSP (disp)
5168 && CONSP (XCAR (disp)))
5169 {
5170 while (!NILP (disp))
5171 {
5172 Lisp_Object elem = XCAR (disp);
5173 if (CONSP (elem)
5174 && CONSP (XCDR (elem))
5175 && EQ (XCAR (elem), prop))
5176 return XCAR (XCDR (elem));
5177
5178 /* Check that we have a proper list before going to the next
5179 element. */
5180 if (CONSP (XCDR (disp)))
5181 disp = XCDR (disp);
5182 else
5183 disp = Qnil;
5184 }
5185 return Qnil;
5186 }
5187 /* A simple display spec. */
5188 else if (CONSP (disp)
5189 && CONSP (XCDR (disp))
5190 && EQ (XCAR (disp), prop))
5191 return XCAR (XCDR (disp));
5192 else
5193 return Qnil;
5194}
5195
5196static Lisp_Object get_display_property (ptrdiff_t bufpos, Lisp_Object prop,
5197 Lisp_Object object)
5198{
5199 return find_display_property (Fget_text_property (make_fixnum (bufpos),
5200
5201 Qdisplay, object),
5202 prop);
5203}
5204
5205static void
5206display_min_width (struct it *it, ptrdiff_t bufpos,
5207 Lisp_Object object, Lisp_Object width_spec)
5208{
5209 /* We're being called at the end of the `min-width' sequence,
5210 probably. */
5211 if (!NILP (it->min_width_property)
5212 && !EQ (width_spec, it->min_width_property))
5213 {
5214 if (!it->glyph_row)
5215 return;
5216
5217 /* Check that we're really right after the sequence of
5218 characters covered by this `min-width'. */
5219 if (bufpos > BEGV
5220 && EQ (it->min_width_property,
5221 get_display_property (bufpos - 1, Qmin_width, object)))
5222 {
5223 Lisp_Object w = Qnil;
5224 double width;
5225#ifdef HAVE_WINDOW_SYSTEM
5226 if (FRAME_WINDOW_P (it->f))
5227 {
5228 struct font *font = NULL;
5229 struct face *face = FACE_FROM_ID (it->f, it->face_id);
5230 font = face->font ? face->font : FRAME_FONT (it->f);
5231 calc_pixel_width_or_height (&width, it,
5232 XCAR (it->min_width_property),
5233 font, true, NULL);
5234 width -= it->current_x - it->min_width_start;
5235 w = list1 (make_int (width));
5236 }
5237 else
5238#endif
5239 {
5240 calc_pixel_width_or_height (&width, it,
5241 XCAR (it->min_width_property),
5242 NULL, true, NULL);
5243 width -= (it->current_x - it->min_width_start) /
5244 FRAME_COLUMN_WIDTH (it->f);
5245 w = make_int (width);
5246 }
5247
5248 /* Insert the stretch glyph. */
5249 it->object = list3 (Qspace, QCwidth, w);
5250 produce_stretch_glyph (it);
5251 it->min_width_property = Qnil;
5252 }
5253 }
5254
5255 /* We're at the start of a `min-width' sequence -- record the
5256 position and the property, so that we can later see if we're at
5257 the end. */
5258 if (CONSP (width_spec))
5259 {
5260 if (bufpos == BEGV
5261 || (bufpos > BEGV
5262 && !EQ (width_spec,
5263 get_display_property (bufpos - 1, Qmin_width, object))))
5264 {
5265 it->min_width_property = width_spec;
5266 it->min_width_start = it->current_x;
5267 }
5268 }
5269}
5270
5271DEFUN ("get-display-property", Fget_display_property,
5272 Sget_display_property, 2, 4, 0,
5273 doc: /* Get the `display' property PROP at POSITION.
5274If OBJECT, this should be a buffer or string where the property is
5275fetched from. This defaults to the current buffer.
5276
5277If PROPERTIES, use those properties instead of the properties at
5278POSITION. */)
5279 (Lisp_Object position, Lisp_Object prop, Lisp_Object object,
5280 Lisp_Object properties)
5281{
5282 if (NILP (properties))
5283 properties = Fget_text_property (position, Qdisplay, object);
5284 else
5285 CHECK_LIST (properties);
5286
5287 return find_display_property (properties, prop);
5288}
5289
5144 5290
5145 5291
5146/*********************************************************************** 5292/***********************************************************************
@@ -5187,16 +5333,22 @@ handle_display_prop (struct it *it)
5187 if (!it->string_from_display_prop_p) 5333 if (!it->string_from_display_prop_p)
5188 it->area = TEXT_AREA; 5334 it->area = TEXT_AREA;
5189 5335
5336 if (!STRINGP (it->string))
5337 object = it->w->contents;
5338
5190 propval = get_char_property_and_overlay (make_fixnum (position->charpos), 5339 propval = get_char_property_and_overlay (make_fixnum (position->charpos),
5191 Qdisplay, object, &overlay); 5340 Qdisplay, object, &overlay);
5341
5342 /* Handle min-width ends. */
5343 if (! NILP (it->min_width_property)
5344 && NILP (find_display_property (propval, Qmin_width)))
5345 display_min_width (it, bufpos, object, Qnil);
5346
5192 if (NILP (propval)) 5347 if (NILP (propval))
5193 return HANDLED_NORMALLY; 5348 return HANDLED_NORMALLY;
5194 /* Now OVERLAY is the overlay that gave us this property, or nil 5349 /* Now OVERLAY is the overlay that gave us this property, or nil
5195 if it was a text property. */ 5350 if it was a text property. */
5196 5351
5197 if (!STRINGP (it->string))
5198 object = it->w->contents;
5199
5200 display_replaced = handle_display_spec (it, propval, object, overlay, 5352 display_replaced = handle_display_spec (it, propval, object, overlay,
5201 position, bufpos, 5353 position, bufpos,
5202 FRAME_WINDOW_P (it->f)); 5354 FRAME_WINDOW_P (it->f));
@@ -5250,6 +5402,7 @@ handle_display_spec (struct it *it, Lisp_Object spec, Lisp_Object object,
5250 && !(CONSP (XCAR (spec)) && EQ (XCAR (XCAR (spec)), Qmargin)) 5402 && !(CONSP (XCAR (spec)) && EQ (XCAR (XCAR (spec)), Qmargin))
5251 && !EQ (XCAR (spec), Qleft_fringe) 5403 && !EQ (XCAR (spec), Qleft_fringe)
5252 && !EQ (XCAR (spec), Qright_fringe) 5404 && !EQ (XCAR (spec), Qright_fringe)
5405 && !EQ (XCAR (spec), Qmin_width)
5253 && !NILP (XCAR (spec))) 5406 && !NILP (XCAR (spec)))
5254 { 5407 {
5255 for (; CONSP (spec); spec = XCDR (spec)) 5408 for (; CONSP (spec); spec = XCDR (spec))
@@ -5483,6 +5636,17 @@ handle_single_display_spec (struct it *it, Lisp_Object spec, Lisp_Object object,
5483 return 0; 5636 return 0;
5484 } 5637 }
5485 5638
5639 /* Handle `(min-width (WIDTH))'. */
5640 if (CONSP (spec)
5641 && EQ (XCAR (spec), Qmin_width)
5642 && CONSP (XCDR (spec))
5643 && CONSP (XCAR (XCDR (spec))))
5644 {
5645 if (it)
5646 display_min_width (it, bufpos, object, XCAR (XCDR (spec)));
5647 return 0;
5648 }
5649
5486 /* Handle `(slice X Y WIDTH HEIGHT)'. */ 5650 /* Handle `(slice X Y WIDTH HEIGHT)'. */
5487 if (CONSP (spec) 5651 if (CONSP (spec)
5488 && EQ (XCAR (spec), Qslice)) 5652 && EQ (XCAR (spec), Qslice))
@@ -7186,6 +7350,7 @@ reseat_1 (struct it *it, struct text_pos pos, bool set_stop_p)
7186 } 7350 }
7187 /* This make the information stored in it->cmp_it invalidate. */ 7351 /* This make the information stored in it->cmp_it invalidate. */
7188 it->cmp_it.id = -1; 7352 it->cmp_it.id = -1;
7353 it->min_width_property = Qnil;
7189} 7354}
7190 7355
7191 7356
@@ -35121,6 +35286,7 @@ be let-bound around code that needs to disable messages temporarily. */);
35121 defsubr (&Smove_point_visually); 35286 defsubr (&Smove_point_visually);
35122 defsubr (&Sbidi_find_overridden_directionality); 35287 defsubr (&Sbidi_find_overridden_directionality);
35123 defsubr (&Sdisplay__line_is_continued_p); 35288 defsubr (&Sdisplay__line_is_continued_p);
35289 defsubr (&Sget_display_property);
35124 35290
35125 DEFSYM (Qmenu_bar_update_hook, "menu-bar-update-hook"); 35291 DEFSYM (Qmenu_bar_update_hook, "menu-bar-update-hook");
35126 DEFSYM (Qoverriding_terminal_local_map, "overriding-terminal-local-map"); 35292 DEFSYM (Qoverriding_terminal_local_map, "overriding-terminal-local-map");
diff --git a/test/src/xdisp-tests.el b/test/src/xdisp-tests.el
index cc67aef8e15..ae4aacd9c7c 100644
--- a/test/src/xdisp-tests.el
+++ b/test/src/xdisp-tests.el
@@ -154,4 +154,20 @@ int main () {
154 nil) 154 nil)
155 138)))) 155 138))))
156 156
157(ert-deftest test-get-display-property ()
158 (with-temp-buffer
159 (insert (propertize "foo" 'face 'bold 'display '(height 2.0)))
160 (should (equal (get-display-property 2 'height) 2.0)))
161 (with-temp-buffer
162 (insert (propertize "foo" 'face 'bold 'display '((height 2.0)
163 (space-width 2.0))))
164 (should (equal (get-display-property 2 'height) 2.0))
165 (should (equal (get-display-property 2 'space-width) 2.0)))
166 (with-temp-buffer
167 (insert (propertize "foo bar" 'face 'bold
168 'display '[(height 2.0)
169 (space-width 20)]))
170 (should (equal (get-display-property 2 'height) 2.0))
171 (should (equal (get-display-property 2 'space-width) 20))))
172
157;;; xdisp-tests.el ends here 173;;; xdisp-tests.el ends here