From c488d3e992d55b9b24e3d82455ee58265fd86b77 Mon Sep 17 00:00:00 2001 From: Alexander Zolotov Date: Tue, 23 Dec 2014 19:52:41 +0300 Subject: [PATCH] HTML: extract common 'unknown element inspection' --- .../model/descriptors/AttributeFinder.java | 4 +- .../relaxNG/RngHtml5CompletionTest.java | 4 + .../html5_overwritten_attributes.xml | 8 ++ .../html5_overwritten_attributes_after.xml | 8 ++ .../HtmlUnknownElementInspection.java | 90 +++++++++++++++++++ 5 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 xml/relaxng/testData/completion/html5_overwritten_attributes.xml create mode 100644 xml/relaxng/testData/completion/html5_overwritten_attributes_after.xml create mode 100644 xml/xml-analysis-impl/src/com/intellij/codeInspection/htmlInspections/HtmlUnknownElementInspection.java diff --git a/xml/relaxng/src/org/intellij/plugins/relaxNG/model/descriptors/AttributeFinder.java b/xml/relaxng/src/org/intellij/plugins/relaxNG/model/descriptors/AttributeFinder.java index 32830be50fcc..5f2b13a1bee4 100644 --- a/xml/relaxng/src/org/intellij/plugins/relaxNG/model/descriptors/AttributeFinder.java +++ b/xml/relaxng/src/org/intellij/plugins/relaxNG/model/descriptors/AttributeFinder.java @@ -66,7 +66,9 @@ class AttributeFinder extends RecursionSaveWalker { if (depth == 1 && (myQname == null || p.getName().contains(myQname))) { myLastAttr = p; - myAttributes.put(p, Pair.create(new LinkedHashMap(), optional > 0)); + if (!myAttributes.containsKey(p)) { + myAttributes.put(p, Pair.create(new LinkedHashMap(), optional > 0)); + } return super.onAttribute(p); } return null; diff --git a/xml/relaxng/test/org/intellij/plugins/relaxNG/RngHtml5CompletionTest.java b/xml/relaxng/test/org/intellij/plugins/relaxNG/RngHtml5CompletionTest.java index 55db78a3dd8b..fce0d34c1709 100644 --- a/xml/relaxng/test/org/intellij/plugins/relaxNG/RngHtml5CompletionTest.java +++ b/xml/relaxng/test/org/intellij/plugins/relaxNG/RngHtml5CompletionTest.java @@ -93,4 +93,8 @@ public class RngHtml5CompletionTest extends HighlightingTestBase { public void testHtml5_17() throws Throwable { doTestCompletion("html5_17"); } + + public void testHtml5_overwritten_attributes() throws Throwable { + myTestFixture.testCompletionTyping("html5_overwritten_attributes.xml", "a\n", "html5_overwritten_attributes_after.xml"); + } } diff --git a/xml/relaxng/testData/completion/html5_overwritten_attributes.xml b/xml/relaxng/testData/completion/html5_overwritten_attributes.xml new file mode 100644 index 000000000000..effe54541449 --- /dev/null +++ b/xml/relaxng/testData/completion/html5_overwritten_attributes.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/xml/relaxng/testData/completion/html5_overwritten_attributes_after.xml b/xml/relaxng/testData/completion/html5_overwritten_attributes_after.xml new file mode 100644 index 000000000000..0d77c11e273e --- /dev/null +++ b/xml/relaxng/testData/completion/html5_overwritten_attributes_after.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/xml/xml-analysis-impl/src/com/intellij/codeInspection/htmlInspections/HtmlUnknownElementInspection.java b/xml/xml-analysis-impl/src/com/intellij/codeInspection/htmlInspections/HtmlUnknownElementInspection.java new file mode 100644 index 000000000000..e00aebcf5069 --- /dev/null +++ b/xml/xml-analysis-impl/src/com/intellij/codeInspection/htmlInspections/HtmlUnknownElementInspection.java @@ -0,0 +1,90 @@ +package com.intellij.codeInspection.htmlInspections; + +import com.intellij.codeInspection.LocalQuickFix; +import com.intellij.codeInspection.ProblemHighlightType; +import com.intellij.codeInspection.ProblemsHolder; +import com.intellij.lang.ASTNode; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.JDOMExternalizableStringList; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.PsiElement; +import com.intellij.psi.xml.XmlAttribute; +import com.intellij.psi.xml.XmlChildRole; +import org.jetbrains.annotations.NotNull; + +import java.util.StringTokenizer; + +abstract public class HtmlUnknownElementInspection extends HtmlLocalInspectionTool implements XmlEntitiesInspection { + public boolean myCustomValuesEnabled = true; + public JDOMExternalizableStringList myValues; + + public HtmlUnknownElementInspection(@NotNull String defaultValues) { + myValues = reparseProperties(defaultValues); + } + + protected static JDOMExternalizableStringList reparseProperties(@NotNull final String properties) { + final JDOMExternalizableStringList result = new JDOMExternalizableStringList(); + + final StringTokenizer tokenizer = new StringTokenizer(properties, ","); + while (tokenizer.hasMoreTokens()) { + result.add(tokenizer.nextToken().toLowerCase().trim()); + } + + return result; + } + + protected static void registerProblemOnAttributeName(@NotNull XmlAttribute attribute, + String message, @NotNull ProblemsHolder holder, + LocalQuickFix... quickfixes) { + final ASTNode node = attribute.getNode(); + assert node != null; + final ASTNode nameNode = XmlChildRole.ATTRIBUTE_NAME_FINDER.findChild(node); + if (nameNode != null) { + final PsiElement nameElement = nameNode.getPsi(); + if (nameElement.getTextLength() > 0) { + holder.registerProblem(nameElement, message, ProblemHighlightType.GENERIC_ERROR_OR_WARNING, quickfixes); + } + } + } + + protected boolean isCustomValue(@NotNull final String value) { + return myValues.contains(value.toLowerCase()); + } + + @Override + public void addEntry(@NotNull final String text) { + final String s = text.trim().toLowerCase(); + if (!isCustomValue(s)) { + myValues.add(s); + } + + if (!isCustomValuesEnabled()) { + myCustomValuesEnabled = true; + } + } + + public boolean isCustomValuesEnabled() { + return myCustomValuesEnabled; + } + + @Override + public String getAdditionalEntries() { + return StringUtil.join(myValues, ","); + } + + public void enableCustomValues(boolean customValuesEnabled) { + myCustomValuesEnabled = customValuesEnabled; + } + + public void updateAdditionalEntries(@NotNull final String values) { + myValues = reparseProperties(values); + } + + protected abstract String getCheckboxTitle(); + + @NotNull + protected abstract String getPanelTitle(); + + @NotNull + protected abstract Logger getLogger(); +}