public static boolean shouldBeValidated(@NotNull XmlTag tag) { PsiElement parent = tag.getParent(); if (parent instanceof XmlTag) { return !skipValidation(parent) && !XmlUtil.tagFromTemplateFramework(tag); } return true; }
private void checkTagByDescriptor(final XmlTag tag) { String name = tag.getName(); XmlElementDescriptor elementDescriptor; final PsiElement parent = tag.getParent(); if (parent instanceof XmlTag) { XmlTag parentTag = (XmlTag) parent; elementDescriptor = XmlUtil.getDescriptorFromContext(tag); final XmlElementDescriptor parentDescriptor = parentTag.getDescriptor(); if (parentDescriptor != null && elementDescriptor == null && shouldBeValidated(tag)) { if (tag instanceof HtmlTag) { // XmlEntitiesInspection inspection = getInspectionProfile(tag, // HtmlStyleLocalInspection.SHORT_NAME); // if (inspection != null /*&& // isAdditionallyDeclared(inspection.getAdditionalEntries(XmlEntitiesInspection.UNKNOWN_TAG), name)*/) { return; // } } addElementsForTag( tag, XmlErrorMessages.message("element.is.not.allowed.here", name), getTagProblemInfoType(tag), null); return; } if (elementDescriptor instanceof AnyXmlElementDescriptor || elementDescriptor == null) { elementDescriptor = tag.getDescriptor(); } if (elementDescriptor == null) return; } else { // root tag elementDescriptor = tag.getDescriptor(); if (elementDescriptor == null) { addElementsForTag( tag, XmlErrorMessages.message("element.must.be.declared", name), HighlightInfoType.WRONG_REF, null); return; } } checkRequiredAttributes(tag, name, elementDescriptor); if (elementDescriptor instanceof Validator) { //noinspection unchecked ((Validator<XmlTag>) elementDescriptor).validate(tag, this); } }
@Nullable private static XmlElementDescriptor getDescriptorFromParent( final XmlTag tag, XmlElementDescriptor elementDescriptor) { final PsiElement parent = tag.getParent(); if (parent instanceof XmlTag) { final XmlElementDescriptor descriptor = ((XmlTag) parent).getDescriptor(); if (descriptor != null) elementDescriptor = descriptor.getElementDescriptor(tag, (XmlTag) parent); } return elementDescriptor; }
public XmlElementDescriptor getElementDescriptor(@NotNull XmlTag tag) { PsiElement parent = tag.getParent(); final String namespace = tag.getNamespace(); while (parent instanceof XmlTag && !namespace.equals(((XmlTag) parent).getNamespace())) parent = parent.getContext(); if (parent instanceof XmlTag) { final XmlTag parentTag = (XmlTag) parent; final XmlElementDescriptor parentDescriptor = parentTag.getDescriptor(); if (parentDescriptor != null) { XmlElementDescriptor elementDescriptorFromParent = parentDescriptor.getElementDescriptor(tag, parentTag); if (elementDescriptorFromParent == null) { elementDescriptorFromParent = getDescriptorFromParent(tag, elementDescriptorFromParent); } if (elementDescriptorFromParent instanceof AnyXmlElementDescriptor) { final XmlElementDescriptor elementDescriptor = getElementDescriptor(tag.getLocalName(), namespace); if (elementDescriptor != null) return elementDescriptor; } return elementDescriptorFromParent; } else { return null; } } else { XmlElementDescriptor elementDescriptor = getElementDescriptor(tag.getLocalName(), tag.getNamespace()); if (elementDescriptor == null) { elementDescriptor = getDescriptorFromParent(tag, elementDescriptor); } return elementDescriptor; } }
@Override protected void checkTag( @NotNull final XmlTag tag, @NotNull final ProblemsHolder holder, final boolean isOnTheFly) { if (!(tag instanceof HtmlTag) || !XmlHighlightVisitor.shouldBeValidated(tag) || isInSpecialHtml5Namespace(tag)) { return; } XmlElementDescriptor descriptorFromContext = XmlUtil.getDescriptorFromContext(tag); PsiElement parent = tag.getParent(); XmlElementDescriptor parentDescriptor = parent instanceof XmlTag ? ((XmlTag) parent).getDescriptor() : null; XmlElementDescriptor ownDescriptor = isAbstractDescriptor(descriptorFromContext) ? tag.getDescriptor() : descriptorFromContext; if (isAbstractDescriptor(ownDescriptor) || (parentDescriptor instanceof HtmlElementDescriptorImpl && ownDescriptor instanceof HtmlElementDescriptorImpl && isAbstractDescriptor(descriptorFromContext))) { final String name = tag.getName(); if (!isCustomValuesEnabled() || !isCustomValue(name)) { final AddCustomTagOrAttributeIntentionAction action = new AddCustomTagOrAttributeIntentionAction( TAG_KEY, name, XmlEntitiesInspection.UNKNOWN_TAG); // todo: support "element is not allowed" message for html5 // some tags in html5 cannot be found in xhtml5.xsd if they are located in incorrect // context, so they get any-element descriptor (ex. "canvas: tag) final String message = isAbstractDescriptor(ownDescriptor) ? XmlErrorMessages.message("unknown.html.tag", name) : XmlErrorMessages.message("element.is.not.allowed.here", name); final PsiElement startTagName = XmlTagUtil.getStartTagNameElement(tag); assert startTagName != null; final PsiElement endTagName = XmlTagUtil.getEndTagNameElement(tag); List<LocalQuickFix> quickfixes = new ArrayList<LocalQuickFix>(); quickfixes.add(action); if (isOnTheFly) { ContainerUtil.addIfNotNull( CreateNSDeclarationIntentionFix.createFix(startTagName, ""), quickfixes); } if (HtmlUtil.isHtml5Tag(name) && !HtmlUtil.hasNonHtml5Doctype(tag)) { quickfixes.add(new SwitchToHtml5WithHighPriorityAction()); } ProblemHighlightType highlightType = tag.getContainingFile().getContext() == null ? ProblemHighlightType.GENERIC_ERROR_OR_WARNING : ProblemHighlightType.INFORMATION; if (startTagName.getTextLength() > 0) { holder.registerProblem( startTagName, message, highlightType, quickfixes.toArray(new LocalQuickFix[quickfixes.size()])); } if (endTagName != null) { holder.registerProblem( endTagName, message, highlightType, quickfixes.toArray(new LocalQuickFix[quickfixes.size()])); } } } }
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; }