boolean (Project project, Editor editor, PsiFile psiFile) { final PsiElement atCaret = getStatementAtCaret(editor, psiFile); XmlTag tagAtCaret = PsiTreeUtil.getParentOfType(atCaret, XmlTag.class); if (tagAtCaret == null) { return false; } try { final ASTNode emptyTagEnd = XmlChildRole.EMPTY_TAG_END_FINDER.findChild(tagAtCaret.getNode()); final ASTNode endTagEnd = XmlChildRole.START_TAG_END_FINDER.findChild(tagAtCaret.getNode()); final Document doc = editor.getDocument(); if (emptyTagEnd == null && endTagEnd == null) { int insertionOffset = tagAtCaret.getTextRange().getEndOffset(); int caretAt = editor.getCaretModel().getOffset(); final CharSequence text = doc.getCharsSequence(); final int probableCommaOffset = CharArrayUtil.shiftForward(text, insertionOffset, " \t"); final PsiElement sibling = tagAtCaret.getNextSibling(); int caretTo = caretAt; char ch; if (caretAt < probableCommaOffset) { final XmlAttribute xmlAttribute = PsiTreeUtil.getParentOfType(atCaret, XmlAttribute.class, false, XmlTag.class); CharSequence tagNameText = null; if (xmlAttribute != null) { final ASTNode node = tagAtCaret.getNode(); if (node != null) { final ASTNode tagName = XmlChildRole.START_TAG_NAME_FINDER.findChild(node); if (tagName != null) { tagNameText = tagName.getText(); } } final XmlAttributeValue valueElement = xmlAttribute.getValueElement(); final TextRange textRange = xmlAttribute.getTextRange(); caretAt = valueElement == null ? textRange.getStartOffset() : getClosingQuote(xmlAttribute).length() == 0 ? textRange.getEndOffset() : caretAt; } if (tagNameText == null) { tagNameText = text.subSequence(tagAtCaret.getTextRange().getStartOffset() + 1, caretAt); } final PsiElement element = psiFile.findElementAt(probableCommaOffset); final XmlTag tag = PsiTreeUtil.getParentOfType(element, XmlTag.class); boolean shouldInsertClosingTag = shouldAfterWrapTextWithTag(caretAt, probableCommaOffset) || shouldInsertClosingTag(xmlAttribute, tagAtCaret); final CharSequence text2insert = getClosingPart(xmlAttribute, tagAtCaret, !shouldInsertClosingTag); if (tag != null && tag.getTextRange().getStartOffset() == probableCommaOffset) { doc.insertString(caretAt, text2insert); if (shouldInsertClosingTag) { doc.insertString(tag.getTextRange().getEndOffset() + text2insert.length(), "</" + tagAtCaret.getName() + ">"); } caretTo = tag.getTextRange().getEndOffset() + text2insert.length(); } else { doc.insertString(caretAt, text2insert); if (shouldInsertClosingTag) { doc.insertString(probableCommaOffset + text2insert.length(), "</" + tagNameText + ">"); } caretTo = probableCommaOffset + text2insert.length(); } } else if (sibling instanceof XmlTag && sibling.getTextRange().getStartOffset() == caretAt) { final XmlAttribute xmlAttribute = PsiTreeUtil.getParentOfType(atCaret, XmlAttribute.class, false, XmlTag.class); final CharSequence text2insert = getClosingPart(xmlAttribute, tagAtCaret, false); doc.insertString(caretAt, text2insert); if (shouldInsertClosingTag(xmlAttribute, tagAtCaret)) { doc.insertString(sibling.getTextRange().getEndOffset() + text2insert.length(), "</" + tagAtCaret.getName() + ">"); } caretTo = sibling.getTextRange().getEndOffset() + text2insert.length(); } else if (probableCommaOffset >= text.length() || ((ch = text.charAt(probableCommaOffset)) != '/' && ch != '>')) { final XmlAttribute xmlAttribute = PsiTreeUtil.getParentOfType(atCaret, XmlAttribute.class, false, XmlTag.class); final String text2insert = getClosingPart(xmlAttribute, tagAtCaret, true); doc.insertString(insertionOffset, text2insert); caretTo = insertionOffset + text2insert.indexOf('>') + 1; } commitChanges(project, editor, psiFile, caretTo, null); } else { final XmlTag unclosedTag = findClosestUnclosedTag(tagAtCaret); if (unclosedTag == null) { return false; } final String closingTagString = "</" + unclosedTag.getName() + ">"; final XmlTag parentTag = unclosedTag.getParentTag(); final ASTNode parentEndTagNode = parentTag != null ? XmlChildRole.CLOSING_TAG_START_FINDER.findChild(parentTag.getNode()) : null; final int offset = parentEndTagNode != null ? parentEndTagNode.getTextRange().getStartOffset() : unclosedTag.getTextRange().getEndOffset(); doc.insertString(offset, closingTagString); commitChanges(project, editor, psiFile, offset, parentTag != null ? parentTag : unclosedTag); } return true; } catch (IncorrectOperationException e) { LOG.error(e); } return false; }