]> code.delx.au - gnu-emacs/commitdiff
Fix bug #9771 with slow redisplay in long lines full of control characters.
authorEli Zaretskii <eliz@gnu.org>
Tue, 18 Oct 2011 16:56:09 +0000 (18:56 +0200)
committerEli Zaretskii <eliz@gnu.org>
Tue, 18 Oct 2011 16:56:09 +0000 (18:56 +0200)
 src/bidi.c (bidi_line_init): Initialize next_en_pos to zero, not -1.
 (bidi_resolve_neutral): Don't enter the expensive loop looking for
 non-neutral characters if the current character is a paragraph
 separator (a.k.a. Newline).  This avoids running the same
 expensive loop twice, once when we consume the preceding newline
 and the other time when the line actually needs to be displayed.
 Avoid the loop when we see neutrals on the base embedding level
 following a character whose directionality is the same as the
 paragraph's.  This avoids running the expensive loop when a line
 ends in a long sequence of neutrals, like control characters.
 Add assertion against STRONG_AL type.  Slightly rearrange code
 that determines the type of a neutral given the first non-neutral
 that follows it.
 (bidi_level_of_next_char): Set next_en_pos to zero when
 invalidating its info.

src/ChangeLog
src/bidi.c

index 07b0418b3998cf2837cd88164c4cd02e77769ef8..d21e638376453171cb9c094aab6b2752d9af4665 100644 (file)
@@ -1,3 +1,22 @@
+2011-10-18  Eli Zaretskii  <eliz@gnu.org>
+
+       Fix part 3 of bug#9771.
+       * bidi.c (bidi_line_init): Initialize next_en_pos to zero, not -1.
+       (bidi_resolve_neutral): Don't enter the expensive loop looking for
+       non-neutral characters if the current character is a paragraph
+       separator (a.k.a. Newline).  This avoids running the same
+       expensive loop twice, once when we consume the preceding newline
+       and the other time when the line actually needs to be displayed.
+       Avoid the loop when we see neutrals on the base embedding level
+       following a character whose directionality is the same as the
+       paragraph's.  This avoids running the expensive loop when a line
+       ends in a long sequence of neutrals, like control characters.
+       Add assertion against STRONG_AL type.  Slightly rearrange code
+       that determines the type of a neutral given the first non-neutral
+       that follows it.
+       (bidi_level_of_next_char): Set next_en_pos to zero when
+       invalidating its info.
+
 2011-10-17  Eli Zaretskii  <eliz@gnu.org>
 
        * xdisp.c (push_display_prop): Determine whether to record string
index f06c146ca84f88b0888285e0d6030a42bd01cbb7..29e3c8173187dd92f79a50e738e90ec9baca7d46 100644 (file)
@@ -846,7 +846,9 @@ bidi_line_init (struct bidi_it *bidi_it)
   bidi_it->level_stack[0].override = NEUTRAL_DIR; /* X1 */
   bidi_it->invalid_levels = 0;
   bidi_it->invalid_rl_levels = -1;
-  bidi_it->next_en_pos = -1;
+  /* Setting this to zero will force its recomputation the first time
+     we need it for W5.  */
+  bidi_it->next_en_pos = 0;
   bidi_it->next_for_ws.type = UNKNOWN_BT;
   bidi_set_sor_type (bidi_it,
                     (bidi_it->paragraph_dir == R2L ? 1 : 0),
@@ -1732,7 +1734,7 @@ bidi_resolve_weak (struct bidi_it *bidi_it)
          if (bidi_it->prev.type_after_w1 == WEAK_EN /* ET/BN w/EN before it */
              || bidi_it->next_en_pos > bidi_it->charpos)
            type = WEAK_EN;
-         else                  /* W5: ET/BN with EN after it.  */
+         else if (bidi_it->next_en_pos >=0) /* W5: ET/BN with EN after it.  */
            {
              EMACS_INT en_pos = bidi_it->charpos + bidi_it->nchars;
              const unsigned char *s = (STRINGP (bidi_it->string.lstring)
@@ -1775,6 +1777,11 @@ bidi_resolve_weak (struct bidi_it *bidi_it)
                  else if (type == WEAK_BN)
                    type = NEUTRAL_ON; /* W6/Retaining */
                }
+             else if (type_of_next == NEUTRAL_B)
+               /* Record the fact that there are no more ENs from
+                  here to the end of paragraph, to avoid entering the
+                  loop above ever again in this paragraph.  */
+               bidi_it->next_en_pos = -1;
            }
        }
     }
@@ -1843,13 +1850,45 @@ bidi_resolve_neutral (struct bidi_it *bidi_it)
        || type == NEUTRAL_ON))
     abort ();
 
-  if (bidi_get_category (type) == NEUTRAL
+  if ((type != NEUTRAL_B /* Don't risk entering the long loop below if
+                           we are already at paragraph end.  */
+       && bidi_get_category (type) == NEUTRAL)
       || (type == WEAK_BN && prev_level == current_level))
     {
       if (bidi_it->next_for_neutral.type != UNKNOWN_BT)
        type = bidi_resolve_neutral_1 (bidi_it->prev_for_neutral.type,
                                       bidi_it->next_for_neutral.type,
                                       current_level);
+      /* The next two "else if" clauses are shortcuts for the
+        important special case when we have a long sequence of
+        neutral or WEAK_BN characters, such as whitespace or nulls or
+        other control characters, on the base embedding level of the
+        paragraph, and that sequence goes all the way to the end of
+        the paragraph and follows a character whose resolved
+        directionality is identical to the base embedding level.
+        (This is what happens in a buffer with plain L2R text that
+        happens to include long sequences of control characters.)  By
+        virtue of N1, the result of examining this long sequence will
+        always be either STRONG_L or STRONG_R, depending on the base
+        embedding level.  So we use this fact directly instead of
+        entering the expensive loop in the "else" clause.  */
+      else if (current_level == 0
+              && bidi_it->prev_for_neutral.type == STRONG_L
+              && !bidi_explicit_dir_char (bidi_it->ch))
+       type = bidi_resolve_neutral_1 (bidi_it->prev_for_neutral.type,
+                                      STRONG_L, current_level);
+      else if (/* current level is 1 */
+              current_level == 1
+              /* base embedding level is also 1 */
+              && bidi_it->level_stack[0].level == 1
+              /* previous character is one of those considered R for
+                 the purposes of W5 */
+              && (bidi_it->prev_for_neutral.type == STRONG_R
+                  || bidi_it->prev_for_neutral.type == WEAK_EN
+                  || bidi_it->prev_for_neutral.type == WEAK_AN)
+              && !bidi_explicit_dir_char (bidi_it->ch))
+       type = bidi_resolve_neutral_1 (bidi_it->prev_for_neutral.type,
+                                      STRONG_R, current_level);
       else
        {
          /* Arrrgh!!  The UAX#9 algorithm is too deeply entrenched in
@@ -1900,6 +1939,9 @@ bidi_resolve_neutral (struct bidi_it *bidi_it)
              case STRONG_L:
              case STRONG_R:
              case STRONG_AL:
+               /* Actually, STRONG_AL cannot happen here, because
+                  bidi_resolve_weak converts it to STRONG_R, per W3.  */
+               xassert (type != STRONG_AL);
                next_type = type;
                break;
              case WEAK_EN:
@@ -1907,7 +1949,6 @@ bidi_resolve_neutral (struct bidi_it *bidi_it)
                /* N1: ``European and Arabic numbers are treated as
                   though they were R.''  */
                next_type = STRONG_R;
-               saved_it.next_for_neutral.type = STRONG_R;
                break;
              case WEAK_BN:
                if (!bidi_explicit_dir_char (bidi_it->ch))
@@ -1920,11 +1961,7 @@ bidi_resolve_neutral (struct bidi_it *bidi_it)
                   member.  */
                if (saved_it.type != WEAK_BN
                    || bidi_get_category (bidi_it->prev.type_after_w1) == NEUTRAL)
-                 {
-                   next_type = bidi_it->prev_for_neutral.type;
-                   saved_it.next_for_neutral.type = next_type;
-                   bidi_check_type (next_type);
-                 }
+                 next_type = bidi_it->prev_for_neutral.type;
                else
                  {
                    /* This is a BN which does not adjoin neutrals.
@@ -1938,7 +1975,9 @@ bidi_resolve_neutral (struct bidi_it *bidi_it)
            }
          type = bidi_resolve_neutral_1 (saved_it.prev_for_neutral.type,
                                         next_type, current_level);
+         saved_it.next_for_neutral.type = next_type;
          saved_it.type = type;
+         bidi_check_type (next_type);
          bidi_check_type (type);
          bidi_copy_it (bidi_it, &saved_it);
        }
@@ -2014,7 +2053,7 @@ bidi_level_of_next_char (struct bidi_it *bidi_it)
        bidi_it->next_for_neutral.type = UNKNOWN_BT;
       if (bidi_it->next_en_pos >= 0
          && bidi_it->charpos >= bidi_it->next_en_pos)
-       bidi_it->next_en_pos = -1;
+       bidi_it->next_en_pos = 0;
       if (bidi_it->next_for_ws.type != UNKNOWN_BT
          && bidi_it->charpos >= bidi_it->next_for_ws.charpos)
        bidi_it->next_for_ws.type = UNKNOWN_BT;