if (CACHE) \
bidi_unshelve_cache (CACHE, 1); \
ITCOPY = ITORIG; \
- CACHE = bidi_shelve_cache(); \
+ CACHE = bidi_shelve_cache (); \
} while (0)
#define RESTORE_IT(pITORIG,pITCOPY,CACHE) \
return line_top_y + line_height;
}
+/* Subroutine of pos_visible_p below. Extracts a display string, if
+ any, from the display spec given as its argument. */
+static Lisp_Object
+string_from_display_spec (Lisp_Object spec)
+{
+ if (CONSP (spec))
+ {
+ while (CONSP (spec))
+ {
+ if (STRINGP (XCAR (spec)))
+ return XCAR (spec);
+ spec = XCDR (spec);
+ }
+ }
+ else if (VECTORP (spec))
+ {
+ ptrdiff_t i;
+
+ for (i = 0; i < ASIZE (spec); i++)
+ {
+ if (STRINGP (AREF (spec, i)))
+ return AREF (spec, i);
+ }
+ return Qnil;
+ }
+
+ return spec;
+}
/* Return 1 if position CHARPOS is visible in window W.
CHARPOS < 0 means return info about WINDOW_END position.
}
}
}
+ else if (IT_CHARPOS (it) != charpos)
+ {
+ Lisp_Object cpos = make_number (charpos);
+ Lisp_Object spec = Fget_char_property (cpos, Qdisplay, Qnil);
+ Lisp_Object string = string_from_display_spec (spec);
+ int newline_in_string = 0;
+
+ if (STRINGP (string))
+ {
+ const char *s = SSDATA (string);
+ const char *e = s + SBYTES (string);
+ while (s < e)
+ {
+ if (*s++ == '\n')
+ {
+ newline_in_string = 1;
+ break;
+ }
+ }
+ }
+ /* The tricky code below is needed because there's a
+ discrepancy between move_it_to and how we set cursor
+ when the display line ends in a newline from a
+ display string. move_it_to will stop _after_ such
+ display strings, whereas set_cursor_from_row
+ conspires with cursor_row_p to place the cursor on
+ the first glyph produced from the display string. */
+
+ /* We have overshoot PT because it is covered by a
+ display property whose value is a string. If the
+ string includes embedded newlines, we are also in the
+ wrong display line. Backtrack to the correct line,
+ where the display string begins. */
+ if (newline_in_string)
+ {
+ Lisp_Object startpos, endpos;
+ EMACS_INT start, end;
+ struct it it3;
+
+ /* Find the first and the last buffer positions
+ covered by the display string. */
+ endpos =
+ Fnext_single_char_property_change (cpos, Qdisplay,
+ Qnil, Qnil);
+ startpos =
+ Fprevious_single_char_property_change (endpos, Qdisplay,
+ Qnil, Qnil);
+ start = XFASTINT (startpos);
+ end = XFASTINT (endpos);
+ /* Move to the last buffer position before the
+ display property. */
+ start_display (&it3, w, top);
+ move_it_to (&it3, start - 1, -1, -1, -1, MOVE_TO_POS);
+ /* Move forward one more line if the position before
+ the display string is a newline or if it is the
+ rightmost character on a line that is
+ continued or word-wrapped. */
+ if (it3.method == GET_FROM_BUFFER
+ && it3.c == '\n')
+ move_it_by_lines (&it3, 1);
+ else if (move_it_in_display_line_to (&it3, -1,
+ it3.current_x
+ + it3.pixel_width,
+ MOVE_TO_X)
+ == MOVE_LINE_CONTINUED)
+ {
+ move_it_by_lines (&it3, 1);
+ /* When we are under word-wrap, the #$@%!
+ move_it_by_lines moves 2 lines, so we need to
+ fix that up. */
+ if (it3.line_wrap == WORD_WRAP)
+ move_it_by_lines (&it3, -1);
+ }
+
+ /* Record the vertical coordinate of the display
+ line where we wound up. */
+ top_y = it3.current_y;
+ if (it3.bidi_p)
+ {
+ /* When characters are reordered for display,
+ the character displayed to the left of the
+ display string could be _after_ the display
+ property in the logical order. Use the
+ smallest vertical position of these two. */
+ start_display (&it3, w, top);
+ move_it_to (&it3, end + 1, -1, -1, -1, MOVE_TO_POS);
+ if (it3.current_y < top_y)
+ top_y = it3.current_y;
+ }
+ /* Move from the top of the window to the beginning
+ of the display line where the display string
+ begins. */
+ start_display (&it3, w, top);
+ move_it_to (&it3, -1, 0, top_y, -1, MOVE_TO_X | MOVE_TO_Y);
+ /* Finally, advance the iterator until we hit the
+ first display element whose character position is
+ CHARPOS, or until the first newline from the
+ display string, which signals the end of the
+ display line. */
+ while (get_next_display_element (&it3))
+ {
+ PRODUCE_GLYPHS (&it3);
+ if (IT_CHARPOS (it3) == charpos
+ || ITERATOR_AT_END_OF_LINE_P (&it3))
+ break;
+ set_iterator_to_next (&it3, 0);
+ }
+ top_x = it3.current_x - it3.pixel_width;
+ /* Normally, we would exit the above loop because we
+ found the display element whose character
+ position is CHARPOS. For the contingency that we
+ didn't, and stopped at the first newline from the
+ display string, move back over the glyphs
+ prfoduced from the string, until we find the
+ rightmost glyph not from the string. */
+ if (IT_CHARPOS (it3) != charpos && EQ (it3.object, string))
+ {
+ struct glyph *g = it3.glyph_row->glyphs[TEXT_AREA]
+ + it3.glyph_row->used[TEXT_AREA];
+
+ while (EQ ((g - 1)->object, string))
+ {
+ --g;
+ top_x -= g->pixel_width;
+ }
+ xassert (g < it3.glyph_row->glyphs[TEXT_AREA]
+ + it3.glyph_row->used[TEXT_AREA]);
+ }
+ }
+ }
*x = top_x;
*y = max (top_y + max (0, it.max_ascent - it.ascent), window_top_y);
rectangle as wide as the glyph, but use a canonical character
width instead. */
wd = glyph->pixel_width - 1;
-#if defined(HAVE_NTGUI) || defined(HAVE_NS)
+#if defined (HAVE_NTGUI) || defined (HAVE_NS)
wd++; /* Why? */
#endif
}
/* How many characters forward to search for a display property or
- display string. Enough for a screenful of 100 lines x 50
- characters in a line. */
-#define MAX_DISP_SCAN 5000
+ display string. Searching too far forward makes the bidi display
+ sluggish, especially in small windows. */
+#define MAX_DISP_SCAN 250
/* Return the character position of a display string at or after
position specified by POSITION. If no display string exists at or
}
/* 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. */
+ started at CHARPOS. If there's no display string at CHARPOS,
+ return -1. 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, struct bidi_string_data *string)
{
if (charpos >= eob || (string->s && !STRINGP (object)))
return eob;
+ /* It could happen that the display property or overlay was removed
+ since we found it in compute_display_string_pos above. One way
+ this can happen is if JIT font-lock was called (through
+ handle_fontified_prop), and jit-lock-functions remove text
+ properties or overlays from the portion of buffer that includes
+ CHARPOS. Muse mode is known to do that, for example. In this
+ case, we return -1 to the caller, to signal that no display
+ string is actually present at CHARPOS. See bidi_fetch_char for
+ how this is handled.
+
+ An alternative would be to never look for display properties past
+ it->stop_charpos. But neither compute_display_string_pos nor
+ bidi_fetch_char that calls it know or care where the next
+ stop_charpos is. */
if (NILP (Fget_char_property (pos, Qdisplay, object)))
- abort ();
+ return -1;
/* Look forward for the first character where the `display' property
changes. */
/* The position newpos is now either ZV or on visible text. */
if (it->bidi_p && newpos < ZV)
{
- /* With bidi iteration, the region of invisible text
- could start and/or end in the middle of a non-base
- embedding level. Therefore, we need to skip
- invisible text using the bidi iterator, starting at
- IT's current position, until we find ourselves
- outside the invisible text. Skipping invisible text
- _after_ bidi iteration avoids affecting the visual
- order of the displayed text when invisible properties
- are added or removed. */
- if (it->bidi_it.first_elt && it->bidi_it.charpos < ZV)
+ EMACS_INT bpos = CHAR_TO_BYTE (newpos);
+
+ if (FETCH_BYTE (bpos) == '\n'
+ || (newpos > BEGV && FETCH_BYTE (bpos - 1) == '\n'))
{
- /* If we were `reseat'ed to a new paragraph,
- determine the paragraph base direction. We need
- to do it now because next_element_from_buffer may
- not have a chance to do it, if we are going to
- skip any text at the beginning, which resets the
- FIRST_ELT flag. */
- bidi_paragraph_init (it->paragraph_embedding,
- &it->bidi_it, 1);
+ /* If the invisible text ends on a newline or the
+ character after a newline, we can avoid the
+ costly, character by character, bidi iteration to
+ newpos, and instead simply reseat the iterator
+ there. That's because all bidi reordering
+ information is tossed at the newline. This is a
+ big win for modes that hide complete lines, like
+ Outline, Org, etc. (Implementation note: the
+ call to reseat_1 is necessary, because it signals
+ to the bidi iterator that it needs to reinit its
+ internal information when the next element for
+ display is requested. */
+ struct text_pos tpos;
+
+ SET_TEXT_POS (tpos, newpos, bpos);
+ reseat_1 (it, tpos, 0);
}
- do
+ else /* Must use the slow method. */
{
- bidi_move_to_visually_next (&it->bidi_it);
+ /* With bidi iteration, the region of invisible text
+ could start and/or end in the middle of a
+ non-base embedding level. Therefore, we need to
+ skip invisible text using the bidi iterator,
+ starting at IT's current position, until we find
+ ourselves outside the invisible text. Skipping
+ invisible text _after_ bidi iteration avoids
+ affecting the visual order of the displayed text
+ when invisible properties are added or
+ removed. */
+ if (it->bidi_it.first_elt && it->bidi_it.charpos < ZV)
+ {
+ /* If we were `reseat'ed to a new paragraph,
+ determine the paragraph base direction. We
+ need to do it now because
+ next_element_from_buffer may not have a
+ chance to do it, if we are going to skip any
+ text at the beginning, which resets the
+ FIRST_ELT flag. */
+ bidi_paragraph_init (it->paragraph_embedding,
+ &it->bidi_it, 1);
+ }
+ do
+ {
+ bidi_move_to_visually_next (&it->bidi_it);
+ }
+ while (it->stop_charpos <= it->bidi_it.charpos
+ && it->bidi_it.charpos < newpos);
+ IT_CHARPOS (*it) = it->bidi_it.charpos;
+ IT_BYTEPOS (*it) = it->bidi_it.bytepos;
+ /* If we overstepped NEWPOS, record its position in
+ the iterator, so that we skip invisible text if
+ later the bidi iteration lands us in the
+ invisible region again. */
+ if (IT_CHARPOS (*it) >= newpos)
+ it->prev_stop = newpos;
}
- while (it->stop_charpos <= it->bidi_it.charpos
- && it->bidi_it.charpos < newpos);
- IT_CHARPOS (*it) = it->bidi_it.charpos;
- IT_BYTEPOS (*it) = it->bidi_it.bytepos;
- /* If we overstepped NEWPOS, record its position in the
- iterator, so that we skip invisible text if later the
- bidi iteration lands us in the invisible region
- again. */
- if (IT_CHARPOS (*it) >= newpos)
- it->prev_stop = newpos;
}
else
{
&& IT_BYTEPOS (*it) == it->bidi_it.bytepos)
|| (STRINGP (it->object)
&& IT_STRING_CHARPOS (*it) == it->bidi_it.charpos
- && IT_STRING_BYTEPOS (*it) == it->bidi_it.bytepos));
+ && IT_STRING_BYTEPOS (*it) == it->bidi_it.bytepos)
+ || (CONSP (it->object) && it->method == GET_FROM_STRETCH));
}
}
{
Lisp_Object dv;
struct charset *unibyte = CHARSET_FROM_ID (charset_unibyte);
- enum { char_is_other = 0, char_is_nbsp, char_is_soft_hyphen }
- nbsp_or_shy = char_is_other;
+ int nonascii_space_p = 0;
+ int nonascii_hyphen_p = 0;
int c = it->c; /* This is the character to display. */
if (! it->multibyte_p && ! ASCII_CHAR_P (c))
goto get_next;
}
+ /* If `nobreak-char-display' is non-nil, we display
+ non-ASCII spaces and hyphens specially. */
if (! ASCII_CHAR_P (c) && ! NILP (Vnobreak_char_display))
- nbsp_or_shy = (c == 0xA0 ? char_is_nbsp
- : c == 0xAD ? char_is_soft_hyphen
- : char_is_other);
+ {
+ if (c == 0xA0)
+ nonascii_space_p = 1;
+ else if (c == 0xAD || c == 0x2010 || c == 0x2011)
+ nonascii_hyphen_p = 1;
+ }
/* Translate control characters into `\003' or `^C' form.
Control characters coming from a display table entry are
the translation. This could easily be changed but I
don't believe that it is worth doing.
- NBSP and SOFT-HYPEN are property translated too.
+ The characters handled by `nobreak-char-display' must be
+ translated too.
Non-printable characters and raw-byte characters are also
translated to octal form. */
&& it->glyph_row
&& (it->glyph_row->mode_line_p || it->avoid_cursor_p))
|| (c != '\n' && c != '\t'))
- : (nbsp_or_shy
+ : (nonascii_space_p
+ || nonascii_hyphen_p
|| CHAR_BYTE8_P (c)
|| ! CHAR_PRINTABLE_P (c))))
{
- /* C is a control character, NBSP, SOFT-HYPEN, raw-byte,
- or a non-printable character which must be displayed
- either as '\003' or as `^C' where the '\\' and '^'
- can be defined in the display table. Fill
+ /* C is a control character, non-ASCII space/hyphen,
+ raw-byte, or a non-printable character which must be
+ displayed either as '\003' or as `^C' where the '\\'
+ and '^' can be defined in the display table. Fill
IT->ctl_chars with glyphs for what we have to
display. Then, set IT->dpvec to these glyphs. */
Lisp_Object gc;
goto display_control;
}
- /* Handle non-break space in the mode where it only gets
+ /* Handle non-ascii space in the mode where it only gets
highlighting. */
- if (EQ (Vnobreak_char_display, Qt)
- && nbsp_or_shy == char_is_nbsp)
+ if (nonascii_space_p && EQ (Vnobreak_char_display, Qt))
{
- /* Merge the no-break-space face into the current face. */
+ /* Merge `nobreak-space' into the current face. */
face_id = merge_faces (it->f, Qnobreak_space, 0,
it->face_id);
-
- c = ' ';
XSETINT (it->ctl_chars[0], ' ');
ctl_len = 1;
goto display_control;
last_escape_glyph_merged_face_id = face_id;
}
- /* Handle soft hyphens in the mode where they only get
- highlighting. */
+ /* Draw non-ASCII hyphen with just highlighting: */
- if (EQ (Vnobreak_char_display, Qt)
- && nbsp_or_shy == char_is_soft_hyphen)
+ if (nonascii_hyphen_p && EQ (Vnobreak_char_display, Qt))
{
XSETINT (it->ctl_chars[0], '-');
ctl_len = 1;
goto display_control;
}
- /* Handle non-break space and soft hyphen
- with the escape glyph. */
+ /* Draw non-ASCII space/hyphen with escape glyph: */
- if (nbsp_or_shy)
+ if (nonascii_space_p || nonascii_hyphen_p)
{
XSETINT (it->ctl_chars[0], escape_glyph);
- c = (nbsp_or_shy == char_is_nbsp ? ' ' : '-');
- XSETINT (it->ctl_chars[1], c);
+ XSETINT (it->ctl_chars[1], nonascii_space_p ? ' ' : '-');
ctl_len = 2;
goto display_control;
}
c = ' ';
for (i = 0; i < cmp->glyph_len; i++)
+ /* TAB in a composition means display glyphs with
+ padding space on the left or right. */
if ((c = COMPOSITION_GLYPH (cmp, i)) != '\t')
break;
}
}
else if (it->bidi_it.charpos == bob
|| (!string_p
- /* FIXME: Should support all Unicode line separators. */
&& (FETCH_CHAR (it->bidi_it.bytepos - 1) == '\n'
|| FETCH_CHAR (it->bidi_it.bytepos) == '\n')))
{
((op & MOVE_TO_POS) != 0 \
&& BUFFERP (it->object) \
&& (IT_CHARPOS (*it) == to_charpos \
- || (!it->bidi_p && IT_CHARPOS (*it) > to_charpos) \
+ || ((!it->bidi_p \
+ || BIDI_AT_BASE_LEVEL (it->bidi_it)) \
+ && IT_CHARPOS (*it) > to_charpos) \
|| (it->what == IT_COMPOSITION \
&& ((IT_CHARPOS (*it) > to_charpos \
&& to_charpos >= it->cmp_it.charpos) \
if ((op & MOVE_TO_POS) != 0
&& BUFFERP (it->object)
&& it->method == GET_FROM_BUFFER
- && ((!it->bidi_p && IT_CHARPOS (*it) > to_charpos)
+ && (((!it->bidi_p
+ /* When the iterator is at base embedding level, we
+ are guaranteed that characters are delivered for
+ display in strictly increasing order of their
+ buffer positions. */
+ || BIDI_AT_BASE_LEVEL (it->bidi_it))
+ && IT_CHARPOS (*it) > to_charpos)
|| (it->bidi_p
&& (prev_method == GET_FROM_IMAGE
|| prev_method == GET_FROM_STRETCH
&& !saw_smaller_pos
&& IT_CHARPOS (*it) > to_charpos))
{
- if (!at_eob_p && IT_CHARPOS (ppos_it) < ZV)
+ if (it->bidi_p
+ && !at_eob_p && IT_CHARPOS (ppos_it) < ZV)
RESTORE_IT (it, &ppos_it, ppos_data);
result = MOVE_POS_MATCH_OR_ZV;
break;
move_it_to (&it2, start_pos, -1, -1, it2.vpos + 1,
MOVE_TO_POS | MOVE_TO_VPOS);
}
- while (!IT_POS_VALID_AFTER_MOVE_P (&it2));
+ while (!(IT_POS_VALID_AFTER_MOVE_P (&it2)
+ /* If we are in a display string which starts at START_POS,
+ and that display string includes a newline, and we are
+ right after that newline (i.e. at the beginning of a
+ display line), exit the loop, because otherwise we will
+ infloop, since move_it_to will see that it is already at
+ START_POS and will not move. */
+ || (it2.method == GET_FROM_STRING
+ && IT_CHARPOS (it2) == start_pos
+ && SREF (it2.string, IT_STRING_BYTEPOS (it2) - 1) == '\n')));
xassert (IT_CHARPOS (*it) >= BEGV);
SAVE_IT (it3, it2, it3data);
reordering. We want to get to the character position
that is immediately after the newline of the previous
line. */
- if (it->bidi_p && IT_CHARPOS (*it) > BEGV
+ if (it->bidi_p
+ && !it->continuation_lines_width
+ && !STRINGP (it->string)
+ && IT_CHARPOS (*it) > BEGV
&& FETCH_BYTE (IT_BYTEPOS (*it) - 1) != '\n')
{
EMACS_INT nl_pos =
= (desired_cursor_row->enabled_p
? desired_cursor_row
: current_cursor_row);
+ int row_r2l_p = cursor_row->reversed_p;
text_area_width = window_box_width (w, TEXT_AREA);
h_margin = hscroll_margin * WINDOW_FRAME_COLUMN_WIDTH (w);
if (!NILP (Fbuffer_local_value (Qauto_hscroll_mode, w->buffer))
- && ((XFASTINT (w->hscroll)
- && w->cursor.x <= h_margin)
- || (cursor_row->enabled_p
- && cursor_row->truncated_on_right_p
- && (w->cursor.x >= text_area_width - h_margin))))
+ /* For left-to-right rows, hscroll when cursor is either
+ (i) inside the right hscroll margin, or (ii) if it is
+ inside the left margin and the window is already
+ hscrolled. */
+ && ((!row_r2l_p
+ && ((XFASTINT (w->hscroll)
+ && w->cursor.x <= h_margin)
+ || (cursor_row->enabled_p
+ && cursor_row->truncated_on_right_p
+ && (w->cursor.x >= text_area_width - h_margin))))
+ /* For right-to-left rows, the logic is similar,
+ except that rules for scrolling to left and right
+ are reversed. E.g., if cursor.x <= h_margin, we
+ need to hscroll "to the right" unconditionally,
+ and that will scroll the screen to the left so as
+ to reveal the next portion of the row. */
+ || (row_r2l_p
+ && ((cursor_row->enabled_p
+ /* FIXME: It is confusing to set the
+ truncated_on_right_p flag when R2L rows
+ are actually truncated on the left. */
+ && cursor_row->truncated_on_right_p
+ && w->cursor.x <= h_margin)
+ || (XFASTINT (w->hscroll)
+ && (w->cursor.x >= text_area_width - h_margin))))))
{
struct it it;
int hscroll;
? (text_area_width - 4 * FRAME_COLUMN_WIDTH (it.f))
: (text_area_width / 2))))
/ FRAME_COLUMN_WIDTH (it.f);
- else if (w->cursor.x >= text_area_width - h_margin)
+ else if ((!row_r2l_p
+ && w->cursor.x >= text_area_width - h_margin)
+ || (row_r2l_p && w->cursor.x <= h_margin))
{
if (hscroll_relative_p)
wanted_x = text_area_width * (1 - hscroll_step_rel)
}
hscroll = max (hscroll, XFASTINT (w->min_hscroll));
- /* Don't call Fset_window_hscroll if value hasn't
- changed because it will prevent redisplay
- optimizations. */
+ /* Don't prevent redisplay optimizations if hscroll
+ hasn't changed, as it will unnecessarily slow down
+ redisplay. */
if (XFASTINT (w->hscroll) != hscroll)
{
XBUFFER (w->buffer)->prevent_redisplay_optimizations_p = 1;
glyph--;
}
}
- else if (match_with_avoid_cursor
- /* A truncated row may not include PT among its
- character positions. Setting the cursor inside the
- scroll margin will trigger recalculation of hscroll
- in hscroll_window_tree. */
- || (row->truncated_on_left_p && pt_old < bpos_min)
- || (row->truncated_on_right_p && pt_old > bpos_max)
- /* Zero-width characters produce no glyphs. */
- || (!string_seen
- && !empty_line_p
- && (row->reversed_p
- ? glyph_after > glyphs_end
- : glyph_after < glyphs_end)))
+ else if (match_with_avoid_cursor)
{
cursor = glyph_after;
x = -1;
x = -1;
+ /* If the row ends in a newline from a display string,
+ reordering could have moved the glyphs belonging to the
+ string out of the [GLYPH_BEFORE..GLYPH_AFTER] range. So
+ in this case we extend the search to the last glyph in
+ the row that was not inserted by redisplay. */
+ if (row->ends_in_newline_from_string_p)
+ {
+ glyph_after = end;
+ pos_after = MATRIX_ROW_END_CHARPOS (row) + delta;
+ }
+
/* 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->continued_p)
return 0;
}
+ /* A truncated row may not include PT among its character positions.
+ Setting the cursor inside the scroll margin will trigger
+ recalculation of hscroll in hscroll_window_tree. But if a
+ display string covers point, defer to the string-handling
+ code below to figure this out. */
+ else if (row->truncated_on_left_p && pt_old < bpos_min)
+ {
+ cursor = glyph_before;
+ x = -1;
+ }
+ else if ((row->truncated_on_right_p && pt_old > bpos_max)
+ /* Zero-width characters produce no glyphs. */
+ || (!empty_line_p
+ && (row->reversed_p
+ ? glyph_after > glyphs_end
+ : glyph_after < glyphs_end)))
+ {
+ cursor = glyph_after;
+ x = -1;
+ }
}
compute_x:
is set, we are done. */
at_zv_p =
MATRIX_ROW (w->current_matrix, w->cursor.vpos)->ends_at_zv_p;
- if (!at_zv_p)
+ if (rv && !at_zv_p
+ && w->cursor.hpos >= 0
+ && w->cursor.hpos < MATRIX_ROW_USED (w->current_matrix,
+ w->cursor.vpos))
{
struct glyph_row *candidate =
MATRIX_ROW (w->current_matrix, w->cursor.vpos);
if (row < bottom_row)
{
struct glyph *glyph = row->glyphs[TEXT_AREA] + w->cursor.hpos;
- struct glyph *end = glyph + row->used[TEXT_AREA];
+ struct glyph *end = row->glyphs[TEXT_AREA] + row->used[TEXT_AREA];
/* Can't use this optimization with bidi-reordered glyph
rows, unless cursor is already at point. */
{
int this_scroll_margin, cursor_height;
- this_scroll_margin = max (0, scroll_margin);
- this_scroll_margin = min (this_scroll_margin, WINDOW_TOTAL_LINES (w) / 4);
+ this_scroll_margin =
+ max (0, min (scroll_margin, WINDOW_TOTAL_LINES (w) / 4));
this_scroll_margin *= FRAME_LINE_HEIGHT (it.f);
cursor_height = MATRIX_ROW (w->desired_matrix, w->cursor.vpos)->height;
push_display_prop (struct it *it, Lisp_Object prop)
{
struct text_pos pos =
- (it->method == GET_FROM_STRING) ? it->current.string_pos : it->current.pos;
+ STRINGP (it->string) ? it->current.string_pos : it->current.pos;
xassert (it->method == GET_FROM_BUFFER
+ || it->method == GET_FROM_DISPLAY_VECTOR
|| it->method == GET_FROM_STRING);
/* We need to save the current buffer/string position, so it will be
Line ends in a newline from buffer eol_pos + 1
Line is continued from buffer max_pos + 1
Line is truncated on right it->current.pos
- Line ends in a newline from string max_pos
+ Line ends in a newline from string max_pos + 1(*)
+ (*) + 1 only when line ends in a forward scan
Line is continued from string max_pos
Line is continued from display vector max_pos
Line is entirely from a string min_pos == max_pos
row->maxpos = it->current.pos;
else if (row->used[TEXT_AREA])
{
- if (row->ends_in_newline_from_string_p)
- SET_TEXT_POS (row->maxpos, max_pos, max_bpos);
+ int seen_this_string = 0;
+ struct glyph_row *r1 = row - 1;
+
+ /* Did we see the same display string on the previous row? */
+ if (STRINGP (it->object)
+ /* this is not the first row */
+ && row > it->w->desired_matrix->rows
+ /* previous row is not the header line */
+ && !r1->mode_line_p
+ /* previous row also ends in a newline from a string */
+ && r1->ends_in_newline_from_string_p)
+ {
+ struct glyph *start, *end;
+
+ /* Search for the last glyph of the previous row that came
+ from buffer or string. Depending on whether the row is
+ L2R or R2L, we need to process it front to back or the
+ other way round. */
+ if (!r1->reversed_p)
+ {
+ start = r1->glyphs[TEXT_AREA];
+ end = start + r1->used[TEXT_AREA];
+ /* Glyphs inserted by redisplay have an integer (zero)
+ as their object. */
+ while (end > start
+ && INTEGERP ((end - 1)->object)
+ && (end - 1)->charpos <= 0)
+ --end;
+ if (end > start)
+ {
+ if (EQ ((end - 1)->object, it->object))
+ seen_this_string = 1;
+ }
+ else
+ /* If all the glyphs of the previous row were inserted
+ by redisplay, it means the previous row was
+ produced from a single newline, which is only
+ possible if that newline came from the same string
+ as the one which produced this ROW. */
+ seen_this_string = 1;
+ }
+ else
+ {
+ end = r1->glyphs[TEXT_AREA] - 1;
+ start = end + r1->used[TEXT_AREA];
+ while (end < start
+ && INTEGERP ((end + 1)->object)
+ && (end + 1)->charpos <= 0)
+ ++end;
+ if (end < start)
+ {
+ if (EQ ((end + 1)->object, it->object))
+ seen_this_string = 1;
+ }
+ else
+ seen_this_string = 1;
+ }
+ }
+ /* Take note of each display string that covers a newline only
+ once, the first time we see it. This is for when a display
+ string includes more than one newline in it. */
+ if (row->ends_in_newline_from_string_p && !seen_this_string)
+ {
+ /* If we were scanning the buffer forward when we displayed
+ the string, we want to account for at least one buffer
+ position that belongs to this row (position covered by
+ the display string), so that cursor positioning will
+ consider this row as a candidate when point is at the end
+ of the visual line represented by this row. This is not
+ required when scanning back, because max_pos will already
+ have a much larger value. */
+ if (CHARPOS (row->end.pos) > max_pos)
+ INC_BOTH (max_pos, max_bpos);
+ SET_TEXT_POS (row->maxpos, max_pos, max_bpos);
+ }
else if (CHARPOS (it->eol_pos) > 0)
SET_TEXT_POS (row->maxpos,
CHARPOS (it->eol_pos) + 1, BYTEPOS (it->eol_pos) + 1);
it->current_x = new_x;
it->continuation_lines_width += new_x;
++it->hpos;
- /* Record the maximum and minimum buffer
- positions seen so far in glyphs that will be
- displayed by this row. */
- if (it->bidi_p)
- RECORD_MAX_MIN_POS (it);
if (i == nglyphs - 1)
{
/* If line-wrap is on, check if a previous
|| IT_DISPLAYING_WHITESPACE (it)))
goto back_to_wrap;
+ /* Record the maximum and minimum buffer
+ positions seen so far in glyphs that will be
+ displayed by this row. */
+ if (it->bidi_p)
+ RECORD_MAX_MIN_POS (it);
set_iterator_to_next (it, 1);
if (IT_OVERFLOW_NEWLINE_INTO_FRINGE (it))
{
}
}
}
+ else if (it->bidi_p)
+ RECORD_MAX_MIN_POS (it);
}
else if (CHAR_GLYPH_PADDING_P (*glyph)
&& !FRAME_WINDOW_P (it->f))
xassert (it->first_visible_x <= it->last_visible_x);
}
}
+ /* Even if this display element produced no glyphs at all,
+ we want to record its position. */
+ if (it->bidi_p && nglyphs == 0)
+ RECORD_MAX_MIN_POS (it);
row->ascent = max (row->ascent, it->max_ascent);
row->height = max (row->height, it->max_ascent + it->max_descent);
EMACS_INT pos = BUF_PT (buf);
EMACS_INT bytepos = BUF_PT_BYTE (buf);
int c;
+ void *itb_data = bidi_shelve_cache ();
set_buffer_temp (buf);
/* bidi_paragraph_init finds the base direction of the paragraph
pos--;
bytepos = CHAR_TO_BYTE (pos);
}
- while ((c = FETCH_BYTE (bytepos)) == '\n'
- || c == ' ' || c == '\t' || c == '\f')
+ if (fast_looking_at (build_string ("[\f\t ]*\n"),
+ pos, bytepos, ZV, ZV_BYTE, Qnil) > 0)
{
- if (bytepos <= BEGV_BYTE)
- break;
- bytepos--;
- pos--;
+ while ((c = FETCH_BYTE (bytepos)) == '\n'
+ || c == ' ' || c == '\t' || c == '\f')
+ {
+ if (bytepos <= BEGV_BYTE)
+ break;
+ bytepos--;
+ pos--;
+ }
+ while (!CHAR_HEAD_P (FETCH_BYTE (bytepos)))
+ bytepos--;
}
- while (!CHAR_HEAD_P (FETCH_BYTE (bytepos)))
- bytepos--;
- itb.charpos = pos;
- itb.bytepos = bytepos;
- itb.nchars = -1;
+ bidi_init_it (pos, bytepos, FRAME_WINDOW_P (SELECTED_FRAME ()), &itb);
+ itb.paragraph_dir = NEUTRAL_DIR;
itb.string.s = NULL;
itb.string.lstring = Qnil;
- itb.frame_window_p = FRAME_WINDOW_P (SELECTED_FRAME ()); /* guesswork */
- itb.first_elt = 1;
- itb.separator_limit = -1;
- itb.paragraph_dir = NEUTRAL_DIR;
-
+ itb.string.bufpos = 0;
+ itb.string.unibyte = 0;
bidi_paragraph_init (NEUTRAL_DIR, &itb, 1);
+ bidi_unshelve_cache (itb_data, 0);
set_buffer_temp (old);
switch (itb.paragraph_dir)
{
? XFLOATINT (X) \
: - 1)
-int
+static int
calc_pixel_width_or_height (double *res, struct it *it, Lisp_Object prop,
struct font *font, int width_p, int *align_to)
{
{
int c = COMPOSITION_GLYPH (s->cmp, i);
+ /* TAB in a composition means display glyphs with padding space
+ on the left or right. */
if (c != '\t')
{
int face_id = FACE_FOR_CHAR (s->f, base_face->ascii_face, c,
if (width > 0 && it->line_wrap != TRUNCATE
&& it->current_x + width > it->last_visible_x)
- width = it->last_visible_x - it->current_x - 1;
+ {
+ width = it->last_visible_x - it->current_x;
+#ifdef HAVE_WINDOW_SYSTEM
+ /* Subtact one more pixel from the stretch width, but only on
+ GUI frames, since on a TTY each glyph is one "pixel" wide. */
+ width -= FRAME_WINDOW_P (it->f);
+#endif
+ }
if (width > 0 && height > 0 && it->glyph_row)
{
object = it->w->buffer;
#ifdef HAVE_WINDOW_SYSTEM
if (FRAME_WINDOW_P (it->f))
- {
- append_stretch_glyph (it, object, width, height, ascent);
- it->ascent = it->phys_ascent = ascent;
- it->descent = it->phys_descent = height - it->ascent;
- it->nglyphs = width > 0 && height > 0 ? 1 : 0;
- take_vertical_position_into_account (it);
- }
+ append_stretch_glyph (it, object, width, height, ascent);
else
#endif
{
while (n--)
tty_append_glyph (it);
it->object = o_object;
- it->pixel_width = width;
}
}
+
+ it->pixel_width = width;
+#ifdef HAVE_WINDOW_SYSTEM
+ if (FRAME_WINDOW_P (it->f))
+ {
+ it->ascent = it->phys_ascent = ascent;
+ it->descent = it->phys_descent = height - it->ascent;
+ it->nglyphs = width > 0 && height > 0 ? 1 : 0;
+ take_vertical_position_into_account (it);
+ }
+ else
+#endif
+ it->nglyphs = width;
}
#ifdef HAVE_WINDOW_SYSTEM
{
int yb = window_text_bottom_y (w);
struct glyph_row *row;
- int cursor_cleared_p;
+ int cursor_cleared_p, phys_cursor_on_p;
struct glyph_row *first_overlapping_row, *last_overlapping_row;
TRACE ((stderr, "expose_window (%d, %d, %d, %d)\n",
else
cursor_cleared_p = 0;
+ /* If the row containing the cursor extends face to end of line,
+ then expose_area might overwrite the cursor outside the
+ rectangle and thus notice_overwritten_cursor might clear
+ w->phys_cursor_on_p. We remember the original value and
+ check later if it is changed. */
+ phys_cursor_on_p = w->phys_cursor_on_p;
+
/* Update lines intersecting rectangle R. */
first_overlapping_row = last_overlapping_row = NULL;
for (row = w->current_matrix->rows;
x_draw_vertical_border (w);
/* Turn the cursor on again. */
- if (cursor_cleared_p)
+ if (cursor_cleared_p
+ || (phys_cursor_on_p && !w->phys_cursor_on_p))
update_window_cursor (w, 1);
}
}
Vshow_trailing_whitespace = Qnil;
DEFVAR_LISP ("nobreak-char-display", Vnobreak_char_display,
- doc: /* *Control highlighting of nobreak space and soft hyphen.
-A value of t means highlight the character itself (for nobreak space,
-use face `nobreak-space').
-A value of nil means no highlighting.
-Other values mean display the escape glyph followed by an ordinary
-space or ordinary hyphen. */);
+ doc: /* Control highlighting of non-ASCII space and hyphen chars.
+If the value is t, Emacs highlights non-ASCII chars which have the
+same appearance as an ASCII space or hyphen, using the `nobreak-space'
+or `escape-glyph' face respectively.
+
+U+00A0 (no-break space), U+00AD (soft hyphen), U+2010 (hyphen), and
+U+2011 (non-breaking hyphen) are affected.
+
+Any other non-nil value means to display these characters as a escape
+glyph followed by an ordinary space or hyphen.
+
+A value of nil means no special handling of these characters. */);
Vnobreak_char_display = Qt;
DEFVAR_LISP ("void-text-area-pointer", Vvoid_text_area_pointer,