mirror of
https://github.com/openharmony/third_party_rust_unicode-width.git
synced 2026-07-19 15:13:40 -04:00
merge master into OpenHarmony-6.0-Release
升级到0.1.14 Created-by: dragonswordy Commit-by: ljy9810 Merged-by: openharmony_ci Description: ### 一、内容说明(相关的Issue) https://gitcode.com/openharmony/third_party_rust_autocfg/issues/2 ### 二、建议测试周期和提测地址 建议测试完成时间:xxxx.xx.xx 投产上线时间:xxxx.xx.xx 提测地址:CI环境/压测环境 测试账号: ### 三、变更内容 * 3.1 关联PR列表 * 3.2 数据库和部署说明 1. 常规更新 2. 重启unicorn 3. 重启sidekiq 4. 迁移任务:是否有迁移任务,没有写 "无" 5. rake脚本:`bundle exec xxx RAILS_ENV = production`;没有写 "无" * 3.4 其他技术优化内容(做了什么,变更了什么) - 重构了 xxxx 代码 - xxxx 算法优化 * 3.5 废弃通知(什么字段、方法弃用?) * 3.6 后向不兼容变更(是否有无法向后兼容的变更?) ### 四、研发自测点(自测哪些?冒烟用例全部自测?) 自测测试结论: ### 五、测试关注点(需要提醒QA重点关注的、可能会忽略的地方) 检查点: | 需求名称 | 是否影响xx公共模块 | 是否需要xx功能 | 需求升级是否依赖其他子产品 | |------|------------|----------|---------------| | xxx | 否 | 需要 | 不需要 | | | | | | 接口测试: 性能测试: 并发测试: 其他: See merge request: openharmony/third_party_rust_unicode-width!6
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
name: build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
pull_request:
|
||||
branches: [ "master" ]
|
||||
|
||||
env:
|
||||
CARGO_INCREMENTAL: 0
|
||||
CARGO_TERM_COLOR: always
|
||||
RUST_BACKTRACE: 1
|
||||
RUSTFLAGS: -D warnings
|
||||
RUSTDOCFLAGS: -D warnings
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build
|
||||
run: cargo build --verbose
|
||||
- name: Run tests
|
||||
run: cargo test --verbose
|
||||
- name: Build docs
|
||||
run: cargo doc
|
||||
- name: Check formatting
|
||||
run: cargo fmt --check
|
||||
- name: Check clippy
|
||||
run: cargo clippy --verbose --lib --tests
|
||||
- name: Build (no default features)
|
||||
run: cargo build --verbose --no-default-features
|
||||
- name: Run tests (no default features)
|
||||
run: cargo test --verbose --no-default-features
|
||||
- name: Check clippy (no default features)
|
||||
run: cargo clippy --verbose --lib --tests --no-default-features
|
||||
|
||||
regen:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.12'
|
||||
- name: Regen
|
||||
run: rm tests/emoji-test.txt && cd scripts && python3 unicode.py
|
||||
- name: Diff
|
||||
run: git update-index --refresh && git diff-index --quiet HEAD --
|
||||
@@ -1,3 +1,6 @@
|
||||
target
|
||||
Cargo.lock
|
||||
scripts/tmp
|
||||
scripts/*.txt
|
||||
scripts/*.rs
|
||||
bench_data/*
|
||||
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
language: rust
|
||||
rust: 'nightly'
|
||||
sudo: false
|
||||
script:
|
||||
- cargo build --verbose --features bench
|
||||
- cargo test --verbose --features bench
|
||||
- cargo bench --verbose --features bench
|
||||
- cargo clean
|
||||
- cargo build --verbose
|
||||
- cargo test --verbose
|
||||
# next line is an ugly hack to fix an annoying bug where rustdoc tries to use the rustc_private unicode_width crate
|
||||
# (there is probably a better fix than this)
|
||||
- rm $(find /home/travis/.rustup -type f -name 'libunicode_width*')
|
||||
- rustdoc --test README.md -L target/debug -L target/debug/deps
|
||||
- cargo doc
|
||||
after_success: |
|
||||
[ $TRAVIS_BRANCH = master ] &&
|
||||
[ $TRAVIS_PULL_REQUEST = false ] &&
|
||||
echo '<meta http-equiv=refresh content=0;url=unicode_width/index.html>' > target/doc/index.html &&
|
||||
pip install ghp-import --user $USER &&
|
||||
$HOME/.local/bin/ghp-import -n target/doc &&
|
||||
git push -qf https://${TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git gh-pages
|
||||
env:
|
||||
global:
|
||||
secure: vHL3zrN8AF+H79jrB8OfzuPqsUHevo6ECzwqXPj2dMSqcSXEeCY/ENAfiyFg+oW8yEVP8X2BS1a/C9yvVQRLqLbm1HbZ/5vUpoggT9S0IhKqZMyAcLYXfIEUDMDQuaSdFndDaHvq8275ScgX1LRv1kcPjQoZHuaXWMH8y/Suvyo=
|
||||
notifications:
|
||||
email:
|
||||
on_success: never
|
||||
@@ -19,8 +19,8 @@ ohos_cargo_crate("lib") {
|
||||
crate_root = "src/lib.rs"
|
||||
|
||||
sources = ["src/lib.rs"]
|
||||
edition = "2015"
|
||||
cargo_pkg_version = "0.1.10"
|
||||
edition = "2021"
|
||||
cargo_pkg_version = "0.1.14"
|
||||
cargo_pkg_authors = "kwantam <kwantam@gmail.com>, Manish Goregaokar <manishsmail@gmail.com>"
|
||||
cargo_pkg_name = "unicode-width"
|
||||
}
|
||||
|
||||
+19
-8
@@ -1,21 +1,30 @@
|
||||
[package]
|
||||
|
||||
name = "unicode-width"
|
||||
version = "0.1.10"
|
||||
authors = ["kwantam <kwantam@gmail.com>", "Manish Goregaokar <manishsmail@gmail.com>"]
|
||||
version = "0.1.14"
|
||||
authors = [
|
||||
"kwantam <kwantam@gmail.com>",
|
||||
"Manish Goregaokar <manishsmail@gmail.com>",
|
||||
]
|
||||
|
||||
homepage = "https://github.com/unicode-rs/unicode-width"
|
||||
repository = "https://github.com/unicode-rs/unicode-width"
|
||||
documentation = "https://unicode-rs.github.io/unicode-width"
|
||||
license = "MIT/Apache-2.0"
|
||||
license = "MIT OR Apache-2.0"
|
||||
categories = [
|
||||
"command-line-interface",
|
||||
"internationalization",
|
||||
"no-std::no-alloc",
|
||||
"text-processing",
|
||||
]
|
||||
keywords = ["text", "width", "unicode"]
|
||||
readme = "README.md"
|
||||
description = """
|
||||
Determine displayed width of `char` and `str` types
|
||||
according to Unicode Standard Annex #11 rules.
|
||||
"""
|
||||
edition = "2021"
|
||||
|
||||
exclude = [ "target/*", "Cargo.lock" ]
|
||||
exclude = ["/.github/*"]
|
||||
|
||||
[dependencies]
|
||||
std = { version = "1.0", package = "rustc-std-workspace-std", optional = true }
|
||||
@@ -23,7 +32,9 @@ core = { version = "1.0", package = "rustc-std-workspace-core", optional = true
|
||||
compiler_builtins = { version = "0.1", optional = true }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
no_std = []
|
||||
bench = []
|
||||
cjk = []
|
||||
default = ["cjk"]
|
||||
rustc-dep-of-std = ['std', 'core', 'compiler_builtins']
|
||||
|
||||
# Legacy, now a no-op
|
||||
no_std = []
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
"Name": "unicode-width",
|
||||
"License": "Apache License V2.0, MIT",
|
||||
"License File": "LICENSE-APACHE, LICENSE-MIT",
|
||||
"Version Number": "0.1.10",
|
||||
"Version Number": "v0.1.14",
|
||||
"Owner": "fangting12@huawei.com",
|
||||
"Upstream URL": "https://github.com/unicode-rs/unicode-width",
|
||||
"Description": "A Rust library that provides support for working with Unicode character widths."
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
# unicode-width
|
||||
# `unicode-width`
|
||||
|
||||
Determine displayed width of `char` and `str` types according to
|
||||
[Unicode Standard Annex #11][UAX11] rules.
|
||||
[](https://github.com/unicode-rs/unicode-width/actions/workflows/rust.yml)
|
||||
[](https://crates.io/crates/unicode-width)
|
||||
[](https://docs.rs/unicode-width/)
|
||||
|
||||
Determine displayed width of `char` and `str` types according to [Unicode Standard Annex #11][UAX11]
|
||||
and other portions of the Unicode standard.
|
||||
|
||||
This crate is `#![no_std]`.
|
||||
|
||||
[UAX11]: http://www.unicode.org/reports/tr11/
|
||||
|
||||
[](https://travis-ci.org/unicode-rs/unicode-width)
|
||||
|
||||
[Documentation](https://unicode-rs.github.io/unicode-width/unicode_width/index.html)
|
||||
|
||||
```rust
|
||||
extern crate unicode_width;
|
||||
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
||||
fn main() {
|
||||
let teststr = "Hello, world!";
|
||||
let width = UnicodeWidthStr::width(teststr);
|
||||
let width = teststr.width();
|
||||
println!("{}", teststr);
|
||||
println!("The above string is {} columns wide.", width);
|
||||
let width = teststr.width_cjk();
|
||||
@@ -25,27 +25,26 @@ fn main() {
|
||||
```
|
||||
|
||||
**NOTE:** The computed width values may not match the actual rendered column
|
||||
width. For example, the woman scientist emoji comprises of a woman emoji, a
|
||||
zero-width joiner and a microscope emoji.
|
||||
width. For example, many Brahmic scripts like Devanagari have complex rendering rules
|
||||
which this crate does not currently handle (and will never fully handle, because
|
||||
the exact rendering depends on the font):
|
||||
|
||||
```rust
|
||||
extern crate unicode_width;
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
||||
fn main() {
|
||||
assert_eq!(UnicodeWidthStr::width("👩"), 2); // Woman
|
||||
assert_eq!(UnicodeWidthStr::width("🔬"), 2); // Microscope
|
||||
assert_eq!(UnicodeWidthStr::width("👩🔬"), 4); // Woman scientist
|
||||
assert_eq!("क".width(), 1); // Devanagari letter Ka
|
||||
assert_eq!("ष".width(), 1); // Devanagari letter Ssa
|
||||
assert_eq!("क्ष".width(), 2); // Ka + Virama + Ssa
|
||||
}
|
||||
```
|
||||
|
||||
See [Unicode Standard Annex #11][UAX11] for precise details on what is and isn't
|
||||
covered by this crate.
|
||||
|
||||
## features
|
||||
|
||||
unicode-width does not depend on libstd, so it can be used in crates
|
||||
with the `#![no_std]` attribute.
|
||||
Additionally, [defective combining character sequences](https://unicode.org/glossary/#defective_combining_character_sequence)
|
||||
and nonstandard [Korean jamo](https://unicode.org/glossary/#jamo) sequences may
|
||||
be rendered with a different width than what this crate says. (This is not an
|
||||
exhaustive list.) For a list of what this crate *does* handle, see
|
||||
[docs.rs](https://docs.rs/unicode-width/latest/unicode_width/#rules-for-determining-width).
|
||||
|
||||
## crates.io
|
||||
|
||||
@@ -54,5 +53,5 @@ to your `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
unicode-width = "0.1.7"
|
||||
unicode-width = "0.1.11"
|
||||
```
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
#![feature(test)]
|
||||
|
||||
extern crate test;
|
||||
|
||||
use std::iter;
|
||||
|
||||
use test::Bencher;
|
||||
|
||||
use unicode_width::{UnicodeWidthChar, UnicodeWidthStr};
|
||||
|
||||
#[bench]
|
||||
fn cargo(b: &mut Bencher) {
|
||||
let string = iter::repeat('a').take(4096).collect::<String>();
|
||||
|
||||
b.iter(|| {
|
||||
for c in string.chars() {
|
||||
test::black_box(UnicodeWidthChar::width(c));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
#[allow(deprecated)]
|
||||
fn stdlib(b: &mut Bencher) {
|
||||
let string = iter::repeat('a').take(4096).collect::<String>();
|
||||
|
||||
b.iter(|| {
|
||||
for c in string.chars() {
|
||||
test::black_box(c.width());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn simple_if(b: &mut Bencher) {
|
||||
let string = iter::repeat('a').take(4096).collect::<String>();
|
||||
|
||||
b.iter(|| {
|
||||
for c in string.chars() {
|
||||
test::black_box(simple_width_if(c));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn simple_match(b: &mut Bencher) {
|
||||
let string = iter::repeat('a').take(4096).collect::<String>();
|
||||
|
||||
b.iter(|| {
|
||||
for c in string.chars() {
|
||||
test::black_box(simple_width_match(c));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn simple_width_if(c: char) -> Option<usize> {
|
||||
let cu = c as u32;
|
||||
if cu < 127 {
|
||||
if cu > 31 {
|
||||
Some(1)
|
||||
} else if cu == 0 {
|
||||
Some(0)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
UnicodeWidthChar::width(c)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn simple_width_match(c: char) -> Option<usize> {
|
||||
match c as u32 {
|
||||
cu if cu == 0 => Some(0),
|
||||
cu if cu < 0x20 => None,
|
||||
cu if cu < 0x7f => Some(1),
|
||||
_ => UnicodeWidthChar::width(c),
|
||||
}
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn enwik8(b: &mut Bencher) {
|
||||
// To benchmark, download & unzip `enwik8` from https://data.deepai.org/enwik8.zip
|
||||
let data_path = "bench_data/enwik8";
|
||||
let string = std::fs::read_to_string(data_path).unwrap_or_default();
|
||||
b.iter(|| test::black_box(UnicodeWidthStr::width(string.as_str())));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn jawiki(b: &mut Bencher) {
|
||||
// To benchmark, download & extract `jawiki-20220501-pages-articles-multistream-index.txt` from
|
||||
// https://dumps.wikimedia.org/jawiki/20220501/jawiki-20220501-pages-articles-multistream-index.txt.bz2
|
||||
let data_path = "bench_data/jawiki-20220501-pages-articles-multistream-index.txt";
|
||||
let string = std::fs::read_to_string(data_path).unwrap_or_default();
|
||||
b.iter(|| test::black_box(UnicodeWidthStr::width(string.as_str())));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn emoji(b: &mut Bencher) {
|
||||
// To benchmark, download emoji-style.txt from https://www.unicode.org/emoji/charts/emoji-style.txt
|
||||
let data_path = "bench_data/emoji-style.txt";
|
||||
let string = std::fs::read_to_string(data_path).unwrap_or_default();
|
||||
b.iter(|| test::black_box(UnicodeWidthStr::width(string.as_str())));
|
||||
}
|
||||
+1852
-201
File diff suppressed because it is too large
Load Diff
+178
-51
@@ -10,66 +10,189 @@
|
||||
|
||||
//! Determine displayed width of `char` and `str` types according to
|
||||
//! [Unicode Standard Annex #11](http://www.unicode.org/reports/tr11/)
|
||||
//! rules.
|
||||
//! and other portions of the Unicode standard.
|
||||
//! See the [Rules for determining width](#rules-for-determining-width) section
|
||||
//! for the exact rules.
|
||||
//!
|
||||
//! This crate is `#![no_std]`.
|
||||
//!
|
||||
//! ```rust
|
||||
//! extern crate unicode_width;
|
||||
//!
|
||||
//! use unicode_width::UnicodeWidthStr;
|
||||
//!
|
||||
//! fn main() {
|
||||
//! let teststr = "Hello, world!";
|
||||
//! let width = UnicodeWidthStr::width(teststr);
|
||||
//! println!("{}", teststr);
|
||||
//! println!("The above string is {} columns wide.", width);
|
||||
//! let width = teststr.width_cjk();
|
||||
//! println!("The above string is {} columns wide (CJK).", width);
|
||||
//! }
|
||||
//! let teststr = "Hello, world!";
|
||||
//! let width = UnicodeWidthStr::width(teststr);
|
||||
//! println!("{}", teststr);
|
||||
//! println!("The above string is {} columns wide.", width);
|
||||
//! ```
|
||||
//!
|
||||
//! # features
|
||||
//! # `"cjk"` feature flag
|
||||
//!
|
||||
//! unicode-width supports a `no_std` feature. This eliminates dependence
|
||||
//! on std, and instead uses equivalent functions from core.
|
||||
//! This crate has one Cargo feature flag, `"cjk"`
|
||||
//! (enabled by default).
|
||||
//! It enables the [`UnicodeWidthChar::width_cjk`]
|
||||
//! and [`UnicodeWidthStr::width_cjk`],
|
||||
//! which perform an alternate width calculation
|
||||
//! more suited to CJK contexts. The flag also unseals the
|
||||
//! [`UnicodeWidthChar`] and [`UnicodeWidthStr`] traits.
|
||||
//!
|
||||
//! # crates.io
|
||||
//! Disabling the flag (with `no_default_features` in `Cargo.toml`)
|
||||
//! will reduce the amount of static data needed by the crate.
|
||||
//!
|
||||
//! You can use this package in your project by adding the following
|
||||
//! to your `Cargo.toml`:
|
||||
//! ```rust
|
||||
//! use unicode_width::UnicodeWidthStr;
|
||||
//!
|
||||
//! ```toml
|
||||
//! [dependencies]
|
||||
//! unicode-width = "0.1.5"
|
||||
//! let teststr = "“𘀀”";
|
||||
//! assert_eq!(teststr.width(), 4);
|
||||
//!
|
||||
//! #[cfg(feature = "cjk")]
|
||||
//! assert_eq!(teststr.width_cjk(), 6);
|
||||
//! ```
|
||||
//!
|
||||
//! # Rules for determining width
|
||||
//!
|
||||
//! This crate currently uses the following rules to determine the width of a
|
||||
//! character or string, in order of decreasing precedence. These may be tweaked in the future.
|
||||
//!
|
||||
//! 1. In the following cases, the width of a string differs from the sum of the widths of its constituent characters:
|
||||
//! - The sequence `"\r\n"` has width 1.
|
||||
//! - Emoji-specific ligatures:
|
||||
//! - Well-formed, fully-qualified [emoji ZWJ sequences] have width 2.
|
||||
//! - [Emoji modifier sequences] have width 2.
|
||||
//! - [Emoji presentation sequences] have width 2.
|
||||
//! - Outside of an East Asian context, [text presentation sequences] have width 1 if their base character:
|
||||
//! - Has the [`Emoji_Presentation`] property, and
|
||||
//! - Is not in the [Enclosed Ideographic Supplement] block.
|
||||
//! - Script-specific ligatures:
|
||||
//! - For all the following ligatures, the insertion of any number of [default-ignorable][`Default_Ignorable_Code_Point`]
|
||||
//! [combining marks] anywhere in the sequence will not change the total width. In addition, for all non-Arabic
|
||||
//! ligatures, the insertion of any number of [`'\u{200D}'` ZERO WIDTH JOINER](https://www.unicode.org/versions/Unicode15.0.0/ch23.pdf#G23126)s
|
||||
//! will not affect the width.
|
||||
//! - **[Arabic]**: A character sequence consisting of one character with [`Joining_Group`]`=Lam`,
|
||||
//! followed by any number of characters with [`Joining_Type`]`=Transparent`, followed by one character
|
||||
//! with [`Joining_Group`]`=Alef`, has total width 1. For example: `لا`, `لآ`, `ڸا`, `لٟٞأ`
|
||||
//! - **[Buginese]**: `"\u{1A15}\u{1A17}\u{200D}\u{1A10}"` (<a, -i> ya, `ᨕᨗᨐ`) has total width 1.
|
||||
//! - **[Hebrew]**: `"א\u{200D}ל"` (Alef-Lamed, `אל`) has total width 1.
|
||||
//! - **[Khmer]**: Coeng signs consisting of `'\u{17D2}'` followed by a character in
|
||||
//! `'\u{1780}'..='\u{1782}' | '\u{1784}'..='\u{1787}' | '\u{1789}'..='\u{178C}' | '\u{178E}'..='\u{1793}' | '\u{1795}'..='\u{1798}' | '\u{179B}'..='\u{179D}' | '\u{17A0}' | '\u{17A2}' | '\u{17A7}' | '\u{17AB}'..='\u{17AC}' | '\u{17AF}'`
|
||||
//! have width 0.
|
||||
//! - **[Lisu]**: Tone letter combinations consisting of a character in the range `'\u{A4F8}'..='\u{A4FB}'`
|
||||
//! followed by a character in the range `'\u{A4FC}'..='\u{A4FD}'` have width 1. For example: `ꓹꓼ`
|
||||
//! - **[Old Turkic]**: `"\u{10C32}\u{200D}\u{10C03}"` (`𐰲𐰃`) has total width 1.
|
||||
//! - **[Tifinagh]**: A sequence of a Tifinagh consonant in the range `'\u{2D31}'..='\u{2D65}' | '\u{2D6F}'`, followed by either
|
||||
//! [`'\u{2D7F}'` TIFINAGH CONSONANT JOINER] or `'\u{200D}'`, followed by another Tifinangh consonant, has total width 1.
|
||||
//! For example: `ⵏ⵿ⴾ`
|
||||
//! - In an East Asian context only, `<`, `=`, or `>` have width 2 when followed by [`'\u{0338}'` COMBINING LONG SOLIDUS OVERLAY].
|
||||
//! The two characters may be separated by any number of characters whose canonical decompositions consist only of characters meeting
|
||||
//! one of the following requirements:
|
||||
//! - Has [`Canonical_Combining_Class`] greater than 1, or
|
||||
//! - Is a [default-ignorable][`Default_Ignorable_Code_Point`] [combining mark][combining marks].
|
||||
//! 2. In all other cases, the width of the string equals the sum of its character widths:
|
||||
//! 1. [`'\u{2D7F}'` TIFINAGH CONSONANT JOINER] has width 1 (outside of the ligatures described previously).
|
||||
//! 2. [`'\u{115F}'` HANGUL CHOSEONG FILLER](https://util.unicode.org/UnicodeJsps/character.jsp?a=115F) and
|
||||
//! [`'\u{17A4}'` KHMER INDEPENDENT VOWEL QAA](https://util.unicode.org/UnicodeJsps/character.jsp?a=17A4) have width 2.
|
||||
//! 3. [`'\u{17D8}'` KHMER SIGN BEYYAL](https://util.unicode.org/UnicodeJsps/character.jsp?a=17D8) has width 3.
|
||||
//! 4. The following have width 0:
|
||||
//! - [Characters](https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=%5Cp%7BDefault_Ignorable_Code_Point%7D)
|
||||
//! with the [`Default_Ignorable_Code_Point`] property.
|
||||
//! - [Characters](https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=%5Cp%7BGrapheme_Extend%7D)
|
||||
//! with the [`Grapheme_Extend`] property.
|
||||
//! - The following 8 characters, all of which have NFD decompositions consisting of two [`Grapheme_Extend`] characters:
|
||||
//! - [`'\u{0CC0}'` KANNADA VOWEL SIGN II](https://util.unicode.org/UnicodeJsps/character.jsp?a=0CC0),
|
||||
//! - [`'\u{0CC7}'` KANNADA VOWEL SIGN EE](https://util.unicode.org/UnicodeJsps/character.jsp?a=0CC7),
|
||||
//! - [`'\u{0CC8}'` KANNADA VOWEL SIGN AI](https://util.unicode.org/UnicodeJsps/character.jsp?a=0CC8),
|
||||
//! - [`'\u{0CCA}'` KANNADA VOWEL SIGN O](https://util.unicode.org/UnicodeJsps/character.jsp?a=0CCA),
|
||||
//! - [`'\u{0CCB}'` KANNADA VOWEL SIGN OO](https://util.unicode.org/UnicodeJsps/character.jsp?a=0CCB),
|
||||
//! - [`'\u{1B3B}'` BALINESE VOWEL SIGN RA REPA TEDUNG](https://util.unicode.org/UnicodeJsps/character.jsp?a=1B3B),
|
||||
//! - [`'\u{1B3D}'` BALINESE VOWEL SIGN LA LENGA TEDUNG](https://util.unicode.org/UnicodeJsps/character.jsp?a=1B3D), and
|
||||
//! - [`'\u{1B43}'` BALINESE VOWEL SIGN PEPET TEDUNG](https://util.unicode.org/UnicodeJsps/character.jsp?a=1B43).
|
||||
//! - [Characters](https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=%5Cp%7BHangul_Syllable_Type%3DV%7D%5Cp%7BHangul_Syllable_Type%3DT%7D)
|
||||
//! with a [`Hangul_Syllable_Type`] of `Vowel_Jamo` (`V`) or `Trailing_Jamo` (`T`).
|
||||
//! - The following [`Prepended_Concatenation_Mark`]s:
|
||||
//! - [`'\u{0605}'` NUMBER MARK ABOVE](https://util.unicode.org/UnicodeJsps/character.jsp?a=0605),
|
||||
//! - [`'\u{070F}'` SYRIAC ABBREVIATION MARK](https://util.unicode.org/UnicodeJsps/character.jsp?a=070F),
|
||||
//! - [`'\u{0890}'` POUND MARK ABOVE](https://util.unicode.org/UnicodeJsps/character.jsp?a=0890),
|
||||
//! - [`'\u{0891}'` PIASTRE MARK ABOVE](https://util.unicode.org/UnicodeJsps/character.jsp?a=0891), and
|
||||
//! - [`'\u{08E2}'` DISPUTED END OF AYAH](https://util.unicode.org/UnicodeJsps/character.jsp?a=08E2).
|
||||
//! - [Characters](https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=%5Cp%7BGrapheme_Cluster_Break%3DPrepend%7D-%5Cp%7BPrepended_Concatenation_Mark%7D)
|
||||
//! with the [`Grapheme_Extend=Prepend`] property, that are not also [`Prepended_Concatenation_Mark`]s.
|
||||
//! - [`'\u{A8FA}'` DEVANAGARI CARET](https://util.unicode.org/UnicodeJsps/character.jsp?a=A8FA).
|
||||
//! 5. [Characters](https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=%5Cp%7BEast_Asian_Width%3DF%7D%5Cp%7BEast_Asian_Width%3DW%7D)
|
||||
//! with an [`East_Asian_Width`] of [`Fullwidth`] or [`Wide`] have width 2.
|
||||
//! 6. Characters fulfilling all of the following conditions have width 2 in an East Asian context, and width 1 otherwise:
|
||||
//! - Has an [`East_Asian_Width`] of [`Ambiguous`], or
|
||||
//! has a canonical decomposition to an [`Ambiguous`] character followed by [`'\u{0338}'` COMBINING LONG SOLIDUS OVERLAY], or
|
||||
//! is [`'\u{0387}'` GREEK ANO TELEIA](https://util.unicode.org/UnicodeJsps/character.jsp?a=0387), and
|
||||
//! - Does not have a [`General_Category`] of `Letter` or `Modifier_Symbol`.
|
||||
//! 7. All other characters have width 1.
|
||||
//!
|
||||
//! [`'\u{0338}'` COMBINING LONG SOLIDUS OVERLAY]: https://util.unicode.org/UnicodeJsps/character.jsp?a=0338
|
||||
//! [`'\u{2D7F}'` TIFINAGH CONSONANT JOINER]: https://util.unicode.org/UnicodeJsps/character.jsp?a=2D7F
|
||||
//!
|
||||
//! [`Canonical_Combining_Class`]: https://www.unicode.org/versions/Unicode15.0.0/ch03.pdf#G50313
|
||||
//! [`Default_Ignorable_Code_Point`]: https://www.unicode.org/versions/Unicode15.0.0/ch05.pdf#G40095
|
||||
//! [`East_Asian_Width`]: https://www.unicode.org/reports/tr11/#ED1
|
||||
//! [`Emoji_Presentation`]: https://unicode.org/reports/tr51/#def_emoji_presentation
|
||||
//! [`General_Category`]: https://www.unicode.org/versions/Unicode15.0.0/ch04.pdf#G124142
|
||||
//! [`Grapheme_Extend=Prepend`]: https://www.unicode.org/reports/tr29/#Prepend
|
||||
//! [`Grapheme_Extend`]: https://www.unicode.org/versions/Unicode15.0.0/ch03.pdf#G52443
|
||||
//! [`Hangul_Syllable_Type`]: https://www.unicode.org/versions/Unicode15.0.0/ch03.pdf#G45593
|
||||
//! [`Joining_Group`]: https://www.unicode.org/versions/Unicode14.0.0/ch09.pdf#G36862
|
||||
//! [`Joining_Type`]: http://www.unicode.org/versions/Unicode15.0.0/ch09.pdf#G50009
|
||||
//! [`Prepended_Concatenation_Mark`]: https://www.unicode.org/versions/Unicode15.0.0/ch23.pdf#G37908
|
||||
//! [`Script`]: https://www.unicode.org/reports/tr24/#Script
|
||||
//!
|
||||
//! [`Fullwidth`]: https://www.unicode.org/reports/tr11/#ED2
|
||||
//! [`Wide`]: https://www.unicode.org/reports/tr11/#ED4
|
||||
//! [`Ambiguous`]: https://www.unicode.org/reports/tr11/#ED6
|
||||
//!
|
||||
//! [combining marks]: https://www.unicode.org/versions/Unicode15.0.0/ch03.pdf#G30602
|
||||
//!
|
||||
//! [emoji ZWJ sequences]: https://www.unicode.org/reports/tr51/#def_emoji_sequence
|
||||
//! [Emoji modifier sequences]: https://www.unicode.org/reports/tr51/#def_emoji_modifier_sequence
|
||||
//! [Emoji presentation sequences]: https://unicode.org/reports/tr51/#def_emoji_presentation_sequence
|
||||
//! [text presentation sequences]: https://unicode.org/reports/tr51/#def_text_presentation_sequence
|
||||
//!
|
||||
//! [Enclosed Ideographic Supplement]: https://unicode.org/charts/nameslist/n_1F200.html
|
||||
//!
|
||||
//! [Arabic]: https://www.unicode.org/versions/Unicode15.0.0/ch09.pdf#G7480
|
||||
//! [Buginese]: https://www.unicode.org/versions/Unicode15.0.0/ch17.pdf#G26743
|
||||
//! [Hebrew]: https://www.unicode.org/versions/Unicode15.0.0/ch09.pdf#G6528
|
||||
//! [Khmer]: https://www.unicode.org/versions/Unicode15.0.0/ch16.pdf#G64642
|
||||
//! [Lisu]: https://www.unicode.org/versions/Unicode15.0.0/ch18.pdf#G44587
|
||||
//! [Old Turkic]: https://www.unicode.org/versions/Unicode15.0.0/ch14.pdf#G41975
|
||||
//! [Tifinagh]: http://www.unicode.org/versions/Unicode15.0.0/ch19.pdf#G43184
|
||||
//!
|
||||
//!
|
||||
//! ## Canonical equivalence
|
||||
//!
|
||||
//! Canonically equivalent strings are assigned the same width (CJK and non-CJK).
|
||||
|
||||
#![deny(missing_docs, unsafe_code)]
|
||||
#![doc(html_logo_url = "https://unicode-rs.github.io/unicode-rs_sm.png",
|
||||
html_favicon_url = "https://unicode-rs.github.io/unicode-rs_sm.png")]
|
||||
|
||||
#![cfg_attr(feature = "bench", feature(test))]
|
||||
#![forbid(unsafe_code)]
|
||||
#![deny(missing_docs)]
|
||||
#![doc(
|
||||
html_logo_url = "https://unicode-rs.github.io/unicode-rs_sm.png",
|
||||
html_favicon_url = "https://unicode-rs.github.io/unicode-rs_sm.png"
|
||||
)]
|
||||
#![no_std]
|
||||
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
extern crate std;
|
||||
|
||||
#[cfg(feature = "bench")]
|
||||
extern crate test;
|
||||
|
||||
use tables::charwidth as cw;
|
||||
pub use tables::UNICODE_VERSION;
|
||||
|
||||
use core::ops::Add;
|
||||
|
||||
mod tables;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
mod private {
|
||||
pub trait Sealed {}
|
||||
#[cfg(not(feature = "cjk"))]
|
||||
impl Sealed for char {}
|
||||
#[cfg(not(feature = "cjk"))]
|
||||
impl Sealed for str {}
|
||||
#[cfg(feature = "cjk")]
|
||||
impl<T: ?Sized> Sealed for T {}
|
||||
}
|
||||
|
||||
/// Methods for determining displayed width of Unicode characters.
|
||||
pub trait UnicodeWidthChar {
|
||||
pub trait UnicodeWidthChar: private::Sealed {
|
||||
/// Returns the character's displayed width in columns, or `None` if the
|
||||
/// character is a control character other than `'\x00'`.
|
||||
/// character is a control character.
|
||||
///
|
||||
/// This function treats characters in the Ambiguous category according
|
||||
/// to [Unicode Standard Annex #11](http://www.unicode.org/reports/tr11/)
|
||||
@@ -78,54 +201,58 @@ pub trait UnicodeWidthChar {
|
||||
fn width(self) -> Option<usize>;
|
||||
|
||||
/// Returns the character's displayed width in columns, or `None` if the
|
||||
/// character is a control character other than `'\x00'`.
|
||||
/// character is a control character.
|
||||
///
|
||||
/// This function treats characters in the Ambiguous category according
|
||||
/// to [Unicode Standard Annex #11](http://www.unicode.org/reports/tr11/)
|
||||
/// as 2 columns wide. This is consistent with the recommendations for
|
||||
/// CJK contexts.
|
||||
#[cfg(feature = "cjk")]
|
||||
fn width_cjk(self) -> Option<usize>;
|
||||
}
|
||||
|
||||
impl UnicodeWidthChar for char {
|
||||
#[inline]
|
||||
fn width(self) -> Option<usize> { cw::width(self, false) }
|
||||
fn width(self) -> Option<usize> {
|
||||
tables::single_char_width(self)
|
||||
}
|
||||
|
||||
#[cfg(feature = "cjk")]
|
||||
#[inline]
|
||||
fn width_cjk(self) -> Option<usize> { cw::width(self, true) }
|
||||
fn width_cjk(self) -> Option<usize> {
|
||||
tables::single_char_width_cjk(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Methods for determining displayed width of Unicode strings.
|
||||
pub trait UnicodeWidthStr {
|
||||
pub trait UnicodeWidthStr: private::Sealed {
|
||||
/// Returns the string's displayed width in columns.
|
||||
///
|
||||
/// Control characters are treated as having zero width.
|
||||
///
|
||||
/// This function treats characters in the Ambiguous category according
|
||||
/// to [Unicode Standard Annex #11](http://www.unicode.org/reports/tr11/)
|
||||
/// as 1 column wide. This is consistent with the recommendations for
|
||||
/// non-CJK contexts, or when the context cannot be reliably determined.
|
||||
fn width<'a>(&'a self) -> usize;
|
||||
fn width(&self) -> usize;
|
||||
|
||||
/// Returns the string's displayed width in columns.
|
||||
///
|
||||
/// Control characters are treated as having zero width.
|
||||
///
|
||||
/// This function treats characters in the Ambiguous category according
|
||||
/// to [Unicode Standard Annex #11](http://www.unicode.org/reports/tr11/)
|
||||
/// as 2 column wide. This is consistent with the recommendations for
|
||||
/// CJK contexts.
|
||||
fn width_cjk<'a>(&'a self) -> usize;
|
||||
#[cfg(feature = "cjk")]
|
||||
fn width_cjk(&self) -> usize;
|
||||
}
|
||||
|
||||
impl UnicodeWidthStr for str {
|
||||
#[inline]
|
||||
fn width(&self) -> usize {
|
||||
self.chars().map(|c| cw::width(c, false).unwrap_or(0)).fold(0, Add::add)
|
||||
tables::str_width(self)
|
||||
}
|
||||
|
||||
#[cfg(feature = "cjk")]
|
||||
#[inline]
|
||||
fn width_cjk(&self) -> usize {
|
||||
self.chars().map(|c| cw::width(c, true).unwrap_or(0)).fold(0, Add::add)
|
||||
tables::str_width_cjk(self)
|
||||
}
|
||||
}
|
||||
|
||||
+21703
-481
File diff suppressed because it is too large
Load Diff
-191
@@ -1,191 +0,0 @@
|
||||
// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#[cfg(feature = "bench")]
|
||||
use std::iter;
|
||||
#[cfg(feature = "bench")]
|
||||
use test::{self, Bencher};
|
||||
#[cfg(feature = "bench")]
|
||||
use super::{UnicodeWidthChar, UnicodeWidthStr};
|
||||
|
||||
use std::prelude::v1::*;
|
||||
|
||||
#[cfg(feature = "bench")]
|
||||
#[bench]
|
||||
fn cargo(b: &mut Bencher) {
|
||||
let string = iter::repeat('a').take(4096).collect::<String>();
|
||||
|
||||
b.iter(|| {
|
||||
for c in string.chars() {
|
||||
test::black_box(UnicodeWidthChar::width(c));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(feature = "bench")]
|
||||
#[bench]
|
||||
#[allow(deprecated)]
|
||||
fn stdlib(b: &mut Bencher) {
|
||||
let string = iter::repeat('a').take(4096).collect::<String>();
|
||||
|
||||
b.iter(|| {
|
||||
for c in string.chars() {
|
||||
test::black_box(c.width());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(feature = "bench")]
|
||||
#[bench]
|
||||
fn simple_if(b: &mut Bencher) {
|
||||
let string = iter::repeat('a').take(4096).collect::<String>();
|
||||
|
||||
b.iter(|| {
|
||||
for c in string.chars() {
|
||||
test::black_box(simple_width_if(c));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(feature = "bench")]
|
||||
#[bench]
|
||||
fn simple_match(b: &mut Bencher) {
|
||||
let string = iter::repeat('a').take(4096).collect::<String>();
|
||||
|
||||
b.iter(|| {
|
||||
for c in string.chars() {
|
||||
test::black_box(simple_width_match(c));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(feature = "bench")]
|
||||
#[inline]
|
||||
fn simple_width_if(c: char) -> Option<usize> {
|
||||
let cu = c as u32;
|
||||
if cu < 127 {
|
||||
if cu > 31 {
|
||||
Some(1)
|
||||
} else if cu == 0 {
|
||||
Some(0)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
UnicodeWidthChar::width(c)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "bench")]
|
||||
#[inline]
|
||||
fn simple_width_match(c: char) -> Option<usize> {
|
||||
match c as u32 {
|
||||
cu if cu == 0 => Some(0),
|
||||
cu if cu < 0x20 => None,
|
||||
cu if cu < 0x7f => Some(1),
|
||||
_ => UnicodeWidthChar::width(c)
|
||||
}
|
||||
}
|
||||
#[cfg(all(feature = "bench", not(feature = "no_std")))]
|
||||
#[bench]
|
||||
fn enwik8(b: &mut Bencher) {
|
||||
// To benchmark, download & unzip `enwik8` from https://data.deepai.org/enwik8.zip
|
||||
let data_path = "bench_data/enwik8";
|
||||
let string = std::fs::read_to_string(data_path).unwrap_or_default();
|
||||
b.iter(|| test::black_box(UnicodeWidthStr::width(string.as_str())));
|
||||
}
|
||||
#[cfg(all(feature = "bench", not(feature = "no_std")))]
|
||||
#[bench]
|
||||
fn jawiki(b: &mut Bencher) {
|
||||
// To benchmark, download & extract `jawiki-20220501-pages-articles-multistream-index.txt` from
|
||||
// https://dumps.wikimedia.org/jawiki/20220501/jawiki-20220501-pages-articles-multistream-index.txt.bz2
|
||||
let data_path = "bench_data/jawiki-20220501-pages-articles-multistream-index.txt";
|
||||
let string = std::fs::read_to_string(data_path).unwrap_or_default();
|
||||
b.iter(|| test::black_box(UnicodeWidthStr::width(string.as_str())));
|
||||
}
|
||||
#[test]
|
||||
fn test_str() {
|
||||
use super::UnicodeWidthStr;
|
||||
|
||||
assert_eq!(UnicodeWidthStr::width("hello"), 10);
|
||||
assert_eq!("hello".width_cjk(), 10);
|
||||
assert_eq!(UnicodeWidthStr::width("\0\0\0\x01\x01"), 0);
|
||||
assert_eq!("\0\0\0\x01\x01".width_cjk(), 0);
|
||||
assert_eq!(UnicodeWidthStr::width(""), 0);
|
||||
assert_eq!("".width_cjk(), 0);
|
||||
assert_eq!(UnicodeWidthStr::width("\u{2081}\u{2082}\u{2083}\u{2084}"), 4);
|
||||
assert_eq!("\u{2081}\u{2082}\u{2083}\u{2084}".width_cjk(), 8);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_emoji() {
|
||||
// Example from the README.
|
||||
use super::UnicodeWidthStr;
|
||||
|
||||
assert_eq!(UnicodeWidthStr::width("👩"), 2); // Woman
|
||||
assert_eq!(UnicodeWidthStr::width("🔬"), 2); // Microscope
|
||||
assert_eq!(UnicodeWidthStr::width("👩🔬"), 4); // Woman scientist
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_char() {
|
||||
use super::UnicodeWidthChar;
|
||||
#[cfg(feature = "no_std")]
|
||||
use core::option::Option::{Some, None};
|
||||
|
||||
assert_eq!(UnicodeWidthChar::width('h'), Some(2));
|
||||
assert_eq!('h'.width_cjk(), Some(2));
|
||||
assert_eq!(UnicodeWidthChar::width('\x00'), Some(0));
|
||||
assert_eq!('\x00'.width_cjk(), Some(0));
|
||||
assert_eq!(UnicodeWidthChar::width('\x01'), None);
|
||||
assert_eq!('\x01'.width_cjk(), None);
|
||||
assert_eq!(UnicodeWidthChar::width('\u{2081}'), Some(1));
|
||||
assert_eq!('\u{2081}'.width_cjk(), Some(2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_char2() {
|
||||
use super::UnicodeWidthChar;
|
||||
#[cfg(feature = "no_std")]
|
||||
use core::option::Option::{Some, None};
|
||||
|
||||
assert_eq!(UnicodeWidthChar::width('\x00'),Some(0));
|
||||
assert_eq!('\x00'.width_cjk(),Some(0));
|
||||
|
||||
assert_eq!(UnicodeWidthChar::width('\x0A'),None);
|
||||
assert_eq!('\x0A'.width_cjk(),None);
|
||||
|
||||
assert_eq!(UnicodeWidthChar::width('w'),Some(1));
|
||||
assert_eq!('w'.width_cjk(),Some(1));
|
||||
|
||||
assert_eq!(UnicodeWidthChar::width('h'),Some(2));
|
||||
assert_eq!('h'.width_cjk(),Some(2));
|
||||
|
||||
assert_eq!(UnicodeWidthChar::width('\u{AD}'),Some(1));
|
||||
assert_eq!('\u{AD}'.width_cjk(),Some(1));
|
||||
|
||||
assert_eq!(UnicodeWidthChar::width('\u{1160}'),Some(0));
|
||||
assert_eq!('\u{1160}'.width_cjk(),Some(0));
|
||||
|
||||
assert_eq!(UnicodeWidthChar::width('\u{a1}'),Some(1));
|
||||
assert_eq!('\u{a1}'.width_cjk(),Some(2));
|
||||
|
||||
assert_eq!(UnicodeWidthChar::width('\u{300}'),Some(0));
|
||||
assert_eq!('\u{300}'.width_cjk(),Some(0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unicode_12() {
|
||||
use super::UnicodeWidthChar;
|
||||
#[cfg(feature = "no_std")]
|
||||
use core::option::Option::{Some, None};
|
||||
|
||||
assert_eq!(UnicodeWidthChar::width('\u{1F971}'), Some(2));
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
+631
@@ -0,0 +1,631 @@
|
||||
// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{BufRead, BufReader},
|
||||
};
|
||||
|
||||
use unicode_width::{UnicodeWidthChar, UnicodeWidthStr};
|
||||
|
||||
macro_rules! assert_width {
|
||||
($s:expr, $nocjk:expr, $cjk:expr $(,)?) => {{
|
||||
assert_eq!($s.width(), $nocjk, "{:?} has the wrong width", $s);
|
||||
#[cfg(feature = "cjk")]
|
||||
assert_eq!($s.width_cjk(), $cjk, "{:?} has the wrong width (CJK)", $s);
|
||||
}};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_str() {
|
||||
assert_width!("hello", 10, 10);
|
||||
assert_width!("\0\0\0\x01\x01", 5, 5);
|
||||
assert_width!("", 0, 0);
|
||||
assert_width!("\u{2081}\u{2082}\u{2083}\u{2084}", 4, 8);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_emoji() {
|
||||
assert_width!("👩", 2, 2); // Woman
|
||||
assert_width!("🔬", 2, 2); // Microscope
|
||||
assert_width!("👩🔬", 2, 2); // Woman scientist
|
||||
}
|
||||
|
||||
// From README
|
||||
#[test]
|
||||
fn test_bad_devanagari() {
|
||||
assert_eq!("क".width(), 1); // Devanagari letter Ka
|
||||
assert_eq!("ष".width(), 1); // Devanagari letter Ssa
|
||||
assert_eq!("क्ष".width(), 2); // Ka + Virama + Ssa
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_char() {
|
||||
assert_width!('h', Some(2), Some(2));
|
||||
assert_width!('\x00', None, None);
|
||||
assert_width!('\x01', None, None);
|
||||
assert_width!('\u{2081}', Some(1), Some(2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_char2() {
|
||||
assert_width!('\x0A', None, None);
|
||||
assert_width!('w', Some(1), Some(1));
|
||||
assert_width!('h', Some(2), Some(2));
|
||||
assert_width!('\u{AD}', Some(0), Some(0));
|
||||
assert_width!('\u{1160}', Some(0), Some(0));
|
||||
assert_width!('\u{a1}', Some(1), Some(2));
|
||||
assert_width!('\u{300}', Some(0), Some(0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unicode_12() {
|
||||
assert_width!('\u{1F971}', Some(2), Some(2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_default_ignorable() {
|
||||
assert_width!('\u{1160}', Some(0), Some(0));
|
||||
assert_width!('\u{3164}', Some(0), Some(0));
|
||||
assert_width!('\u{FFA0}', Some(0), Some(0));
|
||||
assert_width!('\u{E0000}', Some(0), Some(0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ambiguous() {
|
||||
assert_width!("\u{B7}", 1, 2);
|
||||
assert_width!("\u{0387}", 1, 2);
|
||||
assert_width!("\u{A8}", 1, 1);
|
||||
assert_width!("\u{02C9}", 1, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_jamo() {
|
||||
assert_width!('\u{1100}', Some(2), Some(2));
|
||||
assert_width!('\u{A97C}', Some(2), Some(2));
|
||||
// Special case: U+115F HANGUL CHOSEONG FILLER
|
||||
assert_width!('\u{115F}', Some(2), Some(2));
|
||||
assert_width!('\u{1160}', Some(0), Some(0));
|
||||
assert_width!('\u{D7C6}', Some(0), Some(0));
|
||||
assert_width!('\u{11A8}', Some(0), Some(0));
|
||||
assert_width!('\u{D7FB}', Some(0), Some(0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_prepended_concatenation_marks() {
|
||||
for c in [
|
||||
'\u{0600}',
|
||||
'\u{0601}',
|
||||
'\u{0602}',
|
||||
'\u{0603}',
|
||||
'\u{0604}',
|
||||
'\u{06DD}',
|
||||
'\u{110BD}',
|
||||
'\u{110CD}',
|
||||
] {
|
||||
assert_width!(c, Some(1), Some(1));
|
||||
}
|
||||
|
||||
for c in ['\u{0605}', '\u{070F}', '\u{0890}', '\u{0891}', '\u{08E2}'] {
|
||||
assert_width!(c, Some(0), Some(0));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_gcb_prepend() {
|
||||
assert_width!("ൎഉ", 1, 1);
|
||||
assert_width!("\u{11A89}", 0, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_interlinear_annotation_chars() {
|
||||
assert_width!('\u{FFF9}', Some(1), Some(1));
|
||||
assert_width!('\u{FFFA}', Some(1), Some(1));
|
||||
assert_width!('\u{FFFB}', Some(1), Some(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hieroglyph_format_controls() {
|
||||
assert_width!('\u{13430}', Some(1), Some(1));
|
||||
assert_width!('\u{13436}', Some(1), Some(1));
|
||||
assert_width!('\u{1343C}', Some(1), Some(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_marks() {
|
||||
// Nonspacing marks have 0 width
|
||||
assert_width!('\u{0301}', Some(0), Some(0));
|
||||
// Enclosing marks have 0 width
|
||||
assert_width!('\u{20DD}', Some(0), Some(0));
|
||||
// Some spacing marks have width 1
|
||||
assert_width!('\u{09CB}', Some(1), Some(1));
|
||||
// But others have width 0
|
||||
assert_width!('\u{09BE}', Some(0), Some(0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_devanagari_caret() {
|
||||
assert_width!('\u{A8FA}', Some(0), Some(0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_solidus_overlay() {
|
||||
assert_width!("<\u{338}", 1, 2);
|
||||
assert_width!("=\u{338}", 1, 2);
|
||||
assert_width!(">\u{338}", 1, 2);
|
||||
assert_width!("=\u{301}\u{338}", 1, 2);
|
||||
assert_width!("=\u{338}\u{301}", 1, 2);
|
||||
assert_width!("=\u{FE0F}\u{338}", 1, 2);
|
||||
assert_width!("#\u{FE0F}\u{338}", 2, 2);
|
||||
assert_width!("#\u{338}\u{FE0F}", 1, 1);
|
||||
|
||||
assert_width!("\u{06B8}\u{338}\u{0627}", 1, 1);
|
||||
assert_width!("\u{06B8}\u{338}\u{FE0E}\u{0627}", 1, 1);
|
||||
assert_width!("\u{06B8}\u{338}\u{FE0F}\u{0627}", 1, 1);
|
||||
assert_width!("\u{06B8}\u{FE0E}\u{338}\u{0627}", 1, 1);
|
||||
assert_width!("\u{06B8}\u{FE0F}\u{338}\u{0627}", 1, 1);
|
||||
|
||||
assert_width!("=\u{338}\u{0627}", 2, 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_emoji_presentation() {
|
||||
assert_width!('\u{0023}', Some(1), Some(1));
|
||||
assert_width!('\u{FE0F}', Some(0), Some(0));
|
||||
assert_width!("\u{0023}\u{FE0F}", 2, 2);
|
||||
assert_width!("a\u{0023}\u{FE0F}a", 4, 4);
|
||||
assert_width!("\u{0023}a\u{FE0F}", 2, 2);
|
||||
assert_width!("a\u{FE0F}", 1, 1);
|
||||
assert_width!("\u{0023}\u{0023}\u{FE0F}a", 4, 4);
|
||||
assert_width!("\u{002A}\u{FE0F}", 2, 2);
|
||||
assert_width!("\u{23F9}\u{FE0F}", 2, 2);
|
||||
assert_width!("\u{24C2}\u{FE0F}", 2, 2);
|
||||
assert_width!("\u{1F6F3}\u{FE0F}", 2, 2);
|
||||
assert_width!("\u{1F700}\u{FE0F}", 1, 1);
|
||||
assert_width!("\u{002A}\u{301}\u{FE0F}", 1, 1);
|
||||
assert_width!("\u{002A}\u{200D}\u{FE0F}", 1, 1);
|
||||
assert_width!("\u{002A}\u{FE0E}\u{FE0F}", 1, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_text_presentation() {
|
||||
assert_width!('\u{FE0E}', Some(0), Some(0));
|
||||
assert_width!('\u{2648}', Some(2), Some(2));
|
||||
assert_width!("\u{2648}\u{FE0E}", 1, 2);
|
||||
assert_width!("\u{1F21A}\u{FE0E}", 2, 2);
|
||||
assert_width!("\u{0301}\u{FE0E}", 0, 0);
|
||||
assert_width!("a\u{FE0E}", 1, 1);
|
||||
assert_width!("𘀀\u{FE0E}", 2, 2);
|
||||
assert_width!("\u{2648}\u{0301}\u{FE0E}", 2, 2);
|
||||
assert_width!("\u{2648}\u{200D}\u{FE0E}", 2, 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_control_line_break() {
|
||||
assert_width!('\u{2028}', Some(1), Some(1));
|
||||
assert_width!('\u{2029}', Some(1), Some(1));
|
||||
assert_width!('\r', None, None);
|
||||
assert_width!('\n', None, None);
|
||||
assert_width!("\r", 1, 1);
|
||||
// This is 0 due to #60
|
||||
assert_width!("\n", 0, 0);
|
||||
assert_width!("\r\n", 0, 0);
|
||||
assert_width!("\0", 1, 1);
|
||||
assert_width!("1\t2\r\n3\u{85}4", 6, 6);
|
||||
assert_width!("\r\u{FE0F}\n", 1, 1);
|
||||
assert_width!("\r\u{200D}\n", 1, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn char_str_consistent() {
|
||||
let mut s = String::with_capacity(4);
|
||||
for c in '\0'..=char::MAX {
|
||||
// Newlines are special cased (#60)
|
||||
if c == '\n' {
|
||||
continue;
|
||||
}
|
||||
s.clear();
|
||||
s.push(c);
|
||||
assert_eq!(c.width().unwrap_or(1), s.width());
|
||||
#[cfg(feature = "cjk")]
|
||||
assert_eq!(c.width_cjk().unwrap_or(1), s.width_cjk());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lisu_tones() {
|
||||
for c in '\u{A4F8}'..='\u{A4FD}' {
|
||||
assert_width!(c, Some(1), Some(1));
|
||||
assert_width!(String::from(c), 1, 1);
|
||||
}
|
||||
for c1 in '\u{A4F8}'..='\u{A4FD}' {
|
||||
for c2 in '\u{A4F8}'..='\u{A4FD}' {
|
||||
let mut s = String::with_capacity(8);
|
||||
s.push(c1);
|
||||
s.push(c2);
|
||||
match (c1, c2) {
|
||||
('\u{A4F8}'..='\u{A4FB}', '\u{A4FC}'..='\u{A4FD}') => assert_width!(s, 1, 1),
|
||||
_ => assert_width!(s, 2, 2),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert_width!("ꓪꓹ", 2, 2);
|
||||
assert_width!("ꓪꓹꓼ", 2, 2);
|
||||
assert_width!("ꓪꓹ\u{FE0F}ꓼ", 2, 2);
|
||||
assert_width!("ꓪꓹ\u{200D}ꓼ", 2, 2);
|
||||
assert_width!("ꓪꓹꓼ\u{FE0F}", 2, 2);
|
||||
assert_width!("ꓪꓹ\u{0301}ꓼ", 3, 3);
|
||||
assert_width!("ꓪꓹꓹ", 3, 3);
|
||||
assert_width!("ꓪꓼꓼ", 3, 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hebrew_alef_lamed() {
|
||||
assert_width!("\u{05D0}", 1, 1);
|
||||
assert_width!("\u{05DC}", 1, 1);
|
||||
assert_width!("\u{05D0}\u{05DC}", 2, 2);
|
||||
assert_width!("\u{05D0}\u{200D}\u{05DC}", 1, 1);
|
||||
assert_width!(
|
||||
"\u{05D0}\u{200D}\u{200D}\u{200D}\u{200D}\u{200D}\u{200D}\u{200D}\u{05DC}",
|
||||
1,
|
||||
1,
|
||||
);
|
||||
assert_width!("\u{05D0}\u{05D0}\u{200D}\u{05DC}", 2, 2);
|
||||
assert_width!(
|
||||
"\u{05D0}\u{05D0}\u{200D}\u{200D}\u{200D}\u{200D}\u{200D}\u{200D}\u{05DC}",
|
||||
2,
|
||||
2,
|
||||
);
|
||||
assert_width!("\u{05D0}\u{FE0F}\u{200D}\u{FE0F}\u{05DC}\u{FE0F}", 1, 1);
|
||||
assert_width!("\u{05D0}\u{FE0E}\u{200D}\u{FE0E}\u{05DC}\u{FE0E}", 1, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_arabic_lam_alef() {
|
||||
assert_width!("\u{0644}", 1, 1);
|
||||
assert_width!("\u{06B8}", 1, 1);
|
||||
|
||||
assert_width!("\u{0623}", 1, 1);
|
||||
assert_width!("\u{0627}", 1, 1);
|
||||
|
||||
assert_width!("\u{0644}\u{0623}", 1, 1);
|
||||
assert_width!("\u{0644}\u{0627}", 1, 1);
|
||||
assert_width!("\u{06B8}\u{0623}", 1, 1);
|
||||
assert_width!("\u{06B8}\u{0627}", 1, 1);
|
||||
|
||||
assert_width!("\u{0644}\u{065F}\u{065E}\u{0623}", 1, 1);
|
||||
assert_width!("\u{0644}\u{065F}\u{065E}\u{0627}", 1, 1);
|
||||
assert_width!("\u{06B8}\u{065F}\u{065E}\u{0623}", 1, 1);
|
||||
assert_width!("\u{06B8}\u{065F}\u{065E}\u{0627}", 1, 1);
|
||||
|
||||
assert_width!("\u{06B8}\u{FE0E}\u{0627}", 1, 1);
|
||||
assert_width!("\u{06B8}\u{FE0F}\u{0627}", 1, 1);
|
||||
assert_width!("\u{06B8}\u{17B5}\u{0627}", 1, 1);
|
||||
|
||||
assert_width!("\u{0644}\u{0644}\u{0623}", 2, 2);
|
||||
assert_width!("\u{0644}\u{0644}\u{0627}", 2, 2);
|
||||
assert_width!("\u{06B8}\u{06B8}\u{0623}", 2, 2);
|
||||
assert_width!("\u{06B8}\u{06B8}\u{0627}", 2, 2);
|
||||
|
||||
assert_width!("\u{0644}\u{200D}\u{0623}", 2, 2);
|
||||
assert_width!("\u{0644}\u{200D}\u{0627}", 2, 2);
|
||||
assert_width!("\u{06B8}\u{200D}\u{0623}", 2, 2);
|
||||
assert_width!("\u{06B8}\u{200D}\u{0627}", 2, 2);
|
||||
|
||||
assert_width!("\u{0644}\u{1E94B}\u{0623}", 3, 3);
|
||||
assert_width!("\u{0644}\u{1E94B}\u{0627}", 3, 3);
|
||||
assert_width!("\u{06B8}\u{1E94B}\u{0623}", 3, 3);
|
||||
assert_width!("\u{06B8}\u{1E94B}\u{0627}", 3, 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_buginese_a_i_ya() {
|
||||
assert_width!("\u{1A15}", 1, 1);
|
||||
assert_width!("\u{1A17}", 0, 0);
|
||||
assert_width!("\u{1A10}", 1, 1);
|
||||
|
||||
assert_width!("\u{1A15}\u{1A17}\u{200D}\u{1A10}", 1, 1);
|
||||
assert_width!(
|
||||
"\u{1A15}\u{1A17}\u{200D}\u{200D}\u{200D}\u{200D}\u{1A10}",
|
||||
1,
|
||||
1,
|
||||
);
|
||||
assert_width!("\u{1A15}\u{1A17}\u{200D}\u{338}", 1, 1);
|
||||
assert_width!("\u{1A15}\u{FE0E}\u{1A17}\u{200D}", 1, 1);
|
||||
assert_width!("\u{1A15}\u{FE0F}\u{1A17}\u{200D}", 1, 1);
|
||||
assert_width!("\u{1A15}\u{1A17}\u{FE0E}\u{200D}", 1, 1);
|
||||
assert_width!("\u{1A15}\u{1A17}\u{FE0F}\u{200D}", 1, 1);
|
||||
assert_width!("\u{1A15}\u{1A17}\u{200D}\u{FE0E}", 1, 1);
|
||||
assert_width!("\u{1A15}\u{1A17}\u{200D}\u{FE0F}", 1, 1);
|
||||
assert_width!(
|
||||
"\u{1A15}\u{17B5}\u{200D}\u{FE0E}\u{1A17}\u{200D}\u{FE0F}\u{200D}\u{FE0F}",
|
||||
1,
|
||||
1,
|
||||
);
|
||||
|
||||
assert_width!("\u{1A15}\u{1A15}\u{1A17}\u{200D}\u{1A10}", 2, 2);
|
||||
assert_width!(
|
||||
"\u{1A15}\u{1A15}\u{1A17}\u{200D}\u{200D}\u{200D}\u{200D}\u{1A10}",
|
||||
2,
|
||||
2,
|
||||
);
|
||||
|
||||
assert_width!("\u{1A15}\u{1A17}\u{1A10}", 2, 2);
|
||||
assert_width!("\u{1A15}\u{200D}\u{1A10}", 2, 2);
|
||||
assert_width!("\u{1A15}\u{1A10}", 2, 2);
|
||||
assert_width!("\u{1A15}\u{1A17}\u{1A17}\u{200D}\u{1A10}", 2, 2);
|
||||
assert_width!("\u{1A15}\u{1A17}\u{338}\u{200D}\u{1A10}", 2, 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tifinagh_biconsonants() {
|
||||
assert_width!("\u{2D4F}", 1, 1);
|
||||
assert_width!("\u{2D3E}", 1, 1);
|
||||
assert_width!("\u{2D7F}", 1, 1);
|
||||
|
||||
assert_width!("\u{2D4F}\u{200D}\u{2D3E}", 1, 1);
|
||||
assert_width!("\u{2D4F}\u{2D7F}\u{2D3E}", 1, 1);
|
||||
assert_width!("\u{2D4F}\u{200D}\u{2D3E}", 1, 1);
|
||||
assert_width!(
|
||||
"\u{2D4F}\u{FE0F}\u{200D}\u{2D7F}\u{FE0E}\u{200D}\u{17B5}\u{2D3E}",
|
||||
1,
|
||||
1,
|
||||
);
|
||||
|
||||
assert_width!("\u{2D4F}\u{301}\u{2D7F}\u{2D3E}", 3, 3);
|
||||
assert_width!("\u{2D4F}\u{301}\u{200D}\u{2D3E}", 2, 2);
|
||||
assert_width!("\u{2D4F}\u{2D3E}", 2, 2);
|
||||
assert_width!("\u{2D4F}\u{2D7F}\u{2D7F}\u{2D3E}", 4, 4);
|
||||
assert_width!("\u{2D7F}\u{2D3E}", 2, 2);
|
||||
assert_width!("\u{2D7F}\u{2D7F}\u{2D66}", 3, 3);
|
||||
assert_width!("\u{2D66}\u{2D7F}\u{2D3E}", 3, 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_old_turkic_ligature() {
|
||||
assert_width!("\u{10C32}", 1, 1);
|
||||
assert_width!("\u{10C03}", 1, 1);
|
||||
assert_width!("\u{10C32}\u{10C03}", 2, 2);
|
||||
|
||||
assert_width!("\u{10C32}\u{200D}\u{10C03}", 1, 1);
|
||||
assert_width!("\u{10C32}\u{FE0F}\u{200D}\u{FE0E}\u{10C03}", 1, 1);
|
||||
|
||||
assert_width!("\u{10C32}\u{2D7F}\u{10C03}", 3, 3);
|
||||
assert_width!("\u{10C32}\u{0301}\u{200D}\u{10C03}", 2, 2);
|
||||
assert_width!("\u{10C03}\u{200D}\u{10C32}", 2, 2);
|
||||
assert_width!("\u{200D}\u{10C32}", 1, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_khmer_coeng() {
|
||||
assert_width!("ល", 1, 1);
|
||||
assert_width!("ង", 1, 1);
|
||||
assert_width!("លង", 2, 2);
|
||||
assert_width!("ល្ង", 1, 1);
|
||||
|
||||
for c in '\0'..=char::MAX {
|
||||
if matches!(
|
||||
c,
|
||||
'\u{1780}'..='\u{1782}' | '\u{1784}'..='\u{1787}'
|
||||
| '\u{1789}'..='\u{178C}' | '\u{178E}'..='\u{1793}'
|
||||
| '\u{1795}'..='\u{1798}' | '\u{179B}'..='\u{179D}'
|
||||
| '\u{17A0}' | '\u{17A2}' | '\u{17A7}'
|
||||
| '\u{17AB}'..='\u{17AC}' | '\u{17AF}'
|
||||
) {
|
||||
assert_width!(format!("\u{17D2}{c}"), 0, 0);
|
||||
assert_width!(format!("\u{17D2}\u{200D}\u{200D}{c}"), 0, 0);
|
||||
} else {
|
||||
// Newlines are special cased (#60)
|
||||
if c == '\n' {
|
||||
continue;
|
||||
}
|
||||
assert_width!(
|
||||
format!("\u{17D2}{c}"),
|
||||
c.width().unwrap_or(1),
|
||||
c.width_cjk().unwrap_or(1)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_khmer_qaa() {
|
||||
assert_width!("\u{17A4}", 2, 2);
|
||||
assert_width!("\u{17A2}\u{17A6}", 2, 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_khmer_sign_beyyal() {
|
||||
assert_width!("\u{17D8}", 3, 3);
|
||||
assert_width!("\u{17D4}\u{179B}\u{17D4}", 3, 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_emoji_modifier() {
|
||||
assert_width!("\u{1F46A}", 2, 2);
|
||||
assert_width!("\u{1F3FB}", 2, 2);
|
||||
assert_width!("\u{1F46A}\u{1F3FB}", 2, 2);
|
||||
assert_width!("\u{1F46A}\u{200D}\u{200D}\u{1F3FB}", 4, 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_emoji_zwj() {
|
||||
assert_width!("🧑🤝🧑", 2, 2);
|
||||
|
||||
assert_width!("🇮🇱🕊️🇵🇸", 6, 6);
|
||||
assert_width!("🇵🇸\u{200D}🕊️\u{200D}🇮🇱", 2, 2);
|
||||
assert_width!("🇮🇱\u{200D}🕊️\u{200D}\u{200D}🇵🇸", 4, 4);
|
||||
assert_width!("🇵🇸\u{200D}\u{200D}🕊️\u{200D}🇮🇱", 4, 4);
|
||||
|
||||
assert_width!("🇦🇦\u{200D}🇦🇦", 2, 2);
|
||||
assert_width!("🇦🇦\u{200D}🇦🇦🇦", 3, 3);
|
||||
assert_width!("🇦🇦\u{200D}🇦🇦🇦", 3, 3);
|
||||
|
||||
assert_width!("🇦🇦\u{200D}\u{200D}🇦🇦", 4, 4);
|
||||
assert_width!("🇦🇦\u{200D}🇦\u{200D}🇦🇦", 5, 5);
|
||||
assert_width!("🇦🇦\u{200D}🇦🇦\u{200D}🇦🇦", 2, 2);
|
||||
assert_width!("🇦🇦\u{200D}🇦🇦🇦\u{200D}🇦🇦", 5, 5);
|
||||
assert_width!("🇦🇦\u{200D}🇦🇦🇦🇦\u{200D}🇦🇦", 4, 4);
|
||||
assert_width!("🇦🇦\u{200D}🇦🇦🇦🇦🇦\u{200D}🇦🇦", 7, 7);
|
||||
assert_width!("🇦🇦\u{200D}🇦🇦🇦🇦🇦🇦\u{200D}🇦🇦", 6, 6);
|
||||
assert_width!("🇦🇦\u{200D}🇦🇦🇦🇦🇦🇦🇦\u{200D}🇦🇦", 9, 9);
|
||||
|
||||
assert_width!("🏴", 2, 2);
|
||||
assert_width!("🏴\u{200D}🏴\u{200D}🏴", 2, 2);
|
||||
|
||||
assert_width!("🇦👪\u{200D}🏿", 3, 3);
|
||||
assert_width!("🇦🏿\u{200D}🏿", 3, 3);
|
||||
|
||||
assert_width!('🏴', Some(2), Some(2));
|
||||
assert_width!("\u{E0031}", 0, 0);
|
||||
assert_width!("\u{E0063}", 0, 0);
|
||||
assert_width!("\u{E007F}", 0, 0);
|
||||
assert_width!("🏴\u{200D}Ⓜ️", 2, 2);
|
||||
assert_width!("🏴\u{E0031}\u{200D}Ⓜ️", 4, 4);
|
||||
assert_width!("🏴\u{E0063}\u{200D}Ⓜ️", 4, 4);
|
||||
assert_width!("🏴\u{E007F}\u{200D}Ⓜ️", 4, 4);
|
||||
assert_width!("🏴\u{E0031}\u{E007F}\u{200D}Ⓜ️", 4, 4);
|
||||
assert_width!("🏴\u{E0031}\u{E0031}\u{E007F}\u{200D}Ⓜ️", 4, 4);
|
||||
assert_width!("🏴\u{E0031}\u{E0031}\u{E0031}\u{E007F}\u{200D}Ⓜ️", 2, 2);
|
||||
assert_width!(
|
||||
"🏴\u{E0031}\u{E0031}\u{E0031}\u{E0031}\u{E007F}\u{200D}Ⓜ️",
|
||||
4,
|
||||
4,
|
||||
);
|
||||
assert_width!(
|
||||
"🏴\u{E0031}\u{E0031}\u{E0031}\u{E0063}\u{E007F}\u{200D}Ⓜ️",
|
||||
2,
|
||||
2,
|
||||
);
|
||||
assert_width!(
|
||||
"🏴\u{E0031}\u{E0031}\u{E0031}\u{E0063}\u{E0063}\u{E007F}\u{200D}Ⓜ️",
|
||||
2,
|
||||
2,
|
||||
);
|
||||
assert_width!(
|
||||
"🏴\u{E0031}\u{E0031}\u{E0031}\u{E0063}\u{E0063}\u{E0063}\u{E007F}\u{200D}Ⓜ️",
|
||||
2,
|
||||
2,
|
||||
);
|
||||
assert_width!(
|
||||
"🏴\u{E0031}\u{E0031}\u{E0031}\u{E0063}\u{E0063}\u{E0063}\u{E0063}\u{E007F}\u{200D}Ⓜ️",
|
||||
2,
|
||||
2,
|
||||
);
|
||||
assert_width!(
|
||||
"🏴\u{E0031}\u{E0031}\u{E0031}\u{E0063}\u{E0063}\u{E0063}\u{E0063}\u{E0063}\u{E007F}\u{200D}Ⓜ️",
|
||||
4,
|
||||
4,
|
||||
);
|
||||
assert_width!("🏴\u{E0063}\u{E0063}\u{E007F}\u{200D}Ⓜ️", 4, 4);
|
||||
assert_width!("🏴\u{E0063}\u{E0063}\u{E0063}\u{E007F}\u{200D}Ⓜ️", 2, 2);
|
||||
assert_width!(
|
||||
"🏴\u{E0063}\u{E0063}\u{E0063}\u{E0063}\u{E007F}\u{200D}Ⓜ️",
|
||||
2,
|
||||
2,
|
||||
);
|
||||
assert_width!(
|
||||
"🏴\u{E0063}\u{E0063}\u{E0063}\u{E0063}\u{E0063}\u{E007F}\u{200D}Ⓜ️",
|
||||
2,
|
||||
2,
|
||||
);
|
||||
assert_width!(
|
||||
"🏴\u{E0063}\u{E0063}\u{E0063}\u{E0063}\u{E0063}\u{E0063}\u{E007F}\u{200D}Ⓜ️",
|
||||
2,
|
||||
2,
|
||||
);
|
||||
assert_width!(
|
||||
"🏴\u{E0063}\u{E0063}\u{E0063}\u{E0063}\u{E0063}\u{E0063}\u{E0063}\u{E007F}\u{200D}Ⓜ️",
|
||||
4,
|
||||
4,
|
||||
);
|
||||
|
||||
assert_width!("a\u{200D}🏴", 3, 3);
|
||||
assert_width!("👪\u{200D}a", 3, 3);
|
||||
assert_width!("a\u{200D}a", 2, 2);
|
||||
|
||||
assert_width!("*\u{FE0F}", 2, 2);
|
||||
assert_width!("*\u{20E3}", 1, 1);
|
||||
assert_width!("*️⃣", 2, 2);
|
||||
assert_width!("*\u{FE0F}", 2, 2);
|
||||
assert_width!("*\u{20E3}\u{FE0F}", 1, 1);
|
||||
assert_width!("*️⃣\u{200D}👪", 2, 2);
|
||||
assert_width!("*\u{20E3}\u{FE0F}\u{200D}👪", 3, 3);
|
||||
assert_width!("*\u{20E3}\u{200D}👪", 3, 3);
|
||||
assert_width!("*\u{FE0F}\u{200D}👪", 2, 2);
|
||||
assert_width!("*️⃣\u{20E3}\u{200D}👪", 4, 4);
|
||||
assert_width!("*\u{FE0F}\u{FE0F}\u{20E3}\u{200D}👪", 4, 4);
|
||||
|
||||
assert_width!(
|
||||
"🇦👪\u{200D}🏿\u{200D}👪🏻\u{200D}Ⓜ️\u{200D}*\u{FE0F}\u{200D}🇦🇦\u{200D}🏴\u{200D}👪",
|
||||
3,
|
||||
3,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn emoji_test_file() {
|
||||
let norm_file = BufReader::new(
|
||||
File::open("tests/emoji-test.txt")
|
||||
.expect("run `unicode.py` first to download `emoji-test.txt`"),
|
||||
);
|
||||
for line in norm_file.lines() {
|
||||
let line = line.unwrap();
|
||||
if line.is_empty() || line.starts_with('#') {
|
||||
continue;
|
||||
}
|
||||
|
||||
let (cps, status) = line.split_once(';').unwrap();
|
||||
let status = status.trim();
|
||||
if status.starts_with("fully-qualified") || status.starts_with("component") {
|
||||
let emoji: String = cps
|
||||
.trim()
|
||||
.split(' ')
|
||||
.map(|s| char::try_from(u32::from_str_radix(s, 16).unwrap()).unwrap())
|
||||
.collect();
|
||||
dbg!(&emoji);
|
||||
assert_width!(emoji, 2, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_newline_zero_issue_60() {
|
||||
assert_width!("a\na", 2, 2);
|
||||
}
|
||||
|
||||
// Test traits are unsealed
|
||||
|
||||
#[cfg(feature = "cjk")]
|
||||
#[allow(dead_code)]
|
||||
struct Foo;
|
||||
|
||||
#[cfg(feature = "cjk")]
|
||||
impl UnicodeWidthChar for Foo {
|
||||
fn width(self) -> Option<usize> {
|
||||
Some(0)
|
||||
}
|
||||
|
||||
fn width_cjk(self) -> Option<usize> {
|
||||
Some(0)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "cjk")]
|
||||
impl UnicodeWidthStr for Foo {
|
||||
fn width(&self) -> usize {
|
||||
0
|
||||
}
|
||||
|
||||
fn width_cjk(&self) -> usize {
|
||||
0
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user