diff src/xdisp.c @ 96238:1bb123f7f691

(Vtruncate_partial_width_windows): New Lisp_Object, replacing truncate_partial_width_windows. (init_iterator): If Vtruncate_partial_width_windows is an integer, truncate only if the window width is below that integer. (start_display. resize_mini_window, produce_stretch_glyph) (display_string, move_it_in_display_line_to): Use line_wrap. (back_to_previous_visible_line_start, reseat_1): Reset string_from_display_prop_p. (display_line): Extend default face to end of line when wrapping. (display_line, move_it_in_display_line_to): Add ability to wrap continued lines at word boundaries.
author Chong Yidong <cyd@stupidchicken.com>
date Tue, 24 Jun 2008 18:00:20 +0000
parents 9725d7b12f53
children 43fea43f47a2
line wrap: on
line diff
--- a/src/xdisp.c	Tue Jun 24 17:58:50 2008 +0000
+++ b/src/xdisp.c	Tue Jun 24 18:00:20 2008 +0000
@@ -420,7 +420,7 @@
 /* Nonzero means truncate lines in all windows less wide than the
    frame.  */
 
-int truncate_partial_width_windows;
+Lisp_Object Vtruncate_partial_width_windows;
 
 /* A flag to control how to display unibyte 8-bit character.  */
 
@@ -2634,19 +2634,27 @@
     it->tab_width = 8;
 
   /* Are lines in the display truncated?  */
-  it->truncate_lines_p
-    = (base_face_id != DEFAULT_FACE_ID
-       || XINT (it->w->hscroll)
-       || (truncate_partial_width_windows
-	   && !WINDOW_FULL_WIDTH_P (it->w))
-       || !NILP (current_buffer->truncate_lines));
+  if (base_face_id != DEFAULT_FACE_ID
+      || XINT (it->w->hscroll)
+      || (! WINDOW_FULL_WIDTH_P (it->w)
+	  && ((!NILP (Vtruncate_partial_width_windows)
+	       && !INTEGERP (Vtruncate_partial_width_windows))
+	      || (INTEGERP (Vtruncate_partial_width_windows)
+		  && (WINDOW_TOTAL_COLS (it->w)
+		      < XINT (Vtruncate_partial_width_windows))))))
+    it->line_wrap = TRUNCATE;
+  else if (NILP (current_buffer->truncate_lines))
+    it->line_wrap = NILP (current_buffer->word_wrap)
+      ? WINDOW_WRAP : WORD_WRAP;
+  else
+    it->line_wrap = TRUNCATE;
 
   /* Get dimensions of truncation and continuation glyphs.  These are
      displayed as fringe bitmaps under X, so we don't need them for such
      frames.  */
   if (!FRAME_WINDOW_P (it->f))
     {
-      if (it->truncate_lines_p)
+      if (it->line_wrap == TRUNCATE)
 	{
 	  /* We will need the truncation glyph.  */
 	  xassert (it->glyph_row == NULL);
@@ -2696,7 +2704,7 @@
 	 for window-based redisplay.  */
       if (!FRAME_WINDOW_P (it->f))
 	{
-	  if (it->truncate_lines_p)
+	  if (it->line_wrap == TRUNCATE)
 	    it->last_visible_x -= it->truncation_pixel_width;
 	  else
 	    it->last_visible_x -= it->continuation_pixel_width;
@@ -2769,7 +2777,7 @@
 
   /* Don't reseat to previous visible line start if current start
      position is in a string or image.  */
-  if (it->method == GET_FROM_BUFFER && !it->truncate_lines_p)
+  if (it->method == GET_FROM_BUFFER && it->line_wrap != TRUNCATE)
     {
       int start_at_line_beg_p;
       int first_y = it->current_y;
@@ -2794,7 +2802,7 @@
 	     taken us to the start of the continuation line but to the
 	     end of the continued line.  */
 	  if (it->current_x > 0
-	      && !it->truncate_lines_p /* Lines are continued.  */
+	      && it->line_wrap != TRUNCATE /* Lines are continued.  */
 	      && (/* And glyph doesn't fit on the line.  */
 		  new_x > it->last_visible_x
 		  /* Or it fits exactly and we're on a window
@@ -5432,6 +5440,7 @@
 	pos = --IT_CHARPOS (it2);
 	--IT_BYTEPOS (it2);
 	it2.sp = 0;
+	it2.string_from_display_prop_p = 0;
 	if (handle_display_prop (&it2) == HANDLED_RETURN
 	    && !NILP (val = get_char_property_and_overlay
 		      (make_number (pos), Qdisplay, Qnil, &overlay))
@@ -5582,6 +5591,7 @@
   IT_STRING_CHARPOS (*it) = -1;
   IT_STRING_BYTEPOS (*it) = -1;
   it->string = Qnil;
+  it->string_from_display_prop_p = 0;
   it->method = GET_FROM_BUFFER;
   it->object = it->w->buffer;
   it->area = TEXT_AREA;
@@ -6643,11 +6653,20 @@
 {
   enum move_it_result result = MOVE_UNDEFINED;
   struct glyph_row *saved_glyph_row;
+  struct it wrap_it, atpos_it;
+  int may_wrap = 0;
 
   /* Don't produce glyphs in produce_glyphs.  */
   saved_glyph_row = it->glyph_row;
   it->glyph_row = NULL;
 
+  /* Use wrap_it to save a copy of IT wherever a word wrap could
+     occur.  Use atpos_it to save a copy of IT at the desired
+     position, if found, so that we can scan ahead and check if the
+     word later overshoots the window edge.  */
+  wrap_it.sp = -1;
+  atpos_it.sp = -1;
+
 #define BUFFER_POS_REACHED_P()					\
   ((op & MOVE_TO_POS) != 0					\
    && BUFFERP (it->object)					\
@@ -6667,36 +6686,77 @@
 	  && it->method == GET_FROM_BUFFER
 	  && IT_CHARPOS (*it) > to_charpos)
 	{
-	  result = MOVE_POS_MATCH_OR_ZV;
-	  break;
+	  if (it->line_wrap == WORD_WRAP)
+	    {
+	      /* If wrap_it is valid, the current position might be in
+		 a word that is wrapped to the next line, so continue
+		 to see if that happens.  */
+	      if (wrap_it.sp < 0)
+		{
+		  result = MOVE_POS_MATCH_OR_ZV;
+		  break;
+		}
+	      if (atpos_it.sp < 0)
+		atpos_it = *it;
+	    }
+	  else
+	    {
+	      result = MOVE_POS_MATCH_OR_ZV;
+	      break;
+	    }
 	}
 
       /* Stop when ZV reached.
          We used to stop here when TO_CHARPOS reached as well, but that is
          too soon if this glyph does not fit on this line.  So we handle it
          explicitly below.  */
-      if (!get_next_display_element (it)
-	  || (it->truncate_lines_p
-	      && BUFFER_POS_REACHED_P ()))
+      if (!get_next_display_element (it))
 	{
 	  result = MOVE_POS_MATCH_OR_ZV;
 	  break;
 	}
 
+      if (it->line_wrap == TRUNCATE)
+	{
+	  if (BUFFER_POS_REACHED_P ())
+	    {
+	      result = MOVE_POS_MATCH_OR_ZV;
+	      break;
+	    }
+	}
+      else
+	{
+	  /* Remember the line height so far in case the next element
+	     doesn't fit on the line.  */
+	  ascent = it->max_ascent;
+	  descent = it->max_descent;
+
+	  if (it->line_wrap == WORD_WRAP)
+	    {
+	      if (it->what == IT_CHARACTER
+		  && (it->c == ' ' || it->c == '\t'))
+		may_wrap = 1;
+	      else if (may_wrap)
+		{
+		  /* We are done if the position is already found.  */
+		  if (atpos_it.sp >= 0)
+		    {
+		      *it = atpos_it;
+		      atpos_it.sp = -1;
+		      goto buffer_pos_reached;
+		    }
+		  wrap_it = *it;
+		  may_wrap = 0;
+		}
+	    }
+	}
+
       /* The call to produce_glyphs will get the metrics of the
-	 display element IT is loaded with.  We record in x the
-	 x-position before this display element in case it does not
-	 fit on the line.  */
+	 display element IT is loaded with.  Record the x-position
+	 before this display element, in case it doesn't fit on the
+	 line.  */
       x = it->current_x;
 
-      /* Remember the line height so far in case the next element doesn't
-	 fit on the line.  */
-      if (!it->truncate_lines_p)
-	{
-	  ascent = it->max_ascent;
-	  descent = it->max_descent;
-	}
-
       PRODUCE_GLYPHS (it);
 
       if (it->area != TEXT_AREA)
@@ -6740,19 +6800,30 @@
 	      if ((op & MOVE_TO_X) && new_x > to_x)
 		{
 		  if (BUFFER_POS_REACHED_P ())
-		    goto buffer_pos_reached;
+		    {
+		      if (it->line_wrap == WORD_WRAP)
+			{
+			  if (wrap_it.sp < 0)
+			    goto buffer_pos_reached;
+			  if (atpos_it.sp < 0)
+			    atpos_it = *it;
+			}
+		      else
+			goto buffer_pos_reached;
+		    }
 		  it->current_x = x;
 		  result = MOVE_X_REACHED;
 		  break;
 		}
-	      else if (/* Lines are continued.  */
-		       !it->truncate_lines_p
-		       && (/* And glyph doesn't fit on the line.  */
-			   new_x > it->last_visible_x
-			   /* Or it fits exactly and we're on a window
-			      system frame.  */
-			   || (new_x == it->last_visible_x
-			       && FRAME_WINDOW_P (it->f))))
+
+	      if (/* Lines are continued.  */
+		  it->line_wrap != TRUNCATE
+		  && (/* And glyph doesn't fit on the line.  */
+		      new_x > it->last_visible_x
+		      /* Or it fits exactly and we're on a window
+			 system frame.  */
+		      || (new_x == it->last_visible_x
+			  && FRAME_WINDOW_P (it->f))))
 		{
 		  if (/* IT->hpos == 0 means the very first glyph
 			 doesn't fit on the line, e.g. a wide image.  */
@@ -6811,24 +6882,37 @@
 		      it->max_descent = descent;
 		    }
 
+		  if (wrap_it.sp >= 0)
+		    {
+		      *it = wrap_it;
+		      atpos_it.sp = -1;
+		    }
+
 		  TRACE_MOVE ((stderr, "move_it_in: continued at %d\n",
 			       IT_CHARPOS (*it)));
 		  result = MOVE_LINE_CONTINUED;
 		  break;
 		}
-	      else if (BUFFER_POS_REACHED_P ())
-		goto buffer_pos_reached;
-	      else if (new_x > it->first_visible_x)
+
+	      if (BUFFER_POS_REACHED_P ())
+		{
+		  if (it->line_wrap == WORD_WRAP)
+		    {
+		      if (wrap_it.sp < 0)
+			goto buffer_pos_reached;
+		      if (atpos_it.sp < 0)
+			atpos_it = *it;
+		    }
+		  else
+		    goto buffer_pos_reached;
+		}
+
+	      if (new_x > it->first_visible_x)
 		{
 		  /* Glyph is visible.  Increment number of glyphs that
 		     would be displayed.  */
 		  ++it->hpos;
 		}
-	      else
-		{
-		  /* Glyph is completely off the left margin of the display
-		     area.  Nothing to do.  */
-		}
 	    }
 
 	  if (result != MOVE_UNDEFINED)
@@ -6867,7 +6951,7 @@
 
       /* Stop if lines are truncated and IT's current x-position is
 	 past the right edge of the window now.  */
-      if (it->truncate_lines_p
+      if (it->line_wrap == TRUNCATE
 	  && it->current_x >= it->last_visible_x)
 	{
 #ifdef HAVE_WINDOW_SYSTEM
@@ -6893,6 +6977,11 @@
 
 #undef BUFFER_POS_REACHED_P
 
+  /* If we scanned beyond to_pos and didn't find a point to wrap at,
+     return iterator at to_pos.  */
+  if (atpos_it.sp >= 0)
+    *it = atpos_it;
+
   /* Restore the iterator settings altered at the beginning of this
      function.  */
   it->glyph_row = saved_glyph_row;
@@ -8555,7 +8644,7 @@
       max_height = min (total_height, max_height);
 
       /* Find out the height of the text in the window.  */
-      if (it.truncate_lines_p)
+      if (it.line_wrap == TRUNCATE)
 	height = 1;
       else
 	{
@@ -15997,7 +16086,8 @@
       && it->glyph_row->displays_text_p
       && face->box == FACE_NO_BOX
       && face->background == FRAME_BACKGROUND_PIXEL (f)
-      && !face->stipple)
+      && !face->stipple
+      && it->line_wrap != WORD_WRAP)
     return;
 
   /* Set the glyph row flag indicating that the face of the last glyph
@@ -16212,6 +16302,11 @@
 {
   struct glyph_row *row = it->glyph_row;
   Lisp_Object overlay_arrow_string;
+  struct it wrap_it;
+  int may_wrap = 0, wrap_x;
+  int wrap_row_used = -1, wrap_row_ascent, wrap_row_height;
+  int wrap_row_phys_ascent, wrap_row_phys_height;
+  int wrap_row_extra_line_spacing;
 
   /* We always start displaying at hpos zero even if hscrolled.  */
   xassert (it->hpos == 0 && it->current_x == 0);
@@ -16306,12 +16401,31 @@
 
       /* Remember the line height so far in case the next element doesn't
 	 fit on the line.  */
-      if (!it->truncate_lines_p)
+      if (it->line_wrap != TRUNCATE)
 	{
 	  ascent = it->max_ascent;
 	  descent = it->max_descent;
 	  phys_ascent = it->max_phys_ascent;
 	  phys_descent = it->max_phys_descent;
+
+	  if (it->line_wrap == WORD_WRAP && it->area == TEXT_AREA)
+	    {
+	      if (it->what == IT_CHARACTER
+		  && (it->c == ' ' || it->c == '\t'))
+		may_wrap = 1;
+	      else if (may_wrap)
+		{
+		  wrap_it = *it;
+		  wrap_x = x;
+		  wrap_row_used = row->used[TEXT_AREA];
+		  wrap_row_ascent = row->ascent;
+		  wrap_row_height = row->height;
+		  wrap_row_phys_ascent = row->phys_ascent;
+		  wrap_row_phys_height = row->phys_height;
+		  wrap_row_extra_line_spacing = row->extra_line_spacing;
+		  may_wrap = 0;
+		}
+	    }
 	}
 
       PRODUCE_GLYPHS (it);
@@ -16371,7 +16485,7 @@
 	      new_x = x + glyph->pixel_width;
 
 	      if (/* Lines are continued.  */
-		  !it->truncate_lines_p
+		  it->line_wrap != TRUNCATE
 		  && (/* Glyph doesn't fit on the line.  */
 		      new_x > it->last_visible_x
 		      /* Or it fits exactly on a window system frame.  */
@@ -16412,6 +16526,8 @@
 				}
 			    }
 #endif /* HAVE_WINDOW_SYSTEM */
+			  if (wrap_row_used > 0)
+			    goto back_to_wrap;
 			}
 		    }
 		  else if (CHAR_GLYPH_PADDING_P (*glyph)
@@ -16439,6 +16555,26 @@
 		      it->max_phys_ascent = phys_ascent;
 		      it->max_phys_descent = phys_descent;
 		    }
+		  else if (wrap_row_used > 0)
+		    {
+		    back_to_wrap:
+		      *it = wrap_it;
+		      it->continuation_lines_width += wrap_x;
+		      row->used[TEXT_AREA] = wrap_row_used;
+		      row->ascent = wrap_row_ascent;
+		      row->height = wrap_row_height;
+		      row->phys_ascent = wrap_row_phys_ascent;
+		      row->phys_height = wrap_row_phys_height;
+		      row->extra_line_spacing = wrap_row_extra_line_spacing;
+		      row->continued_p = 1;
+		      row->ends_at_zv_p = 0;
+		      row->exact_window_width_line_p = 0;
+		      it->continuation_lines_width += x;
+
+		      /* Make sure that a non-default face is extended
+			 up to the right margin of the window.  */
+		      extend_face_to_end_of_line (it);
+		    }
 		  else if (it->c == '\t' && FRAME_WINDOW_P (it->f))
 		    {
 		      /* A TAB that extends past the right edge of the
@@ -16554,7 +16690,7 @@
 
       /* If we truncate lines, we are done when the last displayed
 	 glyphs reach past the right margin of the window.  */
-      if (it->truncate_lines_p
+      if (it->line_wrap == TRUNCATE
 	  && (FRAME_WINDOW_P (it->f)
 	      ? (it->current_x >= it->last_visible_x)
 	      : (it->current_x > it->last_visible_x)))
@@ -18579,7 +18715,7 @@
 	{
 	  struct glyph *glyph = row->glyphs[TEXT_AREA] + n_glyphs_before + i;
 
-	  if (!it->truncate_lines_p
+	  if (it->line_wrap != TRUNCATE
 	      && x + glyph->pixel_width > max_x)
 	    {
 	      /* End of continued line or max_x reached.  */
@@ -18635,7 +18771,7 @@
       set_iterator_to_next (it, 1);
 
       /* Stop if truncating at the right edge.  */
-      if (it->truncate_lines_p
+      if (it->line_wrap == TRUNCATE
 	  && it->current_x >= it->last_visible_x)
 	{
 	  /* Add truncation mark, but don't do it if the line is
@@ -20583,7 +20719,7 @@
   else
     ascent = (height * FONT_BASE (font)) / FONT_HEIGHT (font);
 
-  if (width > 0 && !it->truncate_lines_p
+  if (width > 0 && it->line_wrap != TRUNCATE
       && it->current_x + width > it->last_visible_x)
     width = it->last_visible_x - it->current_x - 1;
 
@@ -24538,11 +24674,18 @@
   DEFVAR_INT ("debug-end-pos", &debug_end_pos, doc: /* Don't ask.  */);
 #endif
 
-  DEFVAR_BOOL ("truncate-partial-width-windows",
-	       &truncate_partial_width_windows,
-    doc: /* *Non-nil means truncate lines in all windows less than full frame wide.
+  DEFVAR_LISP ("truncate-partial-width-windows",
+	       &Vtruncate_partial_width_windows,
+    doc: /* Non-nil means truncate lines in windows with less than the frame width.
+For an integer value, truncate lines in each window with less than the
+full frame width, provided the window width is less than that integer;
+otherwise, respect the value of `truncate-lines'.
+
+For any other non-nil value, truncate lines in all windows with
+less than the full frame width.
+
 Nil means to respect the value of `truncate-lines'.  */);
-  truncate_partial_width_windows = 1;
+  Vtruncate_partial_width_windows = make_number (30);
 
   DEFVAR_BOOL ("mode-line-inverse-video", &mode_line_inverse_video,
     doc: /* When nil, display the mode-line/header-line/menu-bar in the default face.