public void parseTag() {
    assert token() == XmlTokenType.XML_START_TAG_START : "Tag start expected";
    String originalTagName;
    PsiBuilder.Marker xmlText = null;
    while (shouldContinueMainLoop() && shouldContinueParsingTag()) {
      final IElementType tt = token();
      if (tt == XmlTokenType.XML_START_TAG_START) {
        xmlText = terminateText(xmlText);
        final PsiBuilder.Marker tagStart = mark();

        // Start tag header
        advance();
        originalTagName = parseOpenTagName();

        HtmlTagInfo info = createHtmlTagInfo(originalTagName, tagStart);
        while (openingTagAutoClosesTagInStack(info)) {
          completeTopStackItemBefore(tagStart);
        }
        pushItemToStack(info);

        parseTagHeader(info.getNormalizedName());

        if (token() == XmlTokenType.XML_EMPTY_ELEMENT_END) {
          advance();
          doneTag();
          continue;
        }

        if (token() == XmlTokenType.XML_TAG_END) {
          advance();
        }
        else {
          error(XmlPsiBundle.message("xml.parsing.tag.start.is.not.closed"));
          doneTag();
          continue;
        }

        if (isSingleTag(info)) {
          final PsiBuilder.Marker footer = mark();
          while (token() == XmlTokenType.XML_REAL_WHITE_SPACE) {
            advance();
          }
          if (token() == XmlTokenType.XML_END_TAG_START) {
            advance();
            if (token() == XmlTokenType.XML_NAME) {
              if (info.getNormalizedName().equalsIgnoreCase(myBuilder.getTokenText())) {
                advance();
                footer.drop();
                if (token() == XmlTokenType.XML_TAG_END) {
                  advance();
                }
                doneTag();
                continue;
              }
            }
          }
          footer.rollbackTo();
          doneTag();
        }
      }
      else if (tt == XmlTokenType.XML_PI_START) {
        xmlText = terminateText(xmlText);
        parseProcessingInstruction();
      }
      else if (tt == XmlTokenType.XML_ENTITY_REF_TOKEN || tt == XmlTokenType.XML_CHAR_ENTITY_REF) {
        xmlText = startText(xmlText);
        parseReference();
      }
      else if (tt == XmlTokenType.XML_CDATA_START) {
        xmlText = startText(xmlText);
        parseCData();
      }
      else if (tt == XmlTokenType.XML_COMMENT_START) {
        xmlText = startText(xmlText);
        parseComment();
      }
      else if (tt == XmlTokenType.XML_BAD_CHARACTER) {
        xmlText = startText(xmlText);
        final PsiBuilder.Marker error = mark();
        advance();
        error.error(XmlPsiBundle.message("xml.parsing.unescaped.ampersand.or.nonterminated.character.entity.reference"));
      }
      else if (tt instanceof ICustomParsingType || tt instanceof ILazyParseableElementType) {
        xmlText = terminateText(xmlText);
        maybeRemapCurrentToken(tt);
        advance();
      }
      else if (token() == XmlTokenType.XML_END_TAG_START) {
        xmlText = terminateText(xmlText);
        final PsiBuilder.Marker footer = mark();
        advance();

        String endTagName = parseEndTagName();
        if (endTagName != null) {
          endTagName = normalizeTagName(endTagName);
          final var itemOnStack = !myItemsStack.isEmpty() ? myItemsStack.peek() : null;
          if ((itemOnStack instanceof HtmlTagInfo tagOnStack
               && !tagOnStack.getNormalizedName().equals(endTagName)
               && !StringUtil.toLowerCase(endTagName).endsWith(COMPLETION_NAME))
              || !(itemOnStack instanceof HtmlTagInfo)
          ) {
            if (itemOnStack instanceof HtmlTagInfo tagOnStack && isTagFurtherInStack(endTagName)) {
              footer.rollbackTo();
              if (!canClosingTagAutoClose(tagOnStack, endTagName)) {
                error(XmlPsiBundle.message("xml.parsing.named.element.is.not.closed", tagOnStack.getOriginalName()));
              }
              doneTag();
            }
            else {
              if (token() == XmlTokenType.XML_TAG_END) advance();
              footer.error(XmlPsiBundle.message("xml.parsing.closing.tag.matches.nothing"));
            }
            continue;
          }

          while (token() != XmlTokenType.XML_TAG_END &&
                 token() != XmlTokenType.XML_START_TAG_START &&
                 token() != XmlTokenType.XML_END_TAG_START &&
                 !eof()) {
            error(XmlPsiBundle.message("xml.parsing.unexpected.token"));
            advance();
          }
        }
        else {
          error(XmlPsiBundle.message("xml.parsing.closing.tag.name.missing"));
        }
        footer.drop();

        if (token() == XmlTokenType.XML_TAG_END) {
          advance();
        }
        else {
          error(XmlPsiBundle.message("xml.parsing.closing.tag.is.not.done"));
        }

        if (hasTags()) {
          doneTag();
        }
      }
      else if ((token() == XmlTokenType.XML_REAL_WHITE_SPACE || token() == XmlTokenType.XML_DATA_CHARACTERS) && stackSize() == 0) {
        xmlText = terminateText(xmlText);
        advance();
      }
      else if (hasCustomTagContent()) {
        xmlText = parseCustomTagContent(xmlText);
      }
      else {
        xmlText = startText(xmlText);
        advance();
      }
    }
    terminateText(xmlText);
  }
public void parseTag()