blob: 7b4068b1145aa521a8d4e91b9320ed080856dec8 [file] [log] [blame]
/*
* 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.getModuleWithDependenciesScope());
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();
final Module module2 = o2.getModule();
final int byAlpha = ModulesAlphaComparator.INSTANCE.compare(module1, module2);
if (byAlpha == 0) return 0;
if (currentModule.equals(module1)) return -1;
if (currentModule.equals(module2)) return 1;
return byAlpha;
}
});
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();
}
}
}