1
0
mirror of https://git.savannah.gnu.org/git/emacs.git synced 2025-01-09 15:50:21 +00:00

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.
This commit is contained in:
Lars Ingebrigtsen 2021-11-24 11:55:53 +01:00
parent 8efee422e1
commit a13b437c81
5 changed files with 237 additions and 6 deletions

View File

@ -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}:

View File

@ -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
+++

View File

@ -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;
};

View File

@ -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");

View File

@ -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