stringWidth method
Calculates the display width of s using grapheme cluster iteration.
Each grapheme cluster is measured individually. Clusters containing U+FE0F (emoji presentation selector) are widened to emojiPresentationWidth when the base rune would otherwise be narrow.
Implementation
int stringWidth(String s) {
final asciiWidth = _asciiStringWidth(s);
if (asciiWidth != null) return asciiWidth;
final cachedWidth = _unicodeStringWidthCache[s];
if (cachedWidth != null) return cachedWidth;
final simpleUnicodeWidth = _simpleUnicodeStringWidth(s);
if (simpleUnicodeWidth != null) {
_cacheUnicodeStringWidth(s, simpleUnicodeWidth);
return simpleUnicodeWidth;
}
var width = 0;
// Count display width per grapheme cluster to avoid double-counting
// multi-codepoint clusters (e.g. ZWJ emoji sequences).
for (final g in uni.graphemes(s)) {
final iterator = g.runes.iterator;
if (!iterator.moveNext()) continue;
final first = iterator.current;
var hasEmojiPresentationSelector = first == 0xFE0F;
while (iterator.moveNext()) {
if (iterator.current == 0xFE0F) {
hasEmojiPresentationSelector = true;
break;
}
}
var w = runeWidth(first);
// If the grapheme contains U+FE0F (variation selector 16 — emoji
// presentation), terminals render the base character at emoji width
// even if runeWidth() returned 1.
if (w == 1 && hasEmojiPresentationSelector) {
w = emojiPresentationWidth;
}
width += w;
}
_cacheUnicodeStringWidth(s, width);
return width;
}