# HG changeset patch # User Gerd Moellmann # Date 971442576 0 # Node ID 5d2167f8c6a6c698ac7ce959682b208afc875af1 # Parent bf88937fd880ed6d03469a3391d3f4510d5b252b (cursor_row_p): New function. (try_cursor_movement, display_line): Use it. (append_space): Also save/restore iterator's current character and its length. (init_from_display_pos): Add an assertion. (handle_stop): Don't set iterator's add_overlay_start. (handle_invisible_prop): Likewise. (load_overlay_strings): If text under an overlay is invisible, take both before- and after-strings into account when the iterator is positioned either at the start or at the end of the overlay. (forward_to_next_line_start): Rewritten. (reseat_at_next_visible_line_start): Rewritten. (set_iterator_to_next): Add parameter RESEAT_P. diff -r bf88937fd880 -r 5d2167f8c6a6 src/xdisp.c --- a/src/xdisp.c Fri Oct 13 13:08:45 2000 +0000 +++ b/src/xdisp.c Fri Oct 13 13:09:36 2000 +0000 @@ -628,6 +628,7 @@ /* Function prototypes. */ +static int cursor_row_p P_ ((struct window *, struct glyph_row *)); static int redisplay_mode_lines P_ ((Lisp_Object, int)); static char *decode_mode_spec_coding P_ ((Lisp_Object, char *, int)); static int invisible_text_between_p P_ ((struct it *, int, int)); @@ -700,7 +701,6 @@ static void run_redisplay_end_trigger_hook P_ ((struct it *)); static int get_overlay_strings P_ ((struct it *)); static void next_overlay_string P_ ((struct it *)); -void set_iterator_to_next P_ ((struct it *)); static void reseat P_ ((struct it *, struct text_pos, int)); static void reseat_1 P_ ((struct it *, struct text_pos, int)); static void back_to_previous_visible_line_start P_ ((struct it *)); @@ -726,7 +726,7 @@ static void init_to_row_end P_ ((struct it *, struct window *, struct glyph_row *)); static void back_to_previous_line_start P_ ((struct it *)); -static void forward_to_next_line_start P_ ((struct it *)); +static int forward_to_next_line_start P_ ((struct it *, int *)); static struct text_pos string_pos_nchars_ahead P_ ((struct text_pos, Lisp_Object, int)); static struct text_pos string_pos P_ ((int, Lisp_Object)); @@ -1530,7 +1530,7 @@ if (it->current.dpvec_index >= 0 || it->current.overlay_string_index >= 0) { - set_iterator_to_next (it); + set_iterator_to_next (it, 1); move_it_in_display_line_to (it, -1, -1, 0); } @@ -1607,6 +1607,7 @@ relative_index = (it->current.overlay_string_index % OVERLAY_STRING_CHUNK_SIZE); it->string = it->overlay_strings[relative_index]; + xassert (STRINGP (it->string)); it->current.string_pos = pos->string_pos; it->method = next_element_from_string; } @@ -1686,7 +1687,6 @@ it->dpvec = NULL; it->current.dpvec_index = -1; - it->add_overlay_start = 0; do { @@ -2245,7 +2245,6 @@ = TEXT_PROP_MEANS_INVISIBLE_WITH_ELLIPSIS (prop); handled = HANDLED_RECOMPUTE_PROPS; - it->add_overlay_start = IT_CHARPOS (*it); /* Loop skipping over invisible text. The loop is left at ZV or with IT on the first char being visible again. */ @@ -2976,12 +2975,13 @@ struct it *it; { extern Lisp_Object Qafter_string, Qbefore_string, Qwindow, Qpriority; - Lisp_Object ov, overlay, window, str; + Lisp_Object ov, overlay, window, str, invisible; int start, end; int size = 20; - int n = 0, i, j; + int n = 0, i, j, invis_p; struct overlay_entry *entries = (struct overlay_entry *) alloca (size * sizeof *entries); + int charpos = IT_CHARPOS (*it); /* Append the overlay string STRING of overlay OVERLAY to vector `entries' which has size `size' and currently contains `n' @@ -3020,14 +3020,12 @@ start = OVERLAY_POSITION (OVERLAY_START (overlay)); end = OVERLAY_POSITION (OVERLAY_END (overlay)); - if (end < IT_CHARPOS (*it)) + if (end < charpos) break; /* Skip this overlay if it doesn't start or end at IT's current position. */ - if (end != IT_CHARPOS (*it) - && start != IT_CHARPOS (*it) - && it->add_overlay_start != IT_CHARPOS (*it)) + if (end != charpos && start != charpos) continue; /* Skip this overlay if it doesn't apply to IT->w. */ @@ -3035,15 +3033,20 @@ if (WINDOWP (window) && XWINDOW (window) != it->w) continue; + /* If the text ``under'' the overlay is invisible, both before- + and after-strings from this overlay are visible; start and + end position are indistinguishable. */ + invisible = Foverlay_get (overlay, Qinvisible); + invis_p = TEXT_PROP_MEANS_INVISIBLE (invisible); + /* If overlay has a non-empty before-string, record it. */ - if ((start == IT_CHARPOS (*it) - || start == it->add_overlay_start) + if ((start == charpos || (end == charpos && invis_p)) && (str = Foverlay_get (overlay, Qbefore_string), STRINGP (str)) && XSTRING (str)->size) RECORD_OVERLAY_STRING (overlay, str, 0); /* If overlay has a non-empty after-string, record it. */ - if (end == IT_CHARPOS (*it) + if ((end == charpos || (start == charpos && invis_p)) && (str = Foverlay_get (overlay, Qafter_string), STRINGP (str)) && XSTRING (str)->size) RECORD_OVERLAY_STRING (overlay, str, 1); @@ -3057,14 +3060,12 @@ start = OVERLAY_POSITION (OVERLAY_START (overlay)); end = OVERLAY_POSITION (OVERLAY_END (overlay)); - if (start > IT_CHARPOS (*it)) + if (start > charpos) break; /* Skip this overlay if it doesn't start or end at IT's current position. */ - if (end != IT_CHARPOS (*it) - && start != IT_CHARPOS (*it) - && it->add_overlay_start != IT_CHARPOS (*it)) + if (end != charpos && start != charpos) continue; /* Skip this overlay if it doesn't apply to IT->w. */ @@ -3072,15 +3073,19 @@ if (WINDOWP (window) && XWINDOW (window) != it->w) continue; + /* If the text ``under'' the overlay is invisible, it has a zero + dimension, and both before- and after-strings apply. */ + invisible = Foverlay_get (overlay, Qinvisible); + invis_p = TEXT_PROP_MEANS_INVISIBLE (invisible); + /* If overlay has a non-empty before-string, record it. */ - if ((start == IT_CHARPOS (*it) - || start == it->add_overlay_start) + if ((start == charpos || (end == charpos && invis_p)) && (str = Foverlay_get (overlay, Qbefore_string), STRINGP (str)) && XSTRING (str)->size) RECORD_OVERLAY_STRING (overlay, str, 0); /* If overlay has a non-empty after-string, record it. */ - if (end == IT_CHARPOS (*it) + if ((end == charpos || (start == charpos && invis_p)) && (str = Foverlay_get (overlay, Qafter_string), STRINGP (str)) && XSTRING (str)->size) RECORD_OVERLAY_STRING (overlay, str, 1); @@ -3089,7 +3094,7 @@ #undef RECORD_OVERLAY_STRING /* Sort entries. */ - if (n) + if (n > 1) qsort (entries, n, sizeof *entries, compare_overlay_entries); /* Record the total number of strings to process. */ @@ -3246,14 +3251,74 @@ } -/* Set IT's current position to the next line start. */ - -static void -forward_to_next_line_start (it) +/* Move IT to the next line start. + + Value is non-zero if a newline was found. Set *SKIPPED_P to 1 if + we skipped over part of the text (as opposed to moving the iterator + continuously over the text). Otherwise, don't change the value + of *SKIPPED_P. + + Newlines may come from buffer text, overlay strings, or strings + displayed via the `display' property. That's the reason we can't + simply use find_next_newline_no_quit. */ + +static int +forward_to_next_line_start (it, skipped_p) struct it *it; -{ - IT_CHARPOS (*it) = find_next_newline_no_quit (IT_CHARPOS (*it), 1); - IT_BYTEPOS (*it) = CHAR_TO_BYTE (IT_CHARPOS (*it)); + int *skipped_p; +{ + int newline_found_p, n; + const int MAX_NEWLINE_DISTANCE = 500; + + /* Scan for a newline within MAX_NEWLINE_DISTANCE display elements + from buffer text. */ + n = newline_found_p = 0; + while (n < MAX_NEWLINE_DISTANCE + && get_next_display_element (it) + && !newline_found_p) + { + newline_found_p = ITERATOR_AT_END_OF_LINE_P (it); + set_iterator_to_next (it, 0); + if (!STRINGP (it->string)) + ++n; + } + + /* If we didn't find a newline near enough, see if we can use a + short-cut. */ + if (!newline_found_p && n == MAX_NEWLINE_DISTANCE) + { + int start = IT_CHARPOS (*it); + int limit = find_next_newline_no_quit (start, 1); + Lisp_Object pos; + + xassert (!STRINGP (it->string)); + + /* If there isn't any `display' property in sight, and no + overlays, we can just use the position of the newline in + buffer text. */ + if (it->stop_charpos >= limit + || ((pos = Fnext_single_property_change (make_number (start), + Qdisplay, + Qnil, make_number (limit)), + NILP (pos)) + && next_overlay_change (start) == ZV)) + { + IT_CHARPOS (*it) = limit; + IT_BYTEPOS (*it) = CHAR_TO_BYTE (limit); + *skipped_p = newline_found_p = 1; + } + else + { + while (get_next_display_element (it) + && !newline_found_p) + { + newline_found_p = ITERATOR_AT_END_OF_LINE_P (it); + set_iterator_to_next (it, 0); + } + } + } + + return newline_found_p; } @@ -3334,56 +3399,38 @@ struct it *it; int on_newline_p; { - /* Restore the buffer position when currently not delivering display - elements from the current buffer. This is the case, for example, - when called at the end of a truncated overlay string. */ - while (it->sp) - pop_it (it); - it->method = next_element_from_buffer; - - /* Otherwise, scan_buffer would not work. */ - if (IT_CHARPOS (*it) < ZV) - { - /* If on a newline, advance past it. Otherwise, find the next - newline which automatically gives us the position following - the newline. */ - if (FETCH_BYTE (IT_BYTEPOS (*it)) == '\n') - { - ++IT_CHARPOS (*it); - ++IT_BYTEPOS (*it); - } - else - forward_to_next_line_start (it); - - /* We must either have reached the end of the buffer or end up - after a newline. */ - xassert (IT_CHARPOS (*it) == ZV - || FETCH_BYTE (IT_BYTEPOS (*it) - 1) == '\n'); - - /* Skip over lines that are invisible because they are indented - more than the value of IT->selective. */ - if (it->selective > 0) - while (IT_CHARPOS (*it) < ZV - && indented_beyond_p (IT_CHARPOS (*it), IT_BYTEPOS (*it), - it->selective)) - forward_to_next_line_start (it); - - /* Position on the newline if we should. */ - if (on_newline_p - && IT_CHARPOS (*it) > BEGV - && FETCH_BYTE (IT_BYTEPOS (*it) - 1) == '\n') + int newline_found_p, skipped_p = 0; + + newline_found_p = forward_to_next_line_start (it, &skipped_p); + + /* Skip over lines that are invisible because they are indented + more than the value of IT->selective. */ + if (it->selective > 0) + while (IT_CHARPOS (*it) < ZV + && indented_beyond_p (IT_CHARPOS (*it), IT_BYTEPOS (*it), + it->selective)) + newline_found_p = forward_to_next_line_start (it, &skipped_p); + + /* Position on the newline if that's what's requested. */ + if (on_newline_p && newline_found_p) + { + if (STRINGP (it->string)) + { + if (IT_STRING_CHARPOS (*it) > 0) + { + --IT_STRING_CHARPOS (*it); + --IT_STRING_BYTEPOS (*it); + } + } + else if (IT_CHARPOS (*it) > BEGV) { --IT_CHARPOS (*it); - IT_BYTEPOS (*it) = CHAR_TO_BYTE (IT_CHARPOS (*it)); - } - - /* Set the iterator there. The 0 as the last parameter of - reseat means don't force a text property lookup. The lookup - is then only done if we've skipped past the iterator's - check_charpos'es. This optimization is important because - text property lookups tend to be expensive. */ - reseat (it, it->current.pos, 0); - } + --IT_BYTEPOS (*it); + reseat (it, it->current.pos, 0); + } + } + else if (skipped_p) + reseat (it, it->current.pos, 0); CHECK_IT (it); } @@ -3724,6 +3771,9 @@ /* Move IT to the next display element. + RESEAT_P non-zero means if called on a newline in buffer text, + skip to the next visible line start. + Functions get_next_display_element and set_iterator_to_next are separate because I find this arrangement easier to handle than a get_next_display_element function that also increments IT's @@ -3735,15 +3785,16 @@ decrement position function which would not be easy to write. */ void -set_iterator_to_next (it) +set_iterator_to_next (it, reseat_p) struct it *it; + int reseat_p; { if (it->method == next_element_from_buffer) { /* The current display element of IT is a character from current_buffer. Advance in the buffer, and maybe skip over invisible lines that are so because of selective display. */ - if (ITERATOR_AT_END_OF_LINE_P (it)) + if (ITERATOR_AT_END_OF_LINE_P (it) && reseat_p) reseat_at_next_visible_line_start (it, 0); else { @@ -3806,7 +3857,7 @@ else if (it->dpvec_char_len > 0) { it->len = it->dpvec_char_len; - set_iterator_to_next (it); + set_iterator_to_next (it, reseat_p); } } } @@ -4365,7 +4416,7 @@ if (it->area != TEXT_AREA) { - set_iterator_to_next (it); + set_iterator_to_next (it, 1); continue; } @@ -4423,7 +4474,7 @@ ++it->hpos; it->current_x = new_x; if (i == it->nglyphs - 1) - set_iterator_to_next (it); + set_iterator_to_next (it, 1); } else { @@ -4473,7 +4524,7 @@ /* The current display element has been consumed. Advance to the next. */ - set_iterator_to_next (it); + set_iterator_to_next (it, 1); /* Stop if lines are truncated and IT's current x-position is past the right edge of the window now. */ @@ -4632,7 +4683,7 @@ goto out; case MOVE_NEWLINE_OR_CR: - set_iterator_to_next (it); + set_iterator_to_next (it, 1); it->continuation_lines_width = 0; break; @@ -6998,7 +7049,7 @@ if (ITERATOR_AT_END_OF_LINE_P (it)) break; - set_iterator_to_next (it); + set_iterator_to_next (it, 1); } out:; @@ -8904,17 +8955,11 @@ /* The end position of a row equals the start position of the next row. If PT is there, we would rather - display it in the next line. Exceptions are when the - row ends in the middle of a character, or ends in - ZV. */ - if (MATRIX_ROW_BOTTOM_Y (row) < last_y - && MATRIX_ROW_END_CHARPOS (row) == PT - && !MATRIX_ROW_ENDS_IN_MIDDLE_OF_CHAR_P (row) - && !row->ends_at_zv_p) - { - xassert (row->enabled_p); - ++row; - } + display it in the next line. */ + while (MATRIX_ROW_BOTTOM_Y (row) < last_y + && MATRIX_ROW_END_CHARPOS (row) == PT + && !cursor_row_p (w, row)) + ++row; /* If within the scroll margin, scroll. Note that MATRIX_ROW_BOTTOM_Y gives the pixel position at which @@ -11394,11 +11439,15 @@ if (it->glyph_row->glyphs[TEXT_AREA] + n < it->glyph_row->glyphs[1 + TEXT_AREA]) { - /* Save some values that must not be changed. */ + /* Save some values that must not be changed. + Must save IT->c and IT->len because otherwise + ITERATOR_AT_END_P wouldn't work anymore after + append_space has been called. */ + int saved_what = it->what; + int saved_c = it->c, saved_len = it->len; int saved_x = it->current_x; + int saved_face_id = it->face_id; struct text_pos saved_pos; - int saved_what = it->what; - int saved_face_id = it->face_id; Lisp_Object saved_object; struct face *face; @@ -11423,6 +11472,8 @@ it->position = saved_pos; it->what = saved_what; it->face_id = saved_face_id; + it->len = saved_len; + it->c = saved_c; return 1; } } @@ -11581,6 +11632,38 @@ } +/* Value is non-zero if glyph row ROW in window W should be + used to put the cursor on. */ + +static int +cursor_row_p (w, row) + struct window *w; + struct glyph_row *row; +{ + if (PT == MATRIX_ROW_END_CHARPOS (row)) + { + /* If PT is at the end of ROW, and that row ends with a + newline from a string, we don't want the cursor there. */ + if (row->end.overlay_string_index >= 0 + && ((truncate_partial_width_windows + && !WINDOW_FULL_WIDTH_P (w)) + || !NILP (current_buffer->truncate_lines))) + return 0; + + /* If PT is at the end of ROW, we normally want the cursor + at the start of the row below, except when ROW ends + at ZV or in the middle of a character. */ + if (MATRIX_ROW_ENDS_IN_MIDDLE_OF_CHAR_P (row) + || row->ends_at_zv_p) + return 1; + + return 0; + } + + return 1; +} + + /* Construct the glyph row IT->glyph_row in the desired matrix of IT->w from text at the current position of IT. See dispextern.h for an overview of struct it. Value is non-zero if @@ -11691,7 +11774,7 @@ row->phys_ascent = max (row->phys_ascent, it->max_phys_ascent); row->phys_height = max (row->phys_height, it->max_phys_ascent + it->max_phys_descent); - set_iterator_to_next (it); + set_iterator_to_next (it, 1); continue; } @@ -11753,7 +11836,7 @@ it->continuation_lines_width += new_x; ++it->hpos; if (i == nglyphs - 1) - set_iterator_to_next (it); + set_iterator_to_next (it, 1); } else if (CHAR_GLYPH_PADDING_P (*glyph) && !FRAME_WINDOW_P (it->f)) @@ -11862,14 +11945,14 @@ row->glyphs[TEXT_AREA]->charpos = CHARPOS (it->position); /* Consume the line end. This skips over invisible lines. */ - set_iterator_to_next (it); + set_iterator_to_next (it, 1); it->continuation_lines_width = 0; break; } /* Proceed with next display element. Note that this skips over lines invisible because of selective display. */ - set_iterator_to_next (it); + set_iterator_to_next (it, 1); /* If we truncate lines, we are done when the last displayed glyphs reach past the right margin of the window. */ @@ -11955,17 +12038,9 @@ /* Maybe set the cursor. */ if (it->w->cursor.vpos < 0 && PT >= MATRIX_ROW_START_CHARPOS (row) - && PT <= MATRIX_ROW_END_CHARPOS (row)) - { - /* Also see redisplay_window, case cursor movement in unchanged - window. */ - if (MATRIX_ROW_END_CHARPOS (row) == PT - && !MATRIX_ROW_ENDS_IN_MIDDLE_OF_CHAR_P (row) - && !row->ends_at_zv_p) - ; - else - set_cursor_from_row (it->w, row, it->w->desired_matrix, 0, 0, 0, 0); - } + && PT <= MATRIX_ROW_END_CHARPOS (row) + && cursor_row_p (it->w, row)) + set_cursor_from_row (it->w, row, it->w->desired_matrix, 0, 0, 0, 0); /* Highlight trailing whitespace. */ if (!NILP (Vshow_trailing_whitespace)) @@ -13260,7 +13335,7 @@ break; } - set_iterator_to_next (it); + set_iterator_to_next (it, 1); /* Stop if truncating at the right edge. */ if (it->truncate_lines_p