diff options
| author | Lars Ingebrigtsen | 2021-11-24 11:55:53 +0100 |
|---|---|---|
| committer | Lars Ingebrigtsen | 2021-11-24 11:55:58 +0100 |
| commit | a13b437c81f1f2e54555e7281480ea7e8eee8753 (patch) | |
| tree | 602c0751912e81fef3113bd47720bdad4d649e9e | |
| parent | 8efee422e1915a000f7220e680e3165407171388 (diff) | |
| download | emacs-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.texi | 42 | ||||
| -rw-r--r-- | etc/NEWS | 7 | ||||
| -rw-r--r-- | src/dispextern.h | 6 | ||||
| -rw-r--r-- | src/xdisp.c | 172 | ||||
| -rw-r--r-- | 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 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 |
| 4876 | insert images into text, and to control other aspects of how text | 4876 | insert images into text, and to control other aspects of how text |
| 4877 | displays. The value of the @code{display} property should be a | 4877 | displays. Display specifications in the same @code{display} |
| 4878 | display specification, or a list or vector containing several display | ||
| 4879 | specifications. Display specifications in the same @code{display} | ||
| 4880 | property value generally apply in parallel to the text they cover. | 4878 | property 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, | |||
| 4884 | following the rules of @code{get-char-property}. @xref{Examining | 4882 | following the rules of @code{get-char-property}. @xref{Examining |
| 4885 | Properties}. | 4883 | Properties}. |
| 4886 | 4884 | ||
| 4885 | The value of the @code{display} property should be a display | ||
| 4886 | specification, or a list or vector containing several display | ||
| 4887 | specifications. | ||
| 4888 | |||
| 4889 | @defun get-display-property position prop &optional object properties | ||
| 4890 | This convenience function can be used to get a specific display | ||
| 4891 | property, no matter whether the @code{display} property is a vector, a | ||
| 4892 | list or a simple property. This is like @code{get-text-property} | ||
| 4893 | (@pxref{Examining Properties}), but works on the @code{display} | ||
| 4894 | property 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 | ||
| 4899 | defaults to the current buffer. If the optional @var{properties} | ||
| 4900 | argument is non-@code{nil}, it should be a @code{display} property, | ||
| 4901 | and in that case, @var{position} and @var{object} are ignored. (This | ||
| 4902 | can be useful if you've already gotten the @code{display} property | ||
| 4903 | with @code{get-char-property}, for instance (@pxref{Examining | ||
| 4904 | Properties}). | ||
| 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 | |||
| 5159 | be an integer or float. Characters other than spaces are not affected | 5179 | be an integer or float. Characters other than spaces are not affected |
| 5160 | at all; in particular, this has no effect on tab characters. | 5180 | at all; in particular, this has no effect on tab characters. |
| 5161 | 5181 | ||
| 5182 | @item (min-width (@var{width})) | ||
| 5183 | This display specification adds padding to the end of the text if the | ||
| 5184 | text is shorter than @var{width}. The text is partitioned using the | ||
| 5185 | identity of the parameter, which is why the parameter is a list with | ||
| 5186 | one element. For instance: | ||
| 5187 | |||
| 5188 | @lisp | ||
| 5189 | (insert (propertize "foo" '(display (min-width (6.0))))) | ||
| 5190 | @end lisp | ||
| 5191 | |||
| 5192 | This will add padding after @samp{foo} bringing the total width up to | ||
| 5193 | the width of six normal characters. Note that the ``range'' is | ||
| 5194 | identified by the @code{(6.0)} list, compared with @code{eq}. The | ||
| 5195 | width 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}) |
| 5163 | This display specification makes the text taller or shorter. | 5199 | This display specification makes the text taller or shorter. |
| 5164 | Here are the possibilities for @var{height}: | 5200 | Here are the possibilities for @var{height}: |
| @@ -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'. | ||
| 672 | This is like 'get-text-property', but works on the 'display' text | ||
| 673 | property. | ||
| 674 | |||
| 675 | ** New 'min-width' 'display' property. | ||
| 676 | This 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. */ |
| 823 | enum { REDISPLAY_SOME = 2}; /* Arbitrary choice. */ | 823 | enum { REDISPLAY_SOME = 2}; /* Arbitrary choice. */ |
| 824 | 824 | ||
| 825 | static bool calc_pixel_width_or_height (double *, struct it *, Lisp_Object, | ||
| 826 | struct font *, bool, int *); | ||
| 827 | |||
| 825 | void | 828 | void |
| 826 | redisplay_other_windows (void) | 829 | redisplay_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 | |||
| 5148 | static Lisp_Object | ||
| 5149 | find_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 | |||
| 5196 | static 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 | |||
| 5205 | static void | ||
| 5206 | display_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 | |||
| 5271 | DEFUN ("get-display-property", Fget_display_property, | ||
| 5272 | Sget_display_property, 2, 4, 0, | ||
| 5273 | doc: /* Get the `display' property PROP at POSITION. | ||
| 5274 | If OBJECT, this should be a buffer or string where the property is | ||
| 5275 | fetched from. This defaults to the current buffer. | ||
| 5276 | |||
| 5277 | If PROPERTIES, use those properties instead of the properties at | ||
| 5278 | POSITION. */) | ||
| 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 |