reorder static method

(List<int>, List<int>) reorder(
  1. List<String> visual, {
  2. required bool baseRtl,
})

Returns (logical order indexes, embedding level per original index).

Implementation

static (List<int>, List<int>) reorder(List<String> visual, {required bool baseRtl}) {
  final int count = visual.length;
  final List<int> classes = visual.map(_classOf).toList();
  final List<int> levels = List<int>.filled(count, baseRtl ? 1 : 0);
  final int base = baseRtl ? 1 : 0;

  int strongNear(int index, int step) {
    for (int i = index + step; i >= 0 && i < count; i += step) {
      final int kind = classes[i];
      if (kind == _left || kind == _right) return kind;
    }
    return baseRtl ? _right : _left;
  }

  // Numbers take the direction of neighbouring Latin text (bidi rule W7,
  // approximated on visual neighbours), otherwise they behave like RTL.
  final List<int> resolved = List<int>.from(classes);
  for (int i = 0; i < count; i++) {
    if (classes[i] != _number) continue;
    resolved[i] = strongNear(i, -1) == _left || strongNear(i, 1) == _left ? _left : _right;
  }
  for (int i = 0; i < count; i++) {
    switch (classes[i]) {
      case _right:
        levels[i] = 1;
        break;
      case _left:
        levels[i] = baseRtl ? 2 : 0;
        break;
      case _number:
        levels[i] = resolved[i] == _left ? (baseRtl ? 2 : 0) : 2;
        break;
      default:
        break;
    }
  }
  for (int i = 0; i < count; i++) {
    final int kind = classes[i];
    if (kind != _neutral && kind != _mark) continue;
    int left = i - 1;
    while (left >= 0 && (classes[left] == _neutral || classes[left] == _mark)) {
      left--;
    }
    int right = i + 1;
    while (right < count && (classes[right] == _neutral || classes[right] == _mark)) {
      right++;
    }
    final int leftDir = left < 0 ? (baseRtl ? _right : _left) : resolved[left];
    final int rightDir = right >= count ? (baseRtl ? _right : _left) : resolved[right];
    if (kind == _mark && left >= 0) {
      levels[i] = levels[left];
    } else if (leftDir == rightDir) {
      levels[i] = leftDir == _right ? 1 : (baseRtl ? 2 : 0);
    } else {
      levels[i] = base;
    }
    if (levels[i] < base) levels[i] = base;
  }
  // Digits touching each other across separators ("1.5", "۱۴۰۳/۰۱") stay one number.
  for (int i = 1; i < count - 1; i++) {
    if (classes[i] == _neutral && classes[i - 1] == _number && classes[i + 1] == _number && visual[i].trim().length == 1 && ".,/:-٫٬".contains(visual[i].trim())) {
      levels[i] = levels[i - 1];
    }
  }

  final List<int> order = List<int>.generate(count, (int i) => i);
  int maxLevel = 0;
  for (final int level in levels) {
    if (level > maxLevel) maxLevel = level;
  }
  for (int k = 1; k <= maxLevel; k++) {
    int i = 0;
    while (i < count) {
      if (levels[order[i]] < k) {
        i++;
        continue;
      }
      int j = i;
      while (j + 1 < count && levels[order[j + 1]] >= k) {
        j++;
      }
      int a = i;
      int b = j;
      while (a < b) {
        final int swap = order[a];
        order[a] = order[b];
        order[b] = swap;
        a++;
        b--;
      }
      i = j + 1;
    }
  }
  return (order, levels);
}