mostly completed "register extension" fix (IDEA-60730)

This commit is contained in:
Dmitry Jemerov
2013-02-12 22:22:31 +01:00
parent 33cf80b97b
commit 6f73b44d2b
5 changed files with 160 additions and 46 deletions
+1
View File
@@ -27,6 +27,7 @@
<orderEntry type="module" module-name="xml" />
<orderEntry type="library" scope="TEST" name="Groovy" level="project" />
<orderEntry type="module" module-name="jps-builders" />
<orderEntry type="library" name="Guava" level="project" />
</component>
</module>
@@ -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;
}
}
@@ -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<ExtensionPointCandidate> myEPCandidates;
public RegisterExtensionFix(PsiClass extensionClass, String epName) {
public RegisterExtensionFix(PsiClass extensionClass, List<ExtensionPointCandidate> epCandidates) {
myExtensionClass = extensionClass;
myEPName = epName;
myEPCandidates = epCandidates;
}
@NotNull
@@ -72,19 +79,36 @@ public class RegisterExtensionFix implements IntentionAction {
}
private void doFix(final DomFileElement<IdeaPlugin> element) {
Extension extension = new WriteCommandAction<Extension>(element.getFile().getProject(), element.getFile()) {
if (myEPCandidates.size() == 1) {
registerExtension(element, myEPCandidates.get(0));
}
}
private void registerExtension(final DomFileElement<IdeaPlugin> element, final ExtensionPointCandidate candidate) {
PsiElement navTarget = new WriteCommandAction<PsiElement>(element.getFile().getProject(), element.getFile()) {
@Override
protected void run(Result<Extension> result) throws Throwable {
Extensions extensions = RegisterInspectionFix.getExtension(element.getRootElement(), myEPName);
Extension extension = extensions.addExtension(myEPName);
protected void run(Result<PsiElement> 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;
@@ -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<ExtensionPointCandidate> candidateList = new ArrayList<ExtensionPointCandidate>();
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<ExtensionPointCandidate> 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<ExtensionPointCandidate> 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<ExtensionPointCandidate> 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;
}
@@ -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<String, String> INTELLIJ_MODULES = ImmutableMap.<String, String>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<DomFileElement<IdeaPlugin>> findAppropriateIntelliJModule(String name, List<DomFileElement<IdeaPlugin>> elements) {
String extensionsFile = INTELLIJ_MODULES.get(name);
if (extensionsFile != null) {
for (DomFileElement<IdeaPlugin> element : elements) {
if (element.getFile().getName().equals(extensionsFile)) {
return Collections.singletonList(element);
}
}
}
return elements;
}
private void doFix(DomFileElement<IdeaPlugin> selectedValue, final Project project, final PsiFile file) {
final IdeaPlugin plugin = selectedValue.getRootElement();
Extension extension = new WriteCommandAction<Extension>(project, file) {
@@ -166,6 +194,9 @@ class RegisterInspectionFix implements IntentionAction {
final List<Extensions> 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;