changeset 107595:69c12db7031d

Retrospective commit from 2009-10-05. Continue working on paragraph base direction. Support per-buffer default paragraph direction. buffer.h (struct buffer): New member paragraph_direction. buffer.c (init_buffer_once): Initialize it. (syms_of_buffer): Declare Lisp variables default-paragraph-direction and paragraph-direction. dispextern.h (struct it): New member paragraph_embedding. xdisp.c (init_iterator): Initialize it from the buffer's value of paragraph-direction. <Qright_to_left, Qleft_to_right>: New variables. (syms_of_xdisp): Initialize and staticpro them. (set_iterator_to_next, next_element_from_buffer): Use the value of paragraph_embedding to determine the paragraph direction. bidi.c (bidi_line_init): Fix second argument to bidi_set_sor_type. (bidi_init_it): Initialize paragraph_dir to NEUTRAL_DIR. (bidi_get_next_char_visually): Record the last character of the separator in separator_limit, not the character after that. (bidi_find_paragraph_start): Accept character and byte positions instead of the whole iterator stricture. All callers changed.
author Eli Zaretskii <eliz@gnu.org>
date Fri, 01 Jan 2010 06:22:52 -0500
parents 40b49fa464cf
children 866e76f8ad75
files src/ChangeLog.bidi src/bidi.c src/buffer.c src/buffer.h src/dispextern.h src/xdisp.c
diffstat 6 files changed, 104 insertions(+), 35 deletions(-) [+]
line wrap: on
line diff
--- a/src/ChangeLog.bidi	Fri Jan 01 06:17:13 2010 -0500
+++ b/src/ChangeLog.bidi	Fri Jan 01 06:22:52 2010 -0500
@@ -1,3 +1,26 @@
+2009-10-05  Eli Zaretskii  <eliz@gnu.org>
+
+	* buffer.h (struct buffer): New member paragraph_direction.
+	* buffer.c (init_buffer_once): Initialize it.
+	(syms_of_buffer): Declare Lisp variables
+	default-paragraph-direction and paragraph-direction.
+
+	* dispextern.h (struct it): New member paragraph_embedding.
+	* xdisp.c (init_iterator): Initialize it from the buffer's value
+	of paragraph-direction.
+	<Qright_to_left, Qleft_to_right>: New variables.
+	(syms_of_xdisp): Initialize and staticpro them.
+	(set_iterator_to_next, next_element_from_buffer): Use the value of
+	paragraph_embedding to determine the paragraph direction.
+
+	* bidi.c (bidi_line_init): Fix second argument to
+	bidi_set_sor_type.
+	(bidi_init_it): Initialize paragraph_dir to NEUTRAL_DIR.
+	(bidi_get_next_char_visually): Record the last character of the
+	separator in separator_limit, not the character after that.
+	(bidi_find_paragraph_start): Accept character and byte positions
+	instead of the whole iterator stricture.  All callers changed.
+
 2009-10-04  Eli Zaretskii  <eliz@gnu.org>
 
 	* bidi.c (bidi_at_paragraph_end): Check for paragraph-start if
--- a/src/bidi.c	Fri Jan 01 06:17:13 2010 -0500
+++ b/src/bidi.c	Fri Jan 01 06:22:52 2010 -0500
@@ -807,7 +807,8 @@
   bidi_it->invalid_rl_levels = -1;
   bidi_it->next_en_pos = -1;
   bidi_it->next_for_ws.type = UNKNOWN_BT;
-  bidi_set_sor_type (bidi_it, bidi_it->paragraph_dir,
+  bidi_set_sor_type (bidi_it,
+		     bidi_it->paragraph_dir == R2L ? 1 : 0,
 		     bidi_it->level_stack[0].level); /* X10 */
 
   bidi_cache_reset ();
@@ -816,11 +817,9 @@
 /* Find the beginning of this paragraph by looking back in the buffer.
    Value is the byte position of the paragraph's beginning.  */
 static EMACS_INT
-bidi_find_paragraph_start (struct bidi_it *bidi_it)
+bidi_find_paragraph_start (EMACS_INT pos, EMACS_INT pos_byte)
 {
   Lisp_Object re = Fbuffer_local_value (Qparagraph_start, Fcurrent_buffer ());
-  EMACS_INT pos = bidi_it->charpos;
-  EMACS_INT pos_byte = bidi_it->bytepos;
   EMACS_INT limit = ZV, limit_byte = ZV_BYTE;
 
   if (!STRINGP (re))
@@ -835,7 +834,13 @@
 }
 
 /* Determine the direction, a.k.a. base embedding level, of the
-   paragraph we are about to iterate through.  */
+   paragraph we are about to iterate through.  If DIR is either L2R or
+   R2L, just use that.  Otherwise, determine the paragraph direction
+   from the first strong character of the paragraph.
+
+   Note that this gives the paragraph separator the same direction as
+   the preceding paragraph, even though Emacs generally views the
+   separartor as not belonging to any paragraph.  */
 void
 bidi_paragraph_init (bidi_dir_t dir, struct bidi_it *bidi_it)
 {
@@ -868,24 +873,21 @@
       if (bidi_it->charpos < bidi_it->separator_limit)
 	return;
 
-      /* If we are before another paragraph separator, continue
-	 through that with the previous paragraph direction.  */
-      sep_len = bidi_at_paragraph_end (bidi_it->charpos, bytepos);
-      if (sep_len >= 0)
+      /* If we are on a newline, get past it to where the next
+	 paragraph might start.  */
+      if (FETCH_CHAR (bytepos) == '\n')
 	{
-	  bidi_it->separator_limit += sep_len + 1;
-	  return;
+	  bytepos++;
+	  pos = bidi_it->charpos + 1;
 	}
-      else if (sep_len == -2)
-	/* We are in the middle of a paragraph.  Search back to where
-	   this paragraph starts.  */
-	bytepos = bidi_find_paragraph_start (bidi_it);
+
+      /* We are either at the beginning of a paragraph or in the
+	 middle of it.  Find where this paragraph starts.  */
+      bytepos = bidi_find_paragraph_start (pos, bytepos);
 
       /* We should always be at the beginning of a new line at this
 	 point.  */
-      if (!(bytepos == BEGV_BYTE
-	    || FETCH_CHAR (bytepos) == '\n'
-	    || FETCH_CHAR (bytepos - 1) == '\n'))
+      if (!(bytepos == BEGV_BYTE || FETCH_CHAR (bytepos - 1) == '\n'))
 	abort ();
 
       bidi_it->separator_limit = -1;
@@ -918,14 +920,15 @@
   else
     abort ();
 
-  /* Contrary to UAX#9 clause P3, we only default to L2R if we have no
-     previous usable paragraph direction.  */
+  /* Contrary to UAX#9 clause P3, we only default the paragraph
+     direction to L2R if we have no previous usable paragraph
+     direction.  */
   if (bidi_it->paragraph_dir == NEUTRAL_DIR)
-    bidi_it->paragraph_dir = L2R; /* P3 */
+    bidi_it->paragraph_dir = L2R; /* P3 and ``higher protocols'' */
   if (bidi_it->paragraph_dir == R2L)
-    bidi_it->level_stack[0].level == 1;
+    bidi_it->level_stack[0].level = 1;
   else
-    bidi_it->level_stack[0].level == 0;
+    bidi_it->level_stack[0].level = 0;
 
   bidi_line_init (bidi_it);
 }
@@ -953,6 +956,7 @@
   bidi_set_paragraph_end (bidi_it);
   bidi_it->new_paragraph = 1;
   bidi_it->separator_limit = -1;
+  bidi_it->paragraph_dir = NEUTRAL_DIR;
   bidi_it->type = NEUTRAL_B;
   bidi_it->type_after_w1 = UNKNOWN_BT;
   bidi_it->orig_type = UNKNOWN_BT;
@@ -1943,9 +1947,13 @@
       next_level = bidi_level_of_next_char (bidi_it);
     }
 
-  /* Take note when we are at the end of the paragraph.  The next time
-     we are about to be called, set_iterator_to_next will
-     automatically reinit the paragraph direction, if needed.  */
+  /* Take note when we have just processed the newline that precedes
+     the end of the paragraph.  The next time we are about to be
+     called, set_iterator_to_next will automatically reinit the
+     paragraph direction, if needed.  We do this at the newline before
+     the paragraph separator, because the next character might not be
+     the first character of the next paragraph, due to the bidi
+     reordering.  */
   if (bidi_it->scan_dir == 1
       && bidi_it->orig_type == NEUTRAL_B
       && bidi_it->bytepos < ZV_BYTE)
@@ -1956,9 +1964,9 @@
       if (sep_len >= 0)
 	{
 	  bidi_it->new_paragraph = 1;
-	  /* Record the buffer position of the first character after
-	     the paragraph separator.  */
-	  bidi_it->separator_limit = bidi_it->charpos + 1 + sep_len + 1;
+	  /* Record the buffer position of the last character of the
+	     paragraph separator.  */
+	  bidi_it->separator_limit = bidi_it->charpos + 1 + sep_len;
 	}
     }
 
--- a/src/buffer.c	Fri Jan 01 06:17:13 2010 -0500
+++ b/src/buffer.c	Fri Jan 01 06:22:52 2010 -0500
@@ -5188,6 +5188,7 @@
   buffer_defaults.ctl_arrow = Qt;
   buffer_defaults.bidi_display_reordering = Qnil;
   buffer_defaults.direction_reversed = Qnil;
+  buffer_defaults.paragraph_direction = Qnil;
   buffer_defaults.cursor_type = Qt;
   buffer_defaults.extra_line_spacing = Qnil;
   buffer_defaults.cursor_in_non_selected_windows = Qt;
@@ -5274,6 +5275,7 @@
   XSETFASTINT (buffer_local_flags.category_table, idx); ++idx;
   XSETFASTINT (buffer_local_flags.bidi_display_reordering, idx); ++idx;
   XSETFASTINT (buffer_local_flags.direction_reversed, idx); ++idx;
+  XSETFASTINT (buffer_local_flags.paragraph_direction, idx); ++idx;
   XSETFASTINT (buffer_local_flags.buffer_file_coding_system, idx);
   /* Make this one a permanent local.  */
   buffer_permanent_local_flags[idx++] = 1;
@@ -5545,6 +5547,11 @@
                      doc: /* *Default value of `enable-multibyte-characters' for buffers not overriding it.
 This is the same as (default-value 'enable-multibyte-characters).  */);
 
+  DEFVAR_LISP_NOPRO ("default-paragraph-direction",
+		     &buffer_defaults.paragraph_direction,
+		     doc: /* Default value of `paragraph-direction' for buffers that do not override it.
+This is the same as (default-value 'paragraph-direction).  */);
+
   DEFVAR_LISP_NOPRO ("default-buffer-file-coding-system",
                      &buffer_defaults.buffer_file_coding_system,
                      doc: /* Default value of `buffer-file-coding-system' for buffers not overriding it.
@@ -5806,6 +5813,18 @@
 		     doc: /*Non-nil means reorder bidirectional text for display in the visual order.
 See also the variable `direction-reversed'.  */);
 
+  DEFVAR_PER_BUFFER ("paragraph-direction",
+		     &current_buffer->paragraph_direction, Qnil,
+		     doc: /* *If non-nil, forces directionality of text paragraphs in the buffer.
+			     
+If this is nil (the default), the direction of each paragraph is
+determined by the first strong directional character of its text.
+The values of `right-to-left' and `left-to-right' override that.
+Any other value is treated as nil.
+			     
+This variable has no effect unless the buffer's value of
+\`bidi-display-reordering' is non-nil.  */);
+
  DEFVAR_PER_BUFFER ("truncate-lines", &current_buffer->truncate_lines, Qnil,
 		     doc: /* *Non-nil means do not display continuation lines.
 Instead, give each line of text just one screen line.
--- a/src/buffer.h	Fri Jan 01 06:17:13 2010 -0500
+++ b/src/buffer.h	Fri Jan 01 06:22:52 2010 -0500
@@ -664,6 +664,10 @@
   /* Non-nil means set beginning of lines at the right edge of
      windows.  */
   Lisp_Object direction_reversed;
+  /* If non-nil, specifies which direction of text to force in each
+     paragraph.  Nil means determine paragraph direction dynamically
+     for each paragraph.  */
+  Lisp_Object paragraph_direction;
   /* Non-nil means do selective display;
      see doc string in syms_of_buffer (buffer.c) for details.  */
   Lisp_Object selective_display;
--- a/src/dispextern.h	Fri Jan 01 06:17:13 2010 -0500
+++ b/src/dispextern.h	Fri Jan 01 06:22:52 2010 -0500
@@ -2327,6 +2327,7 @@
 
   /* For iterating over bidirectional text.  */
   struct bidi_it bidi_it;
+  bidi_dir_t paragraph_embedding;
 };
 
 
--- a/src/xdisp.c	Fri Jan 01 06:17:13 2010 -0500
+++ b/src/xdisp.c	Fri Jan 01 06:22:52 2010 -0500
@@ -248,6 +248,7 @@
 Lisp_Object Qgrow_only;
 Lisp_Object Qinhibit_eval_during_redisplay;
 Lisp_Object Qbuffer_position, Qposition, Qobject;
+Lisp_Object Qright_to_left, Qleft_to_right;
 
 /* Cursor shapes */
 Lisp_Object Qbar, Qhbar, Qbox, Qhollow;
@@ -2809,7 +2810,17 @@
   /* If we are to reorder bidirectional text, init the bidi
      iterator.  */
   if (it->bidi_p)
-    bidi_init_it (charpos, bytepos, &it->bidi_it);
+    {
+      /* Note the paragraph direction that this buffer wants to
+	 use.  */
+      if (EQ (current_buffer->paragraph_direction, Qleft_to_right))
+	it->paragraph_embedding = L2R;
+      else if (EQ (current_buffer->paragraph_direction, Qright_to_left))
+	it->paragraph_embedding = R2L;
+      else
+	it->paragraph_embedding = NEUTRAL_DIR;
+      bidi_init_it (charpos, bytepos, &it->bidi_it);
+    }
 
   /* If a buffer position was specified, set the iterator there,
      getting overlays and face properties from that position.  */
@@ -6106,7 +6117,7 @@
 	      /* If this is a new paragraph, determine its base
 		 direction (a.k.a. its base embedding level).  */
 	      if (it->bidi_it.new_paragraph)
-		bidi_paragraph_init (NEUTRAL_DIR, &it->bidi_it);
+		bidi_paragraph_init (it->paragraph_embedding, &it->bidi_it);
 	      bidi_get_next_char_visually (&it->bidi_it);
 	      IT_BYTEPOS (*it) = it->bidi_it.bytepos;
 	      IT_CHARPOS (*it) = it->bidi_it.charpos;
@@ -6527,9 +6538,7 @@
 	  || FETCH_CHAR (it->bidi_it.bytepos - 1) == '\n'
 	  || FETCH_CHAR (it->bidi_it.bytepos) == '\n')
 	{
-	  /* FIXME: NEUTRAL_DIR below should be user-definable and/or
-	     come from some ``higher protocol''.  */
-	  bidi_paragraph_init (NEUTRAL_DIR, &it->bidi_it);
+	  bidi_paragraph_init (it->paragraph_embedding, &it->bidi_it);
 	  bidi_get_next_char_visually (&it->bidi_it);
 	}
       else
@@ -6543,7 +6552,7 @@
 	  IT_BYTEPOS (*it) = CHAR_TO_BYTE (IT_CHARPOS (*it));
 	  it->bidi_it.charpos = IT_CHARPOS (*it);
 	  it->bidi_it.bytepos = IT_BYTEPOS (*it);
-	  bidi_paragraph_init (NEUTRAL_DIR, &it->bidi_it);
+	  bidi_paragraph_init (it->paragraph_embedding, &it->bidi_it);
 	  do {
 	    /* Now return to buffer position where we were asked to
 	       get the next display element, and produce that.  */
@@ -24956,6 +24965,11 @@
   staticpro (&previous_help_echo_string);
   help_echo_pos = -1;
 
+  Qright_to_left = intern ("right-to-left");
+  staticpro (&Qright_to_left);
+  Qleft_to_right = intern ("left-to-right");
+  staticpro (&Qleft_to_right);
+
 #ifdef HAVE_WINDOW_SYSTEM
   DEFVAR_BOOL ("x-stretch-cursor", &x_stretch_cursor_p,
     doc: /* *Non-nil means draw block cursor as wide as the glyph under it.