Subject: bug#28312: 25.2; Arabic script changes when scrolling

From: Eli Zaretskii <[email-masked]>
Date: Thu, 06 Jun 2019 17:04:13 +0300

tags 28312 patch
thanks

> Date: Fri, 01 Sep 2017 11:35:38 +0300
> From: Eli Zaretskii <[email-masked]>
> Cc: [email-masked], [email-masked]
> 
> I'm guessing that somehow we don't redraw enough characters to give
> the font shaper a chance to shape them correctly.  CC'ing Handa-san,
> in the hope that he could have some insights on this.

The guess was correct, but finding why this happens and under what
circumstances proved an elusive problem.

I eventually found the culprit: it turned out we sometimes stop
examining buffer text at arbitrary positions, and don't make sure
these positions are never in the middle of a composable character
sequence.  I implemented a solution, the patch is below.
Unfortunately, it makes redisplay slower in buffers with lots of
composable characters, so I hesitate to install it.

Would people please try the patch and see if Emacs is still reasonably
responsive in buffers with Arabic text, especially when marking or
extending the region under transient-mark-mode?  If I get enough
positive feedback, I will install this.  TIA.

diff --git a/src/composite.c b/src/composite.c
index 7d7ed3f..aba47a9 100644
--- a/src/composite.c
+++ b/src/composite.c
@@ -1427,7 +1427,7 @@ struct position_record
    representing the composition, and return true.  Otherwise, *GSTRING to
    Qnil, and return false.  */
 
-static bool
+bool
 find_automatic_composition (ptrdiff_t pos, ptrdiff_t limit,
 			    ptrdiff_t *start, ptrdiff_t *end,
 			    Lisp_Object *gstring, Lisp_Object string)
diff --git a/src/composite.h b/src/composite.h
index 8675163..b4c6a93 100644
--- a/src/composite.h
+++ b/src/composite.h
@@ -324,7 +324,9 @@ extern bool composition_reseat_it (struct composition_it *, ptrdiff_t,
 				   struct face *, Lisp_Object);
 extern int composition_update_it (struct composition_it *,
                                   ptrdiff_t, ptrdiff_t, Lisp_Object);
-
+extern bool find_automatic_composition (ptrdiff_t, ptrdiff_t,
+					ptrdiff_t *, ptrdiff_t *,
+					Lisp_Object *, Lisp_Object);
 extern ptrdiff_t composition_adjust_point (ptrdiff_t, ptrdiff_t);
 
 INLINE_HEADER_END
diff --git a/src/xdisp.c b/src/xdisp.c
index 5f43815..e4fb48b 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -3618,7 +3618,25 @@ compute_stop_pos (struct it *it)
       /* Set up variables for computing the stop position from text
          property changes.  */
       XSETBUFFER (object, current_buffer);
-      limit = make_fixnum (IT_CHARPOS (*it) + TEXT_PROP_DISTANCE_LIMIT);
+      /* Find the position to limit the search for properties.  It
+	 must not be in the middle of a composable sequence, because
+	 having a stop_charpos there risks breaking the composition
+	 into two or more separate parts, to be submitted separately
+	 to the shaping engine, and that will produce incorrect
+	 shaping with some scripts (e.g., Arabic).  */
+      pos = charpos + TEXT_PROP_DISTANCE_LIMIT;
+      if (!NILP (Vauto_composition_mode))
+	{
+	  ptrdiff_t cpos = charpos, posmax = min (pos, ZV);
+	  ptrdiff_t start = posmax, end = posmax;
+	  Lisp_Object ignored;
+	  while (find_automatic_composition (cpos, posmax, &start, &end,
+					     &ignored, Qnil)
+		 && end < posmax)
+	    cpos = end;
+	  pos = max (end, posmax);
+	}
+      limit = make_fixnum (pos);
     }
 
   /* Get the interval containing IT's position.  Value is a null