@cindex bidirectional editing
@cindex right-to-left text
- Emacs supports editing text written in scripts, such as Arabic and
-Hebrew, whose natural ordering of horizontal text for display is from
-right to left. However, digits and Latin text embedded in these
-scripts are still displayed left to right. It is also not uncommon to
-have small portions of text in Arabic or Hebrew embedded in an otherwise
-Latin document; e.g., as comments and strings in a program source
-file. For these reasons, text that uses these scripts is actually
-@dfn{bidirectional}: a mixture of runs of left-to-right and
-right-to-left characters.
+ Emacs supports editing text written in scripts, such as Arabic,
+Farsi, and Hebrew, whose natural ordering of horizontal text for
+display is from right to left. However, digits and Latin text
+embedded in these scripts are still displayed left to right. It is
+also not uncommon to have small portions of text in Arabic or Hebrew
+embedded in an otherwise Latin document; e.g., as comments and strings
+in a program source file. For these reasons, text that uses these
+scripts is actually @dfn{bidirectional}: a mixture of runs of
+left-to-right and right-to-left characters.
This section describes the facilities and options provided by Emacs
for editing bidirectional text.
@cindex base direction of paragraphs
@cindex paragraph, base direction
+@vindex bidi-paragraph-start-re
+@vindex bidi-paragraph-separate-re
Each paragraph of bidirectional text can have its own @dfn{base
-direction}, either right-to-left or left-to-right. (Paragraph
-@c paragraph-separate etc have no influence on this?
-boundaries are empty lines, i.e., lines consisting entirely of
-whitespace characters.) Text in left-to-right paragraphs begins on
-the screen at the left margin of the window and is truncated or
-continued when it reaches the right margin. By contrast, text in
-right-to-left paragraphs is displayed starting at the right margin and
-is continued or truncated at the left margin.
+direction}, either right-to-left or left-to-right. Text in
+left-to-right paragraphs begins on the screen at the left margin of
+the window and is truncated or continued when it reaches the right
+margin. By contrast, text in right-to-left paragraphs is displayed
+starting at the right margin and is continued or truncated at the left
+margin. By default, paragraph boundaries are empty lines, i.e., lines
+consisting entirely of whitespace characters. To change that, you can
+customize the two variables @code{bidi-paragraph-start-re} and
+@code{bidi-paragraph-separate-re}, whose values should be regular
+expressions (strings); e.g., to have a single newline start a new
+paragraph, set both of these variables to @code{"^"}. These two
+variables are buffer-local (@pxref{Locals}).
@vindex bidi-paragraph-direction
Emacs determines the base direction of each paragraph dynamically,
Right-to-left paragraphs are displayed beginning at the right margin,
and are continued or truncated at the left margin.
+@cindex paragraph-start, and bidirectional display
+@cindex paragraph-separate, and bidirectional display
+ Where exactly paragraphs start and end, for the purpose of the Emacs
+@acronym{UBA} implementation, is determined by the following two
+buffer-local variables (note that that @code{paragraph-start} and
+@code{paragraph-separate} have no influence on this). By default both
+of these variables are @code{nil}, and paragraphs are bounded by empty
+lines, i.e., lines that consist entirely of zero or more whitespace
+characters followed by a newline.
+
+@defvar bidi-paragraph-start-re
+If non-@code{nil}, this variable's value should be a regular
+expression matching a line that starts or separates two paragraphs.
+The regular expression is always matched after a newline, so it is
+best to anchor it, i.e., begin it with a @code{"^"}.
+@end defvar
+
+@defvar bidi-paragraph-separate-re
+If non-@code{nil}, this variable's value should be a regular
+expression matching a line separates two paragraphs. The regular
+expression is always matched after a newline, so it is best to anch
+it, i.e., begin it with a @code{"^"}.
+@end defvar
+
+ If you modify any of these two variables, you should normally modify
+both, to make sure they describe paragraphs consistently. For
+example, to have each new line start a new paragraph for
+bidi-reordering purposes, set both variables to @code{"^"}.
+
By default, Emacs determines the base direction of each paragraph by
looking at the text at its beginning. The precise method of
determining the base direction is specified by the @acronym{UBA}; in a
different buffer while keeping point, mark, markers, and text
properties as intact as possible.
++++
+** More user control of reordering bidirectional text for display.
+The two new variables, 'bidi-paragraph-start-re' and
+'bidi-paragraph-separate-re', allow customization of what exactly are
+paragraphs, for the purposes of bidirectional display.
+
\f
* Changes in Specialized Modes and Packages in Emacs 26.1
Lisp_Object start_re;
ptrdiff_t val;
- sep_re = paragraph_separate_re;
- start_re = paragraph_start_re;
+ if (STRINGP (BVAR (current_buffer, bidi_paragraph_separate_re)))
+ sep_re = BVAR (current_buffer, bidi_paragraph_separate_re);
+ else
+ sep_re = paragraph_separate_re;
+ if (STRINGP (BVAR (current_buffer, bidi_paragraph_start_re)))
+ start_re = BVAR (current_buffer, bidi_paragraph_start_re);
+ else
+ start_re = paragraph_start_re;
val = fast_looking_at (sep_re, charpos, bytepos, ZV, ZV_BYTE, Qnil);
if (val < 0)
static ptrdiff_t
bidi_find_paragraph_start (ptrdiff_t pos, ptrdiff_t pos_byte)
{
- Lisp_Object re = paragraph_start_re;
+ Lisp_Object re =
+ STRINGP (BVAR (current_buffer, bidi_paragraph_start_re))
+ ? BVAR (current_buffer, bidi_paragraph_start_re)
+ : paragraph_start_re;
ptrdiff_t limit = ZV, limit_byte = ZV_BYTE;
struct region_cache *bpc = bidi_paragraph_cache_on_off ();
ptrdiff_t n = 0, oldpos = pos, next;
if (sep_len >= 0)
{
bidi_it->new_paragraph = 1;
- /* Record the buffer position of the last character of the
- paragraph separator. */
- bidi_it->separator_limit
- = bidi_it->charpos + bidi_it->nchars + sep_len;
+ /* Record the buffer position of the last character of
+ the paragraph separator. If the paragraph separator
+ is an empty string (e.g., the regex is "^"), the
+ newline that precedes the end of the paragraph is
+ that last character. */
+ if (sep_len > 0)
+ bidi_it->separator_limit
+ = bidi_it->charpos + bidi_it->nchars + sep_len;
+ else
+ bidi_it->separator_limit = bidi_it->charpos;
}
}
}
b->bidi_display_reordering_ = val;
}
static void
+bset_bidi_paragraph_start_re (struct buffer *b, Lisp_Object val)
+{
+ b->bidi_paragraph_start_re_ = val;
+}
+static void
+bset_bidi_paragraph_separate_re (struct buffer *b, Lisp_Object val)
+{
+ b->bidi_paragraph_separate_re_ = val;
+}
+static void
bset_buffer_file_coding_system (struct buffer *b, Lisp_Object val)
{
b->buffer_file_coding_system_ = val;
swapfield_ (enable_multibyte_characters, Lisp_Object);
swapfield_ (bidi_display_reordering, Lisp_Object);
swapfield_ (bidi_paragraph_direction, Lisp_Object);
+ swapfield_ (bidi_paragraph_separate_re, Lisp_Object);
+ swapfield_ (bidi_paragraph_start_re, Lisp_Object);
/* FIXME: Not sure what we should do with these *_marker fields.
Hopefully they're just nil anyway. */
swapfield_ (pt_marker, Lisp_Object);
XSETFASTINT (BVAR (&buffer_local_flags, category_table), idx); ++idx;
XSETFASTINT (BVAR (&buffer_local_flags, bidi_display_reordering), idx); ++idx;
XSETFASTINT (BVAR (&buffer_local_flags, bidi_paragraph_direction), idx); ++idx;
+ XSETFASTINT (BVAR (&buffer_local_flags, bidi_paragraph_separate_re), idx); ++idx;
+ XSETFASTINT (BVAR (&buffer_local_flags, bidi_paragraph_start_re), idx); ++idx;
XSETFASTINT (BVAR (&buffer_local_flags, buffer_file_coding_system), idx);
/* Make this one a permanent local. */
buffer_permanent_local_flags[idx++] = 1;
bset_ctl_arrow (&buffer_defaults, Qt);
bset_bidi_display_reordering (&buffer_defaults, Qt);
bset_bidi_paragraph_direction (&buffer_defaults, Qnil);
+ bset_bidi_paragraph_start_re (&buffer_defaults, Qnil);
+ bset_bidi_paragraph_separate_re (&buffer_defaults, Qnil);
bset_cursor_type (&buffer_defaults, Qt);
bset_extra_line_spacing (&buffer_defaults, Qnil);
bset_cursor_in_non_selected_windows (&buffer_defaults, Qt);
&BVAR (current_buffer, bidi_display_reordering), Qnil,
doc: /* Non-nil means reorder bidirectional text for display in the visual order. */);
+ DEFVAR_PER_BUFFER ("bidi-paragraph-start-re",
+ &BVAR (current_buffer, bidi_paragraph_start_re), Qnil,
+ doc: /* If non-nil, a regexp matching a line that starts OR separates paragraphs.
+
+The value of nil means to use empty lines as lines that start and
+separate paragraphs.
+
+When Emacs displays bidirectional text, it by default computes
+the base paragraph direction separately for each paragraph.
+Setting this variable changes the places where paragraph base
+direction is recomputed.
+
+The regexp is always matched after a newline, so it is best to
+anchor it by beginning it with a "^".
+
+If you change the value of this variable, be sure to change
+the value of `bidi-paragraph-separate-re' accordingly. For
+example, to have a single newline behave as a paragraph separator,
+set both these variables to "^".
+
+See also `bidi-paragraph-direction'. */);
+
+ DEFVAR_PER_BUFFER ("bidi-paragraph-separate-re",
+ &BVAR (current_buffer, bidi_paragraph_separate_re), Qnil,
+ doc: /* If non-nil, a regexp matching a line that separates paragraphs.
+
+The value of nil means to use empty lines as paragraph separators.
+
+When Emacs displays bidirectional text, it by default computes
+the base paragraph direction separately for each paragraph.
+Setting this variable changes the places where paragraph base
+direction is recomputed.
+
+The regexp is always matched after a newline, so it is best to
+anchor it by beginning it with a "^".
+
+If you change the value of this variable, be sure to change
+the value of `bidi-paragraph-start-re' accordingly. For
+example, to have a single newline behave as a paragraph separator,
+set both these variables to "^".
+
+See also `bidi-paragraph-direction'. */);
+
DEFVAR_PER_BUFFER ("bidi-paragraph-direction",
&BVAR (current_buffer, bidi_paragraph_direction), Qnil,
doc: /* If non-nil, forces directionality of text paragraphs in the buffer.
direction dynamically for each paragraph. */
Lisp_Object bidi_paragraph_direction_;
+ /* If non-nil, a regular expression for bidi paragraph separator. */
+ Lisp_Object bidi_paragraph_separate_re_;
+
+ /* If non-nil, a regular expression for bidi paragraph start. */
+ Lisp_Object bidi_paragraph_start_re_;
+
/* Non-nil means do selective display;
see doc string in syms_of_buffer (buffer.c) for details. */
Lisp_Object selective_display_;