in shell/platform/android/io/flutter/plugin/editing/FlutterTextUtils.java [57:188]
public int getOffsetBefore(CharSequence text, int offset) {
if (offset <= 1) {
return 0;
}
int codePoint = Character.codePointBefore(text, offset);
int deleteCharCount = Character.charCount(codePoint);
int lastOffset = offset - deleteCharCount;
if (lastOffset == 0) {
return 0;
}
// Line Feed
if (codePoint == LINE_FEED) {
codePoint = Character.codePointBefore(text, lastOffset);
if (codePoint == CARRIAGE_RETURN) {
++deleteCharCount;
}
return offset - deleteCharCount;
}
// Flags
if (isRegionalIndicatorSymbol(codePoint)) {
codePoint = Character.codePointBefore(text, lastOffset);
lastOffset -= Character.charCount(codePoint);
int regionalIndicatorSymbolCount = 1;
while (lastOffset > 0 && isRegionalIndicatorSymbol(codePoint)) {
codePoint = Character.codePointBefore(text, lastOffset);
lastOffset -= Character.charCount(codePoint);
regionalIndicatorSymbolCount++;
}
if (regionalIndicatorSymbolCount % 2 == 0) {
deleteCharCount += 2;
}
return offset - deleteCharCount;
}
// Keycaps
if (codePoint == COMBINING_ENCLOSING_KEYCAP) {
codePoint = Character.codePointBefore(text, lastOffset);
lastOffset -= Character.charCount(codePoint);
if (lastOffset > 0 && isVariationSelector(codePoint)) {
int tmpCodePoint = Character.codePointBefore(text, lastOffset);
if (isKeycapBase(tmpCodePoint)) {
deleteCharCount += Character.charCount(codePoint) + Character.charCount(tmpCodePoint);
}
} else if (isKeycapBase(codePoint)) {
deleteCharCount += Character.charCount(codePoint);
}
return offset - deleteCharCount;
}
/**
* Following if statements for Emoji tag sequence and Variation selector are skipping these
* modifiers for going through the last statement that is for handling emojis. They return the
* offset if they don't find proper base characters
*/
// Emoji Tag Sequence
if (codePoint == CANCEL_TAG) { // tag_end
codePoint = Character.codePointBefore(text, lastOffset);
lastOffset -= Character.charCount(codePoint);
while (lastOffset > 0 && isTagSpecChar(codePoint)) { // tag_spec
deleteCharCount += Character.charCount(codePoint);
codePoint = Character.codePointBefore(text, lastOffset);
lastOffset -= Character.charCount(codePoint);
}
if (!isEmoji(codePoint)) { // tag_base not found. Just delete the end.
return offset - 2;
}
deleteCharCount += Character.charCount(codePoint);
}
if (isVariationSelector(codePoint)) {
codePoint = Character.codePointBefore(text, lastOffset);
if (!isEmoji(codePoint)) {
return offset - deleteCharCount;
}
deleteCharCount += Character.charCount(codePoint);
lastOffset -= deleteCharCount;
}
if (isEmoji(codePoint)) {
boolean isZwj = false;
int lastSeenVariantSelectorCharCount = 0;
do {
if (isZwj) {
deleteCharCount += Character.charCount(codePoint) + lastSeenVariantSelectorCharCount + 1;
isZwj = false;
}
lastSeenVariantSelectorCharCount = 0;
if (isEmojiModifier(codePoint)) {
codePoint = Character.codePointBefore(text, lastOffset);
lastOffset -= Character.charCount(codePoint);
if (lastOffset > 0 && isVariationSelector(codePoint)) {
codePoint = Character.codePointBefore(text, lastOffset);
if (!isEmoji(codePoint)) {
return offset - deleteCharCount;
}
lastSeenVariantSelectorCharCount = Character.charCount(codePoint);
lastOffset -= Character.charCount(codePoint);
}
if (isEmojiModifierBase(codePoint)) {
deleteCharCount += lastSeenVariantSelectorCharCount + Character.charCount(codePoint);
}
break;
}
if (lastOffset > 0) {
codePoint = Character.codePointBefore(text, lastOffset);
lastOffset -= Character.charCount(codePoint);
if (codePoint == ZERO_WIDTH_JOINER) {
isZwj = true;
codePoint = Character.codePointBefore(text, lastOffset);
lastOffset -= Character.charCount(codePoint);
if (lastOffset > 0 && isVariationSelector(codePoint)) {
codePoint = Character.codePointBefore(text, lastOffset);
lastSeenVariantSelectorCharCount = Character.charCount(codePoint);
lastOffset -= Character.charCount(codePoint);
}
}
}
if (lastOffset == 0) {
break;
}
} while (isZwj && isEmoji(codePoint));
}
return offset - deleteCharCount;
}