@flighthq/textbidi 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bidiClassBackend.d.ts +5 -0
- package/dist/bidiClassBackend.d.ts.map +1 -0
- package/dist/bidiClassBackend.js +236 -0
- package/dist/bidiClassBackend.js.map +1 -0
- package/dist/getBidiRuns.d.ts +3 -0
- package/dist/getBidiRuns.d.ts.map +1 -0
- package/dist/getBidiRuns.js +21 -0
- package/dist/getBidiRuns.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/reorderBidiLine.d.ts +2 -0
- package/dist/reorderBidiLine.d.ts.map +1 -0
- package/dist/reorderBidiLine.js +44 -0
- package/dist/reorderBidiLine.js.map +1 -0
- package/dist/resolveBidiLevels.d.ts +3 -0
- package/dist/resolveBidiLevels.d.ts.map +1 -0
- package/dist/resolveBidiLevels.js +432 -0
- package/dist/resolveBidiLevels.js.map +1 -0
- package/package.json +37 -0
- package/src/bidiClassBackend.test.ts +53 -0
- package/src/getBidiRuns.test.ts +24 -0
- package/src/reorderBidiLine.test.ts +52 -0
- package/src/resolveBidiLevels.test.ts +48 -0
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { BidiClassBackend } from '@flighthq/types';
|
|
2
|
+
export declare function createCompactBidiClassBackend(): BidiClassBackend;
|
|
3
|
+
export declare function getBidiClassBackend(): BidiClassBackend;
|
|
4
|
+
export declare function setBidiClassBackend(backend: BidiClassBackend | null): void;
|
|
5
|
+
//# sourceMappingURL=bidiClassBackend.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bidiClassBackend.d.ts","sourceRoot":"","sources":["../src/bidiClassBackend.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAa,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAUnE,wBAAgB,6BAA6B,IAAI,gBAAgB,CAEhE;AAKD,wBAAgB,mBAAmB,IAAI,gBAAgB,CAGtD;AAMD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI,GAAG,IAAI,CAE1E"}
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
// Builds the compact bundled bidi-class backend: a from-scratch UAX #9 class lookup over a sorted
|
|
2
|
+
// range table (binary search), covering the COMMON scripts — Basic Latin + Latin-1, the combining
|
|
3
|
+
// diacritical marks (NSM), the Hebrew block (R), the Arabic blocks + Arabic presentation forms
|
|
4
|
+
// (AL/AN/R), European/Arabic numbers, and the explicit embedding/override/isolate format characters.
|
|
5
|
+
// It is a few KB of range data, not the full Unicode database. Scripts outside these ranges (CJK,
|
|
6
|
+
// Indic, Thaana, N'Ko, Syriac, and the rest of Unicode) are NOT covered here — a codepoint with no
|
|
7
|
+
// range resolves to the safe common default 'L'. Full Unicode coverage (every assigned bidi class +
|
|
8
|
+
// bracket-pairing data) is the designated flight-rs table backend, swapped in via setBidiClassBackend.
|
|
9
|
+
export function createCompactBidiClassBackend() {
|
|
10
|
+
return { getBidiClass: getCompactBidiClass };
|
|
11
|
+
}
|
|
12
|
+
// Returns the active bidi-class backend, lazily creating the compact default the first time so there
|
|
13
|
+
// is always an answer. A full-coverage backend (flight-rs) replaces it via setBidiClassBackend;
|
|
14
|
+
// passing null there restores this lazy compact default.
|
|
15
|
+
export function getBidiClassBackend() {
|
|
16
|
+
if (_backend === null)
|
|
17
|
+
_backend = createCompactBidiClassBackend();
|
|
18
|
+
return _backend;
|
|
19
|
+
}
|
|
20
|
+
// Installs a bidi-class backend; pass null to fall back to the lazily-created compact default. Last
|
|
21
|
+
// write wins — registering over an existing backend replaces it. Opt-in and side-effect-free at
|
|
22
|
+
// import: nothing installs until a host calls this (or a resolve*/getBidiRuns query lazily builds the
|
|
23
|
+
// compact one).
|
|
24
|
+
export function setBidiClassBackend(backend) {
|
|
25
|
+
_backend = backend;
|
|
26
|
+
}
|
|
27
|
+
let _backend = null;
|
|
28
|
+
// Looks up `codepoint` in the sorted, non-overlapping range table by binary search. Each entry is
|
|
29
|
+
// three flat numbers [start, end, classOrdinal]; a hit returns the range's class, a miss the common
|
|
30
|
+
// default 'L' (the UAX #9 default for the assigned L-script ranges; unassigned/uncovered codepoints
|
|
31
|
+
// resolve LTR under the compact table — the full flight-rs backend distinguishes them).
|
|
32
|
+
function getCompactBidiClass(codepoint) {
|
|
33
|
+
let lo = 0;
|
|
34
|
+
let hi = _rangeCount - 1;
|
|
35
|
+
while (lo <= hi) {
|
|
36
|
+
const mid = (lo + hi) >> 1;
|
|
37
|
+
const base = mid * 3;
|
|
38
|
+
const start = _ranges[base];
|
|
39
|
+
if (codepoint < start) {
|
|
40
|
+
hi = mid - 1;
|
|
41
|
+
}
|
|
42
|
+
else if (codepoint > _ranges[base + 1]) {
|
|
43
|
+
lo = mid + 1;
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
return _classOrder[_ranges[base + 2]];
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return 'L';
|
|
50
|
+
}
|
|
51
|
+
// Class ordinals used inside the flat range table, so the table is pure numbers (compact, and cheap to
|
|
52
|
+
// scan). Index into this array with an entry's third number to recover the BidiClass string.
|
|
53
|
+
const _classOrder = [
|
|
54
|
+
'L',
|
|
55
|
+
'R',
|
|
56
|
+
'AL',
|
|
57
|
+
'EN',
|
|
58
|
+
'ES',
|
|
59
|
+
'ET',
|
|
60
|
+
'AN',
|
|
61
|
+
'CS',
|
|
62
|
+
'NSM',
|
|
63
|
+
'BN',
|
|
64
|
+
'B',
|
|
65
|
+
'S',
|
|
66
|
+
'WS',
|
|
67
|
+
'ON',
|
|
68
|
+
'LRE',
|
|
69
|
+
'RLE',
|
|
70
|
+
'LRO',
|
|
71
|
+
'RLO',
|
|
72
|
+
'PDF',
|
|
73
|
+
'LRI',
|
|
74
|
+
'RLI',
|
|
75
|
+
'FSI',
|
|
76
|
+
'PDI',
|
|
77
|
+
];
|
|
78
|
+
const L = 0;
|
|
79
|
+
const R = 1;
|
|
80
|
+
const AL = 2;
|
|
81
|
+
const EN = 3;
|
|
82
|
+
const ES = 4;
|
|
83
|
+
const ET = 5;
|
|
84
|
+
const AN = 6;
|
|
85
|
+
const CS = 7;
|
|
86
|
+
const NSM = 8;
|
|
87
|
+
const BN = 9;
|
|
88
|
+
const B = 10;
|
|
89
|
+
const S = 11;
|
|
90
|
+
const WS = 12;
|
|
91
|
+
const ON = 13;
|
|
92
|
+
const LRE = 14;
|
|
93
|
+
const RLE = 15;
|
|
94
|
+
const LRO = 16;
|
|
95
|
+
const RLO = 17;
|
|
96
|
+
const PDF = 18;
|
|
97
|
+
const LRI = 19;
|
|
98
|
+
const RLI = 20;
|
|
99
|
+
const FSI = 21;
|
|
100
|
+
const PDI = 22;
|
|
101
|
+
// The sorted, non-overlapping range table, flattened to [start, end, classOrdinal] triples. Kept in
|
|
102
|
+
// ascending `start` order so getCompactBidiClass can binary-search it. Ranges omitted from the table
|
|
103
|
+
// fall through to the 'L' default. This is the common-script subset; see createCompactBidiClassBackend
|
|
104
|
+
// for the coverage boundary.
|
|
105
|
+
// prettier-ignore
|
|
106
|
+
const _ranges = [
|
|
107
|
+
// Basic Latin (C0 controls, ASCII punctuation, digits, letters)
|
|
108
|
+
0x0000, 0x0008, BN,
|
|
109
|
+
0x0009, 0x0009, S,
|
|
110
|
+
0x000a, 0x000a, B,
|
|
111
|
+
0x000b, 0x000b, S,
|
|
112
|
+
0x000c, 0x000c, WS,
|
|
113
|
+
0x000d, 0x000d, B,
|
|
114
|
+
0x000e, 0x001b, BN,
|
|
115
|
+
0x001c, 0x001e, B,
|
|
116
|
+
0x001f, 0x001f, S,
|
|
117
|
+
0x0020, 0x0020, WS,
|
|
118
|
+
0x0021, 0x0022, ON,
|
|
119
|
+
0x0023, 0x0025, ET,
|
|
120
|
+
0x0026, 0x002a, ON,
|
|
121
|
+
0x002b, 0x002b, ES,
|
|
122
|
+
0x002c, 0x002c, CS,
|
|
123
|
+
0x002d, 0x002d, ES,
|
|
124
|
+
0x002e, 0x002f, CS,
|
|
125
|
+
0x0030, 0x0039, EN,
|
|
126
|
+
0x003a, 0x003a, CS,
|
|
127
|
+
0x003b, 0x0040, ON,
|
|
128
|
+
0x0041, 0x005a, L,
|
|
129
|
+
0x005b, 0x0060, ON,
|
|
130
|
+
0x0061, 0x007a, L,
|
|
131
|
+
0x007b, 0x007e, ON,
|
|
132
|
+
0x007f, 0x0084, BN,
|
|
133
|
+
0x0085, 0x0085, B,
|
|
134
|
+
0x0086, 0x009f, BN,
|
|
135
|
+
// Latin-1 Supplement
|
|
136
|
+
0x00a0, 0x00a0, CS,
|
|
137
|
+
0x00a1, 0x00a1, ON,
|
|
138
|
+
0x00a2, 0x00a5, ET,
|
|
139
|
+
0x00a6, 0x00a9, ON,
|
|
140
|
+
0x00aa, 0x00aa, L,
|
|
141
|
+
0x00ab, 0x00ac, ON,
|
|
142
|
+
0x00ad, 0x00ad, BN,
|
|
143
|
+
0x00ae, 0x00af, ON,
|
|
144
|
+
0x00b0, 0x00b1, ET,
|
|
145
|
+
0x00b2, 0x00b3, EN,
|
|
146
|
+
0x00b4, 0x00b4, ON,
|
|
147
|
+
0x00b5, 0x00b5, L,
|
|
148
|
+
0x00b6, 0x00b8, ON,
|
|
149
|
+
0x00b9, 0x00b9, EN,
|
|
150
|
+
0x00ba, 0x00ba, L,
|
|
151
|
+
0x00bb, 0x00bf, ON,
|
|
152
|
+
0x00c0, 0x00d6, L,
|
|
153
|
+
0x00d7, 0x00d7, ON,
|
|
154
|
+
0x00d8, 0x00f6, L,
|
|
155
|
+
0x00f7, 0x00f7, ON,
|
|
156
|
+
0x00f8, 0x00ff, L,
|
|
157
|
+
// Combining Diacritical Marks
|
|
158
|
+
0x0300, 0x036f, NSM,
|
|
159
|
+
// Hebrew block (letters R; points/accents NSM)
|
|
160
|
+
0x0590, 0x0590, R,
|
|
161
|
+
0x0591, 0x05bd, NSM,
|
|
162
|
+
0x05be, 0x05be, R,
|
|
163
|
+
0x05bf, 0x05bf, NSM,
|
|
164
|
+
0x05c0, 0x05c0, R,
|
|
165
|
+
0x05c1, 0x05c2, NSM,
|
|
166
|
+
0x05c3, 0x05c3, R,
|
|
167
|
+
0x05c4, 0x05c5, NSM,
|
|
168
|
+
0x05c6, 0x05c6, R,
|
|
169
|
+
0x05c7, 0x05c7, NSM,
|
|
170
|
+
0x05c8, 0x05ff, R,
|
|
171
|
+
// Arabic block (letters AL; marks NSM; digits AN/EN; separators)
|
|
172
|
+
0x0600, 0x0605, AN,
|
|
173
|
+
0x0606, 0x0607, ON,
|
|
174
|
+
0x0608, 0x0608, AL,
|
|
175
|
+
0x0609, 0x060a, ET,
|
|
176
|
+
0x060b, 0x060b, AL,
|
|
177
|
+
0x060c, 0x060c, CS,
|
|
178
|
+
0x060d, 0x060f, AL,
|
|
179
|
+
0x0610, 0x061a, NSM,
|
|
180
|
+
0x061b, 0x064a, AL,
|
|
181
|
+
0x064b, 0x065f, NSM,
|
|
182
|
+
0x0660, 0x0669, AN,
|
|
183
|
+
0x066a, 0x066a, ET,
|
|
184
|
+
0x066b, 0x066c, AN,
|
|
185
|
+
0x066d, 0x066f, AL,
|
|
186
|
+
0x0670, 0x0670, NSM,
|
|
187
|
+
0x0671, 0x06d5, AL,
|
|
188
|
+
0x06d6, 0x06dc, NSM,
|
|
189
|
+
0x06dd, 0x06de, AN,
|
|
190
|
+
0x06df, 0x06e4, NSM,
|
|
191
|
+
0x06e5, 0x06e6, AL,
|
|
192
|
+
0x06e7, 0x06e8, NSM,
|
|
193
|
+
0x06e9, 0x06e9, ON,
|
|
194
|
+
0x06ea, 0x06ed, NSM,
|
|
195
|
+
0x06ee, 0x06ef, AL,
|
|
196
|
+
0x06f0, 0x06f9, EN,
|
|
197
|
+
0x06fa, 0x06ff, AL,
|
|
198
|
+
// Arabic Supplement + Extended-A
|
|
199
|
+
0x0750, 0x077f, AL,
|
|
200
|
+
0x08a0, 0x08ff, AL,
|
|
201
|
+
// General punctuation whitespace/format run
|
|
202
|
+
0x1680, 0x1680, WS,
|
|
203
|
+
0x2000, 0x200a, WS,
|
|
204
|
+
0x200b, 0x200d, BN,
|
|
205
|
+
0x200e, 0x200e, L,
|
|
206
|
+
0x200f, 0x200f, R,
|
|
207
|
+
0x2010, 0x2027, ON,
|
|
208
|
+
0x2028, 0x2028, WS,
|
|
209
|
+
0x2029, 0x2029, B,
|
|
210
|
+
0x202a, 0x202a, LRE,
|
|
211
|
+
0x202b, 0x202b, RLE,
|
|
212
|
+
0x202c, 0x202c, PDF,
|
|
213
|
+
0x202d, 0x202d, LRO,
|
|
214
|
+
0x202e, 0x202e, RLO,
|
|
215
|
+
0x202f, 0x202f, CS,
|
|
216
|
+
0x2030, 0x2034, ET,
|
|
217
|
+
0x2035, 0x2059, ON,
|
|
218
|
+
0x205f, 0x205f, WS,
|
|
219
|
+
0x2060, 0x2064, BN,
|
|
220
|
+
0x2066, 0x2066, LRI,
|
|
221
|
+
0x2067, 0x2067, RLI,
|
|
222
|
+
0x2068, 0x2068, FSI,
|
|
223
|
+
0x2069, 0x2069, PDI,
|
|
224
|
+
// Ideographic space
|
|
225
|
+
0x3000, 0x3000, WS,
|
|
226
|
+
// Hebrew + Arabic presentation forms
|
|
227
|
+
0xfb1d, 0xfb1d, R,
|
|
228
|
+
0xfb1e, 0xfb1e, NSM,
|
|
229
|
+
0xfb1f, 0xfb4f, R,
|
|
230
|
+
0xfb50, 0xfdcf, AL,
|
|
231
|
+
0xfdf0, 0xfdff, AL,
|
|
232
|
+
0xfe70, 0xfefe, AL,
|
|
233
|
+
0xfeff, 0xfeff, BN,
|
|
234
|
+
];
|
|
235
|
+
const _rangeCount = _ranges.length / 3;
|
|
236
|
+
//# sourceMappingURL=bidiClassBackend.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bidiClassBackend.js","sourceRoot":"","sources":["../src/bidiClassBackend.ts"],"names":[],"mappings":"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AM,EAAE,MAAM,EAAE,EAAE;IAClB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,EAAE;IAClB,MAAM,EAAE,MAAM,EAAE,EAAE;IAClB,MAAM,EAAE,MAAM,EAAE,EAAE;IAClB,MAAM,EAAE,MAAM,EAAE,EAAE;IAClB,MAAM,EAAE,MAAM,EAAE,EAAE;IAClB,MAAM,EAAE,MAAM,EAAE,EAAE;IAClB,MAAM,EAAE,MAAM,EAAE,EAAE;IAClB,MAAM,EAAE,MAAM,EAAE,EAAE;IAClB,MAAM,EAAE,MAAM,EAAE,EAAE;IAClB,MAAM,EAAE,MAAM,EAAE,EAAE;IAClB,MAAM,EAAE,MAAM,EAAE,EAAE;IAClB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,EAAE;IAClB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,EAAE;IAClB,MAAM,EAAE,MAAM,EAAE,EAAE;IAClB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,EAAE;IAClB,qBAAqB;IACrB,MAAM,EAAE,MAAM,EAAE,EAAE;IAClB,MAAM,EAAE,MAAM,EAAE,EAAE;IAClB,MAAM,EAAE,MAAM,EAAE,EAAE;IAClB,MAAM,EAAE,MAAM,EAAE,EAAE;IAClB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,EAAE;IAClB,MAAM,EAAE,MAAM,EAAE,EAAE;IAClB,MAAM,EAAE,MAAM,EAAE,EAAE;IAClB,MAAM,EAAE,MAAM,EAAE,EAAE;IAClB,MAAM,EAAE,MAAM,EAAE,EAAE;IAClB,MAAM,EAAE,MAAM,EAAE,EAAE;IAClB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,EAAE;IAClB,MAAM,EAAE,MAAM,EAAE,EAAE;IAClB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,EAAE;IAClB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,EAAE,M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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getBidiRuns.d.ts","sourceRoot":"","sources":["../src/getBidiRuns.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAS9D,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,aAAa,GAAG,SAAS,OAAO,EAAE,CAa1F"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { resolveBidiLevels } from './resolveBidiLevels';
|
|
2
|
+
// Resolves `text` under UAX #9 (see resolveBidiLevels) and groups consecutive code units of equal
|
|
3
|
+
// embedding level into directional runs. Each run is a half-open [start, end) range in UTF-16 code
|
|
4
|
+
// units with its `level` and derived `direction` (even → 'ltr', odd → 'rtl'); a shaper shapes each run
|
|
5
|
+
// in its own direction, and reorderBidiLine places the runs of a line in visual order. Returns an empty
|
|
6
|
+
// array for empty text.
|
|
7
|
+
export function getBidiRuns(text, baseDirection) {
|
|
8
|
+
const levels = resolveBidiLevels(text, baseDirection);
|
|
9
|
+
const runs = [];
|
|
10
|
+
const length = levels.length;
|
|
11
|
+
let start = 0;
|
|
12
|
+
for (let i = 1; i <= length; i++) {
|
|
13
|
+
if (i === length || levels[i] !== levels[start]) {
|
|
14
|
+
const level = levels[start];
|
|
15
|
+
runs.push({ start, end: i, level, direction: level % 2 === 0 ? 'ltr' : 'rtl' });
|
|
16
|
+
start = i;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return runs;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=getBidiRuns.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getBidiRuns.js","sourceRoot":"","sources":["../src/getBidiRuns.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD,kGAAkG;AAClG,mGAAmG;AACnG,uGAAuG;AACvG,wGAAwG;AACxG,wBAAwB;AACxB,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,aAA4B;IACpE,MAAM,MAAM,GAAG,iBAAiB,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;IACtD,MAAM,IAAI,GAAc,EAAE,CAAC;IAC3B,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;IAC7B,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACjC,IAAI,CAAC,KAAK,MAAM,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;YAChD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;YAC5B,IAAI,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;YAChF,KAAK,GAAG,CAAC,CAAC;QACZ,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reorderBidiLine.d.ts","sourceRoot":"","sources":["../src/reorderBidiLine.ts"],"names":[],"mappings":"AASA,wBAAgB,eAAe,CAAC,MAAM,EAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,IAAI,CA8B7G"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// UAX #9 rule L2: fill `out` with the visual-order → logical-index mapping for the line [start, end),
|
|
2
|
+
// given resolved embedding `levels` (from resolveBidiLevels). `out[v]` is the logical code-unit index
|
|
3
|
+
// that displays at visual position `v`; `out` is sized to (end - start) and written in place, so a
|
|
4
|
+
// caller can reuse one array across lines (allocation-free in the hot path).
|
|
5
|
+
//
|
|
6
|
+
// L2 reverses each contiguous run of characters at level ≥ N, for every N from the highest level down
|
|
7
|
+
// to the lowest odd level. Reversals compose, so a nested RTL-in-LTR (or the reverse) unwinds to the
|
|
8
|
+
// correct display order. Levels are read through the current `out` mapping, so successive reversals see
|
|
9
|
+
// the already-reordered indices.
|
|
10
|
+
export function reorderBidiLine(levels, start, end, out) {
|
|
11
|
+
const count = end - start;
|
|
12
|
+
out.length = count;
|
|
13
|
+
if (count <= 0)
|
|
14
|
+
return;
|
|
15
|
+
let highest = 0;
|
|
16
|
+
let lowestOdd = 255;
|
|
17
|
+
for (let i = start; i < end; i++) {
|
|
18
|
+
const level = levels[i];
|
|
19
|
+
out[i - start] = i;
|
|
20
|
+
if (level > highest)
|
|
21
|
+
highest = level;
|
|
22
|
+
if (level % 2 === 1 && level < lowestOdd)
|
|
23
|
+
lowestOdd = level;
|
|
24
|
+
}
|
|
25
|
+
for (let level = highest; level >= lowestOdd; level--) {
|
|
26
|
+
for (let k = 0; k < count;) {
|
|
27
|
+
if (levels[out[k]] >= level) {
|
|
28
|
+
let j = k;
|
|
29
|
+
while (j < count && levels[out[j]] >= level)
|
|
30
|
+
j++;
|
|
31
|
+
for (let lo = k, hi = j - 1; lo < hi; lo++, hi--) {
|
|
32
|
+
const tmp = out[lo];
|
|
33
|
+
out[lo] = out[hi];
|
|
34
|
+
out[hi] = tmp;
|
|
35
|
+
}
|
|
36
|
+
k = j;
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
k++;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=reorderBidiLine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reorderBidiLine.js","sourceRoot":"","sources":["../src/reorderBidiLine.ts"],"names":[],"mappings":"AAAA,sGAAsG;AACtG,sGAAsG;AACtG,mGAAmG;AACnG,6EAA6E;AAC7E,EAAE;AACF,sGAAsG;AACtG,qGAAqG;AACrG,wGAAwG;AACxG,iCAAiC;AACjC,MAAM,UAAU,eAAe,CAAC,MAA4B,EAAE,KAAa,EAAE,GAAW,EAAE,GAAa;IACrG,MAAM,KAAK,GAAG,GAAG,GAAG,KAAK,CAAC;IAC1B,GAAG,CAAC,MAAM,GAAG,KAAK,CAAC;IACnB,IAAI,KAAK,IAAI,CAAC;QAAE,OAAO;IAEvB,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,IAAI,SAAS,GAAG,GAAG,CAAC;IACpB,KAAK,IAAI,CAAC,GAAG,KAAK,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;QACjC,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QACxB,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC;QACnB,IAAI,KAAK,GAAG,OAAO;YAAE,OAAO,GAAG,KAAK,CAAC;QACrC,IAAI,KAAK,GAAG,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,SAAS;YAAE,SAAS,GAAG,KAAK,CAAC;IAC9D,CAAC;IAED,KAAK,IAAI,KAAK,GAAG,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,KAAK,EAAE,EAAE,CAAC;QACtD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,GAAI,CAAC;YAC5B,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,EAAE,CAAC;gBAC5B,IAAI,CAAC,GAAG,CAAC,CAAC;gBACV,OAAO,CAAC,GAAG,KAAK,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK;oBAAE,CAAC,EAAE,CAAC;gBACjD,KAAK,IAAI,EAAE,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC;oBACjD,MAAM,GAAG,GAAG,GAAG,CAAC,EAAE,CAAC,CAAC;oBACpB,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,EAAE,CAAC,CAAC;oBAClB,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC;gBAChB,CAAC;gBACD,CAAC,GAAG,CAAC,CAAC;YACR,CAAC;iBAAM,CAAC;gBACN,CAAC,EAAE,CAAC;YACN,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolveBidiLevels.d.ts","sourceRoot":"","sources":["../src/resolveBidiLevels.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAa,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAqBhE,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,aAAa,GAAG,UAAU,CAkCxF"}
|
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
import { getBidiClassBackend } from './bidiClassBackend';
|
|
2
|
+
// Resolves the per-code-unit embedding levels of `text` under the Unicode Bidirectional Algorithm
|
|
3
|
+
// (UAX #9), returning one level per UTF-16 code unit. This is the substance of the package: the full
|
|
4
|
+
// explicit/weak/neutral/implicit resolution a shaper and line-layout consume.
|
|
5
|
+
//
|
|
6
|
+
// Rules implemented: P2/P3 (paragraph base level for 'auto'), X1–X8 explicit embeddings/overrides,
|
|
7
|
+
// X5a–X6a directional isolates (LRI/RLI/FSI/PDI with the overflow/valid isolate counters), X9 (the
|
|
8
|
+
// explicit formatting characters and BN are removed from weak/neutral resolution — retained here as BN
|
|
9
|
+
// so they keep a level for reordering), X10/BD13 (isolating run sequences with sos/eos), W1–W7 (weak
|
|
10
|
+
// types), N1–N2 (neutral runs), and I1–I2 (implicit levels), then L1 (reset separators and trailing
|
|
11
|
+
// whitespace to the paragraph level). N0 (the BD16 paired-bracket rule) is DEFERRED — mirrored
|
|
12
|
+
// brackets in mixed text resolve as plain neutrals (N1/N2), not paired to their content's direction;
|
|
13
|
+
// the full-coverage flight-rs backend / a later pass owns BD16. L2 visual reordering lives in
|
|
14
|
+
// reorderBidiLine.
|
|
15
|
+
//
|
|
16
|
+
// Astral characters occupy two UTF-16 code units; both units are assigned the code point's class and
|
|
17
|
+
// therefore resolve to the same level. `baseDirection` fixes the paragraph level ('ltr' → 0, 'rtl' →
|
|
18
|
+
// 1) or derives it from the first strong character ('auto', rules P2/P3).
|
|
19
|
+
export function resolveBidiLevels(text, baseDirection) {
|
|
20
|
+
const length = text.length;
|
|
21
|
+
const levels = new Uint8Array(length);
|
|
22
|
+
if (length === 0)
|
|
23
|
+
return levels;
|
|
24
|
+
const backend = getBidiClassBackend();
|
|
25
|
+
const original = new Array(length);
|
|
26
|
+
for (let i = 0; i < length; i++) {
|
|
27
|
+
const codepoint = text.codePointAt(i);
|
|
28
|
+
const cls = backend.getBidiClass(codepoint);
|
|
29
|
+
original[i] = cls;
|
|
30
|
+
if (codepoint > 0xffff) {
|
|
31
|
+
original[i + 1] = cls; // trailing surrogate shares the class so both units resolve alike
|
|
32
|
+
i++;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const paragraphLevel = baseDirection === 'ltr' ? 0 : baseDirection === 'rtl' ? 1 : computeParagraphLevel(original, 0, length);
|
|
36
|
+
const matchingPdi = new Int32Array(length).fill(length);
|
|
37
|
+
const matchingInitiator = new Int32Array(length).fill(-1);
|
|
38
|
+
pairIsolates(original, matchingPdi, matchingInitiator);
|
|
39
|
+
// `working` is the type array mutated by overrides (X6) and X9 removal (→ BN); the weak/neutral
|
|
40
|
+
// passes read and rewrite it. `original` is preserved for BD9 isolate matching and L1.
|
|
41
|
+
const working = original.slice();
|
|
42
|
+
const levelArray = new Array(length);
|
|
43
|
+
applyExplicitLevels(original, working, levelArray, matchingPdi, paragraphLevel);
|
|
44
|
+
resolveIsolatingRunSequences(original, working, levelArray, matchingPdi, matchingInitiator, paragraphLevel);
|
|
45
|
+
applyLineReset(original, levelArray, paragraphLevel);
|
|
46
|
+
for (let i = 0; i < length; i++)
|
|
47
|
+
levels[i] = levelArray[i];
|
|
48
|
+
return levels;
|
|
49
|
+
}
|
|
50
|
+
// P2/P3: the base embedding level from the first strong character (L → 0, R/AL → 1) in [start, end),
|
|
51
|
+
// skipping any characters between an isolate initiator and its matching PDI. Defaults to 0 (LTR) when
|
|
52
|
+
// there is no strong character. Also reused by X5c to score an FSI's enclosed text.
|
|
53
|
+
function computeParagraphLevel(types, start, end) {
|
|
54
|
+
let isolateDepth = 0;
|
|
55
|
+
for (let i = start; i < end; i++) {
|
|
56
|
+
const t = types[i];
|
|
57
|
+
if (t === 'LRI' || t === 'RLI' || t === 'FSI') {
|
|
58
|
+
isolateDepth++;
|
|
59
|
+
}
|
|
60
|
+
else if (t === 'PDI') {
|
|
61
|
+
if (isolateDepth > 0)
|
|
62
|
+
isolateDepth--;
|
|
63
|
+
}
|
|
64
|
+
else if (isolateDepth === 0) {
|
|
65
|
+
if (t === 'L')
|
|
66
|
+
return 0;
|
|
67
|
+
if (t === 'R' || t === 'AL')
|
|
68
|
+
return 1;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return 0;
|
|
72
|
+
}
|
|
73
|
+
// BD9: pair each isolate initiator (LRI/RLI/FSI) with its matching PDI and vice versa. Unmatched
|
|
74
|
+
// initiators keep matchingPdi = length; unmatched PDIs keep matchingInitiator = -1.
|
|
75
|
+
function pairIsolates(types, matchingPdi, matchingInitiator) {
|
|
76
|
+
const stack = [];
|
|
77
|
+
for (let i = 0; i < types.length; i++) {
|
|
78
|
+
const t = types[i];
|
|
79
|
+
if (t === 'LRI' || t === 'RLI' || t === 'FSI') {
|
|
80
|
+
stack.push(i);
|
|
81
|
+
}
|
|
82
|
+
else if (t === 'PDI' && stack.length > 0) {
|
|
83
|
+
const initiator = stack.pop();
|
|
84
|
+
matchingPdi[initiator] = i;
|
|
85
|
+
matchingInitiator[i] = initiator;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// X1–X8 + X5a–X6a: walk the directional-status stack, assigning each character its explicit embedding
|
|
90
|
+
// level and applying directional overrides. Explicit formatting characters and BN become BN (X9) but
|
|
91
|
+
// retain the assigned level. Writes into `levelArray`; mutates `working` for overrides and removal.
|
|
92
|
+
function applyExplicitLevels(original, working, levelArray, matchingPdi, paragraphLevel) {
|
|
93
|
+
const MAX_DEPTH = 125;
|
|
94
|
+
const stackLevel = [paragraphLevel];
|
|
95
|
+
const stackOverride = [null];
|
|
96
|
+
const stackIsolate = [false];
|
|
97
|
+
let overflowIsolate = 0;
|
|
98
|
+
let overflowEmbedding = 0;
|
|
99
|
+
let validIsolate = 0;
|
|
100
|
+
for (let i = 0; i < original.length; i++) {
|
|
101
|
+
const t = original[i];
|
|
102
|
+
const top = stackLevel.length - 1;
|
|
103
|
+
switch (t) {
|
|
104
|
+
case 'RLE':
|
|
105
|
+
case 'LRE':
|
|
106
|
+
case 'RLO':
|
|
107
|
+
case 'LRO': {
|
|
108
|
+
levelArray[i] = stackLevel[top];
|
|
109
|
+
working[i] = 'BN';
|
|
110
|
+
const newLevel = t === 'RLE' || t === 'RLO' ? nextOdd(stackLevel[top]) : nextEven(stackLevel[top]);
|
|
111
|
+
if (newLevel <= MAX_DEPTH && overflowIsolate === 0 && overflowEmbedding === 0) {
|
|
112
|
+
stackLevel.push(newLevel);
|
|
113
|
+
stackOverride.push(t === 'RLO' ? 'R' : t === 'LRO' ? 'L' : null);
|
|
114
|
+
stackIsolate.push(false);
|
|
115
|
+
}
|
|
116
|
+
else if (overflowIsolate === 0) {
|
|
117
|
+
overflowEmbedding++;
|
|
118
|
+
}
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
case 'RLI':
|
|
122
|
+
case 'LRI':
|
|
123
|
+
case 'FSI': {
|
|
124
|
+
levelArray[i] = stackLevel[top];
|
|
125
|
+
if (stackOverride[top] !== null)
|
|
126
|
+
working[i] = stackOverride[top];
|
|
127
|
+
let asRtl = t === 'RLI';
|
|
128
|
+
if (t === 'FSI')
|
|
129
|
+
asRtl = computeParagraphLevel(original, i + 1, matchingPdi[i]) === 1;
|
|
130
|
+
const newLevel = asRtl ? nextOdd(stackLevel[top]) : nextEven(stackLevel[top]);
|
|
131
|
+
if (newLevel <= MAX_DEPTH && overflowIsolate === 0 && overflowEmbedding === 0) {
|
|
132
|
+
validIsolate++;
|
|
133
|
+
stackLevel.push(newLevel);
|
|
134
|
+
stackOverride.push(null);
|
|
135
|
+
stackIsolate.push(true);
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
overflowIsolate++;
|
|
139
|
+
}
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
case 'PDI': {
|
|
143
|
+
if (overflowIsolate > 0) {
|
|
144
|
+
overflowIsolate--;
|
|
145
|
+
}
|
|
146
|
+
else if (validIsolate > 0) {
|
|
147
|
+
overflowEmbedding = 0;
|
|
148
|
+
while (!stackIsolate[stackLevel.length - 1]) {
|
|
149
|
+
stackLevel.pop();
|
|
150
|
+
stackOverride.pop();
|
|
151
|
+
stackIsolate.pop();
|
|
152
|
+
}
|
|
153
|
+
stackLevel.pop();
|
|
154
|
+
stackOverride.pop();
|
|
155
|
+
stackIsolate.pop();
|
|
156
|
+
validIsolate--;
|
|
157
|
+
}
|
|
158
|
+
const newTop = stackLevel.length - 1;
|
|
159
|
+
levelArray[i] = stackLevel[newTop];
|
|
160
|
+
if (stackOverride[newTop] !== null)
|
|
161
|
+
working[i] = stackOverride[newTop];
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
case 'PDF': {
|
|
165
|
+
if (overflowIsolate > 0) {
|
|
166
|
+
// isolate overflow shields embeddings; leave the stack alone
|
|
167
|
+
}
|
|
168
|
+
else if (overflowEmbedding > 0) {
|
|
169
|
+
overflowEmbedding--;
|
|
170
|
+
}
|
|
171
|
+
else if (!stackIsolate[top] && stackLevel.length >= 2) {
|
|
172
|
+
stackLevel.pop();
|
|
173
|
+
stackOverride.pop();
|
|
174
|
+
stackIsolate.pop();
|
|
175
|
+
}
|
|
176
|
+
levelArray[i] = stackLevel[stackLevel.length - 1];
|
|
177
|
+
working[i] = 'BN';
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
case 'B': {
|
|
181
|
+
// X8: a paragraph separator terminates all embeddings/isolates and takes the paragraph level.
|
|
182
|
+
stackLevel.length = 1;
|
|
183
|
+
stackOverride.length = 1;
|
|
184
|
+
stackIsolate.length = 1;
|
|
185
|
+
overflowIsolate = 0;
|
|
186
|
+
overflowEmbedding = 0;
|
|
187
|
+
validIsolate = 0;
|
|
188
|
+
levelArray[i] = paragraphLevel;
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
case 'BN': {
|
|
192
|
+
levelArray[i] = stackLevel[top];
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
default: {
|
|
196
|
+
// X6: every other character takes the current level and, under an active override, its type.
|
|
197
|
+
levelArray[i] = stackLevel[top];
|
|
198
|
+
if (stackOverride[top] !== null)
|
|
199
|
+
working[i] = stackOverride[top];
|
|
200
|
+
break;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
// X10 + BD13: form the isolating run sequences (level runs over the non-removed characters, chained
|
|
206
|
+
// across matching isolate initiator/PDI pairs), compute each sequence's sos/eos boundary types, and
|
|
207
|
+
// resolve its weak (W1–W7), neutral (N1–N2), and implicit (I1–I2) levels.
|
|
208
|
+
function resolveIsolatingRunSequences(original, working, levelArray, matchingPdi, matchingInitiator, paragraphLevel) {
|
|
209
|
+
const length = original.length;
|
|
210
|
+
// Reduced sequence: character indices surviving X9 (everything but BN). Level runs and the "previous
|
|
211
|
+
// / next character" boundaries are all defined over this reduced order.
|
|
212
|
+
const kept = [];
|
|
213
|
+
for (let i = 0; i < length; i++)
|
|
214
|
+
if (working[i] !== 'BN')
|
|
215
|
+
kept.push(i);
|
|
216
|
+
if (kept.length === 0)
|
|
217
|
+
return;
|
|
218
|
+
// Level runs: maximal spans of the reduced sequence sharing one level. Each run records its member
|
|
219
|
+
// indices plus its position within `kept` (to find neighbours for sos/eos).
|
|
220
|
+
const runs = [];
|
|
221
|
+
let runStart = 0;
|
|
222
|
+
for (let k = 1; k <= kept.length; k++) {
|
|
223
|
+
if (k === kept.length || levelArray[kept[k]] !== levelArray[kept[runStart]]) {
|
|
224
|
+
const indices = [];
|
|
225
|
+
for (let m = runStart; m < k; m++)
|
|
226
|
+
indices.push(kept[m]);
|
|
227
|
+
runs.push({ indices, keptStart: runStart, keptEnd: k });
|
|
228
|
+
runStart = k;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
const runByFirst = new Map();
|
|
232
|
+
for (let r = 0; r < runs.length; r++)
|
|
233
|
+
runByFirst.set(runs[r].indices[0], r);
|
|
234
|
+
for (let r = 0; r < runs.length; r++) {
|
|
235
|
+
const firstIdx = runs[r].indices[0];
|
|
236
|
+
// A run is a sequence start unless it begins with a PDI that matches an isolate initiator (that
|
|
237
|
+
// PDI's run is a continuation of the initiator's sequence).
|
|
238
|
+
if (original[firstIdx] === 'PDI' && matchingInitiator[firstIdx] !== -1)
|
|
239
|
+
continue;
|
|
240
|
+
const sequence = [];
|
|
241
|
+
let keptStart = runs[r].keptStart;
|
|
242
|
+
let keptEnd = runs[r].keptEnd;
|
|
243
|
+
let current = r;
|
|
244
|
+
for (;;) {
|
|
245
|
+
const run = runs[current];
|
|
246
|
+
for (let m = 0; m < run.indices.length; m++)
|
|
247
|
+
sequence.push(run.indices[m]);
|
|
248
|
+
keptEnd = run.keptEnd;
|
|
249
|
+
const lastIdx = run.indices[run.indices.length - 1];
|
|
250
|
+
const lastType = original[lastIdx];
|
|
251
|
+
if ((lastType === 'LRI' || lastType === 'RLI' || lastType === 'FSI') && matchingPdi[lastIdx] < length) {
|
|
252
|
+
const next = runByFirst.get(matchingPdi[lastIdx]);
|
|
253
|
+
if (next === undefined)
|
|
254
|
+
break;
|
|
255
|
+
current = next;
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
break;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
resolveSequence(original, working, levelArray, kept, sequence, keptStart, keptEnd, matchingPdi, paragraphLevel);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
// Resolves one isolating run sequence in place: computes sos/eos, runs W1–W7 / N1–N2 / I1–I2 over the
|
|
265
|
+
// sequence's characters in logical order, and writes the final implicit levels into `levelArray`.
|
|
266
|
+
function resolveSequence(original, working, levelArray, kept, sequence, keptStart, keptEnd, matchingPdi, paragraphLevel) {
|
|
267
|
+
const seqLevel = levelArray[sequence[0]];
|
|
268
|
+
const prevLevel = keptStart > 0 ? levelArray[kept[keptStart - 1]] : paragraphLevel;
|
|
269
|
+
const sos = Math.max(seqLevel, prevLevel) % 2 === 1 ? 'R' : 'L';
|
|
270
|
+
const lastIdx = sequence[sequence.length - 1];
|
|
271
|
+
const lastType = original[lastIdx];
|
|
272
|
+
const endsUnmatchedIsolate = (lastType === 'LRI' || lastType === 'RLI' || lastType === 'FSI') && matchingPdi[lastIdx] >= original.length;
|
|
273
|
+
const nextLevel = endsUnmatchedIsolate
|
|
274
|
+
? paragraphLevel
|
|
275
|
+
: keptEnd < kept.length
|
|
276
|
+
? levelArray[kept[keptEnd]]
|
|
277
|
+
: paragraphLevel;
|
|
278
|
+
const eos = Math.max(seqLevel, nextLevel) % 2 === 1 ? 'R' : 'L';
|
|
279
|
+
const len = sequence.length;
|
|
280
|
+
const ty = new Array(len);
|
|
281
|
+
for (let k = 0; k < len; k++)
|
|
282
|
+
ty[k] = working[sequence[k]];
|
|
283
|
+
// W1: resolve NSM to the type of the previous character (sos at the start); an NSM after an isolate
|
|
284
|
+
// initiator or PDI becomes ON.
|
|
285
|
+
let prev = sos;
|
|
286
|
+
for (let k = 0; k < len; k++) {
|
|
287
|
+
if (ty[k] === 'NSM') {
|
|
288
|
+
ty[k] = prev === 'LRI' || prev === 'RLI' || prev === 'FSI' || prev === 'PDI' ? 'ON' : prev;
|
|
289
|
+
}
|
|
290
|
+
prev = ty[k];
|
|
291
|
+
}
|
|
292
|
+
// W2: an EN after an AL strong type becomes AN.
|
|
293
|
+
let strong = sos;
|
|
294
|
+
for (let k = 0; k < len; k++) {
|
|
295
|
+
const c = ty[k];
|
|
296
|
+
if (c === 'L' || c === 'R' || c === 'AL')
|
|
297
|
+
strong = c;
|
|
298
|
+
else if (c === 'EN' && strong === 'AL')
|
|
299
|
+
ty[k] = 'AN';
|
|
300
|
+
}
|
|
301
|
+
// W3: AL becomes R.
|
|
302
|
+
for (let k = 0; k < len; k++)
|
|
303
|
+
if (ty[k] === 'AL')
|
|
304
|
+
ty[k] = 'R';
|
|
305
|
+
// W4: a single ES between two ENs, or a single CS between two numbers of the same type, joins them.
|
|
306
|
+
for (let k = 1; k < len - 1; k++) {
|
|
307
|
+
const c = ty[k];
|
|
308
|
+
if (c === 'ES' && ty[k - 1] === 'EN' && ty[k + 1] === 'EN')
|
|
309
|
+
ty[k] = 'EN';
|
|
310
|
+
else if (c === 'CS') {
|
|
311
|
+
if (ty[k - 1] === 'EN' && ty[k + 1] === 'EN')
|
|
312
|
+
ty[k] = 'EN';
|
|
313
|
+
else if (ty[k - 1] === 'AN' && ty[k + 1] === 'AN')
|
|
314
|
+
ty[k] = 'AN';
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
// W5: a run of ET adjacent to an EN becomes EN.
|
|
318
|
+
for (let k = 0; k < len;) {
|
|
319
|
+
if (ty[k] === 'ET') {
|
|
320
|
+
let j = k;
|
|
321
|
+
while (j < len && ty[j] === 'ET')
|
|
322
|
+
j++;
|
|
323
|
+
const before = k > 0 ? ty[k - 1] : sos;
|
|
324
|
+
const after = j < len ? ty[j] : eos;
|
|
325
|
+
if (before === 'EN' || after === 'EN')
|
|
326
|
+
for (let m = k; m < j; m++)
|
|
327
|
+
ty[m] = 'EN';
|
|
328
|
+
k = j;
|
|
329
|
+
}
|
|
330
|
+
else {
|
|
331
|
+
k++;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
// W6: any remaining separator/terminator (ES/ET/CS) becomes ON.
|
|
335
|
+
for (let k = 0; k < len; k++)
|
|
336
|
+
if (ty[k] === 'ES' || ty[k] === 'ET' || ty[k] === 'CS')
|
|
337
|
+
ty[k] = 'ON';
|
|
338
|
+
// W7: an EN after an L strong type becomes L.
|
|
339
|
+
strong = sos;
|
|
340
|
+
for (let k = 0; k < len; k++) {
|
|
341
|
+
const c = ty[k];
|
|
342
|
+
if (c === 'L' || c === 'R')
|
|
343
|
+
strong = c;
|
|
344
|
+
else if (c === 'EN' && strong === 'L')
|
|
345
|
+
ty[k] = 'L';
|
|
346
|
+
}
|
|
347
|
+
// N1: a run of neutral/isolate-formatting characters between two characters of the same direction
|
|
348
|
+
// (L, or R with EN/AN counting as R) takes that direction. N2: any remainder takes the embedding
|
|
349
|
+
// direction. (N0 bracket pairing is deferred — see resolveBidiLevels.)
|
|
350
|
+
const embeddingDir = seqLevel % 2 === 1 ? 'R' : 'L';
|
|
351
|
+
for (let k = 0; k < len;) {
|
|
352
|
+
if (isNeutralOrIsolate(ty[k])) {
|
|
353
|
+
let j = k;
|
|
354
|
+
while (j < len && isNeutralOrIsolate(ty[j]))
|
|
355
|
+
j++;
|
|
356
|
+
const before = k > 0 ? neutralDirection(ty[k - 1]) : sos;
|
|
357
|
+
const after = j < len ? neutralDirection(ty[j]) : eos;
|
|
358
|
+
const resolved = before === after ? before : embeddingDir;
|
|
359
|
+
for (let m = k; m < j; m++)
|
|
360
|
+
ty[m] = resolved;
|
|
361
|
+
k = j;
|
|
362
|
+
}
|
|
363
|
+
else {
|
|
364
|
+
k++;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
// I1/I2: implicit levels. Even embedding level: R → +1, AN/EN → +2. Odd embedding level: L/EN/AN → +1.
|
|
368
|
+
const even = seqLevel % 2 === 0;
|
|
369
|
+
for (let k = 0; k < len; k++) {
|
|
370
|
+
const c = ty[k];
|
|
371
|
+
let lvl = seqLevel;
|
|
372
|
+
if (even) {
|
|
373
|
+
if (c === 'R')
|
|
374
|
+
lvl = seqLevel + 1;
|
|
375
|
+
else if (c === 'AN' || c === 'EN')
|
|
376
|
+
lvl = seqLevel + 2;
|
|
377
|
+
}
|
|
378
|
+
else if (c === 'L' || c === 'EN' || c === 'AN') {
|
|
379
|
+
lvl = seqLevel + 1;
|
|
380
|
+
}
|
|
381
|
+
levelArray[sequence[k]] = lvl;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
// L1: reset the level of segment/paragraph separators, and of any whitespace / isolate-formatting /
|
|
385
|
+
// removed run preceding a separator or the end of the paragraph, back to the paragraph level. Uses the
|
|
386
|
+
// original types (the reset set is defined on them, before resolution).
|
|
387
|
+
function applyLineReset(original, levelArray, paragraphLevel) {
|
|
388
|
+
const length = original.length;
|
|
389
|
+
for (let i = 0; i < length; i++) {
|
|
390
|
+
const t = original[i];
|
|
391
|
+
if (t === 'B' || t === 'S') {
|
|
392
|
+
levelArray[i] = paragraphLevel;
|
|
393
|
+
for (let j = i - 1; j >= 0 && isResetType(original[j]); j--)
|
|
394
|
+
levelArray[j] = paragraphLevel;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
for (let j = length - 1; j >= 0 && isResetType(original[j]); j--)
|
|
398
|
+
levelArray[j] = paragraphLevel;
|
|
399
|
+
}
|
|
400
|
+
// The N1/N2 "neutral or isolate" set (NI): the characters resolved by the neutral rules.
|
|
401
|
+
function isNeutralOrIsolate(t) {
|
|
402
|
+
return t === 'B' || t === 'S' || t === 'WS' || t === 'ON' || t === 'FSI' || t === 'LRI' || t === 'RLI' || t === 'PDI';
|
|
403
|
+
}
|
|
404
|
+
// The L1 reset set: whitespace, isolate formatting, and the X9-removed explicit formatting / BN
|
|
405
|
+
// characters that trail a separator or the line.
|
|
406
|
+
function isResetType(t) {
|
|
407
|
+
return (t === 'WS' ||
|
|
408
|
+
t === 'LRI' ||
|
|
409
|
+
t === 'RLI' ||
|
|
410
|
+
t === 'FSI' ||
|
|
411
|
+
t === 'PDI' ||
|
|
412
|
+
t === 'LRE' ||
|
|
413
|
+
t === 'RLE' ||
|
|
414
|
+
t === 'LRO' ||
|
|
415
|
+
t === 'RLO' ||
|
|
416
|
+
t === 'PDF' ||
|
|
417
|
+
t === 'BN');
|
|
418
|
+
}
|
|
419
|
+
// Direction a resolved strong/number character contributes at a neutral-run boundary (N1): L stays L;
|
|
420
|
+
// R, EN, and AN all count as R.
|
|
421
|
+
function neutralDirection(t) {
|
|
422
|
+
return t === 'L' ? 'L' : 'R';
|
|
423
|
+
}
|
|
424
|
+
// Least even level strictly greater than `level` (for LTR embeddings/isolates, X3/X5b).
|
|
425
|
+
function nextEven(level) {
|
|
426
|
+
return (level + 2) & ~1;
|
|
427
|
+
}
|
|
428
|
+
// Least odd level strictly greater than `level` (for RTL embeddings/isolates, X2/X5a).
|
|
429
|
+
function nextOdd(level) {
|
|
430
|
+
return (level + 1) | 1;
|
|
431
|
+
}
|
|
432
|
+
//# sourceMappingURL=resolveBidiLevels.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolveBidiLevels.js","sourceRoot":"","sources":["../src/resolveBidiLevels.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@flighthq/textbidi",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"default": "./dist/index.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"src/**/*.test.ts",
|
|
16
|
+
"!dist/**/*.test.js",
|
|
17
|
+
"!dist/**/*.test.d.ts",
|
|
18
|
+
"!dist/**/*.test.js.map",
|
|
19
|
+
"!dist/**/*.test.d.ts.map"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsc -b",
|
|
23
|
+
"clean": "tsc -b --clean",
|
|
24
|
+
"test": "vitest run --config vitest.config.ts",
|
|
25
|
+
"test:watch": "vitest --watch --config vitest.config.ts",
|
|
26
|
+
"prepack": "npm run clean && npm run clean:dist && npm run build",
|
|
27
|
+
"clean:dist": "tsx ../../scripts/clean-package-dist.ts"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@flighthq/types": "0.1.0"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"typescript": "^5.3.0"
|
|
34
|
+
},
|
|
35
|
+
"description": "Unicode bidirectional-text itemize layer (UAX #9) — resolves embedding levels and reorders runs over a swappable bidi-class backend defaulting to a compact bundled table",
|
|
36
|
+
"sideEffects": false
|
|
37
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { BidiClassBackend } from '@flighthq/types';
|
|
2
|
+
import { afterEach, describe, expect, it } from 'vitest';
|
|
3
|
+
|
|
4
|
+
import { createCompactBidiClassBackend, getBidiClassBackend, setBidiClassBackend } from './bidiClassBackend';
|
|
5
|
+
import { resolveBidiLevels } from './resolveBidiLevels';
|
|
6
|
+
|
|
7
|
+
afterEach(() => {
|
|
8
|
+
setBidiClassBackend(null);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
describe('createCompactBidiClassBackend', () => {
|
|
12
|
+
it('classifies a representative sample of common-script codepoints', () => {
|
|
13
|
+
const backend = createCompactBidiClassBackend();
|
|
14
|
+
expect(backend.getBidiClass('a'.codePointAt(0) as number)).toBe('L');
|
|
15
|
+
expect(backend.getBidiClass('Z'.codePointAt(0) as number)).toBe('L');
|
|
16
|
+
expect(backend.getBidiClass(0x05d0)).toBe('R'); // Hebrew alef
|
|
17
|
+
expect(backend.getBidiClass(0x0627)).toBe('AL'); // Arabic alef
|
|
18
|
+
expect(backend.getBidiClass('5'.codePointAt(0) as number)).toBe('EN');
|
|
19
|
+
expect(backend.getBidiClass(0x0660)).toBe('AN'); // Arabic-Indic digit zero
|
|
20
|
+
expect(backend.getBidiClass(' '.codePointAt(0) as number)).toBe('WS');
|
|
21
|
+
expect(backend.getBidiClass(0x0301)).toBe('NSM'); // combining acute accent
|
|
22
|
+
expect(backend.getBidiClass(0x2066)).toBe('LRI');
|
|
23
|
+
expect(backend.getBidiClass(0x2069)).toBe('PDI');
|
|
24
|
+
expect(backend.getBidiClass(0x202b)).toBe('RLE');
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('resolves uncovered codepoints to the LTR default', () => {
|
|
28
|
+
const backend = createCompactBidiClassBackend();
|
|
29
|
+
expect(backend.getBidiClass(0x4e2d)).toBe('L'); // CJK ideograph, outside the compact table
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
describe('getBidiClassBackend', () => {
|
|
34
|
+
it('lazily creates the compact default on first access', () => {
|
|
35
|
+
expect(getBidiClassBackend().getBidiClass('a'.codePointAt(0) as number)).toBe('L');
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
describe('setBidiClassBackend', () => {
|
|
40
|
+
it('routes resolveBidiLevels class lookups through the installed backend', () => {
|
|
41
|
+
// A fake backend that reports every character as strong R forces the whole string to level 1.
|
|
42
|
+
const fake: BidiClassBackend = { getBidiClass: () => 'R' };
|
|
43
|
+
setBidiClassBackend(fake);
|
|
44
|
+
expect(Array.from(resolveBidiLevels('abc', 'ltr'))).toEqual([1, 1, 1]);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('restores the compact default when passed null', () => {
|
|
48
|
+
setBidiClassBackend({ getBidiClass: () => 'R' });
|
|
49
|
+
expect(getBidiClassBackend().getBidiClass('a'.codePointAt(0) as number)).toBe('R');
|
|
50
|
+
setBidiClassBackend(null);
|
|
51
|
+
expect(getBidiClassBackend().getBidiClass('a'.codePointAt(0) as number)).toBe('L');
|
|
52
|
+
});
|
|
53
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { getBidiRuns } from './getBidiRuns';
|
|
4
|
+
|
|
5
|
+
const HEBREW = 'שלום';
|
|
6
|
+
|
|
7
|
+
describe('getBidiRuns', () => {
|
|
8
|
+
it('returns a single ltr run for pure-LTR text', () => {
|
|
9
|
+
expect(getBidiRuns('hello', 'auto')).toEqual([{ start: 0, end: 5, level: 0, direction: 'ltr' }]);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('splits a mixed string into ltr / rtl / ltr runs with correct ranges', () => {
|
|
13
|
+
// "hello שלום world" — the joining spaces stay at level 0, so they attach to the Latin runs.
|
|
14
|
+
expect(getBidiRuns(`hello ${HEBREW} world`, 'auto')).toEqual([
|
|
15
|
+
{ start: 0, end: 6, level: 0, direction: 'ltr' },
|
|
16
|
+
{ start: 6, end: 10, level: 1, direction: 'rtl' },
|
|
17
|
+
{ start: 10, end: 16, level: 0, direction: 'ltr' },
|
|
18
|
+
]);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('returns no runs for empty text', () => {
|
|
22
|
+
expect(getBidiRuns('', 'auto')).toEqual([]);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { reorderBidiLine } from './reorderBidiLine';
|
|
4
|
+
|
|
5
|
+
describe('reorderBidiLine', () => {
|
|
6
|
+
it('reverses a pure-RTL run to visual order', () => {
|
|
7
|
+
const levels = new Uint8Array([1, 1, 1]);
|
|
8
|
+
const out: number[] = [];
|
|
9
|
+
reorderBidiLine(levels, 0, 3, out);
|
|
10
|
+
expect(out).toEqual([2, 1, 0]);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('leaves a pure-LTR line in logical order', () => {
|
|
14
|
+
const levels = new Uint8Array([0, 0, 0, 0]);
|
|
15
|
+
const out: number[] = [];
|
|
16
|
+
reorderBidiLine(levels, 0, 4, out);
|
|
17
|
+
expect(out).toEqual([0, 1, 2, 3]);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('reorders only the RTL run of a mixed line', () => {
|
|
21
|
+
// Levels of "hello שלום world": the level-1 Hebrew run (indices 6..9) reverses; the rest stays.
|
|
22
|
+
const levels = new Uint8Array([0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0]);
|
|
23
|
+
const out: number[] = [];
|
|
24
|
+
reorderBidiLine(levels, 0, levels.length, out);
|
|
25
|
+
expect(out).toEqual([0, 1, 2, 3, 4, 5, 9, 8, 7, 6, 10, 11, 12, 13, 14, 15]);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('reorders numbers nested in RTL text (level 2 within level 1) correctly', () => {
|
|
29
|
+
// "שלום 123": Hebrew+space at level 1, digits at level 2. Visually the whole line is RTL, but the
|
|
30
|
+
// digits keep their internal LTR order — L2 reverses level ≥ 2 then level ≥ 1.
|
|
31
|
+
const levels = new Uint8Array([1, 1, 1, 1, 1, 2, 2, 2]);
|
|
32
|
+
const out: number[] = [];
|
|
33
|
+
reorderBidiLine(levels, 0, 8, out);
|
|
34
|
+
expect(out).toEqual([5, 6, 7, 4, 3, 2, 1, 0]);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('writes into a distinct out array and can reuse one across calls', () => {
|
|
38
|
+
const out: number[] = [];
|
|
39
|
+
reorderBidiLine(new Uint8Array([1, 1]), 0, 2, out);
|
|
40
|
+
expect(out).toEqual([1, 0]);
|
|
41
|
+
// Reusing the same array on a shorter line truncates it — no fresh allocation required.
|
|
42
|
+
reorderBidiLine(new Uint8Array([0]), 0, 1, out);
|
|
43
|
+
expect(out).toEqual([0]);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('reorders a sub-range within a larger level buffer', () => {
|
|
47
|
+
const levels = new Uint8Array([0, 1, 1, 1, 0]);
|
|
48
|
+
const out: number[] = [];
|
|
49
|
+
reorderBidiLine(levels, 1, 4, out);
|
|
50
|
+
expect(out).toEqual([3, 2, 1]);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { resolveBidiLevels } from './resolveBidiLevels';
|
|
4
|
+
|
|
5
|
+
// "שלום" — the Hebrew word (four strong-R letters).
|
|
6
|
+
const HEBREW = 'שלום';
|
|
7
|
+
|
|
8
|
+
describe('resolveBidiLevels', () => {
|
|
9
|
+
it('assigns level 0 to a pure-LTR string', () => {
|
|
10
|
+
expect(Array.from(resolveBidiLevels('hello', 'auto'))).toEqual([0, 0, 0, 0, 0]);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('assigns level 1 to a pure-RTL (Hebrew) string under auto base', () => {
|
|
14
|
+
expect(Array.from(resolveBidiLevels(HEBREW, 'auto'))).toEqual([1, 1, 1, 1]);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('embeds an RTL run at an odd level inside an LTR paragraph', () => {
|
|
18
|
+
// "hello שלום world": Latin at level 0, the Hebrew run at level 1, the joining spaces at 0.
|
|
19
|
+
const levels = Array.from(resolveBidiLevels(`hello ${HEBREW} world`, 'auto'));
|
|
20
|
+
expect(levels).toEqual([0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0]);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('gives European numbers in an RTL context an even (LTR) level above the RTL text', () => {
|
|
24
|
+
// "שלום 123": the Hebrew + space stay at level 1, the digits get level 2 (EN under I2).
|
|
25
|
+
const levels = Array.from(resolveBidiLevels(`${HEBREW} 123`, 'auto'));
|
|
26
|
+
expect(levels).toEqual([1, 1, 1, 1, 1, 2, 2, 2]);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('raises the paragraph level for an explicit rtl base', () => {
|
|
30
|
+
// Same mixed string, base 'rtl': the Latin words rise to level 2, the Hebrew + spaces sit at 1.
|
|
31
|
+
const levels = Array.from(resolveBidiLevels(`hello ${HEBREW} world`, 'rtl'));
|
|
32
|
+
expect(levels).toEqual([2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2]);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('keeps an explicit ltr base at level 0 for pure-LTR text even with a leading number', () => {
|
|
36
|
+
expect(Array.from(resolveBidiLevels('12ab', 'ltr'))).toEqual([0, 0, 0, 0]);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('derives an rtl paragraph from a leading strong-R character under auto', () => {
|
|
40
|
+
// First strong char is Hebrew → paragraph level 1; the trailing Latin rises to level 2.
|
|
41
|
+
const levels = Array.from(resolveBidiLevels(`${HEBREW} ab`, 'auto'));
|
|
42
|
+
expect(levels).toEqual([1, 1, 1, 1, 1, 2, 2]);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('returns an empty array for empty text', () => {
|
|
46
|
+
expect(resolveBidiLevels('', 'auto')).toEqual(new Uint8Array(0));
|
|
47
|
+
});
|
|
48
|
+
});
|