Support bidi reordering of text covered by display properties.

src/bidi.c (bidi_copy_it): Use offsetof instead of emulating it.
 (bidi_fetch_char, bidi_fetch_char_advance): New functions.
 (bidi_cache_search, bidi_cache_iterator_state)
 (bidi_paragraph_init, bidi_resolve_explicit, bidi_resolve_weak)
 (bidi_level_of_next_char, bidi_move_to_visually_next): Support
 character positions inside a run of characters covered by a
 display string.
 (bidi_paragraph_init, bidi_resolve_explicit_1)
 (bidi_level_of_next_char): Call bidi_fetch_char and
 bidi_fetch_char_advance instead of FETCH_CHAR and
 FETCH_CHAR_ADVANCE.
 (bidi_init_it): Initialize new members.
 (LRE_CHAR, RLE_CHAR, PDF_CHAR, LRO_CHAR, RLO_CHAR): Remove macro
 definitions.
 (bidi_explicit_dir_char): Lookup character type in bidi_type_table,
 instead of using explicit *_CHAR codes.
 (bidi_resolve_explicit, bidi_resolve_weak): Use
 FETCH_MULTIBYTE_CHAR instead of FETCH_CHAR, as reordering of
 bidirectional text is supported only in multibyte buffers.
 (bidi_init_it): Accept additional argument FRAME_WINDOW_P and use
 it to initialize the frame_window_p member of struct bidi_it.
 (bidi_cache_iterator_state, bidi_resolve_explicit_1)
 (bidi_resolve_explicit, bidi_resolve_weak)
 (bidi_level_of_next_char, bidi_move_to_visually_next): Abort if
 bidi_it->nchars is non-positive.
 (bidi_level_of_next_char): Don't try to lookup the cache for the
 next/previous character if nothing is cached there yet, or if we
 were just reseat()'ed to a new position.
 src/xdisp.c (set_cursor_from_row): Set start and stop points
 according to the row's direction when priming the loop that looks
 for the glyph on which to display cursor.
 (single_display_spec_intangible_p): Function deleted.
 (display_prop_intangible_p): Reimplement to call
 handle_display_spec instead of single_display_spec_intangible_p.
 Accept 3 additional arguments needed by handle_display_spec.  This
 fixes incorrect cursor motion across display property with complex
 values: lists, `(when COND...)' forms, etc.
 (single_display_spec_string_p): Support property values that are
 lists with the argument STRING its top-level element.
 (display_prop_string_p): Fix the condition for processing a
 property that is a list to be consistent with handle_display_spec.
 (handle_display_spec): New function, refactored from the
 last portion of handle_display_prop.
 (compute_display_string_pos): Accept additional argument
 FRAME_WINDOW_P.  Call handle_display_spec to determine whether the
 value of a `display' property is a "replacing spec".
 (handle_single_display_spec): Accept 2 additional arguments BUFPOS
 and FRAME_WINDOW_P.  If IT is NULL, don't set up the iterator from
 the display property, but just return a value indicating whether
 the display property will replace the characters it covers.
 (Fcurrent_bidi_paragraph_direction): Initialize the nchars and
 frame_window_p members of struct bidi_it.
 (compute_display_string_pos, compute_display_string_end): New
 functions.
 (push_it): Accept second argument POSITION, where pop_it should
 jump to continue iteration.
 (reseat_1): Initialize bidi_it.disp_pos.
 src/keyboard.c (adjust_point_for_property): Adjust the call to
 display_prop_intangible_p to its new signature.
 src/dispextern.h (struct bidi_it): New member frame_window_p.
 (bidi_init_it): Update prototypes.
 (display_prop_intangible_p): Update prototype.
 (compute_display_string_pos, compute_display_string_end): Declare
 prototypes.
 (struct bidi_it): New members nchars and disp_pos.  ch_len is now
 EMACS_INT.
This commit is contained in:
Eli Zaretskii 2011-06-04 10:41:44 +03:00
commit fec2107c58
5 changed files with 651 additions and 320 deletions

View file

@ -1,3 +1,77 @@
2011-06-03 Eli Zaretskii <eliz@gnu.org>
Support bidi reordering of text covered by display properties.
* bidi.c (bidi_copy_it): Use offsetof instead of emulating it.
(bidi_fetch_char, bidi_fetch_char_advance): New functions.
(bidi_cache_search, bidi_cache_iterator_state)
(bidi_paragraph_init, bidi_resolve_explicit, bidi_resolve_weak)
(bidi_level_of_next_char, bidi_move_to_visually_next): Support
character positions inside a run of characters covered by a
display string.
(bidi_paragraph_init, bidi_resolve_explicit_1)
(bidi_level_of_next_char): Call bidi_fetch_char and
bidi_fetch_char_advance instead of FETCH_CHAR and
FETCH_CHAR_ADVANCE.
(bidi_init_it): Initialize new members.
(LRE_CHAR, RLE_CHAR, PDF_CHAR, LRO_CHAR, RLO_CHAR): Remove macro
definitions.
(bidi_explicit_dir_char): Lookup character type in bidi_type_table,
instead of using explicit *_CHAR codes.
(bidi_resolve_explicit, bidi_resolve_weak): Use
FETCH_MULTIBYTE_CHAR instead of FETCH_CHAR, as reordering of
bidirectional text is supported only in multibyte buffers.
(bidi_init_it): Accept additional argument FRAME_WINDOW_P and use
it to initialize the frame_window_p member of struct bidi_it.
(bidi_cache_iterator_state, bidi_resolve_explicit_1)
(bidi_resolve_explicit, bidi_resolve_weak)
(bidi_level_of_next_char, bidi_move_to_visually_next): Abort if
bidi_it->nchars is non-positive.
(bidi_level_of_next_char): Don't try to lookup the cache for the
next/previous character if nothing is cached there yet, or if we
were just reseat()'ed to a new position.
* xdisp.c (set_cursor_from_row): Set start and stop points
according to the row's direction when priming the loop that looks
for the glyph on which to display cursor.
(single_display_spec_intangible_p): Function deleted.
(display_prop_intangible_p): Reimplement to call
handle_display_spec instead of single_display_spec_intangible_p.
Accept 3 additional arguments needed by handle_display_spec. This
fixes incorrect cursor motion across display property with complex
values: lists, `(when COND...)' forms, etc.
(single_display_spec_string_p): Support property values that are
lists with the argument STRING its top-level element.
(display_prop_string_p): Fix the condition for processing a
property that is a list to be consistent with handle_display_spec.
(handle_display_spec): New function, refactored from the
last portion of handle_display_prop.
(compute_display_string_pos): Accept additional argument
FRAME_WINDOW_P. Call handle_display_spec to determine whether the
value of a `display' property is a "replacing spec".
(handle_single_display_spec): Accept 2 additional arguments BUFPOS
and FRAME_WINDOW_P. If IT is NULL, don't set up the iterator from
the display property, but just return a value indicating whether
the display property will replace the characters it covers.
(Fcurrent_bidi_paragraph_direction): Initialize the nchars and
frame_window_p members of struct bidi_it.
(compute_display_string_pos, compute_display_string_end): New
functions.
(push_it): Accept second argument POSITION, where pop_it should
jump to continue iteration.
(reseat_1): Initialize bidi_it.disp_pos.
* keyboard.c (adjust_point_for_property): Adjust the call to
display_prop_intangible_p to its new signature.
* dispextern.h (struct bidi_it): New member frame_window_p.
(bidi_init_it): Update prototypes.
(display_prop_intangible_p): Update prototype.
(compute_display_string_pos, compute_display_string_end): Declare
prototypes.
(struct bidi_it): New members nchars and disp_pos. ch_len is now
EMACS_INT.
2011-06-02 Paul Eggert <eggert@cs.ucla.edu>
Malloc failure behavior now depends on size of allocation.

View file

@ -62,15 +62,8 @@ static int bidi_initialized = 0;
static Lisp_Object bidi_type_table, bidi_mirror_table;
/* FIXME: Remove these when bidi_explicit_dir_char uses a lookup table. */
#define LRM_CHAR 0x200E
#define RLM_CHAR 0x200F
#define LRE_CHAR 0x202A
#define RLE_CHAR 0x202B
#define PDF_CHAR 0x202C
#define LRO_CHAR 0x202D
#define RLO_CHAR 0x202E
#define BIDI_EOB -1
/* Local data structures. (Look in dispextern.h for the rest.) */
@ -258,7 +251,7 @@ bidi_copy_it (struct bidi_it *to, struct bidi_it *from)
int i;
/* Copy everything except the level stack and beyond. */
memcpy (to, from, ((size_t)&((struct bidi_it *)0)->level_stack[0]));
memcpy (to, from, offsetof (struct bidi_it, level_stack[0]));
/* Copy the active part of the level stack. */
to->level_stack[0] = from->level_stack[0]; /* level zero is always in use */
@ -319,10 +312,17 @@ bidi_cache_search (EMACS_INT charpos, int level, int dir)
if (bidi_cache_idx)
{
if (charpos < bidi_cache[bidi_cache_last_idx].charpos)
dir = -1;
else if (charpos > bidi_cache[bidi_cache_last_idx].charpos)
dir = 1;
if (dir)
{
dir = -1;
i_start = bidi_cache_last_idx - 1;
}
else if (charpos > (bidi_cache[bidi_cache_last_idx].charpos
+ bidi_cache[bidi_cache_last_idx].nchars - 1))
{
dir = 1;
i_start = bidi_cache_last_idx + 1;
}
else if (dir)
i_start = bidi_cache_last_idx;
else
{
@ -334,14 +334,16 @@ bidi_cache_search (EMACS_INT charpos, int level, int dir)
{
/* Linear search for now; FIXME! */
for (i = i_start; i >= 0; i--)
if (bidi_cache[i].charpos == charpos
if (bidi_cache[i].charpos <= charpos
&& charpos < bidi_cache[i].charpos + bidi_cache[i].nchars
&& (level == -1 || bidi_cache[i].resolved_level <= level))
return i;
}
else
{
for (i = i_start; i < bidi_cache_idx; i++)
if (bidi_cache[i].charpos == charpos
if (bidi_cache[i].charpos <= charpos
&& charpos < bidi_cache[i].charpos + bidi_cache[i].nchars
&& (level == -1 || bidi_cache[i].resolved_level <= level))
return i;
}
@ -426,12 +428,15 @@ bidi_cache_iterator_state (struct bidi_it *bidi_it, int resolved)
If we are outside the range of cached positions, the cache is
useless and must be reset. */
if (idx > 0 &&
(bidi_it->charpos > bidi_cache[idx - 1].charpos + 1
(bidi_it->charpos > (bidi_cache[idx - 1].charpos
+ bidi_cache[idx - 1].nchars)
|| bidi_it->charpos < bidi_cache[0].charpos))
{
bidi_cache_reset ();
idx = 0;
}
if (bidi_it->nchars <= 0)
abort ();
bidi_copy_it (&bidi_cache[idx], bidi_it);
if (!resolved)
bidi_cache[idx].resolved_level = -1;
@ -548,6 +553,7 @@ bidi_set_sor_type (struct bidi_it *bidi_it, int level_before, int level_after)
bidi_it->ignore_bn_limit = 0; /* meaning it's unknown */
}
/* Perform initializations for reordering a new line of bidi text. */
static void
bidi_line_init (struct bidi_it *bidi_it)
{
@ -565,6 +571,65 @@ bidi_line_init (struct bidi_it *bidi_it)
bidi_cache_reset ();
}
/* Fetch and return the character at BYTEPOS/CHARPOS. If that
character is covered by a display string, treat the entire run of
covered characters as a single character u+FFFC, and return their
combined length in CH_LEN and NCHARS. DISP_POS specifies the
character position of the next display string, or -1 if not yet
computed. When the next character is at or beyond that position,
the function updates DISP_POS with the position of the next display
string. */
static inline int
bidi_fetch_char (EMACS_INT bytepos, EMACS_INT charpos, EMACS_INT *disp_pos,
int frame_window_p, EMACS_INT *ch_len, EMACS_INT *nchars)
{
int ch;
/* FIXME: Support strings in addition to buffers. */
/* If we got past the last known position of display string, compute
the position of the next one. That position could be at BYTEPOS. */
if (charpos < ZV && charpos > *disp_pos)
*disp_pos = compute_display_string_pos (charpos, frame_window_p);
/* Fetch the character at BYTEPOS. */
if (bytepos >= ZV_BYTE)
{
ch = BIDI_EOB;
*ch_len = 1;
*nchars = 1;
*disp_pos = ZV;
}
else if (charpos >= *disp_pos)
{
EMACS_INT disp_end_pos;
/* We don't expect to find ourselves in the middle of a display
property. Hopefully, it will never be needed. */
if (charpos > *disp_pos)
abort ();
/* Return the Unicode Object Replacement Character to represent
the entire run of characters covered by the display
string. */
ch = 0xFFFC;
disp_end_pos = compute_display_string_end (*disp_pos);
*nchars = disp_end_pos - *disp_pos;
*ch_len = CHAR_TO_BYTE (disp_end_pos) - bytepos;
}
else
{
ch = FETCH_MULTIBYTE_CHAR (bytepos);
*nchars = 1;
*ch_len = CHAR_BYTES (ch);
}
/* If we just entered a run of characters covered by a display
string, compute the position of the next display string. */
if (charpos + *nchars <= ZV && charpos + *nchars > *disp_pos)
*disp_pos = compute_display_string_pos (charpos + *nchars, frame_window_p);
return ch;
}
/* Find the beginning of this paragraph by looking back in the buffer.
Value is the byte position of the paragraph's beginning. */
static EMACS_INT
@ -576,6 +641,10 @@ bidi_find_paragraph_start (EMACS_INT pos, EMACS_INT pos_byte)
while (pos_byte > BEGV_BYTE
&& fast_looking_at (re, pos, pos_byte, limit, limit_byte, Qnil) < 0)
{
/* FIXME: What if the paragraph beginning is covered by a
display string? And what if a display string covering some
of the text over which we scan back includes
paragraph_start_re? */
pos = find_next_newline_no_quit (pos - 1, -1);
pos_byte = CHAR_TO_BYTE (pos);
}
@ -587,7 +656,7 @@ bidi_find_paragraph_start (EMACS_INT pos, EMACS_INT pos_byte)
R2L, just use that. Otherwise, determine the paragraph direction
from the first strong directional character of the paragraph.
NO_DEFAULT_P non-nil means don't default to L2R if the paragraph
NO_DEFAULT_P non-zero means don't default to L2R if the paragraph
has no strong directional characters and both DIR and
bidi_it->paragraph_dir are NEUTRAL_DIR. In that case, search back
in the buffer until a paragraph is found with a strong character,
@ -622,8 +691,9 @@ bidi_paragraph_init (bidi_dir_t dir, struct bidi_it *bidi_it, int no_default_p)
}
else if (dir == NEUTRAL_DIR) /* P2 */
{
int ch, ch_len;
EMACS_INT pos;
int ch;
EMACS_INT ch_len, nchars;
EMACS_INT pos, disp_pos = -1;
bidi_type_t type;
if (!bidi_initialized)
@ -658,12 +728,12 @@ bidi_paragraph_init (bidi_dir_t dir, struct bidi_it *bidi_it, int no_default_p)
is non-zero. */
do {
bytepos = pstartbyte;
ch = FETCH_CHAR (bytepos);
ch_len = CHAR_BYTES (ch);
pos = BYTE_TO_CHAR (bytepos);
ch = bidi_fetch_char (bytepos, pos, &disp_pos, bidi_it->frame_window_p,
&ch_len, &nchars);
type = bidi_get_type (ch, NEUTRAL_DIR);
for (pos++, bytepos += ch_len;
for (pos += nchars, bytepos += ch_len;
/* NOTE: UAX#9 says to search only for L, AL, or R types
of characters, and ignore RLE, RLO, LRE, and LRO.
However, I'm not sure it makes sense to omit those 4;
@ -683,7 +753,11 @@ bidi_paragraph_init (bidi_dir_t dir, struct bidi_it *bidi_it, int no_default_p)
type = NEUTRAL_B;
break;
}
FETCH_CHAR_ADVANCE (ch, pos, bytepos);
/* Fetch next character and advance to get past it. */
ch = bidi_fetch_char (bytepos, pos, &disp_pos,
bidi_it->frame_window_p, &ch_len, &nchars);
pos += nchars;
bytepos += ch_len;
}
if (type == STRONG_R || type == STRONG_AL) /* P3 */
bidi_it->paragraph_dir = R2L;
@ -702,6 +776,9 @@ bidi_paragraph_init (bidi_dir_t dir, struct bidi_it *bidi_it, int no_default_p)
/* Find the beginning of the previous paragraph, if any. */
while (pbyte > BEGV_BYTE && prevpbyte >= pstartbyte)
{
/* FXIME: What if p is covered by a display
string? See also a FIXME inside
bidi_find_paragraph_start. */
p--;
pbyte = CHAR_TO_BYTE (p);
prevpbyte = bidi_find_paragraph_start (p, pbyte);
@ -738,14 +815,17 @@ bidi_set_paragraph_end (struct bidi_it *bidi_it)
bidi_it->resolved_level = bidi_it->level_stack[0].level;
}
/* Initialize the bidi iterator from buffer position CHARPOS. */
/* Initialize the bidi iterator from buffer/string position CHARPOS. */
void
bidi_init_it (EMACS_INT charpos, EMACS_INT bytepos, struct bidi_it *bidi_it)
bidi_init_it (EMACS_INT charpos, EMACS_INT bytepos, int frame_window_p,
struct bidi_it *bidi_it)
{
if (! bidi_initialized)
bidi_initialize ();
bidi_it->charpos = charpos;
bidi_it->bytepos = bytepos;
bidi_it->frame_window_p = frame_window_p;
bidi_it->nchars = -1; /* to be computed in bidi_resolve_explicit_1 */
bidi_it->first_elt = 1;
bidi_set_paragraph_end (bidi_it);
bidi_it->new_paragraph = 1;
@ -767,6 +847,7 @@ bidi_init_it (EMACS_INT charpos, EMACS_INT bytepos, struct bidi_it *bidi_it)
bidi_it->prev_for_neutral.type_after_w1 =
bidi_it->prev_for_neutral.orig_type = UNKNOWN_BT;
bidi_it->sor = L2R; /* FIXME: should it be user-selectable? */
bidi_it->disp_pos = -1; /* invalid/unknown */
bidi_cache_shrink ();
}
@ -829,12 +910,16 @@ bidi_resolve_neutral_1 (bidi_type_t prev_type, bidi_type_t next_type, int lev)
}
static inline int
bidi_explicit_dir_char (int c)
bidi_explicit_dir_char (int ch)
{
/* FIXME: this should be replaced with a lookup table with suitable
bits set, like standard C ctype macros do. */
return (c == LRE_CHAR || c == LRO_CHAR
|| c == RLE_CHAR || c == RLO_CHAR || c == PDF_CHAR);
bidi_type_t ch_type;
if (!bidi_initialized)
abort ();
ch_type = (bidi_type_t) XINT (CHAR_TABLE_REF (bidi_type_table, ch));
return (ch_type == LRE || ch_type == LRO
|| ch_type == RLE || ch_type == RLO
|| ch_type == PDF);
}
/* A helper function for bidi_resolve_explicit. It advances to the
@ -850,7 +935,10 @@ bidi_resolve_explicit_1 (struct bidi_it *bidi_it)
int new_level;
bidi_dir_t override;
if (bidi_it->bytepos < BEGV_BYTE /* after reseat to BEGV? */
/* If reseat()'ed, don't advance, so as to start iteration from the
position where we were reseated. bidi_it->bytepos can be less
than BEGV_BYTE after reseat to BEGV. */
if (bidi_it->bytepos < BEGV_BYTE
|| bidi_it->first_elt)
{
bidi_it->first_elt = 0;
@ -860,7 +948,11 @@ bidi_resolve_explicit_1 (struct bidi_it *bidi_it)
}
else if (bidi_it->bytepos < ZV_BYTE) /* don't move at ZV */
{
bidi_it->charpos++;
/* Advance to the next character, skipping characters covered by
display strings (nchars > 1). */
if (bidi_it->nchars <= 0)
abort ();
bidi_it->charpos += bidi_it->nchars;
if (bidi_it->ch_len == 0)
abort ();
bidi_it->bytepos += bidi_it->ch_len;
@ -870,17 +962,21 @@ bidi_resolve_explicit_1 (struct bidi_it *bidi_it)
override = bidi_it->level_stack[bidi_it->stack_idx].override;
new_level = current_level;
/* in case it is a unibyte character (not yet implemented) */
/* _fetch_multibyte_char_len = 1; */
if (bidi_it->bytepos >= ZV_BYTE)
{
curchar = BIDI_EOB;
bidi_it->ch_len = 1;
bidi_it->nchars = 1;
bidi_it->disp_pos = ZV;
}
else
{
curchar = FETCH_CHAR (bidi_it->bytepos);
bidi_it->ch_len = CHAR_BYTES (curchar);
/* Fetch the character at BYTEPOS. If it is covered by a
display string, treat the entire run of covered characters as
a single character u+FFFC. */
curchar = bidi_fetch_char (bidi_it->bytepos, bidi_it->charpos,
&bidi_it->disp_pos, bidi_it->frame_window_p,
&bidi_it->ch_len, &bidi_it->nchars);
}
bidi_it->ch = curchar;
@ -1006,10 +1102,10 @@ bidi_resolve_explicit_1 (struct bidi_it *bidi_it)
}
/* Given an iterator state in BIDI_IT, advance one character position
in the buffer to the next character (in the logical order), resolve
any explicit embeddings and directional overrides, and return the
embedding level of the character after resolving explicit
directives and ignoring empty embeddings. */
in the buffer/string to the next character (in the logical order),
resolve any explicit embeddings and directional overrides, and
return the embedding level of the character after resolving
explicit directives and ignoring empty embeddings. */
static int
bidi_resolve_explicit (struct bidi_it *bidi_it)
{
@ -1020,8 +1116,8 @@ bidi_resolve_explicit (struct bidi_it *bidi_it)
&& bidi_it->type == WEAK_BN
&& bidi_it->ignore_bn_limit == 0 /* only if not already known */
&& bidi_it->bytepos < ZV_BYTE /* not already at EOB */
&& bidi_explicit_dir_char (FETCH_CHAR (bidi_it->bytepos
+ bidi_it->ch_len)))
&& bidi_explicit_dir_char (FETCH_MULTIBYTE_CHAR (bidi_it->bytepos
+ bidi_it->ch_len)))
{
/* Avoid pushing and popping embedding levels if the level run
is empty, as this breaks level runs where it shouldn't.
@ -1033,14 +1129,18 @@ bidi_resolve_explicit (struct bidi_it *bidi_it)
bidi_copy_it (&saved_it, bidi_it);
while (bidi_explicit_dir_char (FETCH_CHAR (bidi_it->bytepos
+ bidi_it->ch_len)))
while (bidi_explicit_dir_char (FETCH_MULTIBYTE_CHAR (bidi_it->bytepos
+ bidi_it->ch_len)))
{
/* This advances to the next character, skipping any
characters covered by display strings. */
level = bidi_resolve_explicit_1 (bidi_it);
}
if (bidi_it->nchars <= 0)
abort ();
if (level == prev_level) /* empty embedding */
saved_it.ignore_bn_limit = bidi_it->charpos + 1;
saved_it.ignore_bn_limit = bidi_it->charpos + bidi_it->nchars;
else /* this embedding is non-empty */
saved_it.ignore_bn_limit = -1;
@ -1076,8 +1176,8 @@ bidi_resolve_explicit (struct bidi_it *bidi_it)
return new_level;
}
/* Advance in the buffer, resolve weak types and return the type of
the next character after weak type resolution. */
/* Advance in the buffer/string, resolve weak types and return the
type of the next character after weak type resolution. */
static bidi_type_t
bidi_resolve_weak (struct bidi_it *bidi_it)
{
@ -1156,7 +1256,8 @@ bidi_resolve_weak (struct bidi_it *bidi_it)
{
next_char =
bidi_it->bytepos + bidi_it->ch_len >= ZV_BYTE
? BIDI_EOB : FETCH_CHAR (bidi_it->bytepos + bidi_it->ch_len);
? BIDI_EOB : FETCH_MULTIBYTE_CHAR (bidi_it->bytepos
+ bidi_it->ch_len);
type_of_next = bidi_get_type (next_char, override);
if (type_of_next == WEAK_BN
@ -1204,11 +1305,14 @@ bidi_resolve_weak (struct bidi_it *bidi_it)
type = WEAK_EN;
else /* W5: ET/BN with EN after it. */
{
EMACS_INT en_pos = bidi_it->charpos + 1;
EMACS_INT en_pos = bidi_it->charpos + bidi_it->nchars;
if (bidi_it->nchars <= 0)
abort ();
next_char =
bidi_it->bytepos + bidi_it->ch_len >= ZV_BYTE
? BIDI_EOB : FETCH_CHAR (bidi_it->bytepos + bidi_it->ch_len);
? BIDI_EOB : FETCH_MULTIBYTE_CHAR (bidi_it->bytepos
+ bidi_it->ch_len);
type_of_next = bidi_get_type (next_char, override);
if (type_of_next == WEAK_ET
@ -1299,8 +1403,8 @@ bidi_resolve_neutral (struct bidi_it *bidi_it)
/* Arrrgh!! The UAX#9 algorithm is too deeply entrenched in
the assumption of batch-style processing; see clauses W4,
W5, and especially N1, which require to look far forward
(as well as back) in the buffer. May the fleas of a
thousand camels infest the armpits of those who design
(as well as back) in the buffer/string. May the fleas of
a thousand camels infest the armpits of those who design
supposedly general-purpose algorithms by looking at their
own implementations, and fail to consider other possible
implementations! */
@ -1391,8 +1495,9 @@ bidi_resolve_neutral (struct bidi_it *bidi_it)
}
/* Given an iterator state in BIDI_IT, advance one character position
in the buffer to the next character (in the logical order), resolve
the bidi type of that next character, and return that type. */
in the buffer/string to the next character (in the logical order),
resolve the bidi type of that next character, and return that
type. */
static bidi_type_t
bidi_type_of_next_char (struct bidi_it *bidi_it)
{
@ -1416,15 +1521,16 @@ bidi_type_of_next_char (struct bidi_it *bidi_it)
}
/* Given an iterator state BIDI_IT, advance one character position in
the buffer to the next character (in the logical order), resolve
the embedding and implicit levels of that next character, and
return the resulting level. */
the buffer/string to the next character (in the current scan
direction), resolve the embedding and implicit levels of that next
character, and return the resulting level. */
static int
bidi_level_of_next_char (struct bidi_it *bidi_it)
{
bidi_type_t type;
int level, prev_level = -1;
struct bidi_saved_info next_for_neutral;
EMACS_INT next_char_pos;
if (bidi_it->scan_dir == 1)
{
@ -1466,8 +1572,23 @@ bidi_level_of_next_char (struct bidi_it *bidi_it)
}
next_for_neutral = bidi_it->next_for_neutral;
/* Perhaps it is already cached. */
type = bidi_cache_find (bidi_it->charpos + bidi_it->scan_dir, -1, bidi_it);
/* Perhaps the character we want is already cached. If it is, the
call to bidi_cache_find below will return a type other than
UNKNOWN_BT. */
if (bidi_cache_idx && !bidi_it->first_elt)
{
if (bidi_it->scan_dir > 0)
{
if (bidi_it->nchars <= 0)
abort ();
next_char_pos = bidi_it->charpos + bidi_it->nchars;
}
else
next_char_pos = bidi_it->charpos - 1;
type = bidi_cache_find (next_char_pos, -1, bidi_it);
}
else
type = UNKNOWN_BT;
if (type != UNKNOWN_BT)
{
/* Don't lose the information for resolving neutrals! The
@ -1529,14 +1650,16 @@ bidi_level_of_next_char (struct bidi_it *bidi_it)
int clen = bidi_it->ch_len;
EMACS_INT bpos = bidi_it->bytepos;
EMACS_INT cpos = bidi_it->charpos;
EMACS_INT disp_pos = bidi_it->disp_pos;
EMACS_INT nc = bidi_it->nchars;
bidi_type_t chtype;
int fwp = bidi_it->frame_window_p;
if (bidi_it->nchars <= 0)
abort ();
do {
/*_fetch_multibyte_char_len = 1;*/
ch = bpos + clen >= ZV_BYTE ? BIDI_EOB : FETCH_CHAR (bpos + clen);
bpos += clen;
cpos++;
clen = (ch == BIDI_EOB ? 1 : CHAR_BYTES (ch));
ch = bidi_fetch_char (bpos += clen, cpos += nc, &disp_pos, fwp,
&clen, &nc);
if (ch == '\n' || ch == BIDI_EOB /* || ch == LINESEP_CHAR */)
chtype = NEUTRAL_B;
else
@ -1615,8 +1738,8 @@ bidi_level_of_next_char (struct bidi_it *bidi_it)
If this level's other edge is cached, we simply jump to it, filling
the iterator structure with the iterator state on the other edge.
Otherwise, we walk the buffer until we come back to the same level
as LEVEL.
Otherwise, we walk the buffer or string until we come back to the
same level as LEVEL.
Note: we are not talking here about a ``level run'' in the UAX#9
sense of the term, but rather about a ``level'' which includes
@ -1680,6 +1803,7 @@ bidi_move_to_visually_next (struct bidi_it *bidi_it)
sentinel.bytepos--;
sentinel.ch = '\n'; /* doesn't matter, but why not? */
sentinel.ch_len = 1;
sentinel.nchars = 1;
}
bidi_cache_iterator_state (&sentinel, 1);
}
@ -1750,14 +1874,17 @@ bidi_move_to_visually_next (struct bidi_it *bidi_it)
&& bidi_it->bytepos < ZV_BYTE)
{
EMACS_INT sep_len =
bidi_at_paragraph_end (bidi_it->charpos + 1,
bidi_at_paragraph_end (bidi_it->charpos + bidi_it->nchars,
bidi_it->bytepos + bidi_it->ch_len);
if (bidi_it->nchars <= 0)
abort ();
if (sep_len >= 0)
{
bidi_it->new_paragraph = 1;
/* Record the buffer position of the last character of the
paragraph separator. */
bidi_it->separator_limit = bidi_it->charpos + 1 + sep_len;
bidi_it->separator_limit =
bidi_it->charpos + bidi_it->nchars + sep_len;
}
}
@ -1767,7 +1894,8 @@ bidi_move_to_visually_next (struct bidi_it *bidi_it)
last cached position, the cache's job is done and we can
discard it. */
if (bidi_it->resolved_level == bidi_it->level_stack[0].level
&& bidi_it->charpos > bidi_cache[bidi_cache_idx - 1].charpos)
&& bidi_it->charpos > (bidi_cache[bidi_cache_idx - 1].charpos
+ bidi_cache[bidi_cache_idx - 1].nchars - 1))
bidi_cache_reset ();
/* But as long as we are caching during forward scan, we must
cache each state, or else the cache integrity will be

View file

@ -1816,12 +1816,16 @@ struct bidi_stack {
bidi_dir_t override;
};
/* Data type for iterating over bidi text. */
/* Data type for reordering bidirectional text. */
struct bidi_it {
EMACS_INT bytepos; /* iterator's position in buffer */
EMACS_INT charpos;
int ch; /* character itself */
int ch_len; /* length of its multibyte sequence */
int ch; /* character at that position, or u+FFFC
("object replacement character") for a run
of characters covered by a display string */
EMACS_INT nchars; /* its "length", usually 1; it's > 1 for a run
of characters covered by a display string */
EMACS_INT ch_len; /* its length in bytes */
bidi_type_t type; /* bidi type of this character, after
resolving weak and neutral types */
bidi_type_t type_after_w1; /* original type, after overrides and W1 */
@ -1847,7 +1851,9 @@ struct bidi_it {
int first_elt; /* if non-zero, examine current char first */
bidi_dir_t paragraph_dir; /* current paragraph direction */
int new_paragraph; /* if non-zero, we expect a new paragraph */
int frame_window_p; /* non-zero if displaying on a GUI frame */
EMACS_INT separator_limit; /* where paragraph separator should end */
EMACS_INT disp_pos; /* position of display string after ch */
};
/* Value is non-zero when the bidi iterator is at base paragraph
@ -2944,7 +2950,7 @@ enum tool_bar_item_image
/* Defined in bidi.c */
extern void bidi_init_it (EMACS_INT, EMACS_INT, struct bidi_it *);
extern void bidi_init_it (EMACS_INT, EMACS_INT, int, struct bidi_it *);
extern void bidi_move_to_visually_next (struct bidi_it *);
extern void bidi_paragraph_init (bidi_dir_t, struct bidi_it *, int);
extern int bidi_mirror_char (int);
@ -2955,7 +2961,7 @@ struct glyph_row *row_containing_pos (struct window *, EMACS_INT,
struct glyph_row *,
struct glyph_row *, int);
int line_bottom_y (struct it *);
int display_prop_intangible_p (Lisp_Object);
int display_prop_intangible_p (Lisp_Object, Lisp_Object, EMACS_INT, EMACS_INT);
void resize_echo_area_exactly (void);
int resize_mini_window (struct window *, int);
#if defined USE_TOOLKIT_SCROLL_BARS && !defined USE_GTK
@ -3005,6 +3011,8 @@ extern void reseat_at_previous_visible_line_start (struct it *);
extern Lisp_Object lookup_glyphless_char_display (int, struct it *);
extern int calc_pixel_width_or_height (double *, struct it *, Lisp_Object,
struct font *, int, int *);
extern EMACS_INT compute_display_string_pos (EMACS_INT, int);
extern EMACS_INT compute_display_string_end (EMACS_INT);
#ifdef HAVE_WINDOW_SYSTEM

View file

@ -1729,7 +1729,7 @@ adjust_point_for_property (EMACS_INT last_pt, int modified)
&& PT > BEGV && PT < ZV
&& !NILP (val = get_char_property_and_overlay
(make_number (PT), Qdisplay, Qnil, &overlay))
&& display_prop_intangible_p (val)
&& display_prop_intangible_p (val, overlay, PT, PT_BYTE)
&& (!OVERLAYP (overlay)
? get_property_and_range (PT, Qdisplay, &val, &beg, &end, Qnil)
: (beg = OVERLAY_POSITION (OVERLAY_START (overlay)),

View file

@ -812,7 +812,7 @@ static int try_scrolling (Lisp_Object, int, EMACS_INT, EMACS_INT, int, int);
static int try_cursor_movement (Lisp_Object, struct text_pos, int *);
static int trailing_whitespace_p (EMACS_INT);
static unsigned long int message_log_check_duplicate (EMACS_INT, EMACS_INT);
static void push_it (struct it *);
static void push_it (struct it *, struct text_pos *);
static void pop_it (struct it *);
static void sync_frame_with_window_matrix_rows (struct window *);
static void select_frame_for_redisplay (Lisp_Object);
@ -884,9 +884,11 @@ static void compute_string_pos (struct text_pos *, struct text_pos,
Lisp_Object);
static int face_before_or_after_it_pos (struct it *, int);
static EMACS_INT next_overlay_change (EMACS_INT);
static int handle_display_spec (struct it *, Lisp_Object, Lisp_Object,
Lisp_Object, struct text_pos *, EMACS_INT, int);
static int handle_single_display_spec (struct it *, Lisp_Object,
Lisp_Object, Lisp_Object,
struct text_pos *, int);
struct text_pos *, EMACS_INT, int, int);
static int underlying_face_id (struct it *);
static int in_ellipses_for_invisible_text_p (struct display_pos *,
struct window *);
@ -2564,7 +2566,7 @@ init_iterator (struct it *it, struct window *w,
it->paragraph_embedding = R2L;
else
it->paragraph_embedding = NEUTRAL_DIR;
bidi_init_it (charpos, bytepos, &it->bidi_it);
bidi_init_it (charpos, bytepos, FRAME_WINDOW_P (it->f), &it->bidi_it);
}
/* If a buffer position was specified, set the iterator there,
@ -3085,6 +3087,82 @@ next_overlay_change (EMACS_INT pos)
return endpos;
}
/* Return the character position of a display string at or after CHARPOS.
If no display string exists at or after CHARPOS, return ZV. A
display string is either an overlay with `display' property whose
value is a string, or a `display' text property whose value is a
string. FRAME_WINDOW_P is non-zero when we are displaying a window
on a GUI frame. */
EMACS_INT
compute_display_string_pos (EMACS_INT charpos, int frame_window_p)
{
/* FIXME: Support display properties on strings (object = Qnil means
current buffer). */
Lisp_Object object = Qnil;
Lisp_Object pos, spec;
struct text_pos position;
EMACS_INT bufpos;
if (charpos >= ZV)
return ZV;
/* If the character at CHARPOS is where the display string begins,
return CHARPOS. */
pos = make_number (charpos);
CHARPOS (position) = charpos;
BYTEPOS (position) = CHAR_TO_BYTE (charpos);
bufpos = charpos; /* FIXME! support strings as well */
if (!NILP (spec = Fget_char_property (pos, Qdisplay, object))
&& (charpos <= BEGV
|| !EQ (Fget_char_property (make_number (charpos - 1), Qdisplay,
object),
spec))
&& handle_display_spec (NULL, spec, object, Qnil, &position, bufpos,
frame_window_p))
return charpos;
/* Look forward for the first character with a `display' property
that will replace the underlying text when displayed. */
do {
pos = Fnext_single_char_property_change (pos, Qdisplay, object, Qnil);
CHARPOS (position) = XFASTINT (pos);
BYTEPOS (position) = CHAR_TO_BYTE (CHARPOS (position));
if (CHARPOS (position) >= ZV)
break;
spec = Fget_char_property (pos, Qdisplay, object);
bufpos = CHARPOS (position); /* FIXME! support strings as well */
} while (NILP (spec)
|| !handle_display_spec (NULL, spec, object, Qnil, &position, bufpos,
frame_window_p));
return CHARPOS (position);
}
/* Return the character position of the end of the display string that
started at CHARPOS. A display string is either an overlay with
`display' property whose value is a string or a `display' text
property whose value is a string. */
EMACS_INT
compute_display_string_end (EMACS_INT charpos)
{
/* FIXME: Support display properties on strings (object = Qnil means
current buffer). */
Lisp_Object object = Qnil;
Lisp_Object pos = make_number (charpos);
if (charpos >= ZV)
return ZV;
if (NILP (Fget_char_property (pos, Qdisplay, object)))
abort ();
/* Look forward for the first character where the `display' property
changes. */
pos = Fnext_single_char_property_change (pos, Qdisplay, object, Qnil);
return XFASTINT (pos);
}
/***********************************************************************
@ -3743,8 +3821,9 @@ setup_for_ellipsis (struct it *it, int len)
static enum prop_handled
handle_display_prop (struct it *it)
{
Lisp_Object prop, object, overlay;
Lisp_Object propval, object, overlay;
struct text_pos *position;
EMACS_INT bufpos;
/* Nonzero if some property replaces the display of the text itself. */
int display_replaced_p = 0;
@ -3752,11 +3831,13 @@ handle_display_prop (struct it *it)
{
object = it->string;
position = &it->current.string_pos;
bufpos = CHARPOS (it->current.pos);
}
else
{
XSETWINDOW (object, it->w);
position = &it->current.pos;
bufpos = CHARPOS (*position);
}
/* Reset those iterator values set from display property values. */
@ -3771,9 +3852,9 @@ handle_display_prop (struct it *it)
if (!it->string_from_display_prop_p)
it->area = TEXT_AREA;
prop = get_char_property_and_overlay (make_number (position->charpos),
Qdisplay, object, &overlay);
if (NILP (prop))
propval = get_char_property_and_overlay (make_number (position->charpos),
Qdisplay, object, &overlay);
if (NILP (propval))
return HANDLED_NORMALLY;
/* Now OVERLAY is the overlay that gave us this property, or nil
if it was a text property. */
@ -3781,59 +3862,88 @@ handle_display_prop (struct it *it)
if (!STRINGP (it->string))
object = it->w->buffer;
if (CONSP (prop)
/* Simple properties. */
&& !EQ (XCAR (prop), Qimage)
&& !EQ (XCAR (prop), Qspace)
&& !EQ (XCAR (prop), Qwhen)
&& !EQ (XCAR (prop), Qslice)
&& !EQ (XCAR (prop), Qspace_width)
&& !EQ (XCAR (prop), Qheight)
&& !EQ (XCAR (prop), Qraise)
display_replaced_p = handle_display_spec (it, propval, object, overlay,
position, bufpos,
FRAME_WINDOW_P (it->f));
return display_replaced_p ? HANDLED_RETURN : HANDLED_NORMALLY;
}
/* Subroutine of handle_display_prop. Returns non-zero if the display
specification in SPEC is a replacing specification, i.e. it would
replace the text covered by `display' property with something else,
such as an image or a display string.
See handle_single_display_spec for documentation of arguments.
frame_window_p is non-zero if the window being redisplayed is on a
GUI frame; this argument is used only if IT is NULL, see below.
IT can be NULL, if this is called by the bidi reordering code
through compute_display_string_pos, which see. In that case, this
function only examines SPEC, but does not otherwise "handle" it, in
the sense that it doesn't set up members of IT from the display
spec. */
static int
handle_display_spec (struct it *it, Lisp_Object spec, Lisp_Object object,
Lisp_Object overlay, struct text_pos *position,
EMACS_INT bufpos, int frame_window_p)
{
int replacing_p = 0;
if (CONSP (spec)
/* Simple specerties. */
&& !EQ (XCAR (spec), Qimage)
&& !EQ (XCAR (spec), Qspace)
&& !EQ (XCAR (spec), Qwhen)
&& !EQ (XCAR (spec), Qslice)
&& !EQ (XCAR (spec), Qspace_width)
&& !EQ (XCAR (spec), Qheight)
&& !EQ (XCAR (spec), Qraise)
/* Marginal area specifications. */
&& !(CONSP (XCAR (prop)) && EQ (XCAR (XCAR (prop)), Qmargin))
&& !EQ (XCAR (prop), Qleft_fringe)
&& !EQ (XCAR (prop), Qright_fringe)
&& !NILP (XCAR (prop)))
&& !(CONSP (XCAR (spec)) && EQ (XCAR (XCAR (spec)), Qmargin))
&& !EQ (XCAR (spec), Qleft_fringe)
&& !EQ (XCAR (spec), Qright_fringe)
&& !NILP (XCAR (spec)))
{
for (; CONSP (prop); prop = XCDR (prop))
for (; CONSP (spec); spec = XCDR (spec))
{
if (handle_single_display_spec (it, XCAR (prop), object, overlay,
position, display_replaced_p))
if (handle_single_display_spec (it, XCAR (spec), object, overlay,
position, bufpos, replacing_p,
frame_window_p))
{
display_replaced_p = 1;
replacing_p = 1;
/* If some text in a string is replaced, `position' no
longer points to the position of `object'. */
if (STRINGP (object))
if (!it || STRINGP (object))
break;
}
}
}
else if (VECTORP (prop))
else if (VECTORP (spec))
{
int i;
for (i = 0; i < ASIZE (prop); ++i)
if (handle_single_display_spec (it, AREF (prop, i), object, overlay,
position, display_replaced_p))
for (i = 0; i < ASIZE (spec); ++i)
if (handle_single_display_spec (it, AREF (spec, i), object, overlay,
position, bufpos, replacing_p,
frame_window_p))
{
display_replaced_p = 1;
replacing_p = 1;
/* If some text in a string is replaced, `position' no
longer points to the position of `object'. */
if (STRINGP (object))
if (!it || STRINGP (object))
break;
}
}
else
{
if (handle_single_display_spec (it, prop, object, overlay,
position, 0))
display_replaced_p = 1;
if (handle_single_display_spec (it, spec, object, overlay,
position, bufpos, 0, frame_window_p))
replacing_p = 1;
}
return display_replaced_p ? HANDLED_RETURN : HANDLED_NORMALLY;
return replacing_p;
}
/* Value is the position of the end of the `display' property starting
at START_POS in OBJECT. */
@ -3857,10 +3967,12 @@ display_prop_end (struct it *it, Lisp_Object object, struct text_pos start_pos)
/* Set up IT from a single `display' property specification SPEC. OBJECT
is the object in which the `display' property was found. *POSITION
is the position at which it was found. DISPLAY_REPLACED_P non-zero
means that we previously saw a display specification which already
replaced text display with something else, for example an image;
we ignore such properties after the first one has been processed.
is the position in OBJECT at which the `display' property was found.
BUFPOS is the buffer position of OBJECT (different from POSITION if
OBJECT is not a buffer). DISPLAY_REPLACED_P non-zero means that we
previously saw a display specification which already replaced text
display with something else, for example an image; we ignore such
properties after the first one has been processed.
OVERLAY is the overlay this `display' property came from,
or nil if it was a text property.
@ -3869,17 +3981,22 @@ display_prop_end (struct it *it, Lisp_Object object, struct text_pos start_pos)
cases too, set *POSITION to the position where the `display'
property ends.
If IT is NULL, only examine the property specification in SPEC, but
don't set up IT. In that case, FRAME_WINDOW_P non-zero means SPEC
is intended to be displayed in a window on a GUI frame.
Value is non-zero if something was found which replaces the display
of buffer or string text. */
static int
handle_single_display_spec (struct it *it, Lisp_Object spec, Lisp_Object object,
Lisp_Object overlay, struct text_pos *position,
int display_replaced_p)
EMACS_INT bufpos, int display_replaced_p,
int frame_window_p)
{
Lisp_Object form;
Lisp_Object location, value;
struct text_pos start_pos, save_pos;
struct text_pos start_pos = *position;
int valid_p;
/* If SPEC is a list of the form `(when FORM . VALUE)', evaluate FORM.
@ -3903,11 +4020,12 @@ handle_single_display_spec (struct it *it, Lisp_Object spec, Lisp_Object object,
buffer or string. Bind `position' to the position in the
object where the property was found, and `buffer-position'
to the current position in the buffer. */
if (NILP (object))
XSETBUFFER (object, current_buffer);
specbind (Qobject, object);
specbind (Qposition, make_number (CHARPOS (*position)));
specbind (Qbuffer_position,
make_number (STRINGP (object)
? IT_CHARPOS (*it) : CHARPOS (*position)));
specbind (Qbuffer_position, make_number (bufpos));
GCPRO1 (form);
form = safe_eval (form);
UNGCPRO;
@ -3922,63 +4040,66 @@ handle_single_display_spec (struct it *it, Lisp_Object spec, Lisp_Object object,
&& EQ (XCAR (spec), Qheight)
&& CONSP (XCDR (spec)))
{
if (!FRAME_WINDOW_P (it->f))
return 0;
it->font_height = XCAR (XCDR (spec));
if (!NILP (it->font_height))
if (it)
{
struct face *face = FACE_FROM_ID (it->f, it->face_id);
int new_height = -1;
if (!FRAME_WINDOW_P (it->f))
return 0;
if (CONSP (it->font_height)
&& (EQ (XCAR (it->font_height), Qplus)
|| EQ (XCAR (it->font_height), Qminus))
&& CONSP (XCDR (it->font_height))
&& INTEGERP (XCAR (XCDR (it->font_height))))
it->font_height = XCAR (XCDR (spec));
if (!NILP (it->font_height))
{
/* `(+ N)' or `(- N)' where N is an integer. */
int steps = XINT (XCAR (XCDR (it->font_height)));
if (EQ (XCAR (it->font_height), Qplus))
steps = - steps;
it->face_id = smaller_face (it->f, it->face_id, steps);
}
else if (FUNCTIONP (it->font_height))
{
/* Call function with current height as argument.
Value is the new height. */
Lisp_Object height;
height = safe_call1 (it->font_height,
face->lface[LFACE_HEIGHT_INDEX]);
if (NUMBERP (height))
new_height = XFLOATINT (height);
}
else if (NUMBERP (it->font_height))
{
/* Value is a multiple of the canonical char height. */
struct face *f;
struct face *face = FACE_FROM_ID (it->f, it->face_id);
int new_height = -1;
f = FACE_FROM_ID (it->f,
lookup_basic_face (it->f, DEFAULT_FACE_ID));
new_height = (XFLOATINT (it->font_height)
* XINT (f->lface[LFACE_HEIGHT_INDEX]));
if (CONSP (it->font_height)
&& (EQ (XCAR (it->font_height), Qplus)
|| EQ (XCAR (it->font_height), Qminus))
&& CONSP (XCDR (it->font_height))
&& INTEGERP (XCAR (XCDR (it->font_height))))
{
/* `(+ N)' or `(- N)' where N is an integer. */
int steps = XINT (XCAR (XCDR (it->font_height)));
if (EQ (XCAR (it->font_height), Qplus))
steps = - steps;
it->face_id = smaller_face (it->f, it->face_id, steps);
}
else if (FUNCTIONP (it->font_height))
{
/* Call function with current height as argument.
Value is the new height. */
Lisp_Object height;
height = safe_call1 (it->font_height,
face->lface[LFACE_HEIGHT_INDEX]);
if (NUMBERP (height))
new_height = XFLOATINT (height);
}
else if (NUMBERP (it->font_height))
{
/* Value is a multiple of the canonical char height. */
struct face *f;
f = FACE_FROM_ID (it->f,
lookup_basic_face (it->f, DEFAULT_FACE_ID));
new_height = (XFLOATINT (it->font_height)
* XINT (f->lface[LFACE_HEIGHT_INDEX]));
}
else
{
/* Evaluate IT->font_height with `height' bound to the
current specified height to get the new height. */
int count = SPECPDL_INDEX ();
specbind (Qheight, face->lface[LFACE_HEIGHT_INDEX]);
value = safe_eval (it->font_height);
unbind_to (count, Qnil);
if (NUMBERP (value))
new_height = XFLOATINT (value);
}
if (new_height > 0)
it->face_id = face_with_height (it->f, it->face_id, new_height);
}
else
{
/* Evaluate IT->font_height with `height' bound to the
current specified height to get the new height. */
int count = SPECPDL_INDEX ();
specbind (Qheight, face->lface[LFACE_HEIGHT_INDEX]);
value = safe_eval (it->font_height);
unbind_to (count, Qnil);
if (NUMBERP (value))
new_height = XFLOATINT (value);
}
if (new_height > 0)
it->face_id = face_with_height (it->f, it->face_id, new_height);
}
return 0;
@ -3989,12 +4110,15 @@ handle_single_display_spec (struct it *it, Lisp_Object spec, Lisp_Object object,
&& EQ (XCAR (spec), Qspace_width)
&& CONSP (XCDR (spec)))
{
if (!FRAME_WINDOW_P (it->f))
return 0;
if (it)
{
if (!FRAME_WINDOW_P (it->f))
return 0;
value = XCAR (XCDR (spec));
if (NUMBERP (value) && XFLOATINT (value) > 0)
it->space_width = value;
value = XCAR (XCDR (spec));
if (NUMBERP (value) && XFLOATINT (value) > 0)
it->space_width = value;
}
return 0;
}
@ -4005,20 +4129,23 @@ handle_single_display_spec (struct it *it, Lisp_Object spec, Lisp_Object object,
{
Lisp_Object tem;
if (!FRAME_WINDOW_P (it->f))
return 0;
if (tem = XCDR (spec), CONSP (tem))
if (it)
{
it->slice.x = XCAR (tem);
if (tem = XCDR (tem), CONSP (tem))
if (!FRAME_WINDOW_P (it->f))
return 0;
if (tem = XCDR (spec), CONSP (tem))
{
it->slice.y = XCAR (tem);
it->slice.x = XCAR (tem);
if (tem = XCDR (tem), CONSP (tem))
{
it->slice.width = XCAR (tem);
it->slice.y = XCAR (tem);
if (tem = XCDR (tem), CONSP (tem))
it->slice.height = XCAR (tem);
{
it->slice.width = XCAR (tem);
if (tem = XCDR (tem), CONSP (tem))
it->slice.height = XCAR (tem);
}
}
}
}
@ -4031,36 +4158,43 @@ handle_single_display_spec (struct it *it, Lisp_Object spec, Lisp_Object object,
&& EQ (XCAR (spec), Qraise)
&& CONSP (XCDR (spec)))
{
if (!FRAME_WINDOW_P (it->f))
return 0;
if (it)
{
if (!FRAME_WINDOW_P (it->f))
return 0;
#ifdef HAVE_WINDOW_SYSTEM
value = XCAR (XCDR (spec));
if (NUMBERP (value))
{
struct face *face = FACE_FROM_ID (it->f, it->face_id);
it->voffset = - (XFLOATINT (value)
* (FONT_HEIGHT (face->font)));
}
value = XCAR (XCDR (spec));
if (NUMBERP (value))
{
struct face *face = FACE_FROM_ID (it->f, it->face_id);
it->voffset = - (XFLOATINT (value)
* (FONT_HEIGHT (face->font)));
}
#endif /* HAVE_WINDOW_SYSTEM */
}
return 0;
}
/* Don't handle the other kinds of display specifications
inside a string that we got from a `display' property. */
if (it->string_from_display_prop_p)
if (it && it->string_from_display_prop_p)
return 0;
/* Characters having this form of property are not displayed, so
we have to find the end of the property. */
start_pos = *position;
*position = display_prop_end (it, object, start_pos);
if (it)
{
start_pos = *position;
*position = display_prop_end (it, object, start_pos);
}
value = Qnil;
/* Stop the scan at that end position--we assume that all
text properties change there. */
it->stop_charpos = position->charpos;
if (it)
it->stop_charpos = position->charpos;
/* Handle `(left-fringe BITMAP [FACE])'
and `(right-fringe BITMAP [FACE])'. */
@ -4069,12 +4203,16 @@ handle_single_display_spec (struct it *it, Lisp_Object spec, Lisp_Object object,
|| EQ (XCAR (spec), Qright_fringe))
&& CONSP (XCDR (spec)))
{
int face_id = lookup_basic_face (it->f, DEFAULT_FACE_ID);
int fringe_bitmap;
if (!FRAME_WINDOW_P (it->f))
/* If we return here, POSITION has been advanced
across the text with this property. */
if (it)
{
if (!FRAME_WINDOW_P (it->f))
/* If we return here, POSITION has been advanced
across the text with this property. */
return 0;
}
else if (!frame_window_p)
return 0;
#ifdef HAVE_WINDOW_SYSTEM
@ -4085,46 +4223,47 @@ handle_single_display_spec (struct it *it, Lisp_Object spec, Lisp_Object object,
across the text with this property. */
return 0;
if (CONSP (XCDR (XCDR (spec))))
if (it)
{
Lisp_Object face_name = XCAR (XCDR (XCDR (spec)));
int face_id2 = lookup_derived_face (it->f, face_name,
FRINGE_FACE_ID, 0);
if (face_id2 >= 0)
face_id = face_id2;
}
int face_id = lookup_basic_face (it->f, DEFAULT_FACE_ID);;
/* Save current settings of IT so that we can restore them
when we are finished with the glyph property value. */
if (CONSP (XCDR (XCDR (spec))))
{
Lisp_Object face_name = XCAR (XCDR (XCDR (spec)));
int face_id2 = lookup_derived_face (it->f, face_name,
FRINGE_FACE_ID, 0);
if (face_id2 >= 0)
face_id = face_id2;
}
save_pos = it->position;
it->position = *position;
push_it (it);
it->position = save_pos;
/* Save current settings of IT so that we can restore them
when we are finished with the glyph property value. */
push_it (it, position);
it->area = TEXT_AREA;
it->what = IT_IMAGE;
it->image_id = -1; /* no image */
it->position = start_pos;
it->object = NILP (object) ? it->w->buffer : object;
it->method = GET_FROM_IMAGE;
it->from_overlay = Qnil;
it->face_id = face_id;
it->area = TEXT_AREA;
it->what = IT_IMAGE;
it->image_id = -1; /* no image */
it->position = start_pos;
it->object = NILP (object) ? it->w->buffer : object;
it->method = GET_FROM_IMAGE;
it->from_overlay = Qnil;
it->face_id = face_id;
/* Say that we haven't consumed the characters with
`display' property yet. The call to pop_it in
set_iterator_to_next will clean this up. */
*position = start_pos;
/* Say that we haven't consumed the characters with
`display' property yet. The call to pop_it in
set_iterator_to_next will clean this up. */
*position = start_pos;
if (EQ (XCAR (spec), Qleft_fringe))
{
it->left_user_fringe_bitmap = fringe_bitmap;
it->left_user_fringe_face_id = face_id;
}
else
{
it->right_user_fringe_bitmap = fringe_bitmap;
it->right_user_fringe_face_id = face_id;
if (EQ (XCAR (spec), Qleft_fringe))
{
it->left_user_fringe_bitmap = fringe_bitmap;
it->left_user_fringe_face_id = face_id;
}
else
{
it->right_user_fringe_bitmap = fringe_bitmap;
it->right_user_fringe_face_id = face_id;
}
}
#endif /* HAVE_WINDOW_SYSTEM */
return 1;
@ -4167,18 +4306,19 @@ handle_single_display_spec (struct it *it, Lisp_Object spec, Lisp_Object object,
valid_p = (STRINGP (value)
#ifdef HAVE_WINDOW_SYSTEM
|| (FRAME_WINDOW_P (it->f) && valid_image_p (value))
|| ((it ? FRAME_WINDOW_P (it->f) : frame_window_p)
&& valid_image_p (value))
#endif /* not HAVE_WINDOW_SYSTEM */
|| (CONSP (value) && EQ (XCAR (value), Qspace)));
if (valid_p && !display_replaced_p)
{
if (!it)
return 1;
/* Save current settings of IT so that we can restore them
when we are finished with the glyph property value. */
save_pos = it->position;
it->position = *position;
push_it (it);
it->position = save_pos;
push_it (it, position);
it->from_overlay = overlay;
if (NILP (location))
@ -4235,83 +4375,31 @@ handle_single_display_spec (struct it *it, Lisp_Object spec, Lisp_Object object,
return 0;
}
/* Check if SPEC is a display sub-property value whose text should be
treated as intangible. */
static int
single_display_spec_intangible_p (Lisp_Object prop)
{
/* Skip over `when FORM'. */
if (CONSP (prop) && EQ (XCAR (prop), Qwhen))
{
prop = XCDR (prop);
if (!CONSP (prop))
return 0;
prop = XCDR (prop);
}
if (STRINGP (prop))
return 1;
if (!CONSP (prop))
return 0;
/* Skip over `margin LOCATION'. If LOCATION is in the margins,
we don't need to treat text as intangible. */
if (EQ (XCAR (prop), Qmargin))
{
prop = XCDR (prop);
if (!CONSP (prop))
return 0;
prop = XCDR (prop);
if (!CONSP (prop)
|| EQ (XCAR (prop), Qleft_margin)
|| EQ (XCAR (prop), Qright_margin))
return 0;
}
return (CONSP (prop)
&& (EQ (XCAR (prop), Qimage)
|| EQ (XCAR (prop), Qspace)));
}
/* Check if PROP is a display property value whose text should be
treated as intangible. */
treated as intangible. OVERLAY is the overlay from which PROP
came, or nil if it came from a text property. CHARPOS and BYTEPOS
specify the buffer position covered by PROP. */
int
display_prop_intangible_p (Lisp_Object prop)
display_prop_intangible_p (Lisp_Object prop, Lisp_Object overlay,
EMACS_INT charpos, EMACS_INT bytepos)
{
if (CONSP (prop)
&& CONSP (XCAR (prop))
&& !EQ (Qmargin, XCAR (XCAR (prop))))
{
/* A list of sub-properties. */
while (CONSP (prop))
{
if (single_display_spec_intangible_p (XCAR (prop)))
return 1;
prop = XCDR (prop);
}
}
else if (VECTORP (prop))
{
/* A vector of sub-properties. */
int i;
for (i = 0; i < ASIZE (prop); ++i)
if (single_display_spec_intangible_p (AREF (prop, i)))
return 1;
}
else
return single_display_spec_intangible_p (prop);
int frame_window_p = FRAME_WINDOW_P (XFRAME (selected_frame));
struct text_pos position;
return 0;
SET_TEXT_POS (position, charpos, bytepos);
return handle_display_spec (NULL, prop, Qnil, overlay,
&position, charpos, frame_window_p);
}
/* Return 1 if PROP is a display sub-property value containing STRING. */
/* Return 1 if PROP is a display sub-property value containing STRING.
Implementation note: this and the following function are really
special cases of handle_display_spec and
handle_single_display_spec, and should ideally use the same code.
Until they do, these two pairs must be consistent and must be
modified in sync. */
static int
single_display_spec_string_p (Lisp_Object prop, Lisp_Object string)
@ -4325,6 +4413,16 @@ single_display_spec_string_p (Lisp_Object prop, Lisp_Object string)
prop = XCDR (prop);
if (!CONSP (prop))
return 0;
/* Actually, the condition following `when' should be eval'ed,
like handle_single_display_spec does, and we should return
zero if it evaluates to nil. However, this function is
called only when the buffer was already displayed and some
glyph in the glyph matrix was found to come from a display
string. Therefore, the condition was already evaluated, and
the result was non-nil, otherwise the display string wouldn't
have been displayed and we would have never been called for
this property. Thus, we can skip the evaluation and assume
its result is non-nil. */
prop = XCDR (prop);
}
@ -4341,7 +4439,7 @@ single_display_spec_string_p (Lisp_Object prop, Lisp_Object string)
return 0;
}
return CONSP (prop) && EQ (XCAR (prop), string);
return EQ (prop, string) || (CONSP (prop) && EQ (XCAR (prop), string));
}
@ -4351,8 +4449,8 @@ static int
display_prop_string_p (Lisp_Object prop, Lisp_Object string)
{
if (CONSP (prop)
&& CONSP (XCAR (prop))
&& !EQ (Qmargin, XCAR (XCAR (prop))))
&& !EQ (XCAR (prop), Qwhen)
&& !(CONSP (XCAR (prop)) && EQ (Qmargin, XCAR (XCAR (prop)))))
{
/* A list of sub-properties. */
while (CONSP (prop))
@ -4852,7 +4950,7 @@ get_overlay_strings_1 (struct it *it, EMACS_INT charpos, int compute_stop_p)
/* When called from handle_stop, there might be an empty display
string loaded. In that case, don't bother saving it. */
if (!STRINGP (it->string) || SCHARS (it->string))
push_it (it);
push_it (it, NULL);
/* Set up IT to deliver display elements from the first overlay
string. */
@ -4894,10 +4992,11 @@ get_overlay_strings (struct it *it, EMACS_INT charpos)
/* Save current settings of IT on IT->stack. Called, for example,
before setting up IT for an overlay string, to be able to restore
IT's settings to what they were after the overlay string has been
processed. */
processed. If POSITION is non-NULL, it is the position to save on
the stack instead of IT->position. */
static void
push_it (struct it *it)
push_it (struct it *it, struct text_pos *position)
{
struct iterator_stack_entry *p;
@ -4924,7 +5023,7 @@ push_it (struct it *it)
p->u.stretch.object = it->object;
break;
}
p->position = it->position;
p->position = position ? *position : it->position;
p->current = it->current;
p->end_charpos = it->end_charpos;
p->string_nchars = it->string_nchars;
@ -5382,6 +5481,7 @@ reseat_1 (struct it *it, struct text_pos pos, int set_stop_p)
{
it->bidi_it.first_elt = 1;
it->bidi_it.paragraph_dir = NEUTRAL_DIR;
it->bidi_it.disp_pos = -1;
}
if (set_stop_p)
@ -12688,11 +12788,30 @@ set_cursor_from_row (struct window *w, struct glyph_row *row,
GLYPH_BEFORE and GLYPH_AFTER, and it came from a string
positioned between POS_BEFORE and POS_AFTER in the
buffer. */
struct glyph *stop = glyph_after;
struct glyph *start, *stop;
EMACS_INT pos = pos_before;
x = -1;
for (glyph = glyph_before + incr;
/* GLYPH_BEFORE and GLYPH_AFTER are the glyphs that
correspond to POS_BEFORE and POS_AFTER, respectively. We
need START and STOP in the order that corresponds to the
row's direction as given by its reversed_p flag. If the
directionality of characters between POS_BEFORE and
POS_AFTER is the opposite of the row's base direction,
these characters will have been reordered for display,
and we need to reverse START and STOP. */
if (!row->reversed_p)
{
start = min (glyph_before, glyph_after);
stop = max (glyph_before, glyph_after);
}
else
{
start = max (glyph_before, glyph_after);
stop = min (glyph_before, glyph_after);
}
for (glyph = start + incr;
row->reversed_p ? glyph > stop : glyph < stop; )
{
@ -17111,7 +17230,7 @@ cursor_row_p (struct glyph_row *row)
static int
push_display_prop (struct it *it, Lisp_Object prop)
{
push_it (it);
push_it (it, NULL);
if (STRINGP (prop))
{
@ -18040,6 +18159,8 @@ See also `bidi-paragraph-direction'. */)
bytepos--;
itb.charpos = pos;
itb.bytepos = bytepos;
itb.nchars = -1;
itb.frame_window_p = FRAME_WINDOW_P (SELECTED_FRAME ()); /* guesswork */
itb.first_elt = 1;
itb.separator_limit = -1;
itb.paragraph_dir = NEUTRAL_DIR;