Skip to content

Commit 7a7fcdc

Browse files
Support Unicode 16 (#74)
* Unicode 16: Initial support Includes Kirat Rai normalization behavior. * Support Unicode 16 variation seqs for quotation mark width * Update emoji-test.txt * Remove workaround for Unicode bug fixed in 16.0
1 parent 82d7136 commit 7a7fcdc

File tree

5 files changed

+1349
-198
lines changed

5 files changed

+1349
-198
lines changed

scripts/unicode.py

+105-24
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
from itertools import batched
4444
from typing import Callable, Iterable
4545

46-
UNICODE_VERSION = "15.1.0"
46+
UNICODE_VERSION = "16.0.0"
4747
"""The version of the Unicode data files to download."""
4848

4949
NUM_CODEPOINTS = 0x110000
@@ -175,8 +175,11 @@ class WidthState(enum.IntEnum):
175175
- 4th bit: whether to set top bit on emoji presentation.
176176
If this is set but 3rd is not, the width mode is related to zwj sequences
177177
- 5th from top: whether this is unaffected by ligature-transparent
178+
(if set, should also set 3rd and 4th)
178179
- 6th bit: if 4th is set but this one is not, then this is a ZWJ ligature state
179-
where no ZWJ has been encountered yet; encountering one flips this on"""
180+
where no ZWJ has been encountered yet; encountering one flips this on
181+
- Seventh bit: is VS1 (if CJK) or is VS2 (not CJK)
182+
"""
180183

181184
# BASIC WIDTHS
182185

@@ -264,8 +267,17 @@ class WidthState(enum.IntEnum):
264267
TAG_A6_END_ZWJ_EMOJI_PRESENTATION = 0b0000_0000_0001_1110
265268
"(\\uE0061..=\\uE007A){6} \\uE007F \\u200D `Emoji_Presentation`"
266269

270+
# Kirat Rai
271+
KIRAT_RAI_VOWEL_SIGN_E = 0b0000_0000_0010_0000
272+
"\\u16D67 (\\u16D67 \\u16D67)+ and canonical equivalents"
273+
KIRAT_RAI_VOWEL_SIGN_AI = 0b0000_0000_0010_0001
274+
"(\\u16D68)+ and canonical equivalents"
275+
267276
# VARIATION SELECTORS
268277

278+
VARIATION_SELECTOR_1_OR_2 = 0b0000_0010_0000_0000
279+
"\\uFE00 if CJK, or \\uFE01 otherwise"
280+
269281
# Text presentation sequences (not CJK)
270282
VARIATION_SELECTOR_15 = 0b0100_0000_0000_0000
271283
"\\uFE0E (text presentation sequences)"
@@ -361,6 +373,7 @@ def width_alone(self) -> int:
361373
| WidthState.COMBINING_LONG_SOLIDUS_OVERLAY
362374
| WidthState.VARIATION_SELECTOR_15
363375
| WidthState.VARIATION_SELECTOR_16
376+
| WidthState.VARIATION_SELECTOR_1_OR_2
364377
):
365378
return 0
366379
case (
@@ -493,12 +506,6 @@ def load_zero_widths() -> list[bool]:
493506
lambda cp: operator.setitem(zw_map, cp, True),
494507
)
495508

496-
# Unicode spec bug: these should be `Grapheme_Cluster_Break=Extend`,
497-
# as they canonically decompose to two characters with this property,
498-
# but they aren't.
499-
for c in [0x0CC0, 0x0CC7, 0x0CC8, 0x0CCA, 0x0CCB, 0x1B3B, 0x1B3D, 0x1B43]:
500-
zw_map[c] = True
501-
502509
# Treat `Hangul_Syllable_Type`s of `Vowel_Jamo` and `Trailing_Jamo`
503510
# as zero-width. This matches the behavior of glibc `wcwidth`.
504511
#
@@ -639,6 +646,8 @@ def load_width_maps() -> tuple[list[WidthState], list[WidthState]]:
639646
([0xA4FD], WidthState.LISU_TONE_LETTER_MYA_NA_JEU),
640647
([0xFE0F], WidthState.VARIATION_SELECTOR_16),
641648
([0x10C03], WidthState.OLD_TURKIC_LETTER_ORKHON_I),
649+
([0x16D67], WidthState.KIRAT_RAI_VOWEL_SIGN_E),
650+
([0x16D68], WidthState.KIRAT_RAI_VOWEL_SIGN_AI),
642651
(emoji_presentation, WidthState.EMOJI_PRESENTATION),
643652
(emoji_modifiers, WidthState.EMOJI_MODIFIER),
644653
(regional_indicators, WidthState.REGIONAL_INDICATOR),
@@ -648,9 +657,11 @@ def load_width_maps() -> tuple[list[WidthState], list[WidthState]]:
648657
ea[cp] = width
649658

650659
# East-Asian only
660+
ea[0xFE00] = WidthState.VARIATION_SELECTOR_1_OR_2
651661
ea[0x0338] = WidthState.COMBINING_LONG_SOLIDUS_OVERLAY
652662

653663
# Not East Asian only
664+
not_ea[0xFE01] = WidthState.VARIATION_SELECTOR_1_OR_2
654665
not_ea[0xFE0E] = WidthState.VARIATION_SELECTOR_15
655666

656667
return (not_ea, ea)
@@ -716,7 +727,7 @@ def load_solidus_transparent(
716727
cjk_width_map: list[WidthState],
717728
) -> list[tuple[Codepoint, Codepoint]]:
718729
"""Characters expanding to a canonical combining class above 1, plus `ligature_transparent`s from above.
719-
Ranges matching ones in `ligature_transparent` exactly are excluded (for compression), so it needs to bechecked also.
730+
Ranges matching ones in `ligature_transparent` exactly are excluded (for compression), so it needs to be checked also.
720731
"""
721732

722733
ccc_above_1 = set()
@@ -748,7 +759,7 @@ def load_solidus_transparent(
748759
num_chars = len(ccc_above_1)
749760

750761
for cp in ccc_above_1:
751-
if cp != 0xFE0F:
762+
if cp not in [0xFE00, 0xFE0F]:
752763
assert (
753764
cjk_width_map[cp].table_width() != CharWidthInTable.SPECIAL
754765
), f"U+{cp:X}"
@@ -1304,8 +1315,17 @@ def lookup_fns(
13041315
return (0, next_info.set_emoji_presentation());
13051316
}"""
13061317

1307-
if not is_cjk:
1318+
if is_cjk:
13081319
s += """
1320+
if c == '\\u{FE00}' {
1321+
return (0, next_info.set_vs1_2());
1322+
}
1323+
"""
1324+
else:
1325+
s += """
1326+
if c == '\\u{FE01}' {
1327+
return (0, next_info.set_vs1_2());
1328+
}
13091329
if c == '\\u{FE0E}' {
13101330
return (0, next_info.set_text_presentation());
13111331
}
@@ -1315,9 +1335,19 @@ def lookup_fns(
13151335
} else {
13161336
next_info = next_info.unset_text_presentation();
13171337
}
1318-
}"""
1338+
} else """
13191339

1320-
s += """
1340+
s += """if next_info.is_vs1_2() {
1341+
if matches!(c, '\\u{2018}' | '\\u{2019}' | '\\u{201C}' | '\\u{201D}') {
1342+
return ("""
1343+
1344+
s += str(2 - is_cjk)
1345+
1346+
s += """, WidthInfo::DEFAULT);
1347+
} else {
1348+
next_info = next_info.unset_vs1_2();
1349+
}
1350+
}
13211351
if next_info.is_ligature_transparent() {
13221352
if c == '\\u{200D}' {
13231353
return (0, next_info.set_zwj_bit());
@@ -1496,6 +1526,22 @@ def lookup_fns(
14961526
return (0, WidthInfo::EMOJI_PRESENTATION)
14971527
}}
14981528
1529+
(WidthInfo::KIRAT_RAI_VOWEL_SIGN_E, '\\u{{16D63}}') => {{
1530+
return (0, WidthInfo::DEFAULT);
1531+
}}
1532+
(WidthInfo::KIRAT_RAI_VOWEL_SIGN_E, '\\u{{16D67}}') => {{
1533+
return (0, WidthInfo::KIRAT_RAI_VOWEL_SIGN_AI);
1534+
}}
1535+
(WidthInfo::KIRAT_RAI_VOWEL_SIGN_E, '\\u{{16D68}}') => {{
1536+
return (1, WidthInfo::KIRAT_RAI_VOWEL_SIGN_E);
1537+
}}
1538+
(WidthInfo::KIRAT_RAI_VOWEL_SIGN_E, '\\u{{16D69}}') => {{
1539+
return (0, WidthInfo::DEFAULT);
1540+
}}
1541+
(WidthInfo::KIRAT_RAI_VOWEL_SIGN_AI, '\\u{{16D63}}') => {{
1542+
return (0, WidthInfo::DEFAULT);
1543+
}}
1544+
14991545
// Fallback
15001546
_ => {{}}
15011547
}}
@@ -1562,6 +1608,8 @@ def emit_module(
15621608
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
15631609
struct WidthInfo(u16);
15641610
1611+
const LIGATURE_TRANSPARENT_MASK: u16 = 0b0010_0000_0000_0000;
1612+
15651613
impl WidthInfo {
15661614
/// No special handling necessary
15671615
const DEFAULT: Self = Self(0);
@@ -1591,51 +1639,84 @@ def emit_module(
15911639
15921640
/// Has top bit set
15931641
fn is_emoji_presentation(self) -> bool {{
1594-
(self.0 & 0b1000_0000_0000_0000) == 0b1000_0000_0000_0000
1642+
(self.0 & WidthInfo::VARIATION_SELECTOR_16.0) == WidthInfo::VARIATION_SELECTOR_16.0
15951643
}}
15961644
1597-
/// Has top bit set
15981645
fn is_zwj_emoji_presentation(self) -> bool {{
15991646
(self.0 & 0b1011_0000_0000_0000) == 0b1001_0000_0000_0000
16001647
}}
16011648
16021649
/// Set top bit
16031650
fn set_emoji_presentation(self) -> Self {{
1604-
if (self.0 & 0b0010_0000_0000_0000) == 0b0010_0000_0000_0000
1651+
if (self.0 & LIGATURE_TRANSPARENT_MASK) == LIGATURE_TRANSPARENT_MASK
16051652
|| (self.0 & 0b1001_0000_0000_0000) == 0b0001_0000_0000_0000
16061653
{{
1607-
Self(self.0 | 0b1000_0000_0000_0000)
1654+
Self(
1655+
self.0
1656+
| WidthInfo::VARIATION_SELECTOR_16.0
1657+
& !WidthInfo::VARIATION_SELECTOR_15.0
1658+
& !WidthInfo::VARIATION_SELECTOR_1_OR_2.0,
1659+
)
16081660
}} else {{
16091661
Self::VARIATION_SELECTOR_16
16101662
}}
16111663
}}
16121664
16131665
/// Clear top bit
16141666
fn unset_emoji_presentation(self) -> Self {{
1615-
if (self.0 & 0b0010_0000_0000_0000) == 0b0010_0000_0000_0000 {{
1616-
Self(self.0 & 0b0111_1111_1111_1111)
1667+
if (self.0 & LIGATURE_TRANSPARENT_MASK) == LIGATURE_TRANSPARENT_MASK {{
1668+
Self(self.0 & !WidthInfo::VARIATION_SELECTOR_16.0)
16171669
}} else {{
16181670
Self::DEFAULT
16191671
}}
16201672
}}
16211673
16221674
/// Has 2nd bit set
16231675
fn is_text_presentation(self) -> bool {{
1624-
(self.0 & 0b0100_0000_0000_0000) == 0b0100_0000_0000_0000
1676+
(self.0 & WidthInfo::VARIATION_SELECTOR_15.0) == WidthInfo::VARIATION_SELECTOR_15.0
16251677
}}
16261678
16271679
/// Set 2nd bit
16281680
fn set_text_presentation(self) -> Self {{
1629-
if (self.0 & 0b0010_0000_0000_0000) == 0b0010_0000_0000_0000 {{
1630-
Self(self.0 | 0b0100_0000_0000_0000)
1681+
if (self.0 & LIGATURE_TRANSPARENT_MASK) == LIGATURE_TRANSPARENT_MASK {{
1682+
Self(
1683+
self.0
1684+
| WidthInfo::VARIATION_SELECTOR_15.0
1685+
& !WidthInfo::VARIATION_SELECTOR_16.0
1686+
& !WidthInfo::VARIATION_SELECTOR_1_OR_2.0,
1687+
)
16311688
}} else {{
1632-
Self(0b0100_0000_0000_0000)
1689+
Self(WidthInfo::VARIATION_SELECTOR_15.0)
16331690
}}
16341691
}}
16351692
16361693
/// Clear 2nd bit
16371694
fn unset_text_presentation(self) -> Self {{
1638-
Self(self.0 & 0b1011_1111_1111_1111)
1695+
Self(self.0 & !WidthInfo::VARIATION_SELECTOR_15.0)
1696+
}}
1697+
1698+
/// Has 7th bit set
1699+
fn is_vs1_2(self) -> bool {{
1700+
(self.0 & WidthInfo::VARIATION_SELECTOR_1_OR_2.0) == WidthInfo::VARIATION_SELECTOR_1_OR_2.0
1701+
}}
1702+
1703+
/// Set 7th bit
1704+
fn set_vs1_2(self) -> Self {{
1705+
if (self.0 & LIGATURE_TRANSPARENT_MASK) == LIGATURE_TRANSPARENT_MASK {{
1706+
Self(
1707+
self.0
1708+
| WidthInfo::VARIATION_SELECTOR_1_OR_2.0
1709+
& !WidthInfo::VARIATION_SELECTOR_15.0
1710+
& !WidthInfo::VARIATION_SELECTOR_16.0,
1711+
)
1712+
}} else {{
1713+
Self(WidthInfo::VARIATION_SELECTOR_1_OR_2.0)
1714+
}}
1715+
}}
1716+
1717+
/// Clear 7th bit
1718+
fn unset_vs1_2(self) -> Self {{
1719+
Self(self.0 & !WidthInfo::VARIATION_SELECTOR_1_OR_2.0)
16391720
}}
16401721
}}
16411722

src/lib.rs

+21-25
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,12 @@
6262
//! - Outside of an East Asian context, [text presentation sequences] have width 1 if their base character:
6363
//! - Has the [`Emoji_Presentation`] property, and
6464
//! - Is not in the [Enclosed Ideographic Supplement] block.
65+
//! - [`'\u{2018}'`, `'\u{2019}'`, `'\u{201C}'`, and `'\u{201D}'`][General Punctuation] always have width 1 when followed by '\u{FE00}',
66+
//! and width 2 when followed by '\u{FE01}'.
6567
//! - Script-specific ligatures:
6668
//! - For all the following ligatures, the insertion of any number of [default-ignorable][`Default_Ignorable_Code_Point`]
6769
//! [combining marks] anywhere in the sequence will not change the total width. In addition, for all non-Arabic
68-
//! ligatures, the insertion of any number of [`'\u{200D}'` ZERO WIDTH JOINER](https://www.unicode.org/versions/Unicode15.0.0/ch23.pdf#G23126)s
70+
//! ligatures, the insertion of any number of [`'\u{200D}'` ZERO WIDTH JOINER](https://www.unicode.org/versions/Unicode16.0.0/core-spec/chapter-23/#G23126)s
6971
//! will not affect the width.
7072
//! - **[Arabic]**: A character sequence consisting of one character with [`Joining_Group`]`=Lam`,
7173
//! followed by any number of characters with [`Joining_Type`]`=Transparent`, followed by one character
@@ -75,6 +77,7 @@
7577
//! - **[Khmer]**: Coeng signs consisting of `'\u{17D2}'` followed by a character in
7678
//! `'\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}'`
7779
//! have width 0.
80+
//! - **[Kirat Rai]**: Any sequence canonically equivalent to `'\u{16D68}'`, `'\u{16D69}'`, or `'\u{16D6A}'` has total width 1.
7881
//! - **[Lisu]**: Tone letter combinations consisting of a character in the range `'\u{A4F8}'..='\u{A4FB}'`
7982
//! followed by a character in the range `'\u{A4FC}'..='\u{A4FD}'` have width 1. For example: `ꓹꓼ`
8083
//! - **[Old Turkic]**: `"\u{10C32}\u{200D}\u{10C03}"` (`𐰲‍𐰃`) has total width 1.
@@ -96,15 +99,6 @@
9699
//! with the [`Default_Ignorable_Code_Point`] property.
97100
//! - [Characters](https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=%5Cp%7BGrapheme_Extend%7D)
98101
//! with the [`Grapheme_Extend`] property.
99-
//! - The following 8 characters, all of which have NFD decompositions consisting of two [`Grapheme_Extend`] characters:
100-
//! - [`'\u{0CC0}'` KANNADA VOWEL SIGN II](https://util.unicode.org/UnicodeJsps/character.jsp?a=0CC0),
101-
//! - [`'\u{0CC7}'` KANNADA VOWEL SIGN EE](https://util.unicode.org/UnicodeJsps/character.jsp?a=0CC7),
102-
//! - [`'\u{0CC8}'` KANNADA VOWEL SIGN AI](https://util.unicode.org/UnicodeJsps/character.jsp?a=0CC8),
103-
//! - [`'\u{0CCA}'` KANNADA VOWEL SIGN O](https://util.unicode.org/UnicodeJsps/character.jsp?a=0CCA),
104-
//! - [`'\u{0CCB}'` KANNADA VOWEL SIGN OO](https://util.unicode.org/UnicodeJsps/character.jsp?a=0CCB),
105-
//! - [`'\u{1B3B}'` BALINESE VOWEL SIGN RA REPA TEDUNG](https://util.unicode.org/UnicodeJsps/character.jsp?a=1B3B),
106-
//! - [`'\u{1B3D}'` BALINESE VOWEL SIGN LA LENGA TEDUNG](https://util.unicode.org/UnicodeJsps/character.jsp?a=1B3D), and
107-
//! - [`'\u{1B43}'` BALINESE VOWEL SIGN PEPET TEDUNG](https://util.unicode.org/UnicodeJsps/character.jsp?a=1B43).
108102
//! - [Characters](https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=%5Cp%7BHangul_Syllable_Type%3DV%7D%5Cp%7BHangul_Syllable_Type%3DT%7D)
109103
//! with a [`Hangul_Syllable_Type`] of `Vowel_Jamo` (`V`) or `Trailing_Jamo` (`T`).
110104
//! - The following [`Prepended_Concatenation_Mark`]s:
@@ -130,18 +124,18 @@
130124
//! [`'\u{0338}'` COMBINING LONG SOLIDUS OVERLAY]: https://util.unicode.org/UnicodeJsps/character.jsp?a=0338
131125
//! [`'\u{2D7F}'` TIFINAGH CONSONANT JOINER]: https://util.unicode.org/UnicodeJsps/character.jsp?a=2D7F
132126
//!
133-
//! [`Canonical_Combining_Class`]: https://www.unicode.org/versions/Unicode15.0.0/ch03.pdf#G50313
134-
//! [`Default_Ignorable_Code_Point`]: https://www.unicode.org/versions/Unicode15.0.0/ch05.pdf#G40095
127+
//! [`Canonical_Combining_Class`]: https://www.unicode.org/versions/Unicode16.0.0/core-spec/chapter-3/#G50313
128+
//! [`Default_Ignorable_Code_Point`]: https://www.unicode.org/versions/Unicode16.0.0/core-spec/chapter-5/#G40095
135129
//! [`East_Asian_Width`]: https://www.unicode.org/reports/tr11/#ED1
136130
//! [`Emoji_Presentation`]: https://unicode.org/reports/tr51/#def_emoji_presentation
137-
//! [`General_Category`]: https://www.unicode.org/versions/Unicode15.0.0/ch04.pdf#G124142
131+
//! [`General_Category`]: https://www.unicode.org/versions/Unicode16.0.0/core-spec/chapter-4/#G124142
138132
//! [`Grapheme_Extend=Prepend`]: https://www.unicode.org/reports/tr29/#Prepend
139-
//! [`Grapheme_Extend`]: https://www.unicode.org/versions/Unicode15.0.0/ch03.pdf#G52443
140-
//! [`Hangul_Syllable_Type`]: https://www.unicode.org/versions/Unicode15.0.0/ch03.pdf#G45593
133+
//! [`Grapheme_Extend`]: https://www.unicode.org/versions/Unicode16.0.0/core-spec/chapter-3/#G52443
134+
//! [`Hangul_Syllable_Type`]: https://www.unicode.org/versions/Unicode16.0.0/core-spec/chapter-3/#G45593
141135
//! [`Joining_Group`]: https://www.unicode.org/versions/Unicode14.0.0/ch09.pdf#G36862
142-
//! [`Joining_Type`]: http://www.unicode.org/versions/Unicode15.0.0/ch09.pdf#G50009
136+
//! [`Joining_Type`]: http://www.unicode.org/versions/Unicode16.0.0/core-spec/chapter-9/#G50009
143137
//! [`Line_Break`]: https://www.unicode.org/reports/tr14/#LD5
144-
//! [`Prepended_Concatenation_Mark`]: https://www.unicode.org/versions/Unicode15.0.0/ch23.pdf#G37908
138+
//! [`Prepended_Concatenation_Mark`]: https://www.unicode.org/versions/Unicode16.0.0/core-spec/chapter-23/#G37908
145139
//! [`Script`]: https://www.unicode.org/reports/tr24/#Script
146140
//!
147141
//! [`Fullwidth`]: https://www.unicode.org/reports/tr11/#ED2
@@ -150,22 +144,24 @@
150144
//!
151145
//! [`AI`]: https://www.unicode.org/reports/tr14/#AI
152146
//!
153-
//! [combining marks]: https://www.unicode.org/versions/Unicode15.0.0/ch03.pdf#G30602
147+
//! [combining marks]: https://www.unicode.org/versions/Unicode16.0.0/core-spec/chapter-3/#G30602
154148
//!
155149
//! [emoji ZWJ sequences]: https://www.unicode.org/reports/tr51/#def_emoji_sequence
156150
//! [Emoji modifier sequences]: https://www.unicode.org/reports/tr51/#def_emoji_modifier_sequence
157151
//! [Emoji presentation sequences]: https://unicode.org/reports/tr51/#def_emoji_presentation_sequence
158152
//! [text presentation sequences]: https://unicode.org/reports/tr51/#def_text_presentation_sequence
159153
//!
154+
//! [General Punctuation]: https://www.unicode.org/charts/PDF/Unicode-16.0/U160-2000.pdf
160155
//! [Enclosed Ideographic Supplement]: https://unicode.org/charts/nameslist/n_1F200.html
161156
//!
162-
//! [Arabic]: https://www.unicode.org/versions/Unicode15.0.0/ch09.pdf#G7480
163-
//! [Buginese]: https://www.unicode.org/versions/Unicode15.0.0/ch17.pdf#G26743
164-
//! [Hebrew]: https://www.unicode.org/versions/Unicode15.0.0/ch09.pdf#G6528
165-
//! [Khmer]: https://www.unicode.org/versions/Unicode15.0.0/ch16.pdf#G64642
166-
//! [Lisu]: https://www.unicode.org/versions/Unicode15.0.0/ch18.pdf#G44587
167-
//! [Old Turkic]: https://www.unicode.org/versions/Unicode15.0.0/ch14.pdf#G41975
168-
//! [Tifinagh]: http://www.unicode.org/versions/Unicode15.0.0/ch19.pdf#G43184
157+
//! [Arabic]: https://www.unicode.org/versions/Unicode16.0.0/core-spec/chapter-9/#G7480
158+
//! [Buginese]: https://www.unicode.org/versions/Unicode16.0.0/core-spec/chapter-17/#G26743
159+
//! [Hebrew]: https://www.unicode.org/versions/Unicode16.0.0/core-spec/chapter-9/#G6528
160+
//! [Khmer]: https://www.unicode.org/versions/Unicode16.0.0/core-spec/chapter-16/#G64642
161+
//! [Kirat Rai]: https://www.unicode.org/versions/Unicode16.0.0/core-spec/chapter-13/#G746409
162+
//! [Lisu]: https://www.unicode.org/versions/Unicode16.0.0/core-spec/chapter-18/#G44587
163+
//! [Old Turkic]: https://www.unicode.org/versions/Unicode16.0.0/core-spec/chapter-14/#G41975
164+
//! [Tifinagh]: http://www.unicode.org/versions/Unicode16.0.0/core-spec/chapter-19/#G43184
169165
//!
170166
//!
171167
//! ## Canonical equivalence

0 commit comments

Comments
 (0)