From 830d2037f6b2d4bfd64f5cb2fba06ae95f99b55a Mon Sep 17 00:00:00 2001 From: peter Date: Tue, 4 Aug 2015 11:27:46 +0200 Subject: [PATCH] suggest to create missing dom attributes (IDEA-63544) --- .../codeInsight/createRequiredAttribute.xml | 9 +++ .../createRequiredAttribute_after.xml | 9 +++ .../PluginXmlFunctionalTest.groovy | 6 ++ .../com/intellij/spellchecker/jetbrains.dic | 1 + .../highlighting/DefineAttributeQuickFix.java | 63 +++++++++++++++++++ .../DomHighlightingHelperImpl.java | 58 ++++++----------- 6 files changed, 107 insertions(+), 39 deletions(-) create mode 100644 plugins/devkit/testData/codeInsight/createRequiredAttribute.xml create mode 100644 plugins/devkit/testData/codeInsight/createRequiredAttribute_after.xml create mode 100644 xml/dom-impl/src/com/intellij/util/xml/highlighting/DefineAttributeQuickFix.java diff --git a/plugins/devkit/testData/codeInsight/createRequiredAttribute.xml b/plugins/devkit/testData/codeInsight/createRequiredAttribute.xml new file mode 100644 index 000000000000..6b1d975f1084 --- /dev/null +++ b/plugins/devkit/testData/codeInsight/createRequiredAttribute.xml @@ -0,0 +1,9 @@ + + com.intellij + IDEA CORE + + + n id="a"/> + + + \ No newline at end of file diff --git a/plugins/devkit/testData/codeInsight/createRequiredAttribute_after.xml b/plugins/devkit/testData/codeInsight/createRequiredAttribute_after.xml new file mode 100644 index 000000000000..64dc1fe15b18 --- /dev/null +++ b/plugins/devkit/testData/codeInsight/createRequiredAttribute_after.xml @@ -0,0 +1,9 @@ + + com.intellij + IDEA CORE + + + + + + \ No newline at end of file diff --git a/plugins/devkit/testSources/codeInsight/PluginXmlFunctionalTest.groovy b/plugins/devkit/testSources/codeInsight/PluginXmlFunctionalTest.groovy index d423ea731512..b816180c5c34 100644 --- a/plugins/devkit/testSources/codeInsight/PluginXmlFunctionalTest.groovy +++ b/plugins/devkit/testSources/codeInsight/PluginXmlFunctionalTest.groovy @@ -275,4 +275,10 @@ public class PluginXmlFunctionalTest extends JavaCodeInsightFixtureTestCase { configureByFile(); myFixture.testHighlighting(true, true, true); } + + public void testCreateRequiredAttribute() { + myFixture.configureByFile(getTestName(true) + ".xml") + myFixture.launchAction(myFixture.findSingleIntention("Define class attribute")) + myFixture.checkResultByFile(getTestName(true) + "_after.xml") + } } diff --git a/spellchecker/src/com/intellij/spellchecker/jetbrains.dic b/spellchecker/src/com/intellij/spellchecker/jetbrains.dic index 50f3b36aaa71..8d6d27a15588 100644 --- a/spellchecker/src/com/intellij/spellchecker/jetbrains.dic +++ b/spellchecker/src/com/intellij/spellchecker/jetbrains.dic @@ -571,6 +571,7 @@ subpartition subpartitions subst substring +subtag subtree subtrees subview diff --git a/xml/dom-impl/src/com/intellij/util/xml/highlighting/DefineAttributeQuickFix.java b/xml/dom-impl/src/com/intellij/util/xml/highlighting/DefineAttributeQuickFix.java new file mode 100644 index 000000000000..96baf45c3737 --- /dev/null +++ b/xml/dom-impl/src/com/intellij/util/xml/highlighting/DefineAttributeQuickFix.java @@ -0,0 +1,63 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.util.xml.highlighting; + +import com.intellij.codeInsight.FileModificationService; +import com.intellij.codeInspection.LocalQuickFix; +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.fileEditor.OpenFileDescriptor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.xml.XmlAttribute; +import com.intellij.psi.xml.XmlTag; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +/** + * @author peter + */ +public class DefineAttributeQuickFix implements LocalQuickFix { + private static final Logger LOG = Logger.getInstance("#com.intellij.spring.model.highlighting.DefineAttributeQuickFix"); + private final String myAttrName; + + public DefineAttributeQuickFix(@NonNls final String attrName) { + myAttrName = attrName; + } + + @NotNull + public String getName() { + return "Define " + myAttrName + " attribute"; + } + + @NotNull + public String getFamilyName() { + return "Define attribute"; + } + + public void applyFix(@NotNull final Project project, @NotNull final ProblemDescriptor descriptor) { + try { + final XmlTag tag = (XmlTag)descriptor.getPsiElement(); + if (!FileModificationService.getInstance().preparePsiElementForWrite(descriptor.getPsiElement().getContainingFile())) return; + final XmlAttribute attribute = tag.setAttribute(myAttrName, "", ""); + new OpenFileDescriptor(project, tag.getContainingFile().getVirtualFile(), + attribute.getValueElement().getTextRange().getStartOffset() + 1).navigate(true); + } + catch (IncorrectOperationException e) { + LOG.error(e); + } + } +} diff --git a/xml/dom-impl/src/com/intellij/util/xml/highlighting/DomHighlightingHelperImpl.java b/xml/dom-impl/src/com/intellij/util/xml/highlighting/DomHighlightingHelperImpl.java index b9f49b22f81b..6a2b5ec4a3c8 100644 --- a/xml/dom-impl/src/com/intellij/util/xml/highlighting/DomHighlightingHelperImpl.java +++ b/xml/dom-impl/src/com/intellij/util/xml/highlighting/DomHighlightingHelperImpl.java @@ -17,15 +17,12 @@ package com.intellij.util.xml.highlighting; import com.intellij.codeInsight.FileModificationService; import com.intellij.codeInsight.daemon.impl.analysis.XmlHighlightVisitor; -import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.codeInspection.LocalQuickFix; import com.intellij.codeInspection.ProblemDescriptor; import com.intellij.ide.IdeBundle; import com.intellij.lang.annotation.HighlightSeverity; -import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.psi.PsiFile; import com.intellij.psi.PsiReference; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.xml.XmlAttributeValue; @@ -44,7 +41,6 @@ import com.intellij.xml.XmlBundle; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -74,14 +70,16 @@ public class DomHighlightingHelperImpl extends DomHighlightingHelper { if (required.value()) { final String xmlElementName = element.getXmlElementName(); if (element instanceof GenericAttributeValue) { - return Arrays.asList(holder.createProblem(element, IdeBundle.message("attribute.0.should.be.defined", xmlElementName))); + return Collections.singletonList(holder + .createProblem(element, IdeBundle.message("attribute.0.should.be.defined", xmlElementName), + new DefineAttributeQuickFix(xmlElementName))); } - return Arrays.asList( + return Collections.singletonList( holder.createProblem( element, HighlightSeverity.ERROR, IdeBundle.message("child.tag.0.should.be.defined", xmlElementName), - new AddRequiredSubtagFix(xmlElementName, element.getXmlElementNamespace(), element.getParent().getXmlTag()) + new AddRequiredSubtagFix(xmlElementName, element.getXmlElementNamespace()) ) ); } @@ -130,6 +128,7 @@ public class DomHighlightingHelperImpl extends DomHighlightingHelper { } } final boolean isResolvingConverter = converter instanceof ResolvingConverter; + //noinspection unchecked if (!hasBadResolve && (domReference != null || isResolvingConverter && hasBadResolve(domReference = new GenericDomValueReference(element)))) { @@ -137,7 +136,7 @@ public class DomHighlightingHelperImpl extends DomHighlightingHelper { final String errorMessage = converter .getErrorMessage(element.getStringValue(), ConvertContextFactory.createConvertContext( DomManagerImpl.getDomInvocationHandler(element))); - if (errorMessage != null && XmlHighlightVisitor.getErrorDescription(domReference) != null) { + if (errorMessage != null) { list.add(holder.createResolveProblem(element, domReference)); } } @@ -169,10 +168,11 @@ public class DomHighlightingHelperImpl extends DomHighlightingHelper { final String typeName = ElementPresentationManager.getTypeNameForObject(element); final GenericDomValue genericDomValue = domElement.getGenericInfo().getNameDomElement(element); if (genericDomValue != null) { - return Arrays.asList(holder.createProblem(genericDomValue, DomUtil.getFile(domElement).equals(DomUtil.getFile(element)) - ? IdeBundle.message("model.highlighting.identity", typeName) - : IdeBundle.message("model.highlighting.identity.in.other.file", typeName, - domElement.getXmlTag().getContainingFile().getName()))); + return Collections.singletonList(holder.createProblem(genericDomValue, DomUtil.getFile(domElement).equals(DomUtil.getFile(element)) + ? IdeBundle.message("model.highlighting.identity", typeName) + : IdeBundle.message("model.highlighting.identity.in.other.file", typeName, + domElement.getXmlTag().getContainingFile() + .getName()))); } } } @@ -236,15 +236,13 @@ public class DomHighlightingHelperImpl extends DomHighlightingHelper { } - private static class AddRequiredSubtagFix implements LocalQuickFix, IntentionAction { + private static class AddRequiredSubtagFix implements LocalQuickFix { private final String tagName; private final String tagNamespace; - private final XmlTag parentTag; - public AddRequiredSubtagFix(@NotNull String _tagName, @NotNull String _tagNamespace, @NotNull XmlTag _parentTag) { + public AddRequiredSubtagFix(@NotNull String _tagName, @NotNull String _tagNamespace) { tagName = _tagName; tagNamespace = _tagNamespace; - parentTag = _parentTag; } @Override @@ -253,39 +251,21 @@ public class DomHighlightingHelperImpl extends DomHighlightingHelper { return XmlBundle.message("insert.required.tag.fix", tagName); } - @Override - @NotNull - public String getText() { - return getName(); - } - @Override @NotNull public String getFamilyName() { return getName(); } - @Override - public boolean isAvailable(@NotNull final Project project, final Editor editor, final PsiFile file) { - return true; - } - - @Override - public void invoke(@NotNull final Project project, final Editor editor, final PsiFile file) throws IncorrectOperationException { - doFix(); - } - - @Override - public boolean startInWriteAction() { - return true; - } - @Override public void applyFix(@NotNull final Project project, @NotNull final ProblemDescriptor descriptor) { - doFix(); + XmlTag tag = PsiTreeUtil.getParentOfType(descriptor.getPsiElement(), XmlTag.class, false); + if (tag != null) { + doFix(tag); + } } - private void doFix() { + private void doFix(XmlTag parentTag) { if (!FileModificationService.getInstance().prepareFileForWrite(parentTag.getContainingFile())) return; try {