aboutsummaryrefslogtreecommitdiffstats
path: root/vanilla/node_modules/bidi-js/src/reordering.js
diff options
context:
space:
mode:
authorAdam Mathes <adam@adammathes.com>2026-02-13 21:34:48 -0800
committerAdam Mathes <adam@adammathes.com>2026-02-13 21:34:48 -0800
commit76cb9c2a39d477a64824a985ade40507e3bbade1 (patch)
tree41e997aa9c6f538d3a136af61dae9424db2005a9 /vanilla/node_modules/bidi-js/src/reordering.js
parent819a39a21ac992b1393244a4c283bbb125208c69 (diff)
downloadneko-76cb9c2a39d477a64824a985ade40507e3bbade1.tar.gz
neko-76cb9c2a39d477a64824a985ade40507e3bbade1.tar.bz2
neko-76cb9c2a39d477a64824a985ade40507e3bbade1.zip
feat(vanilla): add testing infrastructure and tests (NK-wjnczv)
Diffstat (limited to 'vanilla/node_modules/bidi-js/src/reordering.js')
-rw-r--r--vanilla/node_modules/bidi-js/src/reordering.js99
1 files changed, 99 insertions, 0 deletions
diff --git a/vanilla/node_modules/bidi-js/src/reordering.js b/vanilla/node_modules/bidi-js/src/reordering.js
new file mode 100644
index 0000000..94a42ed
--- /dev/null
+++ b/vanilla/node_modules/bidi-js/src/reordering.js
@@ -0,0 +1,99 @@
+import { getBidiCharType, TRAILING_TYPES } from './charTypes.js'
+import { getMirroredCharacter } from './mirroring.js'
+
+/**
+ * Given a start and end denoting a single line within a string, and a set of precalculated
+ * bidi embedding levels, produce a list of segments whose ordering should be flipped, in sequence.
+ * @param {string} string - the full input string
+ * @param {GetEmbeddingLevelsResult} embeddingLevelsResult - the result object from getEmbeddingLevels
+ * @param {number} [start] - first character in a subset of the full string
+ * @param {number} [end] - last character in a subset of the full string
+ * @return {number[][]} - the list of start/end segments that should be flipped, in order.
+ */
+export function getReorderSegments(string, embeddingLevelsResult, start, end) {
+ let strLen = string.length
+ start = Math.max(0, start == null ? 0 : +start)
+ end = Math.min(strLen - 1, end == null ? strLen - 1 : +end)
+
+ const segments = []
+ embeddingLevelsResult.paragraphs.forEach(paragraph => {
+ const lineStart = Math.max(start, paragraph.start)
+ const lineEnd = Math.min(end, paragraph.end)
+ if (lineStart < lineEnd) {
+ // Local slice for mutation
+ const lineLevels = embeddingLevelsResult.levels.slice(lineStart, lineEnd + 1)
+
+ // 3.4 L1.4: Reset any sequence of whitespace characters and/or isolate formatting characters at the
+ // end of the line to the paragraph level.
+ for (let i = lineEnd; i >= lineStart && (getBidiCharType(string[i]) & TRAILING_TYPES); i--) {
+ lineLevels[i] = paragraph.level
+ }
+
+ // L2. From the highest level found in the text to the lowest odd level on each line, including intermediate levels
+ // not actually present in the text, reverse any contiguous sequence of characters that are at that level or higher.
+ let maxLevel = paragraph.level
+ let minOddLevel = Infinity
+ for (let i = 0; i < lineLevels.length; i++) {
+ const level = lineLevels[i]
+ if (level > maxLevel) maxLevel = level
+ if (level < minOddLevel) minOddLevel = level | 1
+ }
+ for (let lvl = maxLevel; lvl >= minOddLevel; lvl--) {
+ for (let i = 0; i < lineLevels.length; i++) {
+ if (lineLevels[i] >= lvl) {
+ const segStart = i
+ while (i + 1 < lineLevels.length && lineLevels[i + 1] >= lvl) {
+ i++
+ }
+ if (i > segStart) {
+ segments.push([segStart + lineStart, i + lineStart])
+ }
+ }
+ }
+ }
+ }
+ })
+ return segments
+}
+
+/**
+ * @param {string} string
+ * @param {GetEmbeddingLevelsResult} embedLevelsResult
+ * @param {number} [start]
+ * @param {number} [end]
+ * @return {string} the new string with bidi segments reordered
+ */
+export function getReorderedString(string, embedLevelsResult, start, end) {
+ const indices = getReorderedIndices(string, embedLevelsResult, start, end)
+ const chars = [...string]
+ indices.forEach((charIndex, i) => {
+ chars[i] = (
+ (embedLevelsResult.levels[charIndex] & 1) ? getMirroredCharacter(string[charIndex]) : null
+ ) || string[charIndex]
+ })
+ return chars.join('')
+}
+
+/**
+ * @param {string} string
+ * @param {GetEmbeddingLevelsResult} embedLevelsResult
+ * @param {number} [start]
+ * @param {number} [end]
+ * @return {number[]} an array with character indices in their new bidi order
+ */
+export function getReorderedIndices(string, embedLevelsResult, start, end) {
+ const segments = getReorderSegments(string, embedLevelsResult, start, end)
+ // Fill an array with indices
+ const indices = []
+ for (let i = 0; i < string.length; i++) {
+ indices[i] = i
+ }
+ // Reverse each segment in order
+ segments.forEach(([start, end]) => {
+ const slice = indices.slice(start, end + 1)
+ for (let i = slice.length; i--;) {
+ indices[end - i] = slice[i]
+ }
+ })
+ return indices
+}