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:
parent
8efee422e1
commit
a13b437c81
@ -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}:
|
||||
|
7
etc/NEWS
7
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
|
||||
|
||||
+++
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
172
src/xdisp.c
172
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");
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user