aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeff Vander Stoep <jeffv@google.com>2023-02-06 16:13:32 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2023-02-06 16:13:32 +0000
commit40f6c4d70e1fa4661bf352087a4dcd7a5766f9e1 (patch)
tree22f9fa6373b6ccdda5b2e33787b5e58a685d3d21
parent2de7d669f174ff808b54dc6e11da360c45d13fc7 (diff)
parent5c486de1d8072ad8dbbc1213e73207685283690d (diff)
downloadunicode-bidi-40f6c4d70e1fa4661bf352087a4dcd7a5766f9e1.tar.gz
Upgrade unicode-bidi to 0.3.10 am: 60ba640195 am: 5c486de1d8
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/unicode-bidi/+/2422685 Change-Id: I8366b331d58c4ad56fe7c84882e1c6c22b85d61e Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r--.appveyor.yml6
-rw-r--r--.cargo_vcs_info.json6
-rw-r--r--Android.bp6
-rw-r--r--Cargo.lock.saved175
-rw-r--r--Cargo.toml7
-rw-r--r--Cargo.toml.orig7
-rw-r--r--METADATA10
-rw-r--r--src/char_data/mod.rs18
-rw-r--r--src/char_data/tables.rs311
-rw-r--r--src/data_source.rs30
-rw-r--r--src/explicit.rs44
-rw-r--r--src/implicit.rs521
-rw-r--r--src/lib.rs93
-rw-r--r--src/prepare.rs102
14 files changed, 896 insertions, 440 deletions
diff --git a/.appveyor.yml b/.appveyor.yml
index 228d09a..1bd43a1 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -1,7 +1,7 @@
install:
- - ps: Start-FileDownload 'https://static.rust-lang.org/dist/rust-nightly-i686-pc-windows-gnu.exe'
- - rust-nightly-i686-pc-windows-gnu.exe /VERYSILENT /NORESTART /DIR="C:\Program Files (x86)\Rust"
- - SET PATH=%PATH%;C:\Program Files (x86)\Rust\bin
+ - appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
+ - rustup-init -yv --default-toolchain nightly
+ - set PATH=%PATH%;%USERPROFILE%\.cargo\bin
- rustc -V
- cargo -V
- git submodule update --init --recursive
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
new file mode 100644
index 0000000..67b5a63
--- /dev/null
+++ b/.cargo_vcs_info.json
@@ -0,0 +1,6 @@
+{
+ "git": {
+ "sha1": "cd1de5d1ddbba789c29b6d69811ef49c820eefd4"
+ },
+ "path_in_vcs": ""
+} \ No newline at end of file
diff --git a/Android.bp b/Android.bp
index 8d412ec..fdc1e2b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -40,10 +40,11 @@ license {
rust_library {
name: "libunicode_bidi",
+ // has rustc warnings
host_supported: true,
crate_name: "unicode_bidi",
cargo_env_compat: true,
- cargo_pkg_version: "0.3.8",
+ cargo_pkg_version: "0.3.10",
srcs: ["src/lib.rs"],
edition: "2018",
features: [
@@ -60,10 +61,11 @@ rust_library {
rust_test {
name: "unicode-bidi_test_src_lib",
+ // has rustc warnings
host_supported: true,
crate_name: "unicode_bidi",
cargo_env_compat: true,
- cargo_pkg_version: "0.3.8",
+ cargo_pkg_version: "0.3.10",
srcs: ["src/lib.rs"],
test_suites: ["general-tests"],
auto_gen_config: true,
diff --git a/Cargo.lock.saved b/Cargo.lock.saved
deleted file mode 100644
index 3936ffd..0000000
--- a/Cargo.lock.saved
+++ /dev/null
@@ -1,175 +0,0 @@
-# This file is automatically @generated by Cargo.
-# It is not intended for manual editing.
-version = 3
-
-[[package]]
-name = "flame"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1fc2706461e1ee94f55cab2ed2e3d34ae9536cfa830358ef80acff1a3dacab30"
-dependencies = [
- "lazy_static",
- "serde",
- "serde_derive",
- "serde_json",
- "thread-id",
-]
-
-[[package]]
-name = "flamer"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "36b732da54fd4ea34452f2431cf464ac7be94ca4b339c9cd3d3d12eb06fe7aab"
-dependencies = [
- "flame",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "itoa"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
-
-[[package]]
-name = "lazy_static"
-version = "0.2.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73"
-
-[[package]]
-name = "libc"
-version = "0.2.124"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "21a41fed9d98f27ab1c6d161da622a4fa35e8a54a8adc24bbf3ddd0ef70b0e50"
-
-[[package]]
-name = "proc-macro2"
-version = "1.0.37"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1"
-dependencies = [
- "unicode-xid",
-]
-
-[[package]]
-name = "quote"
-version = "1.0.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
-dependencies = [
- "proc-macro2",
-]
-
-[[package]]
-name = "redox_syscall"
-version = "0.1.57"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
-
-[[package]]
-name = "ryu"
-version = "1.0.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
-
-[[package]]
-name = "serde"
-version = "1.0.136"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789"
-dependencies = [
- "serde_derive",
-]
-
-[[package]]
-name = "serde_derive"
-version = "1.0.136"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "serde_json"
-version = "1.0.79"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95"
-dependencies = [
- "itoa",
- "ryu",
- "serde",
-]
-
-[[package]]
-name = "serde_test"
-version = "1.0.136"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "21675ba6f9d97711cc00eee79d8dd7d0a31e571c350fb4d8a7c78f70c0e7b0e9"
-dependencies = [
- "serde",
-]
-
-[[package]]
-name = "syn"
-version = "1.0.91"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d"
-dependencies = [
- "proc-macro2",
- "quote",
- "unicode-xid",
-]
-
-[[package]]
-name = "thread-id"
-version = "3.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c7fbf4c9d56b320106cd64fd024dadfa0be7cb4706725fc44a7d7ce952d820c1"
-dependencies = [
- "libc",
- "redox_syscall",
- "winapi",
-]
-
-[[package]]
-name = "unicode-bidi"
-version = "0.3.8"
-dependencies = [
- "flame",
- "flamer",
- "serde",
- "serde_test",
-]
-
-[[package]]
-name = "unicode-xid"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
-
-[[package]]
-name = "winapi"
-version = "0.3.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
-dependencies = [
- "winapi-i686-pc-windows-gnu",
- "winapi-x86_64-pc-windows-gnu",
-]
-
-[[package]]
-name = "winapi-i686-pc-windows-gnu"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
-
-[[package]]
-name = "winapi-x86_64-pc-windows-gnu"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
diff --git a/Cargo.toml b/Cargo.toml
index 9a8ca4a..02ea8fa 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -12,7 +12,7 @@
[package]
edition = "2018"
name = "unicode-bidi"
-version = "0.3.8"
+version = "0.3.10"
authors = ["The Servo Project Developers"]
exclude = [
"benches/**",
@@ -42,6 +42,11 @@ repository = "https://github.com/servo/unicode-bidi"
[lib]
name = "unicode_bidi"
+[[test]]
+name = "conformance_tests"
+path = "tests/conformance_tests.rs"
+required-features = ["hardcoded-data"]
+
[dependencies.flame]
version = "0.2"
optional = true
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index a1d900f..02bc85b 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,6 +1,6 @@
[package]
name = "unicode-bidi"
-version = "0.3.8"
+version = "0.3.10"
authors = ["The Servo Project Developers"]
license = "MIT OR Apache-2.0"
description = "Implementation of the Unicode Bidirectional Algorithm"
@@ -48,3 +48,8 @@ unstable = [] # travis-cargo needs it
bench_it = []
flame_it = ["flame", "flamer"]
with_serde = ["serde"] # DEPRECATED, please use `serde` feature, instead.
+
+[[test]]
+name = "conformance_tests"
+required-features = ["hardcoded-data"]
+path = "tests/conformance_tests.rs"
diff --git a/METADATA b/METADATA
index 6d32a69..2b51f3e 100644
--- a/METADATA
+++ b/METADATA
@@ -11,13 +11,13 @@ third_party {
}
url {
type: ARCHIVE
- value: "https://static.crates.io/crates/unicode-bidi/unicode-bidi-0.3.8.crate"
+ value: "https://static.crates.io/crates/unicode-bidi/unicode-bidi-0.3.10.crate"
}
- version: "0.3.8"
+ version: "0.3.10"
license_type: NOTICE
last_upgrade_date {
- year: 2022
- month: 12
- day: 19
+ year: 2023
+ month: 2
+ day: 6
}
}
diff --git a/src/char_data/mod.rs b/src/char_data/mod.rs
index a8b4527..4edf5b8 100644
--- a/src/char_data/mod.rs
+++ b/src/char_data/mod.rs
@@ -19,10 +19,10 @@ use core::cmp::Ordering::{Equal, Greater, Less};
#[cfg(feature = "hardcoded-data")]
use self::tables::bidi_class_table;
+use crate::data_source::BidiMatchedOpeningBracket;
use crate::BidiClass::*;
#[cfg(feature = "hardcoded-data")]
use crate::BidiDataSource;
-
/// Hardcoded Bidi data that ships with the unicode-bidi crate.
///
/// This can be enabled with the default `hardcoded-data` Cargo feature.
@@ -42,6 +42,22 @@ pub fn bidi_class(c: char) -> BidiClass {
bsearch_range_value_table(c, bidi_class_table)
}
+/// If this character is a bracket according to BidiBrackets.txt,
+/// return the corresponding *normalized* *opening bracket* of the pair,
+/// and whether or not it itself is an opening bracket.
+pub(crate) fn bidi_matched_opening_bracket(c: char) -> Option<BidiMatchedOpeningBracket> {
+ for pair in self::tables::bidi_pairs_table {
+ if pair.0 == c || pair.1 == c {
+ let skeleton = pair.2.unwrap_or(pair.0);
+ return Some(BidiMatchedOpeningBracket {
+ opening: skeleton,
+ is_open: pair.0 == c,
+ });
+ }
+ }
+ None
+}
+
pub fn is_rtl(bidi_class: BidiClass) -> bool {
match bidi_class {
RLE | RLO | RLI => true,
diff --git a/src/char_data/tables.rs b/src/char_data/tables.rs
index 502ae9e..ecdcf49 100644
--- a/src/char_data/tables.rs
+++ b/src/char_data/tables.rs
@@ -5,7 +5,7 @@
#![cfg_attr(rustfmt, rustfmt_skip)]
/// The [Unicode version](http://www.unicode.org/versions/) of data
-pub const UNICODE_VERSION: (u64, u64, u64) = (14, 0, 0);
+pub const UNICODE_VERSION: (u64, u64, u64) = (15, 0, 0);
#[allow(non_camel_case_types)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
@@ -140,7 +140,7 @@ pub const bidi_class_table: &'static [(char, char, BidiClass)] = &[
'\u{cb9}', L), ('\u{cbc}', '\u{cbc}', NSM), ('\u{cbd}', '\u{cc4}', L), ('\u{cc6}', '\u{cc8}',
L), ('\u{cca}', '\u{ccb}', L), ('\u{ccc}', '\u{ccd}', NSM), ('\u{cd5}', '\u{cd6}', L),
('\u{cdd}', '\u{cde}', L), ('\u{ce0}', '\u{ce1}', L), ('\u{ce2}', '\u{ce3}', NSM), ('\u{ce6}',
- '\u{cef}', L), ('\u{cf1}', '\u{cf2}', L), ('\u{d00}', '\u{d01}', NSM), ('\u{d02}', '\u{d0c}',
+ '\u{cef}', L), ('\u{cf1}', '\u{cf3}', L), ('\u{d00}', '\u{d01}', NSM), ('\u{d02}', '\u{d0c}',
L), ('\u{d0e}', '\u{d10}', L), ('\u{d12}', '\u{d3a}', L), ('\u{d3b}', '\u{d3c}', NSM),
('\u{d3d}', '\u{d40}', L), ('\u{d41}', '\u{d44}', NSM), ('\u{d46}', '\u{d48}', L), ('\u{d4a}',
'\u{d4c}', L), ('\u{d4d}', '\u{d4d}', NSM), ('\u{d4e}', '\u{d4f}', L), ('\u{d54}', '\u{d61}',
@@ -154,7 +154,7 @@ pub const bidi_class_table: &'static [(char, char, BidiClass)] = &[
('\u{e4f}', '\u{e5b}', L), ('\u{e81}', '\u{e82}', L), ('\u{e84}', '\u{e84}', L), ('\u{e86}',
'\u{e8a}', L), ('\u{e8c}', '\u{ea3}', L), ('\u{ea5}', '\u{ea5}', L), ('\u{ea7}', '\u{eb0}', L),
('\u{eb1}', '\u{eb1}', NSM), ('\u{eb2}', '\u{eb3}', L), ('\u{eb4}', '\u{ebc}', NSM), ('\u{ebd}',
- '\u{ebd}', L), ('\u{ec0}', '\u{ec4}', L), ('\u{ec6}', '\u{ec6}', L), ('\u{ec8}', '\u{ecd}',
+ '\u{ebd}', L), ('\u{ec0}', '\u{ec4}', L), ('\u{ec6}', '\u{ec6}', L), ('\u{ec8}', '\u{ece}',
NSM), ('\u{ed0}', '\u{ed9}', L), ('\u{edc}', '\u{edf}', L), ('\u{f00}', '\u{f17}', L),
('\u{f18}', '\u{f19}', NSM), ('\u{f1a}', '\u{f34}', L), ('\u{f35}', '\u{f35}', NSM), ('\u{f36}',
'\u{f36}', L), ('\u{f37}', '\u{f37}', NSM), ('\u{f38}', '\u{f38}', L), ('\u{f39}', '\u{f39}',
@@ -361,96 +361,102 @@ pub const bidi_class_table: &'static [(char, char, BidiClass)] = &[
'\u{10cff}', R), ('\u{10d00}', '\u{10d23}', AL), ('\u{10d24}', '\u{10d27}', NSM), ('\u{10d28}',
'\u{10d2f}', R), ('\u{10d30}', '\u{10d39}', AN), ('\u{10d3a}', '\u{10e5f}', R), ('\u{10e60}',
'\u{10e7e}', AN), ('\u{10e7f}', '\u{10eaa}', R), ('\u{10eab}', '\u{10eac}', NSM), ('\u{10ead}',
- '\u{10f2f}', R), ('\u{10f30}', '\u{10f45}', AL), ('\u{10f46}', '\u{10f50}', NSM), ('\u{10f51}',
- '\u{10f59}', AL), ('\u{10f5a}', '\u{10f81}', R), ('\u{10f82}', '\u{10f85}', NSM), ('\u{10f86}',
- '\u{10fff}', R), ('\u{11000}', '\u{11000}', L), ('\u{11001}', '\u{11001}', NSM), ('\u{11002}',
- '\u{11037}', L), ('\u{11038}', '\u{11046}', NSM), ('\u{11047}', '\u{1104d}', L), ('\u{11052}',
- '\u{11065}', ON), ('\u{11066}', '\u{1106f}', L), ('\u{11070}', '\u{11070}', NSM), ('\u{11071}',
- '\u{11072}', L), ('\u{11073}', '\u{11074}', NSM), ('\u{11075}', '\u{11075}', L), ('\u{1107f}',
- '\u{11081}', NSM), ('\u{11082}', '\u{110b2}', L), ('\u{110b3}', '\u{110b6}', NSM), ('\u{110b7}',
- '\u{110b8}', L), ('\u{110b9}', '\u{110ba}', NSM), ('\u{110bb}', '\u{110c1}', L), ('\u{110c2}',
- '\u{110c2}', NSM), ('\u{110cd}', '\u{110cd}', L), ('\u{110d0}', '\u{110e8}', L), ('\u{110f0}',
- '\u{110f9}', L), ('\u{11100}', '\u{11102}', NSM), ('\u{11103}', '\u{11126}', L), ('\u{11127}',
- '\u{1112b}', NSM), ('\u{1112c}', '\u{1112c}', L), ('\u{1112d}', '\u{11134}', NSM), ('\u{11136}',
- '\u{11147}', L), ('\u{11150}', '\u{11172}', L), ('\u{11173}', '\u{11173}', NSM), ('\u{11174}',
- '\u{11176}', L), ('\u{11180}', '\u{11181}', NSM), ('\u{11182}', '\u{111b5}', L), ('\u{111b6}',
- '\u{111be}', NSM), ('\u{111bf}', '\u{111c8}', L), ('\u{111c9}', '\u{111cc}', NSM), ('\u{111cd}',
- '\u{111ce}', L), ('\u{111cf}', '\u{111cf}', NSM), ('\u{111d0}', '\u{111df}', L), ('\u{111e1}',
- '\u{111f4}', L), ('\u{11200}', '\u{11211}', L), ('\u{11213}', '\u{1122e}', L), ('\u{1122f}',
- '\u{11231}', NSM), ('\u{11232}', '\u{11233}', L), ('\u{11234}', '\u{11234}', NSM), ('\u{11235}',
- '\u{11235}', L), ('\u{11236}', '\u{11237}', NSM), ('\u{11238}', '\u{1123d}', L), ('\u{1123e}',
- '\u{1123e}', NSM), ('\u{11280}', '\u{11286}', L), ('\u{11288}', '\u{11288}', L), ('\u{1128a}',
- '\u{1128d}', L), ('\u{1128f}', '\u{1129d}', L), ('\u{1129f}', '\u{112a9}', L), ('\u{112b0}',
- '\u{112de}', L), ('\u{112df}', '\u{112df}', NSM), ('\u{112e0}', '\u{112e2}', L), ('\u{112e3}',
- '\u{112ea}', NSM), ('\u{112f0}', '\u{112f9}', L), ('\u{11300}', '\u{11301}', NSM), ('\u{11302}',
- '\u{11303}', L), ('\u{11305}', '\u{1130c}', L), ('\u{1130f}', '\u{11310}', L), ('\u{11313}',
- '\u{11328}', L), ('\u{1132a}', '\u{11330}', L), ('\u{11332}', '\u{11333}', L), ('\u{11335}',
- '\u{11339}', L), ('\u{1133b}', '\u{1133c}', NSM), ('\u{1133d}', '\u{1133f}', L), ('\u{11340}',
- '\u{11340}', NSM), ('\u{11341}', '\u{11344}', L), ('\u{11347}', '\u{11348}', L), ('\u{1134b}',
- '\u{1134d}', L), ('\u{11350}', '\u{11350}', L), ('\u{11357}', '\u{11357}', L), ('\u{1135d}',
- '\u{11363}', L), ('\u{11366}', '\u{1136c}', NSM), ('\u{11370}', '\u{11374}', NSM), ('\u{11400}',
- '\u{11437}', L), ('\u{11438}', '\u{1143f}', NSM), ('\u{11440}', '\u{11441}', L), ('\u{11442}',
- '\u{11444}', NSM), ('\u{11445}', '\u{11445}', L), ('\u{11446}', '\u{11446}', NSM), ('\u{11447}',
- '\u{1145b}', L), ('\u{1145d}', '\u{1145d}', L), ('\u{1145e}', '\u{1145e}', NSM), ('\u{1145f}',
- '\u{11461}', L), ('\u{11480}', '\u{114b2}', L), ('\u{114b3}', '\u{114b8}', NSM), ('\u{114b9}',
- '\u{114b9}', L), ('\u{114ba}', '\u{114ba}', NSM), ('\u{114bb}', '\u{114be}', L), ('\u{114bf}',
- '\u{114c0}', NSM), ('\u{114c1}', '\u{114c1}', L), ('\u{114c2}', '\u{114c3}', NSM), ('\u{114c4}',
- '\u{114c7}', L), ('\u{114d0}', '\u{114d9}', L), ('\u{11580}', '\u{115b1}', L), ('\u{115b2}',
- '\u{115b5}', NSM), ('\u{115b8}', '\u{115bb}', L), ('\u{115bc}', '\u{115bd}', NSM), ('\u{115be}',
- '\u{115be}', L), ('\u{115bf}', '\u{115c0}', NSM), ('\u{115c1}', '\u{115db}', L), ('\u{115dc}',
- '\u{115dd}', NSM), ('\u{11600}', '\u{11632}', L), ('\u{11633}', '\u{1163a}', NSM), ('\u{1163b}',
- '\u{1163c}', L), ('\u{1163d}', '\u{1163d}', NSM), ('\u{1163e}', '\u{1163e}', L), ('\u{1163f}',
- '\u{11640}', NSM), ('\u{11641}', '\u{11644}', L), ('\u{11650}', '\u{11659}', L), ('\u{11660}',
- '\u{1166c}', ON), ('\u{11680}', '\u{116aa}', L), ('\u{116ab}', '\u{116ab}', NSM), ('\u{116ac}',
- '\u{116ac}', L), ('\u{116ad}', '\u{116ad}', NSM), ('\u{116ae}', '\u{116af}', L), ('\u{116b0}',
- '\u{116b5}', NSM), ('\u{116b6}', '\u{116b6}', L), ('\u{116b7}', '\u{116b7}', NSM), ('\u{116b8}',
- '\u{116b9}', L), ('\u{116c0}', '\u{116c9}', L), ('\u{11700}', '\u{1171a}', L), ('\u{1171d}',
- '\u{1171f}', NSM), ('\u{11720}', '\u{11721}', L), ('\u{11722}', '\u{11725}', NSM), ('\u{11726}',
- '\u{11726}', L), ('\u{11727}', '\u{1172b}', NSM), ('\u{11730}', '\u{11746}', L), ('\u{11800}',
- '\u{1182e}', L), ('\u{1182f}', '\u{11837}', NSM), ('\u{11838}', '\u{11838}', L), ('\u{11839}',
- '\u{1183a}', NSM), ('\u{1183b}', '\u{1183b}', L), ('\u{118a0}', '\u{118f2}', L), ('\u{118ff}',
- '\u{11906}', L), ('\u{11909}', '\u{11909}', L), ('\u{1190c}', '\u{11913}', L), ('\u{11915}',
- '\u{11916}', L), ('\u{11918}', '\u{11935}', L), ('\u{11937}', '\u{11938}', L), ('\u{1193b}',
- '\u{1193c}', NSM), ('\u{1193d}', '\u{1193d}', L), ('\u{1193e}', '\u{1193e}', NSM), ('\u{1193f}',
- '\u{11942}', L), ('\u{11943}', '\u{11943}', NSM), ('\u{11944}', '\u{11946}', L), ('\u{11950}',
- '\u{11959}', L), ('\u{119a0}', '\u{119a7}', L), ('\u{119aa}', '\u{119d3}', L), ('\u{119d4}',
- '\u{119d7}', NSM), ('\u{119da}', '\u{119db}', NSM), ('\u{119dc}', '\u{119df}', L), ('\u{119e0}',
- '\u{119e0}', NSM), ('\u{119e1}', '\u{119e4}', L), ('\u{11a00}', '\u{11a00}', L), ('\u{11a01}',
- '\u{11a06}', NSM), ('\u{11a07}', '\u{11a08}', L), ('\u{11a09}', '\u{11a0a}', NSM), ('\u{11a0b}',
- '\u{11a32}', L), ('\u{11a33}', '\u{11a38}', NSM), ('\u{11a39}', '\u{11a3a}', L), ('\u{11a3b}',
- '\u{11a3e}', NSM), ('\u{11a3f}', '\u{11a46}', L), ('\u{11a47}', '\u{11a47}', NSM), ('\u{11a50}',
- '\u{11a50}', L), ('\u{11a51}', '\u{11a56}', NSM), ('\u{11a57}', '\u{11a58}', L), ('\u{11a59}',
- '\u{11a5b}', NSM), ('\u{11a5c}', '\u{11a89}', L), ('\u{11a8a}', '\u{11a96}', NSM), ('\u{11a97}',
- '\u{11a97}', L), ('\u{11a98}', '\u{11a99}', NSM), ('\u{11a9a}', '\u{11aa2}', L), ('\u{11ab0}',
- '\u{11af8}', L), ('\u{11c00}', '\u{11c08}', L), ('\u{11c0a}', '\u{11c2f}', L), ('\u{11c30}',
- '\u{11c36}', NSM), ('\u{11c38}', '\u{11c3d}', NSM), ('\u{11c3e}', '\u{11c45}', L), ('\u{11c50}',
- '\u{11c6c}', L), ('\u{11c70}', '\u{11c8f}', L), ('\u{11c92}', '\u{11ca7}', NSM), ('\u{11ca9}',
- '\u{11ca9}', L), ('\u{11caa}', '\u{11cb0}', NSM), ('\u{11cb1}', '\u{11cb1}', L), ('\u{11cb2}',
- '\u{11cb3}', NSM), ('\u{11cb4}', '\u{11cb4}', L), ('\u{11cb5}', '\u{11cb6}', NSM), ('\u{11d00}',
- '\u{11d06}', L), ('\u{11d08}', '\u{11d09}', L), ('\u{11d0b}', '\u{11d30}', L), ('\u{11d31}',
- '\u{11d36}', NSM), ('\u{11d3a}', '\u{11d3a}', NSM), ('\u{11d3c}', '\u{11d3d}', NSM),
- ('\u{11d3f}', '\u{11d45}', NSM), ('\u{11d46}', '\u{11d46}', L), ('\u{11d47}', '\u{11d47}', NSM),
- ('\u{11d50}', '\u{11d59}', L), ('\u{11d60}', '\u{11d65}', L), ('\u{11d67}', '\u{11d68}', L),
- ('\u{11d6a}', '\u{11d8e}', L), ('\u{11d90}', '\u{11d91}', NSM), ('\u{11d93}', '\u{11d94}', L),
- ('\u{11d95}', '\u{11d95}', NSM), ('\u{11d96}', '\u{11d96}', L), ('\u{11d97}', '\u{11d97}', NSM),
- ('\u{11d98}', '\u{11d98}', L), ('\u{11da0}', '\u{11da9}', L), ('\u{11ee0}', '\u{11ef2}', L),
- ('\u{11ef3}', '\u{11ef4}', NSM), ('\u{11ef5}', '\u{11ef8}', L), ('\u{11fb0}', '\u{11fb0}', L),
- ('\u{11fc0}', '\u{11fd4}', L), ('\u{11fd5}', '\u{11fdc}', ON), ('\u{11fdd}', '\u{11fe0}', ET),
- ('\u{11fe1}', '\u{11ff1}', ON), ('\u{11fff}', '\u{12399}', L), ('\u{12400}', '\u{1246e}', L),
- ('\u{12470}', '\u{12474}', L), ('\u{12480}', '\u{12543}', L), ('\u{12f90}', '\u{12ff2}', L),
- ('\u{13000}', '\u{1342e}', L), ('\u{13430}', '\u{13438}', L), ('\u{14400}', '\u{14646}', L),
- ('\u{16800}', '\u{16a38}', L), ('\u{16a40}', '\u{16a5e}', L), ('\u{16a60}', '\u{16a69}', L),
- ('\u{16a6e}', '\u{16abe}', L), ('\u{16ac0}', '\u{16ac9}', L), ('\u{16ad0}', '\u{16aed}', L),
- ('\u{16af0}', '\u{16af4}', NSM), ('\u{16af5}', '\u{16af5}', L), ('\u{16b00}', '\u{16b2f}', L),
- ('\u{16b30}', '\u{16b36}', NSM), ('\u{16b37}', '\u{16b45}', L), ('\u{16b50}', '\u{16b59}', L),
- ('\u{16b5b}', '\u{16b61}', L), ('\u{16b63}', '\u{16b77}', L), ('\u{16b7d}', '\u{16b8f}', L),
- ('\u{16e40}', '\u{16e9a}', L), ('\u{16f00}', '\u{16f4a}', L), ('\u{16f4f}', '\u{16f4f}', NSM),
- ('\u{16f50}', '\u{16f87}', L), ('\u{16f8f}', '\u{16f92}', NSM), ('\u{16f93}', '\u{16f9f}', L),
- ('\u{16fe0}', '\u{16fe1}', L), ('\u{16fe2}', '\u{16fe2}', ON), ('\u{16fe3}', '\u{16fe3}', L),
- ('\u{16fe4}', '\u{16fe4}', NSM), ('\u{16ff0}', '\u{16ff1}', L), ('\u{17000}', '\u{187f7}', L),
- ('\u{18800}', '\u{18cd5}', L), ('\u{18d00}', '\u{18d08}', L), ('\u{1aff0}', '\u{1aff3}', L),
- ('\u{1aff5}', '\u{1affb}', L), ('\u{1affd}', '\u{1affe}', L), ('\u{1b000}', '\u{1b122}', L),
- ('\u{1b150}', '\u{1b152}', L), ('\u{1b164}', '\u{1b167}', L), ('\u{1b170}', '\u{1b2fb}', L),
+ '\u{10efc}', R), ('\u{10efd}', '\u{10eff}', NSM), ('\u{10f00}', '\u{10f2f}', R), ('\u{10f30}',
+ '\u{10f45}', AL), ('\u{10f46}', '\u{10f50}', NSM), ('\u{10f51}', '\u{10f59}', AL), ('\u{10f5a}',
+ '\u{10f81}', R), ('\u{10f82}', '\u{10f85}', NSM), ('\u{10f86}', '\u{10fff}', R), ('\u{11000}',
+ '\u{11000}', L), ('\u{11001}', '\u{11001}', NSM), ('\u{11002}', '\u{11037}', L), ('\u{11038}',
+ '\u{11046}', NSM), ('\u{11047}', '\u{1104d}', L), ('\u{11052}', '\u{11065}', ON), ('\u{11066}',
+ '\u{1106f}', L), ('\u{11070}', '\u{11070}', NSM), ('\u{11071}', '\u{11072}', L), ('\u{11073}',
+ '\u{11074}', NSM), ('\u{11075}', '\u{11075}', L), ('\u{1107f}', '\u{11081}', NSM), ('\u{11082}',
+ '\u{110b2}', L), ('\u{110b3}', '\u{110b6}', NSM), ('\u{110b7}', '\u{110b8}', L), ('\u{110b9}',
+ '\u{110ba}', NSM), ('\u{110bb}', '\u{110c1}', L), ('\u{110c2}', '\u{110c2}', NSM), ('\u{110cd}',
+ '\u{110cd}', L), ('\u{110d0}', '\u{110e8}', L), ('\u{110f0}', '\u{110f9}', L), ('\u{11100}',
+ '\u{11102}', NSM), ('\u{11103}', '\u{11126}', L), ('\u{11127}', '\u{1112b}', NSM), ('\u{1112c}',
+ '\u{1112c}', L), ('\u{1112d}', '\u{11134}', NSM), ('\u{11136}', '\u{11147}', L), ('\u{11150}',
+ '\u{11172}', L), ('\u{11173}', '\u{11173}', NSM), ('\u{11174}', '\u{11176}', L), ('\u{11180}',
+ '\u{11181}', NSM), ('\u{11182}', '\u{111b5}', L), ('\u{111b6}', '\u{111be}', NSM), ('\u{111bf}',
+ '\u{111c8}', L), ('\u{111c9}', '\u{111cc}', NSM), ('\u{111cd}', '\u{111ce}', L), ('\u{111cf}',
+ '\u{111cf}', NSM), ('\u{111d0}', '\u{111df}', L), ('\u{111e1}', '\u{111f4}', L), ('\u{11200}',
+ '\u{11211}', L), ('\u{11213}', '\u{1122e}', L), ('\u{1122f}', '\u{11231}', NSM), ('\u{11232}',
+ '\u{11233}', L), ('\u{11234}', '\u{11234}', NSM), ('\u{11235}', '\u{11235}', L), ('\u{11236}',
+ '\u{11237}', NSM), ('\u{11238}', '\u{1123d}', L), ('\u{1123e}', '\u{1123e}', NSM), ('\u{1123f}',
+ '\u{11240}', L), ('\u{11241}', '\u{11241}', NSM), ('\u{11280}', '\u{11286}', L), ('\u{11288}',
+ '\u{11288}', L), ('\u{1128a}', '\u{1128d}', L), ('\u{1128f}', '\u{1129d}', L), ('\u{1129f}',
+ '\u{112a9}', L), ('\u{112b0}', '\u{112de}', L), ('\u{112df}', '\u{112df}', NSM), ('\u{112e0}',
+ '\u{112e2}', L), ('\u{112e3}', '\u{112ea}', NSM), ('\u{112f0}', '\u{112f9}', L), ('\u{11300}',
+ '\u{11301}', NSM), ('\u{11302}', '\u{11303}', L), ('\u{11305}', '\u{1130c}', L), ('\u{1130f}',
+ '\u{11310}', L), ('\u{11313}', '\u{11328}', L), ('\u{1132a}', '\u{11330}', L), ('\u{11332}',
+ '\u{11333}', L), ('\u{11335}', '\u{11339}', L), ('\u{1133b}', '\u{1133c}', NSM), ('\u{1133d}',
+ '\u{1133f}', L), ('\u{11340}', '\u{11340}', NSM), ('\u{11341}', '\u{11344}', L), ('\u{11347}',
+ '\u{11348}', L), ('\u{1134b}', '\u{1134d}', L), ('\u{11350}', '\u{11350}', L), ('\u{11357}',
+ '\u{11357}', L), ('\u{1135d}', '\u{11363}', L), ('\u{11366}', '\u{1136c}', NSM), ('\u{11370}',
+ '\u{11374}', NSM), ('\u{11400}', '\u{11437}', L), ('\u{11438}', '\u{1143f}', NSM), ('\u{11440}',
+ '\u{11441}', L), ('\u{11442}', '\u{11444}', NSM), ('\u{11445}', '\u{11445}', L), ('\u{11446}',
+ '\u{11446}', NSM), ('\u{11447}', '\u{1145b}', L), ('\u{1145d}', '\u{1145d}', L), ('\u{1145e}',
+ '\u{1145e}', NSM), ('\u{1145f}', '\u{11461}', L), ('\u{11480}', '\u{114b2}', L), ('\u{114b3}',
+ '\u{114b8}', NSM), ('\u{114b9}', '\u{114b9}', L), ('\u{114ba}', '\u{114ba}', NSM), ('\u{114bb}',
+ '\u{114be}', L), ('\u{114bf}', '\u{114c0}', NSM), ('\u{114c1}', '\u{114c1}', L), ('\u{114c2}',
+ '\u{114c3}', NSM), ('\u{114c4}', '\u{114c7}', L), ('\u{114d0}', '\u{114d9}', L), ('\u{11580}',
+ '\u{115b1}', L), ('\u{115b2}', '\u{115b5}', NSM), ('\u{115b8}', '\u{115bb}', L), ('\u{115bc}',
+ '\u{115bd}', NSM), ('\u{115be}', '\u{115be}', L), ('\u{115bf}', '\u{115c0}', NSM), ('\u{115c1}',
+ '\u{115db}', L), ('\u{115dc}', '\u{115dd}', NSM), ('\u{11600}', '\u{11632}', L), ('\u{11633}',
+ '\u{1163a}', NSM), ('\u{1163b}', '\u{1163c}', L), ('\u{1163d}', '\u{1163d}', NSM), ('\u{1163e}',
+ '\u{1163e}', L), ('\u{1163f}', '\u{11640}', NSM), ('\u{11641}', '\u{11644}', L), ('\u{11650}',
+ '\u{11659}', L), ('\u{11660}', '\u{1166c}', ON), ('\u{11680}', '\u{116aa}', L), ('\u{116ab}',
+ '\u{116ab}', NSM), ('\u{116ac}', '\u{116ac}', L), ('\u{116ad}', '\u{116ad}', NSM), ('\u{116ae}',
+ '\u{116af}', L), ('\u{116b0}', '\u{116b5}', NSM), ('\u{116b6}', '\u{116b6}', L), ('\u{116b7}',
+ '\u{116b7}', NSM), ('\u{116b8}', '\u{116b9}', L), ('\u{116c0}', '\u{116c9}', L), ('\u{11700}',
+ '\u{1171a}', L), ('\u{1171d}', '\u{1171f}', NSM), ('\u{11720}', '\u{11721}', L), ('\u{11722}',
+ '\u{11725}', NSM), ('\u{11726}', '\u{11726}', L), ('\u{11727}', '\u{1172b}', NSM), ('\u{11730}',
+ '\u{11746}', L), ('\u{11800}', '\u{1182e}', L), ('\u{1182f}', '\u{11837}', NSM), ('\u{11838}',
+ '\u{11838}', L), ('\u{11839}', '\u{1183a}', NSM), ('\u{1183b}', '\u{1183b}', L), ('\u{118a0}',
+ '\u{118f2}', L), ('\u{118ff}', '\u{11906}', L), ('\u{11909}', '\u{11909}', L), ('\u{1190c}',
+ '\u{11913}', L), ('\u{11915}', '\u{11916}', L), ('\u{11918}', '\u{11935}', L), ('\u{11937}',
+ '\u{11938}', L), ('\u{1193b}', '\u{1193c}', NSM), ('\u{1193d}', '\u{1193d}', L), ('\u{1193e}',
+ '\u{1193e}', NSM), ('\u{1193f}', '\u{11942}', L), ('\u{11943}', '\u{11943}', NSM), ('\u{11944}',
+ '\u{11946}', L), ('\u{11950}', '\u{11959}', L), ('\u{119a0}', '\u{119a7}', L), ('\u{119aa}',
+ '\u{119d3}', L), ('\u{119d4}', '\u{119d7}', NSM), ('\u{119da}', '\u{119db}', NSM), ('\u{119dc}',
+ '\u{119df}', L), ('\u{119e0}', '\u{119e0}', NSM), ('\u{119e1}', '\u{119e4}', L), ('\u{11a00}',
+ '\u{11a00}', L), ('\u{11a01}', '\u{11a06}', NSM), ('\u{11a07}', '\u{11a08}', L), ('\u{11a09}',
+ '\u{11a0a}', NSM), ('\u{11a0b}', '\u{11a32}', L), ('\u{11a33}', '\u{11a38}', NSM), ('\u{11a39}',
+ '\u{11a3a}', L), ('\u{11a3b}', '\u{11a3e}', NSM), ('\u{11a3f}', '\u{11a46}', L), ('\u{11a47}',
+ '\u{11a47}', NSM), ('\u{11a50}', '\u{11a50}', L), ('\u{11a51}', '\u{11a56}', NSM), ('\u{11a57}',
+ '\u{11a58}', L), ('\u{11a59}', '\u{11a5b}', NSM), ('\u{11a5c}', '\u{11a89}', L), ('\u{11a8a}',
+ '\u{11a96}', NSM), ('\u{11a97}', '\u{11a97}', L), ('\u{11a98}', '\u{11a99}', NSM), ('\u{11a9a}',
+ '\u{11aa2}', L), ('\u{11ab0}', '\u{11af8}', L), ('\u{11b00}', '\u{11b09}', L), ('\u{11c00}',
+ '\u{11c08}', L), ('\u{11c0a}', '\u{11c2f}', L), ('\u{11c30}', '\u{11c36}', NSM), ('\u{11c38}',
+ '\u{11c3d}', NSM), ('\u{11c3e}', '\u{11c45}', L), ('\u{11c50}', '\u{11c6c}', L), ('\u{11c70}',
+ '\u{11c8f}', L), ('\u{11c92}', '\u{11ca7}', NSM), ('\u{11ca9}', '\u{11ca9}', L), ('\u{11caa}',
+ '\u{11cb0}', NSM), ('\u{11cb1}', '\u{11cb1}', L), ('\u{11cb2}', '\u{11cb3}', NSM), ('\u{11cb4}',
+ '\u{11cb4}', L), ('\u{11cb5}', '\u{11cb6}', NSM), ('\u{11d00}', '\u{11d06}', L), ('\u{11d08}',
+ '\u{11d09}', L), ('\u{11d0b}', '\u{11d30}', L), ('\u{11d31}', '\u{11d36}', NSM), ('\u{11d3a}',
+ '\u{11d3a}', NSM), ('\u{11d3c}', '\u{11d3d}', NSM), ('\u{11d3f}', '\u{11d45}', NSM),
+ ('\u{11d46}', '\u{11d46}', L), ('\u{11d47}', '\u{11d47}', NSM), ('\u{11d50}', '\u{11d59}', L),
+ ('\u{11d60}', '\u{11d65}', L), ('\u{11d67}', '\u{11d68}', L), ('\u{11d6a}', '\u{11d8e}', L),
+ ('\u{11d90}', '\u{11d91}', NSM), ('\u{11d93}', '\u{11d94}', L), ('\u{11d95}', '\u{11d95}', NSM),
+ ('\u{11d96}', '\u{11d96}', L), ('\u{11d97}', '\u{11d97}', NSM), ('\u{11d98}', '\u{11d98}', L),
+ ('\u{11da0}', '\u{11da9}', L), ('\u{11ee0}', '\u{11ef2}', L), ('\u{11ef3}', '\u{11ef4}', NSM),
+ ('\u{11ef5}', '\u{11ef8}', L), ('\u{11f00}', '\u{11f01}', NSM), ('\u{11f02}', '\u{11f10}', L),
+ ('\u{11f12}', '\u{11f35}', L), ('\u{11f36}', '\u{11f3a}', NSM), ('\u{11f3e}', '\u{11f3f}', L),
+ ('\u{11f40}', '\u{11f40}', NSM), ('\u{11f41}', '\u{11f41}', L), ('\u{11f42}', '\u{11f42}', NSM),
+ ('\u{11f43}', '\u{11f59}', L), ('\u{11fb0}', '\u{11fb0}', L), ('\u{11fc0}', '\u{11fd4}', L),
+ ('\u{11fd5}', '\u{11fdc}', ON), ('\u{11fdd}', '\u{11fe0}', ET), ('\u{11fe1}', '\u{11ff1}', ON),
+ ('\u{11fff}', '\u{12399}', L), ('\u{12400}', '\u{1246e}', L), ('\u{12470}', '\u{12474}', L),
+ ('\u{12480}', '\u{12543}', L), ('\u{12f90}', '\u{12ff2}', L), ('\u{13000}', '\u{1343f}', L),
+ ('\u{13440}', '\u{13440}', NSM), ('\u{13441}', '\u{13446}', L), ('\u{13447}', '\u{13455}', NSM),
+ ('\u{14400}', '\u{14646}', L), ('\u{16800}', '\u{16a38}', L), ('\u{16a40}', '\u{16a5e}', L),
+ ('\u{16a60}', '\u{16a69}', L), ('\u{16a6e}', '\u{16abe}', L), ('\u{16ac0}', '\u{16ac9}', L),
+ ('\u{16ad0}', '\u{16aed}', L), ('\u{16af0}', '\u{16af4}', NSM), ('\u{16af5}', '\u{16af5}', L),
+ ('\u{16b00}', '\u{16b2f}', L), ('\u{16b30}', '\u{16b36}', NSM), ('\u{16b37}', '\u{16b45}', L),
+ ('\u{16b50}', '\u{16b59}', L), ('\u{16b5b}', '\u{16b61}', L), ('\u{16b63}', '\u{16b77}', L),
+ ('\u{16b7d}', '\u{16b8f}', L), ('\u{16e40}', '\u{16e9a}', L), ('\u{16f00}', '\u{16f4a}', L),
+ ('\u{16f4f}', '\u{16f4f}', NSM), ('\u{16f50}', '\u{16f87}', L), ('\u{16f8f}', '\u{16f92}', NSM),
+ ('\u{16f93}', '\u{16f9f}', L), ('\u{16fe0}', '\u{16fe1}', L), ('\u{16fe2}', '\u{16fe2}', ON),
+ ('\u{16fe3}', '\u{16fe3}', L), ('\u{16fe4}', '\u{16fe4}', NSM), ('\u{16ff0}', '\u{16ff1}', L),
+ ('\u{17000}', '\u{187f7}', L), ('\u{18800}', '\u{18cd5}', L), ('\u{18d00}', '\u{18d08}', L),
+ ('\u{1aff0}', '\u{1aff3}', L), ('\u{1aff5}', '\u{1affb}', L), ('\u{1affd}', '\u{1affe}', L),
+ ('\u{1b000}', '\u{1b122}', L), ('\u{1b132}', '\u{1b132}', L), ('\u{1b150}', '\u{1b152}', L),
+ ('\u{1b155}', '\u{1b155}', L), ('\u{1b164}', '\u{1b167}', L), ('\u{1b170}', '\u{1b2fb}', L),
('\u{1bc00}', '\u{1bc6a}', L), ('\u{1bc70}', '\u{1bc7c}', L), ('\u{1bc80}', '\u{1bc88}', L),
('\u{1bc90}', '\u{1bc99}', L), ('\u{1bc9c}', '\u{1bc9c}', L), ('\u{1bc9d}', '\u{1bc9e}', NSM),
('\u{1bc9f}', '\u{1bc9f}', L), ('\u{1bca0}', '\u{1bca3}', BN), ('\u{1cf00}', '\u{1cf2d}', NSM),
@@ -460,51 +466,78 @@ pub const bidi_class_table: &'static [(char, char, BidiClass)] = &[
('\u{1d183}', '\u{1d184}', L), ('\u{1d185}', '\u{1d18b}', NSM), ('\u{1d18c}', '\u{1d1a9}', L),
('\u{1d1aa}', '\u{1d1ad}', NSM), ('\u{1d1ae}', '\u{1d1e8}', L), ('\u{1d1e9}', '\u{1d1ea}', ON),
('\u{1d200}', '\u{1d241}', ON), ('\u{1d242}', '\u{1d244}', NSM), ('\u{1d245}', '\u{1d245}', ON),
- ('\u{1d2e0}', '\u{1d2f3}', L), ('\u{1d300}', '\u{1d356}', ON), ('\u{1d360}', '\u{1d378}', L),
- ('\u{1d400}', '\u{1d454}', L), ('\u{1d456}', '\u{1d49c}', L), ('\u{1d49e}', '\u{1d49f}', L),
- ('\u{1d4a2}', '\u{1d4a2}', L), ('\u{1d4a5}', '\u{1d4a6}', L), ('\u{1d4a9}', '\u{1d4ac}', L),
- ('\u{1d4ae}', '\u{1d4b9}', L), ('\u{1d4bb}', '\u{1d4bb}', L), ('\u{1d4bd}', '\u{1d4c3}', L),
- ('\u{1d4c5}', '\u{1d505}', L), ('\u{1d507}', '\u{1d50a}', L), ('\u{1d50d}', '\u{1d514}', L),
- ('\u{1d516}', '\u{1d51c}', L), ('\u{1d51e}', '\u{1d539}', L), ('\u{1d53b}', '\u{1d53e}', L),
- ('\u{1d540}', '\u{1d544}', L), ('\u{1d546}', '\u{1d546}', L), ('\u{1d54a}', '\u{1d550}', L),
- ('\u{1d552}', '\u{1d6a5}', L), ('\u{1d6a8}', '\u{1d6da}', L), ('\u{1d6db}', '\u{1d6db}', ON),
- ('\u{1d6dc}', '\u{1d714}', L), ('\u{1d715}', '\u{1d715}', ON), ('\u{1d716}', '\u{1d74e}', L),
- ('\u{1d74f}', '\u{1d74f}', ON), ('\u{1d750}', '\u{1d788}', L), ('\u{1d789}', '\u{1d789}', ON),
- ('\u{1d78a}', '\u{1d7c2}', L), ('\u{1d7c3}', '\u{1d7c3}', ON), ('\u{1d7c4}', '\u{1d7cb}', L),
- ('\u{1d7ce}', '\u{1d7ff}', EN), ('\u{1d800}', '\u{1d9ff}', L), ('\u{1da00}', '\u{1da36}', NSM),
- ('\u{1da37}', '\u{1da3a}', L), ('\u{1da3b}', '\u{1da6c}', NSM), ('\u{1da6d}', '\u{1da74}', L),
- ('\u{1da75}', '\u{1da75}', NSM), ('\u{1da76}', '\u{1da83}', L), ('\u{1da84}', '\u{1da84}', NSM),
- ('\u{1da85}', '\u{1da8b}', L), ('\u{1da9b}', '\u{1da9f}', NSM), ('\u{1daa1}', '\u{1daaf}', NSM),
- ('\u{1df00}', '\u{1df1e}', L), ('\u{1e000}', '\u{1e006}', NSM), ('\u{1e008}', '\u{1e018}', NSM),
- ('\u{1e01b}', '\u{1e021}', NSM), ('\u{1e023}', '\u{1e024}', NSM), ('\u{1e026}', '\u{1e02a}',
- NSM), ('\u{1e100}', '\u{1e12c}', L), ('\u{1e130}', '\u{1e136}', NSM), ('\u{1e137}', '\u{1e13d}',
- L), ('\u{1e140}', '\u{1e149}', L), ('\u{1e14e}', '\u{1e14f}', L), ('\u{1e290}', '\u{1e2ad}', L),
- ('\u{1e2ae}', '\u{1e2ae}', NSM), ('\u{1e2c0}', '\u{1e2eb}', L), ('\u{1e2ec}', '\u{1e2ef}', NSM),
- ('\u{1e2f0}', '\u{1e2f9}', L), ('\u{1e2ff}', '\u{1e2ff}', ET), ('\u{1e7e0}', '\u{1e7e6}', L),
- ('\u{1e7e8}', '\u{1e7eb}', L), ('\u{1e7ed}', '\u{1e7ee}', L), ('\u{1e7f0}', '\u{1e7fe}', L),
- ('\u{1e800}', '\u{1e8cf}', R), ('\u{1e8d0}', '\u{1e8d6}', NSM), ('\u{1e8d7}', '\u{1e943}', R),
- ('\u{1e944}', '\u{1e94a}', NSM), ('\u{1e94b}', '\u{1ec70}', R), ('\u{1ec71}', '\u{1ecb4}', AL),
- ('\u{1ecb5}', '\u{1ed00}', R), ('\u{1ed01}', '\u{1ed3d}', AL), ('\u{1ed3e}', '\u{1edff}', R),
- ('\u{1ee00}', '\u{1eeef}', AL), ('\u{1eef0}', '\u{1eef1}', ON), ('\u{1eef2}', '\u{1eeff}', AL),
- ('\u{1ef00}', '\u{1efff}', R), ('\u{1f000}', '\u{1f02b}', ON), ('\u{1f030}', '\u{1f093}', ON),
- ('\u{1f0a0}', '\u{1f0ae}', ON), ('\u{1f0b1}', '\u{1f0bf}', ON), ('\u{1f0c1}', '\u{1f0cf}', ON),
- ('\u{1f0d1}', '\u{1f0f5}', ON), ('\u{1f100}', '\u{1f10a}', EN), ('\u{1f10b}', '\u{1f10f}', ON),
- ('\u{1f110}', '\u{1f12e}', L), ('\u{1f12f}', '\u{1f12f}', ON), ('\u{1f130}', '\u{1f169}', L),
- ('\u{1f16a}', '\u{1f16f}', ON), ('\u{1f170}', '\u{1f1ac}', L), ('\u{1f1ad}', '\u{1f1ad}', ON),
- ('\u{1f1e6}', '\u{1f202}', L), ('\u{1f210}', '\u{1f23b}', L), ('\u{1f240}', '\u{1f248}', L),
- ('\u{1f250}', '\u{1f251}', L), ('\u{1f260}', '\u{1f265}', ON), ('\u{1f300}', '\u{1f6d7}', ON),
- ('\u{1f6dd}', '\u{1f6ec}', ON), ('\u{1f6f0}', '\u{1f6fc}', ON), ('\u{1f700}', '\u{1f773}', ON),
- ('\u{1f780}', '\u{1f7d8}', ON), ('\u{1f7e0}', '\u{1f7eb}', ON), ('\u{1f7f0}', '\u{1f7f0}', ON),
- ('\u{1f800}', '\u{1f80b}', ON), ('\u{1f810}', '\u{1f847}', ON), ('\u{1f850}', '\u{1f859}', ON),
- ('\u{1f860}', '\u{1f887}', ON), ('\u{1f890}', '\u{1f8ad}', ON), ('\u{1f8b0}', '\u{1f8b1}', ON),
- ('\u{1f900}', '\u{1fa53}', ON), ('\u{1fa60}', '\u{1fa6d}', ON), ('\u{1fa70}', '\u{1fa74}', ON),
- ('\u{1fa78}', '\u{1fa7c}', ON), ('\u{1fa80}', '\u{1fa86}', ON), ('\u{1fa90}', '\u{1faac}', ON),
- ('\u{1fab0}', '\u{1faba}', ON), ('\u{1fac0}', '\u{1fac5}', ON), ('\u{1fad0}', '\u{1fad9}', ON),
- ('\u{1fae0}', '\u{1fae7}', ON), ('\u{1faf0}', '\u{1faf6}', ON), ('\u{1fb00}', '\u{1fb92}', ON),
- ('\u{1fb94}', '\u{1fbca}', ON), ('\u{1fbf0}', '\u{1fbf9}', EN), ('\u{20000}', '\u{2a6df}', L),
- ('\u{2a700}', '\u{2b738}', L), ('\u{2b740}', '\u{2b81d}', L), ('\u{2b820}', '\u{2cea1}', L),
- ('\u{2ceb0}', '\u{2ebe0}', L), ('\u{2f800}', '\u{2fa1d}', L), ('\u{30000}', '\u{3134a}', L),
- ('\u{e0001}', '\u{e0001}', BN), ('\u{e0020}', '\u{e007f}', BN), ('\u{e0100}', '\u{e01ef}', NSM),
- ('\u{f0000}', '\u{ffffd}', L), ('\u{100000}', '\u{10fffd}', L)
+ ('\u{1d2c0}', '\u{1d2d3}', L), ('\u{1d2e0}', '\u{1d2f3}', L), ('\u{1d300}', '\u{1d356}', ON),
+ ('\u{1d360}', '\u{1d378}', L), ('\u{1d400}', '\u{1d454}', L), ('\u{1d456}', '\u{1d49c}', L),
+ ('\u{1d49e}', '\u{1d49f}', L), ('\u{1d4a2}', '\u{1d4a2}', L), ('\u{1d4a5}', '\u{1d4a6}', L),
+ ('\u{1d4a9}', '\u{1d4ac}', L), ('\u{1d4ae}', '\u{1d4b9}', L), ('\u{1d4bb}', '\u{1d4bb}', L),
+ ('\u{1d4bd}', '\u{1d4c3}', L), ('\u{1d4c5}', '\u{1d505}', L), ('\u{1d507}', '\u{1d50a}', L),
+ ('\u{1d50d}', '\u{1d514}', L), ('\u{1d516}', '\u{1d51c}', L), ('\u{1d51e}', '\u{1d539}', L),
+ ('\u{1d53b}', '\u{1d53e}', L), ('\u{1d540}', '\u{1d544}', L), ('\u{1d546}', '\u{1d546}', L),
+ ('\u{1d54a}', '\u{1d550}', L), ('\u{1d552}', '\u{1d6a5}', L), ('\u{1d6a8}', '\u{1d6da}', L),
+ ('\u{1d6db}', '\u{1d6db}', ON), ('\u{1d6dc}', '\u{1d714}', L), ('\u{1d715}', '\u{1d715}', ON),
+ ('\u{1d716}', '\u{1d74e}', L), ('\u{1d74f}', '\u{1d74f}', ON), ('\u{1d750}', '\u{1d788}', L),
+ ('\u{1d789}', '\u{1d789}', ON), ('\u{1d78a}', '\u{1d7c2}', L), ('\u{1d7c3}', '\u{1d7c3}', ON),
+ ('\u{1d7c4}', '\u{1d7cb}', L), ('\u{1d7ce}', '\u{1d7ff}', EN), ('\u{1d800}', '\u{1d9ff}', L),
+ ('\u{1da00}', '\u{1da36}', NSM), ('\u{1da37}', '\u{1da3a}', L), ('\u{1da3b}', '\u{1da6c}', NSM),
+ ('\u{1da6d}', '\u{1da74}', L), ('\u{1da75}', '\u{1da75}', NSM), ('\u{1da76}', '\u{1da83}', L),
+ ('\u{1da84}', '\u{1da84}', NSM), ('\u{1da85}', '\u{1da8b}', L), ('\u{1da9b}', '\u{1da9f}', NSM),
+ ('\u{1daa1}', '\u{1daaf}', NSM), ('\u{1df00}', '\u{1df1e}', L), ('\u{1df25}', '\u{1df2a}', L),
+ ('\u{1e000}', '\u{1e006}', NSM), ('\u{1e008}', '\u{1e018}', NSM), ('\u{1e01b}', '\u{1e021}',
+ NSM), ('\u{1e023}', '\u{1e024}', NSM), ('\u{1e026}', '\u{1e02a}', NSM), ('\u{1e030}',
+ '\u{1e06d}', L), ('\u{1e08f}', '\u{1e08f}', NSM), ('\u{1e100}', '\u{1e12c}', L), ('\u{1e130}',
+ '\u{1e136}', NSM), ('\u{1e137}', '\u{1e13d}', L), ('\u{1e140}', '\u{1e149}', L), ('\u{1e14e}',
+ '\u{1e14f}', L), ('\u{1e290}', '\u{1e2ad}', L), ('\u{1e2ae}', '\u{1e2ae}', NSM), ('\u{1e2c0}',
+ '\u{1e2eb}', L), ('\u{1e2ec}', '\u{1e2ef}', NSM), ('\u{1e2f0}', '\u{1e2f9}', L), ('\u{1e2ff}',
+ '\u{1e2ff}', ET), ('\u{1e4d0}', '\u{1e4eb}', L), ('\u{1e4ec}', '\u{1e4ef}', NSM), ('\u{1e4f0}',
+ '\u{1e4f9}', L), ('\u{1e7e0}', '\u{1e7e6}', L), ('\u{1e7e8}', '\u{1e7eb}', L), ('\u{1e7ed}',
+ '\u{1e7ee}', L), ('\u{1e7f0}', '\u{1e7fe}', L), ('\u{1e800}', '\u{1e8cf}', R), ('\u{1e8d0}',
+ '\u{1e8d6}', NSM), ('\u{1e8d7}', '\u{1e943}', R), ('\u{1e944}', '\u{1e94a}', NSM), ('\u{1e94b}',
+ '\u{1ec70}', R), ('\u{1ec71}', '\u{1ecb4}', AL), ('\u{1ecb5}', '\u{1ed00}', R), ('\u{1ed01}',
+ '\u{1ed3d}', AL), ('\u{1ed3e}', '\u{1edff}', R), ('\u{1ee00}', '\u{1eeef}', AL), ('\u{1eef0}',
+ '\u{1eef1}', ON), ('\u{1eef2}', '\u{1eeff}', AL), ('\u{1ef00}', '\u{1efff}', R), ('\u{1f000}',
+ '\u{1f02b}', ON), ('\u{1f030}', '\u{1f093}', ON), ('\u{1f0a0}', '\u{1f0ae}', ON), ('\u{1f0b1}',
+ '\u{1f0bf}', ON), ('\u{1f0c1}', '\u{1f0cf}', ON), ('\u{1f0d1}', '\u{1f0f5}', ON), ('\u{1f100}',
+ '\u{1f10a}', EN), ('\u{1f10b}', '\u{1f10f}', ON), ('\u{1f110}', '\u{1f12e}', L), ('\u{1f12f}',
+ '\u{1f12f}', ON), ('\u{1f130}', '\u{1f169}', L), ('\u{1f16a}', '\u{1f16f}', ON), ('\u{1f170}',
+ '\u{1f1ac}', L), ('\u{1f1ad}', '\u{1f1ad}', ON), ('\u{1f1e6}', '\u{1f202}', L), ('\u{1f210}',
+ '\u{1f23b}', L), ('\u{1f240}', '\u{1f248}', L), ('\u{1f250}', '\u{1f251}', L), ('\u{1f260}',
+ '\u{1f265}', ON), ('\u{1f300}', '\u{1f6d7}', ON), ('\u{1f6dc}', '\u{1f6ec}', ON), ('\u{1f6f0}',
+ '\u{1f6fc}', ON), ('\u{1f700}', '\u{1f776}', ON), ('\u{1f77b}', '\u{1f7d9}', ON), ('\u{1f7e0}',
+ '\u{1f7eb}', ON), ('\u{1f7f0}', '\u{1f7f0}', ON), ('\u{1f800}', '\u{1f80b}', ON), ('\u{1f810}',
+ '\u{1f847}', ON), ('\u{1f850}', '\u{1f859}', ON), ('\u{1f860}', '\u{1f887}', ON), ('\u{1f890}',
+ '\u{1f8ad}', ON), ('\u{1f8b0}', '\u{1f8b1}', ON), ('\u{1f900}', '\u{1fa53}', ON), ('\u{1fa60}',
+ '\u{1fa6d}', ON), ('\u{1fa70}', '\u{1fa7c}', ON), ('\u{1fa80}', '\u{1fa88}', ON), ('\u{1fa90}',
+ '\u{1fabd}', ON), ('\u{1fabf}', '\u{1fac5}', ON), ('\u{1face}', '\u{1fadb}', ON), ('\u{1fae0}',
+ '\u{1fae8}', ON), ('\u{1faf0}', '\u{1faf8}', ON), ('\u{1fb00}', '\u{1fb92}', ON), ('\u{1fb94}',
+ '\u{1fbca}', ON), ('\u{1fbf0}', '\u{1fbf9}', EN), ('\u{20000}', '\u{2a6df}', L), ('\u{2a700}',
+ '\u{2b739}', L), ('\u{2b740}', '\u{2b81d}', L), ('\u{2b820}', '\u{2cea1}', L), ('\u{2ceb0}',
+ '\u{2ebe0}', L), ('\u{2f800}', '\u{2fa1d}', L), ('\u{30000}', '\u{3134a}', L), ('\u{31350}',
+ '\u{323af}', L), ('\u{e0001}', '\u{e0001}', BN), ('\u{e0020}', '\u{e007f}', BN), ('\u{e0100}',
+ '\u{e01ef}', NSM), ('\u{f0000}', '\u{ffffd}', L), ('\u{100000}', '\u{10fffd}', L)
+];
+
+pub const bidi_pairs_table: &'static [(char, char, Option<char>)] = &[
+ ('\u{28}', '\u{29}', None), ('\u{5b}', '\u{5d}', None), ('\u{7b}', '\u{7d}', None), ('\u{f3a}',
+ '\u{f3b}', None), ('\u{f3c}', '\u{f3d}', None), ('\u{169b}', '\u{169c}', None), ('\u{2045}',
+ '\u{2046}', None), ('\u{207d}', '\u{207e}', None), ('\u{208d}', '\u{208e}', None), ('\u{2308}',
+ '\u{2309}', None), ('\u{230a}', '\u{230b}', None), ('\u{2329}', '\u{232a}', Some('\u{3008}')),
+ ('\u{2768}', '\u{2769}', None), ('\u{276a}', '\u{276b}', None), ('\u{276c}', '\u{276d}', None),
+ ('\u{276e}', '\u{276f}', None), ('\u{2770}', '\u{2771}', None), ('\u{2772}', '\u{2773}', None),
+ ('\u{2774}', '\u{2775}', None), ('\u{27c5}', '\u{27c6}', None), ('\u{27e6}', '\u{27e7}', None),
+ ('\u{27e8}', '\u{27e9}', None), ('\u{27ea}', '\u{27eb}', None), ('\u{27ec}', '\u{27ed}', None),
+ ('\u{27ee}', '\u{27ef}', None), ('\u{2983}', '\u{2984}', None), ('\u{2985}', '\u{2986}', None),
+ ('\u{2987}', '\u{2988}', None), ('\u{2989}', '\u{298a}', None), ('\u{298b}', '\u{298c}', None),
+ ('\u{298d}', '\u{2990}', None), ('\u{298f}', '\u{298e}', None), ('\u{2991}', '\u{2992}', None),
+ ('\u{2993}', '\u{2994}', None), ('\u{2995}', '\u{2996}', None), ('\u{2997}', '\u{2998}', None),
+ ('\u{29d8}', '\u{29d9}', None), ('\u{29da}', '\u{29db}', None), ('\u{29fc}', '\u{29fd}', None),
+ ('\u{2e22}', '\u{2e23}', None), ('\u{2e24}', '\u{2e25}', None), ('\u{2e26}', '\u{2e27}', None),
+ ('\u{2e28}', '\u{2e29}', None), ('\u{2e55}', '\u{2e56}', None), ('\u{2e57}', '\u{2e58}', None),
+ ('\u{2e59}', '\u{2e5a}', None), ('\u{2e5b}', '\u{2e5c}', None), ('\u{3008}', '\u{3009}', None),
+ ('\u{300a}', '\u{300b}', None), ('\u{300c}', '\u{300d}', None), ('\u{300e}', '\u{300f}', None),
+ ('\u{3010}', '\u{3011}', None), ('\u{3014}', '\u{3015}', None), ('\u{3016}', '\u{3017}', None),
+ ('\u{3018}', '\u{3019}', None), ('\u{301a}', '\u{301b}', None), ('\u{fe59}', '\u{fe5a}', None),
+ ('\u{fe5b}', '\u{fe5c}', None), ('\u{fe5d}', '\u{fe5e}', None), ('\u{ff08}', '\u{ff09}', None),
+ ('\u{ff3b}', '\u{ff3d}', None), ('\u{ff5b}', '\u{ff5d}', None), ('\u{ff5f}', '\u{ff60}', None),
+ ('\u{ff62}', '\u{ff63}', None)
];
diff --git a/src/data_source.rs b/src/data_source.rs
index 3958a25..319ad53 100644
--- a/src/data_source.rs
+++ b/src/data_source.rs
@@ -9,8 +9,38 @@
use crate::BidiClass;
+/// This is the return value of [`BidiDataSource::bidi_matched_opening_bracket()`].
+///
+/// It represents the matching *normalized* opening bracket for a given bracket in a bracket pair,
+/// and whether or not that bracket is opening.
+#[derive(Debug, Copy, Clone)]
+pub struct BidiMatchedOpeningBracket {
+ /// The corresponding opening bracket in this bracket pair, normalized
+ ///
+ /// In case of opening brackets, this will be the bracket itself, except for when the bracket
+ /// is not normalized, in which case it will be the normalized form.
+ pub opening: char,
+ /// Whether or not the requested bracket was an opening bracket. True for opening
+ pub is_open: bool,
+}
+
/// This trait abstracts over a data source that is able to produce the Unicode Bidi class for a given
/// character
pub trait BidiDataSource {
fn bidi_class(&self, c: char) -> BidiClass;
+ /// If this character is a bracket according to BidiBrackets.txt,
+ /// return the corresponding *normalized* *opening bracket* of the pair,
+ /// and whether or not it itself is an opening bracket.
+ ///
+ /// This effectively buckets brackets into equivalence classes keyed on the
+ /// normalized opening bracket.
+ ///
+ /// The default implementation will pull in a small amount of hardcoded data,
+ /// regardless of the `hardcoded-data` feature. This is in part for convenience
+ /// (since this data is small and changes less often), and in part so that this method can be
+ /// added without needing a breaking version bump.
+ /// Override this method in your custom data source to prevent the use of hardcoded data.
+ fn bidi_matched_opening_bracket(&self, c: char) -> Option<BidiMatchedOpeningBracket> {
+ crate::char_data::bidi_matched_opening_bracket(c)
+ }
}
diff --git a/src/explicit.rs b/src/explicit.rs
index e9d579f..a9b13e8 100644
--- a/src/explicit.rs
+++ b/src/explicit.rs
@@ -47,13 +47,17 @@ pub fn compute(
RLE | LRE | RLO | LRO | RLI | LRI | FSI => {
let last_level = stack.last().level;
+ // <https://www.unicode.org/reports/tr9/#Retaining_Explicit_Formatting_Characters>
+ levels[i] = last_level;
+
// X5a-X5c: Isolate initiators get the level of the last entry on the stack.
let is_isolate = match original_classes[i] {
RLI | LRI | FSI => true,
_ => false,
};
if is_isolate {
- levels[i] = last_level;
+ // Redundant due to "Retaining explicit formatting characters" step.
+ // levels[i] = last_level;
match stack.last().status {
OverrideStatus::RTL => processing_classes[i] = R,
OverrideStatus::LTR => processing_classes[i] = L,
@@ -90,6 +94,13 @@ pub fn compute(
} else if overflow_isolate_count == 0 {
overflow_embedding_count += 1;
}
+
+ if !is_isolate {
+ // X9 +
+ // <https://www.unicode.org/reports/tr9/#Retaining_Explicit_Formatting_Characters>
+ // (PDF handled below)
+ processing_classes[i] = BN;
+ }
}
// <http://www.unicode.org/reports/tr9/#X6a>
@@ -123,31 +134,34 @@ pub fn compute(
// <http://www.unicode.org/reports/tr9/#X7>
PDF => {
if overflow_isolate_count > 0 {
- continue;
- }
- if overflow_embedding_count > 0 {
+ // do nothing
+ } else if overflow_embedding_count > 0 {
overflow_embedding_count -= 1;
- continue;
- }
- if stack.last().status != OverrideStatus::Isolate && stack.vec.len() >= 2 {
+ } else if stack.last().status != OverrideStatus::Isolate && stack.vec.len() >= 2 {
stack.vec.pop();
}
- // The spec doesn't explicitly mention this step, but it is necessary.
- // See the reference implementations for comparison.
+ // <https://www.unicode.org/reports/tr9/#Retaining_Explicit_Formatting_Characters>
levels[i] = stack.last().level;
+ // X9 part of retaining explicit formatting characters.
+ processing_classes[i] = BN;
}
- // Nothing
- B | BN => {}
+ // Nothing.
+ // BN case moved down to X6, see <https://www.unicode.org/reports/tr9/#Retaining_Explicit_Formatting_Characters>
+ B => {}
// <http://www.unicode.org/reports/tr9/#X6>
_ => {
let last = stack.last();
levels[i] = last.level;
- match last.status {
- OverrideStatus::RTL => processing_classes[i] = R,
- OverrideStatus::LTR => processing_classes[i] = L,
- _ => {}
+ // This condition is not in the spec, but I am pretty sure that is a spec bug.
+ // https://www.unicode.org/L2/L2023/23014-amd-to-uax9.pdf
+ if original_classes[i] != BN {
+ match last.status {
+ OverrideStatus::RTL => processing_classes[i] = R,
+ OverrideStatus::LTR => processing_classes[i] = L,
+ _ => {}
+ }
}
}
}
diff --git a/src/implicit.rs b/src/implicit.rs
index bf37f17..294af7c 100644
--- a/src/implicit.rs
+++ b/src/implicit.rs
@@ -14,104 +14,213 @@ use core::cmp::max;
use super::char_data::BidiClass::{self, *};
use super::level::Level;
-use super::prepare::{not_removed_by_x9, removed_by_x9, IsolatingRunSequence, LevelRun};
+use super::prepare::{not_removed_by_x9, removed_by_x9, IsolatingRunSequence};
+use super::BidiDataSource;
/// 3.3.4 Resolving Weak Types
///
/// <http://www.unicode.org/reports/tr9/#Resolving_Weak_Types>
#[cfg_attr(feature = "flame_it", flamer::flame)]
-pub fn resolve_weak(sequence: &IsolatingRunSequence, processing_classes: &mut [BidiClass]) {
- // FIXME (#8): This function applies steps W1-W6 in a single pass. This can produce
- // incorrect results in cases where a "later" rule changes the value of `prev_class` seen
- // by an "earlier" rule. We should either split this into separate passes, or preserve
- // extra state so each rule can see the correct previous class.
-
- // FIXME: Also, this could be the cause of increased failure for using longer-UTF-8 chars in
- // conformance tests, like BidiTest:69635 (AL ET EN)
+pub fn resolve_weak(
+ text: &str,
+ sequence: &IsolatingRunSequence,
+ processing_classes: &mut [BidiClass],
+) {
+ // Note: The spec treats these steps as individual passes that are applied one after the other
+ // on the entire IsolatingRunSequence at once. We instead collapse it into a single iteration,
+ // which is straightforward for rules that are based on the state of the current character, but not
+ // for rules that care about surrounding characters. To deal with them, we retain additional state
+ // about previous character classes that may have since been changed by later rules.
- let mut prev_class = sequence.sos;
+ // The previous class for the purposes of rule W4/W6, not tracking changes made after or during W4.
+ let mut prev_class_before_w4 = sequence.sos;
+ // The previous class for the purposes of rule W5.
+ let mut prev_class_before_w5 = sequence.sos;
+ // The previous class for the purposes of rule W1, not tracking changes from any other rules.
+ let mut prev_class_before_w1 = sequence.sos;
let mut last_strong_is_al = false;
let mut et_run_indices = Vec::new(); // for W5
+ let mut bn_run_indices = Vec::new(); // for W5 + <https://www.unicode.org/reports/tr9/#Retaining_Explicit_Formatting_Characters>
+
+ for (run_index, level_run) in sequence.runs.iter().enumerate() {
+ for i in &mut level_run.clone() {
+ if processing_classes[i] == BN {
+ // <https://www.unicode.org/reports/tr9/#Retaining_Explicit_Formatting_Characters>
+ // Keeps track of bn runs for W5 in case we see an ET.
+ bn_run_indices.push(i);
+ // BNs aren't real, skip over them.
+ continue;
+ }
+
+ // Store the processing class of all rules before W2/W1.
+ // Used to keep track of the last strong character for W2. W3 is able to insert new strong
+ // characters, so we don't want to be misled by it.
+ let mut w2_processing_class = processing_classes[i];
- // Like sequence.runs.iter().flat_map(Clone::clone), but make indices itself clonable.
- fn id(x: LevelRun) -> LevelRun {
- x
- }
- let mut indices = sequence
- .runs
- .iter()
- .cloned()
- .flat_map(id as fn(LevelRun) -> LevelRun);
-
- while let Some(i) = indices.next() {
- match processing_classes[i] {
// <http://www.unicode.org/reports/tr9/#W1>
- NSM => {
- processing_classes[i] = match prev_class {
+ //
+
+ if processing_classes[i] == NSM {
+ processing_classes[i] = match prev_class_before_w1 {
RLI | LRI | FSI | PDI => ON,
- _ => prev_class,
+ _ => prev_class_before_w1,
};
+ // W1 occurs before W2, update this.
+ w2_processing_class = processing_classes[i];
}
- EN => {
- if last_strong_is_al {
- // W2. If previous strong char was AL, change EN to AN.
- processing_classes[i] = AN;
- } else {
+
+ prev_class_before_w1 = processing_classes[i];
+
+ // <http://www.unicode.org/reports/tr9/#W2>
+ // <http://www.unicode.org/reports/tr9/#W3>
+ //
+ match processing_classes[i] {
+ EN => {
+ if last_strong_is_al {
+ // W2. If previous strong char was AL, change EN to AN.
+ processing_classes[i] = AN;
+ }
+ }
+ // W3.
+ AL => processing_classes[i] = R,
+ _ => {}
+ }
+
+ // update last_strong_is_al.
+ match w2_processing_class {
+ L | R => {
+ last_strong_is_al = false;
+ }
+ AL => {
+ last_strong_is_al = true;
+ }
+ _ => {}
+ }
+
+ let class_before_w456 = processing_classes[i];
+
+ // <http://www.unicode.org/reports/tr9/#W4>
+ // <http://www.unicode.org/reports/tr9/#W5>
+ // <http://www.unicode.org/reports/tr9/#W6> (separators only)
+ // (see below for W6 terminator code)
+ //
+ match processing_classes[i] {
+ // <http://www.unicode.org/reports/tr9/#W6>
+ EN => {
// W5. If a run of ETs is adjacent to an EN, change the ETs to EN.
for j in &et_run_indices {
processing_classes[*j] = EN;
}
et_run_indices.clear();
}
- }
- // <http://www.unicode.org/reports/tr9/#W3>
- AL => processing_classes[i] = R,
- // <http://www.unicode.org/reports/tr9/#W4>
- ES | CS => {
- let next_class = indices
- .clone()
- .map(|j| processing_classes[j])
- .find(not_removed_by_x9)
- .unwrap_or(sequence.eos);
- processing_classes[i] = match (prev_class, processing_classes[i], next_class) {
- (EN, ES, EN) | (EN, CS, EN) => EN,
- (AN, CS, AN) => AN,
- (_, _, _) => ON,
+ // <http://www.unicode.org/reports/tr9/#W4>
+ // <http://www.unicode.org/reports/tr9/#W6>
+ ES | CS => {
+ // See https://github.com/servo/unicode-bidi/issues/86 for improving this.
+ // We want to make sure we check the correct next character by skipping past the rest
+ // of this one.
+ if let Some(ch) = text.get(i..).and_then(|s| s.chars().next()) {
+ let mut next_class = sequence
+ .iter_forwards_from(i + ch.len_utf8(), run_index)
+ .map(|j| processing_classes[j])
+ // <https://www.unicode.org/reports/tr9/#Retaining_Explicit_Formatting_Characters>
+ .find(not_removed_by_x9)
+ .unwrap_or(sequence.eos);
+ if next_class == EN && last_strong_is_al {
+ // Apply W2 to next_class. We know that last_strong_is_al
+ // has no chance of changing on this character so we can still assume its value
+ // will be the same by the time we get to it.
+ next_class = AN;
+ }
+ processing_classes[i] =
+ match (prev_class_before_w4, processing_classes[i], next_class) {
+ // W4
+ (EN, ES, EN) | (EN, CS, EN) => EN,
+ // W4
+ (AN, CS, AN) => AN,
+ // W6 (separators only)
+ (_, _, _) => ON,
+ };
+
+ // W6 + <https://www.unicode.org/reports/tr9/#Retaining_Explicit_Formatting_Characters>
+ // We have to do this before W5 gets its grubby hands on these characters and thinks
+ // they're part of an ET run.
+ // We check for ON to ensure that we had hit the W6 branch above, since this `ES | CS` match
+ // arm handles both W4 and W6.
+ if processing_classes[i] == ON {
+ for idx in sequence.iter_backwards_from(i, run_index) {
+ let class = &mut processing_classes[idx];
+ if *class != BN {
+ break;
+ }
+ *class = ON;
+ }
+ for idx in sequence.iter_forwards_from(i + ch.len_utf8(), run_index) {
+ let class = &mut processing_classes[idx];
+ if *class != BN {
+ break;
+ }
+ *class = ON;
+ }
+ }
+ } else {
+ // We're in the middle of a character, copy over work done for previous bytes
+ // since it's going to be the same answer.
+ processing_classes[i] = processing_classes[i - 1];
+ }
}
- }
- // <http://www.unicode.org/reports/tr9/#W5>
- ET => {
- match prev_class {
- EN => processing_classes[i] = EN,
- _ => et_run_indices.push(i), // In case this is followed by an EN.
+ // <http://www.unicode.org/reports/tr9/#W5>
+ ET => {
+ match prev_class_before_w5 {
+ EN => processing_classes[i] = EN,
+ _ => {
+ // <https://www.unicode.org/reports/tr9/#Retaining_Explicit_Formatting_Characters>
+ // If there was a BN run before this, that's now a part of this ET run.
+ et_run_indices.extend(&bn_run_indices);
+
+ // In case this is followed by an EN.
+ et_run_indices.push(i);
+ }
+ }
}
+ _ => {}
}
- class => {
- if removed_by_x9(class) {
- continue;
+
+ // Common loop iteration code
+ //
+
+ // <https://www.unicode.org/reports/tr9/#Retaining_Explicit_Formatting_Characters>
+ // BN runs would have already continued the loop, clear them before we get to the next one.
+ bn_run_indices.clear();
+
+ // W6 above only deals with separators, so it doesn't change anything W5 cares about,
+ // so we still can update this after running that part of W6.
+ prev_class_before_w5 = processing_classes[i];
+
+ // <http://www.unicode.org/reports/tr9/#W6> (terminators only)
+ // (see above for W6 separator code)
+ //
+ if prev_class_before_w5 != ET {
+ // W6. If we didn't find an adjacent EN, turn any ETs into ON instead.
+ for j in &et_run_indices {
+ processing_classes[*j] = ON;
}
+ et_run_indices.clear();
}
- }
- prev_class = processing_classes[i];
- match prev_class {
- L | R => {
- last_strong_is_al = false;
- }
- AL => {
- last_strong_is_al = true;
- }
- _ => {}
- }
- if prev_class != ET {
- // W6. If we didn't find an adjacent EN, turn any ETs into ON instead.
- for j in &et_run_indices {
- processing_classes[*j] = ON;
- }
- et_run_indices.clear();
+ // We stashed this before W4/5/6 could get their grubby hands on it, and it's not
+ // used in the W6 terminator code below so we can update it now.
+ prev_class_before_w4 = class_before_w456;
}
}
+ // Rerun this check in case we ended with a sequence of BNs (i.e., we'd never
+ // hit the end of the for loop above).
+ // W6. If we didn't find an adjacent EN, turn any ETs into ON instead.
+ for j in &et_run_indices {
+ processing_classes[*j] = ON;
+ }
+ et_run_indices.clear();
// W7. If the previous strong char was L, change EN to L.
let mut last_strong_is_l = sequence.sos == L;
@@ -127,6 +236,8 @@ pub fn resolve_weak(sequence: &IsolatingRunSequence, processing_classes: &mut [B
R | AL => {
last_strong_is_l = false;
}
+ // <https://www.unicode.org/reports/tr9/#Retaining_Explicit_Formatting_Characters>
+ // Already scanning past BN here.
_ => {}
}
}
@@ -137,22 +248,172 @@ pub fn resolve_weak(sequence: &IsolatingRunSequence, processing_classes: &mut [B
///
/// <http://www.unicode.org/reports/tr9/#Resolving_Neutral_Types>
#[cfg_attr(feature = "flame_it", flamer::flame)]
-pub fn resolve_neutral(
+pub fn resolve_neutral<D: BidiDataSource>(
+ text: &str,
+ data_source: &D,
sequence: &IsolatingRunSequence,
levels: &[Level],
+ original_classes: &[BidiClass],
processing_classes: &mut [BidiClass],
) {
+ // e = embedding direction
let e: BidiClass = levels[sequence.runs[0].start].bidi_class();
+ let not_e = if e == BidiClass::L {
+ BidiClass::R
+ } else {
+ BidiClass::L
+ };
+ // N0. Process bracket pairs.
+
+ // > Identify the bracket pairs in the current isolating run sequence according to BD16.
+ // We use processing_classes, not original_classes, due to BD14/BD15
+ let bracket_pairs = identify_bracket_pairs(text, data_source, sequence, processing_classes);
+
+ // > For each bracket-pair element in the list of pairs of text positions
+ //
+ // Note: Rust ranges are interpreted as [start..end), be careful using `pair` directly
+ // for indexing as it will include the opening bracket pair but not the closing one.
+ for pair in bracket_pairs {
+ #[cfg(feature = "std")]
+ debug_assert!(
+ pair.start < processing_classes.len(),
+ "identify_bracket_pairs returned a range that is out of bounds!"
+ );
+ #[cfg(feature = "std")]
+ debug_assert!(
+ pair.end < processing_classes.len(),
+ "identify_bracket_pairs returned a range that is out of bounds!"
+ );
+ let mut found_e = false;
+ let mut found_not_e = false;
+ let mut class_to_set = None;
+
+ let start_len_utf8 = text[pair.start..].chars().next().unwrap().len_utf8();
+ // > Inspect the bidirectional types of the characters enclosed within the bracket pair.
+ //
+ // `pair` is [start, end) so we will end up processing the opening character but not the closing one.
+ //
+ for enclosed_i in sequence.iter_forwards_from(pair.start + start_len_utf8, pair.start_run) {
+ if enclosed_i >= pair.end {
+ #[cfg(feature = "std")]
+ debug_assert!(
+ enclosed_i == pair.end,
+ "If we skipped past this, the iterator is broken"
+ );
+ break;
+ }
+ let class = processing_classes[enclosed_i];
+ if class == e {
+ found_e = true;
+ } else if class == not_e {
+ found_not_e = true;
+ } else if class == BidiClass::EN || class == BidiClass::AN {
+ // > Within this scope, bidirectional types EN and AN are treated as R.
+ if e == BidiClass::L {
+ found_not_e = true;
+ } else {
+ found_e = true;
+ }
+ }
+
+ // If we have found a character with the class of the embedding direction
+ // we can bail early.
+ if found_e {
+ break;
+ }
+ }
+ // > If any strong type (either L or R) matching the embedding direction is found
+ if found_e {
+ // > .. set the type for both brackets in the pair to match the embedding direction
+ class_to_set = Some(e);
+ // > Otherwise, if there is a strong type it must be opposite the embedding direction
+ } else if found_not_e {
+ // > Therefore, test for an established context with a preceding strong type by
+ // > checking backwards before the opening paired bracket
+ // > until the first strong type (L, R, or sos) is found.
+ // (see note above about processing_classes and character boundaries)
+ let mut previous_strong = sequence
+ .iter_backwards_from(pair.start, pair.start_run)
+ .map(|i| processing_classes[i])
+ .find(|class| {
+ *class == BidiClass::L
+ || *class == BidiClass::R
+ || *class == BidiClass::EN
+ || *class == BidiClass::AN
+ })
+ .unwrap_or(sequence.sos);
+
+ // > Within this scope, bidirectional types EN and AN are treated as R.
+ if previous_strong == BidiClass::EN || previous_strong == BidiClass::AN {
+ previous_strong = BidiClass::R;
+ }
+
+ // > If the preceding strong type is also opposite the embedding direction,
+ // > context is established,
+ // > so set the type for both brackets in the pair to that direction.
+ // AND
+ // > Otherwise set the type for both brackets in the pair to the embedding direction.
+ // > Either way it gets set to previous_strong
+ //
+ // Both branches amount to setting the type to the strong type.
+ class_to_set = Some(previous_strong);
+ }
+
+ if let Some(class_to_set) = class_to_set {
+ // Update all processing classes corresponding to the start and end elements, as requested.
+ // We should include all bytes of the character, not the first one.
+ let end_len_utf8 = text[pair.end..].chars().next().unwrap().len_utf8();
+ for class in &mut processing_classes[pair.start..pair.start + start_len_utf8] {
+ *class = class_to_set;
+ }
+ for class in &mut processing_classes[pair.end..pair.end + end_len_utf8] {
+ *class = class_to_set;
+ }
+ // <https://www.unicode.org/reports/tr9/#Retaining_Explicit_Formatting_Characters>
+ for idx in sequence.iter_backwards_from(pair.start, pair.start_run) {
+ let class = &mut processing_classes[idx];
+ if *class != BN {
+ break;
+ }
+ *class = class_to_set;
+ }
+ // > Any number of characters that had original bidirectional character type NSM prior to the application of
+ // > W1 that immediately follow a paired bracket which changed to L or R under N0 should change to match the type of their preceding bracket.
+
+ // This rule deals with sequences of NSMs, so we can just update them all at once, we don't need to worry
+ // about character boundaries. We do need to be careful to skip the full set of bytes for the parentheses characters.
+ let nsm_start = pair.start + start_len_utf8;
+ for idx in sequence.iter_forwards_from(nsm_start, pair.start_run) {
+ let class = original_classes[idx];
+ if class == BidiClass::NSM || processing_classes[idx] == BN {
+ processing_classes[idx] = class_to_set;
+ } else {
+ break;
+ }
+ }
+ let nsm_end = pair.end + end_len_utf8;
+ for idx in sequence.iter_forwards_from(nsm_end, pair.end_run) {
+ let class = original_classes[idx];
+ if class == BidiClass::NSM || processing_classes[idx] == BN {
+ processing_classes[idx] = class_to_set;
+ } else {
+ break;
+ }
+ }
+ }
+ // > Otherwise, there are no strong types within the bracket pair
+ // > Therefore, do not set the type for that bracket pair
+ }
+
+ // N1 and N2.
+ // Indices of every byte in this isolating run sequence
let mut indices = sequence.runs.iter().flat_map(Clone::clone);
let mut prev_class = sequence.sos;
-
while let Some(mut i) = indices.next() {
- // N0. Process bracket pairs.
- // TODO
-
// Process sequences of NI characters.
let mut ni_run = Vec::new();
- if is_NI(processing_classes[i]) {
+ // The BN is for <https://www.unicode.org/reports/tr9/#Retaining_Explicit_Formatting_Characters>
+ if is_NI(processing_classes[i]) || processing_classes[i] == BN {
// Consume a run of consecutive NI characters.
ni_run.push(i);
let mut next_class;
@@ -160,11 +421,9 @@ pub fn resolve_neutral(
match indices.next() {
Some(j) => {
i = j;
- if removed_by_x9(processing_classes[i]) {
- continue;
- }
next_class = processing_classes[j];
- if is_NI(next_class) {
+ // The BN is for <https://www.unicode.org/reports/tr9/#Retaining_Explicit_Formatting_Characters>
+ if is_NI(next_class) || next_class == BN {
ni_run.push(i);
} else {
break;
@@ -176,7 +435,6 @@ pub fn resolve_neutral(
}
};
}
-
// N1-N2.
//
// <http://www.unicode.org/reports/tr9/#N1>
@@ -203,6 +461,105 @@ pub fn resolve_neutral(
}
}
+struct BracketPair {
+ /// The text-relative index of the opening bracket.
+ start: usize,
+ /// The text-relative index of the closing bracket.
+ end: usize,
+ /// The index of the run (in the run sequence) that the opening bracket is in.
+ start_run: usize,
+ /// The index of the run (in the run sequence) that the closing bracket is in.
+ end_run: usize,
+}
+/// 3.1.3 Identifying Bracket Pairs
+///
+/// Returns all paired brackets in the source, as indices into the
+/// text source.
+///
+/// <https://www.unicode.org/reports/tr9/#BD16>
+fn identify_bracket_pairs<D: BidiDataSource>(
+ text: &str,
+ data_source: &D,
+ run_sequence: &IsolatingRunSequence,
+ original_classes: &[BidiClass],
+) -> Vec<BracketPair> {
+ let mut ret = vec![];
+ let mut stack = vec![];
+
+ for (run_index, level_run) in run_sequence.runs.iter().enumerate() {
+ let slice = if let Some(slice) = text.get(level_run.clone()) {
+ slice
+ } else {
+ #[cfg(feature = "std")]
+ std::debug_assert!(
+ false,
+ "Found broken indices in level run: found indices {}..{} for string of length {}",
+ level_run.start,
+ level_run.end,
+ text.len()
+ );
+ return ret;
+ };
+
+ for (i, ch) in slice.char_indices() {
+ let actual_index = level_run.start + i;
+ // All paren characters are ON.
+ // From BidiBrackets.txt:
+ // > The Unicode property value stability policy guarantees that characters
+ // > which have bpt=o or bpt=c also have bc=ON and Bidi_M=Y
+ if original_classes[level_run.start + i] != BidiClass::ON {
+ continue;
+ }
+
+ if let Some(matched) = data_source.bidi_matched_opening_bracket(ch) {
+ if matched.is_open {
+ // > If an opening paired bracket is found ...
+
+ // > ... and there is no room in the stack,
+ // > stop processing BD16 for the remainder of the isolating run sequence.
+ if stack.len() >= 63 {
+ break;
+ }
+ // > ... push its Bidi_Paired_Bracket property value and its text position onto the stack
+ stack.push((matched.opening, actual_index, run_index))
+ } else {
+ // > If a closing paired bracket is found, do the following
+
+ // > Declare a variable that holds a reference to the current stack element
+ // > and initialize it with the top element of the stack.
+ // AND
+ // > Else, if the current stack element is not at the bottom of the stack
+ for (stack_index, element) in stack.iter().enumerate().rev() {
+ // > Compare the closing paired bracket being inspected or its canonical
+ // > equivalent to the bracket in the current stack element.
+ if element.0 == matched.opening {
+ // > If the values match, meaning the two characters form a bracket pair, then
+
+ // > Append the text position in the current stack element together with the
+ // > text position of the closing paired bracket to the list.
+ let pair = BracketPair {
+ start: element.1,
+ end: actual_index,
+ start_run: element.2,
+ end_run: run_index,
+ };
+ ret.push(pair);
+
+ // > Pop the stack through the current stack element inclusively.
+ stack.truncate(stack_index);
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ // > Sort the list of pairs of text positions in ascending order based on
+ // > the text position of the opening paired bracket.
+ ret.sort_by_key(|r| r.start);
+ ret
+}
+
/// 3.3.6 Resolving Implicit Levels
///
/// Returns the maximum embedding level in the paragraph.
@@ -211,7 +568,6 @@ pub fn resolve_neutral(
#[cfg_attr(feature = "flame_it", flamer::flame)]
pub fn resolve_levels(original_classes: &[BidiClass], levels: &mut [Level]) -> Level {
let mut max_level = Level::ltr();
-
assert_eq!(original_classes.len(), levels.len());
for i in 0..levels.len() {
match (levels[i].is_rtl(), original_classes[i]) {
@@ -219,6 +575,7 @@ pub fn resolve_levels(original_classes: &[BidiClass], levels: &mut [Level]) -> L
(false, R) | (true, L) | (true, EN) | (true, AN) => {
levels[i].raise(1).expect("Level number error")
}
+ // <https://www.unicode.org/reports/tr9/#Retaining_Explicit_Formatting_Characters> handled here
(_, _) => {}
}
max_level = max(max_level, levels[i]);
diff --git a/src/lib.rs b/src/lib.rs
index bda9dd8..81d4fb5 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -354,8 +354,15 @@ impl<'text> BidiInfo<'text> {
let sequences = prepare::isolating_run_sequences(para.level, original_classes, levels);
for sequence in &sequences {
- implicit::resolve_weak(sequence, processing_classes);
- implicit::resolve_neutral(sequence, levels, processing_classes);
+ implicit::resolve_weak(text, sequence, processing_classes);
+ implicit::resolve_neutral(
+ text,
+ data_source,
+ sequence,
+ levels,
+ original_classes,
+ processing_classes,
+ );
}
implicit::resolve_levels(processing_classes, levels);
@@ -411,6 +418,71 @@ impl<'text> BidiInfo<'text> {
result.into()
}
+ /// Reorders pre-calculated levels of a sequence of characters.
+ ///
+ /// NOTE: This is a convenience method that does not use a `Paragraph` object. It is
+ /// intended to be used when an application has determined the levels of the objects (character sequences)
+ /// and just needs to have them reordered.
+ ///
+ /// the index map will result in `indexMap[visualIndex]==logicalIndex`.
+ ///
+ /// # # Example
+ /// ```
+ /// use unicode_bidi::BidiInfo;
+ /// use unicode_bidi::Level;
+ ///
+ /// let l0 = Level::from(0);
+ /// let l1 = Level::from(1);
+ /// let l2 = Level::from(2);
+ ///
+ /// let levels = vec![l0, l0, l0, l0];
+ /// let index_map = BidiInfo::reorder_visual(&levels);
+ /// assert_eq!(levels.len(), index_map.len());
+ /// assert_eq!(index_map, [0, 1, 2, 3]);
+ ///
+ /// let levels: Vec<Level> = vec![l0, l0, l0, l1, l1, l1, l2, l2];
+ /// let index_map = BidiInfo::reorder_visual(&levels);
+ /// assert_eq!(levels.len(), index_map.len());
+ /// assert_eq!(index_map, [0, 1, 2, 5, 4, 3, 6, 7]);
+ /// ```
+ pub fn reorder_visual(levels: &[Level]) -> Vec<usize> {
+ // Gets the next range
+ fn next_range(levels: &[level::Level], start_index: usize) -> Range<usize> {
+ if levels.is_empty() || start_index >= levels.len() {
+ return start_index..start_index;
+ }
+
+ let mut end_index = start_index + 1;
+ while end_index < levels.len() {
+ if levels[start_index] != levels[end_index] {
+ return start_index..end_index;
+ }
+ end_index += 1;
+ }
+
+ start_index..end_index
+ }
+
+ if levels.is_empty() {
+ return vec![];
+ }
+ let mut result: Vec<usize> = (0..levels.len()).collect();
+
+ let mut range: Range<usize> = 0..0;
+ loop {
+ range = next_range(levels, range.end);
+ if levels[range.start].is_rtl() {
+ result[range.clone()].reverse();
+ }
+
+ if range.end >= levels.len() {
+ break;
+ }
+ }
+
+ result
+ }
+
/// Find the level runs within a line and return them in visual order.
///
/// `line` is a range of bytes indices within `levels`.
@@ -434,10 +506,9 @@ impl<'text> BidiInfo<'text> {
let line_str: &str = &self.text[line.clone()];
let mut reset_from: Option<usize> = Some(0);
let mut reset_to: Option<usize> = None;
+ let mut prev_level = para.level;
for (i, c) in line_str.char_indices() {
match line_classes[i] {
- // Ignored by X9
- RLE | LRE | RLO | LRO | PDF | BN => {}
// Segment separator, Paragraph separator
B | S => {
assert_eq!(reset_to, None);
@@ -452,6 +523,15 @@ impl<'text> BidiInfo<'text> {
reset_from = Some(i);
}
}
+ // <https://www.unicode.org/reports/tr9/#Retaining_Explicit_Formatting_Characters>
+ // same as above + set the level
+ RLE | LRE | RLO | LRO | PDF | BN => {
+ if reset_from == None {
+ reset_from = Some(i);
+ }
+ // also set the level to previous
+ line_levels[i] = prev_level;
+ }
_ => {
reset_from = None;
}
@@ -463,6 +543,7 @@ impl<'text> BidiInfo<'text> {
reset_from = None;
reset_to = None;
}
+ prev_level = line_levels[i];
}
if let Some(from) = reset_from {
for level in &mut line_levels[from..] {
@@ -874,7 +955,7 @@ mod tests {
assert_eq!(reorder_paras("א(ב)ג."), vec![".ג)ב(א"]);
// With mirrorable characters on level boundry
- assert_eq!(reorder_paras("אב(גד[&ef].)gh"), vec!["ef].)gh&[דג(בא"]);
+ assert_eq!(reorder_paras("אב(גד[&ef].)gh"), vec!["gh).]ef&[דג(בא"]);
}
fn reordered_levels_for_paras(text: &str) -> Vec<Vec<Level>> {
@@ -1023,7 +1104,7 @@ mod tests {
}
}
-#[cfg(all(feature = "serde", test))]
+#[cfg(all(feature = "serde", feature = "hardcoded-data", test))]
mod serde_tests {
use super::*;
use serde_test::{assert_tokens, Token};
diff --git a/src/prepare.rs b/src/prepare.rs
index 7b95236..21675e6 100644
--- a/src/prepare.rs
+++ b/src/prepare.rs
@@ -89,12 +89,35 @@ pub fn isolating_run_sequences(
.map(|sequence: Vec<LevelRun>| {
assert!(!sequence.is_empty());
- let start_of_seq = sequence[0].start;
- let end_of_seq = sequence[sequence.len() - 1].end;
- let seq_level = levels[start_of_seq];
+ let mut result = IsolatingRunSequence {
+ runs: sequence,
+ sos: L,
+ eos: L,
+ };
+
+ let start_of_seq = result.runs[0].start;
+ let runs_len = result.runs.len();
+ let end_of_seq = result.runs[runs_len - 1].end;
+
+ // > (not counting characters removed by X9)
+ let seq_level = result
+ .iter_forwards_from(start_of_seq, 0)
+ .filter(|i| not_removed_by_x9(&original_classes[*i]))
+ .map(|i| levels[i])
+ .next()
+ .unwrap_or(levels[start_of_seq]);
+
+ // XXXManishearth the spec talks of a start and end level,
+ // but for a given IRS the two should be equivalent, yes?
+ let end_level = result
+ .iter_backwards_from(end_of_seq, runs_len - 1)
+ .filter(|i| not_removed_by_x9(&original_classes[*i]))
+ .map(|i| levels[i])
+ .next()
+ .unwrap_or(levels[end_of_seq - 1]);
#[cfg(test)]
- for run in sequence.clone() {
+ for run in result.runs.clone() {
for idx in run {
if not_removed_by_x9(&original_classes[idx]) {
assert_eq!(seq_level, levels[idx]);
@@ -111,8 +134,19 @@ pub fn isolating_run_sequences(
None => para_level,
};
+ // Get the last non-removed character to check if it is an isolate initiator.
+ // The spec calls for an unmatched one, but matched isolate initiators
+ // will never be at the end of a level run (otherwise there would be more to the run).
+ // We unwrap_or(BN) because BN marks removed classes and it won't matter for the check.
+ let last_non_removed = original_classes[..end_of_seq]
+ .iter()
+ .copied()
+ .rev()
+ .find(not_removed_by_x9)
+ .unwrap_or(BN);
+
// Get the level of the next non-removed char after the runs.
- let succ_level = if let RLI | LRI | FSI = original_classes[end_of_seq - 1] {
+ let succ_level = if let RLI | LRI | FSI = last_non_removed {
para_level
} else {
match original_classes[end_of_seq..]
@@ -124,15 +158,63 @@ pub fn isolating_run_sequences(
}
};
- IsolatingRunSequence {
- runs: sequence,
- sos: max(seq_level, pred_level).bidi_class(),
- eos: max(seq_level, succ_level).bidi_class(),
- }
+ result.sos = max(seq_level, pred_level).bidi_class();
+ result.eos = max(end_level, succ_level).bidi_class();
+ result
})
.collect()
}
+impl IsolatingRunSequence {
+ /// Returns the full range of text represented by this isolating run sequence
+ pub(crate) fn text_range(&self) -> Range<usize> {
+ if let (Some(start), Some(end)) = (self.runs.first(), self.runs.last()) {
+ start.start..end.end
+ } else {
+ return 0..0;
+ }
+ }
+
+ /// Given a text-relative position `pos` and an index of the level run it is in,
+ /// produce an iterator of all characters after and pos (`pos..`) that are in this
+ /// run sequence
+ pub(crate) fn iter_forwards_from(
+ &self,
+ pos: usize,
+ level_run_index: usize,
+ ) -> impl Iterator<Item = usize> + '_ {
+ let runs = &self.runs[level_run_index..];
+
+ // Check that it is in range
+ // (we can't use contains() since we want an inclusive range)
+ #[cfg(feature = "std")]
+ debug_assert!(runs[0].start <= pos && pos <= runs[0].end);
+
+ (pos..runs[0].end).chain(runs[1..].iter().flat_map(Clone::clone))
+ }
+
+ /// Given a text-relative position `pos` and an index of the level run it is in,
+ /// produce an iterator of all characters before and excludingpos (`..pos`) that are in this
+ /// run sequence
+ pub(crate) fn iter_backwards_from(
+ &self,
+ pos: usize,
+ level_run_index: usize,
+ ) -> impl Iterator<Item = usize> + '_ {
+ let prev_runs = &self.runs[..level_run_index];
+ let current = &self.runs[level_run_index];
+
+ // Check that it is in range
+ // (we can't use contains() since we want an inclusive range)
+ #[cfg(feature = "std")]
+ debug_assert!(current.start <= pos && pos <= current.end);
+
+ (current.start..pos)
+ .rev()
+ .chain(prev_runs.iter().rev().flat_map(Clone::clone))
+ }
+}
+
/// Finds the level runs in a paragraph.
///
/// <http://www.unicode.org/reports/tr9/#BD7>