/* Low-level bidirectional buffer-scanning functions for GNU Emacs.
- Copyright (C) 2000, 2001, 2004, 2005 Free Software Foundation, Inc.
+ Copyright (C) 2000-2001, 2004-2005, 2009-2011
+ Free Software Foundation, Inc.
This file is part of GNU Emacs.
-GNU Emacs is free software; you can redistribute it and/or modify
+GNU Emacs is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
-any later version.
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
GNU Emacs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
-
You should have received a copy of the GNU General Public License
-along with GNU Emacs; see the file COPYING. If not, write to
-the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-Boston, MA 02110-1301, USA. */
+along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
+
+/* Written by Eli Zaretskii <eliz@gnu.org>.
-/* A sequential implementation of the Unicode Bidirectional algorithm,
+ A sequential implementation of the Unicode Bidirectional algorithm,
as per UAX#9, a part of the Unicode Standard.
Unlike the reference and most other implementations, this one is
- designed to be called once for every character in the buffer.
+ designed to be called once for every character in the buffer or
+ string.
- The main entry point is bidi_get_next_char_visually. Each time it
+ The main entry point is bidi_move_to_visually_next. Each time it
is called, it finds the next character in the visual order, and
returns its information in a special structure. The caller is then
expected to process this character for display or any other
- purposes, and call bidi_get_next_char_visually for the next
- character. See the comments in bidi_get_next_char_visually for
- more details about its algorithm that finds the next visual-order
+ purposes, and call bidi_move_to_visually_next for the next
+ character. See the comments in bidi_move_to_visually_next for more
+ details about its algorithm that finds the next visual-order
character by resolving their levels on the fly.
+ The two other entry points are bidi_paragraph_init and
+ bidi_mirror_char. The first determines the base direction of a
+ paragraph, while the second returns the mirrored version of its
+ argument character.
+
+ If you want to understand the code, you will have to read it
+ together with the relevant portions of UAX#9. The comments include
+ references to UAX#9 rules, for that very reason.
+
A note about references to UAX#9 rules: if the reference says
something like "X9/Retaining", it means that you need to refer to
rule X9 and to its modifications decribed in the "Implementation
Notes" section of UAX#9, under "Retaining Format Codes". */
-#ifdef HAVE_CONFIG_H
#include <config.h>
-#endif
-
#include <stdio.h>
-
-#ifdef HAVE_STRING_H
-#include <string.h>
-#endif
+#include <setjmp.h>
#include "lisp.h"
#include "buffer.h"
static int bidi_initialized = 0;
-static Lisp_Object bidi_type_table;
+static Lisp_Object bidi_type_table, bidi_mirror_table;
+/* FIXME: Remove these when bidi_explicit_dir_char uses a lookup table. */
#define LRM_CHAR 0x200E
#define RLM_CHAR 0x200F
#define LRE_CHAR 0x202A
#define LRO_CHAR 0x202D
#define RLO_CHAR 0x202E
-#define CHARSET_HEBREW 0x88
-#define CHARSET_ARABIC 0x87
-#define CHARSET_SYRIAC -1 /* these are undefined yet, -1 is invalid */
-#define CHARSET_THAANA -1
-
-/* FIXME: need to define wrappers for FETCH_CHAR etc. that return
- BIDI_EOB when they hit ZV. */
#define BIDI_EOB -1
-#define BIDI_BOB -2
-
-#ifdef TEST_STANDALONE
-/* Testing. */
-
-static unsigned char *input_buf;
-static size_t input_buf_size;
-
-int _fetch_multibyte_char_len, _c_c_;
-
-#undef FETCH_CHAR_ADVANCE
-#define FETCH_CHAR_ADVANCE(ch, cp, bp) \
- do { \
- ch = input_buf[cp]; \
- (cp)++; \
- (bp)++; \
- if (ch == '\0') \
- ch = BIDI_EOB; \
- } while (0)
-
-#undef FETCH_CHAR
-#define FETCH_CHAR(n) ((_c_c_ = input_buf[n]) ? _c_c_ : BIDI_EOB)
-
-#undef CHAR_CHARSET
-#define CHAR_CHARSET(c) \
- (((c) >= 128 || ((c) < 8 && (c)) || ((c) >= 'A' && (c) < 'X')) \
- ? CHARSET_HEBREW \
- : ((((c) >= 'X' && (c) <= 'Z') || ((c) >= '6' && (c) <= '9')) \
- ? CHARSET_ARABIC \
- : CHARSET_ASCII))
-
-#undef CHAR_TO_BYTE
-#define CHAR_TO_BYTE(pos) (pos)
-
-#define char_bytes(ch) 1
-
-#undef LRE_CHAR
-#undef LRO_CHAR
-#undef RLE_CHAR
-#undef RLO_CHAR
-#undef PDF_CHAR
-#undef RLM_CHAR
-#undef LRM_CHAR
-
-#define LRE_CHAR 1
-#define LRO_CHAR 2
-#define RLE_CHAR 3
-#define RLO_CHAR 4
-#define PDF_CHAR 5
-#define RLM_CHAR 6
-#define LRM_CHAR 7
-
-static const char *bidi_name[] =
- {
- "[???]", "[LRE]", "[LRO]", "[RLE]", "[RLO]", "[PDF]", "[RLM]", "[LRM]"
- };
-
-#endif /* TEST_STANDALONE */
/* Local data structures. (Look in dispextern.h for the rest.) */
/* What we need to know about the current paragraph. */
struct bidi_paragraph_info {
- int start_bytepos; /* byte position where it begins */
- int end_bytepos; /* byte position where it ends */
- int embedding_level; /* its basic embedding level */
- bidi_dir_t base_dir; /* its base direction */
+ EMACS_INT start_bytepos; /* byte position where it begins */
+ EMACS_INT end_bytepos; /* byte position where it ends */
+ int embedding_level; /* its basic embedding level */
+ bidi_dir_t base_dir; /* its base direction */
};
/* Data type for describing the bidirectional character categories. */
STRONG
} bidi_category_t;
+extern int bidi_ignore_explicit_marks_for_paragraph_level EXTERNALLY_VISIBLE;
int bidi_ignore_explicit_marks_for_paragraph_level = 1;
-bidi_dir_t bidi_overriding_paragraph_direction = NEUTRAL_DIR;
-
-/* FIXME: Unused? */
-#define ASCII_BIDI_TYPE_SET(STR, TYPE) \
- do { \
- unsigned char *p; \
- for (p = (STR); *p; p++) \
- CHAR_TABLE_SET (bidi_type_table, *p, (TYPE)); \
- } while (0)
+static Lisp_Object paragraph_start_re, paragraph_separate_re;
+static Lisp_Object Qparagraph_start, Qparagraph_separate;
static void
-bidi_initialize ()
+bidi_initialize (void)
{
- /* FIXME: This should come from the Unicode Database. */
- struct {
- int from, to;
- bidi_type_t type;
- } bidi_type[] =
- { { 0x0000, 0x0008, WEAK_BN },
- { 0x0009, 0x0000, NEUTRAL_S },
- { 0x000A, 0x0000, NEUTRAL_B },
- { 0x000B, 0x0000, NEUTRAL_S },
- { 0x000C, 0x0000, NEUTRAL_WS },
- { 0x000D, 0x0000, NEUTRAL_B },
- { 0x000E, 0x001B, WEAK_BN },
- { 0x001C, 0x001E, NEUTRAL_B },
- { 0x001F, 0x0000, NEUTRAL_S },
- { 0x0020, 0x0000, NEUTRAL_WS },
- { 0x0021, 0x0022, NEUTRAL_ON },
- { 0x0023, 0x0025, WEAK_ET },
- { 0x0026, 0x002A, NEUTRAL_ON },
- { 0x002B, 0x0000, WEAK_ET },
- { 0x002C, 0x0000, WEAK_CS },
- { 0x002D, 0x0000, WEAK_ET },
- { 0x002E, 0x0000, WEAK_CS },
- { 0x002F, 0x0000, WEAK_ES },
- { 0x0030, 0x0039, WEAK_EN },
- { 0x003A, 0x0000, WEAK_CS },
- { 0x003B, 0x0040, NEUTRAL_ON },
- { 0x005B, 0x0060, NEUTRAL_ON },
- { 0x007B, 0x007E, NEUTRAL_ON },
- { 0x007F, 0x0084, WEAK_BN },
- { 0x0085, 0x0000, NEUTRAL_B },
- { 0x0086, 0x009F, WEAK_BN },
- { 0x00A0, 0x0000, WEAK_CS },
- { 0x00A1, 0x0000, NEUTRAL_ON },
- { 0x00A2, 0x00A5, WEAK_ET },
- { 0x00A6, 0x00A9, NEUTRAL_ON },
- { 0x00AB, 0x00AF, NEUTRAL_ON },
- { 0x00B0, 0x00B1, WEAK_ET },
- { 0x00B2, 0x00B3, WEAK_EN },
- { 0x00B4, 0x0000, NEUTRAL_ON },
- { 0x00B6, 0x00B8, NEUTRAL_ON },
- { 0x00B9, 0x0000, WEAK_EN },
- { 0x00BB, 0x00BF, NEUTRAL_ON },
- { 0x00D7, 0x0000, NEUTRAL_ON },
- { 0x00F7, 0x0000, NEUTRAL_ON },
- { 0x02B9, 0x02BA, NEUTRAL_ON },
- { 0x02C2, 0x02CF, NEUTRAL_ON },
- { 0x02D2, 0x02DF, NEUTRAL_ON },
- { 0x02E5, 0x02ED, NEUTRAL_ON },
- { 0x0300, 0x036F, WEAK_NSM },
- { 0x0374, 0x0375, NEUTRAL_ON },
- { 0x037E, 0x0385, NEUTRAL_ON },
- { 0x0387, 0x0000, NEUTRAL_ON },
- { 0x03F6, 0x0000, NEUTRAL_ON },
- { 0x0483, 0x0489, WEAK_NSM },
- { 0x058A, 0x0000, NEUTRAL_ON },
- { 0x0591, 0x05BD, WEAK_NSM },
- { 0x05BE, 0x0000, STRONG_R },
- { 0x05BF, 0x0000, WEAK_NSM },
- { 0x05C0, 0x0000, STRONG_R },
- { 0x05C1, 0x05C2, WEAK_NSM },
- { 0x05C3, 0x0000, STRONG_R },
- { 0x05C4, 0x0000, WEAK_NSM },
- { 0x05D0, 0x05F4, STRONG_R },
- { 0x060C, 0x0000, WEAK_CS },
- { 0x061B, 0x064A, STRONG_AL },
- { 0x064B, 0x0655, WEAK_NSM },
- { 0x0660, 0x0669, WEAK_AN },
- { 0x066A, 0x0000, WEAK_ET },
- { 0x066B, 0x066C, WEAK_AN },
- { 0x066D, 0x066F, STRONG_AL },
- { 0x0670, 0x0000, WEAK_NSM },
- { 0x0671, 0x06D5, STRONG_AL },
- { 0x06D6, 0x06DC, WEAK_NSM },
- { 0x06DD, 0x0000, STRONG_AL },
- { 0x06DE, 0x06E4, WEAK_NSM },
- { 0x06E5, 0x06E6, STRONG_AL },
- { 0x06E7, 0x06E8, WEAK_NSM },
- { 0x06E9, 0x0000, NEUTRAL_ON },
- { 0x06EA, 0x06ED, WEAK_NSM },
- { 0x06F0, 0x06F9, WEAK_EN },
- { 0x06FA, 0x070D, STRONG_AL },
- { 0x070F, 0x0000, WEAK_BN },
- { 0x0710, 0x0000, STRONG_AL },
- { 0x0711, 0x0000, WEAK_NSM },
- { 0x0712, 0x072C, STRONG_AL },
- { 0x0730, 0x074A, WEAK_NSM },
- { 0x0780, 0x07A5, STRONG_AL },
- { 0x07A6, 0x07B0, WEAK_NSM },
- { 0x07B1, 0x0000, STRONG_AL },
- { 0x0901, 0x0902, WEAK_NSM },
- { 0x093C, 0x0000, WEAK_NSM },
- { 0x0941, 0x0948, WEAK_NSM },
- { 0x094D, 0x0000, WEAK_NSM },
- { 0x0951, 0x0954, WEAK_NSM },
- { 0x0962, 0x0963, WEAK_NSM },
- { 0x0981, 0x0000, WEAK_NSM },
- { 0x09BC, 0x0000, WEAK_NSM },
- { 0x09C1, 0x09C4, WEAK_NSM },
- { 0x09CD, 0x0000, WEAK_NSM },
- { 0x09E2, 0x09E3, WEAK_NSM },
- { 0x09F2, 0x09F3, WEAK_ET },
- { 0x0A02, 0x0000, WEAK_NSM },
- { 0x0A3C, 0x0000, WEAK_NSM },
- { 0x0A41, 0x0A4D, WEAK_NSM },
- { 0x0A70, 0x0A71, WEAK_NSM },
- { 0x0A81, 0x0A82, WEAK_NSM },
- { 0x0ABC, 0x0000, WEAK_NSM },
- { 0x0AC1, 0x0AC8, WEAK_NSM },
- { 0x0ACD, 0x0000, WEAK_NSM },
- { 0x0B01, 0x0000, WEAK_NSM },
- { 0x0B3C, 0x0000, WEAK_NSM },
- { 0x0B3F, 0x0000, WEAK_NSM },
- { 0x0B41, 0x0B43, WEAK_NSM },
- { 0x0B4D, 0x0B56, WEAK_NSM },
- { 0x0B82, 0x0000, WEAK_NSM },
- { 0x0BC0, 0x0000, WEAK_NSM },
- { 0x0BCD, 0x0000, WEAK_NSM },
- { 0x0C3E, 0x0C40, WEAK_NSM },
- { 0x0C46, 0x0C56, WEAK_NSM },
- { 0x0CBF, 0x0000, WEAK_NSM },
- { 0x0CC6, 0x0000, WEAK_NSM },
- { 0x0CCC, 0x0CCD, WEAK_NSM },
- { 0x0D41, 0x0D43, WEAK_NSM },
- { 0x0D4D, 0x0000, WEAK_NSM },
- { 0x0DCA, 0x0000, WEAK_NSM },
- { 0x0DD2, 0x0DD6, WEAK_NSM },
- { 0x0E31, 0x0000, WEAK_NSM },
- { 0x0E34, 0x0E3A, WEAK_NSM },
- { 0x0E3F, 0x0000, WEAK_ET },
- { 0x0E47, 0x0E4E, WEAK_NSM },
- { 0x0EB1, 0x0000, WEAK_NSM },
- { 0x0EB4, 0x0EBC, WEAK_NSM },
- { 0x0EC8, 0x0ECD, WEAK_NSM },
- { 0x0F18, 0x0F19, WEAK_NSM },
- { 0x0F35, 0x0000, WEAK_NSM },
- { 0x0F37, 0x0000, WEAK_NSM },
- { 0x0F39, 0x0000, WEAK_NSM },
- { 0x0F3A, 0x0F3D, NEUTRAL_ON },
- { 0x0F71, 0x0F7E, WEAK_NSM },
- { 0x0F80, 0x0F84, WEAK_NSM },
- { 0x0F86, 0x0F87, WEAK_NSM },
- { 0x0F90, 0x0FBC, WEAK_NSM },
- { 0x0FC6, 0x0000, WEAK_NSM },
- { 0x102D, 0x1030, WEAK_NSM },
- { 0x1032, 0x1037, WEAK_NSM },
- { 0x1039, 0x0000, WEAK_NSM },
- { 0x1058, 0x1059, WEAK_NSM },
- { 0x1680, 0x0000, NEUTRAL_WS },
- { 0x169B, 0x169C, NEUTRAL_ON },
- { 0x1712, 0x1714, WEAK_NSM },
- { 0x1732, 0x1734, WEAK_NSM },
- { 0x1752, 0x1753, WEAK_NSM },
- { 0x1772, 0x1773, WEAK_NSM },
- { 0x17B7, 0x17BD, WEAK_NSM },
- { 0x17C6, 0x0000, WEAK_NSM },
- { 0x17C9, 0x17D3, WEAK_NSM },
- { 0x17DB, 0x0000, WEAK_ET },
- { 0x1800, 0x180A, NEUTRAL_ON },
- { 0x180B, 0x180D, WEAK_NSM },
- { 0x180E, 0x0000, WEAK_BN },
- { 0x18A9, 0x0000, WEAK_NSM },
- { 0x1FBD, 0x0000, NEUTRAL_ON },
- { 0x1FBF, 0x1FC1, NEUTRAL_ON },
- { 0x1FCD, 0x1FCF, NEUTRAL_ON },
- { 0x1FDD, 0x1FDF, NEUTRAL_ON },
- { 0x1FED, 0x1FEF, NEUTRAL_ON },
- { 0x1FFD, 0x1FFE, NEUTRAL_ON },
- { 0x2000, 0x200A, NEUTRAL_WS },
- { 0x200B, 0x200D, WEAK_BN },
- { 0x200F, 0x0000, STRONG_R },
- { 0x2010, 0x2027, NEUTRAL_ON },
- { 0x2028, 0x0000, NEUTRAL_WS },
- { 0x2029, 0x0000, NEUTRAL_B },
- { 0x202A, 0x0000, LRE },
- { 0x202B, 0x0000, RLE },
- { 0x202C, 0x0000, PDF },
- { 0x202D, 0x0000, LRO },
- { 0x202E, 0x0000, RLO },
- { 0x202F, 0x0000, NEUTRAL_WS },
- { 0x2030, 0x2034, WEAK_ET },
- { 0x2035, 0x2057, NEUTRAL_ON },
- { 0x205F, 0x0000, NEUTRAL_WS },
- { 0x2060, 0x206F, WEAK_BN },
- { 0x2070, 0x0000, WEAK_EN },
- { 0x2074, 0x2079, WEAK_EN },
- { 0x207A, 0x207B, WEAK_ET },
- { 0x207C, 0x207E, NEUTRAL_ON },
- { 0x2080, 0x2089, WEAK_EN },
- { 0x208A, 0x208B, WEAK_ET },
- { 0x208C, 0x208E, NEUTRAL_ON },
- { 0x20A0, 0x20B1, WEAK_ET },
- { 0x20D0, 0x20EA, WEAK_NSM },
- { 0x2100, 0x2101, NEUTRAL_ON },
- { 0x2103, 0x2106, NEUTRAL_ON },
- { 0x2108, 0x2109, NEUTRAL_ON },
- { 0x2114, 0x0000, NEUTRAL_ON },
- { 0x2116, 0x2118, NEUTRAL_ON },
- { 0x211E, 0x2123, NEUTRAL_ON },
- { 0x2125, 0x0000, NEUTRAL_ON },
- { 0x2127, 0x0000, NEUTRAL_ON },
- { 0x2129, 0x0000, NEUTRAL_ON },
- { 0x212E, 0x0000, WEAK_ET },
- { 0x2132, 0x0000, NEUTRAL_ON },
- { 0x213A, 0x0000, NEUTRAL_ON },
- { 0x2140, 0x2144, NEUTRAL_ON },
- { 0x214A, 0x215F, NEUTRAL_ON },
- { 0x2190, 0x2211, NEUTRAL_ON },
- { 0x2212, 0x2213, WEAK_ET },
- { 0x2214, 0x2335, NEUTRAL_ON },
- { 0x237B, 0x2394, NEUTRAL_ON },
- { 0x2396, 0x244A, NEUTRAL_ON },
- { 0x2460, 0x249B, WEAK_EN },
- { 0x24EA, 0x0000, WEAK_EN },
- { 0x24EB, 0x2FFB, NEUTRAL_ON },
- { 0x3000, 0x0000, NEUTRAL_WS },
- { 0x3001, 0x3004, NEUTRAL_ON },
- { 0x3008, 0x3020, NEUTRAL_ON },
- { 0x302A, 0x302F, WEAK_NSM },
- { 0x3030, 0x0000, NEUTRAL_ON },
- { 0x3036, 0x3037, NEUTRAL_ON },
- { 0x303D, 0x303F, NEUTRAL_ON },
- { 0x3099, 0x309A, WEAK_NSM },
- { 0x309B, 0x309C, NEUTRAL_ON },
- { 0x30A0, 0x0000, NEUTRAL_ON },
- { 0x30FB, 0x0000, NEUTRAL_ON },
- { 0x3251, 0x325F, NEUTRAL_ON },
- { 0x32B1, 0x32BF, NEUTRAL_ON },
- { 0xA490, 0xA4C6, NEUTRAL_ON },
- { 0xFB1D, 0x0000, STRONG_R },
- { 0xFB1E, 0x0000, WEAK_NSM },
- { 0xFB1F, 0xFB28, STRONG_R },
- { 0xFB29, 0x0000, WEAK_ET },
- { 0xFB2A, 0xFB4F, STRONG_R },
- { 0xFB50, 0xFD3D, STRONG_AL },
- { 0xFD3E, 0xFD3F, NEUTRAL_ON },
- { 0xFD50, 0xFDFC, STRONG_AL },
- { 0xFE00, 0xFE23, WEAK_NSM },
- { 0xFE30, 0xFE4F, NEUTRAL_ON },
- { 0xFE50, 0x0000, WEAK_CS },
- { 0xFE51, 0x0000, NEUTRAL_ON },
- { 0xFE52, 0x0000, WEAK_CS },
- { 0xFE54, 0x0000, NEUTRAL_ON },
- { 0xFE55, 0x0000, WEAK_CS },
- { 0xFE56, 0xFE5E, NEUTRAL_ON },
- { 0xFE5F, 0x0000, WEAK_ET },
- { 0xFE60, 0xFE61, NEUTRAL_ON },
- { 0xFE62, 0xFE63, WEAK_ET },
- { 0xFE64, 0xFE68, NEUTRAL_ON },
- { 0xFE69, 0xFE6A, WEAK_ET },
- { 0xFE6B, 0x0000, NEUTRAL_ON },
- { 0xFE70, 0xFEFC, STRONG_AL },
- { 0xFEFF, 0x0000, WEAK_BN },
- { 0xFF01, 0xFF02, NEUTRAL_ON },
- { 0xFF03, 0xFF05, WEAK_ET },
- { 0xFF06, 0xFF0A, NEUTRAL_ON },
- { 0xFF0B, 0x0000, WEAK_ET },
- { 0xFF0C, 0x0000, WEAK_CS },
- { 0xFF0D, 0x0000, WEAK_ET },
- { 0xFF0E, 0x0000, WEAK_CS },
- { 0xFF0F, 0x0000, WEAK_ES },
- { 0xFF10, 0xFF19, WEAK_EN },
- { 0xFF1A, 0x0000, WEAK_CS },
- { 0xFF1B, 0xFF20, NEUTRAL_ON },
- { 0xFF3B, 0xFF40, NEUTRAL_ON },
- { 0xFF5B, 0xFF65, NEUTRAL_ON },
- { 0xFFE0, 0xFFE1, WEAK_ET },
- { 0xFFE2, 0xFFE4, NEUTRAL_ON },
- { 0xFFE5, 0xFFE6, WEAK_ET },
- { 0xFFE8, 0xFFEE, NEUTRAL_ON },
- { 0xFFF9, 0xFFFB, WEAK_BN },
- { 0xFFFC, 0xFFFD, NEUTRAL_ON },
- { 0x1D167, 0x1D169, WEAK_NSM },
- { 0x1D173, 0x1D17A, WEAK_BN },
- { 0x1D17B, 0x1D182, WEAK_NSM },
- { 0x1D185, 0x1D18B, WEAK_NSM },
- { 0x1D1AA, 0x1D1AD, WEAK_NSM },
- { 0x1D7CE, 0x1D7FF, WEAK_EN },
- { 0xE0001, 0xE007F, WEAK_BN } };
+
+#include "biditype.h"
+#include "bidimirror.h"
+
int i;
bidi_type_table = Fmake_char_table (Qnil, make_number (STRONG_L));
+ staticpro (&bidi_type_table);
for (i = 0; i < sizeof bidi_type / sizeof bidi_type[0]; i++)
- char_table_set_range (bidi_type_table, bidi_type[i].from,
- bidi_type[i].to ? bidi_type[i].to : bidi_type[i].from,
+ char_table_set_range (bidi_type_table, bidi_type[i].from, bidi_type[i].to,
make_number (bidi_type[i].type));
+
+ bidi_mirror_table = Fmake_char_table (Qnil, Qnil);
+ staticpro (&bidi_mirror_table);
+
+ for (i = 0; i < sizeof bidi_mirror / sizeof bidi_mirror[0]; i++)
+ char_table_set (bidi_mirror_table, bidi_mirror[i].from,
+ make_number (bidi_mirror[i].to));
+
+ Qparagraph_start = intern ("paragraph-start");
+ staticpro (&Qparagraph_start);
+ paragraph_start_re = Fsymbol_value (Qparagraph_start);
+ if (!STRINGP (paragraph_start_re))
+ paragraph_start_re = build_string ("\f\\|[ \t]*$");
+ staticpro (¶graph_start_re);
+ Qparagraph_separate = intern ("paragraph-separate");
+ staticpro (&Qparagraph_separate);
+ paragraph_separate_re = Fsymbol_value (Qparagraph_separate);
+ if (!STRINGP (paragraph_separate_re))
+ paragraph_separate_re = build_string ("[ \t\f]*$");
+ staticpro (¶graph_separate_re);
bidi_initialized = 1;
}
-static int
-bidi_is_arabic_number (int ch)
+/* Return the bidi type of a character CH, subject to the current
+ directional OVERRIDE. */
+static INLINE bidi_type_t
+bidi_get_type (int ch, bidi_dir_t override)
{
-#ifdef TEST_STANDALONE
- return ch >= '6' && ch <= '9';
-#else
- return 0; /* FIXME! */
-#endif
+ bidi_type_t default_type;
+
+ if (ch == BIDI_EOB)
+ return NEUTRAL_B;
+ if (ch < 0 || ch > MAX_CHAR)
+ abort ();
+
+ default_type = (bidi_type_t) XINT (CHAR_TABLE_REF (bidi_type_table, ch));
+
+ if (override == NEUTRAL_DIR)
+ return default_type;
+
+ switch (default_type)
+ {
+ /* Although UAX#9 does not tell, it doesn't make sense to
+ override NEUTRAL_B and LRM/RLM characters. */
+ case NEUTRAL_B:
+ case LRE:
+ case LRO:
+ case RLE:
+ case RLO:
+ case PDF:
+ return default_type;
+ default:
+ switch (ch)
+ {
+ case LRM_CHAR:
+ case RLM_CHAR:
+ return default_type;
+ default:
+ if (override == L2R) /* X6 */
+ return STRONG_L;
+ else if (override == R2L)
+ return STRONG_R;
+ else
+ abort (); /* can't happen: handled above */
+ }
+ }
}
-/* Return the bidi type of a character CH. */
-bidi_type_t
-bidi_get_type (int ch)
+static void
+bidi_check_type (bidi_type_t type)
{
- return (bidi_type_t) XINT (CHAR_TABLE_REF (bidi_type_table, ch));
+ if (type < UNKNOWN_BT || type > NEUTRAL_ON)
+ abort ();
}
/* Given a bidi TYPE of a character, return its category. */
-bidi_category_t
+static INLINE bidi_category_t
bidi_get_category (bidi_type_t type)
{
switch (type)
}
}
-/* FIXME: exceedingly temporary! Should consult the Unicode database
- of character properties. */
+/* Return the mirrored character of C, if it has one. If C has no
+ mirrored counterpart, return C.
+ Note: The conditions in UAX#9 clause L4 regarding the surrounding
+ context must be tested by the caller. */
int
bidi_mirror_char (int c)
{
- static const char mirrored_pairs[] = "()<>[]{}";
- const char *p = strchr (mirrored_pairs, c);
+ Lisp_Object val;
+
+ if (c == BIDI_EOB)
+ return c;
+ if (c < 0 || c > MAX_CHAR)
+ abort ();
- if (p)
+ val = CHAR_TABLE_REF (bidi_mirror_table, c);
+ if (INTEGERP (val))
{
- size_t i = p - mirrored_pairs;
+ int v = XINT (val);
- if ((i & 1) == 0)
- return mirrored_pairs[i + 1];
- else
- return mirrored_pairs[i - 1];
+ if (v < 0 || v > MAX_CHAR)
+ abort ();
+
+ return v;
}
+
return c;
}
/* Copy the bidi iterator from FROM to TO. To save cycles, this only
copies the part of the level stack that is actually in use. */
-static inline void
+static INLINE void
bidi_copy_it (struct bidi_it *to, struct bidi_it *from)
{
int i;
- /* Copy everything except the level stack. */
- memcpy (to, from, ((int)&((struct bidi_it *)0)->level_stack[0]));
+ /* Copy everything except the level stack and beyond. */
+ memcpy (to, from, ((size_t)&((struct bidi_it *)0)->level_stack[0]));
/* Copy the active part of the level stack. */
to->level_stack[0] = from->level_stack[0]; /* level zero is always in use */
/* Caching the bidi iterator states. */
-static struct bidi_it bidi_cache[1000]; /* FIXME: make this dynamically allocated! */
-static int bidi_cache_idx;
-static int bidi_cache_last_idx;
+#define BIDI_CACHE_CHUNK 200
+static struct bidi_it *bidi_cache;
+static size_t bidi_cache_size = 0;
+static size_t elsz = sizeof (struct bidi_it);
+static int bidi_cache_idx; /* next unused cache slot */
+static int bidi_cache_last_idx; /* slot of last cache hit */
-static inline void
+static INLINE void
bidi_cache_reset (void)
{
bidi_cache_idx = 0;
bidi_cache_last_idx = -1;
}
-static inline void
+static INLINE void
+bidi_cache_shrink (void)
+{
+ if (bidi_cache_size > BIDI_CACHE_CHUNK)
+ {
+ bidi_cache_size = BIDI_CACHE_CHUNK;
+ bidi_cache =
+ (struct bidi_it *) xrealloc (bidi_cache, bidi_cache_size * elsz);
+ }
+ bidi_cache_reset ();
+}
+
+static INLINE void
bidi_cache_fetch_state (int idx, struct bidi_it *bidi_it)
{
int current_scan_dir = bidi_it->scan_dir;
level less or equal to LEVEL. if LEVEL is -1, disregard the
resolved levels in cached states. DIR, if non-zero, means search
in that direction from the last cache hit. */
-static inline int
-bidi_cache_search (int charpos, int level, int dir)
+static INLINE int
+bidi_cache_search (EMACS_INT charpos, int level, int dir)
{
int i, i_start;
return -1;
}
-static inline void
+static INLINE void
bidi_cache_iterator_state (struct bidi_it *bidi_it, int resolved)
{
int idx;
if (idx < 0)
{
idx = bidi_cache_idx;
- if (idx > sizeof (bidi_cache) / sizeof (bidi_cache[0]) - 1)
- abort ();
+ /* Enlarge the cache as needed. */
+ if (idx >= bidi_cache_size)
+ {
+ bidi_cache_size += BIDI_CACHE_CHUNK;
+ bidi_cache =
+ (struct bidi_it *) xrealloc (bidi_cache, bidi_cache_size * elsz);
+ }
+ /* Character positions should correspond to cache positions 1:1.
+ If we are outside the range of cached positions, the cache is
+ useless and must be reset. */
+ if (idx > 0 &&
+ (bidi_it->charpos > bidi_cache[idx - 1].charpos + 1
+ || bidi_it->charpos < bidi_cache[0].charpos))
+ {
+ bidi_cache_reset ();
+ idx = 0;
+ }
bidi_copy_it (&bidi_cache[idx], bidi_it);
if (!resolved)
bidi_cache[idx].resolved_level = -1;
/* Copy only the members which could have changed, to avoid
costly copying of the entire struct. */
bidi_cache[idx].type = bidi_it->type;
- bidi_cache[idx].orig_type = bidi_it->orig_type;
+ bidi_check_type (bidi_it->type);
+ bidi_cache[idx].type_after_w1 = bidi_it->type_after_w1;
+ bidi_check_type (bidi_it->type_after_w1);
if (resolved)
bidi_cache[idx].resolved_level = bidi_it->resolved_level;
else
bidi_cache_idx = idx + 1;
}
-static inline bidi_type_t
-bidi_cache_find (int charpos, int level, struct bidi_it *bidi_it)
+static INLINE bidi_type_t
+bidi_cache_find (EMACS_INT charpos, int level, struct bidi_it *bidi_it)
{
int i = bidi_cache_search (charpos, level, bidi_it->scan_dir);
{
bidi_dir_t current_scan_dir = bidi_it->scan_dir;
- *bidi_it = bidi_cache[i];
+ bidi_copy_it (bidi_it, &bidi_cache[i]);
bidi_cache_last_idx = i;
/* Don't let scan direction from from the cached state override
the current scan direction. */
return UNKNOWN_BT;
}
-static inline int
+static INLINE int
bidi_peek_at_next_level (struct bidi_it *bidi_it)
{
if (bidi_cache_idx == 0 || bidi_cache_last_idx == -1)
return bidi_cache[bidi_cache_last_idx + bidi_it->scan_dir].resolved_level;
}
-/* Return non-zero if buffer's byte position POS is the last character
- of a paragraph. THIS_CH is the character preceding the one at POS in
- the buffer. */
-int
-bidi_at_paragraph_end (int this_ch, int pos)
+/* Check if buffer position CHARPOS/BYTEPOS is the end of a paragraph.
+ Value is the non-negative length of the paragraph separator
+ following the buffer position, -1 if position is at the beginning
+ of a new paragraph, or -2 if position is neither at beginning nor
+ at end of a paragraph. */
+static EMACS_INT
+bidi_at_paragraph_end (EMACS_INT charpos, EMACS_INT bytepos)
{
- int next_ch = FETCH_CHAR (pos);
+ Lisp_Object sep_re;
+ Lisp_Object start_re;
+ EMACS_INT val;
- /* FIXME: This should support all Unicode characters that can end a
- paragraph. */
- return (this_ch == '\n' && next_ch == '\n') || this_ch == BIDI_EOB;
+ sep_re = paragraph_separate_re;
+ start_re = paragraph_start_re;
+
+ val = fast_looking_at (sep_re, charpos, bytepos, ZV, ZV_BYTE, Qnil);
+ if (val < 0)
+ {
+ if (fast_looking_at (start_re, charpos, bytepos, ZV, ZV_BYTE, Qnil) >= 0)
+ val = -1;
+ else
+ val = -2;
+ }
+
+ return val;
}
/* Determine the start-of-run (sor) directional type given the two
embedding levels on either side of the run boundary. Also, update
the saved info about previously seen characters, since that info is
generally valid for a single level run. */
-static inline void
+static INLINE void
bidi_set_sor_type (struct bidi_it *bidi_it, int level_before, int level_after)
{
int higher_level = level_before > level_after ? level_before : level_after;
that we find on the two sides of the level boundary (see UAX#9,
clause X10), and so we don't need to know the final embedding
level to which we descend after processing all the PDFs. */
- if (level_before < level_after || !bidi_it->prev_was_pdf)
+ if (!bidi_it->prev_was_pdf || level_before < level_after)
/* FIXME: should the default sor direction be user selectable? */
bidi_it->sor = (higher_level & 1) != 0 ? R2L : L2R;
if (level_before > level_after)
bidi_it->prev_was_pdf = 1;
bidi_it->prev.type = UNKNOWN_BT;
- bidi_it->last_strong.type = bidi_it->last_strong.orig_type =
- bidi_it->last_strong.pristine_type = UNKNOWN_BT;
+ bidi_it->last_strong.type = bidi_it->last_strong.type_after_w1 =
+ bidi_it->last_strong.orig_type = UNKNOWN_BT;
bidi_it->prev_for_neutral.type = bidi_it->sor == R2L ? STRONG_R : STRONG_L;
bidi_it->prev_for_neutral.charpos = bidi_it->charpos;
bidi_it->prev_for_neutral.bytepos = bidi_it->bytepos;
- bidi_it->next_for_neutral.type = bidi_it->next_for_neutral.orig_type =
- bidi_it->next_for_neutral.pristine_type = UNKNOWN_BT;
+ bidi_it->next_for_neutral.type = bidi_it->next_for_neutral.type_after_w1 =
+ bidi_it->next_for_neutral.orig_type = UNKNOWN_BT;
bidi_it->ignore_bn_limit = 0; /* meaning it's unknown */
}
+static void
+bidi_line_init (struct bidi_it *bidi_it)
+{
+ bidi_it->scan_dir = 1; /* FIXME: do we need to have control on this? */
+ bidi_it->resolved_level = bidi_it->level_stack[0].level;
+ 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;
+ bidi_it->next_for_ws.type = UNKNOWN_BT;
+ bidi_set_sor_type (bidi_it,
+ bidi_it->paragraph_dir == R2L ? 1 : 0,
+ bidi_it->level_stack[0].level); /* X10 */
+
+ bidi_cache_reset ();
+}
+
+/* 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 (EMACS_INT pos, EMACS_INT pos_byte)
+{
+ Lisp_Object re = paragraph_start_re;
+ EMACS_INT limit = ZV, limit_byte = ZV_BYTE;
+
+ while (pos_byte > BEGV_BYTE
+ && fast_looking_at (re, pos, pos_byte, limit, limit_byte, Qnil) < 0)
+ {
+ pos = find_next_newline_no_quit (pos - 1, -1);
+ pos_byte = CHAR_TO_BYTE (pos);
+ }
+ return pos_byte;
+}
+
+/* Determine the base direction, a.k.a. base embedding level, of the
+ 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 directional character of the paragraph.
+
+ NO_DEFAULT_P non-nil means don't default to L2R if the paragraph
+ has no strong directional characters and both DIR and
+ bidi_it->paragraph_dir are NEUTRAL_DIR. In that case, search back
+ in the buffer until a paragraph is found with a strong character,
+ or until hitting BEGV. In the latter case, fall back to L2R. This
+ flag is used in current-bidi-paragraph-direction.
+
+ Note that this function 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)
+bidi_paragraph_init (bidi_dir_t dir, struct bidi_it *bidi_it, int no_default_p)
{
- bidi_it->level_stack[0].level = 0;
- if (dir == R2L)
- bidi_it->level_stack[0].level = 1;
+ EMACS_INT bytepos = bidi_it->bytepos;
+ EMACS_INT pstartbyte;
+
+ /* Special case for an empty buffer. */
+ if (bytepos == BEGV_BYTE && bytepos == ZV_BYTE)
+ dir = L2R;
+ /* We should never be called at EOB or before BEGV. */
+ else if (bytepos >= ZV_BYTE || bytepos < BEGV_BYTE)
+ abort ();
+
+ if (dir == L2R)
+ {
+ bidi_it->paragraph_dir = L2R;
+ bidi_it->new_paragraph = 0;
+ }
+ else if (dir == R2L)
+ {
+ bidi_it->paragraph_dir = R2L;
+ bidi_it->new_paragraph = 0;
+ }
else if (dir == NEUTRAL_DIR) /* P2 */
{
+ int ch, ch_len;
+ EMACS_INT pos;
bidi_type_t type;
- int pos = bidi_it->charpos, bytepos = bidi_it->bytepos;
- int ch;
- if (pos < 0)
- pos = bytepos = 0;
- else if (bidi_it->ch != BIDI_EOB)
+ if (!bidi_initialized)
+ bidi_initialize ();
+
+ /* If we are inside a paragraph separator, we are just waiting
+ for the separator to be exhausted; use the previous paragraph
+ direction. But don't do that if we have been just reseated,
+ because we need to reinitialize below in that case. */
+ if (!bidi_it->first_elt
+ && bidi_it->charpos < bidi_it->separator_limit)
+ return;
+
+ /* If we are on a newline, get past it to where the next
+ paragraph might start. But don't do that at BEGV since then
+ we are potentially in a new paragraph that doesn't yet
+ exist. */
+ pos = bidi_it->charpos;
+ if (bytepos > BEGV_BYTE && FETCH_CHAR (bytepos) == '\n')
{
+ bytepos++;
pos++;
- bytepos += bidi_it->ch_len;
}
- ch = FETCH_CHAR (bytepos);
- pos++;
- bytepos += CHAR_BYTES (ch);
-
- /* FIXME: should actually go to where the paragraph begins and
- start the loop below from there, since UAX#9 says to find the
- first strong directional character in the paragraph. */
-
- for (type = bidi_get_type (ch);
- /* NOTE: UAX#9 says to search only for L, AL, or R types of
- characters, and ignore RLE, RLO, LRE, and LRO. However,
- I'm not sure it makes sense to omit those 4; should try
- with and without that to see the effect. */
- (bidi_get_category (type) != STRONG)
- || (bidi_ignore_explicit_marks_for_paragraph_level
- && (type == RLE || type == RLO
- || type == LRE || type == LRO));
- type = bidi_get_type (ch))
- {
- if (type == NEUTRAL_B || bidi_at_paragraph_end (ch, bytepos))
- break;
- FETCH_CHAR_ADVANCE (ch, pos, bytepos);
- }
- if (type == STRONG_R || type == STRONG_AL) /* P3 */
- bidi_it->level_stack[0].level = 1;
+ /* We are either at the beginning of a paragraph or in the
+ middle of it. Find where this paragraph starts. */
+ pstartbyte = bidi_find_paragraph_start (pos, bytepos);
+ bidi_it->separator_limit = -1;
+ bidi_it->new_paragraph = 0;
+
+ /* The following loop is run more than once only if NO_DEFAULT_P
+ is non-zero. */
+ do {
+ bytepos = pstartbyte;
+ ch = FETCH_CHAR (bytepos);
+ ch_len = CHAR_BYTES (ch);
+ pos = BYTE_TO_CHAR (bytepos);
+ type = bidi_get_type (ch, NEUTRAL_DIR);
+
+ for (pos++, bytepos += ch_len;
+ /* NOTE: UAX#9 says to search only for L, AL, or R types
+ of characters, and ignore RLE, RLO, LRE, and LRO.
+ However, I'm not sure it makes sense to omit those 4;
+ should try with and without that to see the effect. */
+ (bidi_get_category (type) != STRONG)
+ || (bidi_ignore_explicit_marks_for_paragraph_level
+ && (type == RLE || type == RLO
+ || type == LRE || type == LRO));
+ type = bidi_get_type (ch, NEUTRAL_DIR))
+ {
+ if (type == NEUTRAL_B && bidi_at_paragraph_end (pos, bytepos) >= -1)
+ break;
+ if (bytepos >= ZV_BYTE)
+ {
+ /* Pretend there's a paragraph separator at end of
+ buffer. */
+ type = NEUTRAL_B;
+ break;
+ }
+ FETCH_CHAR_ADVANCE (ch, pos, bytepos);
+ }
+ if (type == STRONG_R || type == STRONG_AL) /* P3 */
+ bidi_it->paragraph_dir = R2L;
+ else if (type == STRONG_L)
+ bidi_it->paragraph_dir = L2R;
+ if (no_default_p && bidi_it->paragraph_dir == NEUTRAL_DIR)
+ {
+ /* If this paragraph is at BEGV, default to L2R. */
+ if (pstartbyte == BEGV_BYTE)
+ bidi_it->paragraph_dir = L2R; /* P3 and HL1 */
+ else
+ {
+ EMACS_INT prevpbyte = pstartbyte;
+ EMACS_INT p = BYTE_TO_CHAR (pstartbyte), pbyte = pstartbyte;
+
+ /* Find the beginning of the previous paragraph, if any. */
+ while (pbyte > BEGV_BYTE && prevpbyte >= pstartbyte)
+ {
+ p--;
+ pbyte = CHAR_TO_BYTE (p);
+ prevpbyte = bidi_find_paragraph_start (p, pbyte);
+ }
+ pstartbyte = prevpbyte;
+ }
+ }
+ } while (no_default_p && bidi_it->paragraph_dir == NEUTRAL_DIR);
}
- bidi_it->scan_dir = 1; /* FIXME: do we need to have control on this? */
- bidi_it->resolved_level = bidi_it->level_stack[0].level;
- bidi_it->level_stack[0].override = NEUTRAL_DIR; /* X1 */
- bidi_it->invalid_levels = 0;
- bidi_it->invalid_rl_levels = -1;
- bidi_it->new_paragraph = 0;
- bidi_it->next_en_pos = -1;
- bidi_it->next_for_ws.type = UNKNOWN_BT;
- bidi_set_sor_type (bidi_it, bidi_it->level_stack[0].level, 0); /* X10 */
+ else
+ abort ();
- bidi_cache_reset ();
+ /* Contrary to UAX#9 clause P3, we only default the paragraph
+ direction to L2R if we have no previous usable paragraph
+ direction. This is allowed by the HL1 clause. */
+ if (bidi_it->paragraph_dir != L2R && bidi_it->paragraph_dir != R2L)
+ bidi_it->paragraph_dir = L2R; /* P3 and HL1 ``higher-level protocols'' */
+ if (bidi_it->paragraph_dir == R2L)
+ bidi_it->level_stack[0].level = 1;
+ else
+ bidi_it->level_stack[0].level = 0;
+
+ bidi_line_init (bidi_it);
}
-/* Do whatever UAX#9 clause X8 says should be done at paragraph's end,
- and set the new paragraph flag in the iterator. */
-static inline void
+/* Do whatever UAX#9 clause X8 says should be done at paragraph's
+ end. */
+static INLINE void
bidi_set_paragraph_end (struct bidi_it *bidi_it)
{
bidi_it->invalid_levels = 0;
bidi_it->invalid_rl_levels = -1;
bidi_it->stack_idx = 0;
bidi_it->resolved_level = bidi_it->level_stack[0].level;
- bidi_it->new_paragraph = 1;
}
-/* Initialize the bidi iterator from buffer position POS for paragraph
- direction DIR. Return the embedding level at POS. */
+/* Initialize the bidi iterator from buffer position CHARPOS. */
void
-bidi_init_it (int pos, bidi_dir_t dir, struct bidi_it *bidi_it)
+bidi_init_it (EMACS_INT charpos, EMACS_INT bytepos, struct bidi_it *bidi_it)
{
if (! bidi_initialized)
bidi_initialize ();
+ bidi_it->charpos = charpos;
+ bidi_it->bytepos = bytepos;
+ bidi_it->first_elt = 1;
bidi_set_paragraph_end (bidi_it);
- bidi_it->charpos = pos;
- if (pos <= 0)
- {
- bidi_it->bytepos = bidi_it->charpos;
- bidi_it->ch_len = 1; /* so that incrementing bytepos works */
- }
- else
- {
- bidi_it->bytepos = CHAR_TO_BYTE (pos);
- bidi_it->ch_len
- = MULTIBYTE_FORM_LENGTH (BYTE_POS_ADDR (bidi_it->bytepos),
- MAX_MULTIBYTE_LENGTH);
- }
- bidi_it->ch = '\x1d'; /* FIXME: should be U+2029 */
+ bidi_it->new_paragraph = 1;
+ bidi_it->separator_limit = -1;
bidi_it->type = NEUTRAL_B;
- bidi_it->orig_type = UNKNOWN_BT;
- bidi_it->pristine_type = UNKNOWN_BT;
+ bidi_it->type_after_w1 = NEUTRAL_B;
+ bidi_it->orig_type = NEUTRAL_B;
bidi_it->prev_was_pdf = 0;
- bidi_it->prev.type = bidi_it->prev.orig_type = UNKNOWN_BT;
- bidi_it->last_strong.type = bidi_it->last_strong.orig_type =
- bidi_it->last_strong.pristine_type = UNKNOWN_BT;
+ bidi_it->prev.type = bidi_it->prev.type_after_w1 =
+ bidi_it->prev.orig_type = UNKNOWN_BT;
+ bidi_it->last_strong.type = bidi_it->last_strong.type_after_w1 =
+ bidi_it->last_strong.orig_type = UNKNOWN_BT;
bidi_it->next_for_neutral.charpos = -1;
bidi_it->next_for_neutral.type =
- bidi_it->next_for_neutral.orig_type =
- bidi_it->next_for_neutral.pristine_type = UNKNOWN_BT;
+ bidi_it->next_for_neutral.type_after_w1 =
+ bidi_it->next_for_neutral.orig_type = UNKNOWN_BT;
bidi_it->prev_for_neutral.charpos = -1;
bidi_it->prev_for_neutral.type =
- bidi_it->prev_for_neutral.orig_type =
- bidi_it->prev_for_neutral.pristine_type = UNKNOWN_BT;
+ bidi_it->prev_for_neutral.type_after_w1 =
+ bidi_it->prev_for_neutral.orig_type = UNKNOWN_BT;
bidi_it->sor = L2R; /* FIXME: should it be user-selectable? */
- bidi_paragraph_init (dir, bidi_it);
+ bidi_cache_shrink ();
}
/* Push the current embedding level and override status; reset the
current level to LEVEL and the current override status to OVERRIDE. */
-static inline void
+static INLINE void
bidi_push_embedding_level (struct bidi_it *bidi_it,
int level, bidi_dir_t override)
{
/* Pop the embedding level and directional override status from the
stack, and return the new level. */
-static inline int
+static INLINE int
bidi_pop_embedding_level (struct bidi_it *bidi_it)
{
/* UAX#9 says to ignore invalid PDFs. */
}
/* Record in SAVED_INFO the information about the current character. */
-static inline void
+static INLINE void
bidi_remember_char (struct bidi_saved_info *saved_info,
struct bidi_it *bidi_it)
{
saved_info->charpos = bidi_it->charpos;
saved_info->bytepos = bidi_it->bytepos;
saved_info->type = bidi_it->type;
+ bidi_check_type (bidi_it->type);
+ saved_info->type_after_w1 = bidi_it->type_after_w1;
+ bidi_check_type (bidi_it->type_after_w1);
saved_info->orig_type = bidi_it->orig_type;
- saved_info->pristine_type = bidi_it->pristine_type;
+ bidi_check_type (bidi_it->orig_type);
}
/* Resolve the type of a neutral character according to the type of
surrounding strong text and the current embedding level. */
-static inline bidi_type_t
+static INLINE bidi_type_t
bidi_resolve_neutral_1 (bidi_type_t prev_type, bidi_type_t next_type, int lev)
{
/* N1: European and Arabic numbers are treated as though they were R. */
return STRONG_R;
}
-static inline int
+static INLINE int
bidi_explicit_dir_char (int c)
{
/* FIXME: this should be replaced with a lookup table with suitable
int new_level;
bidi_dir_t override;
- if (bidi_it->charpos < 0)
- bidi_it->charpos = bidi_it->bytepos = 0;
- else
+ if (bidi_it->bytepos < BEGV_BYTE /* after reseat to BEGV? */
+ || bidi_it->first_elt)
+ {
+ bidi_it->first_elt = 0;
+ if (bidi_it->charpos < BEGV)
+ bidi_it->charpos = BEGV;
+ bidi_it->bytepos = CHAR_TO_BYTE (bidi_it->charpos);
+ }
+ else if (bidi_it->bytepos < ZV_BYTE) /* don't move at ZV */
{
bidi_it->charpos++;
+ if (bidi_it->ch_len == 0)
+ abort ();
bidi_it->bytepos += bidi_it->ch_len;
}
/* in case it is a unibyte character (not yet implemented) */
/* _fetch_multibyte_char_len = 1; */
- curchar = FETCH_CHAR (bidi_it->bytepos);
+ if (bidi_it->bytepos >= ZV_BYTE)
+ {
+ curchar = BIDI_EOB;
+ bidi_it->ch_len = 1;
+ }
+ else
+ {
+ curchar = FETCH_CHAR (bidi_it->bytepos);
+ bidi_it->ch_len = CHAR_BYTES (curchar);
+ }
bidi_it->ch = curchar;
- bidi_it->ch_len = CHAR_BYTES (curchar);
- type = bidi_get_type (curchar);
- bidi_it->pristine_type = type;
+ /* Don't apply directional override here, as all the types we handle
+ below will not be affected by the override anyway, and we need
+ the original type unaltered. The override will be applied in
+ bidi_resolve_weak. */
+ type = bidi_get_type (curchar, NEUTRAL_DIR);
+ bidi_it->orig_type = type;
+ bidi_check_type (bidi_it->orig_type);
if (type != PDF)
bidi_it->prev_was_pdf = 0;
- bidi_it->orig_type = UNKNOWN_BT;
+ bidi_it->type_after_w1 = UNKNOWN_BT;
switch (type)
{
case RLE: /* X2 */
case RLO: /* X4 */
- bidi_it->orig_type = type;
+ bidi_it->type_after_w1 = type;
+ bidi_check_type (bidi_it->type_after_w1);
type = WEAK_BN; /* X9/Retaining */
if (bidi_it->ignore_bn_limit <= 0)
{
/* Compute the least odd embedding level greater than
the current level. */
new_level = ((current_level + 1) & ~1) + 1;
- if (bidi_it->orig_type == RLE)
+ if (bidi_it->type_after_w1 == RLE)
override = NEUTRAL_DIR;
else
override = R2L;
bidi_it->invalid_rl_levels++;
}
}
- else if (bidi_it->prev.orig_type == WEAK_EN /* W5/Retaining */
+ else if (bidi_it->prev.type_after_w1 == WEAK_EN /* W5/Retaining */
|| bidi_it->next_en_pos > bidi_it->charpos)
type = WEAK_EN;
break;
case LRE: /* X3 */
case LRO: /* X5 */
- bidi_it->orig_type = type;
+ bidi_it->type_after_w1 = type;
+ bidi_check_type (bidi_it->type_after_w1);
type = WEAK_BN; /* X9/Retaining */
if (bidi_it->ignore_bn_limit <= 0)
{
/* Compute the least even embedding level greater than
the current level. */
new_level = ((current_level + 2) & ~1);
- if (bidi_it->orig_type == LRE)
+ if (bidi_it->type_after_w1 == LRE)
override = NEUTRAL_DIR;
else
override = L2R;
bidi_it->invalid_rl_levels++;
}
}
- else if (bidi_it->prev.orig_type == WEAK_EN /* W5/Retaining */
+ else if (bidi_it->prev.type_after_w1 == WEAK_EN /* W5/Retaining */
|| bidi_it->next_en_pos > bidi_it->charpos)
type = WEAK_EN;
break;
case PDF: /* X7 */
- bidi_it->orig_type = type;
+ bidi_it->type_after_w1 = type;
+ bidi_check_type (bidi_it->type_after_w1);
type = WEAK_BN; /* X9/Retaining */
if (bidi_it->ignore_bn_limit <= 0)
{
bidi_it->invalid_rl_levels--;
}
}
- else if (bidi_it->prev.orig_type == WEAK_EN /* W5/Retaining */
+ else if (bidi_it->prev.type_after_w1 == WEAK_EN /* W5/Retaining */
|| bidi_it->next_en_pos > bidi_it->charpos)
type = WEAK_EN;
break;
}
bidi_it->type = type;
+ bidi_check_type (bidi_it->type);
return new_level;
}
if (prev_level < new_level
&& bidi_it->type == WEAK_BN
&& bidi_it->ignore_bn_limit == 0 /* only if not already known */
+ && bidi_it->bytepos < ZV_BYTE /* not already at EOB */
&& bidi_explicit_dir_char (FETCH_CHAR (bidi_it->bytepos
+ bidi_it->ch_len)))
{
}
}
- /* For when the paragraph end is defined by anything other than a
- special Unicode character (a.k.a. ``higher protocols''). */
- if (bidi_it->type != NEUTRAL_B)
- if (bidi_at_paragraph_end (bidi_it->ch,
- bidi_it->bytepos + bidi_it->ch_len))
- bidi_it->type = NEUTRAL_B;
-
if (bidi_it->type == NEUTRAL_B) /* X8 */
{
bidi_set_paragraph_end (bidi_it);
- bidi_it->orig_type = bidi_it->type; /* needed below and in L1 */
+ /* This is needed by bidi_resolve_weak below, and in L1. */
+ bidi_it->type_after_w1 = bidi_it->type;
+ bidi_check_type (bidi_it->type_after_w1);
}
return new_level;
/* Advance in the buffer, resolve weak types and return the type of
the next character after weak type resolution. */
-bidi_type_t
+static bidi_type_t
bidi_resolve_weak (struct bidi_it *bidi_it)
{
bidi_type_t type;
}
else if (type == NEUTRAL_S || type == NEUTRAL_WS
|| type == WEAK_BN || type == STRONG_AL)
- bidi_it->orig_type = type; /* needed in L1 */
+ bidi_it->type_after_w1 = type; /* needed in L1 */
+ bidi_check_type (bidi_it->type_after_w1);
/* Level and directional override status are already recorded in
bidi_it, and do not need any change; see X6. */
type = STRONG_R;
else if (override == L2R)
type = STRONG_L;
- else if (type == STRONG_AL)
- type = STRONG_R; /* W3 */
- else if (type == WEAK_NSM) /* W1 */
- {
- /* Note that we don't need to consider the case where the prev
- character has its type overridden by an RLO or LRO: such
- characters are outside the current level run, and thus not
- relevant to this NSM. Thus, NSM gets the pristine_type of
- the previous character. */
- if (bidi_it->prev.type != UNKNOWN_BT)
- type = bidi_it->prev.pristine_type;
- else if (bidi_it->sor == R2L)
- type = STRONG_R;
- else if (bidi_it->sor == L2R)
- type = STRONG_L;
- else /* shouldn't happen! */
- abort ();
- if (type == WEAK_EN /* W2 after W1 */
- && bidi_it->last_strong.orig_type == STRONG_AL)
- type = WEAK_AN;
- }
- else if (type == WEAK_EN /* W2 */
- && bidi_it->last_strong.orig_type == STRONG_AL)
- type = WEAK_AN;
- else if ((type == WEAK_ES
- && (bidi_it->prev.orig_type == WEAK_EN /* W4 */
- && (bidi_it->prev.pristine_type == WEAK_EN
- || bidi_it->prev.pristine_type == WEAK_NSM))) /* aft W1 */
- || (type == WEAK_CS
- && ((bidi_it->prev.orig_type == WEAK_EN
- && (bidi_it->prev.pristine_type == WEAK_EN /* W4 */
- || bidi_it->prev.pristine_type == WEAK_NSM)) /* a/W1 */
- || bidi_it->prev.orig_type == WEAK_AN))) /* W4 */
+ else
{
- next_char = FETCH_CHAR (bidi_it->bytepos + bidi_it->ch_len);
- type_of_next = bidi_get_type (next_char);
-
- if (type_of_next == WEAK_BN
- || bidi_explicit_dir_char (next_char))
+ if (type == WEAK_NSM) /* W1 */
{
- bidi_copy_it (&saved_it, bidi_it);
- while (bidi_resolve_explicit (bidi_it) == new_level
- && bidi_it->type == WEAK_BN)
- ;
- type_of_next = bidi_it->type;
- bidi_copy_it (bidi_it, &saved_it);
- }
-
- /* If the next character is EN, but the last strong-type
- character is AL, that next EN will be changed to AN when we
- process it in W2 above. So in that case, this ES should not
- be changed into EN. */
- if (type == WEAK_ES
- && type_of_next == WEAK_EN
- && bidi_it->last_strong.orig_type != STRONG_AL)
- type = WEAK_EN;
- else if (type == WEAK_CS)
- {
- if (bidi_it->prev.orig_type == WEAK_AN
- && (type_of_next == WEAK_AN
- /* If the next character is EN, but the last
- strong-type character is AL, EN will be later
- changed to AN when we process it in W2 above. So
- in that case, this ES should not be changed into
- EN. */
- || (type_of_next == WEAK_EN
- && bidi_it->last_strong.orig_type == STRONG_AL)))
- type = WEAK_AN;
- else if (bidi_it->prev.orig_type == WEAK_EN
- && type_of_next == WEAK_EN
- && bidi_it->last_strong.orig_type != STRONG_AL)
- type = WEAK_EN;
+ /* Note that we don't need to consider the case where the
+ prev character has its type overridden by an RLO or LRO,
+ because then either the type of this NSM would have been
+ also overridden, or the previous character is outside the
+ current level run, and thus not relevant to this NSM.
+ This is why NSM gets the type_after_w1 of the previous
+ character. */
+ if (bidi_it->prev.type_after_w1 != UNKNOWN_BT
+ /* if type_after_w1 is NEUTRAL_B, this NSM is at sor */
+ && bidi_it->prev.type_after_w1 != NEUTRAL_B)
+ type = bidi_it->prev.type_after_w1;
+ else if (bidi_it->sor == R2L)
+ type = STRONG_R;
+ else if (bidi_it->sor == L2R)
+ type = STRONG_L;
+ else /* shouldn't happen! */
+ abort ();
}
- }
- else if (type == WEAK_ET /* W5: ET with EN before or after it */
- || type == WEAK_BN) /* W5/Retaining */
- {
- if (bidi_it->prev.orig_type == WEAK_EN /* ET/BN with EN before it */
- || bidi_it->next_en_pos > bidi_it->charpos)
- type = WEAK_EN;
- /* W5: ET with EN after it. */
- else
+ if (type == WEAK_EN /* W2 */
+ && bidi_it->last_strong.type_after_w1 == STRONG_AL)
+ type = WEAK_AN;
+ else if (type == STRONG_AL) /* W3 */
+ type = STRONG_R;
+ else if ((type == WEAK_ES /* W4 */
+ && bidi_it->prev.type_after_w1 == WEAK_EN
+ && bidi_it->prev.orig_type == WEAK_EN)
+ || (type == WEAK_CS
+ && ((bidi_it->prev.type_after_w1 == WEAK_EN
+ && bidi_it->prev.orig_type == WEAK_EN)
+ || bidi_it->prev.type_after_w1 == WEAK_AN)))
{
- int en_pos = bidi_it->charpos + 1;
-
- next_char = FETCH_CHAR (bidi_it->bytepos + bidi_it->ch_len);
- type_of_next = bidi_get_type (next_char);
+ next_char =
+ bidi_it->bytepos + bidi_it->ch_len >= ZV_BYTE
+ ? BIDI_EOB : FETCH_CHAR (bidi_it->bytepos + bidi_it->ch_len);
+ type_of_next = bidi_get_type (next_char, override);
- if (type_of_next == WEAK_ET
- || type_of_next == WEAK_BN
+ if (type_of_next == WEAK_BN
|| bidi_explicit_dir_char (next_char))
{
bidi_copy_it (&saved_it, bidi_it);
while (bidi_resolve_explicit (bidi_it) == new_level
- && (bidi_it->type == WEAK_BN || bidi_it->type == WEAK_ET))
+ && bidi_it->type == WEAK_BN)
;
type_of_next = bidi_it->type;
- en_pos = bidi_it->charpos;
bidi_copy_it (bidi_it, &saved_it);
}
- if (type_of_next == WEAK_EN)
+
+ /* If the next character is EN, but the last strong-type
+ character is AL, that next EN will be changed to AN when
+ we process it in W2 above. So in that case, this ES
+ should not be changed into EN. */
+ if (type == WEAK_ES
+ && type_of_next == WEAK_EN
+ && bidi_it->last_strong.type_after_w1 != STRONG_AL)
+ type = WEAK_EN;
+ else if (type == WEAK_CS)
+ {
+ if (bidi_it->prev.type_after_w1 == WEAK_AN
+ && (type_of_next == WEAK_AN
+ /* If the next character is EN, but the last
+ strong-type character is AL, EN will be later
+ changed to AN when we process it in W2 above.
+ So in that case, this ES should not be
+ changed into EN. */
+ || (type_of_next == WEAK_EN
+ && bidi_it->last_strong.type_after_w1 == STRONG_AL)))
+ type = WEAK_AN;
+ else if (bidi_it->prev.type_after_w1 == WEAK_EN
+ && type_of_next == WEAK_EN
+ && bidi_it->last_strong.type_after_w1 != STRONG_AL)
+ type = WEAK_EN;
+ }
+ }
+ else if (type == WEAK_ET /* W5: ET with EN before or after it */
+ || type == WEAK_BN) /* W5/Retaining */
+ {
+ 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. */
{
- /* If the last strong character is AL, the EN we've
- found will become AN when we get to it (W2). */
- if (bidi_it->last_strong.orig_type != STRONG_AL)
+ EMACS_INT en_pos = bidi_it->charpos + 1;
+
+ next_char =
+ bidi_it->bytepos + bidi_it->ch_len >= ZV_BYTE
+ ? BIDI_EOB : FETCH_CHAR (bidi_it->bytepos + bidi_it->ch_len);
+ type_of_next = bidi_get_type (next_char, override);
+
+ if (type_of_next == WEAK_ET
+ || type_of_next == WEAK_BN
+ || bidi_explicit_dir_char (next_char))
{
- type = WEAK_EN;
- /* Remember this EN position, to speed up processing
- of the next ETs. */
- bidi_it->next_en_pos = en_pos;
+ bidi_copy_it (&saved_it, bidi_it);
+ while (bidi_resolve_explicit (bidi_it) == new_level
+ && (bidi_it->type == WEAK_BN
+ || bidi_it->type == WEAK_ET))
+ ;
+ type_of_next = bidi_it->type;
+ en_pos = bidi_it->charpos;
+ bidi_copy_it (bidi_it, &saved_it);
+ }
+ if (type_of_next == WEAK_EN)
+ {
+ /* If the last strong character is AL, the EN we've
+ found will become AN when we get to it (W2). */
+ if (bidi_it->last_strong.type_after_w1 != STRONG_AL)
+ {
+ type = WEAK_EN;
+ /* Remember this EN position, to speed up processing
+ of the next ETs. */
+ bidi_it->next_en_pos = en_pos;
+ }
+ else if (type == WEAK_BN)
+ type = NEUTRAL_ON; /* W6/Retaining */
}
- else if (type == WEAK_BN)
- type = NEUTRAL_ON; /* W6/Retaining */
}
}
}
if (type == WEAK_ES || type == WEAK_ET || type == WEAK_CS /* W6 */
- || (type == WEAK_BN && (bidi_it->prev.orig_type == WEAK_CS /* W6/Ret. */
- || bidi_it->prev.orig_type == WEAK_ES
- || bidi_it->prev.orig_type == WEAK_ET)))
+ || (type == WEAK_BN
+ && (bidi_it->prev.type_after_w1 == WEAK_CS /* W6/Retaining */
+ || bidi_it->prev.type_after_w1 == WEAK_ES
+ || bidi_it->prev.type_after_w1 == WEAK_ET)))
type = NEUTRAL_ON;
/* Store the type we've got so far, before we clobber it with strong
types in W7 and while resolving neutral types. But leave alone
the original types that were recorded above, because we will need
them for the L1 clause. */
- if (bidi_it->orig_type == UNKNOWN_BT)
- bidi_it->orig_type = type;
+ if (bidi_it->type_after_w1 == UNKNOWN_BT)
+ bidi_it->type_after_w1 = type;
+ bidi_check_type (bidi_it->type_after_w1);
if (type == WEAK_EN) /* W7 */
{
- if ((bidi_it->last_strong.orig_type == STRONG_L)
+ if ((bidi_it->last_strong.type_after_w1 == STRONG_L)
|| (bidi_it->last_strong.type == UNKNOWN_BT && bidi_it->sor == L2R))
type = STRONG_L;
}
bidi_it->type = type;
+ bidi_check_type (bidi_it->type);
return type;
}
-bidi_type_t
+static bidi_type_t
bidi_resolve_neutral (struct bidi_it *bidi_it)
{
int prev_level = bidi_it->level_stack[bidi_it->stack_idx].level;
do {
/* Record the info about the previous character, so that
it will be cached below with this state. */
- if (bidi_it->orig_type != WEAK_BN /* W1/Retaining */
+ if (bidi_it->type_after_w1 != WEAK_BN /* W1/Retaining */
&& bidi_it->type != WEAK_BN)
bidi_remember_char (&bidi_it->prev, bidi_it);
type = bidi_resolve_weak (bidi_it);
stored by bidi_set_sor_type in the prev_for_neutral
member. */
if (saved_it.type != WEAK_BN
- || bidi_get_category (bidi_it->prev.orig_type) == NEUTRAL)
+ || 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);
}
else
{
type = bidi_resolve_neutral_1 (saved_it.prev_for_neutral.type,
next_type, current_level);
saved_it.type = type;
+ bidi_check_type (type);
bidi_copy_it (bidi_it, &saved_it);
}
}
/* Given an iterator state in BIDI_IT, advance one character position
in the buffer to the next character (in the logical order), resolve
the bidi type of that next character, and return that type. */
-bidi_type_t
+static bidi_type_t
bidi_type_of_next_char (struct bidi_it *bidi_it)
{
bidi_type_t type;
the buffer to the next character (in the logical order), resolve
the embedding and implicit levels of that next character, and
return the resulting level. */
-int
+static int
bidi_level_of_next_char (struct bidi_it *bidi_it)
{
bidi_type_t type;
if (bidi_it->scan_dir == 1)
{
/* There's no sense in trying to advance if we hit end of text. */
- if (bidi_it->ch == BIDI_EOB)
+ if (bidi_it->bytepos >= ZV_BYTE)
return bidi_it->resolved_level;
/* Record the info about the previous character. */
- if (bidi_it->orig_type != WEAK_BN /* W1/Retaining */
+ if (bidi_it->type_after_w1 != WEAK_BN /* W1/Retaining */
&& bidi_it->type != WEAK_BN)
bidi_remember_char (&bidi_it->prev, bidi_it);
- if (bidi_it->orig_type == STRONG_R || bidi_it->orig_type == STRONG_L
- || bidi_it->orig_type == STRONG_AL)
+ if (bidi_it->type_after_w1 == STRONG_R
+ || bidi_it->type_after_w1 == STRONG_L
+ || bidi_it->type_after_w1 == STRONG_AL)
bidi_remember_char (&bidi_it->last_strong, bidi_it);
/* FIXME: it sounds like we don't need both prev and
prev_for_neutral members, but I'm leaving them both for now. */
|| type == WEAK_AN))
abort ();
bidi_it->type = type;
+ bidi_check_type (bidi_it->type);
/* For L1 below, we need to know, for each WS character, whether
- it belongs to a sequence of WS characters preceeding a newline
+ it belongs to a sequence of WS characters preceding a newline
or a TAB or a paragraph separator. */
- if (bidi_it->pristine_type == NEUTRAL_WS
+ if (bidi_it->orig_type == NEUTRAL_WS
&& bidi_it->next_for_ws.type == UNKNOWN_BT)
{
int ch;
int clen = bidi_it->ch_len;
- int bpos = bidi_it->bytepos;
- int cpos = bidi_it->charpos;
+ EMACS_INT bpos = bidi_it->bytepos;
+ EMACS_INT cpos = bidi_it->charpos;
bidi_type_t chtype;
do {
/*_fetch_multibyte_char_len = 1;*/
- ch = FETCH_CHAR (bpos + clen);
+ ch = bpos + clen >= ZV_BYTE ? BIDI_EOB : FETCH_CHAR (bpos + clen);
bpos += clen;
cpos++;
- clen = CHAR_BYTES (ch);
- if (ch == '\n' /* || ch == LINESEP_CHAR */)
+ clen = (ch == BIDI_EOB ? 1 : CHAR_BYTES (ch));
+ if (ch == '\n' || ch == BIDI_EOB /* || ch == LINESEP_CHAR */)
chtype = NEUTRAL_B;
else
- chtype = bidi_get_type (ch);
+ chtype = bidi_get_type (ch, NEUTRAL_DIR);
} while (chtype == NEUTRAL_WS || chtype == WEAK_BN
|| bidi_explicit_dir_char (ch)); /* L1/Retaining */
bidi_it->next_for_ws.type = chtype;
+ bidi_check_type (bidi_it->next_for_ws.type);
bidi_it->next_for_ws.charpos = cpos;
bidi_it->next_for_ws.bytepos = bpos;
}
/* Resolve implicit levels, with a twist: PDFs get the embedding
level of the enbedding they terminate. See below for the
reason. */
- if (bidi_it->pristine_type == PDF
+ if (bidi_it->orig_type == PDF
/* Don't do this if this formatting code didn't change the
embedding level due to invalid or empty embeddings. */
&& prev_level != level)
we want it to be displayed as
- {RLO}STet{PDF}
+ {PDF}STet{RLO}
not as
ugly side effect does not happen.
(This is, of course, only important if the formatting codes
- are actually displayed, but Emacs does display them if the
- user wants to.) */
+ are actually displayed, but Emacs does need to display them
+ if the user wants to.) */
level = prev_level;
}
- else if (bidi_it->pristine_type == NEUTRAL_B /* L1 */
- || bidi_it->pristine_type == NEUTRAL_S
- || bidi_it->ch == '\n' /* || bidi_it->ch == LINESEP_CHAR */
- || (bidi_it->pristine_type == NEUTRAL_WS
+ else if (bidi_it->orig_type == NEUTRAL_B /* L1 */
+ || bidi_it->orig_type == NEUTRAL_S
+ || bidi_it->ch == '\n' || bidi_it->ch == BIDI_EOB
+ /* || bidi_it->ch == LINESEP_CHAR */
+ || (bidi_it->orig_type == NEUTRAL_WS
&& (bidi_it->next_for_ws.type == NEUTRAL_B
|| bidi_it->next_for_ws.type == NEUTRAL_S)))
level = bidi_it->level_stack[0].level;
}
void
-bidi_get_next_char_visually (struct bidi_it *bidi_it)
+bidi_move_to_visually_next (struct bidi_it *bidi_it)
{
int old_level, new_level, next_level;
- struct bidi_it prev_bidi_it;
+ struct bidi_it sentinel;
if (bidi_it->scan_dir == 0)
{
bidi_it->scan_dir = 1; /* default to logical order */
}
- if (bidi_it->new_paragraph)
- bidi_paragraph_init (bidi_overriding_paragraph_direction, bidi_it);
+ /* If we just passed a newline, initialize for the next line. */
+ if (!bidi_it->first_elt && bidi_it->orig_type == NEUTRAL_B)
+ bidi_line_init (bidi_it);
+
+ /* Prepare the sentinel iterator state, and cache it. When we bump
+ into it, scanning backwards, we'll know that the last non-base
+ level is exhausted. */
if (bidi_cache_idx == 0)
- bidi_copy_it (&prev_bidi_it, bidi_it);
+ {
+ bidi_copy_it (&sentinel, bidi_it);
+ if (bidi_it->first_elt)
+ {
+ sentinel.charpos--; /* cached charpos needs to be monotonic */
+ sentinel.bytepos--;
+ sentinel.ch = '\n'; /* doesn't matter, but why not? */
+ sentinel.ch_len = 1;
+ }
+ bidi_cache_iterator_state (&sentinel, 1);
+ }
old_level = bidi_it->resolved_level;
new_level = bidi_level_of_next_char (bidi_it);
- if (bidi_it->ch == BIDI_EOB)
- return;
/* Reordering of resolved levels (clause L2) is implemented by
jumping to the other edge of the level and flipping direction of
- scanning the buffer whenever we find a level change. */
+ scanning the text whenever we find a level change. */
if (new_level != old_level)
{
int ascending = new_level > old_level;
int incr = ascending ? 1 : -1;
int expected_next_level = old_level + incr;
- /* If we don't have anything cached yet, we need to cache the
- previous character we've seen, since we'll need it to record
- where to jump when the last non-base level is exhausted. */
- if (bidi_cache_idx == 0)
- bidi_cache_iterator_state (&prev_bidi_it, 1);
/* Jump (or walk) to the other edge of this level. */
bidi_find_other_level_edge (bidi_it, level_to_search, !ascending);
/* Switch scan direction and peek at the next character in the
next_level = bidi_level_of_next_char (bidi_it);
}
+ /* 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, whereas we _must_ know the paragraph base direction
+ _before_ we process the paragraph's text, since the base
+ direction affects the reordering. */
+ if (bidi_it->scan_dir == 1
+ && bidi_it->orig_type == NEUTRAL_B
+ && bidi_it->bytepos < ZV_BYTE)
+ {
+ EMACS_INT sep_len =
+ bidi_at_paragraph_end (bidi_it->charpos + 1,
+ bidi_it->bytepos + bidi_it->ch_len);
+ 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 + 1 + sep_len;
+ }
+ }
+
if (bidi_it->scan_dir == 1 && bidi_cache_idx)
{
/* If we are at paragraph's base embedding level and beyond the
/* This is meant to be called from within the debugger, whenever you
wish to examine the cache contents. */
+void bidi_dump_cached_states (void) EXTERNALLY_VISIBLE;
void
bidi_dump_cached_states (void)
{
fputs ("\n", stderr);
fputs ("pos ", stderr);
for (i = 0; i < bidi_cache_idx; i++)
- fprintf (stderr, "%*d", ndigits, bidi_cache[i].charpos);
+ fprintf (stderr, "%*"pI"d", ndigits, bidi_cache[i].charpos);
fputs ("\n", stderr);
}
-
-#ifdef TEST_STANDALONE
-
-#include <sys/stat.h>
-#include <signal.h>
-
-static char display_line[80];
-static int simulate_display;
-static int incr = 1;
-
-void
-signal_catcher (int sig)
-{
- if (simulate_display)
- puts (display_line);
- else
- {
- puts ("<");
- fflush (stdout);
- }
- signal (sig, SIG_DFL);
- raise (sig);
-}
-
-void
-put_line (char *p)
-{
- if (simulate_display)
- {
- if (incr == -1)
- {
- if (p >= display_line)
- memset (display_line, ' ', p - display_line + 1);
- }
- else
- *p = '\0';
- fputs (display_line, stdout);
- }
- fflush (stdout);
-}
-
-char *
-init_display_direction (bidi_dir_t default_dir, int base_level)
-{
- char *p;
-
- /* To which display margin should we flush the lines? */
- switch (default_dir)
- {
- case NEUTRAL_DIR:
- if ((base_level & 1) == 0)
- {
- p = display_line;
- incr = 1;
- }
- else
- {
- p = display_line + sizeof (display_line) - 1;
- *p-- = '\0';
- incr = -1;
- }
- break;
- case L2R:
- p = display_line;
- incr = 1;
- break;
- case R2L:
- p = display_line + sizeof (display_line) - 1;
- *p-- = '\0';
- incr = -1;
- break;
- default:
- abort ();
- }
-
- return p;
-}
-
-static char *
-continuation_line (char *p, int need)
-{
- if (incr == -1)
- {
- if (p < display_line + need)
- {
- *p-- = '/';
- put_line (p);
- putc ('\n', stdout);
- memset (display_line, '>', sizeof(display_line) - 1);
- p = display_line + sizeof (display_line) - 1;
- *p-- = '\0';
- }
- }
- else
- {
- if (p > display_line + sizeof(display_line) - need - 2)
- {
- *p++ = '\\';
- put_line (p);
- putc ('\n', stdout);
- memset (display_line, '<', sizeof(display_line) - 1);
- p = display_line;
- }
- }
-
- return p;
-}
-
-int main (int argc, char *argv[])
-{
- bidi_dir_t default_dir = NEUTRAL_DIR;
- char lots_of_equals[] = "\n===============================================================================\n";
-
-
- if (argc > 1 && argv[1][0] == '-')
- {
- switch (argv[1][1])
- {
- case 'R':
- default_dir = R2L;
- simulate_display = 1;
- ++argv;
- break;
- case 'L':
- default_dir = L2R;
- simulate_display = 1;
- ++argv;
- break;
- case 'N':
- simulate_display = 1;
- ++argv;
- break;
- default:
- break;
- }
- bidi_overriding_paragraph_direction = default_dir;
- }
-
- for (argv++; *argv; argv++)
- {
- FILE *in = fopen (*argv, "rb");
- struct stat stat_buf;
- struct bidi_it iterator;
- size_t i;
- char *p = display_line;
- int base_level;
- unsigned char *s, *d, *s_end;
-
- if (!in || stat (*argv, &stat_buf))
- {
- perror (*argv);
- continue;
- }
-
- if (stat_buf.st_size > input_buf_size)
- {
- input_buf = realloc (input_buf, stat_buf.st_size + 1);
- if (!input_buf)
- {
- perror ("realloc input buffer");
- continue;
- }
- input_buf_size = stat_buf.st_size;
- }
- if (fread (input_buf, 1, stat_buf.st_size, in) != stat_buf.st_size)
- {
- perror ("reading input");
- continue;
- }
- input_buf[stat_buf.st_size] = '\0';
- for (d = s = input_buf, s_end = s + stat_buf.st_size - 1; *s; s++)
- {
- if (*s != '\r' || s >= s_end || s[1] != '\n')
- *d++ = *s;
- }
- stat_buf.st_size = d - input_buf;
- input_buf[stat_buf.st_size] = '\0';
-
- /* Done with administrivia, now for some real work... */
- signal (SIGABRT, signal_catcher);
- signal (SIGINT, signal_catcher);
- bidi_init_it (-1, default_dir, &iterator);
- if (simulate_display)
- {
- p = init_display_direction (default_dir,
- iterator.level_stack[0].level);
- }
-
- memset (display_line, incr == -1 ? '>' : '<', sizeof (display_line) - 1);
- display_line[sizeof (display_line) - 1] = '\0';
- base_level = iterator.level_stack[0].level;
-
- for (i = 0; i <= stat_buf.st_size; i++)
- {
- int c;
-
- bidi_get_next_char_visually (&iterator);
- c = iterator.ch;
-
- if (c == '\n' || c == BIDI_EOB)
- {
- if (simulate_display)
- {
- put_line (p);
- /* FIXME: if -R or -L, need to init paragraph here. */
- }
- if (c == BIDI_EOB)
- break;
- putc (c, stdout);
- }
- else if (c >= LRE_CHAR && c <= LRM_CHAR)
- {
- if (simulate_display)
- {
- p = continuation_line (p, 5);
- if (incr == -1)
- {
- memcpy (p - 4, bidi_name[c], 5);
- p -= 5;
- }
- else
- {
- memcpy (p, bidi_name[c], 5);
- p += 5;
- }
- }
- else
- fputs (bidi_name[c], stdout);
- }
- else if (c < ' ')
- {
- if (simulate_display)
- {
- p = continuation_line (p, 2);
- if (incr == -1)
- {
- *p-- = '@' + c;
- *p-- = '^';
- }
- else
- {
- *p++ = '^';
- *p++ = '@' + c;
- }
- }
- else
- printf ("^%c", (c | 0x40));
- }
- else
- {
- int c1 = (iterator.type == STRONG_R) ? bidi_mirror_char (c) : c;
-
- if (simulate_display)
- {
- p = continuation_line (p, 1);
- *p = c1;
- p += incr;
- }
- else
- putc (c1, stdout);
- }
-
- if (iterator.ch == '\n')
- {
- if (base_level != iterator.level_stack[0].level)
- base_level = iterator.level_stack[0].level;
- p = init_display_direction (default_dir, base_level);
- memset (display_line, incr == -1 ? '>' : '<',
- sizeof (display_line) - 1);
- }
- }
- fputs (lots_of_equals, stdout);
- fclose (in);
- }
- return 0;
-}
-#endif