diff --git a/plugins/devkit/devkit.iml b/plugins/devkit/devkit.iml index 77509ce030da..a4dfa50b500e 100644 --- a/plugins/devkit/devkit.iml +++ b/plugins/devkit/devkit.iml @@ -27,6 +27,7 @@ + diff --git a/plugins/devkit/src/inspections/quickfix/ExtensionPointCandidate.java b/plugins/devkit/src/inspections/quickfix/ExtensionPointCandidate.java new file mode 100644 index 000000000000..b9acfee9b324 --- /dev/null +++ b/plugins/devkit/src/inspections/quickfix/ExtensionPointCandidate.java @@ -0,0 +1,37 @@ +/* + * 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 org.jetbrains.idea.devkit.inspections.quickfix; + +/** +* @author yole +*/ +class ExtensionPointCandidate { + public final String epName; + public final String attributeName; + public final String beanClassName; + + ExtensionPointCandidate(String epName, String attributeName, String beanClassName) { + this.epName = epName; + this.attributeName = attributeName; + this.beanClassName = beanClassName; + } + + ExtensionPointCandidate(String epName) { + this.epName = epName; + this.attributeName = "implementation"; + this.beanClassName = null; + } +} diff --git a/plugins/devkit/src/inspections/quickfix/RegisterExtensionFix.java b/plugins/devkit/src/inspections/quickfix/RegisterExtensionFix.java index f233f6ea4821..c4fa1dfb014b 100644 --- a/plugins/devkit/src/inspections/quickfix/RegisterExtensionFix.java +++ b/plugins/devkit/src/inspections/quickfix/RegisterExtensionFix.java @@ -16,15 +16,20 @@ package org.jetbrains.idea.devkit.inspections.quickfix; import com.intellij.codeInsight.intention.IntentionAction; +import com.intellij.lang.LanguageExtensionPoint; import com.intellij.openapi.application.Result; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.extensions.KeyedFactoryEPBean; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; +import com.intellij.psi.xml.XmlAttribute; import com.intellij.psi.xml.XmlTag; import com.intellij.util.Consumer; import com.intellij.util.IncorrectOperationException; +import com.intellij.util.KeyedLazyInstanceEP; import com.intellij.util.PsiNavigateUtil; import com.intellij.util.xml.DomFileElement; import org.jetbrains.annotations.NotNull; @@ -32,16 +37,18 @@ import org.jetbrains.idea.devkit.dom.Extension; import org.jetbrains.idea.devkit.dom.Extensions; import org.jetbrains.idea.devkit.dom.IdeaPlugin; +import java.util.List; + /** * @author yole */ public class RegisterExtensionFix implements IntentionAction { private final PsiClass myExtensionClass; - private final String myEPName; + private final List myEPCandidates; - public RegisterExtensionFix(PsiClass extensionClass, String epName) { + public RegisterExtensionFix(PsiClass extensionClass, List epCandidates) { myExtensionClass = extensionClass; - myEPName = epName; + myEPCandidates = epCandidates; } @NotNull @@ -72,19 +79,36 @@ public class RegisterExtensionFix implements IntentionAction { } private void doFix(final DomFileElement element) { - Extension extension = new WriteCommandAction(element.getFile().getProject(), element.getFile()) { + if (myEPCandidates.size() == 1) { + registerExtension(element, myEPCandidates.get(0)); + } + } + + private void registerExtension(final DomFileElement element, final ExtensionPointCandidate candidate) { + PsiElement navTarget = new WriteCommandAction(element.getFile().getProject(), element.getFile()) { @Override - protected void run(Result result) throws Throwable { - Extensions extensions = RegisterInspectionFix.getExtension(element.getRootElement(), myEPName); - Extension extension = extensions.addExtension(myEPName); + protected void run(Result result) throws Throwable { + Extensions extensions = RegisterInspectionFix.getExtension(element.getRootElement(), candidate.epName); + Extension extension = extensions.addExtension(candidate.epName); XmlTag tag = extension.getXmlTag(); - tag.setAttribute("implementation", myExtensionClass.getQualifiedName()); - result.setResult(extension); + PsiElement navTarget = null; + if (KeyedFactoryEPBean.class.getName().equals(candidate.beanClassName) || + KeyedLazyInstanceEP.class.getName().equals(candidate.beanClassName)) { + XmlAttribute attr = tag.setAttribute("key", ""); + navTarget = attr.getValueElement(); + } + else if (LanguageExtensionPoint.class.getName().equals(candidate.beanClassName)) { + XmlAttribute attr = tag.setAttribute("language", ""); + navTarget = attr.getValueElement(); + } + tag.setAttribute(candidate.attributeName, myExtensionClass.getQualifiedName()); + result.setResult(navTarget != null ? navTarget : extension.getXmlTag()); } }.execute().throwException().getResultObject(); - PsiNavigateUtil.navigate(extension.getXmlTag()); + PsiNavigateUtil.navigate(navTarget); } + @Override public boolean startInWriteAction() { return false; diff --git a/plugins/devkit/src/inspections/quickfix/RegisterExtensionFixProvider.java b/plugins/devkit/src/inspections/quickfix/RegisterExtensionFixProvider.java index 21d0e05c4e9c..dab63ac2f7e5 100644 --- a/plugins/devkit/src/inspections/quickfix/RegisterExtensionFixProvider.java +++ b/plugins/devkit/src/inspections/quickfix/RegisterExtensionFixProvider.java @@ -22,16 +22,19 @@ import com.intellij.codeInspection.LocalInspectionEP; import com.intellij.codeInspection.LocalInspectionTool; import com.intellij.codeInspection.reference.UnusedDeclarationFixProvider; import com.intellij.ide.highlighter.XmlFileType; -import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.psi.*; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.ProjectScope; -import com.intellij.psi.search.searches.ReferencesSearch; +import com.intellij.psi.search.PsiNonJavaFileReferenceProcessor; +import com.intellij.psi.search.PsiSearchHelper; import com.intellij.psi.util.InheritanceUtil; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.xml.XmlTag; import org.jetbrains.annotations.NotNull; +import java.util.ArrayList; +import java.util.List; + /** * @author Dmitry Avdeev * Date: 1/19/12 @@ -51,50 +54,68 @@ public class RegisterExtensionFixProvider implements UnusedDeclarationFixProvide if (InheritanceUtil.isInheritor(parentClass, GlobalInspectionTool.class.getName())) { return new IntentionAction[] { new RegisterInspectionFix(parentClass, InspectionEP.GLOBAL_INSPECTION) }; } - PsiField epField = findEPNameField(parentClass); - if (epField != null) { - String epName = findEPNameForClass(epField.getContainingClass()); - if (epName != null) { - return new IntentionAction[] { new RegisterExtensionFix(parentClass, epName) }; - } + List candidateList = new ArrayList(); + findExtensionPointCandidatesInHierarchy(parentClass, candidateList); + if (!candidateList.isEmpty()) { + return new IntentionAction[] { new RegisterExtensionFix(parentClass, candidateList) }; } return IntentionAction.EMPTY_ARRAY; } - private static String findEPNameForClass(PsiClass aClass) { - GlobalSearchScope scope = GlobalSearchScope.getScopeRestrictedByFileTypes(ProjectScope.getAllScope(aClass.getProject()), XmlFileType.INSTANCE); - for (PsiReference reference : ReferencesSearch.search(aClass, scope)) { - XmlTag tag = PsiTreeUtil.getParentOfType(reference.getElement(), XmlTag.class); - if (tag != null && "extensionPoint".equals(tag.getName())) { - String qName = tag.getAttributeValue("qualifiedName"); - if (qName != null) { - return qName; - } - String name = tag.getAttributeValue("name"); - if (name != null) { - return "com.intellij." + name; - } + private static void findExtensionPointCandidatesInHierarchy(PsiClass aClass, List list) { + for (PsiClass superClass : aClass.getSupers()) { + if (CommonClassNames.JAVA_LANG_OBJECT.equals(superClass.getQualifiedName())) { + continue; } + findExtensionPointCandidates(superClass, list); + findExtensionPointCandidatesInHierarchy(superClass, list); } - return null; } - private static PsiField findEPNameField(PsiClass aClass) { - for (PsiField field : aClass.getFields()) { - if (field.getType() instanceof PsiClassType) { - PsiClassType classType = (PsiClassType)field.getType(); - PsiClassType.ClassResolveResult resolved = classType.resolveGenerics(); - PsiClass fieldClass = resolved.getElement(); - if (fieldClass != null && ExtensionPointName.class.getName().equals(fieldClass.getQualifiedName())) { - return field; - } + private static void findExtensionPointCandidates(PsiClass aClass, final List list) { + String name = aClass.getQualifiedName(); + if (name == null) { + return; + } + GlobalSearchScope scope = GlobalSearchScope.getScopeRestrictedByFileTypes(ProjectScope.getAllScope(aClass.getProject()), XmlFileType.INSTANCE); + PsiSearchHelper.SERVICE.getInstance(aClass.getProject()).processUsagesInNonJavaFiles(name, new PsiNonJavaFileReferenceProcessor() { + @Override + public boolean process(PsiFile file, int startOffset, int endOffset) { + PsiElement element = file.findElementAt(startOffset); + processExtensionPointCandidate(element, list); + return true; + } + }, scope); + } + + private static void processExtensionPointCandidate(PsiElement element, List list) { + XmlTag tag = PsiTreeUtil.getParentOfType(element, XmlTag.class); + if (tag == null) return; + if ("extensionPoint".equals(tag.getName())) { + String epName = getEPName(tag); + if (epName != null) { + list.add(new ExtensionPointCandidate(epName)); } } - for (PsiClass superClass: aClass.getSupers()) { - PsiField epField = findEPNameField(superClass); - if (epField != null) { - return epField; - } + else if ("with".equals(tag.getName())) { + XmlTag extensionPointTag = tag.getParentTag(); + if (!"extensionPoint".equals(extensionPointTag.getName())) return; + String attrName = tag.getAttributeValue("attribute"); + String epName = getEPName(extensionPointTag); + String beanClassName = extensionPointTag.getAttributeValue("beanClass"); + if (attrName == null || epName == null) return; + list.add(new ExtensionPointCandidate(epName, attrName, beanClassName)); + } + } + + private static String getEPName(XmlTag tag) { + String qName = tag.getAttributeValue("qualifiedName"); + if (qName != null) { + return qName; + } + String name = tag.getAttributeValue("name"); + if (name != null) { + return "com.intellij." + name; } return null; } diff --git a/plugins/devkit/src/inspections/quickfix/RegisterInspectionFix.java b/plugins/devkit/src/inspections/quickfix/RegisterInspectionFix.java index 6eace3d6321f..cf629df6796f 100644 --- a/plugins/devkit/src/inspections/quickfix/RegisterInspectionFix.java +++ b/plugins/devkit/src/inspections/quickfix/RegisterInspectionFix.java @@ -15,6 +15,7 @@ */ package org.jetbrains.idea.devkit.inspections.quickfix; +import com.google.common.collect.ImmutableMap; import com.intellij.codeInsight.hint.HintManager; import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.codeInspection.InspectionEP; @@ -43,6 +44,7 @@ import com.intellij.util.PsiNavigateUtil; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.xml.DomFileElement; import com.intellij.util.xml.DomService; +import com.intellij.xml.util.IncludedXmlTag; import org.jetbrains.annotations.NotNull; import org.jetbrains.idea.devkit.DevKitBundle; import org.jetbrains.idea.devkit.dom.Extension; @@ -50,6 +52,7 @@ import org.jetbrains.idea.devkit.dom.Extensions; import org.jetbrains.idea.devkit.dom.IdeaPlugin; import javax.swing.*; +import java.util.Collections; import java.util.List; /** @@ -108,6 +111,8 @@ class RegisterInspectionFix implements IntentionAction { } }); + elements = findAppropriateIntelliJModule(module.getName(), elements); + if (elements.isEmpty()) { HintManager.getInstance().showErrorHint(editor, "Cannot find plugin descriptor"); return; @@ -146,6 +151,29 @@ class RegisterInspectionFix implements IntentionAction { .showInBestPositionFor(editor); } + private static final ImmutableMap INTELLIJ_MODULES = ImmutableMap.builder() + .put("platform-api", "PlatformExtensions.xml") + .put("platform-impl", "PlatformExtensions.xml") + .put("lang-api", "LangExtensions.xml") + .put("lang-impl", "LangExtensions.xml") + .put("vcs-api", "VcsExtensions.xml") + .put("vcs-impl", "VcsExtensions.xml") + .put("openapi", "IdeaPlugin.xml") + .put("java-impl", "IdeaPlugin.xml") + .build(); + + private static List> findAppropriateIntelliJModule(String name, List> elements) { + String extensionsFile = INTELLIJ_MODULES.get(name); + if (extensionsFile != null) { + for (DomFileElement element : elements) { + if (element.getFile().getName().equals(extensionsFile)) { + return Collections.singletonList(element); + } + } + } + return elements; + } + private void doFix(DomFileElement selectedValue, final Project project, final PsiFile file) { final IdeaPlugin plugin = selectedValue.getRootElement(); Extension extension = new WriteCommandAction(project, file) { @@ -166,6 +194,9 @@ class RegisterInspectionFix implements IntentionAction { final List extensionsList = plugin.getExtensions(); Extensions extensions = null; for (Extensions e : extensionsList) { + if (e.getXmlTag() instanceof IncludedXmlTag) { + continue; + } String s = e.getDefaultExtensionNs().getStringValue(); if (s != null && epName.startsWith(s)) { extensions = e;