]> code.delx.au - gnu-emacs/blobdiff - src/indent.c
(current_column_1, Fmove_to_column):
[gnu-emacs] / src / indent.c
index e075e236d1b21353680d6febe49f6f56ab02a027..cf8e361a00cfcefe5a6d0d5bb00e24e32c2194ba 100644 (file)
@@ -5,7 +5,7 @@ This file is part of GNU Emacs.
 
 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 1, or (at your option)
+the Free Software Foundation; either version 2, or (at your option)
 any later version.
 
 GNU Emacs is distributed in the hope that it will be useful,
@@ -15,12 +15,15 @@ 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, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
 
 
 #include <config.h>
 #include "lisp.h"
 #include "buffer.h"
+#include "charset.h"
+#include "category.h"
 #include "indent.h"
 #include "frame.h"
 #include "window.h"
@@ -49,19 +52,25 @@ int last_known_column_point;
 /* Value of MODIFF when current_column was called */
 int last_known_column_modified;
 
+static int current_column_1 ();
+static int position_indentation ();
+
+/* Cache of beginning of line found by the last call of
+   current_column. */
+int current_column_bol_cache;
+
 /* Get the display table to use for the current buffer.  */
 
-struct Lisp_Vector *
+struct Lisp_Char_Table *
 buffer_display_table ()
 {
   Lisp_Object thisbuf;
 
   thisbuf = current_buffer->display_table;
-  if (VECTORP (thisbuf) && XVECTOR (thisbuf)->size == DISP_TABLE_SIZE)
-    return XVECTOR (thisbuf);
-  if (VECTORP (Vstandard_display_table)
-      && XVECTOR (Vstandard_display_table)->size == DISP_TABLE_SIZE)
-    return XVECTOR (Vstandard_display_table);
+  if (DISP_TABLE_P (thisbuf))
+    return XCHAR_TABLE (thisbuf);
+  if (DISP_TABLE_P (Vstandard_display_table))
+    return XCHAR_TABLE (Vstandard_display_table);
   return 0;
 }
 \f
@@ -72,7 +81,7 @@ buffer_display_table ()
 static int
 character_width (c, dp)
      int c;
-     struct Lisp_Vector *dp;
+     struct Lisp_Char_Table *dp;
 {
   Lisp_Object elt;
 
@@ -106,7 +115,7 @@ character_width (c, dp)
    invalidate the buffer's width_run_cache.  */
 int
 disptab_matches_widthtab (disptab, widthtab)
-     struct Lisp_Vector *disptab;
+     struct Lisp_Char_Table *disptab;
      struct Lisp_Vector *widthtab;
 {
   int i;
@@ -126,7 +135,7 @@ disptab_matches_widthtab (disptab, widthtab)
 void
 recompute_width_table (buf, disptab)
      struct buffer *buf;
-     struct Lisp_Vector *disptab;
+     struct Lisp_Char_Table *disptab;
 {
   int i;
   struct Lisp_Vector *widthtab;
@@ -146,7 +155,10 @@ recompute_width_table (buf, disptab)
 static void
 width_run_cache_on_off ()
 {
-  if (NILP (current_buffer->cache_long_line_scans))
+  if (NILP (current_buffer->cache_long_line_scans)
+      /* And, for the moment, this feature doesn't work on multibyte
+         characters.  */
+      || !NILP (current_buffer->enable_multibyte_characters))
     {
       /* It should be off.  */
       if (current_buffer->width_run_cache)
@@ -167,6 +179,92 @@ width_run_cache_on_off ()
     }
 }
 
+\f
+/* Skip some invisible characters starting from POS.
+   This includes characters invisible because of text properties
+   and characters invisible because of overlays.
+
+   If position POS is followed by invisible characters,
+   skip some of them and return the position after them.
+   Otherwise return POS itself.
+
+   Set *NEXT_BOUNDARY_P to the next position at which
+   it will be necessary to call this function again.
+
+   Don't scan past TO, and don't set *NEXT_BOUNDARY_P
+   to a value greater than TO.
+
+   If WINDOW is non-nil, and this buffer is displayed in WINDOW,
+   take account of overlays that apply only in WINDOW.
+
+   We don't necessarily skip all the invisible characters after POS
+   because that could take a long time.  We skip a reasonable number
+   which can be skipped quickly.  If there might be more invisible
+   characters immediately following, then *NEXT_BOUNDARY_P
+   will equal the return value.  */
+
+static int
+skip_invisible (pos, next_boundary_p, to, window)
+     int pos;
+     int *next_boundary_p;
+     int to;
+     Lisp_Object window;
+{
+  Lisp_Object prop, position, overlay_limit, proplimit;
+  Lisp_Object buffer;
+  int end;
+
+  XSETFASTINT (position, pos);
+  XSETBUFFER (buffer, current_buffer);
+
+  /* Give faster response for overlay lookup near POS.  */
+  recenter_overlay_lists (current_buffer, pos);
+
+  /* We must not advance farther than the next overlay change.
+     The overlay change might change the invisible property;
+     or there might be overlay strings to be displayed there.  */
+  overlay_limit = Fnext_overlay_change (position);
+  /* As for text properties, this gives a lower bound
+     for where the invisible text property could change.  */
+  proplimit = Fnext_property_change (position, buffer, Qt);
+  if (XFASTINT (overlay_limit) < XFASTINT (proplimit))
+    proplimit = overlay_limit;
+  /* PROPLIMIT is now a lower bound for the next change
+     in invisible status.  If that is plenty far away,
+     use that lower bound.  */
+  if (XFASTINT (proplimit) > pos + 100 || XFASTINT (proplimit) >= to)
+    *next_boundary_p = XFASTINT (proplimit);
+  /* Otherwise, scan for the next `invisible' property change.  */
+  else
+    {
+      /* Don't scan terribly far.  */
+      XSETFASTINT (proplimit, min (pos + 100, to));
+      /* No matter what. don't go past next overlay change.  */
+      if (XFASTINT (overlay_limit) < XFASTINT (proplimit))
+       proplimit = overlay_limit;
+      end = XFASTINT (Fnext_single_property_change (position, Qinvisible,
+                                                   buffer, proplimit));
+#if 0
+      /* Don't put the boundary in the middle of multibyte form if
+         there is no actual property change.  */
+      if (end == pos + 100
+         && !NILP (current_buffer->enable_multibyte_characters)
+         && end < ZV)
+       while (pos < end && !CHAR_HEAD_P (POS_ADDR (end)))
+         end--;
+#endif
+      *next_boundary_p = end;
+    }
+  /* if the `invisible' property is set, we can skip to
+     the next property change */
+  if (!NILP (window) && EQ (XWINDOW (window)->buffer, buffer))
+    prop = Fget_char_property (position, Qinvisible, window);
+  else
+    prop = Fget_char_property (position, Qinvisible, buffer);
+  if (TEXT_PROP_MEANS_INVISIBLE (prop))
+    return *next_boundary_p;
+  return pos;
+}
 \f
 DEFUN ("current-column", Fcurrent_column, Scurrent_column, 0, 0, 0,
   "Return the horizontal position of point.  Beginning of line is column 0.\n\
@@ -187,6 +285,7 @@ however, ^M is treated as end of line when `selective-display' is t.")
 
 /* Cancel any recorded value of the horizontal position.  */
 
+void
 invalidate_current_column ()
 {
   last_known_column_point = 0;
@@ -202,20 +301,31 @@ current_column ()
   register int c;
   register int tab_width = XINT (current_buffer->tab_width);
   int ctl_arrow = !NILP (current_buffer->ctl_arrow);
-  register struct Lisp_Vector *dp = buffer_display_table ();
+  register struct Lisp_Char_Table *dp = buffer_display_table ();
   int stopchar;
 
-  if (point == last_known_column_point
+  if (PT == last_known_column_point
       && MODIFF == last_known_column_modified)
     return last_known_column;
 
+  /* If the buffer has overlays, text properties, or multibyte, 
+     use a more general algorithm.  */
+  if (BUF_INTERVALS (current_buffer)
+      || !NILP (current_buffer->overlays_before)
+      || !NILP (current_buffer->overlays_after)
+      || !NILP (current_buffer->enable_multibyte_characters))
+    return current_column_1 (PT);
+
+  /* Scan backwards from point to the previous newline,
+     counting width.  Tab characters are the only complicated case.  */
+
   /* Make a pointer for decrementing through the chars before point.  */
-  ptr = &FETCH_CHAR (point - 1) + 1;
+  ptr = BYTE_POS_ADDR (PT_BYTE - 1) + 1;
   /* Make a pointer to where consecutive chars leave off,
      going backwards from point.  */
-  if (point == BEGV)
+  if (PT == BEGV)
     stop = ptr;
-  else if (point <= GPT || BEGV > GPT)
+  else if (PT <= GPT || BEGV > GPT)
     stop = BEGV_ADDR;
   else
     stop = GAP_END_ADDR;
@@ -244,10 +354,12 @@ current_column ()
        col += XVECTOR (DISP_CHAR_VECTOR (dp, c))->size;
       else if (c >= 040 && c < 0177)
        col++;
-      else if (c == '\n')
-       break;
-      else if (c == '\r' && EQ (current_buffer->selective_display, Qt))
-       break;
+      else if (c == '\n'
+              || (c == '\r' && EQ (current_buffer->selective_display, Qt)))
+       {
+         ptr++;
+         break;
+       }
       else if (c == '\t')
        {
          if (tab_seen)
@@ -267,8 +379,136 @@ current_column ()
       col += post_tab;
     }
 
+  if (ptr == BEGV_ADDR)
+    current_column_bol_cache = BEGV;
+  else
+    current_column_bol_cache = BYTE_TO_CHAR (PTR_BYTE_POS (ptr));
+
   last_known_column = col;
-  last_known_column_point = point;
+  last_known_column_point = PT;
+  last_known_column_modified = MODIFF;
+
+  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 int
+current_column_1 (pos)
+     int pos;
+{
+  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 ();
+  int multibyte = !NILP (current_buffer->enable_multibyte_characters);
+
+  /* Start the scan at the beginning of this line with column number 0.  */
+  register int col = 0;
+  int scan, scan_byte;
+  int next_boundary, next_boundary_byte;
+  int opoint = PT, opoint_byte = PT_BYTE;
+
+  scan_newline (pos, CHAR_TO_BYTE (pos), 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;
+  next_boundary_byte = scan_byte;
+
+  if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
+
+  /* Scan forward to the target position.  */
+  while (scan < pos)
+    {
+      int c;
+
+      /* Occasionally we may need to skip invisible text.  */
+      while (scan == next_boundary)
+       {
+         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, pos, Qnil);
+         if (scan >= pos)
+           goto endloop;
+         if (scan != old_scan)
+           scan_byte = CHAR_TO_BYTE (scan);
+         next_boundary_byte = CHAR_TO_BYTE (next_boundary);
+       }
+
+      c = FETCH_BYTE (scan);
+      if (dp != 0 && VECTORP (DISP_CHAR_VECTOR (dp, c)))
+       {
+         col += XVECTOR (DISP_CHAR_VECTOR (dp, c))->size;
+         scan++;
+         scan_byte++;
+         continue;
+       }
+      if (c == '\n')
+       break;
+      if (c == '\r' && EQ (current_buffer->selective_display, Qt))
+       break;
+      scan++;
+      scan_byte++;
+      if (c == '\t')
+       {
+         int prev_col = col;
+         col += tab_width;
+         col = col / tab_width * tab_width;
+       }
+      else if (multibyte && BASE_LEADING_CODE_P (c))
+       {
+         scan_byte--;
+         /* Start of multi-byte form.  */
+         if (c == LEADING_CODE_COMPOSITION)
+           {
+             unsigned char *ptr = BYTE_POS_ADDR (scan_byte);
+
+             int cmpchar_id
+               = str_cmpchar_id (ptr, next_boundary_byte - scan_byte);
+             if (cmpchar_id >= 0)
+               {
+                 scan_byte += cmpchar_table[cmpchar_id]->len;
+                 col += cmpchar_table[cmpchar_id]->width;
+               }
+             else
+               {               /* invalid composite character */
+                 scan_byte++;
+                 col += 4;
+               }
+           }
+         else
+           {
+             /* Here, we check that the following bytes are valid
+                constituents of multi-byte form.  */
+             int len = BYTES_BY_CHAR_HEAD (c), i;
+
+             for (i = 1, scan_byte++; i < len; i++, scan_byte++)
+               /* We don't need range checking for PTR because there
+                  are anchors (`\0') at GAP and Z.  */
+               if (CHAR_HEAD_P (FETCH_BYTE (scan_byte)))
+                 break;
+
+             if (i < len)
+               col += 4, scan_byte -= i - 1;
+             else
+               col += WIDTH_BY_CHAR_HEAD (c);
+           }
+       }
+      else if (ctl_arrow && (c < 040 || c == 0177))
+        col += 2;
+      else if (c < 040 || c >= 0177)
+        col += 4;
+      else
+       col++;
+    }
+ endloop:
+
+  last_known_column = col;
+  last_known_column_point = PT;
   last_known_column_modified = MODIFF;
 
   return col;
@@ -289,7 +529,7 @@ string_display_width (string, beg, end)
   register int c;
   register int tab_width = XINT (current_buffer->tab_width);
   int ctl_arrow = !NILP (current_buffer->ctl_arrow);
-  register struct Lisp_Vector *dp = buffer_display_table ();
+  register struct Lisp_Char_Table *dp = buffer_display_table ();
   int b, e;
 
   if (NILP (end))
@@ -354,23 +594,23 @@ string_display_width (string, beg, end)
 \f
 DEFUN ("indent-to", Findent_to, Sindent_to, 1, 2, "NIndent to column: ",
   "Indent from point with tabs and spaces until COLUMN is reached.\n\
-Optional second argument MIN says always do at least MIN spaces\n\
-even if that goes past COLUMN; by default, MIN is zero.")
-  (col, minimum)
-     Lisp_Object col, minimum;
+Optional second argument MININUM says always do at least MININUM spaces\n\
+even if that goes past COLUMN; by default, MININUM is zero.")
+  (column, minimum)
+     Lisp_Object column, minimum;
 {
   int mincol;
   register int fromcol;
   register int tab_width = XINT (current_buffer->tab_width);
 
-  CHECK_NUMBER (col, 0);
+  CHECK_NUMBER (column, 0);
   if (NILP (minimum))
     XSETFASTINT (minimum, 0);
   CHECK_NUMBER (minimum, 1);
 
   fromcol = current_column ();
   mincol = fromcol + XINT (minimum);
-  if (mincol < XINT (col)) mincol = XINT (col);
+  if (mincol < XINT (column)) mincol = XINT (column);
 
   if (fromcol == mincol)
     return make_number (mincol);
@@ -389,15 +629,15 @@ even if that goes past COLUMN; by default, MIN is zero.")
        }
     }
 
-  XSETFASTINT (col, mincol - fromcol);
-  Finsert_char (make_number (' '), col, Qt);
+  XSETFASTINT (column, mincol - fromcol);
+  Finsert_char (make_number (' '), column, Qt);
 
   last_known_column = mincol;
-  last_known_column_point = point;
+  last_known_column_point = PT;
   last_known_column_modified = MODIFF;
 
-  XSETINT (col, mincol);
-  return col;
+  XSETINT (column, mincol);
+  return column;
 }
 
 \f
@@ -409,35 +649,74 @@ following any initial whitespace.")
   ()
 {
   Lisp_Object val;
+  int opoint = PT, opoint_byte = PT_BYTE;
 
-  XSETFASTINT (val, position_indentation (find_next_newline (point, -1)));
+  scan_newline (PT, PT_BYTE, BEGV, BEGV_BYTE, -1, 1);
+
+  XSETFASTINT (val, position_indentation (PT_BYTE));
+  SET_PT_BOTH (opoint, opoint_byte);
   return val;
 }
 
-position_indentation (pos)
-     register int pos;
+static int
+position_indentation (pos_byte)
+     register int pos_byte;
 {
   register int column = 0;
   register int tab_width = XINT (current_buffer->tab_width);
   register unsigned char *p;
   register unsigned char *stop;
+  unsigned char *start;
+  int next_boundary_byte = pos_byte;
+  int ceiling = next_boundary_byte;
 
   if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
 
-  stop = &FETCH_CHAR (BUFFER_CEILING_OF (pos)) + 1;
-  p = &FETCH_CHAR (pos);
+  p = BYTE_POS_ADDR (pos_byte);
+  /* STOP records the value of P at which we will need
+     to think about the gap, or about invisible text,
+     or about the end of the buffer.  */
+  stop = p;
+  /* START records the starting value of P.  */
+  start = p;
   while (1)
     {
       while (p == stop)
        {
-         if (pos == ZV)
+         int stop_pos_byte;
+
+         /* If we have updated P, set POS_BYTE to match.
+            The first time we enter the loop, POS_BYTE is already right.  */
+         if (p != start)
+           pos_byte = PTR_BYTE_POS (p);
+         /* Consider the various reasons STOP might have been set here.  */
+         if (pos_byte == ZV_BYTE)
            return column;
-         pos += p - &FETCH_CHAR (pos);
-         p = &FETCH_CHAR (pos);
-         stop = &FETCH_CHAR (BUFFER_CEILING_OF (pos)) + 1;
+         if (pos_byte == next_boundary_byte)
+           {
+             int next_boundary;
+             int pos = BYTE_TO_CHAR (pos_byte);
+             pos = skip_invisible (pos, &next_boundary, ZV, Qnil);
+             pos_byte = CHAR_TO_BYTE (pos);
+             next_boundary_byte = CHAR_TO_BYTE (next_boundary);
+           }
+         if (pos_byte >= ceiling)
+           ceiling = BUFFER_CEILING_OF (pos_byte) + 1;
+         /* Compute the next place we need to stop and think,
+            and set STOP accordingly.  */
+         stop_pos_byte = min (ceiling, next_boundary_byte);
+         /* The -1 and +1 arrange to point at the first byte of gap
+            (if STOP_POS_BYTE is the position of the gap)
+            rather than at the data after the gap.  */
+            
+         stop = BYTE_POS_ADDR (stop_pos_byte - 1) + 1;
+         p = BYTE_POS_ADDR (pos_byte);
        }
       switch (*p++)
        {
+       case 0240:
+         if (! NILP (current_buffer->enable_multibyte_characters))
+           return column;
        case ' ':
          column++;
          break;
@@ -445,7 +724,22 @@ position_indentation (pos)
          column += tab_width - column % tab_width;
          break;
        default:
-         return column;
+         if (ASCII_BYTE_P (p[-1])
+             || NILP (current_buffer->enable_multibyte_characters))
+           return column;
+         {
+           int c;
+           pos_byte = PTR_BYTE_POS (p - 1);
+           c = FETCH_MULTIBYTE_CHAR (pos_byte);
+           if (CHAR_HAS_CATEGORY (c, ' '))
+             {
+               column++;
+               INC_POS (pos_byte);
+               p = BYTE_POS_ADDR (pos_byte);
+             }
+           else
+             return column;
+         }
        }
     }
 }
@@ -453,17 +747,24 @@ position_indentation (pos)
 /* Test whether the line beginning at POS is indented beyond COLUMN.
    Blank lines are treated as if they had the same indentation as the
    preceding line.  */
+
 int
-indented_beyond_p (pos, column)
-     int pos, column;
+indented_beyond_p (pos, pos_byte, column)
+     int pos, pos_byte, column;
 {
-  while (pos > BEGV && FETCH_CHAR (pos) == '\n')
-    pos = find_next_newline_no_quit (pos - 1, -1);
-  return (position_indentation (pos) >= column);
-}
+  Lisp_Object val;
+  int opoint = PT, opoint_byte = PT_BYTE;
+
+  SET_PT_BOTH (pos, pos_byte);
+  while (PT > BEGV && FETCH_BYTE (PT_BYTE) == '\n')
+    scan_newline (PT - 1, PT_BYTE - 1, BEGV, BEGV_BYTE, -1, 0);
 
+  XSETFASTINT (val, position_indentation (PT_BYTE));
+  SET_PT_BOTH (opoint, opoint_byte);
+  return val;
+}
 \f
-DEFUN ("move-to-column", Fmove_to_column, Smove_to_column, 1, 2, 0,
+DEFUN ("move-to-column", Fmove_to_column, Smove_to_column, 1, 2, "p",
   "Move point to column COLUMN in the current line.\n\
 The column of a character is calculated by adding together the widths\n\
 as displayed of the previous characters in the line.\n\
@@ -475,7 +776,9 @@ If specified column is within a character, point goes after that character.\n\
 If it's past end of line, point goes to end of line.\n\n\
 A non-nil second (optional) argument FORCE means, if the line\n\
 is too short to reach column COLUMN then add spaces/tabs to get there,\n\
-and if COLUMN is in the middle of a tab character, change it to spaces.")
+and if COLUMN is in the middle of a tab character, change it to spaces.\n\
+\n\
+The return value is the current column.")
   (column, force)
      Lisp_Object column, force;
 {
@@ -485,34 +788,61 @@ and if COLUMN is in the middle of a tab character, change it to spaces.")
   register int end;
   register int tab_width = XINT (current_buffer->tab_width);
   register int ctl_arrow = !NILP (current_buffer->ctl_arrow);
-  register struct Lisp_Vector *dp = buffer_display_table ();
+  register struct Lisp_Char_Table *dp = buffer_display_table ();
+  register int multibyte = !NILP (current_buffer->enable_multibyte_characters);
 
   Lisp_Object val;
   int prev_col;
   int c;
+  int next_boundary;
+
+  int pos_byte, end_byte, next_boundary_byte;
 
   if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
   CHECK_NATNUM (column, 0);
   goal = XINT (column);
 
- retry:
-  pos = point;
+  pos = PT;
+  pos_byte = PT_BYTE;
   end = ZV;
+  end_byte = ZV_BYTE;
+  next_boundary = pos;
+  next_boundary_byte = PT_BYTE;
 
   /* If we're starting past the desired column,
      back up to beginning of line and scan from there.  */
   if (col > goal)
     {
-      pos = find_next_newline (pos, -1);
+      end = pos;
+      pos = current_column_bol_cache;
+      pos_byte = CHAR_TO_BYTE (pos);
       col = 0;
     }
 
-  while (col < goal && pos < end)
+  while (pos < end)
     {
-      c = FETCH_CHAR (pos);
+      while (pos == next_boundary)
+       {
+         int prev = pos;
+         pos = skip_invisible (pos, &next_boundary, end, Qnil);
+         if (pos != prev)
+           pos_byte = CHAR_TO_BYTE (pos);
+         next_boundary_byte = CHAR_TO_BYTE (next_boundary);
+         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;
+
+      c = FETCH_BYTE (pos_byte);
       if (dp != 0 && VECTORP (DISP_CHAR_VECTOR (dp, c)))
        {
          col += XVECTOR (DISP_CHAR_VECTOR (dp, c))->size;
+         pos_byte++;
          pos++;
          continue;
        }
@@ -521,6 +851,7 @@ and if COLUMN is in the middle of a tab character, change it to spaces.")
       if (c == '\r' && EQ (current_buffer->selective_display, Qt))
        break;
       pos++;
+      pos_byte++;
       if (c == '\t')
        {
          prev_col = col;
@@ -529,25 +860,69 @@ and if COLUMN is in the middle of a tab character, change it to spaces.")
        }
       else if (ctl_arrow && (c < 040 || c == 0177))
         col += 2;
-      else if (c < 040 || c >= 0177)
+      else if (c < 040 || c == 0177)
         col += 4;
-      else
+      else if (c < 0177)
        col++;
+      else if (multibyte && BASE_LEADING_CODE_P (c))
+       {
+         /* Start of multi-byte form.  */
+         unsigned char *ptr;
+
+         pos_byte--;           /* rewind to the character head */
+         ptr = BYTE_POS_ADDR (pos_byte);
+         if (c == LEADING_CODE_COMPOSITION)
+           {
+             int cmpchar_id = str_cmpchar_id (ptr, end_byte - pos_byte);
+
+             if (cmpchar_id >= 0)
+               {
+                 col += cmpchar_table[cmpchar_id]->width;
+                 pos_byte += cmpchar_table[cmpchar_id]->len;
+               }
+             else
+               {               /* invalid composite character */
+                 col += 4;
+                 pos_byte++;
+               }
+           }
+         else
+           {
+             /* Here, we check that the following bytes are valid
+                constituents of multi-byte form.  */
+             int len = BYTES_BY_CHAR_HEAD (c), i;
+
+             for (i = 1, ptr++; i < len; i++, ptr++)
+               /* We don't need range checking for PTR because there
+                  are anchors (`\0') both at GPT and Z.  */
+               if (CHAR_HEAD_P (*ptr))
+                 break;
+
+             if (i < len)
+               col += 4, pos_byte++;
+             else
+               col += WIDTH_BY_CHAR_HEAD (c), pos_byte += i;
+           }
+       }
+      else
+       col += 4;
     }
+ endloop:
 
-  SET_PT (pos);
+  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)
     {
-      int old_point;
+      int old_point, old_point_byte;
 
-      del_range (point - 1, point);
+      del_range (PT - 1, PT);
       Findent_to (make_number (goal), Qnil);
-      old_point = point;
+      old_point = PT;
+      old_point_byte = PT_BYTE;
       Findent_to (make_number (col), Qnil);
-      SET_PT (old_point);
+      SET_PT_BOTH (old_point, old_point_byte);
       /* Set the last_known... vars consistently.  */
       col = goal;
     }
@@ -557,13 +932,12 @@ and if COLUMN is in the middle of a tab character, change it to spaces.")
     Findent_to (make_number (col = goal), Qnil);
 
   last_known_column = col;
-  last_known_column_point = point;
+  last_known_column_point = PT;
   last_known_column_modified = MODIFF;
 
   XSETFASTINT (val, col);
   return val;
 }
-
 \f
 /* compute_motion: compute buffer posn given screen posn and vice versa */
 
@@ -576,6 +950,12 @@ struct position val_compute_motion;
    can't hit the requested column exactly (because of a tab or other
    multi-column character), overshoot.
 
+   DID_MOTION is 1 if FROMHPOS has already accounted for overlay strings
+   at FROM.  This is the case if FROMVPOS and FROMVPOS came from an
+   earlier call to compute_motion.  The other common case is that FROMHPOS
+   is zero and FROM is a position that "belongs" at column zero, but might
+   be shifted by overlay strings; in this case DID_MOTION should be 0.
+
    WIDTH is the number of columns available to display text;
    compute_motion uses this to handle continuation lines and such.
    HSCROLL is the number of columns not being displayed at the left
@@ -595,11 +975,6 @@ struct position val_compute_motion;
    into account.  That is, beginning-of-line moves you to the hpos
    -HSCROLL + (HSCROLL > 0).
 
-   Note that FROMHPOS and TOHPOS should be expressed in real screen
-   columns, taking HSCROLL and the truncation glyph at the left margin
-   into account.  That is, beginning-of-line moves you to the hpos
-   -HSCROLL + (HSCROLL > 0).
-
    For example, to find the buffer position of column COL of line LINE
    of a certain window, pass the window's starting location as FROM
    and the window's upper-left coordinates as FROMVPOS and FROMHPOS.
@@ -629,8 +1004,9 @@ struct position val_compute_motion;
    the scroll bars if they are turned on.  */
 
 struct position *
-compute_motion (from, fromvpos, fromhpos, to, tovpos, tohpos, width, hscroll, tab_offset, win)
+compute_motion (from, fromvpos, fromhpos, did_motion, to, tovpos, tohpos, width, hscroll, tab_offset, win)
      int from, fromvpos, fromhpos, to, tovpos, tohpos;
+     int did_motion;
      register int width;
      int hscroll, tab_offset;
      struct window *win;
@@ -639,23 +1015,22 @@ compute_motion (from, fromvpos, fromhpos, to, tovpos, tohpos, width, hscroll, ta
   register int vpos = fromvpos;
 
   register int pos;
+  int pos_byte;
   register int c;
   register int tab_width = XFASTINT (current_buffer->tab_width);
   register int ctl_arrow = !NILP (current_buffer->ctl_arrow);
-  register struct Lisp_Vector *dp = window_display_table (win);
+  register struct Lisp_Char_Table *dp = window_display_table (win);
   int selective
     = (INTEGERP (current_buffer->selective_display)
        ? XINT (current_buffer->selective_display)
        : !NILP (current_buffer->selective_display) ? -1 : 0);
-  int prev_vpos = vpos, prev_hpos = 0;
+  int prev_hpos = 0;
   int selective_rlen
     = (selective && dp && VECTORP (DISP_INVIS_VECTOR (dp))
        ? XVECTOR (DISP_INVIS_VECTOR (dp))->size : 0);
-#ifdef USE_TEXT_PROPERTIES
-  /* The next location where the `invisible' property changes */
-  int next_invisible = from;
-  Lisp_Object prop, position;
-#endif
+  /* The next location where the `invisible' property changes, or an
+     overlay starts or ends.  */
+  int next_boundary = from;
 
   /* For computing runs of characters with similar widths.
      Invariant: width_run_width is zero, or all the characters
@@ -669,8 +1044,18 @@ compute_motion (from, fromvpos, fromhpos, to, tovpos, tohpos, width, hscroll, ta
 
   /* The next buffer pos where we should consult the width run cache. */
   int next_width_run = from;
+  Lisp_Object window;
+
+  int multibyte = !NILP (current_buffer->enable_multibyte_characters);
+  int wide_column = 0;         /* Set to 1 when a previous character
+                                  is wide-colomn.  */
+  int prev_pos;                        /* Previous buffer position.  */
+  int prev_pos_byte;           /* Previous buffer position.  */
+  int contin_hpos;             /* HPOS of last column of continued line.  */
+  int prev_tab_offset;         /* Previous tab offset.  */
 
   XSETBUFFER (buffer, current_buffer);
+  XSETWINDOW (window, win);
 
   width_run_cache_on_off ();
   if (dp == buffer_display_table ())
@@ -683,68 +1068,199 @@ compute_motion (from, fromvpos, fromhpos, to, tovpos, tohpos, width, hscroll, ta
     width_table = 0;
 
   if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
-  for (pos = from; pos < to; )
+
+  pos = prev_pos = from;
+  pos_byte = prev_pos_byte = CHAR_TO_BYTE (from);
+  contin_hpos = 0;
+  prev_tab_offset = tab_offset;
+  while (1)
     {
-      /* Stop if past the target screen position.  */
-      if (vpos > tovpos
-         || (vpos == tovpos && hpos >= tohpos))
-       break;
+      while (pos == next_boundary)
+       {
+         int pos_here = pos;
+         int newpos;
+
+         /* If the caller says that the screen position came from an earlier
+            call to compute_motion, then we've already accounted for the
+            overlay strings at point.  This is only true the first time
+            through, so clear the flag after testing it.  */
+         if (!did_motion)
+           /* We need to skip past the overlay strings.  Currently those
+              strings must not contain TAB;
+              if we want to relax that restriction, something will have
+              to be changed here.  */
+           {
+             unsigned char *ovstr;
+             int ovlen = overlay_strings (pos, win, &ovstr);
+             hpos += (multibyte ? strwidth (ovstr, ovlen) : ovlen);
+           }
+         did_motion = 0;
 
-      prev_vpos = vpos;
-      prev_hpos = hpos;
+         if (pos >= to)
+           break;
 
-#ifdef USE_TEXT_PROPERTIES
-      /* if the `invisible' property is set, we can skip to
-        the next property change */
-      while (pos == next_invisible && pos < to)
+         /* Advance POS past invisible characters
+            (but not necessarily all that there are here),
+            and store in next_boundary the next position where
+            we need to call skip_invisible.  */
+         newpos = skip_invisible (pos, &next_boundary, to, window);
+
+         if (newpos >= to)
+           goto after_loop;
+
+         if (newpos != pos_here)
+           {
+             pos = newpos;
+             pos_byte = CHAR_TO_BYTE (pos);
+           }
+       }
+
+      /* Handle right margin.  */
+      /* Note on a wide-column character.
+
+        Characters are classified into the following three categories
+        according to the width (columns occupied on screen).
+
+        (1) single-column character: ex. `a'
+        (2) multi-column character: ex. `^A', TAB, `\033'
+        (3) wide-column character: ex. Japanese character, Chinese character
+            (In the following example, `W_' stands for them.)
+
+        Multi-column characters can be divided around the right margin,
+        but wide-column characters cannot.
+
+        NOTE:
+
+        (*) The cursor is placed on the next character after the point.
+
+            ----------
+            abcdefghi\
+            j        ^---- next after the point
+            ^---  next char. after the point.
+            ----------
+                     In case of sigle-column character
+
+            ----------
+            abcdefgh\\
+            033     ^----  next after the point, next char. after the point.
+            ----------
+                     In case of multi-column character
+
+            ----------
+            abcdefgh\\
+            W_      ^---- next after the point
+            ^----  next char. after the point.
+            ----------
+                     In case of wide-column character 
+
+        The problem here is continuation at a wide-column character.
+        In this case, the line may shorter less than WIDTH.
+        And we find the continuation AFTER it occurs.
+
+       */
+
+      if (hpos > width)
        {
-         XSETFASTINT (position, pos);
+         if (hscroll
+             || (truncate_partial_width_windows
+                 && width + 1 < FRAME_WIDTH (XFRAME (WINDOW_FRAME (win))))
+             || !NILP (current_buffer->truncate_lines))
+           {
+             /* Truncating: skip to newline.  */
+             if (pos <= to)  /* This IF is needed because we may past TO */
+               {
+                 pos = find_before_next_newline (pos, to, 1);
+                 pos_byte = CHAR_TO_BYTE (pos);
+               }
+             hpos = width;
+             /* If we just skipped next_boundary,
+                loop around in the main while
+                and handle it.  */
+             if (pos >= next_boundary)
+               next_boundary = pos + 1;
+             prev_hpos = width;
+             prev_tab_offset = tab_offset;
+           }
+         else
+           {
+             /* Continuing.  */
+             /* Remember the previous value.  */
+             prev_tab_offset = tab_offset;
+
+             if (wide_column)
+               {
+                 hpos -= prev_hpos;
+                 tab_offset += prev_hpos;
+               }
+             else
+               {
+                 tab_offset += width;
+                 hpos -= width;
+               }
+             vpos++;
+             contin_hpos = prev_hpos;
+             prev_hpos = 0;
+           }
+       }
 
-         /* Give faster response for overlay lookup near POS.  */
-         recenter_overlay_lists (current_buffer, pos);
+      /* Stop if past the target buffer position or screen position.  */
+      if (pos > to)
+       {
+         /* Go back to the previous position.  */
+         pos = prev_pos;
+         pos_byte = prev_pos_byte;
+         hpos = prev_hpos;
+         tab_offset = prev_tab_offset;
+
+         /* NOTE on contin_hpos, hpos, and prev_hpos.
+
+            ----------
+            abcdefgh\\
+            W_      ^----  contin_hpos
+            | ^-----  hpos
+            \---- prev_hpos
+            ----------
+          */
+
+         if (contin_hpos && prev_hpos == 0
+             && contin_hpos < width && !wide_column)
+           {
+             /* Line breaking occurs in the middle of multi-column
+                character.  Go back to previous line.  */
+             hpos = contin_hpos;
+             vpos = vpos - 1;
+           }
+         else if (c == '\n')
+           /* If previous character is NEWLINE,
+              set VPOS back to previous line */
+           vpos = vpos - 1;
+         break;
+       }
 
-         prop = Fget_char_property (position,
-                                    Qinvisible,
-                                    Fcurrent_buffer ());
-         {
-           Lisp_Object end, limit, proplimit;
-
-           /* We must not advance farther than the next overlay change.
-              The overlay change might change the invisible property;
-              we have no way of telling.  */
-           limit = Fnext_overlay_change (position);
-           /* As for text properties, this gives a lower bound
-              for where the invisible text property could change.  */
-           proplimit = Fnext_property_change (position, buffer, Qt);
-           if (XFASTINT (limit) < XFASTINT (proplimit))
-             proplimit = limit;
-           /* PROPLIMIT is now a lower bound for the next change
-              in invisible status.  If that is plenty far away,
-              use that lower bound.  */
-           if (XFASTINT (proplimit) > pos + 100 || XFASTINT (proplimit) >= to)
-             next_invisible = XINT (proplimit);
-           /* Otherwise, scan for the next `invisible' property change.  */
-           else
-             {
-               /* Don't scan terribly far.  */
-               XSETFASTINT (proplimit, min (pos + 100, to));
-               /* No matter what. don't go past next overlay change.  */
-               if (XFASTINT (limit) < XFASTINT (proplimit))
-                 proplimit = limit;
-               end = Fnext_single_property_change (position, Qinvisible,
-                                                   buffer, proplimit);
-               if (INTEGERP (end) && XINT (end) < to)
-                 next_invisible = XINT (end);
-               else
-                 next_invisible = to;
-             }
-           if (TEXT_PROP_MEANS_INVISIBLE (prop))
-             pos = next_invisible;
-         }
+      if (vpos > tovpos || vpos == tovpos && hpos >= tohpos)
+       {
+         if (contin_hpos && prev_hpos == 0
+             && ((hpos > tohpos && contin_hpos == width) || wide_column))
+           { /* Line breaks because we can't put the character at the
+                previous line any more.  It is not the multi-column
+                character continued in middle.  Go back to previous
+                buffer position, screen position, and set tab offset
+                to previous value.  It's the beginning of the
+                line.  */
+             pos = prev_pos;
+             pos_byte = prev_pos_byte;
+             hpos = prev_hpos;
+             tab_offset = prev_tab_offset;
+           }
+         break;
        }
-      if (pos >= to)
+      if (pos == ZV) /* We cannot go beyond ZV.  Stop here. */
        break;
-#endif
+
+      prev_hpos = hpos;
+      prev_pos = pos;
+      prev_pos_byte = pos_byte;
+      wide_column = 0;
 
       /* Consult the width run cache to see if we can avoid inspecting
          the text character-by-character.  */
@@ -788,7 +1304,11 @@ compute_motion (from, fromvpos, fromhpos, to, tovpos, tohpos, width, hscroll, ta
               hpos = run_end_hpos;
               if (run_end > pos)
                 prev_hpos = hpos - common_width;
-              pos = run_end;
+             if (pos != run_end)
+               {
+                 pos = run_end;
+                 pos_byte = CHAR_TO_BYTE (pos);
+               }
             }
 
           next_width_run = run_end + 1;
@@ -796,131 +1316,158 @@ compute_motion (from, fromvpos, fromhpos, to, tovpos, tohpos, width, hscroll, ta
 
       /* We have to scan the text character-by-character.  */
       else
-        {
-          c = FETCH_CHAR (pos);
-          pos++;
-
-          /* Perhaps add some info to the width_run_cache.  */
-          if (current_buffer->width_run_cache)
-            {
-              /* Is this character part of the current run?  If so, extend
-                 the run.  */
-              if (pos - 1 == width_run_end
-                  && width_table[c] == width_run_width)
-                width_run_end = pos;
-
-              /* The previous run is over, since this is a character at a
-                 different position, or a different width.  */
-              else
-                {
-                  /* Have we accumulated a run to put in the cache?
-                     (Currently, we only cache runs of width == 1).  */
-                  if (width_run_start < width_run_end
-                      && width_run_width == 1)
-                    know_region_cache (current_buffer,
-                                       current_buffer->width_run_cache,
-                                       width_run_start, width_run_end);
-
-                  /* Start recording a new width run.  */
-                  width_run_width = width_table[c];
-                  width_run_start = pos - 1;
-                  width_run_end = pos;
-                }
-            }
+       {
+         c = FETCH_BYTE (pos_byte);
+         pos++, pos_byte++;
 
-          if (dp != 0 && VECTORP (DISP_CHAR_VECTOR (dp, c)))
-            hpos += XVECTOR (DISP_CHAR_VECTOR (dp, c))->size;
-          else if (c >= 040 && c < 0177)
-            hpos++;
-          else if (c == '\t')
-            {
-              hpos += tab_width - ((hpos + tab_offset + hscroll - (hscroll > 0)
-                                    /* Add tab_width here to make sure
-                                       positive.  hpos can be negative
-                                       after continuation but can't be
-                                       less than -tab_width.  */
-                                    + tab_width)
-                                   % tab_width);
-            }
-          else if (c == '\n')
-            {
-              if (selective > 0 && indented_beyond_p (pos, selective))
-                {
-                  /* Skip any number of invisible lines all at once */
-                  do
-                    pos = find_before_next_newline (pos, to, 1) + 1;
-                  while (pos < to
-                         && indented_beyond_p (pos, selective));
-                  /* Allow for the " ..." that is displayed for them. */
-                  if (selective_rlen)
-                    {
-                      hpos += selective_rlen;
-                      if (hpos >= width)
-                        hpos = width;
-                    }
-                 --pos;
-                  /* We have skipped the invis text, but not the
-                     newline after.  */
-                }
-              else
-                {
-                  /* A visible line.  */
-                  vpos++;
-                  hpos = 0;
-                  hpos -= hscroll;
-                  /* Count the truncation glyph on column 0 */
-                  if (hscroll > 0)
-                    hpos++;
-                  tab_offset = 0;
-                }
-            }
-          else if (c == CR && selective < 0)
-            {
-              /* In selective display mode,
-                 everything from a ^M to the end of the line is invisible.
-                 Stop *before* the real newline.  */
-              pos = find_before_next_newline (pos, to, 1);
-              /* Allow for the " ..." that is displayed for them. */
-              if (selective_rlen)
-                {
-                  hpos += selective_rlen;
-                  if (hpos >= width)
-                    hpos = width;
-                }
-            }
-          else
-            hpos += (ctl_arrow && c < 0200) ? 2 : 4;
-        }
+         /* Perhaps add some info to the width_run_cache.  */
+         if (current_buffer->width_run_cache)
+           {
+             /* Is this character part of the current run?  If so, extend
+                the run.  */
+             if (pos - 1 == width_run_end
+                 && XFASTINT (width_table[c]) == width_run_width)
+               width_run_end = pos;
+
+             /* The previous run is over, since this is a character at a
+                different position, or a different width.  */
+             else
+               {
+                 /* Have we accumulated a run to put in the cache?
+                    (Currently, we only cache runs of width == 1).  */
+                 if (width_run_start < width_run_end
+                     && width_run_width == 1)
+                   know_region_cache (current_buffer,
+                                      current_buffer->width_run_cache,
+                                      width_run_start, width_run_end);
+
+                 /* Start recording a new width run.  */
+                 width_run_width = XFASTINT (width_table[c]);
+                 width_run_start = pos - 1;
+                 width_run_end = pos;
+               }
+           }
 
-      /* Handle right margin.  */
-      if (hpos >= width
-         && (hpos > width
-             || (pos < ZV
-                 && FETCH_CHAR (pos) != '\n')))
-       {
-         if (vpos > tovpos
-             || (vpos == tovpos && hpos >= tohpos))
-           break;
-         if (hscroll
-             || (truncate_partial_width_windows
-                 && width + 1 < FRAME_WIDTH (XFRAME (WINDOW_FRAME (win))))
-             || !NILP (current_buffer->truncate_lines))
+         if (dp != 0 && VECTORP (DISP_CHAR_VECTOR (dp, c))
+             && ! (multibyte && BASE_LEADING_CODE_P (c)))
+           hpos += XVECTOR (DISP_CHAR_VECTOR (dp, c))->size;
+         else if (c >= 040 && c < 0177)
+           hpos++;
+         else if (c == '\t')
            {
-             /* Truncating: skip to newline.  */
-              pos = find_before_next_newline (pos, to, 1);
-             hpos = width;
+             int tem = (hpos + tab_offset + hscroll - (hscroll > 0)) % tab_width;
+             if (tem < 0)
+               tem += tab_width;
+             hpos += tab_width - tem;
            }
-         else
+         else if (c == '\n')
            {
-             /* Continuing.  */
-             vpos++;
-             hpos -= width;
-             tab_offset += width;
+             if (selective > 0
+                 && indented_beyond_p (pos, pos_byte, selective))
+               {
+                 /* If (pos == to), we don't have to take care of
+                   selective display.  */
+                 if (pos < to)
+                   {
+                     /* Skip any number of invisible lines all at once */
+                     do
+                       {
+                         pos = find_before_next_newline (pos, to, 1) + 1;
+                         pos_byte = CHAR_TO_BYTE (pos);
+                       }
+                     while (pos < to
+                            && indented_beyond_p (pos, pos_byte, selective));
+                     /* Allow for the " ..." that is displayed for them. */
+                     if (selective_rlen)
+                       {
+                         hpos += selective_rlen;
+                         if (hpos >= width)
+                           hpos = width;
+                       }
+                     DEC_BOTH (pos, pos_byte);
+                     /* We have skipped the invis text, but not the
+                       newline after.  */
+                   }
+               }
+             else
+               {
+                 /* A visible line.  */
+                 vpos++;
+                 hpos = 0;
+                 hpos -= hscroll;
+                 /* Count the truncation glyph on column 0 */
+                 if (hscroll > 0)
+                   hpos++;
+                 tab_offset = 0;
+               }
+             contin_hpos = 0;
            }
-
+         else if (c == CR && selective < 0)
+           {
+             /* In selective display mode,
+                everything from a ^M to the end of the line is invisible.
+                Stop *before* the real newline.  */
+             if (pos < to)
+               {
+                 pos = find_before_next_newline (pos, to, 1);
+                 pos_byte = CHAR_TO_BYTE (pos);
+               }
+             /* If we just skipped next_boundary,
+                loop around in the main while
+                and handle it.  */
+             if (pos > next_boundary)
+               next_boundary = pos;
+             /* Allow for the " ..." that is displayed for them. */
+             if (selective_rlen)
+               {
+                 hpos += selective_rlen;
+                 if (hpos >= width)
+                   hpos = width;
+               }
+           }
+         else if (multibyte && BASE_LEADING_CODE_P (c))
+           {
+             /* Start of multi-byte form.  */
+             unsigned char *ptr;
+             int len, actual_len;
+
+             pos--, pos_byte--;                /* rewind POS */
+
+             ptr = BYTE_POS_ADDR (pos_byte);
+             len = BUFFER_CEILING_OF (pos_byte) - pos_byte + 1;
+
+             c = STRING_CHAR_AND_LENGTH (ptr, len, actual_len);
+
+             if (dp != 0 && VECTORP (DISP_CHAR_VECTOR (dp, c)))
+               hpos += XVECTOR (DISP_CHAR_VECTOR (dp, c))->size;
+             else if (actual_len == 1)
+               hpos += 4;
+             else if (COMPOSITE_CHAR_P (c))
+               {
+                 int id = COMPOSITE_CHAR_ID (c);
+                 int width = (id < n_cmpchars) ? cmpchar_table[id]->width : 0;
+                 hpos += width;
+                 if (width > 1)
+                   wide_column = 1;
+               }
+             else
+               {
+                 int width = WIDTH_BY_CHAR_HEAD (*ptr);
+                 hpos += width;
+                 if (width > 1)
+                   wide_column = 1;
+               }
+
+             pos++;
+             pos_byte += actual_len;
+           }
+         else
+           hpos += (ctl_arrow && c < 0200) ? 2 : 4;
        }
     }
 
+ after_loop:
+
   /* Remember any final width run in the cache.  */
   if (current_buffer->width_run_cache
       && width_run_width == 1
@@ -929,15 +1476,15 @@ compute_motion (from, fromvpos, fromhpos, to, tovpos, tohpos, width, hscroll, ta
                        width_run_start, width_run_end);
 
   val_compute_motion.bufpos = pos;
+  val_compute_motion.bytepos = pos_byte;
   val_compute_motion.hpos = hpos;
   val_compute_motion.vpos = vpos;
   val_compute_motion.prevhpos = prev_hpos;
+  /* We alalways handle all of them here; none of them remain to do.  */
+  val_compute_motion.ovstring_chars_done = 0;
 
   /* Nonzero if have just continued a line */
-  val_compute_motion.contin
-    = (pos != from
-       && (val_compute_motion.vpos != prev_vpos)
-       && c != '\n');
+  val_compute_motion.contin = (contin_hpos && prev_hpos == 0);
 
   return &val_compute_motion;
 }
@@ -965,9 +1512,10 @@ TAB-OFFSET is the number of columns of the first tab that aren't\n\
 being displayed, perhaps because the line was continued within it.\n\
 If OFFSETS is nil, HSCROLL and TAB-OFFSET are assumed to be zero.\n\
 \n\
-WINDOW is the window to operate on.  Currently this is used only to\n\
-find the display table.  It does not matter what buffer WINDOW displays;\n\
-`compute-motion' always operates on the current buffer.\n\
+WINDOW is the window to operate on.  It is used to choose the display table;\n\
+if it is showing the current buffer, it is used also for\n\
+deciding which overlay properties apply.\n\
+Note that `compute-motion' always operates on the current buffer.\n\
 \n\
 The value is a list of five elements:\n\
   (POS HPOS VPOS PREVHPOS CONTIN)\n\
@@ -1022,7 +1570,7 @@ DEFUN ("compute-motion", Fcompute_motion, Scompute_motion, 7, 7, 0,
     CHECK_LIVE_WINDOW (window, 0);
 
   pos = compute_motion (XINT (from), XINT (XCONS (frompos)->cdr),
-                       XINT (XCONS (frompos)->car),
+                       XINT (XCONS (frompos)->car), 0,
                        XINT (to), XINT (XCONS (topos)->cdr),
                        XINT (XCONS (topos)->car),
                        XINT (width), hscroll, tab_offset,
@@ -1040,29 +1588,6 @@ DEFUN ("compute-motion", Fcompute_motion, Scompute_motion, 7, 7, 0,
                                     Fcons (pos->contin ? Qt : Qnil, Qnil)))));
 
 }
-\f
-/* Return the column of position POS in window W's buffer.
-   The result is rounded down to a multiple of the internal width of W.
-   This is the amount of indentation of position POS
-   that is not visible in its horizontal position in the window.  */
-
-int
-pos_tab_offset (w, pos)
-     struct window *w;
-     register int pos;
-{
-  int opoint = PT;
-  int col;
-  int width = window_internal_width (w) - 1;
-
-  if (pos == BEGV || FETCH_CHAR (pos - 1) == '\n')
-    return 0;
-  TEMP_SET_PT (pos);
-  col = current_column ();
-  TEMP_SET_PT (opoint);
-  return col - (col % width);
-}
-
 \f
 /* Fvertical_motion and vmotion */
 struct position val_vmotion;
@@ -1079,6 +1604,7 @@ vmotion (from, vtarget, w)
   register int vpos = 0;
   Lisp_Object prevline;
   register int first;
+  int from_byte;
   int lmargin = hscroll > 0 ? 1 - hscroll : 0;
   int selective
     = (INTEGERP (current_buffer->selective_display)
@@ -1086,6 +1612,7 @@ vmotion (from, vtarget, w)
        : !NILP (current_buffer->selective_display) ? -1 : 0);
   Lisp_Object window;
   int start_hpos = 0;
+  int did_motion;
 
   XSETWINDOW (window, w);
 
@@ -1105,7 +1632,7 @@ vmotion (from, vtarget, w)
   if (vpos >= vtarget)
     {
       /* To move upward, go a line at a time until
-        we have gone at least far enough */
+        we have gone at least far enough */
 
       first = 1;
 
@@ -1116,7 +1643,9 @@ vmotion (from, vtarget, w)
          XSETFASTINT (prevline, find_next_newline_no_quit (from - 1, -1));
          while (XFASTINT (prevline) > BEGV
                 && ((selective > 0
-                     && indented_beyond_p (XFASTINT (prevline), selective))
+                     && indented_beyond_p (XFASTINT (prevline),
+                                           CHAR_TO_BYTE (XFASTINT (prevline)),
+                                           selective))
 #ifdef USE_TEXT_PROPERTIES
                     /* watch out for newlines with `invisible' property */
                     || (propval = Fget_char_property (prevline,
@@ -1131,8 +1660,19 @@ vmotion (from, vtarget, w)
          pos = *compute_motion (XFASTINT (prevline), 0,
                                 lmargin + (XFASTINT (prevline) == BEG
                                            ? start_hpos : 0),
-                                from, 1 << (INTBITS - 2), 0,
-                                width, hscroll, 0, w);
+                                0,
+                                from, 
+                                /* Don't care for VPOS...  */
+                                1 << (BITS_PER_SHORT - 1),
+                                /* ... nor HPOS.  */
+                                1 << (BITS_PER_SHORT - 1),
+                                width, hscroll,
+                                /* This compensates for start_hpos
+                                   so that a tab as first character
+                                   still occupies 8 columns.  */
+                                (XFASTINT (prevline) == BEG
+                                 ? -start_hpos : 0),
+                                w);
          vpos -= pos.vpos;
          first = 0;
          from = XFASTINT (prevline);
@@ -1144,10 +1684,13 @@ vmotion (from, vtarget, w)
       if (vpos >= vtarget)
        {
          val_vmotion.bufpos = from;
+         val_vmotion.bytepos = CHAR_TO_BYTE (from);
          val_vmotion.vpos = vpos;
          val_vmotion.hpos = lmargin;
          val_vmotion.contin = 0;
          val_vmotion.prevhpos = 0;
+         val_vmotion.ovstring_chars_done = 0;
+         val_vmotion.tab_offset = 0; /* For accumulating tab offset.  */
          return &val_vmotion;
        }
 
@@ -1155,14 +1698,17 @@ vmotion (from, vtarget, w)
     }
   /* Moving downward is simple, but must calculate from beg of line
      to determine hpos of starting point */
-  if (from > BEGV && FETCH_CHAR (from - 1) != '\n')
+  from_byte = CHAR_TO_BYTE (from);
+  if (from > BEGV && FETCH_BYTE (from_byte - 1) != '\n')
     {
-       Lisp_Object propval;
+      Lisp_Object propval;
 
       XSETFASTINT (prevline, find_next_newline_no_quit (from, -1));
       while (XFASTINT (prevline) > BEGV
             && ((selective > 0
-                 && indented_beyond_p (XFASTINT (prevline), selective))
+                 && indented_beyond_p (XFASTINT (prevline),
+                                       CHAR_TO_BYTE (XFASTINT (prevline)),
+                                       selective))
 #ifdef USE_TEXT_PROPERTIES
                 /* watch out for newlines with `invisible' property */
                 || (propval = Fget_char_property (prevline, Qinvisible,
@@ -1176,33 +1722,52 @@ vmotion (from, vtarget, w)
       pos = *compute_motion (XFASTINT (prevline), 0,
                             lmargin + (XFASTINT (prevline) == BEG
                                        ? start_hpos : 0),
-                            from, 1 << (INTBITS - 2), 0,
-                            width, hscroll, 0, w);
+                            0,
+                            from, 
+                            /* Don't care for VPOS...  */
+                            1 << (BITS_PER_SHORT - 1),
+                            /* ... nor HPOS.  */
+                            1 << (BITS_PER_SHORT - 1),
+                            width, hscroll,
+                            (XFASTINT (prevline) == BEG ? -start_hpos : 0),
+                            w);
+      did_motion = 1;
     }
   else
     {
       pos.hpos = lmargin + (from == BEG ? start_hpos : 0);
       pos.vpos = 0;
+      pos.tab_offset = 0;
+      did_motion = 0;
     }
-  return compute_motion (from, vpos, pos.hpos,
-                        ZV, vtarget, - (1 << (INTBITS - 2)),
-                        width, hscroll, pos.vpos * width, w);
+  return compute_motion (from, vpos, pos.hpos, did_motion,
+                        ZV, vtarget, - (1 << (BITS_PER_SHORT - 1)),
+                        width, hscroll,
+                        pos.tab_offset - (from == BEG ? start_hpos : 0),
+                        w);
 }
 
 DEFUN ("vertical-motion", Fvertical_motion, Svertical_motion, 1, 2, 0,
-  "Move to start of screen line LINES lines down.\n\
-If LINES is negative, this is moving up.\n\
+  "Move point to start of the screen line LINES lines down.\n\
+If LINES is negative, this means moving up.\n\
+\n\
+This function is an ordinary cursor motion function\n\
+which calculates the new position based on how text would be displayed.\n\
+The new position may be the start of a line,\n\
+or just the start of a continuation line.\n\
+The function returns number of screen lines moved over;\n\
+that usually equals LINES, but may be closer to zero\n\
+if beginning or end of buffer was reached.\n\
 \n\
 The optional second argument WINDOW specifies the window to use for\n\
 parameters such as width, horizontal scrolling, and so on.\n\
-the default is the selected window.\n\
-It does not matter what buffer is displayed in WINDOW.\n\
-`vertical-motion' always uses the current buffer.\n\
+The default is to use the selected window's parameters.\n\
 \n\
-Sets point to position found; this may be start of line\n\
-or just the start of a continuation line.\n\
-Returns number of lines moved; may be closer to zero than LINES\n\
-if beginning or end of buffer was reached.")
+`vertical-motion' always uses the current buffer,\n\
+regardless of which buffer is displayed in WINDOW.\n\
+This is consistent with other cursor motion functions\n\
+and makes it possible to use `vertical-motion' in any buffer,\n\
+whether or not it is currently displayed in some window.")
   (lines, window)
      Lisp_Object lines, window;
 {
@@ -1214,7 +1779,7 @@ if beginning or end of buffer was reached.")
   else
     window = selected_window;
 
-  pos = *vmotion (point, XINT (lines), XWINDOW (window));
+  pos = *vmotion (PT, (int) XINT (lines), XWINDOW (window));
 
   SET_PT (pos.bufpos);
   return make_number (pos.vpos);