private void checkDuplicateAttribute(XmlTag tag, final XmlAttribute attribute) { if (skipValidation(tag)) { return; } final XmlAttribute[] attributes = tag.getAttributes(); final PsiFile containingFile = tag.getContainingFile(); final XmlExtension extension = containingFile instanceof XmlFile ? XmlExtension.getExtension(containingFile) : XmlExtension.DEFAULT_EXTENSION; for (XmlAttribute tagAttribute : attributes) { ProgressManager.checkCanceled(); if (attribute != tagAttribute && Comparing.strEqual(attribute.getName(), tagAttribute.getName())) { final String localName = attribute.getLocalName(); if (extension.canBeDuplicated(tagAttribute)) continue; // multiple import attributes are allowed in jsp directive HighlightInfo highlightInfo = HighlightInfo.createHighlightInfo( getTagProblemInfoType(tag), XmlChildRole.ATTRIBUTE_NAME_FINDER.findChild( SourceTreeToPsiMap.psiElementToTree(attribute)), XmlErrorMessages.message("duplicate.attribute", localName)); addToResults(highlightInfo); IntentionAction intentionAction = new RemoveAttributeIntentionFix(localName, attribute); QuickFixAction.registerQuickFixAction(highlightInfo, intentionAction); } } }
@Override public void visitXmlAttributeValue(XmlAttributeValue value) { final PsiElement parent = value.getParent(); if (!(parent instanceof XmlAttribute)) { checkReferences(value); return; } XmlAttribute attribute = (XmlAttribute) parent; XmlTag tag = attribute.getParent(); XmlElementDescriptor elementDescriptor = tag.getDescriptor(); XmlAttributeDescriptor attributeDescriptor = elementDescriptor != null ? elementDescriptor.getAttributeDescriptor(attribute) : null; if (attributeDescriptor != null && !skipValidation(value)) { String error = attributeDescriptor.validateValue(value, attribute.getValue()); if (error != null) { addToResults(HighlightInfo.createHighlightInfo(getTagProblemInfoType(tag), value, error)); return; } } checkReferences(value); }
@Nullable private HighlightInfo reportAttributeProblem( final XmlTag tag, final String localName, final XmlAttribute attribute, final String localizedMessage) { final RemoveAttributeIntentionFix removeAttributeIntention = new RemoveAttributeIntentionFix(localName, attribute); if (!(tag instanceof HtmlTag)) { final HighlightInfoType tagProblemInfoType = HighlightInfoType.WRONG_REF; final ASTNode node = SourceTreeToPsiMap.psiElementToTree(attribute); assert node != null; final ASTNode child = XmlChildRole.ATTRIBUTE_NAME_FINDER.findChild(node); assert child != null; final HighlightInfo highlightInfo = HighlightInfo.createHighlightInfo(tagProblemInfoType, child, localizedMessage); addToResults(highlightInfo); QuickFixAction.registerQuickFixAction(highlightInfo, removeAttributeIntention); return highlightInfo; } return null; }
@Override public void visitXmlProcessingInstruction(XmlProcessingInstruction processingInstruction) { super.visitXmlProcessingInstruction(processingInstruction); PsiElement parent = processingInstruction.getParent(); if (parent instanceof XmlProlog && processingInstruction.getText().startsWith("<?xml")) { for (PsiElement e = PsiTreeUtil.prevLeaf(processingInstruction); e != null; e = PsiTreeUtil.prevLeaf(e)) { if (e instanceof PsiWhiteSpace && PsiTreeUtil.prevLeaf(e) != null || e instanceof OuterLanguageElement) { continue; } PsiElement eParent = e.getParent(); if (eParent instanceof PsiComment) e = eParent; if (eParent instanceof XmlProcessingInstruction) break; addToResults( HighlightInfo.createHighlightInfo( HighlightInfoType.ERROR, e, XmlErrorMessages.message("xml.declaration.should.precede.all.document.content"))); } } }
private void doCheckRefs(final PsiElement value, final PsiReference[] references, int start) { for (int i = start; i < references.length; ++i) { PsiReference reference = references[i]; ProgressManager.checkCanceled(); if (reference == null) { continue; } if (!hasBadResolve(reference, false)) { continue; } String description = getErrorDescription(reference); final int startOffset = reference.getElement().getTextRange().getStartOffset(); final TextRange referenceRange = reference.getRangeInElement(); // logging for IDEADEV-29655 if (referenceRange.getStartOffset() > referenceRange.getEndOffset()) { LOG.error( "Reference range start offset > end offset: " + reference + ", start offset: " + referenceRange.getStartOffset() + ", end offset: " + referenceRange.getEndOffset()); } HighlightInfoType type = getTagProblemInfoType(PsiTreeUtil.getParentOfType(value, XmlTag.class)); if (value instanceof XmlAttributeValue) { PsiElement parent = value.getParent(); if (parent instanceof XmlAttribute) { String name = ((XmlAttribute) parent).getName().toLowerCase(); if (type.getSeverity(null).compareTo(HighlightInfoType.WARNING.getSeverity(null)) > 0 && name.endsWith("stylename")) { type = HighlightInfoType.WARNING; } else if (name.equals("href") && type.getSeverity(null) == HighlightInfoType.WARNING.getSeverity(null)) { continue; } } } HighlightInfo info = HighlightInfo.createHighlightInfo( type, startOffset + referenceRange.getStartOffset(), startOffset + referenceRange.getEndOffset(), description); addToResults(info); if (reference instanceof QuickFixProvider) ((QuickFixProvider) reference).registerQuickfix(info, reference); UnresolvedReferenceQuickFixProvider.registerReferenceFixes( reference, new QuickFixActionRegistrarImpl(info)); } }
public void addMessage( final PsiElement context, final String message, final ErrorType type, final IntentionAction... fixes) { if (message != null && message.length() > 0) { final PsiFile containingFile = context.getContainingFile(); final HighlightInfoType defaultInfoType = type == ErrorType.ERROR ? HighlightInfoType.ERROR : type == ErrorType.WARNING ? HighlightInfoType.WARNING : HighlightInfoType.WEAK_WARNING; if (context instanceof XmlTag && XmlExtension.getExtension(containingFile).shouldBeHighlightedAsTag((XmlTag) context)) { addElementsForTagWithManyQuickFixes((XmlTag) context, message, defaultInfoType, fixes); } else { final PsiElement contextOfFile = containingFile.getContext(); final HighlightInfo highlightInfo; if (contextOfFile != null) { TextRange range = InjectedLanguageManager.getInstance(context.getProject()) .injectedToHost(context, context.getTextRange()); highlightInfo = HighlightInfo.createHighlightInfo(defaultInfoType, range, message); } else { highlightInfo = HighlightInfo.createHighlightInfo(HighlightInfoType.WRONG_REF, context, message); } if (fixes != null) { for (final IntentionAction quickFixAction : fixes) { if (quickFixAction == null) continue; QuickFixAction.registerQuickFixAction(highlightInfo, quickFixAction); } } addToResults(highlightInfo); } } }
private void bindMessageToAstNode( final PsiElement childByRole, final HighlightInfoType warning, final int offset, int length, final String localizedMessage, IntentionAction... quickFixActions) { if (childByRole != null) { final TextRange textRange = childByRole.getTextRange(); if (length == -1) length = textRange.getLength(); final int startOffset = textRange.getStartOffset() + offset; HighlightInfo highlightInfo = HighlightInfo.createHighlightInfo( warning, childByRole, startOffset, startOffset + length, localizedMessage, HighlightInfo.htmlEscapeToolTip(localizedMessage)); if (highlightInfo == null) { highlightInfo = HighlightInfo.createHighlightInfo( warning, new TextRange(startOffset, startOffset + length), localizedMessage, localizedMessage, NONEMPTY_TEXT_ATTRIBUTES); } for (final IntentionAction quickFixAction : quickFixActions) { if (quickFixAction == null) continue; QuickFixAction.registerQuickFixAction(highlightInfo, textRange, quickFixAction, null); } addToResults(highlightInfo); } }
@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 addMessage(PsiElement context, String message, int type) { if (message != null && message.length() > 0) { if (context instanceof XmlTag && XmlExtension.getExtension(context.getContainingFile()) .shouldBeHighlightedAsTag((XmlTag) context)) { HighlightInfoType infoType = type == ERROR ? HighlightInfoType.ERROR : type == WARNING ? HighlightInfoType.WARNING : HighlightInfoType.WEAK_WARNING; addElementsForTag((XmlTag) context, message, infoType, null); } else { addToResults( HighlightInfo.createHighlightInfo(HighlightInfoType.WRONG_REF, context, message)); } } }
private void checkAttribute(XmlAttribute attribute) { XmlTag tag = attribute.getParent(); final String name = attribute.getName(); PsiElement prevLeaf = PsiTreeUtil.prevLeaf(attribute); if (!(prevLeaf instanceof PsiWhiteSpace)) { TextRange textRange = attribute.getTextRange(); addToResults( HighlightInfo.createHighlightInfo( tag instanceof HtmlTag ? HighlightInfoType.WARNING : HighlightInfoType.ERROR, textRange.getStartOffset(), textRange.getStartOffset(), XmlErrorMessages.message("attribute.should.be.preceded.with.space"))); } if (attribute.isNamespaceDeclaration()) { checkReferences(attribute.getValueElement()); return; } final String namespace = attribute.getNamespace(); if (XmlUtil.XML_SCHEMA_INSTANCE_URI.equals(namespace)) { checkReferences(attribute.getValueElement()); return; } XmlElementDescriptor elementDescriptor = tag.getDescriptor(); if (elementDescriptor == null || elementDescriptor instanceof AnyXmlElementDescriptor || ourDoJaxpTesting) { return; } XmlAttributeDescriptor attributeDescriptor = elementDescriptor.getAttributeDescriptor(attribute); if (attributeDescriptor == null) { if (!XmlUtil.attributeFromTemplateFramework(name, tag)) { final String localizedMessage = XmlErrorMessages.message("attribute.is.not.allowed.here", name); final HighlightInfo highlightInfo = reportAttributeProblem(tag, name, attribute, localizedMessage); if (highlightInfo != null) { final XmlFile xmlFile = (XmlFile) tag.getContainingFile(); if (xmlFile != null) { XmlExtension.getExtension(xmlFile).createAddAttributeFix(attribute, highlightInfo); } } } } else { checkDuplicateAttribute(tag, attribute); if (tag instanceof HtmlTag && attribute.getValueElement() == null && !HtmlUtil.isSingleHtmlAttribute(name)) { final String localizedMessage = XmlErrorMessages.message("empty.attribute.is.not.allowed", name); reportAttributeProblem(tag, name, attribute, localizedMessage); } // we skip resolve of attribute references since there is separate check when taking attribute // descriptors PsiReference[] attrRefs = attribute.getReferences(); doCheckRefs(attribute, attrRefs, attribute.getNamespacePrefix().length() > 0 ? 2 : 1); } }