private static void registerProblem( @NotNull final AnnotationHolder holder, @NotNull final XmlTag tag, @Nullable final XmlToken start, @NotNull final XmlToken end) { final String tagName = (tag instanceof HtmlTag) ? tag.getName().toLowerCase() : tag.getName(); final String endTokenText = (tag instanceof HtmlTag) ? end.getText().toLowerCase() : end.getText(); final RenameTagBeginOrEndIntentionAction renameEndAction = new RenameTagBeginOrEndIntentionAction(tagName, endTokenText, false); final RenameTagBeginOrEndIntentionAction renameStartAction = new RenameTagBeginOrEndIntentionAction(endTokenText, tagName, true); if (start != null) { final Annotation annotation = holder.createErrorAnnotation( start, XmlErrorMessages.message("tag.has.wrong.closing.tag.name")); annotation.registerFix(renameEndAction); annotation.registerFix(renameStartAction); } final Annotation annotation = holder.createErrorAnnotation(end, XmlErrorMessages.message("wrong.closing.tag.name")); annotation.registerFix(new RemoveExtraClosingTagIntentionAction()); annotation.registerFix(renameEndAction); annotation.registerFix(renameStartAction); }
@Override public void visitXmlToken(XmlToken token) { IElementType tokenType = token.getTokenType(); if (tokenType == XmlTokenType.XML_NAME || tokenType == XmlTokenType.XML_TAG_NAME) { PsiElement element = token.getPrevSibling(); while (element instanceof PsiWhiteSpace) element = element.getPrevSibling(); if (element instanceof XmlToken) { if (((XmlToken) element).getTokenType() == XmlTokenType.XML_START_TAG_START) { PsiElement parent = element.getParent(); if (parent instanceof XmlTag && !(token.getNextSibling() instanceof OuterLanguageElement)) { checkTag((XmlTag) parent); } } } else { PsiElement parent = token.getParent(); if (parent instanceof XmlAttribute && !(token.getNextSibling() instanceof OuterLanguageElement)) { checkAttribute((XmlAttribute) parent); } } } else if (tokenType == XmlTokenType.XML_DATA_CHARACTERS && token.getParent() instanceof XmlText) { if (token.textContains(']') && token.textContains('>')) { String s = token.getText(); String marker = "]]>"; int i = s.indexOf(marker); if (i != -1) { // TODO: fix TextRange textRange = token.getTextRange(); int start = textRange.getStartOffset() + i; HighlightInfoType type = PsiTreeUtil.getParentOfType(token, XmlTag.class) instanceof HtmlTag ? HighlightInfoType.WARNING : HighlightInfoType.ERROR; HighlightInfo info = HighlightInfo.createHighlightInfo( type, start, start + marker.length(), XmlErrorMessages.message( "cdata.end.should.not.appear.in.content.unless.to.mark.end.of.cdata.section")); addToResults(info); } } } }
public void annotate(final PsiElement psiElement, final AnnotationHolder holder) { if (psiElement instanceof XmlToken) { final PsiElement parent = psiElement.getParent(); if (parent instanceof XmlTag) { final XmlTag tag = (XmlTag) parent; final XmlToken start = XmlTagUtil.getStartTagNameElement(tag); XmlToken endTagName = XmlTagUtil.getEndTagNameElement(tag); if (endTagName != null && !(tag instanceof HtmlTag) && !tag.getName().equals(endTagName.getText())) { registerProblem(holder, tag, start, endTagName); } else if (endTagName == null && !(tag instanceof HtmlTag && HtmlUtil.isSingleHtmlTag(tag.getName()))) { final PsiErrorElement errorElement = PsiTreeUtil.getChildOfType(tag, PsiErrorElement.class); endTagName = findEndTagName(errorElement); if (endTagName != null) { registerProblem(holder, tag, start, endTagName); } } } else if (parent instanceof PsiErrorElement) { if (XmlTokenType.XML_NAME == ((XmlToken) psiElement).getTokenType()) { final PsiFile psiFile = psiElement.getContainingFile(); if (psiFile != null && (HTMLLanguage.INSTANCE == psiFile.getViewProvider().getBaseLanguage() || HTMLLanguage.INSTANCE == parent.getLanguage())) { final String message = XmlErrorMessages.message("xml.parsing.closing.tag.mathes.nothing"); if (message.equals(((PsiErrorElement) parent).getErrorDescription())) { final Annotation annotation = holder.createWarningAnnotation(parent, message); annotation.registerFix(new RemoveExtraClosingTagIntentionAction()); } } } } } }
public Result beforeCharTyped( final char c, final Project project, final Editor editor, final PsiFile editedFile, final FileType fileType) { final XmlEditorOptions xmlEditorOptions = XmlEditorOptions.getInstance(); if (c == '>' && xmlEditorOptions != null && xmlEditorOptions.isAutomaticallyInsertClosingTag() && (editedFile.getLanguage() instanceof XMLLanguage || editedFile.getViewProvider().getBaseLanguage() instanceof XMLLanguage)) { PsiDocumentManager.getInstance(project).commitAllDocuments(); PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument()); FileViewProvider provider = editedFile.getViewProvider(); int offset = editor.getCaretModel().getOffset(); PsiElement element, elementAtCaret = null; if (offset < editor.getDocument().getTextLength()) { elementAtCaret = element = provider.findElementAt(offset, XMLLanguage.class); if (!(element instanceof PsiWhiteSpace)) { boolean nonAcceptableDelimiter = true; if (element instanceof XmlToken) { IElementType tokenType = ((XmlToken) element).getTokenType(); if (tokenType == XmlTokenType.XML_START_TAG_START || tokenType == XmlTokenType.XML_END_TAG_START) { if (offset > 0) { PsiElement previousElement = provider.findElementAt(offset - 1, XMLLanguage.class); if (previousElement instanceof XmlToken) { tokenType = ((XmlToken) previousElement).getTokenType(); element = previousElement; nonAcceptableDelimiter = false; } } } else if (tokenType == XmlTokenType.XML_NAME) { if (element.getNextSibling() instanceof PsiErrorElement) { nonAcceptableDelimiter = false; } } if (tokenType == XmlTokenType.XML_TAG_END || tokenType == XmlTokenType.XML_EMPTY_ELEMENT_END && element.getTextOffset() == offset - 1) { editor.getCaretModel().moveToOffset(offset + 1); editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); return Result.STOP; } } if (nonAcceptableDelimiter) return Result.CONTINUE; } else { // check if right after empty end PsiElement previousElement = provider.findElementAt(offset - 1, XMLLanguage.class); if (previousElement instanceof XmlToken) { final IElementType tokenType = ((XmlToken) previousElement).getTokenType(); if (tokenType == XmlTokenType.XML_EMPTY_ELEMENT_END) { return Result.STOP; } } } PsiElement parent = element.getParent(); if (parent instanceof XmlText) { final String text = parent.getText(); // check / final int index = offset - parent.getTextOffset() - 1; if (index >= 0 && text.charAt(index) == '/') { return Result.CONTINUE; // already seen / } element = parent.getPrevSibling(); } else if (parent instanceof XmlTag && !(element.getPrevSibling() instanceof XmlTag)) { element = parent; } else if (parent instanceof XmlAttributeValue) { element = parent; } } else { element = provider.findElementAt(editor.getDocument().getTextLength() - 1, XMLLanguage.class); if (element == null) return Result.CONTINUE; element = element.getParent(); } if (offset > 0 && offset <= editor.getDocument().getTextLength()) { if (editor.getDocument().getCharsSequence().charAt(offset - 1) == '/') { // Some languages (e.g. GSP) allow character '/' in tag name. return Result.CONTINUE; } } if (element instanceof XmlAttributeValue) { element = element.getParent().getParent(); } while (element instanceof PsiWhiteSpace) element = element.getPrevSibling(); if (element instanceof XmlDocument) { // hack for closing tags in RHTML element = element.getLastChild(); } if (element == null) return Result.CONTINUE; if (!(element instanceof XmlTag)) { if (element instanceof XmlTokenImpl && element.getPrevSibling() != null && element.getPrevSibling().getText().equals("<")) { // tag is started and there is another text in the end editor.getDocument().insertString(offset, "</" + element.getText() + ">"); } return Result.CONTINUE; } XmlTag tag = (XmlTag) element; if (XmlUtil.getTokenOfType(tag, XmlTokenType.XML_TAG_END) != null) return Result.CONTINUE; if (XmlUtil.getTokenOfType(tag, XmlTokenType.XML_EMPTY_ELEMENT_END) != null) return Result.CONTINUE; final XmlToken startToken = XmlUtil.getTokenOfType(tag, XmlTokenType.XML_START_TAG_START); if (startToken == null || !startToken.getText().equals("<")) return Result.CONTINUE; String name = tag.getName(); if (elementAtCaret instanceof XmlToken && ((XmlToken) elementAtCaret).getTokenType() == XmlTokenType.XML_NAME) { name = name.substring(0, offset - elementAtCaret.getTextOffset()); } if (tag instanceof HtmlTag && HtmlUtil.isSingleHtmlTag(name)) return Result.CONTINUE; if ("".equals(name)) return Result.CONTINUE; int tagOffset = tag.getTextRange().getStartOffset(); final XmlToken nameToken = XmlUtil.getTokenOfType(tag, XmlTokenType.XML_NAME); if (nameToken != null && nameToken.getTextRange().getStartOffset() > offset) return Result.CONTINUE; HighlighterIterator iterator = ((EditorEx) editor).getHighlighter().createIterator(tagOffset); if (BraceMatchingUtil.matchBrace( editor.getDocument().getCharsSequence(), editedFile.getFileType(), iterator, true, true)) { PsiElement parent = tag.getParent(); boolean hasBalance = true; while (parent instanceof XmlTag && name.equals(((XmlTag) parent).getName())) { ASTNode astNode = XmlChildRole.CLOSING_TAG_NAME_FINDER.findChild(parent.getNode()); if (astNode == null) { hasBalance = false; break; } parent = parent.getParent(); } if (hasBalance) { hasBalance = false; for (ASTNode node = parent.getNode().getLastChildNode(); node != null; node = node.getTreePrev()) { ASTNode leaf = node; if (leaf.getElementType() == TokenType.ERROR_ELEMENT) { ASTNode firstChild = leaf.getFirstChildNode(); if (firstChild != null) leaf = firstChild; else { PsiElement psiElement = PsiTreeUtil.nextLeaf(leaf.getPsi()); leaf = psiElement != null ? psiElement.getNode() : null; } if (leaf != null && leaf.getElementType() == TokenType.WHITE_SPACE) { PsiElement psiElement = PsiTreeUtil.nextLeaf(leaf.getPsi()); if (psiElement != null) leaf = psiElement.getNode(); } } if (leaf != null && leaf.getElementType() == XmlTokenType.XML_END_TAG_START) { ASTNode treeNext = leaf.getTreeNext(); IElementType treeNextType; if (treeNext != null && ((treeNextType = treeNext.getElementType()) == XmlTokenType.XML_NAME || treeNextType == XmlTokenType.XML_TAG_NAME)) { if (name.equals(treeNext.getText())) { ASTNode parentEndName = parent instanceof XmlTag ? XmlChildRole.CLOSING_TAG_NAME_FINDER.findChild(parent.getNode()) : null; hasBalance = !(parent instanceof XmlTag) || parentEndName != null && !parentEndName.getText().equals(name); break; } } } } } if (hasBalance) return Result.CONTINUE; } TextRange cdataReformatRange = null; final XmlElementDescriptor descriptor = tag.getDescriptor(); if (descriptor instanceof XmlElementDescriptorWithCDataContent) { final XmlElementDescriptorWithCDataContent cDataContainer = (XmlElementDescriptorWithCDataContent) descriptor; if (cDataContainer.requiresCdataBracesInContext(tag)) { int rangeStart = offset; @NonNls final String cDataStart = "><![CDATA["; final String inserted = cDataStart + "\n]]>"; editor.getDocument().insertString(offset, inserted); final int newoffset = offset + cDataStart.length(); editor.getCaretModel().moveToOffset(newoffset); offset += inserted.length(); cdataReformatRange = new TextRange(rangeStart, offset + 1); } } editor.getDocument().insertString(offset, "</" + name + ">"); if (cdataReformatRange != null) { PsiDocumentManager.getInstance(project).commitDocument(editor.getDocument()); try { CodeStyleManager.getInstance(project) .reformatText( file, cdataReformatRange.getStartOffset(), cdataReformatRange.getEndOffset()); } catch (IncorrectOperationException e) { LOG.error(e); } } return cdataReformatRange != null ? Result.STOP : Result.CONTINUE; } return Result.CONTINUE; }