changeset 111107:f3721a6253a8

Fix mouse highlight in bidi-reordered continued lines. xdisp.c (row_containing_pos): Don't return too early when CHARPOS is in a bidi-reordered continued line. Return immediately when the first hit is found in a line that is not continued, or when an exact match for CHARPOS is found. (mouse_face_from_buffer_pos): Rewrite to not assume that START_CHARPOS is always in mouse_face_beg_row. If necessary, swap mouse_face_beg_row and mouse_face_end_row so that the former is always above the latter or identical to it. Continued lines that begin or end outside of the visible region still don't work.
author Eli Zaretskii <eliz@gnu.org>
date Sat, 02 Oct 2010 17:05:20 +0200
parents f8d92bb352b9
children d35341cade2a
files src/ChangeLog src/xdisp.c
diffstat 2 files changed, 192 insertions(+), 178 deletions(-) [+]
line wrap: on
line diff
--- a/src/ChangeLog	Sat Oct 02 13:48:56 2010 +0200
+++ b/src/ChangeLog	Sat Oct 02 17:05:20 2010 +0200
@@ -5,6 +5,14 @@
 	(note_mouse_highlight): Clear mouse highlight when mouse pointer
 	is in a R2L row on the stretch glyph that stands for no text
 	beyond the line end.
+	(row_containing_pos): Don't return too early when CHARPOS is in a
+	bidi-reordered continued line.  Return immediately when the first
+	hit is found in a line that is not continued, or when an exact
+	match for CHARPOS is found.
+	(mouse_face_from_buffer_pos): Rewrite to not assume that
+	START_CHARPOS is always in mouse_face_beg_row.  If necessary, swap
+	mouse_face_beg_row and mouse_face_end_row so that the former is
+	always above the latter or identical to it.
 
 2010-08-28  Eli Zaretskii  <eliz@gnu.org>
 
--- a/src/xdisp.c	Sat Oct 02 13:48:56 2010 +0200
+++ b/src/xdisp.c	Sat Oct 02 17:05:20 2010 +0200
@@ -15329,10 +15329,12 @@
 	{
 	  struct glyph *g;
 
-	  if (NILP (XBUFFER (w->buffer)->bidi_display_reordering))
+	  if (NILP (XBUFFER (w->buffer)->bidi_display_reordering)
+	      || (!best_row && !row->continued_p))
 	    return row;
 	  /* In bidi-reordered rows, there could be several rows
-	     occluding point.  We need to find the one which fits
+	     occluding point, all of them belonging to the same
+	     continued line.  We need to find the row which fits
 	     CHARPOS the best.  */
 	  for (g = row->glyphs[TEXT_AREA];
 	       g < row->glyphs[TEXT_AREA] + row->used[TEXT_AREA];
@@ -15344,11 +15346,14 @@
 		    {
 		      mindif = eabs (g->charpos - charpos);
 		      best_row = row;
-		    }
-		}
-	    }
-	}
-      else if (best_row)
+		      /* Exact match always wins.  */
+		      if (mindif == 0)
+			return best_row;
+		    }
+		}
+	    }
+	}
+      else if (best_row && !row->continued_p)
 	return best_row;
       ++row;
     }
@@ -23894,7 +23899,7 @@
 {
   struct window *w = XWINDOW (window);
   struct glyph_row *first = MATRIX_FIRST_TEXT_ROW (w->current_matrix);
-  struct glyph_row *row, *r;
+  struct glyph_row *r1, *r2;
   struct glyph *glyph, *end;
   EMACS_INT ignore, pos;
   int x;
@@ -23903,7 +23908,7 @@
   xassert (NILP (before_string) || STRINGP (before_string));
   xassert (NILP (after_string) || STRINGP (after_string));
 
-  /* Find the first highlighted glyph.  */
+  /* Find the row with START_CHARPOS.  */
   if (start_charpos < MATRIX_ROW_START_CHARPOS (first)
       && (NILP (XBUFFER (w->buffer)->bidi_display_reordering)
 	  || row_containing_pos (w, start_charpos, first, NULL, 0) == NULL))
@@ -23915,20 +23920,16 @@
     }
   else
     {
-      /* FIXME: this assumes that START_CHARPOS is in beg_row.  This
-	 is false for reordered lines that are continued.  Need to
-	 compute beg_row and end_row separately from beg_col and
-	 end_col.  */
-      row = row_containing_pos (w, start_charpos, first, NULL, 0);
-      if (row == NULL)
-	row = MATRIX_ROW (w->current_matrix, XFASTINT (w->window_end_vpos));
+      r1 = row_containing_pos (w, start_charpos, first, NULL, 0);
+      if (r1 == NULL)
+	r1 = MATRIX_ROW (w->current_matrix, XFASTINT (w->window_end_vpos));
 
       /* If the before-string or display-string contains newlines,
 	 row_containing_pos skips to its last row.  Move back.  */
       if (!NILP (before_string) || !NILP (display_string))
 	{
 	  struct glyph_row *prev;
-	  while ((prev = row - 1, prev >= first)
+	  while ((prev = r1 - 1, prev >= first)
 		 && MATRIX_ROW_END_CHARPOS (prev) == start_charpos
 		 && prev->used[TEXT_AREA] > 0)
 	    {
@@ -23939,139 +23940,16 @@
 		  || !(EQ (glyph->object, before_string)
 		       || EQ (glyph->object, display_string)))
 		break;
-	      row = prev;
-	    }
-	}
-
-      dpyinfo->mouse_face_beg_y = row->y;
-      dpyinfo->mouse_face_beg_row = MATRIX_ROW_VPOS (row, w->current_matrix);
-
-      /* For a bidi-reordered row, the positions of BEFORE_STRING,
-	 AFTER_STRING, DISPLAY_STRING, START_CHARPOS, and END_CHARPOS
-	 could be anywhere in the row and in any order.  The strategy
-	 below is to find the leftmost and the rightmost glyph that
-	 belongs to either of these 3 strings, or whose position is
-	 between START_CHARPOS and END_CHARPOS, and highlight all the
-	 glyphs between those two.  This may cover more than just the
-	 text between START_CHARPOS and END_CHARPOS if the range of
-	 characters strides the bidi level boundary, e.g. if the
-	 beginning is in R2L text while the end is in L2R text or vice
-	 versa.  */
-      if (!row->reversed_p)
-	{
-	  /* This row is in a left to right paragraph.  Scan it left
-	     to right.  */
-	  glyph = row->glyphs[TEXT_AREA];
-	  end = glyph + row->used[TEXT_AREA];
-	  x = row->x;
-
-	  /* Skip truncation glyphs at the start of the glyph row.  */
-	  if (row->displays_text_p)
-	    for (; glyph < end
-		   && INTEGERP (glyph->object)
-		   && glyph->charpos < 0;
-		 ++glyph)
-	      x += glyph->pixel_width;
-
-	  /* Scan the glyph row, looking for BEFORE_STRING,
-	     AFTER_STRING, or DISPLAY_STRING, and the first glyph from
-	     buffer whose position is between START_CHARPOS and
-	     END_CHARPOS.  */
-	  for (; glyph < end
-		 && !INTEGERP (glyph->object)
-		 && !EQ (glyph->object, display_string)
-		 && !(BUFFERP (glyph->object)
-		      && (glyph->charpos >= start_charpos
-			  && glyph->charpos < end_charpos));
-	       ++glyph)
-	    {
-	      /* BEFORE_STRING or AFTER_STRING are only relevant if
-		 they are present at buffer positions between
-		 START_CHARPOS and END_CHARPOS, or if they come from
-		 an overlay.  */
-	      if (EQ (glyph->object, before_string))
-		{
-		  pos = string_buffer_position (w, before_string,
-						start_charpos);
-		  /* If pos == 0, it means before_string came from an
-		     overlay, not from a buffer position.  */
-		  if (!pos || pos >= start_charpos && pos < end_charpos)
-		    break;
-		}
-	      else if (EQ (glyph->object, after_string))
-		{
-		  pos = string_buffer_position (w, after_string, end_charpos);
-		  if (!pos || pos >= start_charpos && pos < end_charpos)
-		    break;
-		}
-	      x += glyph->pixel_width;
-	    }
-	  dpyinfo->mouse_face_beg_x = x;
-	  dpyinfo->mouse_face_beg_col = glyph - row->glyphs[TEXT_AREA];
-	}
-      else
-	{
-	  /* This row is in a right to left paragraph.  Scan it right
-	     to left.  */
-	  struct glyph *g;
-
-	  end = row->glyphs[TEXT_AREA] - 1;
-	  glyph = end + row->used[TEXT_AREA];
-
-	  /* Skip truncation glyphs at the start of the glyph row.  */
-	  if (row->displays_text_p)
-	    for (; glyph > end
-		   && INTEGERP (glyph->object)
-		   && glyph->charpos < 0;
-		 --glyph)
-	      ;
-
-	  /* Scan the glyph row, looking for BEFORE_STRING,
-	     AFTER_STRING, or DISPLAY_STRING, and the first glyph from
-	     buffer whose position is between START_CHARPOS and
-	     END_CHARPOS.  */
-	  for (; glyph > end
-		 && !INTEGERP (glyph->object)
-		 && !EQ (glyph->object, display_string)
-		 && !(BUFFERP (glyph->object)
-		      && (glyph->charpos >= start_charpos
-			  && glyph->charpos < end_charpos));
-	       --glyph)
-	    {
-	      /* BEFORE_STRING or AFTER_STRING are only relevant if
-		 they are present at buffer positions between
-		 START_CHARPOS and END_CHARPOS, or if they come from
-		 an overlay.  */
-	      if (EQ (glyph->object, before_string))
-		{
-		  pos = string_buffer_position (w, before_string,
-						start_charpos);
-		  /* If pos == 0, it means before_string came from an
-		     overlay, not from a buffer position.  */
-		  if (!pos || pos >= start_charpos && pos < end_charpos)
-		    break;
-		}
-	      else if (EQ (glyph->object, after_string))
-		{
-		  pos = string_buffer_position (w, after_string, end_charpos);
-		  if (!pos || pos >= start_charpos && pos < end_charpos)
-		    break;
-		}
-	    }
-
-	  glyph++; /* first glyph to the right of the highlighted area */
-	  for (g = row->glyphs[TEXT_AREA], x = row->x; g < glyph; g++)
-	    x += g->pixel_width;
-	  dpyinfo->mouse_face_beg_x = x;
-	  dpyinfo->mouse_face_beg_col = glyph - row->glyphs[TEXT_AREA];
-	}
-    }
-
-  /* Find the last highlighted glyph.  */
-  r = row_containing_pos (w, end_charpos, first, NULL, 0);
-  if (r == NULL)
-    {
-      r = MATRIX_ROW (w->current_matrix, XFASTINT (w->window_end_vpos));
+	      r1 = prev;
+	    }
+	}
+    }
+
+  /* Find the row with END_CHARPOS.  */
+  r2 = row_containing_pos (w, end_charpos, first, NULL, 0);
+  if (r2 == NULL)
+    {
+      r2 = MATRIX_ROW (w->current_matrix, XFASTINT (w->window_end_vpos));
       dpyinfo->mouse_face_past_end = 1;
     }
   else if (!NILP (after_string))
@@ -24081,36 +23959,164 @@
       struct glyph_row *last
 	= MATRIX_ROW (w->current_matrix, XFASTINT (w->window_end_vpos));
 
-      for (next = r + 1;
+      for (next = r2 + 1;
 	   next <= last
 	     && next->used[TEXT_AREA] > 0
 	     && EQ (next->glyphs[TEXT_AREA]->object, after_string);
 	   ++next)
-	r = next;
+	r2 = next;
+    }
+
+  /* The rest of the display engine assumes that mouse_face_beg_row is
+     either above below mouse_face_end_row or identical to it.  But
+     with bidi-reordered continued lines, the row for START_CHARPOS
+     could be below the row for END_CHARPOS.  If so, swap the rows and
+     store them in correct order.  */
+  if (r1->y > r2->y)
+    {
+      struct glyph_row *tem = r2;
+
+      r2 = r1;
+      r1 = tem;
+    }
+  dpyinfo->mouse_face_beg_y = r1->y;
+  dpyinfo->mouse_face_beg_row = MATRIX_ROW_VPOS (r1, w->current_matrix);
+  dpyinfo->mouse_face_end_y = r2->y;
+  dpyinfo->mouse_face_end_row = MATRIX_ROW_VPOS (r2, w->current_matrix);
+
+  /* For a bidi-reordered row, the positions of BEFORE_STRING,
+     AFTER_STRING, DISPLAY_STRING, START_CHARPOS, and END_CHARPOS
+     could be anywhere in the row and in any order.  The strategy
+     below is to find the leftmost and the rightmost glyph that
+     belongs to either of these 3 strings, or whose position is
+     between START_CHARPOS and END_CHARPOS, and highlight all the
+     glyphs between those two.  This may cover more than just the text
+     between START_CHARPOS and END_CHARPOS if the range of characters
+     strides the bidi level boundary, e.g. if the beginning is in R2L
+     text while the end is in L2R text or vice versa.  */
+  if (!r1->reversed_p)
+    {
+      /* This row is in a left to right paragraph.  Scan it left to
+	 right.  */
+      glyph = r1->glyphs[TEXT_AREA];
+      end = glyph + r1->used[TEXT_AREA];
+      x = r1->x;
+
+      /* Skip truncation glyphs at the start of the glyph row.  */
+      if (r1->displays_text_p)
+	for (; glyph < end
+	       && INTEGERP (glyph->object)
+	       && glyph->charpos < 0;
+	     ++glyph)
+	  x += glyph->pixel_width;
+
+      /* Scan the glyph row, looking for BEFORE_STRING, AFTER_STRING,
+	 or DISPLAY_STRING, and the first glyph from buffer whose
+	 position is between START_CHARPOS and END_CHARPOS.  */
+      for (; glyph < end
+	     && !INTEGERP (glyph->object)
+	     && !EQ (glyph->object, display_string)
+	     && !(BUFFERP (glyph->object)
+		  && (glyph->charpos >= start_charpos
+		      && glyph->charpos < end_charpos));
+	   ++glyph)
+	{
+	  /* BEFORE_STRING or AFTER_STRING are only relevant if they
+	     are present at buffer positions between START_CHARPOS and
+	     END_CHARPOS, or if they come from an overlay.  */
+	  if (EQ (glyph->object, before_string))
+	    {
+	      pos = string_buffer_position (w, before_string,
+					    start_charpos);
+	      /* If pos == 0, it means before_string came from an
+		 overlay, not from a buffer position.  */
+	      if (!pos || pos >= start_charpos && pos < end_charpos)
+		break;
+	    }
+	  else if (EQ (glyph->object, after_string))
+	    {
+	      pos = string_buffer_position (w, after_string, end_charpos);
+	      if (!pos || pos >= start_charpos && pos < end_charpos)
+		break;
+	    }
+	  x += glyph->pixel_width;
+	}
+      dpyinfo->mouse_face_beg_x = x;
+      dpyinfo->mouse_face_beg_col = glyph - r1->glyphs[TEXT_AREA];
+    }
+  else
+    {
+      /* This row is in a right to left paragraph.  Scan it right to
+	 left.  */
+      struct glyph *g;
+
+      end = r1->glyphs[TEXT_AREA] - 1;
+      glyph = end + r1->used[TEXT_AREA];
+
+      /* Skip truncation glyphs at the start of the glyph row.  */
+      if (r1->displays_text_p)
+	for (; glyph > end
+	       && INTEGERP (glyph->object)
+	       && glyph->charpos < 0;
+	     --glyph)
+	  ;
+
+      /* Scan the glyph row, looking for BEFORE_STRING, AFTER_STRING,
+	 or DISPLAY_STRING, and the first glyph from buffer whose
+	 position is between START_CHARPOS and END_CHARPOS.  */
+      for (; glyph > end
+	     && !INTEGERP (glyph->object)
+	     && !EQ (glyph->object, display_string)
+	     && !(BUFFERP (glyph->object)
+		  && (glyph->charpos >= start_charpos
+		      && glyph->charpos < end_charpos));
+	   --glyph)
+	{
+	  /* BEFORE_STRING or AFTER_STRING are only relevant if they
+	     are present at buffer positions between START_CHARPOS and
+	     END_CHARPOS, or if they come from an overlay.  */
+	  if (EQ (glyph->object, before_string))
+	    {
+	      pos = string_buffer_position (w, before_string, start_charpos);
+	      /* If pos == 0, it means before_string came from an
+		 overlay, not from a buffer position.  */
+	      if (!pos || pos >= start_charpos && pos < end_charpos)
+		break;
+	    }
+	  else if (EQ (glyph->object, after_string))
+	    {
+	      pos = string_buffer_position (w, after_string, end_charpos);
+	      if (!pos || pos >= start_charpos && pos < end_charpos)
+		break;
+	    }
+	}
+
+      glyph++; /* first glyph to the right of the highlighted area */
+      for (g = r1->glyphs[TEXT_AREA], x = r1->x; g < glyph; g++)
+	x += g->pixel_width;
+      dpyinfo->mouse_face_beg_x = x;
+      dpyinfo->mouse_face_beg_col = glyph - r1->glyphs[TEXT_AREA];
     }
 
   /* If the highlight ends in a different row, compute GLYPH and END
-     for the end row.  */
-  if (r != row)
-    {
-      if (!r->reversed_p)
-	{
-	  glyph = r->glyphs[TEXT_AREA];
-	  end = glyph + r->used[TEXT_AREA];
-	  x = r->x;
-	}
-      else
-	{
-	  end = r->glyphs[TEXT_AREA] - 1;
-	  glyph = end + r->used[TEXT_AREA];
-	}
-      row = r;
-    }
-
-  dpyinfo->mouse_face_end_y = row->y;
-  dpyinfo->mouse_face_end_row = MATRIX_ROW_VPOS (row, w->current_matrix);
-
-  if (!row->reversed_p)
+     for the end row.  Otherwise, reuse the values computed above for
+     the row where the highlight begins.  */
+  if (r2 != r1)
+    {
+      if (!r2->reversed_p)
+	{
+	  glyph = r2->glyphs[TEXT_AREA];
+	  end = glyph + r2->used[TEXT_AREA];
+	  x = r2->x;
+	}
+      else
+	{
+	  end = r2->glyphs[TEXT_AREA] - 1;
+	  glyph = end + r2->used[TEXT_AREA];
+	}
+    }
+
+  if (!r2->reversed_p)
     {
       /* Skip truncation and continuation glyphs near the end of the
 	 row, and also blanks and stretch glyphs inserted by
@@ -24153,14 +24159,14 @@
 	x += glyph->pixel_width;
 
       dpyinfo->mouse_face_end_x = x;
-      dpyinfo->mouse_face_end_col = glyph - row->glyphs[TEXT_AREA];
+      dpyinfo->mouse_face_end_col = glyph - r2->glyphs[TEXT_AREA];
     }
   else
     {
       /* Skip truncation and continuation glyphs near the end of the
 	 row, and also blanks and stretch glyphs inserted by
 	 extend_face_to_end_of_line.  */
-      x = row->x;
+      x = r2->x;
       end++;
       while (end < glyph
 	     && INTEGERP (end->object)
@@ -24200,7 +24206,7 @@
 	  x += end->pixel_width;
 	}
       dpyinfo->mouse_face_end_x = x;
-      dpyinfo->mouse_face_end_col = end - row->glyphs[TEXT_AREA];
+      dpyinfo->mouse_face_end_col = end - r2->glyphs[TEXT_AREA];
     }
 
   dpyinfo->mouse_face_window = window;
@@ -24859,8 +24865,8 @@
 	  || !MATRIX_ROW (w->current_matrix, vpos)->displays_text_p
 	  /* R2L rows have a stretch glyph at their front, which
 	     stands for no text, whereas L2R rows have no glyphs at
-	     all beyond the end of text.  Treat such stretch glyphs as
-	     NULL glyphs in L2R rows.  */
+	     all beyond the end of text.  Treat such stretch glyphs
+	     like we do with NULL glyphs in L2R rows.  */
 	  || (MATRIX_ROW (w->current_matrix, vpos)->reversed_p
 	      && glyph == MATRIX_ROW (w->current_matrix, vpos)->glyphs[TEXT_AREA]
 	      && glyph->type == STRETCH_GLYPH