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 {