Skip to content

Commit 41fce53

Browse files
committed
fix: update plugin dependency checks and notifications, fixes #402
1 parent b78d12e commit 41fce53

15 files changed

+298
-39
lines changed

src/main/java/de/php_perfect/intellij/ddev/database/AutoConfigureDataSourceListener.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
import de.php_perfect.intellij.ddev.DatabaseInfoChangedListener;
55
import de.php_perfect.intellij.ddev.cmd.DatabaseInfo;
66
import de.php_perfect.intellij.ddev.settings.DdevSettingsState;
7+
import de.php_perfect.intellij.ddev.util.FeatureRequiredPlugins;
8+
import de.php_perfect.intellij.ddev.util.PluginChecker;
79
import org.jetbrains.annotations.NotNull;
810
import org.jetbrains.annotations.Nullable;
911

@@ -30,6 +32,10 @@ public void onDatabaseInfoChanged(@Nullable DatabaseInfo databaseInfo) {
3032
return;
3133
}
3234

35+
if (PluginChecker.isMissingRequiredPlugins(this.project, FeatureRequiredPlugins.DATABASE, "database auto-registration")) {
36+
return;
37+
}
38+
3339
final DataSourceConfig.Type type = switch (databaseInfo.type()) {
3440
case MYSQL -> DataSourceConfig.Type.MYSQL;
3541
case MARIADB -> DataSourceConfig.Type.MARIADB;

src/main/java/de/php_perfect/intellij/ddev/node/AutoConfigureNodeInterpreterListener.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
import de.php_perfect.intellij.ddev.cmd.Description;
77
import de.php_perfect.intellij.ddev.dockerCompose.DdevComposeFileLoader;
88
import de.php_perfect.intellij.ddev.settings.DdevSettingsState;
9+
import de.php_perfect.intellij.ddev.util.FeatureRequiredPlugins;
10+
import de.php_perfect.intellij.ddev.util.PluginChecker;
911
import org.jetbrains.annotations.NotNull;
1012
import org.jetbrains.annotations.Nullable;
1113

@@ -33,6 +35,10 @@ public void onDescriptionChanged(@Nullable Description description) {
3335
return;
3436
}
3537

38+
if (PluginChecker.isMissingRequiredPlugins(this.project, FeatureRequiredPlugins.NODE_INTERPRETER, "Node.js interpreter auto-registration")) {
39+
return;
40+
}
41+
3642
final NodeInterpreterConfig nodeInterpreterConfig = new NodeInterpreterConfig(description.getName(), composeFile.getPath(), "node");
3743
NodeInterpreterProvider.getInstance(this.project).configureNodeInterpreter(nodeInterpreterConfig);
3844
}

src/main/java/de/php_perfect/intellij/ddev/notification/DdevNotifier.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ public interface DdevNotifier {
1010

1111
void notifyAlreadyLatestVersion();
1212

13-
void notifyMissingPlugin(@NotNull String pluginName);
13+
void notifyMissingPlugin(@NotNull String pluginName, @NotNull String featureName);
14+
15+
void notifyMissingPlugins(@NotNull String pluginNames, @NotNull String featureName);
1416

1517
void notifyPhpInterpreterUpdated(@NotNull String phpVersion);
1618

src/main/java/de/php_perfect/intellij/ddev/notification/DdevNotifierImpl.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,25 @@ public void notifyAlreadyLatestVersion() {
5858
}
5959

6060
@Override
61-
public void notifyMissingPlugin(final @NotNull String pluginName) {
61+
public void notifyMissingPlugin(final @NotNull String pluginName, final @NotNull String featureName) {
6262
ApplicationManager.getApplication().invokeLater(() -> NotificationGroupManager.getInstance()
6363
.getNotificationGroup(STICKY)
6464
.createNotification(
6565
DdevIntegrationBundle.message("notification.MissingPlugin.title"),
66-
DdevIntegrationBundle.message("notification.MissingPlugin.text", pluginName),
66+
DdevIntegrationBundle.message("notification.MissingPlugin.withFeature.text", pluginName, featureName),
67+
NotificationType.WARNING
68+
)
69+
.addAction(new ManagePluginsAction())
70+
.notify(this.project), ModalityState.nonModal());
71+
}
72+
73+
@Override
74+
public void notifyMissingPlugins(final @NotNull String pluginNames, final @NotNull String featureName) {
75+
ApplicationManager.getApplication().invokeLater(() -> NotificationGroupManager.getInstance()
76+
.getNotificationGroup(STICKY)
77+
.createNotification(
78+
DdevIntegrationBundle.message("notification.MissingPlugins.title"),
79+
DdevIntegrationBundle.message("notification.MissingPlugins.withFeature.text", pluginNames, featureName),
6780
NotificationType.WARNING
6881
)
6982
.addAction(new ManagePluginsAction())

src/main/java/de/php_perfect/intellij/ddev/php/ConfigurationProviderImpl.java

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,15 @@
11
package de.php_perfect.intellij.ddev.php;
22

3-
import com.intellij.ide.plugins.PluginManager;
4-
import com.intellij.openapi.extensions.PluginId;
53
import com.intellij.openapi.project.Project;
64
import com.intellij.openapi.vfs.VirtualFile;
75
import de.php_perfect.intellij.ddev.cmd.Description;
86
import de.php_perfect.intellij.ddev.dockerCompose.DdevComposeFileLoader;
9-
import de.php_perfect.intellij.ddev.notification.DdevNotifier;
107
import de.php_perfect.intellij.ddev.settings.DdevSettingsState;
8+
import de.php_perfect.intellij.ddev.util.FeatureRequiredPlugins;
9+
import de.php_perfect.intellij.ddev.util.PluginChecker;
1110
import org.jetbrains.annotations.NotNull;
1211

13-
import java.util.List;
14-
1512
public final class ConfigurationProviderImpl implements ConfigurationProvider {
16-
private static final List<String> REQUIRED_PLUGINS = List.of(
17-
"org.jetbrains.plugins.phpstorm-remote-interpreter",
18-
"org.jetbrains.plugins.phpstorm-docker",
19-
"Docker"
20-
);
2113

2214
private final @NotNull Project project;
2315

@@ -41,15 +33,8 @@ public void configure(@NotNull Description description) {
4133
return;
4234
}
4335

44-
final var pluginManager = PluginManager.getInstance();
45-
46-
for (final String id : REQUIRED_PLUGINS) {
47-
final PluginId pluginId = PluginId.findId(id);
48-
49-
if (pluginId == null || pluginManager.findEnabledPlugin(pluginId) == null) {
50-
DdevNotifier.getInstance(this.project).notifyMissingPlugin(id);
51-
return;
52-
}
36+
if (PluginChecker.isMissingRequiredPlugins(this.project, FeatureRequiredPlugins.PHP_INTERPRETER, "PHP interpreter auto-registration")) {
37+
return;
5338
}
5439

5540
final DdevInterpreterConfig ddevInterpreterConfig = new DdevInterpreterConfig(description.getName(), "php" + description.getPhpVersion(), composeFile.getPath());

src/main/java/de/php_perfect/intellij/ddev/settings/DdevSettingsComponent.java

Lines changed: 80 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package de.php_perfect.intellij.ddev.settings;
22

3+
import com.intellij.icons.AllIcons;
34
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
45
import com.intellij.openapi.project.Project;
56
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
@@ -8,11 +9,16 @@
89
import com.intellij.ui.components.JBLabel;
910
import com.intellij.util.ui.FormBuilder;
1011
import com.intellij.util.ui.JBUI;
12+
import com.intellij.util.ui.UIUtil;
1113
import de.php_perfect.intellij.ddev.DdevIntegrationBundle;
14+
import de.php_perfect.intellij.ddev.util.FeatureRequiredPlugins;
15+
import de.php_perfect.intellij.ddev.util.PluginChecker;
1216
import org.jetbrains.annotations.NotNull;
1317

1418
import javax.swing.*;
19+
import javax.swing.BoxLayout;
1520
import java.awt.*;
21+
import java.util.List;
1622

1723
public final class DdevSettingsComponent {
1824
private final @NotNull JPanel jPanel;
@@ -32,7 +38,7 @@ public DdevSettingsComponent(Project project) {
3238
checkForUpdatesComment.setForeground(UIManager.getColor("Component.infoForeground"));
3339
checkForUpdatesComment.setBorder(JBUI.Borders.emptyLeft(24));
3440
checkForUpdatesPanel.add(checkForUpdatesComment, BorderLayout.CENTER);
35-
41+
3642
JPanel watchDdevPanel = new JPanel(new BorderLayout());
3743
watchDdevPanel.add(this.watchDdevCheckbox, BorderLayout.NORTH);
3844
JLabel watchDdevComment = new JLabel(DdevIntegrationBundle.message("settings.watchDdev.description"));
@@ -41,13 +47,35 @@ public DdevSettingsComponent(Project project) {
4147
watchDdevComment.setBorder(JBUI.Borders.emptyLeft(24));
4248
watchDdevPanel.add(watchDdevComment, BorderLayout.CENTER);
4349

50+
// Set up warning indicators and tooltips
51+
@NotNull JPanel autoConfigureDataSourcePanel = new JPanel(new BorderLayout());
52+
@NotNull JLabel dataSourceWarningLabel = new JLabel(AllIcons.General.Warning);
53+
@NotNull JLabel dataSourceWarningText = new JLabel();
54+
setupWarningIndicator(autoConfigureDataSourcePanel, this.autoConfigureDataSource, dataSourceWarningLabel,
55+
dataSourceWarningText, FeatureRequiredPlugins.DATABASE, "database auto-registration");
56+
57+
@NotNull JPanel autoConfigurePhpInterpreterPanel = new JPanel(new BorderLayout());
58+
@NotNull JLabel phpWarningLabel = new JLabel(AllIcons.General.Warning);
59+
@NotNull JLabel phpWarningText = new JLabel();
60+
setupWarningIndicator(autoConfigurePhpInterpreterPanel, this.autoConfigurePhpInterpreter, phpWarningLabel,
61+
phpWarningText, FeatureRequiredPlugins.PHP_INTERPRETER, "PHP interpreter auto-registration");
62+
63+
@NotNull JPanel autoConfigureNodeJsInterpreterPanel = new JPanel(new BorderLayout());
64+
@NotNull JLabel nodeWarningLabel = new JLabel(AllIcons.General.Warning);
65+
@NotNull JLabel nodeWarningText = new JLabel();
66+
setupWarningIndicator(autoConfigureNodeJsInterpreterPanel, this.autoConfigureNodeJsInterpreter, nodeWarningLabel,
67+
nodeWarningText, FeatureRequiredPlugins.NODE_INTERPRETER, "Node.js interpreter auto-registration");
68+
4469
final JPanel panel = new JPanel();
4570
panel.setBorder(IdeBorderFactory.createTitledBorder(DdevIntegrationBundle.message("settings.automaticConfiguration"), true));
46-
panel.setLayout(new GridBagLayout());
47-
final GridBagConstraints gc = new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1, 0, GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, JBUI.emptyInsets(), 0, 0);
48-
panel.add(this.autoConfigureDataSource, gc);
49-
panel.add(this.autoConfigurePhpInterpreter, gc);
50-
panel.add(this.autoConfigureNodeJsInterpreter, gc);
71+
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
72+
73+
// Add feature panels with some vertical spacing
74+
panel.add(autoConfigureDataSourcePanel);
75+
panel.add(Box.createVerticalStrut(5));
76+
panel.add(autoConfigurePhpInterpreterPanel);
77+
panel.add(Box.createVerticalStrut(5));
78+
panel.add(autoConfigureNodeJsInterpreterPanel);
5179

5280
this.ddevBinary.addBrowseFolderListener(
5381
project,
@@ -65,6 +93,52 @@ public DdevSettingsComponent(Project project) {
6593
.getPanel();
6694
}
6795

96+
/**
97+
* Sets up a warning indicator for a feature that requires plugins.
98+
*
99+
* @param panel The panel to add the components to
100+
* @param checkbox The checkbox for the feature
101+
* @param warningLabel The warning icon to show if plugins are missing
102+
* @param warningText The label to display the warning text
103+
* @param requiredPlugins The list of required plugin IDs
104+
* @param featureName The name of the feature for the message
105+
*/
106+
private void setupWarningIndicator(@NotNull JPanel panel, @NotNull JBCheckBox checkbox, @NotNull JLabel warningLabel,
107+
@NotNull JLabel warningText, @NotNull List<String> requiredPlugins, @NotNull String featureName) {
108+
// Set up panel with vertical layout
109+
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
110+
111+
// Add checkbox to panel
112+
JPanel checkboxPanel = new JPanel(new BorderLayout());
113+
checkboxPanel.add(checkbox, BorderLayout.WEST);
114+
panel.add(checkboxPanel);
115+
116+
// Check if any required plugins are missing
117+
List<String> missingPlugins = PluginChecker.getMissingPlugins(requiredPlugins);
118+
119+
if (missingPlugins.isEmpty()) {
120+
// No missing plugins, hide warning
121+
warningLabel.setVisible(false);
122+
warningText.setVisible(false);
123+
} else {
124+
// Create warning message
125+
String warningMessage = DdevIntegrationBundle.message("settings.warning.missingPlugins", String.join(", ", missingPlugins));
126+
127+
// Set up warning text
128+
warningText.setText(" " + warningMessage);
129+
warningText.setForeground(UIUtil.getErrorForeground());
130+
131+
// Create warning panel with left alignment and indentation
132+
JPanel warningPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0));
133+
warningPanel.setBorder(JBUI.Borders.emptyLeft(24));
134+
warningPanel.add(warningLabel);
135+
warningPanel.add(warningText);
136+
137+
// Add warning panel below the checkbox
138+
panel.add(warningPanel);
139+
}
140+
}
141+
68142
public @NotNull JPanel getPanel() {
69143
return this.jPanel;
70144
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package de.php_perfect.intellij.ddev.util;
2+
3+
import org.jetbrains.annotations.NotNull;
4+
5+
import java.util.List;
6+
7+
/**
8+
* Utility class to hold the required plugins for each feature.
9+
*/
10+
public final class FeatureRequiredPlugins {
11+
private FeatureRequiredPlugins() {
12+
// Utility class, no instances
13+
}
14+
15+
/**
16+
* Required plugins for PHP interpreter auto-registration.
17+
*/
18+
public static final @NotNull List<String> PHP_INTERPRETER = List.of(
19+
"com.jetbrains.php",
20+
"org.jetbrains.plugins.phpstorm-remote-interpreter",
21+
"org.jetbrains.plugins.phpstorm-docker"
22+
);
23+
24+
/**
25+
* Required plugins for Node.js interpreter auto-registration.
26+
*/
27+
public static final @NotNull List<String> NODE_INTERPRETER = List.of(
28+
"NodeJS",
29+
"org.jetbrains.plugins.node-remote-interpreter"
30+
);
31+
32+
/**
33+
* Required plugins for database auto-registration.
34+
*/
35+
public static final @NotNull List<String> DATABASE = List.of(
36+
"com.intellij.database"
37+
);
38+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package de.php_perfect.intellij.ddev.util;
2+
3+
import com.intellij.ide.plugins.PluginManager;
4+
import com.intellij.openapi.extensions.PluginId;
5+
import com.intellij.openapi.project.Project;
6+
import de.php_perfect.intellij.ddev.notification.DdevNotifier;
7+
import org.jetbrains.annotations.NotNull;
8+
9+
import java.util.ArrayList;
10+
import java.util.List;
11+
12+
/**
13+
* Utility class to check for required plugins.
14+
*/
15+
public final class PluginChecker {
16+
private PluginChecker() {
17+
// Utility class, no instances
18+
}
19+
20+
/**
21+
* Checks if any required plugin is missing.
22+
* If any plugins are missing, it will notify the user and return true.
23+
*
24+
* @param project The current project
25+
* @param requiredPlugins List of plugin IDs to check
26+
* @param featureName The name of the feature that requires these plugins
27+
* @return true if any plugin is missing, false if all are available
28+
*/
29+
public static boolean isMissingRequiredPlugins(@NotNull Project project, @NotNull List<String> requiredPlugins, @NotNull String featureName) {
30+
List<String> missingPluginNames = getMissingPlugins(requiredPlugins);
31+
32+
if (!missingPluginNames.isEmpty()) {
33+
String missingPluginsDisplay = String.join(", ", missingPluginNames);
34+
if (missingPluginNames.size() == 1) {
35+
DdevNotifier.getInstance(project).notifyMissingPlugin(missingPluginsDisplay, featureName);
36+
} else {
37+
DdevNotifier.getInstance(project).notifyMissingPlugins(missingPluginsDisplay, featureName);
38+
}
39+
return true;
40+
}
41+
42+
return false;
43+
}
44+
45+
/**
46+
* Checks if any required plugin is missing without showing notifications.
47+
* This is useful for UI components that want to check dependencies without showing notifications.
48+
*
49+
* @param requiredPlugins List of plugin IDs to check
50+
* @return List of missing plugin display names, empty if all are available
51+
*/
52+
public static @NotNull List<String> getMissingPlugins(@NotNull List<String> requiredPlugins) {
53+
final var pluginManager = PluginManager.getInstance();
54+
final List<String> missingPluginNames = new ArrayList<>();
55+
56+
for (final String id : requiredPlugins) {
57+
final PluginId pluginId = PluginId.findId(id);
58+
59+
if (pluginId == null || pluginManager.findEnabledPlugin(pluginId) == null) {
60+
String displayName = PluginDisplayNameMapper.getDisplayName(id);
61+
missingPluginNames.add(displayName);
62+
}
63+
}
64+
65+
return missingPluginNames;
66+
}
67+
}

0 commit comments

Comments
 (0)