]> code.delx.au - gnu-emacs/blobdiff - src/indent.c
Fix use of int instead of EMACS_INT in editfns.c.
[gnu-emacs] / src / indent.c
index 9ac4478bf574ec1b4ab1d2fefa2f602c172accc8..970904cba7b0961bb65cad828549a5de04b2fc8c 100644 (file)
@@ -1,14 +1,14 @@
 /* Indentation functions.
    Copyright (C) 1985, 1986, 1987, 1988, 1993, 1994, 1995, 1998, 2000, 2001,
 /* 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.
 
                  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
 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
 
 GNU Emacs is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -16,17 +16,17 @@ 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
 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 <http://www.gnu.org/licenses/>.  */
 
 #include <config.h>
 #include <stdio.h>
 
 #include <config.h>
 #include <stdio.h>
+#include <setjmp.h>
 
 #include "lisp.h"
 #include "buffer.h"
 #include "character.h"
 #include "category.h"
 
 #include "lisp.h"
 #include "buffer.h"
 #include "character.h"
 #include "category.h"
+#include "composite.h"
 #include "indent.h"
 #include "keyboard.h"
 #include "frame.h"
 #include "indent.h"
 #include "keyboard.h"
 #include "frame.h"
@@ -35,6 +35,7 @@ Boston, MA 02110-1301, USA.  */
 #include "termopts.h"
 #include "disptab.h"
 #include "intervals.h"
 #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;
 #include "region-cache.h"
 
 /* Indentation can insert tabs if this is non-zero;
@@ -60,8 +61,8 @@ EMACS_INT last_known_column_point;
 
 static int last_known_column_modified;
 
 
 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. */
 
 /* Cache of beginning of line found by the last call of
    current_column. */
@@ -71,7 +72,7 @@ static EMACS_INT current_column_bol_cache;
 /* Get the display table to use for the current buffer.  */
 
 struct Lisp_Char_Table *
 /* Get the display table to use for the current buffer.  */
 
 struct Lisp_Char_Table *
-buffer_display_table ()
+buffer_display_table (void)
 {
   Lisp_Object thisbuf;
 
 {
   Lisp_Object thisbuf;
 
@@ -88,9 +89,7 @@ buffer_display_table ()
 /* Return the width of character C under display table DP.  */
 
 static int
 /* 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;
 
 {
   Lisp_Object elt;
 
@@ -124,9 +123,7 @@ character_width (c, dp)
    invalidate the buffer's width_run_cache.  */
 
 int
    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;
 
 {
   int i;
 
@@ -144,9 +141,7 @@ disptab_matches_widthtab (disptab, widthtab)
 /* Recompute BUF's width table, using the display table DISPTAB.  */
 
 void
 /* 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;
 {
   int i;
   struct Lisp_Vector *widthtab;
@@ -165,7 +160,7 @@ recompute_width_table (buf, disptab)
    state of current_buffer's cache_long_line_scans variable.  */
 
 static void
    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
 {
   if (NILP (current_buffer->cache_long_line_scans)
       /* And, for the moment, this feature doesn't work on multibyte
@@ -216,11 +211,7 @@ width_run_cache_on_off ()
    will equal the return value.  */
 
 EMACS_INT
    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;
 {
   Lisp_Object prop, position, overlay_limit, proplimit;
   Lisp_Object buffer, tmp;
@@ -252,7 +243,7 @@ skip_invisible (pos, next_boundary_p, to, window)
     {
       /* Don't scan terribly far.  */
       XSETFASTINT (proplimit, min (pos + 100, to));
     {
       /* 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,
       if (XFASTINT (overlay_limit) < XFASTINT (proplimit))
        proplimit = overlay_limit;
       tmp = Fnext_single_property_change (position, Qinvisible,
@@ -282,32 +273,6 @@ skip_invisible (pos, next_boundary_p, to, window)
   return pos;
 }
 \f
   return pos;
 }
 \f
-/* 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;
-}
-\f
 /* Set variables WIDTH and BYTES for a multibyte sequence starting at P.
 
    DP is a display table or NULL.
 /* Set variables WIDTH and BYTES for a multibyte sequence starting at P.
 
    DP is a display table or NULL.
@@ -320,7 +285,7 @@ check_composition (pos, pos_byte, point, len, len_byte, width)
     int c;                                                             \
                                                                        \
     wide_column = 0;                                                   \
     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                                                               \
     if (BYTES_BY_CHAR_HEAD (*p) != bytes)                              \
       width = bytes * 4;                                               \
     else                                                               \
@@ -347,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.  */)
 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 */
 {
   Lisp_Object temp;
   XSETFASTINT (temp, (int) current_column ()); /* iftc */
@@ -357,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
 /* Cancel any recorded value of the horizontal position.  */
 
 void
-invalidate_current_column ()
+invalidate_current_column (void)
 {
   last_known_column_point = 0;
 }
 
 double
 {
   last_known_column_point = 0;
 }
 
 double
-current_column ()
+current_column (void)
 {
   register int col;
   register unsigned char *ptr, *stop;
 {
   register int col;
   register unsigned char *ptr, *stop;
@@ -504,7 +469,6 @@ current_column ()
   return col;
 }
 \f
   return col;
 }
 \f
-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
 
 /* Check the presence of a display property and compute its width.
    If a property was found and its width was found as well, return
@@ -531,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;
        width = XINT (prop) - col;
       else if (FLOATP (prop))
        width = (int)(XFLOAT_DATA (prop) + 0.5) - col;
-           
+
       if (width >= 0)
        {
          EMACS_INT start;
       if (width >= 0)
        {
          EMACS_INT start;
@@ -558,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);
   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;
 
   /* Start the scan at the beginning of this line with column number 0.  */
   register EMACS_INT col = 0, prev_col = 0;
@@ -574,7 +541,13 @@ scan_for_column (EMACS_INT *endpos, EMACS_INT *goalcol, EMACS_INT *prevcol)
   next_boundary = scan;
   }
 
   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;
   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)
 
   /* Scan forward to the target position.  */
   while (scan < end)
@@ -601,20 +574,6 @@ scan_for_column (EMACS_INT *endpos, EMACS_INT *goalcol, EMACS_INT *prevcol)
        break;
       prev_col = col;
 
        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);
       { /* Check display property.  */
        EMACS_INT end;
        int width = check_display_width (scan, col, &end);
@@ -629,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
       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;
          && VECTORP (DISP_CHAR_VECTOR (dp, c)))
        {
          Lisp_Object charvec;
@@ -686,7 +668,7 @@ scan_for_column (EMACS_INT *endpos, EMACS_INT *goalcol, EMACS_INT *prevcol)
              col += tab_width;
              col = col / tab_width * tab_width;
            }
              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;
            {
              /* Start of multi-byte form.  */
              unsigned char *ptr;
@@ -730,7 +712,7 @@ scan_for_column (EMACS_INT *endpos, EMACS_INT *goalcol, EMACS_INT *prevcol)
    due to text properties or overlays.  */
 
 static double
    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;
 {
   EMACS_INT col = MOST_POSITIVE_FIXNUM;
   EMACS_INT opoint = PT;
@@ -829,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.  */)
 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;
 {
   int mincol;
   register int fromcol;
@@ -874,14 +855,14 @@ The return value is COLUMN.  */)
 }
 
 \f
 }
 
 \f
-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.  */)
 
 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;
 {
   Lisp_Object val;
   int opoint = PT, opoint_byte = PT_BYTE;
@@ -894,8 +875,7 @@ following any initial whitespace.  */)
 }
 
 static double
 }
 
 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);
 {
   register EMACS_INT column = 0;
   register EMACS_INT tab_width = XINT (current_buffer->tab_width);
@@ -984,9 +964,7 @@ position_indentation (pos_byte)
    preceding line.  */
 
 int
    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;
 {
   double val;
   int opoint = PT, opoint_byte = PT_BYTE;
@@ -1018,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, 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;
 {
   EMACS_INT pos;
   EMACS_INT col, prev_col;
@@ -1144,12 +1121,7 @@ struct position val_compute_motion;
    the scroll bars if they are turned on.  */
 
 struct position *
    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;
 {
   register EMACS_INT hpos = fromhpos;
   register EMACS_INT vpos = fromvpos;
@@ -1197,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;
 
   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);
 
   XSETBUFFER (buffer, current_buffer);
   XSETWINDOW (window, win);
 
@@ -1237,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;
   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)
   while (1)
     {
       while (pos == next_boundary)
@@ -1351,10 +1329,20 @@ compute_motion (from, fromvpos, fromhpos, did_motion, to, tovpos, tohpos, width,
 
       if (hpos > 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
              || !NILP (current_buffer->truncate_lines))
            {
              /* Truncating: skip to newline, unless we are already past
@@ -1514,21 +1502,29 @@ compute_motion (from, fromvpos, fromhpos, did_motion, to, tovpos, tohpos, width,
          EMACS_INT i, n;
          Lisp_Object charvec;
 
          EMACS_INT i, n;
          Lisp_Object charvec;
 
-         c = FETCH_BYTE (pos_byte);
-
          /* Check composition sequence.  */
          /* 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.  */
          pos++, pos_byte++;
 
          /* Perhaps add some info to the width_run_cache.  */
@@ -1560,7 +1556,7 @@ compute_motion (from, fromvpos, fromhpos, did_motion, to, tovpos, tohpos, width,
            }
 
          if (dp != 0
            }
 
          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);
              && VECTORP (DISP_CHAR_VECTOR (dp, c)))
            {
              charvec = DISP_CHAR_VECTOR (dp, c);
@@ -1666,7 +1662,7 @@ compute_motion (from, fromvpos, fromhpos, did_motion, to, tovpos, tohpos, width,
                        hpos = 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;
                {
                  /* Start of multi-byte form.  */
                  unsigned char *ptr;
@@ -1758,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.  */)
 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;
 {
   struct window *w;
   Lisp_Object bufpos, hpos, vpos, prevhpos;
@@ -1839,9 +1833,7 @@ visible section of the buffer, and pass LINE and COL as TOPOS.  */)
 struct position val_vmotion;
 
 struct position *
 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;
 {
   EMACS_INT hscroll = XINT (w->hscroll);
   struct position pos;
@@ -1995,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.
 
 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.  */)
 `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;
 {
   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))
 
   CHECK_NUMBER (lines);
   if (! NILP (window))
@@ -2033,68 +2038,110 @@ whether or not it is currently displayed in some window.  */)
     }
   else
     {
     }
   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);
 
       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);
 
       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;
       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));
     }
 
       SET_PT_BOTH (IT_CHARPOS (it), IT_BYTEPOS (it));
     }
@@ -2110,7 +2157,7 @@ whether or not it is currently displayed in some window.  */)
 /* File's initialization.  */
 
 void
 /* 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.  */);
 {
   DEFVAR_BOOL ("indent-tabs-mode", &indent_tabs_mode,
               doc: /* *Indentation can insert tabs if this is non-nil.  */);