mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
DevKit: extract PluginDescriptorChooser, improve plugin.xml candidates list presentation, fix dumb mode error
This commit is contained in:
@@ -0,0 +1,237 @@
|
||||
/*
|
||||
* Copyright 2000-2014 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;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.intellij.codeInsight.hint.HintManager;
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.module.ModuleUtilCore;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.ProjectRootManager;
|
||||
import com.intellij.openapi.roots.ui.configuration.ModulesAlphaComparator;
|
||||
import com.intellij.openapi.ui.popup.JBPopupFactory;
|
||||
import com.intellij.openapi.ui.popup.ListSeparator;
|
||||
import com.intellij.openapi.ui.popup.PopupStep;
|
||||
import com.intellij.openapi.ui.popup.util.BaseListPopupStep;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.util.Consumer;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.ui.EmptyIcon;
|
||||
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.annotations.Nullable;
|
||||
import org.jetbrains.idea.devkit.dom.Extensions;
|
||||
import org.jetbrains.idea.devkit.dom.IdeaPlugin;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
public class PluginDescriptorChooser {
|
||||
|
||||
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();
|
||||
|
||||
public static void show(final Project project,
|
||||
final Editor editor,
|
||||
final PsiFile file,
|
||||
final Consumer<DomFileElement<IdeaPlugin>> consumer) {
|
||||
final Module module = ModuleUtilCore.findModuleForPsiElement(file);
|
||||
assert module != null;
|
||||
List<DomFileElement<IdeaPlugin>> elements =
|
||||
DomService.getInstance().getFileElements(IdeaPlugin.class,
|
||||
project,
|
||||
module.getModuleContentWithDependenciesScope());
|
||||
|
||||
elements = ContainerUtil.filter(elements, new Condition<DomFileElement<IdeaPlugin>>() {
|
||||
@Override
|
||||
public boolean value(DomFileElement<IdeaPlugin> element) {
|
||||
VirtualFile virtualFile = element.getFile().getVirtualFile();
|
||||
return virtualFile != null && ProjectRootManager.getInstance(project).getFileIndex().isInContent(virtualFile);
|
||||
}
|
||||
});
|
||||
|
||||
elements = findAppropriateIntelliJModule(module.getName(), elements);
|
||||
|
||||
if (elements.isEmpty()) {
|
||||
HintManager.getInstance().showErrorHint(editor, "Cannot find plugin descriptor");
|
||||
return;
|
||||
}
|
||||
|
||||
if (elements.size() == 1) {
|
||||
consumer.consume(elements.get(0));
|
||||
return;
|
||||
}
|
||||
|
||||
final BaseListPopupStep<PluginDescriptorCandidate> popupStep =
|
||||
new BaseListPopupStep<PluginDescriptorCandidate>("Choose Plugin Descriptor",
|
||||
createCandidates(module, elements)) {
|
||||
|
||||
@Override
|
||||
public boolean isSpeedSearchEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Icon getIconFor(PluginDescriptorCandidate candidate) {
|
||||
return candidate.getIcon();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getTextFor(PluginDescriptorCandidate candidate) {
|
||||
return candidate.getText();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public ListSeparator getSeparatorAbove(PluginDescriptorCandidate candidate) {
|
||||
final String separatorText = candidate.getSeparatorText();
|
||||
if (separatorText != null) {
|
||||
return new ListSeparator(separatorText);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PopupStep onChosen(PluginDescriptorCandidate selectedValue, boolean finalChoice) {
|
||||
consumer.consume(selectedValue.myDomFileElement);
|
||||
return FINAL_CHOICE;
|
||||
}
|
||||
};
|
||||
JBPopupFactory.getInstance().createListPopup(popupStep).showInBestPositionFor(editor);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Extensions findOrCreateExtensionsForEP(DomFileElement<IdeaPlugin> domFileElement, String epName) {
|
||||
final IdeaPlugin ideaPlugin = domFileElement.getRootElement();
|
||||
final List<Extensions> extensionsList = ideaPlugin.getExtensions();
|
||||
for (Extensions extensions : extensionsList) {
|
||||
if (extensions.getXmlTag() instanceof IncludedXmlTag) {
|
||||
continue;
|
||||
}
|
||||
String s = extensions.getDefaultExtensionNs().getStringValue();
|
||||
if (s != null && epName.startsWith(s)) {
|
||||
return extensions;
|
||||
}
|
||||
}
|
||||
|
||||
Extensions extensions = ideaPlugin.addExtensions();
|
||||
final String epPrefix = StringUtil.getPackageName(epName);
|
||||
extensions.getDefaultExtensionNs().setStringValue(epPrefix);
|
||||
return extensions;
|
||||
}
|
||||
|
||||
private static List<PluginDescriptorCandidate> createCandidates(final Module currentModule,
|
||||
List<DomFileElement<IdeaPlugin>> elements) {
|
||||
Collections.sort(elements, new Comparator<DomFileElement<IdeaPlugin>>() {
|
||||
@Override
|
||||
public int compare(DomFileElement<IdeaPlugin> o1, DomFileElement<IdeaPlugin> o2) {
|
||||
// current module = first group
|
||||
final Module module1 = o1.getModule();
|
||||
if (currentModule.equals(module1)) return -1;
|
||||
final Module module2 = o2.getModule();
|
||||
if (currentModule.equals(module2)) return 1;
|
||||
|
||||
return ModulesAlphaComparator.INSTANCE.compare(module1, module2);
|
||||
}
|
||||
});
|
||||
Collections.sort(elements, new Comparator<DomFileElement<IdeaPlugin>>() {
|
||||
@Override
|
||||
public int compare(DomFileElement<IdeaPlugin> o1, DomFileElement<IdeaPlugin> o2) {
|
||||
if (!Comparing.equal(o1.getModule(), o2.getModule())) return 0;
|
||||
return o1.getFile().getName().compareTo(o2.getFile().getName());
|
||||
}
|
||||
});
|
||||
|
||||
return ContainerUtil.map(elements, new Function<DomFileElement<IdeaPlugin>, PluginDescriptorCandidate>() {
|
||||
|
||||
private Module myLastModule = currentModule;
|
||||
|
||||
@Override
|
||||
public PluginDescriptorCandidate fun(DomFileElement<IdeaPlugin> element) {
|
||||
final Module module = element.getModule();
|
||||
boolean startsNewGroup = !myLastModule.equals(module);
|
||||
myLastModule = module;
|
||||
return new PluginDescriptorCandidate(element, startsNewGroup);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static List<DomFileElement<IdeaPlugin>> findAppropriateIntelliJModule(String moduleName,
|
||||
List<DomFileElement<IdeaPlugin>> elements) {
|
||||
String extensionsFile = INTELLIJ_MODULES.get(moduleName);
|
||||
if (extensionsFile != null) {
|
||||
for (DomFileElement<IdeaPlugin> element : elements) {
|
||||
if (element.getFile().getName().equals(extensionsFile)) {
|
||||
return Collections.singletonList(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
return elements;
|
||||
}
|
||||
|
||||
|
||||
private static class PluginDescriptorCandidate {
|
||||
private final DomFileElement<IdeaPlugin> myDomFileElement;
|
||||
private final boolean myStartsNewGroup;
|
||||
|
||||
private PluginDescriptorCandidate(DomFileElement<IdeaPlugin> domFileElement,
|
||||
boolean startsNewGroup) {
|
||||
myDomFileElement = domFileElement;
|
||||
myStartsNewGroup = startsNewGroup;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
final String name = myDomFileElement.getFile().getName();
|
||||
final String pluginId = getPluginId();
|
||||
return pluginId != null ? name + " [" + pluginId + "]" : name;
|
||||
}
|
||||
|
||||
public Icon getIcon() {
|
||||
return getPluginId() != null ? AllIcons.Nodes.Plugin : EmptyIcon.create(AllIcons.Nodes.Plugin);
|
||||
}
|
||||
|
||||
public String getSeparatorText() {
|
||||
if (!myStartsNewGroup) return null;
|
||||
|
||||
final Module module = myDomFileElement.getModule();
|
||||
return module == null ? null : module.getName();
|
||||
}
|
||||
|
||||
private String getPluginId() {
|
||||
return myDomFileElement.getRootElement().getPluginId();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import com.intellij.openapi.command.WriteCommandAction;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.extensions.KeyedFactoryEPBean;
|
||||
import com.intellij.openapi.fileTypes.FileTypeExtensionPoint;
|
||||
import com.intellij.openapi.project.DumbService;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.popup.JBPopupFactory;
|
||||
import com.intellij.openapi.ui.popup.PopupStep;
|
||||
@@ -71,12 +72,12 @@ public class RegisterExtensionFix implements IntentionAction {
|
||||
|
||||
@Override
|
||||
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
|
||||
return true;
|
||||
return !DumbService.isDumb(project);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(@NotNull Project project, final Editor editor, PsiFile file) throws IncorrectOperationException {
|
||||
RegisterInspectionFix.choosePluginDescriptor(project, editor, file, new Consumer<DomFileElement<IdeaPlugin>>() {
|
||||
PluginDescriptorChooser.show(project, editor, file, new Consumer<DomFileElement<IdeaPlugin>>() {
|
||||
@Override
|
||||
public void consume(DomFileElement<IdeaPlugin> element) {
|
||||
doFix(editor, element);
|
||||
@@ -101,11 +102,11 @@ public class RegisterExtensionFix implements IntentionAction {
|
||||
}
|
||||
}
|
||||
|
||||
private void registerExtension(final DomFileElement<IdeaPlugin> element, final ExtensionPointCandidate candidate) {
|
||||
PsiElement navTarget = new WriteCommandAction<PsiElement>(element.getFile().getProject(), element.getFile()) {
|
||||
private void registerExtension(final DomFileElement<IdeaPlugin> selectedValue, final ExtensionPointCandidate candidate) {
|
||||
PsiElement navTarget = new WriteCommandAction<PsiElement>(selectedValue.getFile().getProject(), selectedValue.getFile()) {
|
||||
@Override
|
||||
protected void run(Result<PsiElement> result) throws Throwable {
|
||||
Extensions extensions = RegisterInspectionFix.getExtension(element.getRootElement(), candidate.epName);
|
||||
protected void run(@NotNull Result<PsiElement> result) throws Throwable {
|
||||
Extensions extensions = PluginDescriptorChooser.findOrCreateExtensionsForEP(selectedValue, candidate.epName);
|
||||
Extension extension = extensions.addExtension(candidate.epName);
|
||||
XmlTag tag = extension.getXmlTag();
|
||||
PsiElement navTarget = null;
|
||||
|
||||
@@ -15,45 +15,27 @@
|
||||
*/
|
||||
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;
|
||||
import com.intellij.ide.TypePresentationService;
|
||||
import com.intellij.openapi.application.Result;
|
||||
import com.intellij.openapi.command.WriteCommandAction;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.module.ModuleUtilCore;
|
||||
import com.intellij.openapi.project.DumbService;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.ProjectRootManager;
|
||||
import com.intellij.openapi.ui.popup.JBPopupFactory;
|
||||
import com.intellij.openapi.ui.popup.PopupStep;
|
||||
import com.intellij.openapi.ui.popup.util.BaseListPopupStep;
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiClass;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.xml.XmlTag;
|
||||
import com.intellij.util.Consumer;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
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;
|
||||
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;
|
||||
|
||||
/**
|
||||
* @author Dmitry Avdeev
|
||||
*/
|
||||
@@ -81,12 +63,12 @@ class RegisterInspectionFix implements IntentionAction {
|
||||
|
||||
@Override
|
||||
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
|
||||
return true;
|
||||
return !DumbService.isDumb(project);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(@NotNull final Project project, final Editor editor, final PsiFile file) throws IncorrectOperationException {
|
||||
choosePluginDescriptor(project, editor, file, new Consumer<DomFileElement<IdeaPlugin>>() {
|
||||
PluginDescriptorChooser.show(project, editor, file, new Consumer<DomFileElement<IdeaPlugin>>() {
|
||||
@Override
|
||||
public void consume(DomFileElement<IdeaPlugin> element) {
|
||||
doFix(element, project, file);
|
||||
@@ -94,95 +76,12 @@ class RegisterInspectionFix implements IntentionAction {
|
||||
});
|
||||
}
|
||||
|
||||
public static void choosePluginDescriptor(final Project project, Editor editor, final PsiFile file,
|
||||
final Consumer<DomFileElement<IdeaPlugin>> consumer) {
|
||||
Module module = ModuleUtilCore.findModuleForPsiElement(file);
|
||||
assert module != null;
|
||||
List<DomFileElement<IdeaPlugin>> elements =
|
||||
DomService.getInstance().getFileElements(IdeaPlugin.class, project, module.getModuleContentWithDependenciesScope());
|
||||
|
||||
elements = ContainerUtil.filter(elements, new Condition<DomFileElement<IdeaPlugin>>() {
|
||||
@Override
|
||||
public boolean value(DomFileElement<IdeaPlugin> element) {
|
||||
VirtualFile virtualFile = element.getFile().getVirtualFile();
|
||||
return virtualFile != null && ProjectRootManager.getInstance(project).getFileIndex().isInContent(virtualFile);
|
||||
}
|
||||
});
|
||||
|
||||
elements = findAppropriateIntelliJModule(module.getName(), elements);
|
||||
|
||||
if (elements.isEmpty()) {
|
||||
HintManager.getInstance().showErrorHint(editor, "Cannot find plugin descriptor");
|
||||
return;
|
||||
}
|
||||
|
||||
if (elements.size() == 1) {
|
||||
consumer.consume(elements.get(0));
|
||||
return;
|
||||
}
|
||||
|
||||
final BaseListPopupStep<DomFileElement<IdeaPlugin>> popupStep =
|
||||
new BaseListPopupStep<DomFileElement<IdeaPlugin>>("Choose Plugin Descriptor", elements) {
|
||||
|
||||
@Override
|
||||
public boolean isSpeedSearchEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Icon getIconFor(DomFileElement<IdeaPlugin> aValue) {
|
||||
return TypePresentationService.getService().getIcon(aValue);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getTextFor(DomFileElement<IdeaPlugin> value) {
|
||||
final String name = value.getFile().getName();
|
||||
final Module module = value.getModule();
|
||||
return module != null ? name + " [" + module.getName() + "]" : name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PopupStep onChosen(DomFileElement<IdeaPlugin> selectedValue, boolean finalChoice) {
|
||||
consumer.consume(selectedValue);
|
||||
return FINAL_CHOICE;
|
||||
}
|
||||
};
|
||||
JBPopupFactory.getInstance().createListPopup(popupStep)
|
||||
.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 moduleName,
|
||||
List<DomFileElement<IdeaPlugin>> elements) {
|
||||
String extensionsFile = INTELLIJ_MODULES.get(moduleName);
|
||||
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();
|
||||
private void doFix(final DomFileElement<IdeaPlugin> selectedValue, final Project project, final PsiFile file) {
|
||||
Extension extension = new WriteCommandAction<Extension>(project, file) {
|
||||
|
||||
@Override
|
||||
protected void run(@NotNull Result<Extension> result) throws Throwable {
|
||||
final Extensions extensions = getExtension(plugin, myEp.getName());
|
||||
final Extensions extensions = PluginDescriptorChooser.findOrCreateExtensionsForEP(selectedValue, myEp.getName());
|
||||
Extension extension = extensions.addExtension(myEp.getName());
|
||||
XmlTag tag = extension.getXmlTag();
|
||||
tag.setAttribute("implementationClass", myPsiClass.getQualifiedName());
|
||||
@@ -192,24 +91,6 @@ class RegisterInspectionFix implements IntentionAction {
|
||||
PsiNavigateUtil.navigate(extension.getXmlTag());
|
||||
}
|
||||
|
||||
public static Extensions getExtension(IdeaPlugin plugin, String epName) {
|
||||
final List<Extensions> extensionsList = plugin.getExtensions();
|
||||
for (Extensions extensions : extensionsList) {
|
||||
if (extensions.getXmlTag() instanceof IncludedXmlTag) {
|
||||
continue;
|
||||
}
|
||||
String s = extensions.getDefaultExtensionNs().getStringValue();
|
||||
if (s != null && epName.startsWith(s)) {
|
||||
return extensions;
|
||||
}
|
||||
}
|
||||
|
||||
Extensions extensions = plugin.addExtensions();
|
||||
final String epPrefix = StringUtil.getPackageName(epName);
|
||||
extensions.getDefaultExtensionNs().setStringValue(epPrefix);
|
||||
return extensions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean startInWriteAction() {
|
||||
return false;
|
||||
|
||||
Reference in New Issue
Block a user