X-Git-Url: http://git.hcoop.net/bpt/emacs.git/blobdiff_plain/f76840f36cf60feecaf21d55d24ace948800fef7..e8ccb8690456cba24bb354b968921516bd55375e:/src/indent.c diff --git a/src/indent.c b/src/indent.c index 2d48dc746d..f10b235427 100644 --- a/src/indent.c +++ b/src/indent.c @@ -1,13 +1,14 @@ /* Indentation functions. Copyright (C) 1985, 1986, 1987, 1988, 1993, 1994, 1995, 1998, 2000, 2001, - 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. + 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 + Free Software Foundation, Inc. This file is part of GNU Emacs. -GNU Emacs is free software; you can redistribute it and/or modify +GNU Emacs is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 3, or (at your option) -any later version. +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. GNU Emacs is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -15,17 +16,16 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License -along with GNU Emacs; see the file COPYING. If not, write to -the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -Boston, MA 02110-1301, USA. */ +along with GNU Emacs. If not, see . */ #include #include #include "lisp.h" #include "buffer.h" -#include "charset.h" +#include "character.h" #include "category.h" +#include "composite.h" #include "indent.h" #include "keyboard.h" #include "frame.h" @@ -34,6 +34,7 @@ Boston, MA 02110-1301, USA. */ #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; @@ -281,32 +282,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; - 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. @@ -327,7 +302,7 @@ check_composition (pos, pos_byte, point, len, len_byte, width) if (dp != 0 && VECTORP (DISP_CHAR_VECTOR (dp, c))) \ width = XVECTOR (DISP_CHAR_VECTOR (dp, c))->size; \ else \ - width = WIDTH_BY_CHAR_HEAD (*p); \ + width = CHAR_WIDTH (c); \ if (width > 1) \ wide_column = width; \ } \ @@ -446,9 +421,9 @@ current_column () next_element_from_display_vector does it. */ Lisp_Object entry = AREF (charvec, i); - if (INTEGERP (entry) - && GLYPH_CHAR_VALID_P (XFASTINT (entry))) - c = FAST_GLYPH_CHAR (XFASTINT (entry)); + if (GLYPH_CODE_P (entry) + && GLYPH_CODE_CHAR_VALID_P (entry)) + c = GLYPH_CODE_CHAR (entry); else c = ' '; } @@ -503,68 +478,154 @@ current_column () return col; } -/* Return the column number of position POS - by scanning forward from the beginning of the line. - This function handles characters that are invisible - due to text properties or overlays. */ +extern Lisp_Object Qspace, QCwidth, QCalign_to; -static double -current_column_1 () +/* Check the presence of a display property and compute its width. + If a property was found and its width was found as well, return + its width (>= 0) and set the position of the end of the property + in ENDPOS. + Otherwise just return -1. */ +static int +check_display_width (EMACS_INT pos, EMACS_INT col, EMACS_INT *endpos) +{ + Lisp_Object val, overlay; + + if (CONSP (val = get_char_property_and_overlay + (make_number (pos), Qdisplay, Qnil, &overlay)) + && EQ (Qspace, XCAR (val))) + { /* FIXME: Use calc_pixel_width_or_height, as in term.c. */ + Lisp_Object plist = XCDR (val), prop; + int width = -1; + + if ((prop = Fplist_get (plist, QCwidth), NATNUMP (prop))) + width = XINT (prop); + else if (FLOATP (prop)) + width = (int)(XFLOAT_DATA (prop) + 0.5); + else if ((prop = Fplist_get (plist, QCalign_to), NATNUMP (prop))) + width = XINT (prop) - col; + else if (FLOATP (prop)) + width = (int)(XFLOAT_DATA (prop) + 0.5) - col; + + if (width >= 0) + { + EMACS_INT start; + if (OVERLAYP (overlay)) + *endpos = OVERLAY_POSITION (OVERLAY_END (overlay)); + else + get_property_and_range (pos, Qdisplay, &val, &start, endpos, Qnil); + return width; + } + } + return -1; +} + +/* Scanning from the beginning of the current line, stop at the buffer + position ENDPOS or at the column GOALCOL or at the end of line, whichever + comes first. + Return the resulting buffer position and column in ENDPOS and GOALCOL. + PREVCOL gets set to the column of the previous position (it's always + strictly smaller than the goal column). */ +static void +scan_for_column (EMACS_INT *endpos, EMACS_INT *goalcol, EMACS_INT *prevcol) { register EMACS_INT tab_width = XINT (current_buffer->tab_width); 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; + register EMACS_INT col = 0, prev_col = 0; + EMACS_INT goal = goalcol ? *goalcol : MOST_POSITIVE_FIXNUM; + EMACS_INT end = endpos ? *endpos : PT; EMACS_INT scan, scan_byte; EMACS_INT next_boundary; + { EMACS_INT opoint = PT, opoint_byte = PT_BYTE; - scan_newline (PT, PT_BYTE, BEGV, BEGV_BYTE, -1, 1); current_column_bol_cache = PT; scan = PT, scan_byte = PT_BYTE; SET_PT_BOTH (opoint, opoint_byte); 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; + bzero (&cmp_it, 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 < opoint) + while (scan < end) { int c; /* Occasionally we may need to skip invisible text. */ while (scan == next_boundary) { - int old_scan = scan; + EMACS_INT old_scan = scan; /* This updates NEXT_BOUNDARY to the next place where we might need to skip more invisible text. */ - scan = skip_invisible (scan, &next_boundary, opoint, Qnil); - if (scan >= opoint) - goto endloop; + scan = skip_invisible (scan, &next_boundary, end, Qnil); if (scan != old_scan) scan_byte = CHAR_TO_BYTE (scan); + if (scan >= end) + goto endloop; } - /* Check composition sequence. */ - { - int len, len_byte, width; + /* Test reaching the goal column. We do this after skipping + invisible characters, so that we put point before the + character on which the cursor will appear. */ + if (col >= goal) + break; + prev_col = col; - if (check_composition (scan, scan_byte, opoint, - &len, &len_byte, &width)) + { /* Check display property. */ + EMACS_INT end; + int width = check_display_width (scan, col, &end); + if (width >= 0) { - scan += len; - scan_byte += len_byte; - if (scan <= opoint) - col += width; - continue; + col += width; + if (end > scan) /* Avoid infinite loops with 0-width overlays. */ + { + scan = end; scan_byte = charpos_to_bytepos (scan); + continue; + } } } + /* 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)) && VECTORP (DISP_CHAR_VECTOR (dp, c))) @@ -573,7 +634,7 @@ current_column_1 () EMACS_INT i, n; /* This character is displayed using a vector of glyphs. - Update the column based on those glyphs. */ + Update the column/position based on those glyphs. */ charvec = DISP_CHAR_VECTOR (dp, c); n = ASIZE (charvec); @@ -582,12 +643,11 @@ current_column_1 () { /* This should be handled the same as next_element_from_display_vector does it. */ - Lisp_Object entry; - entry = AREF (charvec, i); + Lisp_Object entry = AREF (charvec, i); - if (INTEGERP (entry) - && GLYPH_CHAR_VALID_P (XFASTINT (entry))) - c = FAST_GLYPH_CHAR (XFASTINT (entry)); + if (GLYPH_CODE_P (entry) + && GLYPH_CODE_CHAR_VALID_P (entry)) + c = GLYPH_CODE_CHAR (entry); else c = ' '; @@ -606,8 +666,8 @@ current_column_1 () } else { - /* The display table says nothing for this character. - Display it as itself. */ + /* The display table doesn't affect this character; + it displays as itself. */ if (c == '\n') goto endloop; @@ -620,15 +680,15 @@ current_column_1 () } else if (multibyte && BASE_LEADING_CODE_P (c)) { + /* Start of multi-byte form. */ unsigned char *ptr; int bytes, width, wide_column; ptr = BYTE_POS_ADDR (scan_byte); MULTIBYTE_BYTES_WIDTH (ptr, dp); - scan_byte += bytes; /* Subtract one to compensate for the increment that is going to happen below. */ - scan_byte--; + scan_byte += bytes - 1; col += width; } else if (ctl_arrow && (c < 040 || c == 0177)) @@ -648,6 +708,26 @@ current_column_1 () last_known_column_point = PT; last_known_column_modified = MODIFF; + if (goalcol) + *goalcol = col; + if (endpos) + *endpos = scan; + if (prevcol) + *prevcol = prev_col; +} + +/* Return the column number of position POS + by scanning forward from the beginning of the line. + This function handles characters that are invisible + due to text properties or overlays. */ + +static double +current_column_1 () +{ + EMACS_INT col = MOST_POSITIVE_FIXNUM; + EMACS_INT opoint = PT; + + scan_for_column (&opoint, &col, NULL); return col; } @@ -933,181 +1013,48 @@ The return value is the current column. */) (column, force) Lisp_Object column, force; { - register EMACS_INT pos; - register EMACS_INT col = current_column (); - register EMACS_INT goal; - register EMACS_INT end; - register int tab_width = XINT (current_buffer->tab_width); - register int ctl_arrow = !NILP (current_buffer->ctl_arrow); - register struct Lisp_Char_Table *dp = buffer_display_table (); - register int multibyte = !NILP (current_buffer->enable_multibyte_characters); - - Lisp_Object val; - EMACS_INT prev_col = 0; - int c = 0; - EMACS_INT next_boundary, pos_byte; + EMACS_INT pos; + EMACS_INT col, prev_col; + EMACS_INT goal; - if (tab_width <= 0 || tab_width > 1000) tab_width = 8; CHECK_NATNUM (column); goal = XINT (column); - pos = PT; - pos_byte = PT_BYTE; - end = ZV; - - /* If we're starting past the desired column, - back up to beginning of line and scan from there. */ - if (col > goal) - { - end = pos; - pos = current_column_bol_cache; - pos_byte = CHAR_TO_BYTE (pos); - col = 0; - } + col = goal; + pos = ZV; + scan_for_column (&pos, &col, &prev_col); - next_boundary = pos; + SET_PT (pos); - while (pos < end) + /* If a tab char made us overshoot, change it to spaces + and scan through it again. */ + if (!NILP (force) && col > goal) { - while (pos == next_boundary) - { - EMACS_INT prev = pos; - pos = skip_invisible (pos, &next_boundary, end, Qnil); - if (pos != prev) - pos_byte = CHAR_TO_BYTE (pos); - if (pos >= end) - goto endloop; - } - - /* Test reaching the goal column. We do this after skipping - invisible characters, so that we put point before the - character on which the cursor will appear. */ - if (col >= goal) - break; - - /* Check composition sequence. */ - { - int len, len_byte, width; - - if (check_composition (pos, pos_byte, Z, &len, &len_byte, &width)) - { - pos += len; - pos_byte += len_byte; - col += width; - continue; - } - } - - c = FETCH_BYTE (pos_byte); - - /* See if there is a display table and it relates - to this character. */ - - if (dp != 0 - && ! (multibyte && BASE_LEADING_CODE_P (c)) - && VECTORP (DISP_CHAR_VECTOR (dp, c))) - { - Lisp_Object charvec; - EMACS_INT i, n; - - /* This character is displayed using a vector of glyphs. - Update the position based on those glyphs. */ - - charvec = DISP_CHAR_VECTOR (dp, c); - n = ASIZE (charvec); - - for (i = 0; i < n; i++) - { - /* This should be handled the same as - next_element_from_display_vector does it. */ - - Lisp_Object entry; - entry = AREF (charvec, i); - - if (INTEGERP (entry) - && GLYPH_CHAR_VALID_P (XFASTINT (entry))) - c = FAST_GLYPH_CHAR (XFASTINT (entry)); - else - c = ' '; + int c; + EMACS_INT pos_byte = PT_BYTE; - if (c == '\n') - goto endloop; - if (c == '\r' && EQ (current_buffer->selective_display, Qt)) - goto endloop; - if (c == '\t') - { - prev_col = col; - col += tab_width; - col = col / tab_width * tab_width; - } - else - ++col; - } - } - else + DEC_POS (pos_byte); + c = FETCH_CHAR (pos_byte); + if (c == '\t' && prev_col < goal) { - /* The display table doesn't affect this character; - it displays as itself. */ - - if (c == '\n') - goto endloop; - if (c == '\r' && EQ (current_buffer->selective_display, Qt)) - goto endloop; - if (c == '\t') - { - prev_col = col; - col += tab_width; - col = col / tab_width * tab_width; - } - else if (ctl_arrow && (c < 040 || c == 0177)) - col += 2; - else if (c < 040 || c == 0177) - col += 4; - else if (c < 0177) - col++; - else if (multibyte && BASE_LEADING_CODE_P (c)) - { - /* Start of multi-byte form. */ - unsigned char *ptr; - int bytes, width, wide_column; - - ptr = BYTE_POS_ADDR (pos_byte); - MULTIBYTE_BYTES_WIDTH (ptr, dp); - pos_byte += bytes - 1; - col += width; - } - else - col += 4; + EMACS_INT goal_pt, goal_pt_byte; + + /* Insert spaces in front of the tab to reach GOAL. Do this + first so that a marker at the end of the tab gets + adjusted. */ + SET_PT_BOTH (PT - 1, PT_BYTE - 1); + Finsert_char (make_number (' '), make_number (goal - prev_col), Qt); + + /* Now delete the tab, and indent to COL. */ + del_range (PT, PT + 1); + goal_pt = PT; + goal_pt_byte = PT_BYTE; + Findent_to (make_number (col), Qnil); + SET_PT_BOTH (goal_pt, goal_pt_byte); + + /* Set the last_known... vars consistently. */ + col = goal; } - - pos++; - pos_byte++; - } - endloop: - - SET_PT_BOTH (pos, pos_byte); - - /* If a tab char made us overshoot, change it to spaces - and scan through it again. */ - if (!NILP (force) && col > goal && c == '\t' && prev_col < goal) - { - EMACS_INT goal_pt, goal_pt_byte; - - /* Insert spaces in front of the tab to reach GOAL. Do this - first so that a marker at the end of the tab gets - adjusted. */ - SET_PT_BOTH (PT - 1, PT_BYTE - 1); - Finsert_char (make_number (' '), make_number (goal - prev_col), Qt); - - /* Now delete the tab, and indent to COL. */ - del_range (PT, PT + 1); - goal_pt = PT; - goal_pt_byte = PT_BYTE; - Findent_to (make_number (col), Qnil); - SET_PT_BOTH (goal_pt, goal_pt_byte); - - /* Set the last_known... vars consistently. */ - col = goal; } /* If line ends prematurely, add space to the end. */ @@ -1118,8 +1065,7 @@ The return value is the current column. */) last_known_column_point = PT; last_known_column_modified = MODIFF; - XSETFASTINT (val, col); - return val; + return make_number (col); } /* compute_motion: compute buffer posn given screen posn and vice versa */ @@ -1243,6 +1189,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); @@ -1283,6 +1231,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; + bzero (&cmp_it, sizeof cmp_it); + cmp_it.id = -1; + composition_compute_stop_pos (&cmp_it, pos, pos_byte, to, Qnil); + while (1) { while (pos == next_boundary) @@ -1397,10 +1349,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 @@ -1560,21 +1522,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. */ @@ -1626,9 +1596,9 @@ compute_motion (from, fromvpos, fromhpos, did_motion, to, tovpos, tohpos, width, next_element_from_display_vector does it. */ Lisp_Object entry = AREF (charvec, i); - if (INTEGERP (entry) - && GLYPH_CHAR_VALID_P (XFASTINT (entry))) - c = FAST_GLYPH_CHAR (XFASTINT (entry)); + if (GLYPH_CODE_P (entry) + && GLYPH_CODE_CHAR_VALID_P (entry)) + c = GLYPH_CODE_CHAR (entry); else c = ' '; } @@ -2041,6 +2011,10 @@ 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 @@ -2054,6 +2028,16 @@ whether or not it is currently displayed in some window. */) 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)) @@ -2079,68 +2063,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)); }