# HG changeset patch # User Jim Blandy # Date 781654444 0 # Node ID 4dcc0221b449a5675da8af7aa6129daf39ae865c # Parent a549857b7a1364170de85cdd3d43c9e5cf3b9e8a * indent.c: #include "region-cache.h". (character_width, disptab_matches_widthtab, recompute_width_table, width_run_cache_on_off): New functions. (compute_motion): Call width_run_cache_on_off. If this buffer's width run cache is enabled, consult it to see if we need to traverse a region character-by-character; store information in the cache after doing so. Call find_before_next_newline instead of writing out an equivalent loop explicitly, to take advantage of the newline cache. Doc fixes. diff -r a549857b7a13 -r 4dcc0221b449 src/indent.c --- a/src/indent.c Sat Oct 08 22:13:44 1994 +0000 +++ b/src/indent.c Sat Oct 08 22:14:04 1994 +0000 @@ -28,6 +28,7 @@ #include "termopts.h" #include "disptab.h" #include "intervals.h" +#include "region-cache.h" /* Indentation can insert tabs if this is non-zero; otherwise always uses spaces */ @@ -64,6 +65,112 @@ return 0; } +/* Width run cache considerations. */ + +/* Return the width of character C under display table DP. */ +static int +character_width (c, dp) + int c; + struct Lisp_Vector *dp; +{ + Lisp_Object elt; + + /* These width computations were determined by examining the cases + in display_text_line. */ + + /* Some characters are never handled by the display table. */ + if (c == '\n' || c == '\t' || c == '\015') + return 0; + + /* Everything else might be handled by the display table, if it's + present and the element is right. */ + else if (dp && (elt = DISP_CHAR_VECTOR (dp, c), + VECTORP (elt))) + return XVECTOR (elt)->size; + + /* In the absence of display table perversities, printing characters + have width 1. */ + else if (c >= 040 && c < 0177) + return 1; + + /* Everybody else (control characters, metacharacters) has other + widths. We could return their actual widths here, but they + depend on things like ctl_arrow and crud like that, and they're + not very common at all. So we'll just claim we don't know their + widths. */ + else + return 0; +} + +/* Return true iff the display table DISPTAB specifies the same widths + for characters as WIDTHTAB. We use this to decide when to + invalidate the buffer's width_run_cache. */ +int +disptab_matches_widthtab (disptab, widthtab) + struct Lisp_Vector *disptab; + struct Lisp_Vector *widthtab; +{ + int i; + + if (widthtab->size != 256) + abort (); + + for (i = 0; i < 256; i++) + if (character_width (i, disptab) + != XFASTINT (widthtab->contents[i])) + return 0; + + return 1; +} + +/* Recompute BUF's width table, using the display table DISPTAB. */ +void +recompute_width_table (buf, disptab) + struct buffer *buf; + struct Lisp_Vector *disptab; +{ + int i; + struct Lisp_Vector *widthtab + = (VECTORP (buf->width_table) + ? XVECTOR (buf->width_table) + : XVECTOR (Fmake_vector (make_number (256), make_number (0)))); + + if (widthtab->size != 256) + abort (); + + for (i = 0; i < 256; i++) + widthtab->contents[i] = character_width (i, disptab); +} + +/* Allocate or free the width run cache, as requested by the current + state of current_buffer's cache_long_line_scans variable. */ +static void +width_run_cache_on_off () +{ + if (NILP (current_buffer->cache_long_line_scans)) + { + /* It should be off. */ + if (current_buffer->width_run_cache) + { + free_region_cache (current_buffer->width_run_cache); + current_buffer->width_run_cache = 0; + current_buffer->width_table = Qnil; + } + } + else + { + /* It should be on. */ + if (current_buffer->width_run_cache == 0) + { + current_buffer->width_run_cache = new_region_cache (); + current_buffer->width_table = Fmake_vector (make_number (256), + make_number (0)); + recompute_width_table (current_buffer, buffer_display_table ()); + } + } +} + + DEFUN ("current-column", Fcurrent_column, Scurrent_column, 0, 0, 0, "Return the horizontal position of point. Beginning of line is column 0.\n\ This is calculated by adding together the widths of all the displayed\n\ @@ -173,7 +280,6 @@ return col; } - DEFUN ("indent-to", Findent_to, Sindent_to, 1, 2, "NIndent to column: ", "Indent from point with tabs and spaces until COLUMN is reached.\n\ Optional second argument MIN says always do at least MIN spaces\n\ @@ -221,6 +327,7 @@ XSETINT (col, mincol); return col; } + DEFUN ("current-indentation", Fcurrent_indentation, Scurrent_indentation, 0, 0, 0, @@ -282,6 +389,7 @@ pos = find_next_newline_no_quit (pos - 1, -1); return (position_indentation (pos) >= column); } + DEFUN ("move-to-column", Fmove_to_column, Smove_to_column, 1, 2, 0, "Move point to column COLUMN in the current line.\n\ @@ -379,13 +487,18 @@ XSETFASTINT (val, col); return val; } + +/* compute_motion: compute buffer posn given screen posn and vice versa */ + struct position val_compute_motion; /* Scan the current buffer forward from offset FROM, pretending that this is at line FROMVPOS, column FROMHPOS, until reaching buffer offset TO or line TOVPOS, column TOHPOS (whichever comes first), - and return the ending buffer position and screen location. + and return the ending buffer position and screen location. If we + can't hit the requested column exactly (because of a tab or other + multi-column character), overshoot. WIDTH is the number of columns available to display text; compute_motion uses this to handle continuation lines and such. @@ -397,8 +510,14 @@ compute_motion returns a pointer to a struct position. The bufpos member gives the buffer position at the end of the scan, and hpos - and vpos give its cartesian location. I'm not clear on what the - other members are. + and vpos give its cartesian location. prevhpos is the column at + which the character before bufpos started, and contin is non-zero + if we reached the current line by continuing the previous. + + Note that FROMHPOS and TOHPOS should be expressed in real screen + columns, taking HSCROLL and the truncation glyph at the left margin + into account. That is, beginning-of-line moves you to the hpos + -HSCROLL + (HSCROLL > 0). Note that FROMHPOS and TOHPOS should be expressed in real screen columns, taking HSCROLL and the truncation glyph at the left margin @@ -452,7 +571,7 @@ = (INTEGERP (current_buffer->selective_display) ? XINT (current_buffer->selective_display) : !NILP (current_buffer->selective_display) ? -1 : 0); - int prev_vpos, prev_hpos = 0; + int prev_vpos = vpos, prev_hpos = 0; int selective_rlen = (selective && dp && VECTORP (DISP_INVIS_VECTOR (dp)) ? XVECTOR (DISP_INVIS_VECTOR (dp))->size : 0); @@ -462,8 +581,30 @@ Lisp_Object prop, position; #endif + /* For computing runs of characters with similar widths. + Invariant: width_run_width is zero, or all the characters + from width_run_start to width_run_end have a fixed width of + width_run_width. */ + int width_run_start = from; + int width_run_end = from; + int width_run_width = 0; + Lisp_Object *width_table; + + /* The next buffer pos where we should consult the width run cache. */ + int next_width_run = from; + + width_run_cache_on_off (); + if (dp == buffer_display_table ()) + width_table = (VECTORP (current_buffer->width_table) + ? XVECTOR (current_buffer->width_table)->contents + : 0); + else + /* If the window has its own display table, we can't use the width + run cache, because that's based on the buffer's display table. */ + width_table = 0; + if (tab_width <= 0 || tab_width > 1000) tab_width = 8; - for (pos = from; pos < to; pos++) + for (pos = from; pos < to; ) { /* Stop if past the target screen position. */ if (vpos > tovpos @@ -504,74 +645,158 @@ if (pos >= to) break; #endif - c = FETCH_CHAR (pos); - if (c >= 040 && c < 0177 - && (dp == 0 || !VECTORP (DISP_CHAR_VECTOR (dp, c)))) - hpos++; - else if (c == '\t') - { - hpos += tab_width - ((hpos + tab_offset + hscroll - (hscroll > 0) - /* Add tab_width here to make sure positive. - hpos can be negative after continuation - but can't be less than -tab_width. */ - + tab_width) - % tab_width); - } - else if (c == '\n') - { - if (selective > 0 && indented_beyond_p (pos + 1, selective)) - { - /* Skip any number of invisible lines all at once */ - do - { - while (++pos < to && FETCH_CHAR (pos) != '\n'); - } - while (pos < to && indented_beyond_p (pos + 1, selective)); - pos--; /* Reread the newline on the next pass. */ - /* Allow for the " ..." that is displayed for them. */ - if (selective_rlen) - { - hpos += selective_rlen; - if (hpos >= width) - hpos = width; - } - /* We have skipped the invis text, but not the newline after. */ - } - else - { - /* A visible line. */ - vpos++; - hpos = 0; - hpos -= hscroll; - if (hscroll > 0) hpos++; /* Truncation glyph on column 0 */ - tab_offset = 0; - } - } - else if (c == CR && selective < 0) - { - /* In selective display mode, - everything from a ^M to the end of the line is invisible */ - while (pos < to && FETCH_CHAR (pos) != '\n') pos++; - /* Stop *before* the real newline. */ - pos--; - /* Allow for the " ..." that is displayed for them. */ - if (selective_rlen) - { - hpos += selective_rlen; - if (hpos >= width) - hpos = width; - } - } - else if (dp != 0 && VECTORP (DISP_CHAR_VECTOR (dp, c))) - hpos += XVECTOR (DISP_CHAR_VECTOR (dp, c))->size; + + /* Consult the width run cache to see if we can avoid inspecting + the text character-by-character. */ + if (current_buffer->width_run_cache && pos >= next_width_run) + { + int run_end; + int common_width + = region_cache_forward (current_buffer, + current_buffer->width_run_cache, + pos, &run_end); + + /* A width of zero means the character's width varies (like + a tab), is meaningless (like a newline), or we just don't + want to skip over it for some other reason. */ + if (common_width != 0) + { + int run_end_hpos; + + /* Don't go past the final buffer posn the user + requested. */ + if (run_end > to) + run_end = to; + + run_end_hpos = hpos + (run_end - pos) * common_width; + + /* Don't go past the final horizontal position the user + requested. */ + if (vpos == tovpos && run_end_hpos > tohpos) + { + run_end = pos + (tohpos - hpos) / common_width; + run_end_hpos = hpos + (run_end - pos) * common_width; + } + + /* Don't go past the margin. */ + if (run_end_hpos >= width) + { + run_end = pos + (width - hpos) / common_width; + run_end_hpos = hpos + (run_end - pos) * common_width; + } + + hpos = run_end_hpos; + if (run_end > pos) + prev_hpos = hpos - common_width; + pos = run_end; + } + + next_width_run = run_end + 1; + } + + /* We have to scan the text character-by-character. */ else - hpos += (ctl_arrow && c < 0200) ? 2 : 4; + { + c = FETCH_CHAR (pos); + pos++; + + /* Perhaps add some info to the width_run_cache. */ + if (current_buffer->width_run_cache) + { + /* Is this character part of the current run? If so, extend + the run. */ + if (pos - 1 == width_run_end + && width_table[c] == width_run_width) + width_run_end = pos; + + /* The previous run is over, since this is a character at a + different position, or a different width. */ + else + { + /* Have we accumulated a run to put in the cache? + (Currently, we only cache runs of width == 1. */ + if (width_run_start < width_run_end + && width_run_width == 1) + know_region_cache (current_buffer, + current_buffer->width_run_cache, + width_run_start, width_run_end); + + /* Start recording a new width run. */ + width_run_width = width_table[c]; + width_run_start = pos - 1; + width_run_end = pos; + } + } + + if (c >= 040 && c < 0177 + && (dp == 0 || ! VECTORP (DISP_CHAR_VECTOR (dp, c)))) + hpos++; + else if (c == '\t') + { + hpos += tab_width - ((hpos + tab_offset + hscroll - (hscroll > 0) + /* Add tab_width here to make sure + positive. hpos can be negative + after continuation but can't be + less than -tab_width. */ + + tab_width) + % tab_width); + } + else if (c == '\n') + { + if (selective > 0 && indented_beyond_p (pos, selective)) + { + /* Skip any number of invisible lines all at once */ + do + pos = find_before_next_newline (pos, to, 1); + while (pos < to + && indented_beyond_p (pos, selective)); + /* Allow for the " ..." that is displayed for them. */ + if (selective_rlen) + { + hpos += selective_rlen; + if (hpos >= width) + hpos = width; + } + /* We have skipped the invis text, but not the + newline after. */ + } + else + { + /* A visible line. */ + vpos++; + hpos = 0; + hpos -= hscroll; + /* Count the truncation glyph on column 0 */ + if (hscroll > 0) + hpos++; + tab_offset = 0; + } + } + else if (c == CR && selective < 0) + { + /* In selective display mode, + everything from a ^M to the end of the line is invisible. + Stop *before* the real newline. */ + pos = find_before_next_newline (pos, to, 1); + /* Allow for the " ..." that is displayed for them. */ + if (selective_rlen) + { + hpos += selective_rlen; + if (hpos >= width) + hpos = width; + } + } + else if (dp != 0 && VECTORP (DISP_CHAR_VECTOR (dp, c))) + hpos += XVECTOR (DISP_CHAR_VECTOR (dp, c))->size; + else + hpos += (ctl_arrow && c < 0200) ? 2 : 4; + } /* Handle right margin. */ if (hpos >= width && (hpos > width - || (pos < ZV - 1 - && FETCH_CHAR (pos + 1) != '\n'))) + || (pos < ZV + && FETCH_CHAR (pos) != '\n'))) { if (vpos > tovpos || (vpos == tovpos && hpos >= tohpos)) @@ -582,8 +807,7 @@ || !NILP (current_buffer->truncate_lines)) { /* Truncating: skip to newline. */ - while (pos < to && FETCH_CHAR (pos) != '\n') pos++; - pos--; + pos = find_before_next_newline (pos, to, 1); hpos = width; } else @@ -597,6 +821,13 @@ } } + /* Remember any final width run in the cache. */ + if (current_buffer->width_run_cache + && width_run_width == 1 + && width_run_start < width_run_end) + know_region_cache (current_buffer, current_buffer->width_run_cache, + width_run_start, width_run_end); + val_compute_motion.bufpos = pos; val_compute_motion.hpos = hpos; val_compute_motion.vpos = vpos; @@ -710,8 +941,8 @@ } -/* Return the column of position POS in window W's buffer, - rounded down to a multiple of the internal width of W. +/* Return the column of position POS in window W's buffer. + The result is rounded down to a multiple of the internal width of W. This is the amount of indentation of position POS that is not visible in its horizontal position in the window. */ @@ -732,10 +963,8 @@ return col - (col % width); } -/* start_hpos is the hpos of the first character of the buffer: - zero except for the minibuffer window, - where it is the width of the prompt. */ - + +/* Fvertical_motion and vmotion */ struct position val_vmotion; struct position * @@ -884,6 +1113,8 @@ return make_number (pos.vpos); } +/* file's initialization. */ + syms_of_indent () { DEFVAR_BOOL ("indent-tabs-mode", &indent_tabs_mode,