]> 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 b4443825c5d596ce0975cbcfd5875e0eb3899481..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,77 +469,162 @@ current_column ()
   return col;
 }
 \f
   return col;
 }
 \f
-/* 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 ()
+/* 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);
 {
   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.  */
 
   /* 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 scan, scan_byte;
   EMACS_INT next_boundary;
+  {
   EMACS_INT opoint = PT, opoint_byte = PT_BYTE;
   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;
   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;
 
   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.  */
 
   /* 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 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.  */
          /* 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 != 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);
 
       c = FETCH_BYTE (scan_byte);
 
+      /* See if there is a display table and it relates
+        to this character.  */
+
       if (dp != 0
       if (dp != 0
-         && ! (multibyte && BASE_LEADING_CODE_P (c))
+         && ! (multibyte && 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.
          && VECTORP (DISP_CHAR_VECTOR (dp, c)))
        {
          Lisp_Object charvec;
          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);
 
          charvec = DISP_CHAR_VECTOR (dp, c);
          n = ASIZE (charvec);
@@ -606,8 +656,8 @@ current_column_1 ()
        }
       else
        {
        }
       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;
 
          if (c == '\n')
            goto endloop;
@@ -618,17 +668,17 @@ current_column_1 ()
              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;
              int bytes, width, wide_column;
 
              ptr = BYTE_POS_ADDR (scan_byte);
              MULTIBYTE_BYTES_WIDTH (ptr, dp);
              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.  */
              /* 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))
              col += width;
            }
          else if (ctl_arrow && (c < 040 || c == 0177))
@@ -648,6 +698,26 @@ current_column_1 ()
   last_known_column_point = PT;
   last_known_column_modified = MODIFF;
 
   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 (void)
+{
+  EMACS_INT col = MOST_POSITIVE_FIXNUM;
+  EMACS_INT opoint = PT;
+
+  scan_for_column (&opoint, &col, NULL);
   return col;
 }
 \f
   return col;
 }
 \f
@@ -741,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;
@@ -786,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;
@@ -806,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);
@@ -896,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;
@@ -930,182 +996,50 @@ 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)
 {
 {
-  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);
 
   CHECK_NATNUM (column);
   goal = XINT (column);
 
-  pos = PT;
-  pos_byte = PT_BYTE;
-  end = ZV;
+  col = goal;
+  pos = ZV;
+  scan_for_column (&pos, &col, &prev_col);
 
 
-  /* 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;
-    }
+  SET_PT (pos);
 
 
-  next_boundary = 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 = AREF (charvec, i);
-
-             if (GLYPH_CODE_P (entry)
-                 && GLYPH_CODE_CHAR_VALID_P (entry))
-               c = GLYPH_CODE_CHAR (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.  */
     }
 
   /* If line ends prematurely, add space to the end.  */
@@ -1116,8 +1050,7 @@ The return value is the current column.  */)
   last_known_column_point = PT;
   last_known_column_modified = MODIFF;
 
   last_known_column_point = PT;
   last_known_column_modified = MODIFF;
 
-  XSETFASTINT (val, col);
-  return val;
+  return make_number (col);
 }
 \f
 /* compute_motion: compute buffer posn given screen posn and vice versa */
 }
 \f
 /* compute_motion: compute buffer posn given screen posn and vice versa */
@@ -1188,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;
@@ -1241,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);
 
@@ -1281,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)
@@ -1395,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
@@ -1558,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.  */
@@ -1604,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);
@@ -1710,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;
@@ -1802,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;
@@ -1883,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;
@@ -2039,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))
@@ -2077,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));
     }
@@ -2154,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.  */);