in lib/src/grapheme_clusters/breaks.dart [236:296]
bool isGraphemeClusterBoundary(String text, int start, int end, int index) {
assert(0 <= start);
assert(start <= index);
assert(index <= end);
assert(end <= text.length);
// Uses the backwards automaton because answering the question
// might be answered by looking only at the code points around the
// index, but it may also require looking further back. It never
// requires looking further ahead, though.
// The backwards automaton is built for this use case.
// Most of the apparent complication in this function is merely dealing with
// surrogates.
if (start < index && index < end) {
// Something on both sides of index.
var char = text.codeUnitAt(index);
var prevChar = text.codeUnitAt(index - 1);
var catAfter = categoryControl;
if (char & 0xF800 != 0xD800) {
catAfter = low(char);
} else if (char & 0xFC00 == 0xD800) {
// Lead surrogate. Combine with following tail surrogate,
// otherwise it's a control and always a boundary.
if (index + 1 >= end) return true;
var nextChar = text.codeUnitAt(index + 1);
if (nextChar & 0xFC00 != 0xDC00) return true;
catAfter = high(char, nextChar);
} else {
// Tail surrogate after index. Either combines with lead surrogate
// before or is always a bundary.
return prevChar & 0xFC00 != 0xD800;
}
var catBefore = categoryControl;
if (prevChar & 0xFC00 != 0xDC00) {
catBefore = low(prevChar);
index -= 1;
} else {
// If no prior lead surrogate, it's a control and always a boundary.
index -= 2;
if (start <= index) {
var prevPrevChar = text.codeUnitAt(index);
if (prevPrevChar & 0xFC00 != 0xD800) {
return true;
}
catBefore = high(prevPrevChar, prevChar);
} else {
return true;
}
}
var state = moveBack(stateEoTNoBreak, catAfter);
// It requires at least two moves from EoT to trigger a lookahead,
// either ZWJ+Pic or RI+RI.
assert(state < stateLookaheadMin);
state = moveBack(state, catBefore);
if (state >= stateLookaheadMin) {
state = lookAhead(text, start, index, state);
}
return state & stateNoBreak == 0;
}
// Always boundary at EoT or SoT, unless there is nothing between them.
return start != end;
}