mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
UnknownSdkTracker - handle unset SDKs, fix more tests
GitOrigin-RevId: 95f42e222bf7411535046ca59027452d244d553c
This commit is contained in:
committed by
intellij-monorepo-bot
parent
fcbf5a8013
commit
8fcbb0f1f3
+29
-36
@@ -1,6 +1,7 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.java.codeInsight.daemon.impl;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.WriteAction;
|
||||
import com.intellij.openapi.projectRoots.JavaSdk;
|
||||
import com.intellij.openapi.projectRoots.ProjectJdkTable;
|
||||
@@ -14,6 +15,8 @@ import com.intellij.openapi.roots.ModuleRootModificationUtil;
|
||||
import com.intellij.openapi.roots.ProjectRootManager;
|
||||
import com.intellij.testFramework.IdeaTestUtil;
|
||||
import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase;
|
||||
import com.intellij.util.ThrowableRunnable;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -45,9 +48,7 @@ public class SdkSetupNotificationTest extends JavaCodeInsightFixtureTestCase {
|
||||
final List<SdkFixInfo> fixes = detectMissingSdks();
|
||||
assertThat(fixes)
|
||||
.withFailMessage(String.valueOf(fixes))
|
||||
.hasSize(1)
|
||||
.first()
|
||||
.returns("missingSDK", SdkFixInfo::getSdkName);
|
||||
.hasSize(1);
|
||||
}
|
||||
|
||||
public void testMissingModuleJdk() {
|
||||
@@ -60,9 +61,7 @@ public class SdkSetupNotificationTest extends JavaCodeInsightFixtureTestCase {
|
||||
final List<SdkFixInfo> fixes = detectMissingSdks();
|
||||
assertThat(fixes)
|
||||
.withFailMessage(String.valueOf(fixes))
|
||||
.hasSize(1)
|
||||
.first()
|
||||
.returns("missingSDK", SdkFixInfo::getSdkName);
|
||||
.hasSize(1);
|
||||
}
|
||||
|
||||
public void testNoProjectSdk() {
|
||||
@@ -73,20 +72,10 @@ public class SdkSetupNotificationTest extends JavaCodeInsightFixtureTestCase {
|
||||
assertThat(fixes)
|
||||
.withFailMessage(String.valueOf(fixes))
|
||||
.isNotEmpty();
|
||||
|
||||
/*
|
||||
final IntentionActionWithOptions action = fixes.getIntentionAction();
|
||||
assertThat(action).isNotNull();
|
||||
final String text = action.getText();
|
||||
assertThat(text).isNotNull();
|
||||
if (!text.startsWith("Setup SDK")) {
|
||||
final int length = Math.min(text.length(), "Setup SDK".length());
|
||||
assertThat(text.substring(0, length)).isEqualTo("Setup SDK");
|
||||
}*/
|
||||
}
|
||||
|
||||
public void testModuleSdk() {
|
||||
Sdk sdk = IdeaTestUtil.getMockJdk18();
|
||||
Sdk sdk = addSdkIfNeeded(IdeaTestUtil.getMockJdk18());
|
||||
ModuleRootModificationUtil.setModuleSdk(getModule(), sdk);
|
||||
|
||||
final List<SdkFixInfo> fixes = detectMissingSdks();
|
||||
@@ -102,15 +91,6 @@ public class SdkSetupNotificationTest extends JavaCodeInsightFixtureTestCase {
|
||||
assertThat(fixes)
|
||||
.withFailMessage(String.valueOf(fixes))
|
||||
.isNotEmpty();
|
||||
|
||||
/*final IntentionActionWithOptions action = panel.getIntentionAction();
|
||||
assertThat(action).isNotNull();
|
||||
final String text = action.getText();
|
||||
assertThat(text).isNotNull();
|
||||
if (!text.startsWith("Setup SDK")) {
|
||||
final int length = Math.min(text.length(), "Setup SDK".length());
|
||||
assertThat(text.substring(0, length)).isEqualTo("Setup SDK");
|
||||
}*/
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -129,16 +109,29 @@ public class SdkSetupNotificationTest extends JavaCodeInsightFixtureTestCase {
|
||||
|
||||
private void setProjectSdk(@Nullable Sdk sdk) {
|
||||
WriteAction.run(() -> {
|
||||
ProjectJdkTable jdkTable = ProjectJdkTable.getInstance();
|
||||
|
||||
if (sdk != null) {
|
||||
final Sdk foundJdk = jdkTable.findJdk(sdk.getName());
|
||||
if (foundJdk == null) {
|
||||
jdkTable.addJdk(sdk, myFixture.getProjectDisposable());
|
||||
}
|
||||
}
|
||||
|
||||
ProjectRootManager.getInstance(getProject()).setProjectSdk(sdk);
|
||||
ProjectRootManager.getInstance(getProject()).setProjectSdk(addSdkIfNeeded(sdk));
|
||||
});
|
||||
}
|
||||
|
||||
@Contract("null->null;!null->!null")
|
||||
private Sdk addSdkIfNeeded(Sdk sdk) {
|
||||
if (sdk == null) return null;
|
||||
|
||||
ProjectJdkTable jdkTable = ProjectJdkTable.getInstance();
|
||||
|
||||
final Sdk foundJdk = jdkTable.findJdk(sdk.getName());
|
||||
if (foundJdk != null) return sdk;
|
||||
|
||||
ThrowableRunnable<RuntimeException> addSdk = () -> {
|
||||
jdkTable.addJdk(sdk, myFixture.getProjectDisposable());
|
||||
};
|
||||
|
||||
if (ApplicationManager.getApplication().isWriteAccessAllowed()) {
|
||||
addSdk.run();
|
||||
} else {
|
||||
WriteAction.run(addSdk);
|
||||
}
|
||||
|
||||
return sdk;
|
||||
}
|
||||
}
|
||||
|
||||
+105
-20
@@ -1,16 +1,17 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.openapi.projectRoots.impl;
|
||||
|
||||
import com.intellij.openapi.application.WriteAction;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.module.ModuleManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.ModuleJdkOrderEntry;
|
||||
import com.intellij.openapi.roots.ModuleRootManager;
|
||||
import com.intellij.openapi.roots.OrderEntry;
|
||||
import com.intellij.openapi.roots.ProjectRootManager;
|
||||
import com.intellij.openapi.projectRoots.Sdk;
|
||||
import com.intellij.openapi.roots.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -39,16 +40,31 @@ public class SdkUsagesCollector {
|
||||
List<SdkUsage> contributeUsages(@NotNull Project project);
|
||||
}
|
||||
|
||||
public static class SdkUsage {
|
||||
public static final class SdkUsage {
|
||||
private final String myUsagePresentableText;
|
||||
private final String mySdkName;
|
||||
private final String mySdkTypeName;
|
||||
private Consumer<Sdk> mySdkSetAction = null;
|
||||
private Runnable myProjectSdkSetAction = null;
|
||||
|
||||
public SdkUsage(@NotNull String sdkName, @Nullable String sdkTypeName) {
|
||||
public SdkUsage(@NotNull String usagePresentableText,
|
||||
@Nullable String sdkName,
|
||||
@Nullable String sdkTypeName) {
|
||||
myUsagePresentableText = usagePresentableText;
|
||||
mySdkName = sdkName;
|
||||
mySdkTypeName = sdkTypeName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return UI friendly description of the SDK-using entity,
|
||||
* e.g. Project SDK or Module SDK
|
||||
*/
|
||||
@NotNull
|
||||
public String getUsagePresentableText() {
|
||||
return myUsagePresentableText;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getSdkName() {
|
||||
return mySdkName;
|
||||
}
|
||||
@@ -57,6 +73,45 @@ public class SdkUsagesCollector {
|
||||
public String getSdkTypeName() {
|
||||
return mySdkTypeName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return an action to change the currently selected SDK
|
||||
* to a given SDK. Return {@code null} to disallow that action
|
||||
*/
|
||||
@Nullable
|
||||
public Consumer<Sdk> getSdkSetAction() {
|
||||
return mySdkSetAction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the action for {@link #getSdkSetAction()}
|
||||
* @see #getSdkSetAction()
|
||||
*/
|
||||
@NotNull
|
||||
public SdkUsage withSetSdkAction(@NotNull Consumer<Sdk> setSdkAction) {
|
||||
mySdkSetAction = setSdkAction;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return an action to change the currently selected SDK
|
||||
* to a project SDK. The action should work no matter if project SDK is valid or not.
|
||||
* Return {@code null} to diasllow that action
|
||||
*/
|
||||
@Nullable
|
||||
public Runnable getProjectSdkSetAction() {
|
||||
return myProjectSdkSetAction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the action for {@link #getProjectSdkSetAction()}
|
||||
* @see #getProjectSdkSetAction()
|
||||
*/
|
||||
@NotNull
|
||||
public SdkUsage withSetProjectSdkAction(@NotNull Runnable setProjectSdkAction) {
|
||||
myProjectSdkSetAction = setProjectSdkAction;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,10 +132,10 @@ public class SdkUsagesCollector {
|
||||
String sdkName = manager.getProjectSdkName();
|
||||
String sdkType = manager.getProjectSdkTypeName();
|
||||
|
||||
if (sdkName != null) {
|
||||
return Collections.singletonList(new SdkUsage(sdkName, sdkType));
|
||||
}
|
||||
return Collections.emptyList();
|
||||
SdkUsage usage = new SdkUsage("Project SDK", sdkName, sdkType)
|
||||
.withSetSdkAction(sdk -> WriteAction.run(() -> ProjectRootManager.getInstance(project).setProjectSdk(sdk)));
|
||||
|
||||
return Collections.singletonList(usage);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,18 +145,48 @@ public class SdkUsagesCollector {
|
||||
public List<SdkUsage> contributeUsages(@NotNull Project project) {
|
||||
List<SdkUsage> usages = new ArrayList<>();
|
||||
for (Module module : ModuleManager.getInstance(project).getModules()) {
|
||||
ModuleRootManager manager = ModuleRootManager.getInstance(module);
|
||||
for (OrderEntry orderEntry : manager.getOrderEntries()) {
|
||||
if (orderEntry instanceof ModuleJdkOrderEntry) {
|
||||
String jdkName = ((ModuleJdkOrderEntry)orderEntry).getJdkName();
|
||||
String jdkType = ((ModuleJdkOrderEntry)orderEntry).getJdkTypeName();
|
||||
if (jdkName != null) {
|
||||
usages.add(new SdkUsage(jdkName, jdkType));
|
||||
}
|
||||
}
|
||||
}
|
||||
contributeForModule(module, usages);
|
||||
}
|
||||
return usages;
|
||||
}
|
||||
|
||||
private void contributeForModule(@NotNull Module module, @NotNull List<SdkUsage> usages) {
|
||||
ModuleRootManager manager = ModuleRootManager.getInstance(module);
|
||||
String jdkName = null;
|
||||
String jdkType = null;
|
||||
|
||||
for (OrderEntry orderEntry : manager.getOrderEntries()) {
|
||||
if (!(orderEntry instanceof JdkOrderEntry)) continue;
|
||||
|
||||
if (orderEntry instanceof InheritedJdkOrderEntry) return;
|
||||
if (orderEntry instanceof ModuleJdkOrderEntry) {
|
||||
jdkName = ((ModuleJdkOrderEntry)orderEntry).getJdkName();
|
||||
jdkType = ((ModuleJdkOrderEntry)orderEntry).getJdkTypeName();
|
||||
continue;
|
||||
}
|
||||
|
||||
Logger.getInstance(getClass()).error("Unexpected OrderEntry: " + orderEntry.getClass().getName() + ": " + orderEntry);
|
||||
}
|
||||
|
||||
SdkUsage usage = new SdkUsage("module \"" + module.getName() + "\"", jdkName, jdkType)
|
||||
.withSetProjectSdkAction(() -> {
|
||||
WriteAction.run(() -> {
|
||||
if (module.isDisposed()) return;
|
||||
ModifiableRootModel mod = ModuleRootManager.getInstance(module).getModifiableModel();
|
||||
mod.inheritSdk();
|
||||
mod.commit();
|
||||
});
|
||||
})
|
||||
.withSetSdkAction(sdk -> {
|
||||
WriteAction.run(() -> {
|
||||
if (module.isDisposed()) return;
|
||||
ModifiableRootModel mod = ModuleRootManager.getInstance(module).getModifiableModel();
|
||||
mod.setSdk(sdk);
|
||||
mod.commit();
|
||||
});
|
||||
});
|
||||
|
||||
usages.add(usage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+89
-73
@@ -7,11 +7,12 @@ import com.intellij.openapi.fileEditor.FileEditor;
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager;
|
||||
import com.intellij.openapi.fileEditor.FileEditorManagerListener;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.projectRoots.Sdk;
|
||||
import com.intellij.openapi.projectRoots.SdkType;
|
||||
import com.intellij.openapi.projectRoots.impl.SdkUsagesCollector.SdkUsage;
|
||||
import com.intellij.openapi.projectRoots.impl.UnknownSdkResolver.DownloadSdkFix;
|
||||
import com.intellij.openapi.projectRoots.impl.UnknownSdkResolver.UnknownSdk;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.ui.EditorNotificationPanel;
|
||||
import com.intellij.util.SmartList;
|
||||
@@ -22,6 +23,7 @@ import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class UnknownSdkEditorNotification implements Disposable {
|
||||
public static final Key<List<MissingSdkNotificationPanel>> NOTIFICATIONS = Key.create("notifications added to the editor");
|
||||
@@ -34,7 +36,7 @@ public class UnknownSdkEditorNotification implements Disposable {
|
||||
|
||||
private final Project myProject;
|
||||
private final FileEditorManager myFileEditorManager;
|
||||
private final Map<String, SdkFixInfo> myNotifications = new TreeMap<>();
|
||||
private final Set<SdkFixInfo> myNotifications = new LinkedHashSet<>();
|
||||
|
||||
UnknownSdkEditorNotification(@NotNull Project project) {
|
||||
myProject = project;
|
||||
@@ -51,23 +53,17 @@ public class UnknownSdkEditorNotification implements Disposable {
|
||||
});
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private MissingSdkNotificationPanel createPanelFor(@NotNull SdkFixInfo info) {
|
||||
Pair<UnknownSdk, DownloadSdkFix> fixInfo = info.getFixInfo();
|
||||
String sdkName = info.getSdkName();
|
||||
private void setupPanel(@NotNull MissingSdkNotificationPanel panel,
|
||||
@NotNull String sdkName,
|
||||
@Nullable UnknownSdk unknownSdk,
|
||||
@Nullable DownloadSdkFix fix) {
|
||||
|
||||
SdkType sdkType = fixInfo != null ? fixInfo.first.getSdkType() : null;
|
||||
SdkType sdkType = unknownSdk != null ? unknownSdk.getSdkType() : null;
|
||||
String sdkTypeName = sdkType != null ? sdkType.getPresentableName() : "SDK";
|
||||
|
||||
MissingSdkNotificationPanel panel = new MissingSdkNotificationPanel(info);
|
||||
panel.setProject(myProject);
|
||||
panel.setProviderKey(EDITOR_NOTIFICATIONS_KEY);
|
||||
panel.setText(sdkTypeName + " \"" + sdkName + "\" is missing");
|
||||
|
||||
if (fixInfo != null) {
|
||||
UnknownSdk unknownSdk = fixInfo.first;
|
||||
DownloadSdkFix fix = fixInfo.second;
|
||||
|
||||
if (fix != null && unknownSdk != null) {
|
||||
panel.createActionLabel("Download " + sdkTypeName + " (" + fix.getDownloadDescription() + ")", () -> {
|
||||
removeNotification(panel);
|
||||
UnknownSdkTracker.getInstance(myProject).applyDownloadableFix(unknownSdk, fix);
|
||||
@@ -85,8 +81,37 @@ public class UnknownSdkEditorNotification implements Disposable {
|
||||
.showSdkSelectionPopup(sdkName, sdkType, container, () -> removeNotification(panel));
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return panel;
|
||||
private void setupPanel(@NotNull MissingSdkNotificationPanel panel,
|
||||
@NotNull String source,
|
||||
@Nullable Runnable setProjectSdk,
|
||||
@Nullable Consumer<Sdk> setSdk) {
|
||||
|
||||
panel.setProviderKey(EDITOR_NOTIFICATIONS_KEY);
|
||||
panel.setText("SDK is not set for " + source);
|
||||
|
||||
if (setProjectSdk != null) {
|
||||
panel.createActionLabel("Use Project SDK", () -> {
|
||||
setProjectSdk.run();
|
||||
removeNotification(panel);
|
||||
});
|
||||
}
|
||||
|
||||
if (setSdk != null) {
|
||||
panel.createActionLabel("Configure...", () -> {
|
||||
//FileEditorManager#addTopComponent wraps the panel to implement borders, unwrapping
|
||||
Container container = panel.getParent();
|
||||
if (container == null) container = panel;
|
||||
|
||||
UnknownSdkTracker
|
||||
.getInstance(myProject)
|
||||
.showSdkSelectionPopup(null, container, sdk -> {
|
||||
setSdk.accept(sdk);
|
||||
removeNotification(panel);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -94,19 +119,56 @@ public class UnknownSdkEditorNotification implements Disposable {
|
||||
|
||||
@NotNull
|
||||
public List<SdkFixInfo> getNotifications() {
|
||||
return ImmutableList.copyOf(myNotifications.values());
|
||||
return ImmutableList.copyOf(myNotifications);
|
||||
}
|
||||
|
||||
public void showNotifications(@NotNull List<String> unifiableSdkNames,
|
||||
public void showNotifications(@NotNull List<SdkUsage> unsetSdks,
|
||||
@NotNull List<String> unifiableSdkNames,
|
||||
@NotNull Map<UnknownSdk, DownloadSdkFix> files) {
|
||||
myNotifications.clear();
|
||||
|
||||
for (SdkUsage usage : unsetSdks) {
|
||||
myNotifications.add(new SdkFixInfo() {
|
||||
@Override
|
||||
public void setupNotificationPanel(@NotNull MissingSdkNotificationPanel panel) {
|
||||
setupPanel(panel, usage.getUsagePresentableText(), usage.getProjectSdkSetAction(), usage.getSdkSetAction());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SdkFixInfo { sdkUsage: " + usage.getUsagePresentableText() + " }";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for (String name : unifiableSdkNames) {
|
||||
myNotifications.put(name, SdkFixInfo.forBroken(name));
|
||||
myNotifications.add(new SdkFixInfo() {
|
||||
@Override
|
||||
public void setupNotificationPanel(@NotNull MissingSdkNotificationPanel panel) {
|
||||
setupPanel(panel, name, (UnknownSdk)null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SdkFixInfo { unknownName: '" + name + "' }";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for (Map.Entry<UnknownSdk, DownloadSdkFix> e : files.entrySet()) {
|
||||
myNotifications.put(e.getKey().getSdkName(), SdkFixInfo.forFix(e));
|
||||
UnknownSdk key = e.getKey();
|
||||
DownloadSdkFix fix = e.getValue();
|
||||
myNotifications.add(new SdkFixInfo() {
|
||||
@Override
|
||||
public void setupNotificationPanel(@NotNull MissingSdkNotificationPanel panel) {
|
||||
setupPanel(panel, key.getSdkName(), key, fix);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SdkFixInfo { name: '" + key.getSdkName() + "', fix: " + fix.getDownloadDescription() + " }";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for (FileEditor editor : myFileEditorManager.getAllEditors()) {
|
||||
@@ -115,7 +177,7 @@ public class UnknownSdkEditorNotification implements Disposable {
|
||||
}
|
||||
|
||||
private void removeNotification(@NotNull MissingSdkNotificationPanel expiredPanel) {
|
||||
myNotifications.remove(expiredPanel.myInfo.getSdkName());
|
||||
myNotifications.remove(expiredPanel.myInfo);
|
||||
|
||||
for (FileEditor editor : myFileEditorManager.getAllEditors()) {
|
||||
List<MissingSdkNotificationPanel> notifications = editor.getUserData(NOTIFICATIONS);
|
||||
@@ -145,8 +207,11 @@ public class UnknownSdkEditorNotification implements Disposable {
|
||||
editor.putUserData(NOTIFICATIONS, notifications);
|
||||
}
|
||||
|
||||
for (SdkFixInfo info : myNotifications.values()) {
|
||||
MissingSdkNotificationPanel notification = createPanelFor(info);
|
||||
for (SdkFixInfo info : myNotifications) {
|
||||
MissingSdkNotificationPanel notification = new MissingSdkNotificationPanel(info);
|
||||
notification.setProject(myProject);
|
||||
notification.setProviderKey(EDITOR_NOTIFICATIONS_KEY);
|
||||
info.setupNotificationPanel(notification);
|
||||
|
||||
notifications.add(notification);
|
||||
myFileEditorManager.addTopComponent(editor, notification);
|
||||
@@ -166,56 +231,7 @@ public class UnknownSdkEditorNotification implements Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
public static class SdkFixInfo {
|
||||
@NotNull
|
||||
static SdkFixInfo forBroken(@NotNull String sdkName) {
|
||||
return new SdkFixInfo(sdkName);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
static SdkFixInfo forFix(@NotNull Map.Entry<? extends UnknownSdk, DownloadSdkFix> e) {
|
||||
UnknownSdk sdk = e.getKey();
|
||||
DownloadSdkFix fix = e.getValue();
|
||||
return new SdkFixInfo(sdk.getSdkName()) {
|
||||
@NotNull
|
||||
@Override
|
||||
public Pair<UnknownSdk, DownloadSdkFix> getFixInfo() {
|
||||
return Pair.create(sdk, fix);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private final String mySdkName;
|
||||
|
||||
private SdkFixInfo(@NotNull String sdkName) {
|
||||
mySdkName = sdkName;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getSdkName() {
|
||||
return mySdkName;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Pair<UnknownSdk, DownloadSdkFix> getFixInfo() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int hashCode() {
|
||||
return Objects.hashCode(mySdkName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean equals(Object obj) {
|
||||
return obj instanceof SdkFixInfo && Objects.equals(((SdkFixInfo)obj).mySdkName, mySdkName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SdkFixInfo{" +
|
||||
"mySdkName='" + mySdkName + '\'' +
|
||||
'}';
|
||||
}
|
||||
public interface SdkFixInfo {
|
||||
void setupNotificationPanel(@NotNull MissingSdkNotificationPanel panel);
|
||||
}
|
||||
}
|
||||
|
||||
+38
-29
@@ -1,9 +1,9 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.openapi.projectRoots.impl;
|
||||
|
||||
import com.google.common.collect.*;
|
||||
import com.google.common.collect.MultimapBuilder;
|
||||
import com.google.common.collect.SetMultimap;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.application.Application;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.ReadAction;
|
||||
import com.intellij.openapi.application.WriteAction;
|
||||
@@ -27,17 +27,16 @@ import com.intellij.openapi.roots.ui.configuration.projectRoot.SdkDownloadTracke
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.util.Consumer;
|
||||
import com.intellij.util.TripleFunction;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static com.intellij.openapi.progress.PerformInBackgroundOption.ALWAYS_BACKGROUND;
|
||||
import static com.intellij.openapi.projectRoots.impl.UnknownSdkResolver.UnknownSdk;
|
||||
@@ -69,28 +68,28 @@ public class UnknownSdkTracker {
|
||||
}
|
||||
|
||||
private void updateUnknownSdksWithProgress(@NotNull ProgressIndicator indicator) {
|
||||
List<String> totallyUnknownSdks = new ArrayList<>();
|
||||
List<SdkUsage> unsetSdks = new ArrayList<>();
|
||||
List<String> missingSdks = new ArrayList<>();
|
||||
List<UnknownSdk> fixable = new ArrayList<>();
|
||||
ReadAction.run(() -> collectAndGroupSdkUsages(totallyUnknownSdks, fixable));
|
||||
ReadAction.run(() -> collectAndGroupSdkUsages(unsetSdks, missingSdks, fixable));
|
||||
|
||||
List<UnknownSdkLookup> lookups = collectSdkLookups(indicator);
|
||||
Map<UnknownSdk, LocalSdkFix> localFixes = findFixesAndRemoveFixable(indicator, fixable, lookups, UnknownSdkLookup::proposeLocalFix);
|
||||
Map<UnknownSdk, DownloadSdkFix> downloadFixes = findFixesAndRemoveFixable(indicator, fixable, lookups, UnknownSdkLookup::proposeDownload);
|
||||
fixable.forEach(it -> totallyUnknownSdks.add(it.getSdkName()));
|
||||
fixable.forEach(it -> missingSdks.add(it.getSdkName()));
|
||||
|
||||
ApplicationManager.getApplication().invokeLater(() -> {
|
||||
indicator.setText("Configuring SDKs...");
|
||||
configureLocalSdks(localFixes);
|
||||
|
||||
UnknownSdkBalloonNotification.getInstance(myProject).notifyFixedSdks(localFixes);
|
||||
UnknownSdkEditorNotification.getInstance(myProject).showNotifications(totallyUnknownSdks, downloadFixes);
|
||||
UnknownSdkEditorNotification.getInstance(myProject).showNotifications(unsetSdks, missingSdks, downloadFixes);
|
||||
});
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private List<UnknownSdkLookup> collectSdkLookups(@NotNull ProgressIndicator indicator) {
|
||||
List<UnknownSdkLookup> lookups = new ArrayList<>();
|
||||
|
||||
UnknownSdkResolver.EP_NAME.forEachExtensionSafe(ext -> {
|
||||
UnknownSdkLookup resolver = ext.createResolver(myProject, indicator);
|
||||
if (resolver != null) {
|
||||
@@ -100,7 +99,15 @@ public class UnknownSdkTracker {
|
||||
return lookups;
|
||||
}
|
||||
|
||||
private void collectAndGroupSdkUsages(@NotNull List<String> totallyUnknownSdks,
|
||||
/**
|
||||
* Collects all SDK usages from the project model and splits them
|
||||
* into the specified groups
|
||||
* @param undefinedSdks all usages where SDK is not set, e.g. lack of Project or Module SDK
|
||||
* @param totallyUnknownSdks all named SDKs that are not present and where SdkType is missing or contains different values
|
||||
* @param resolvableSdks all usages where fix extension points from {@link UnknownSdkResolver#EP_NAME} are possible to apply
|
||||
*/
|
||||
private void collectAndGroupSdkUsages(@NotNull List<SdkUsage> undefinedSdks,
|
||||
@NotNull List<String> totallyUnknownSdks,
|
||||
@NotNull List<UnknownSdk> resolvableSdks) {
|
||||
SetMultimap<String, String> sdkToTypes = MultimapBuilder
|
||||
.treeKeys(String.CASE_INSENSITIVE_ORDER)
|
||||
@@ -111,6 +118,11 @@ public class UnknownSdkTracker {
|
||||
for (SdkUsage usage : SdkUsagesCollector.getInstance(myProject).collectSdkUsages()) {
|
||||
String sdkName = usage.getSdkName();
|
||||
|
||||
if (sdkName == null) {
|
||||
undefinedSdks.add(usage);
|
||||
continue;
|
||||
}
|
||||
|
||||
//we do not track existing SDKs
|
||||
if (jdkTable.findJdk(sdkName) != null) continue;
|
||||
|
||||
@@ -177,6 +189,15 @@ public class UnknownSdkTracker {
|
||||
@Nullable SdkType sdkType,
|
||||
@NotNull Container underneathRightOfComponent,
|
||||
@NotNull Runnable onSelectionMade) {
|
||||
showSdkSelectionPopup(sdkType, underneathRightOfComponent, sdk -> {
|
||||
registerNewSdkInJdkTable(sdkName, sdk);
|
||||
onSelectionMade.run();
|
||||
});
|
||||
}
|
||||
|
||||
public void showSdkSelectionPopup(@Nullable SdkType sdkType,
|
||||
@NotNull Container underneathRightOfComponent,
|
||||
@NotNull Consumer<? super Sdk> onSelectionMade) {
|
||||
ProjectSdksModel model = new ProjectSdksModel();
|
||||
SdkListModelBuilder modelBuilder = new SdkListModelBuilder(
|
||||
myProject,
|
||||
@@ -191,21 +212,22 @@ public class UnknownSdkTracker {
|
||||
modelBuilder
|
||||
);
|
||||
|
||||
AtomicBoolean wasSdkCreated = new AtomicBoolean(false);
|
||||
AtomicReference<Sdk> wasSdkCreated = new AtomicReference<>(null);
|
||||
model.addListener(new SdkModel.Listener() {
|
||||
@Override
|
||||
public void sdkAdded(@NotNull Sdk sdk) {
|
||||
//TODO: handle if an existing item is selected!
|
||||
//it is easier and safer than committing the ProjectSdksModel instance
|
||||
registerNewSdkInJdkTable(sdkName, sdk);
|
||||
wasSdkCreated.set(true);
|
||||
wasSdkCreated.set(sdk);
|
||||
}
|
||||
});
|
||||
|
||||
popup.showUnderneathToTheRightOf(
|
||||
underneathRightOfComponent,
|
||||
() -> {
|
||||
if (wasSdkCreated.get()) {
|
||||
onSelectionMade.run();
|
||||
Sdk sdk = wasSdkCreated.get();
|
||||
if (sdk != null) {
|
||||
onSelectionMade.consume(sdk);
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -299,18 +321,5 @@ public class UnknownSdkTracker {
|
||||
public SdkType getSdkType() {
|
||||
return mySdkType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof MissingSdkInfo)) return false;
|
||||
MissingSdkInfo info = (MissingSdkInfo)o;
|
||||
return mySdkName.equals(info.mySdkName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(mySdkName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user