Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,8 @@ public static void showInstallConfirmation(@Nonnull Project project, boolean for
: "Install this plugin to automate migrating your apps to Azure with Copilot.";
} else {
message = forUpgrade
? "To upgrade your apps, you'll need two plugins: GitHub Copilot and app modernization."
: "To migrate to Azure, you'll need two plugins: GitHub Copilot and app modernization.";
? "To upgrade your apps, you'll need to install GitHub Copilot modernization."
: "To migrate to Azure, you'll need to install GitHub Copilot modernization.";
}
AppModUtils.logTelemetryEvent("plugin." + action + ".install-prompt-shown", Map.of("copilotInstalled", String.valueOf(copilotInstalled)));
if (Messages.showOkCancelDialog(project, message, title, "Install", "Cancel", Messages.getQuestionIcon()) == Messages.OK) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,11 @@ public void update(@NotNull AnActionEvent e) {
}
e.getPresentation().setEnabledAndVisible(true);
// e.getPresentation().setText(SCAN_AND_RESOLVE_CVES_WITH_COPILOT_DISPLAY_NAME);
final String baseText = getTemplatePresentation().getText();
if (!AppModPluginInstaller.isAppModPluginInstalled()) {
e.getPresentation().setText(e.getPresentation().getText() + AppModPluginInstaller.TO_INSTALL_APP_MODE_PLUGIN);
e.getPresentation().setText(baseText + AppModPluginInstaller.TO_INSTALL_APP_MODE_PLUGIN);
} else {
e.getPresentation().setText(baseText);
}
} catch (Throwable ex) {
// In case of any error, hide the action
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,11 @@ public void update(@NotNull AnActionEvent e) {
isMavenBuildFile(file) ||
isGradleBuildFile(file);
}
final String baseText = getTemplatePresentation().getText();
if (!isAppModPluginInstalled()) {
e.getPresentation().setText(e.getPresentation().getText() + TO_INSTALL_APP_MODE_PLUGIN);
e.getPresentation().setText(baseText + TO_INSTALL_APP_MODE_PLUGIN);
} else {
e.getPresentation().setText(baseText);
}
if (visible){
AppModUtils.logTelemetryEvent("showJavaUpgradeContextMenuAction", Map.of("appmodPluginInstalled", String.valueOf(isAppModPluginInstalled())));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,15 @@
package com.microsoft.azure.toolkit.intellij.appmod.javaupgrade.action;

import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.ActionPlaces;
import com.intellij.openapi.actionSystem.ActionPopupMenu;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.actionSystem.Presentation;
import com.intellij.openapi.actionSystem.Separator;
import com.intellij.openapi.actionSystem.ex.ActionManagerEx;
import com.intellij.openapi.actionSystem.ex.ActionPopupMenuListener;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.startup.ProjectActivity;
import com.microsoft.azure.toolkit.intellij.appmod.common.AppModPluginInstaller;
Expand All @@ -21,6 +26,8 @@
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.concurrent.atomic.AtomicBoolean;

/**
* Registers the Upgrade action into the GitHub Copilot context menu at runtime.
* This is needed because the Copilot plugin creates its context menu groups dynamically.
Expand All @@ -31,17 +38,67 @@ public class UpgradeActionRegistrar implements ProjectActivity {
private static final String UPGRADE_ACTION_ID = "AzureToolkit.JavaUpgradeContextMenu";
private static final String PROJECT_VIEW_POPUP_MENU = "ProjectViewPopupMenu";

// Application-level guard so we install the popup listener only once per IDE process,
// even if multiple projects are opened (this ProjectActivity runs per-project).
private static final AtomicBoolean POPUP_LISTENER_INSTALLED = new AtomicBoolean(false);

@Nullable
@Override
public Object execute(@NotNull Project project, @NotNull Continuation<? super Unit> continuation) {
try{
// Eager attempt: works on 2nd+ project open within the same IDE process,
// after the GitHub Copilot plugin has populated its dynamic submenu.
discoverAndRegisterAction();
// Lazy fallback (fixes the first-open race): re-attempt the registration
// every time the Project View popup is created. The Copilot submenu is
// guaranteed to exist by the time the user right-clicks, and the call
// is cheap + idempotent thanks to the containsAction guard.
installLazyRegistrationListener();
} catch (Throwable e) {
log.error("Failed to register Upgrade action in Copilot context menu.", e);
}
return Unit.INSTANCE;
}

/**
* Installs an application-scoped {@link ActionPopupMenuListener} (only once per IDE
* process) that re-runs {@link #discoverAndRegisterAction()} whenever the Project
* View popup menu is opened. This is the lazy fallback for the first project open
* after IDE launch, where {@link ProjectActivity}s from us and from the GitHub Copilot
* plugin race and our discovery can miss Copilot's not-yet-created submenu.
*/
private void installLazyRegistrationListener() {
if (!POPUP_LISTENER_INSTALLED.compareAndSet(false, true)) {
return;
}
try {
ActionManagerEx.getInstanceEx().addActionPopupMenuListener(new ActionPopupMenuListener() {
@Override
public void actionPopupMenuCreated(@NotNull ActionPopupMenu menu) {
// Only react to the Project View right-click popup; ignore all
// other popups (editor, tool windows, etc.) to keep this cheap.
if (!ActionPlaces.PROJECT_VIEW_POPUP.equals(menu.getPlace())) {
return;
}
try {
discoverAndRegisterAction();
} catch (Throwable ex) {
log.warn("Lazy registration of Upgrade action into Copilot submenu failed.", ex);
}
}

@Override
public void actionPopupMenuReleased(@NotNull ActionPopupMenu menu) {
// no-op
}
}, ApplicationManager.getApplication());
} catch (Throwable e) {
// Roll back the flag so a later project open can try installing again.
POPUP_LISTENER_INSTALLED.set(false);
log.warn("Failed to install lazy registration listener for Upgrade action.", e);
}
}

private void discoverAndRegisterAction() {
// Only proceed if Copilot plugin is installed
if (!AppModPluginInstaller.isCopilotInstalled()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public class JavaVersionNotificationService {
private static final String NOTIFICATIONS_ENABLED_KEY = "azure.toolkit.java.version.notifications.enabled";
private static final String DEFERRED_UNTIL_KEY = "azure.toolkit.java.version.deferred_until";
private static final long DEFER_INTERVAL_MS = 10 * 24 * 60 * 60 * 1000L; // 10 days in milliseconds
private static final String DEFAULT_MODEL_NAME = "Claude Sonnet 4.5";
private static final String DEFAULT_MODEL_NAME = "Claude Sonnet 4.6";

// GitHub Copilot plugin ID
private static final String COPILOT_PLUGIN_ID = "com.github.copilot";
Expand Down