(string_width): New function.
[bpt/emacs.git] / src / indent.c
index 520825c..fa757f9 100644 (file)
@@ -68,6 +68,7 @@ buffer_display_table ()
 /* Width run cache considerations.  */
 
 /* Return the width of character C under display table DP.  */
+
 static int
 character_width (c, dp)
      int c;
@@ -78,18 +79,16 @@ character_width (c, dp)
   /* These width computations were determined by examining the cases
      in display_text_line.  */
 
-  /* Some characters are never handled by the display table.  */
-  if (c == '\n' || c == '\t' || c == '\015')
-    return 0;
-
-  /* Everything else might be handled by the display table, if it's
+  /* Everything can be handled by the display table, if it's
      present and the element is right.  */
-  else if (dp && (elt = DISP_CHAR_VECTOR (dp, c),
-                  VECTORP (elt)))
+  if (dp && (elt = DISP_CHAR_VECTOR (dp, c), VECTORP (elt)))
     return XVECTOR (elt)->size;
 
-  /* In the absence of display table perversities, printing characters
-     have width 1.  */
+  /* Some characters are special.  */
+  if (c == '\n' || c == '\t' || c == '\015')
+    return 0;
+
+  /* Printing characters have width 1.  */
   else if (c >= 040 && c < 0177)
     return 1;
 
@@ -241,11 +240,10 @@ current_column ()
        }
 
       c = *--ptr;
-      if (c >= 040 && c < 0177
-         && (dp == 0 || !VECTORP (DISP_CHAR_VECTOR (dp, c))))
-       {
-         col++;
-       }
+      if (dp != 0 && VECTORP (DISP_CHAR_VECTOR (dp, c)))
+       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))
@@ -259,8 +257,6 @@ current_column ()
          col = 0;
          tab_seen = 1;
        }
-      else if (dp != 0 && VECTORP (DISP_CHAR_VECTOR (dp, c)))
-       col += XVECTOR (DISP_CHAR_VECTOR (dp, c))->size;
       else
        col += (ctl_arrow && c < 0200) ? 2 : 4;
     }
@@ -278,6 +274,84 @@ current_column ()
   return col;
 }
 \f
+/* Return the width in columns of the part of STRING from BEG to END.
+   If BEG is nil, that stands for the beginning of STRING.
+   If END is nil, that stands for the end of STRING.  */
+
+static int
+string_width (string, beg, end)
+     Lisp_Object string, beg, end;
+{
+  register int col;
+  register unsigned char *ptr, *stop;
+  register int tab_seen;
+  int post_tab;
+  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 ();
+  int b, e;
+
+  if (NILP (end))
+    e = XSTRING (string)->size;
+  else
+    {
+      CHECK_NUMBER (end, 0);
+      e = XINT (end);
+    }
+
+  if (NILP (beg))
+    b = 0;
+  else
+    {
+      CHECK_NUMBER (beg, 0);
+      b = XINT (beg);
+    }
+
+  /* Make a pointer for decrementing through the chars before point.  */
+  ptr = XSTRING (string)->data + e;
+  /* Make a pointer to where consecutive chars leave off,
+     going backwards from point.  */
+  stop = XSTRING (string)->data + b;
+
+  if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
+
+  col = 0, tab_seen = 0, post_tab = 0;
+
+  while (1)
+    {
+      if (ptr == stop)
+       break;
+
+      c = *--ptr;
+      if (dp != 0 && VECTORP (DISP_CHAR_VECTOR (dp, c)))
+       col += XVECTOR (DISP_CHAR_VECTOR (dp, c))->size;
+      else if (c >= 040 && c < 0177)
+       col++;
+      else if (c == '\n')
+       break;
+      else if (c == '\t')
+       {
+         if (tab_seen)
+           col = ((col + tab_width) / tab_width) * tab_width;
+
+         post_tab += col;
+         col = 0;
+         tab_seen = 1;
+       }
+      else
+       col += (ctl_arrow && c < 0200) ? 2 : 4;
+    }
+
+  if (tab_seen)
+    {
+      col = ((col + tab_width) / tab_width) * tab_width;
+      col += post_tab;
+    }
+
+  return col;
+}
+\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\
@@ -436,6 +510,12 @@ and if COLUMN is in the middle of a tab character, change it to spaces.")
   while (col < goal && pos < end)
     {
       c = FETCH_CHAR (pos);
+      if (dp != 0 && VECTORP (DISP_CHAR_VECTOR (dp, c)))
+       {
+         col += XVECTOR (DISP_CHAR_VECTOR (dp, c))->size;
+         pos++;
+         break;
+       }
       if (c == '\n')
        break;
       if (c == '\r' && EQ (current_buffer->selective_display, Qt))
@@ -447,8 +527,6 @@ and if COLUMN is in the middle of a tab character, change it to spaces.")
          col += tab_width;
          col = col / tab_width * tab_width;
        }
-      else if (dp != 0 && VECTORP (DISP_CHAR_VECTOR (dp, c)))
-       col += XVECTOR (DISP_CHAR_VECTOR (dp, c))->size;
       else if (ctl_arrow && (c < 040 || c == 0177))
         col += 2;
       else if (c < 040 || c >= 0177)
@@ -631,23 +709,30 @@ compute_motion (from, fromvpos, fromhpos, to, tovpos, tohpos, width, hscroll, ta
          {
            Lisp_Object end, limit, proplimit;
 
-           /* Get a quit lower bound for how soon property might change.  */
+           /* 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 (proplimit) < XFASTINT (limit))
-             limit = proplimit;
-           /* LIMIT is now a lower bound for the next change
+           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 (limit) > pos + 100 || XFASTINT (limit) >= to)
-             next_invisible = XINT (limit);
+           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 (limit, min (pos + 100, to));
+               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, limit);
+                                                   buffer, proplimit);
                if (INTEGERP (end) && XINT (end) < to)
                  next_invisible = XINT (end);
                else
@@ -743,8 +828,9 @@ compute_motion (from, fromvpos, fromhpos, to, tovpos, tohpos, width, hscroll, ta
                 }
             }
 
-          if (c >= 040 && c < 0177
-              && (dp == 0 || ! VECTORP (DISP_CHAR_VECTOR (dp, c))))
+          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')
             {
@@ -802,8 +888,6 @@ compute_motion (from, fromvpos, fromhpos, to, tovpos, tohpos, width, hscroll, ta
                     hpos = width;
                 }
             }
-          else if (dp != 0 && VECTORP (DISP_CHAR_VECTOR (dp, c)))
-            hpos += XVECTOR (DISP_CHAR_VECTOR (dp, c))->size;
           else
             hpos += (ctl_arrow && c < 0200) ? 2 : 4;
         }
@@ -1003,7 +1087,15 @@ vmotion (from, vtarget, width, hscroll, window)
          && marker_position (XWINDOW (window)->start) == BEG
      here is deliberate; I think we want to measure from the prompt
      position even if the minibuffer window has scrolled.  */
-  int start_hpos = (EQ (window, minibuf_window) ? minibuf_prompt_width : 0);
+  int start_hpos = 0;
+
+  if (EQ (window, minibuf_window))
+    {
+      if (minibuf_prompt_width == 0)
+       minibuf_prompt_width = string_width (minibuf_prompt, Qnil, Qnil);
+
+      start_hpos = minibuf_prompt_width;
+    }
 
  retry:
   if (vtarget > vpos)