X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/9ec0b715e262f45bb434f2c068a86de2c42528e3..29cdc13ed61e5a64ba30df1030029898a26b7947:/src/indent.c diff --git a/src/indent.c b/src/indent.c index 9d69936d44..970904cba7 100644 --- a/src/indent.c +++ b/src/indent.c @@ -1,6 +1,6 @@ /* Indentation functions. Copyright (C) 1985, 1986, 1987, 1988, 1993, 1994, 1995, 1998, 2000, 2001, - 2002, 2003, 2004, 2005, 2006, 2007, 2008 + 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc. This file is part of GNU Emacs. @@ -20,11 +20,13 @@ along with GNU Emacs. If not, see . */ #include #include +#include #include "lisp.h" #include "buffer.h" #include "character.h" #include "category.h" +#include "composite.h" #include "indent.h" #include "keyboard.h" #include "frame.h" @@ -33,6 +35,7 @@ along with GNU Emacs. If not, see . */ #include "termopts.h" #include "disptab.h" #include "intervals.h" +#include "dispextern.h" #include "region-cache.h" /* Indentation can insert tabs if this is non-zero; @@ -58,8 +61,8 @@ EMACS_INT last_known_column_point; static int last_known_column_modified; -static double current_column_1 P_ ((void)); -static double position_indentation P_ ((int)); +static double current_column_1 (void); +static double position_indentation (int); /* Cache of beginning of line found by the last call of current_column. */ @@ -69,7 +72,7 @@ static EMACS_INT current_column_bol_cache; /* Get the display table to use for the current buffer. */ struct Lisp_Char_Table * -buffer_display_table () +buffer_display_table (void) { Lisp_Object thisbuf; @@ -86,9 +89,7 @@ buffer_display_table () /* Return the width of character C under display table DP. */ static int -character_width (c, dp) - int c; - struct Lisp_Char_Table *dp; +character_width (int c, struct Lisp_Char_Table *dp) { Lisp_Object elt; @@ -122,9 +123,7 @@ character_width (c, dp) invalidate the buffer's width_run_cache. */ int -disptab_matches_widthtab (disptab, widthtab) - struct Lisp_Char_Table *disptab; - struct Lisp_Vector *widthtab; +disptab_matches_widthtab (struct Lisp_Char_Table *disptab, struct Lisp_Vector *widthtab) { int i; @@ -142,9 +141,7 @@ disptab_matches_widthtab (disptab, widthtab) /* Recompute BUF's width table, using the display table DISPTAB. */ void -recompute_width_table (buf, disptab) - struct buffer *buf; - struct Lisp_Char_Table *disptab; +recompute_width_table (struct buffer *buf, struct Lisp_Char_Table *disptab) { int i; struct Lisp_Vector *widthtab; @@ -163,7 +160,7 @@ recompute_width_table (buf, disptab) state of current_buffer's cache_long_line_scans variable. */ static void -width_run_cache_on_off () +width_run_cache_on_off (void) { if (NILP (current_buffer->cache_long_line_scans) /* And, for the moment, this feature doesn't work on multibyte @@ -214,11 +211,7 @@ width_run_cache_on_off () will equal the return value. */ EMACS_INT -skip_invisible (pos, next_boundary_p, to, window) - EMACS_INT pos; - EMACS_INT *next_boundary_p; - EMACS_INT to; - Lisp_Object window; +skip_invisible (EMACS_INT pos, EMACS_INT *next_boundary_p, EMACS_INT to, Lisp_Object window) { Lisp_Object prop, position, overlay_limit, proplimit; Lisp_Object buffer, tmp; @@ -250,7 +243,7 @@ skip_invisible (pos, next_boundary_p, to, window) { /* Don't scan terribly far. */ XSETFASTINT (proplimit, min (pos + 100, to)); - /* No matter what. don't go past next overlay change. */ + /* No matter what, don't go past next overlay change. */ if (XFASTINT (overlay_limit) < XFASTINT (proplimit)) proplimit = overlay_limit; tmp = Fnext_single_property_change (position, Qinvisible, @@ -280,32 +273,6 @@ skip_invisible (pos, next_boundary_p, to, window) return pos; } -/* If a composition starts at POS/POS_BYTE and it doesn't stride over - POINT, set *LEN / *LEN_BYTE to the character and byte lengths, *WIDTH - to the width, and return 1. Otherwise, return 0. */ - -static int -check_composition (pos, pos_byte, point, len, len_byte, width) - int pos, pos_byte, point; - int *len, *len_byte, *width; -{ - Lisp_Object prop; - EMACS_INT start, end; - int id; - - if (! find_composition (pos, -1, &start, &end, &prop, Qnil) - || pos != start || point < end - || !COMPOSITION_VALID_P (start, end, prop)) - return 0; - if ((id = get_composition_id (pos, pos_byte, end - pos, prop, Qnil)) < 0) - return 0; - - *len = COMPOSITION_LENGTH (prop); - *len_byte = CHAR_TO_BYTE (end) - pos_byte; - *width = composition_table[id]->width; - return 1; -} - /* Set variables WIDTH and BYTES for a multibyte sequence starting at P. DP is a display table or NULL. @@ -318,7 +285,7 @@ check_composition (pos, pos_byte, point, len, len_byte, width) int c; \ \ wide_column = 0; \ - c = STRING_CHAR_AND_LENGTH (p, MAX_MULTIBYTE_LENGTH, bytes); \ + c = STRING_CHAR_AND_LENGTH (p, bytes); \ if (BYTES_BY_CHAR_HEAD (*p) != bytes) \ width = bytes * 4; \ else \ @@ -345,7 +312,7 @@ Whether the line is visible (if `selective-display' is t) has no effect; however, ^M is treated as end of line when `selective-display' is t. Text that has an invisible property is considered as having width 0, unless `buffer-invisibility-spec' specifies that it is replaced by an ellipsis. */) - () + (void) { Lisp_Object temp; XSETFASTINT (temp, (int) current_column ()); /* iftc */ @@ -355,13 +322,13 @@ Text that has an invisible property is considered as having width 0, unless /* Cancel any recorded value of the horizontal position. */ void -invalidate_current_column () +invalidate_current_column (void) { last_known_column_point = 0; } double -current_column () +current_column (void) { register int col; register unsigned char *ptr, *stop; @@ -502,7 +469,6 @@ current_column () return col; } -extern Lisp_Object Qspace, QCwidth, QCalign_to; /* Check the presence of a display property and compute its width. If a property was found and its width was found as well, return @@ -529,7 +495,7 @@ check_display_width (EMACS_INT pos, EMACS_INT col, EMACS_INT *endpos) width = XINT (prop) - col; else if (FLOATP (prop)) width = (int)(XFLOAT_DATA (prop) + 0.5) - col; - + if (width >= 0) { EMACS_INT start; @@ -556,6 +522,9 @@ scan_for_column (EMACS_INT *endpos, EMACS_INT *goalcol, EMACS_INT *prevcol) register int ctl_arrow = !NILP (current_buffer->ctl_arrow); register struct Lisp_Char_Table *dp = buffer_display_table (); int multibyte = !NILP (current_buffer->enable_multibyte_characters); + struct composition_it cmp_it; + Lisp_Object window; + struct window *w; /* Start the scan at the beginning of this line with column number 0. */ register EMACS_INT col = 0, prev_col = 0; @@ -572,7 +541,13 @@ scan_for_column (EMACS_INT *endpos, EMACS_INT *goalcol, EMACS_INT *prevcol) next_boundary = scan; } + window = Fget_buffer_window (Fcurrent_buffer (), Qnil); + w = ! NILP (window) ? XWINDOW (window) : NULL; + if (tab_width <= 0 || tab_width > 1000) tab_width = 8; + memset (&cmp_it, 0, sizeof cmp_it); + cmp_it.id = -1; + composition_compute_stop_pos (&cmp_it, scan, scan_byte, end, Qnil); /* Scan forward to the target position. */ while (scan < end) @@ -599,20 +574,6 @@ scan_for_column (EMACS_INT *endpos, EMACS_INT *goalcol, EMACS_INT *prevcol) break; prev_col = col; - { /* Check composition sequence. */ - int len, len_byte, width; - - if (check_composition (scan, scan_byte, end, - &len, &len_byte, &width)) - { - scan += len; - scan_byte += len_byte; - if (scan <= end) - col += width; - continue; - } - } - { /* Check display property. */ EMACS_INT end; int width = check_display_width (scan, col, &end); @@ -627,13 +588,36 @@ scan_for_column (EMACS_INT *endpos, EMACS_INT *goalcol, EMACS_INT *prevcol) } } + /* Check composition sequence. */ + if (cmp_it.id >= 0 + || (scan == cmp_it.stop_pos + && composition_reseat_it (&cmp_it, scan, scan_byte, end, + w, NULL, Qnil))) + composition_update_it (&cmp_it, scan, scan_byte, Qnil); + if (cmp_it.id >= 0) + { + scan += cmp_it.nchars; + scan_byte += cmp_it.nbytes; + if (scan <= end) + col += cmp_it.width; + if (cmp_it.to == cmp_it.nglyphs) + { + cmp_it.id = -1; + composition_compute_stop_pos (&cmp_it, scan, scan_byte, end, + Qnil); + } + else + cmp_it.from = cmp_it.to; + continue; + } + c = FETCH_BYTE (scan_byte); /* See if there is a display table and it relates to this character. */ if (dp != 0 - && ! (multibyte && BASE_LEADING_CODE_P (c)) + && ! (multibyte && LEADING_CODE_P (c)) && VECTORP (DISP_CHAR_VECTOR (dp, c))) { Lisp_Object charvec; @@ -684,7 +668,7 @@ scan_for_column (EMACS_INT *endpos, EMACS_INT *goalcol, EMACS_INT *prevcol) col += tab_width; col = col / tab_width * tab_width; } - else if (multibyte && BASE_LEADING_CODE_P (c)) + else if (multibyte && LEADING_CODE_P (c)) { /* Start of multi-byte form. */ unsigned char *ptr; @@ -728,7 +712,7 @@ scan_for_column (EMACS_INT *endpos, EMACS_INT *goalcol, EMACS_INT *prevcol) due to text properties or overlays. */ static double -current_column_1 () +current_column_1 (void) { EMACS_INT col = MOST_POSITIVE_FIXNUM; EMACS_INT opoint = PT; @@ -827,8 +811,7 @@ Optional second argument MINIMUM says always do at least MINIMUM spaces even if that goes past COLUMN; by default, MINIMUM is zero. The return value is COLUMN. */) - (column, minimum) - Lisp_Object column, minimum; + (Lisp_Object column, Lisp_Object minimum) { int mincol; register int fromcol; @@ -872,14 +855,14 @@ The return value is COLUMN. */) } -static double position_indentation P_ ((int)); +static double position_indentation (int); DEFUN ("current-indentation", Fcurrent_indentation, Scurrent_indentation, 0, 0, 0, doc: /* Return the indentation of the current line. This is the horizontal position of the character following any initial whitespace. */) - () + (void) { Lisp_Object val; int opoint = PT, opoint_byte = PT_BYTE; @@ -892,8 +875,7 @@ following any initial whitespace. */) } static double -position_indentation (pos_byte) - register int pos_byte; +position_indentation (register int pos_byte) { register EMACS_INT column = 0; register EMACS_INT tab_width = XINT (current_buffer->tab_width); @@ -982,9 +964,7 @@ position_indentation (pos_byte) preceding line. */ int -indented_beyond_p (pos, pos_byte, column) - int pos, pos_byte; - double column; +indented_beyond_p (int pos, int pos_byte, double column) { double val; int opoint = PT, opoint_byte = PT_BYTE; @@ -1016,8 +996,7 @@ In addition, if FORCE is t, and the line is too short to reach COLUMN, add spaces/tabs to get there. The return value is the current column. */) - (column, force) - Lisp_Object column, force; + (Lisp_Object column, Lisp_Object force) { EMACS_INT pos; EMACS_INT col, prev_col; @@ -1142,12 +1121,7 @@ struct position val_compute_motion; the scroll bars if they are turned on. */ struct position * -compute_motion (from, fromvpos, fromhpos, did_motion, to, tovpos, tohpos, width, hscroll, tab_offset, win) - EMACS_INT from, fromvpos, fromhpos, to, tovpos, tohpos; - int did_motion; - EMACS_INT width; - EMACS_INT hscroll, tab_offset; - struct window *win; +compute_motion (EMACS_INT from, EMACS_INT fromvpos, EMACS_INT fromhpos, int did_motion, EMACS_INT to, EMACS_INT tovpos, EMACS_INT tohpos, EMACS_INT width, EMACS_INT hscroll, EMACS_INT tab_offset, struct window *win) { register EMACS_INT hpos = fromhpos; register EMACS_INT vpos = fromvpos; @@ -1195,6 +1169,8 @@ compute_motion (from, fromvpos, fromhpos, did_motion, to, tovpos, tohpos, width, EMACS_INT prev_tab_offset; /* Previous tab offset. */ EMACS_INT continuation_glyph_width; + struct composition_it cmp_it; + XSETBUFFER (buffer, current_buffer); XSETWINDOW (window, win); @@ -1235,6 +1211,10 @@ compute_motion (from, fromvpos, fromhpos, did_motion, to, tovpos, tohpos, width, pos_byte = prev_pos_byte = CHAR_TO_BYTE (from); contin_hpos = 0; prev_tab_offset = tab_offset; + memset (&cmp_it, 0, sizeof cmp_it); + cmp_it.id = -1; + composition_compute_stop_pos (&cmp_it, pos, pos_byte, to, Qnil); + while (1) { while (pos == next_boundary) @@ -1349,10 +1329,20 @@ compute_motion (from, fromvpos, fromhpos, did_motion, to, tovpos, tohpos, width, if (hpos > width) { - if (hscroll - || (truncate_partial_width_windows - && ((width + continuation_glyph_width) - < FRAME_COLS (XFRAME (WINDOW_FRAME (win))))) + int total_width = width + continuation_glyph_width; + int truncate = 0; + + if (!NILP (Vtruncate_partial_width_windows) + && (total_width < FRAME_COLS (XFRAME (WINDOW_FRAME (win))))) + { + if (INTEGERP (Vtruncate_partial_width_windows)) + truncate + = total_width < XFASTINT (Vtruncate_partial_width_windows); + else + truncate = 1; + } + + if (hscroll || truncate || !NILP (current_buffer->truncate_lines)) { /* Truncating: skip to newline, unless we are already past @@ -1512,21 +1502,29 @@ compute_motion (from, fromvpos, fromhpos, did_motion, to, tovpos, tohpos, width, EMACS_INT i, n; Lisp_Object charvec; - c = FETCH_BYTE (pos_byte); - /* Check composition sequence. */ - { - int len, len_byte, width; - - if (check_composition (pos, pos_byte, to, &len, &len_byte, &width)) - { - pos += len; - pos_byte += len_byte; - hpos += width; - continue; - } - } + if (cmp_it.id >= 0 + || (pos == cmp_it.stop_pos + && composition_reseat_it (&cmp_it, pos, pos_byte, to, win, + NULL, Qnil))) + composition_update_it (&cmp_it, pos, pos_byte, Qnil); + if (cmp_it.id >= 0) + { + pos += cmp_it.nchars; + pos_byte += cmp_it.nbytes; + hpos += cmp_it.width; + if (cmp_it.to == cmp_it.nglyphs) + { + cmp_it.id = -1; + composition_compute_stop_pos (&cmp_it, pos, pos_byte, to, + Qnil); + } + else + cmp_it.from = cmp_it.to; + continue; + } + c = FETCH_BYTE (pos_byte); pos++, pos_byte++; /* Perhaps add some info to the width_run_cache. */ @@ -1558,7 +1556,7 @@ compute_motion (from, fromvpos, fromhpos, did_motion, to, tovpos, tohpos, width, } if (dp != 0 - && ! (multibyte && BASE_LEADING_CODE_P (c)) + && ! (multibyte && LEADING_CODE_P (c)) && VECTORP (DISP_CHAR_VECTOR (dp, c))) { charvec = DISP_CHAR_VECTOR (dp, c); @@ -1664,7 +1662,7 @@ compute_motion (from, fromvpos, fromhpos, did_motion, to, tovpos, tohpos, width, hpos = width; } } - else if (multibyte && BASE_LEADING_CODE_P (c)) + else if (multibyte && LEADING_CODE_P (c)) { /* Start of multi-byte form. */ unsigned char *ptr; @@ -1756,9 +1754,7 @@ of a certain window, pass the window's starting location as FROM and the window's upper-left coordinates as FROMPOS. Pass the buffer's (point-max) as TO, to limit the scan to the end of the visible section of the buffer, and pass LINE and COL as TOPOS. */) - (from, frompos, to, topos, width, offsets, window) - Lisp_Object from, frompos, to, topos; - Lisp_Object width, offsets, window; + (Lisp_Object from, Lisp_Object frompos, Lisp_Object to, Lisp_Object topos, Lisp_Object width, Lisp_Object offsets, Lisp_Object window) { struct window *w; Lisp_Object bufpos, hpos, vpos, prevhpos; @@ -1837,9 +1833,7 @@ visible section of the buffer, and pass LINE and COL as TOPOS. */) struct position val_vmotion; struct position * -vmotion (from, vtarget, w) - register EMACS_INT from, vtarget; - struct window *w; +vmotion (register EMACS_INT from, register EMACS_INT vtarget, struct window *w) { EMACS_INT hscroll = XINT (w->hscroll); struct position pos; @@ -1993,19 +1987,32 @@ The optional second argument WINDOW specifies the window to use for parameters such as width, horizontal scrolling, and so on. The default is to use the selected window's parameters. +LINES can optionally take the form (COLS . LINES), in which case +the motion will not stop at the start of a screen line but on +its column COLS (if such exists on that line, that is). + `vertical-motion' always uses the current buffer, regardless of which buffer is displayed in WINDOW. This is consistent with other cursor motion functions and makes it possible to use `vertical-motion' in any buffer, whether or not it is currently displayed in some window. */) - (lines, window) - Lisp_Object lines, window; + (Lisp_Object lines, Lisp_Object window) { struct it it; struct text_pos pt; struct window *w; Lisp_Object old_buffer; struct gcpro gcpro1; + Lisp_Object lcols = Qnil; + double cols; + + /* Allow LINES to be of the form (HPOS . VPOS) aka (COLUMNS . LINES). */ + if (CONSP (lines) && (NUMBERP (XCAR (lines)))) + { + lcols = XCAR (lines); + cols = INTEGERP (lcols) ? (double) XINT (lcols) : XFLOAT_DATA (lcols); + lines = XCDR (lines); + } CHECK_NUMBER (lines); if (! NILP (window)) @@ -2031,68 +2038,110 @@ whether or not it is currently displayed in some window. */) } else { - int it_start; - int oselective; - int it_overshoot_expected; + int it_start, first_x, it_overshoot_expected; SET_TEXT_POS (pt, PT, PT_BYTE); start_display (&it, w, pt); - - /* Scan from the start of the line containing PT. If we don't - do this, we start moving with IT->current_x == 0, while PT is - really at some x > 0. The effect is, in continuation lines, that - we end up with the iterator placed at where it thinks X is 0, - while the end position is really at some X > 0, the same X that - PT had. */ + first_x = it.first_visible_x; it_start = IT_CHARPOS (it); - /* We expect the call to move_it_to, further down, to overshoot - if the starting point is on an image, stretch glyph, - composition, or Lisp string. We won't need to backtrack in - this situation, except for one corner case: when the Lisp - string contains a newline. */ - if (it.method == GET_FROM_STRING) + /* See comments below for why we calculate this. */ + if (XINT (lines) > 0) { - const char *s = SDATA (it.string); - const char *e = s + SBYTES (it.string); - - while (s < e && *s != '\n') - ++s; - - /* If there is no newline in the string, we need to check - whether there is a newline immediately after the string - in move_it_to below. This may happen if there is an - overlay with an after-string just before the newline. */ - it_overshoot_expected = (s == e) ? -1 : 0; + if (it.cmp_it.id >= 0) + it_overshoot_expected = 1; + else if (it.method == GET_FROM_STRING) + { + const char *s = SDATA (it.string); + const char *e = s + SBYTES (it.string); + while (s < e && *s != '\n') + ++s; + it_overshoot_expected = (s == e) ? -1 : 0; + } + else + it_overshoot_expected = (it.method == GET_FROM_IMAGE + || it.method == GET_FROM_STRETCH); } - else - it_overshoot_expected = (it.method == GET_FROM_IMAGE - || it.method == GET_FROM_STRETCH - || it.method == GET_FROM_COMPOSITION); + /* Scan from the start of the line containing PT. If we don't + do this, we start moving with IT->current_x == 0, while PT is + really at some x > 0. */ reseat_at_previous_visible_line_start (&it); it.current_x = it.hpos = 0; - /* Temporarily disable selective display so we don't move too far */ - oselective = it.selective; - it.selective = 0; - move_it_to (&it, PT, -1, -1, -1, MOVE_TO_POS); - it.selective = oselective; - - /* Move back if we got too far. This may happen if - truncate-lines is on and PT is beyond right margin. - Don't go back if the overshoot is expected (see above). */ - if (IT_CHARPOS (it) > it_start && XINT (lines) > 0 - && (!it_overshoot_expected - || (it_overshoot_expected < 0 - && it.method == GET_FROM_BUFFER - && it.c == '\n'))) - move_it_by_lines (&it, -1, 0); - - it.vpos = 0; - /* Do this even if LINES is 0, so that we move back - to the beginning of the current line as we ought. */ - if (XINT (lines) >= 0 || IT_CHARPOS (it) > 0) - move_it_by_lines (&it, XINT (lines), 0); + if (IT_CHARPOS (it) != PT) + /* We used to temporarily disable selective display here; the + comment said this is "so we don't move too far" (2005-01-19 + checkin by kfs). But this does nothing useful that I can + tell, and it causes Bug#2694 . -- cyd */ + move_it_to (&it, PT, -1, -1, -1, MOVE_TO_POS); + + if (XINT (lines) <= 0) + { + it.vpos = 0; + /* Do this even if LINES is 0, so that we move back to the + beginning of the current line as we ought. */ + if (XINT (lines) == 0 || IT_CHARPOS (it) > 0) + move_it_by_lines (&it, XINT (lines), 0); + } + else + { + if (IT_CHARPOS (it) > it_start) + { + /* IT may move too far if truncate-lines is on and PT + lies beyond the right margin. In that case, + backtrack unless the starting point is on an image, + stretch glyph, composition, or Lisp string. */ + if (!it_overshoot_expected + /* Also, backtrack if the Lisp string contains no + newline, but there is a newline right after it. + In this case, IT overshoots if there is an + after-string just before the newline. */ + || (it_overshoot_expected < 0 + && it.method == GET_FROM_BUFFER + && it.c == '\n')) + move_it_by_lines (&it, -1, 0); + it.vpos = 0; + move_it_by_lines (&it, XINT (lines), 0); + } + else + { + /* Otherwise, we are at the first row occupied by PT, + which might span multiple screen lines (e.g., if it's + on a multi-line display string). We want to start + from the last line that it occupies. */ + if (it_start < ZV) + { + while (IT_CHARPOS (it) <= it_start) + { + it.vpos = 0; + move_it_by_lines (&it, 1, 0); + } + if (XINT (lines) > 1) + move_it_by_lines (&it, XINT (lines) - 1, 0); + } + else + { + it.vpos = 0; + move_it_by_lines (&it, XINT (lines), 0); + } + } + } + + /* Move to the goal column, if one was specified. */ + if (!NILP (lcols)) + { + /* If the window was originally hscrolled, move forward by + the hscrolled amount first. */ + if (first_x > 0) + { + move_it_in_display_line (&it, ZV, first_x, MOVE_TO_X); + it.current_x = 0; + } + move_it_in_display_line + (&it, ZV, + (int)(cols * FRAME_COLUMN_WIDTH (XFRAME (w->frame)) + 0.5), + MOVE_TO_X); + } SET_PT_BOTH (IT_CHARPOS (it), IT_BYTEPOS (it)); } @@ -2108,7 +2157,7 @@ whether or not it is currently displayed in some window. */) /* File's initialization. */ void -syms_of_indent () +syms_of_indent (void) { DEFVAR_BOOL ("indent-tabs-mode", &indent_tabs_mode, doc: /* *Indentation can insert tabs if this is non-nil. */);