public int getOffsetBefore()

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;
  }