From ee7b6b7c8cf717bcb5ebdfeaf044555ae84f0f9e Mon Sep 17 00:00:00 2001 From: Shruti Sinha <44882001+shruti0085@users.noreply.github.com> Date: Wed, 9 Jul 2025 09:18:01 -0700 Subject: [PATCH 01/40] Pass modelSelction flag to server (#477) Currently, we pass the modelSelection flag in createChat, but we need to pass it to server so that server knows if client has the ability or not. With this change we pass the modelSelection in awsCapabilities in lsp initilization. --- .../toolkits/eclipse/amazonq/lsp/AmazonQLspServerBuilder.java | 1 + 1 file changed, 1 insertion(+) diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspServerBuilder.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspServerBuilder.java index 51d7f74ea..e351ba861 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspServerBuilder.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspServerBuilder.java @@ -56,6 +56,7 @@ private Map getInitializationOptions(final ClientMetadata metada qOptions.put("developerProfiles", true); qOptions.put("customizationsWithMetadata", true); qOptions.put("mcp", true); + qOptions.put("modelSelection", true); awsClientCapabilities.put("q", qOptions); Map window = new HashMap<>(); window.put("showSaveFileDialog", true); From df41895a81ed8b33182a14c2666ab0f9c4b53f76 Mon Sep 17 00:00:00 2001 From: Shruti Sinha <44882001+shruti0085@users.noreply.github.com> Date: Wed, 9 Jul 2025 09:49:35 -0700 Subject: [PATCH 02/40] feat: Add pinned context support for Amazon Q chat (#473) This PR implements pinned context functionality for Amazon Q Eclipse plugin, allowing users to pin files to their chat conversations for persistent context. This feature improves the relevance of Amazon Q responses by maintaining important context throughout the conversation. * Adds the ability to pin/unpin files in Amazon Q chat conversations * Implements automatic active editor tracking with 100ms debouncing * Enables the @pin Context feature visible in the chat UI * Provides LSP server integration for pinned context operations * Handles UTF8 rendering on Windows devices This change also remove the isUriInWorkspace check for when lsp sends showDocument notification to client. This check would gate the ability to open documents not present in the workspace which is required for paths associated with prompts/rules that are stored on disk and require opening it in the IDE for editing. --- .../chat/ChatCommunicationManager.java | 26 ++++ .../chat/models/ChatUIInboundCommandName.java | 5 +- .../eclipse/amazonq/lsp/AmazonQLspClient.java | 2 + .../amazonq/lsp/AmazonQLspClientImpl.java | 97 ++++++++++++-- .../eclipse/amazonq/lsp/AmazonQLspServer.java | 15 +++ .../amazonq/lsp/AmazonQLspServerBuilder.java | 1 + .../editor/ActiveEditorChangeListener.java | 120 ++++++++++++++++++ .../eclipse/amazonq/plugin/Activator.java | 4 + .../assets/ChatWebViewAssetProvider.java | 2 +- .../amazonq/util/QEclipseEditorUtils.java | 9 +- .../views/AmazonQChatViewActionHandler.java | 4 + .../eclipse/amazonq/views/model/Command.java | 5 +- 12 files changed, 275 insertions(+), 15 deletions(-) create mode 100644 plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/editor/ActiveEditorChangeListener.java diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatCommunicationManager.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatCommunicationManager.java index 069acb638..b4dea9977 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatCommunicationManager.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatCommunicationManager.java @@ -258,6 +258,32 @@ public void sendMessageToChatServer(final Command command, final ChatMessage mes Activator.getLogger().error("Error processing mcpServerClick: " + e); } break; + case LIST_RULES: + try { + Object listRulesResponse = amazonQLspServer.listRules(message.getData()).get(); + var listRulesCommand = ChatUIInboundCommand.createCommand(ChatUIInboundCommandName.ListRules.getValue(), + listRulesResponse); + Activator.getEventBroker().post(ChatUIInboundCommand.class, listRulesCommand); + } catch (Exception e) { + Activator.getLogger().error("Error processing listRules: " + e); + } + break; + case RULE_CLICK: + try { + Object ruleClickResponse = amazonQLspServer.ruleClick(message.getData()).get(); + var ruleClickCommand = ChatUIInboundCommand.createCommand(ChatUIInboundCommandName.RuleClick.getValue(), + ruleClickResponse); + Activator.getEventBroker().post(ChatUIInboundCommand.class, ruleClickCommand); + } catch (Exception e) { + Activator.getLogger().error("Error processing ruleClick: " + e); + } + break; + case PINNED_CONTEXT_ADD: + amazonQLspServer.pinnedContextAdd(message.getData()); + break; + case PINNED_CONTEXT_REMOVE: + amazonQLspServer.pinnedContextRemove(message.getData()); + break; default: throw new AmazonQPluginException("Unexpected command received from Chat UI: " + command.toString()); } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/models/ChatUIInboundCommandName.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/models/ChatUIInboundCommandName.java index a9fa44eda..22f8fd8c8 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/models/ChatUIInboundCommandName.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/models/ChatUIInboundCommandName.java @@ -15,7 +15,10 @@ public enum ChatUIInboundCommandName { GenericCommand("genericCommand"), ChatOptionsUpdate("aws/chat/chatOptionsUpdate"), ListMcpServers("aws/chat/listMcpServers"), - McpServerClick("aws/chat/mcpServerClick"); + McpServerClick("aws/chat/mcpServerClick"), + ListRules("aws/chat/listRules"), + RuleClick("aws/chat/ruleClick"), + SendPinnedContext("aws/chat/sendPinnedContext"); private final String value; diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspClient.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspClient.java index d50d5bc77..54ae81373 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspClient.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspClient.java @@ -61,4 +61,6 @@ public interface AmazonQLspClient extends LanguageClient { @JsonNotification("aws/didCreateDirectory") void didCreateDirectory(Object params); + @JsonNotification("aws/chat/sendPinnedContext") + void sendPinnedContext(Object params); } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspClientImpl.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspClientImpl.java index 8f44c782c..9e05f716c 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspClientImpl.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspClientImpl.java @@ -7,6 +7,7 @@ import java.net.URI; import java.net.URISyntaxException; import java.net.URL; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -17,13 +18,16 @@ import java.util.Optional; import java.util.UUID; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicReference; +import org.apache.commons.lang3.StringUtils; import org.eclipse.core.filesystem.EFS; import org.eclipse.core.filesystem.IFileStore; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; + import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.jface.action.IAction; @@ -64,6 +68,7 @@ import software.aws.toolkits.eclipse.amazonq.chat.ChatAsyncResultManager; import software.aws.toolkits.eclipse.amazonq.chat.ChatCommunicationManager; import software.aws.toolkits.eclipse.amazonq.chat.models.ChatUIInboundCommand; +import software.aws.toolkits.eclipse.amazonq.chat.models.ChatUIInboundCommandName; import software.aws.toolkits.eclipse.amazonq.chat.models.GetSerializedChatParams; import software.aws.toolkits.eclipse.amazonq.chat.models.GetSerializedChatResult; import software.aws.toolkits.eclipse.amazonq.chat.models.SerializedChatResult; @@ -82,6 +87,7 @@ import software.aws.toolkits.eclipse.amazonq.lsp.model.SsoProfileData; import software.aws.toolkits.eclipse.amazonq.lsp.model.TelemetryEvent; import software.aws.toolkits.eclipse.amazonq.plugin.Activator; +import software.aws.toolkits.eclipse.amazonq.util.QEclipseEditorUtils; import software.aws.toolkits.eclipse.amazonq.preferences.AmazonQPreferencePage; import software.aws.toolkits.eclipse.amazonq.telemetry.service.DefaultTelemetryService; import software.aws.toolkits.eclipse.amazonq.util.Constants; @@ -223,15 +229,10 @@ public final CompletableFuture showDocument(final ShowDocume } else { Display.getDefault().syncExec(() -> { try { - if (!isUriInWorkspace(uri)) { - Activator.getLogger().error("Attempted to open file outside workspace: " + uri); - success[0] = false; - } else { - IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); - IFileStore fileStore = EFS.getLocalFileSystem().getStore(new URI(uri)); - IDE.openEditorOnFileStore(page, fileStore); - success[0] = true; - } + IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); + IFileStore fileStore = EFS.getLocalFileSystem().getStore(new URI(uri)); + IDE.openEditorOnFileStore(page, fileStore); + success[0] = true; } catch (Exception e) { Activator.getLogger().error("Error in UI thread while opening URI: " + uri, e); success[0] = false; @@ -575,4 +576,82 @@ private boolean isUriInWorkspace(final String uri) { return false; } } + + @Override + public final void sendPinnedContext(final Object params) { + Object updatedParams = params; + Optional fileUri = getActiveFileUri(); + if (fileUri.isPresent()) { + Map textDocument = Map.of("uri", fileUri.get()); + if (params instanceof Map) { + @SuppressWarnings("unchecked") + Map paramsMap = new HashMap<>((Map) params); + paramsMap.put("textDocument", textDocument); + updatedParams = paramsMap; + } else { + updatedParams = Map.of("params", params, "textDocument", textDocument); + } + } + + var sendPinnedContextCommand = ChatUIInboundCommand.createCommand(ChatUIInboundCommandName.SendPinnedContext.getValue(), updatedParams); + Activator.getEventBroker().post(ChatUIInboundCommand.class, sendPinnedContextCommand); + } + + private Optional getActiveFileUri() { + AtomicReference> fileUri = new AtomicReference<>(); + Display.getDefault().syncExec(() -> { + try { + fileUri.set(getActiveEditorRelativePath()); + } catch (Exception e) { + Activator.getLogger().error("Error getting active file URI", e); + fileUri.set(Optional.empty()); + } + }); + return fileUri.get(); + } + + private Optional getActiveEditorRelativePath() { + var activeEditor = QEclipseEditorUtils.getActiveTextEditor(); + if (activeEditor == null) { + return Optional.empty(); + } + return QEclipseEditorUtils.getOpenFileUri(activeEditor.getEditorInput()) + .map(this::getRelativePath); + } + + private String getRelativePath(final String absoluteUri) { + try { + if (StringUtils.isBlank(absoluteUri)) { + return absoluteUri; + } + + var uri = new URI(absoluteUri); + var activeFilePath = new File(uri).getCanonicalPath(); + + // Get workspace root path + var workspace = ResourcesPlugin.getWorkspace(); + var workspacePath = workspace.getRoot().getLocation(); + if (workspacePath == null) { + return activeFilePath; + } + + var workspaceRoot = workspacePath.toFile().getCanonicalPath(); + if (StringUtils.isBlank(workspaceRoot)) { + return activeFilePath; + } + + if (StringUtils.startsWithIgnoreCase(activeFilePath, workspaceRoot)) { + var workspaceRootPath = Paths.get(workspaceRoot); + var activeFilePathObj = Paths.get(activeFilePath); + var relativePath = workspaceRootPath.relativize(activeFilePathObj).normalize(); + return relativePath.toString().replace('\\', '/'); + } + + // Not in workspace, return absolute path + return activeFilePath; + } catch (Exception e) { + Activator.getLogger().error("Error occurred when attempting to determine relative path for: " + absoluteUri, e); + return absoluteUri; + } + } } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspServer.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspServer.java index 06c0572d7..caca62b7d 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspServer.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspServer.java @@ -129,4 +129,19 @@ CompletableFuture> getConfi @JsonRequest("aws/chat/mcpServerClick") CompletableFuture mcpServerClick(Object params); + + @JsonRequest("aws/chat/listRules") + CompletableFuture listRules(Object params); + + @JsonRequest("aws/chat/ruleClick") + CompletableFuture ruleClick(Object params); + + @JsonNotification("aws/chat/pinnedContextAdd") + void pinnedContextAdd(Object params); + + @JsonNotification("aws/chat/pinnedContextRemove") + void pinnedContextRemove(Object params); + + @JsonNotification("aws/chat/activeEditorChanged") + void activeEditorChanged(Object params); } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspServerBuilder.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspServerBuilder.java index e351ba861..c07f37d1b 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspServerBuilder.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspServerBuilder.java @@ -56,6 +56,7 @@ private Map getInitializationOptions(final ClientMetadata metada qOptions.put("developerProfiles", true); qOptions.put("customizationsWithMetadata", true); qOptions.put("mcp", true); + qOptions.put("pinnedContextEnabled", true); qOptions.put("modelSelection", true); awsClientCapabilities.put("q", qOptions); Map window = new HashMap<>(); diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/editor/ActiveEditorChangeListener.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/editor/ActiveEditorChangeListener.java new file mode 100644 index 000000000..24a9878b2 --- /dev/null +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/editor/ActiveEditorChangeListener.java @@ -0,0 +1,120 @@ +// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.eclipse.amazonq.lsp.editor; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ScheduledFuture; + +import org.eclipse.swt.widgets.Display; +import org.eclipse.ui.IPartListener2; +import org.eclipse.ui.IWorkbenchPartReference; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.texteditor.ITextEditor; + +import software.aws.toolkits.eclipse.amazonq.plugin.Activator; +import software.aws.toolkits.eclipse.amazonq.util.QEclipseEditorUtils; +import software.aws.toolkits.eclipse.amazonq.util.ThreadingUtils; + +public final class ActiveEditorChangeListener implements IPartListener2 { + private static ActiveEditorChangeListener instance; + private static final long DEBOUNCE_DELAY_MS = 100L; + private ScheduledFuture debounceTask; + private IWorkbenchWindow registeredWindow; + + private ActiveEditorChangeListener() { } + + public static ActiveEditorChangeListener getInstance() { + if (instance == null) { + instance = new ActiveEditorChangeListener(); + } + return instance; + } + + public void initialize() { + Display.getDefault().asyncExec(() -> { + try { + registeredWindow = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); + if (registeredWindow != null) { + registeredWindow.getPartService().addPartListener(this); + } + } catch (Exception e) { + Activator.getLogger().error("Failed to initialize ActiveEditorChangeListener", e); + } + }); + } + + public void stop() { + if (debounceTask != null) { + debounceTask.cancel(true); + } + try { + if (registeredWindow != null) { + registeredWindow.getPartService().removePartListener(this); + } + } catch (Exception e) { + Activator.getLogger().error("Error stopping ActiveEditorChangeListener", e); + } + } + + @Override + public void partActivated(final IWorkbenchPartReference partRef) { + if (partRef.getPart(false) instanceof ITextEditor) { + ITextEditor editor = (ITextEditor) partRef.getPart(false); + handleEditorChange(editor); + } + } + + @Override + public void partClosed(final IWorkbenchPartReference partRef) { + if (partRef.getPart(false) instanceof ITextEditor) { + handleEditorChange(null); + } + } + + private void handleEditorChange(final ITextEditor editor) { + // Cancel any pending notification + if (debounceTask != null) { + debounceTask.cancel(false); + } + + // Schedule a new notification after the debounce period + debounceTask = (ScheduledFuture) ThreadingUtils.scheduleAsyncTaskWithDelay(() -> { + Display.getDefault().syncExec(() -> { + try { + Map params = createActiveEditorParams(editor); + var lspServer = Activator.getLspProvider().getAmazonQServer().get(); + lspServer.activeEditorChanged(params); + } catch (Exception e) { + Activator.getLogger().error("Failed to send active editor changed notification", e); + } + }); + }, DEBOUNCE_DELAY_MS); + } + + private Map createActiveEditorParams(final ITextEditor editor) { + Map params = new HashMap<>(); + if (editor != null) { + Optional fileUri = QEclipseEditorUtils.getOpenFileUri(editor.getEditorInput()); + if (fileUri.isPresent()) { + Map textDocument = new HashMap<>(); + textDocument.put("uri", fileUri.get()); + params.put("textDocument", textDocument); + QEclipseEditorUtils.getSelectionRange(editor).ifPresent(range -> { + Map cursorState = new HashMap<>(); + cursorState.put("range", range); + params.put("cursorState", cursorState); + }); + } + } else { + // Editor is null (closed), send null values + params.put("textDocument", null); + params.put("cursorState", null); + } + + return params; + } +} diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/plugin/Activator.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/plugin/Activator.java index 9fc1dbb60..233bfb2a5 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/plugin/Activator.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/plugin/Activator.java @@ -12,6 +12,7 @@ import software.aws.toolkits.eclipse.amazonq.inlineChat.InlineChatEditorListener; import software.aws.toolkits.eclipse.amazonq.lsp.auth.DefaultLoginService; import software.aws.toolkits.eclipse.amazonq.lsp.auth.LoginService; +import software.aws.toolkits.eclipse.amazonq.lsp.editor.ActiveEditorChangeListener; import software.aws.toolkits.eclipse.amazonq.providers.browser.AmazonQBrowserProvider; import software.aws.toolkits.eclipse.amazonq.providers.lsp.LspProvider; import software.aws.toolkits.eclipse.amazonq.providers.lsp.LspProviderImpl; @@ -39,6 +40,7 @@ public class Activator extends AbstractUIPlugin { private static ViewRouter viewRouter = ViewRouter.builder().build(); private final InlineChatEditorListener editorListener; private static WorkspaceChangeListener workspaceListener = WorkspaceChangeListener.getInstance(); + private static ActiveEditorChangeListener activeEditorListener = ActiveEditorChangeListener.getInstance(); public Activator() { super(); @@ -56,6 +58,7 @@ public Activator() { editorListener = InlineChatEditorListener.getInstance(); editorListener.initialize(); workspaceListener.start(); + activeEditorListener.initialize(); } @Override @@ -64,6 +67,7 @@ public final void stop(final BundleContext context) throws Exception { super.stop(context); plugin = null; workspaceListener.stop(); + activeEditorListener.stop(); ThreadingUtils.shutdown(); } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/providers/assets/ChatWebViewAssetProvider.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/providers/assets/ChatWebViewAssetProvider.java index 39fce82f2..9ed96da95 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/providers/assets/ChatWebViewAssetProvider.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/providers/assets/ChatWebViewAssetProvider.java @@ -144,7 +144,7 @@ private String generateJS(final String jsEntrypoint) { var disclaimerAcknowledged = Activator.getPluginStore().get(PluginStoreKeys.CHAT_DISCLAIMER_ACKNOWLEDGED); var pairProgrammingAcknowledged = Activator.getPluginStore().get(PluginStoreKeys.PAIR_PROGRAMMING_ACKNOWLEDGED); return String.format(""" - + "; + } + return ""; } private String generateJS(final String jsEntrypoint) { @@ -151,22 +197,11 @@ private String generateJS(final String jsEntrypoint) { waitForFunction('ideCommand') .then(() => { function refreshUi() { - document.querySelectorAll('[class*="mynah-ui-icon-"]').forEach(icon => { - icon.style.transform = 'none'; - void icon.offsetHeight; - icon.style.transform = 'translateZ(0)'; - }); document.querySelectorAll('[class*="mynah-chat-wrapper"]').forEach(wrapper => { wrapper.style.overflow = 'visible'; }); } - document.addEventListener('visibilitychange', () => { - if (document.visibilityState === 'visible') { - refreshUi(); - } - }); - const mynahUI = amazonQChat.createChat({ postMessage: (message) => { ideCommand(JSON.stringify(message)); From 88d5907441c69c64deb83843ebac81b73400ce87 Mon Sep 17 00:00:00 2001 From: Shruti Sinha <44882001+shruti0085@users.noreply.github.com> Date: Tue, 26 Aug 2025 11:17:20 -0700 Subject: [PATCH 15/40] Bump version to 2.5.0 (#507) Bump version to 2.5.0 similar to #502. Created using the update-version script --- feature/feature.xml | 4 ++-- feature/pom.xml | 2 +- plugin/META-INF/MANIFEST.MF | 2 +- plugin/pom.xml | 2 +- pom.xml | 2 +- telemetry/pom.xml | 2 +- updatesite/category.xml | 2 +- updatesite/pom.xml | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/feature/feature.xml b/feature/feature.xml index 9ebd98654..cdb6a69d1 100644 --- a/feature/feature.xml +++ b/feature/feature.xml @@ -2,7 +2,7 @@ + version="2.5.0.qualifier"> Amazon Q Developer helps users build faster across the entire software development lifecycle by providing tailored responses and code recommendations that conform to their team's internal libraries, proprietary algorithmic techniques, and enterprise code style. @@ -198,6 +198,6 @@ https://github.com/aws/amazon-q-eclipse/blob/main/attribution.xml id="amazon-q-eclipse" download-size="11000" install-size="0" - version="2.4.0.qualifier" + version="2.5.0.qualifier" unpack="false"/> diff --git a/feature/pom.xml b/feature/pom.xml index 3a666f345..d95396bdb 100644 --- a/feature/pom.xml +++ b/feature/pom.xml @@ -6,7 +6,7 @@ software.aws.toolkits.eclipse amazon-q-eclipse-group - 2.4.0-SNAPSHOT + 2.5.0-SNAPSHOT ../ diff --git a/plugin/META-INF/MANIFEST.MF b/plugin/META-INF/MANIFEST.MF index a4cdafe94..33012d0b2 100644 --- a/plugin/META-INF/MANIFEST.MF +++ b/plugin/META-INF/MANIFEST.MF @@ -4,7 +4,7 @@ Bundle-Name: Amazon Q for Eclipse Bundle-Provider: Amazon Web Services Bundle-RequiredExecutionEnvironment: JavaSE-17 Bundle-SymbolicName: amazon-q-eclipse;singleton:=true -Bundle-Version: 2.4.0.qualifier +Bundle-Version: 2.5.0.qualifier Automatic-Module-Name: amazon.q.eclipse Bundle-ActivationPolicy: lazy Bundle-Activator: software.aws.toolkits.eclipse.amazonq.plugin.Activator diff --git a/plugin/pom.xml b/plugin/pom.xml index 4ba0d11b1..400d7422f 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -6,7 +6,7 @@ software.aws.toolkits.eclipse amazon-q-eclipse-group - 2.4.0-SNAPSHOT + 2.5.0-SNAPSHOT ../ diff --git a/pom.xml b/pom.xml index 5d2de98cc..bc478310a 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ software.aws.toolkits.eclipse amazon-q-eclipse-group - 2.4.0-SNAPSHOT + 2.5.0-SNAPSHOT pom diff --git a/telemetry/pom.xml b/telemetry/pom.xml index c7651e2b5..d4123ec62 100644 --- a/telemetry/pom.xml +++ b/telemetry/pom.xml @@ -6,7 +6,7 @@ software.aws.toolkits.eclipse amazon-q-eclipse-group - 2.4.0-SNAPSHOT + 2.5.0-SNAPSHOT ../ diff --git a/updatesite/category.xml b/updatesite/category.xml index c590fb654..7ec9042d2 100644 --- a/updatesite/category.xml +++ b/updatesite/category.xml @@ -1,6 +1,6 @@ - + diff --git a/updatesite/pom.xml b/updatesite/pom.xml index b78ce3a83..2de4a79b1 100644 --- a/updatesite/pom.xml +++ b/updatesite/pom.xml @@ -6,7 +6,7 @@ software.aws.toolkits.eclipse amazon-q-eclipse-group - 2.4.0-SNAPSHOT + 2.5.0-SNAPSHOT ../ From 0e7e5bd631e9a44b47ae4f9a481a42dbc08d2b76 Mon Sep 17 00:00:00 2001 From: Shruti Sinha <44882001+shruti0085@users.noreply.github.com> Date: Wed, 27 Aug 2025 09:12:00 -0700 Subject: [PATCH 16/40] feat: Add ability to override manifest urls (#509) Adds ability to override manifest urls when local testing with the env variable Q_MANIFEST. When set, this url will be used to fetch and launch the language server from. --- .../providers/lsp/LspManagerProvider.java | 17 +++- .../providers/LspManagerProviderTest.java | 81 +++++++++++++++++++ 2 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 plugin/tst/software/aws/toolkits/eclipse/amazonq/providers/LspManagerProviderTest.java diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/providers/lsp/LspManagerProvider.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/providers/lsp/LspManagerProvider.java index 3d78c422f..8bee44399 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/providers/lsp/LspManagerProvider.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/providers/lsp/LspManagerProvider.java @@ -3,9 +3,12 @@ package software.aws.toolkits.eclipse.amazonq.providers.lsp; +import org.apache.commons.lang3.StringUtils; + import software.aws.toolkits.eclipse.amazonq.lsp.manager.DefaultLspManager; import software.aws.toolkits.eclipse.amazonq.lsp.manager.LspConstants; import software.aws.toolkits.eclipse.amazonq.lsp.manager.LspManager; +import software.aws.toolkits.eclipse.amazonq.plugin.Activator; public final class LspManagerProvider { @@ -29,8 +32,20 @@ public static LspManager getInstance() { private static LspManager createLspManager() { return DefaultLspManager.builder() .withLspExecutablePrefix(LspConstants.CW_LSP_FILENAME) - .withManifestUrl(LspConstants.CW_MANIFEST_URL) + .withManifestUrl(getManifestUrl()) .build(); } + public static String getManifestUrl() { + var manifestUrl = getEnvironmentVariable("Q_MANIFEST"); + if (StringUtils.isBlank(manifestUrl) || !manifestUrl.endsWith("manifest.json")) { + return LspConstants.CW_MANIFEST_URL; + } + Activator.getLogger().info("Using manifest url override: " + manifestUrl); + return manifestUrl; + } + + public static String getEnvironmentVariable(final String variableName) { + return System.getenv(variableName); + } } diff --git a/plugin/tst/software/aws/toolkits/eclipse/amazonq/providers/LspManagerProviderTest.java b/plugin/tst/software/aws/toolkits/eclipse/amazonq/providers/LspManagerProviderTest.java new file mode 100644 index 000000000..2d5935139 --- /dev/null +++ b/plugin/tst/software/aws/toolkits/eclipse/amazonq/providers/LspManagerProviderTest.java @@ -0,0 +1,81 @@ +// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.eclipse.amazonq.providers; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mockStatic; +import java.util.stream.Stream; + +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.MockedStatic; + +import software.aws.toolkits.eclipse.amazonq.extensions.implementation.ActivatorStaticMockExtension; +import software.aws.toolkits.eclipse.amazonq.lsp.manager.LspConstants; +import software.aws.toolkits.eclipse.amazonq.providers.lsp.LspManagerProvider; + +class LspManagerProviderTest { + + @RegisterExtension + private static ActivatorStaticMockExtension activatorStaticMockExtension = new ActivatorStaticMockExtension(); + + private static Stream validManifestOverride() { + return Stream.of( + Arguments.of("https://custom.example.com/manifest.json", "https://custom.example.com/manifest.json"), + Arguments.of("https://another.domain.com/path/manifest.json", "https://another.domain.com/path/manifest.json") + ); + } + + @ParameterizedTest + @MethodSource("validManifestOverride") + void testGetManifestUrlWithValidOverride(final String envValue, final String expectedUrl) { + try (MockedStatic mockedProvider = mockStatic(LspManagerProvider.class)) { + mockedProvider.when(() -> LspManagerProvider.getEnvironmentVariable("Q_MANIFEST")).thenReturn(envValue); + mockedProvider.when(() -> LspManagerProvider.getManifestUrl()).thenCallRealMethod(); + String manifestUrl = LspManagerProvider.getManifestUrl(); + assertEquals(expectedUrl, manifestUrl); + } + } + + private static Stream noManifestOverride() { + return Stream.of( + Arguments.of((String) null), + Arguments.of(""), + Arguments.of(" ") + ); + } + + @ParameterizedTest + @MethodSource("noManifestOverride") + void testGetManifestUrlWithNoOverride(final String envValue) { + try (MockedStatic mockedProvider = mockStatic(LspManagerProvider.class)) { + mockedProvider.when(() -> LspManagerProvider.getEnvironmentVariable("Q_MANIFEST")).thenReturn(envValue); + mockedProvider.when(() -> LspManagerProvider.getManifestUrl()).thenCallRealMethod(); + String manifestUrl = LspManagerProvider.getManifestUrl(); + assertEquals(LspConstants.CW_MANIFEST_URL, manifestUrl); + } + } + + private static Stream invalidManifestOverride() { + return Stream.of( + Arguments.of("https://custom.example.com/invalid.txt"), + Arguments.of("not-a-url"), + Arguments.of("https://example.com/file.xml"), + Arguments.of("https://example.com/manifest") + ); + } + + @ParameterizedTest + @MethodSource("invalidManifestOverride") + void testGetManifestUrlWithInvalidOverride(final String envValue) { + try (MockedStatic mockedProvider = mockStatic(LspManagerProvider.class)) { + mockedProvider.when(() -> LspManagerProvider.getEnvironmentVariable("Q_MANIFEST")).thenReturn(envValue); + mockedProvider.when(() -> LspManagerProvider.getManifestUrl()).thenCallRealMethod(); + String manifestUrl = LspManagerProvider.getManifestUrl(); + assertEquals(LspConstants.CW_MANIFEST_URL, manifestUrl); + } + } +} From 0039d6a7e3034724cca6b2731226c3cc5d06a982 Mon Sep 17 00:00:00 2001 From: Shruti Sinha <44882001+shruti0085@users.noreply.github.com> Date: Wed, 27 Aug 2025 10:49:19 -0700 Subject: [PATCH 17/40] Fix tests failure on Windows due to incorrect path (#508) Fixes test failures when run locally on Windows due to incorrect unix hardcoded path. --- .../QLspConnectionProviderTest.java | 38 ++++++++++++++----- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/plugin/tst/software/aws/toolkits/eclipse/amazonq/lsp/connection/QLspConnectionProviderTest.java b/plugin/tst/software/aws/toolkits/eclipse/amazonq/lsp/connection/QLspConnectionProviderTest.java index 31983e1b0..d0a65460a 100644 --- a/plugin/tst/software/aws/toolkits/eclipse/amazonq/lsp/connection/QLspConnectionProviderTest.java +++ b/plugin/tst/software/aws/toolkits/eclipse/amazonq/lsp/connection/QLspConnectionProviderTest.java @@ -14,6 +14,7 @@ import java.io.IOException; import java.io.OutputStream; +import java.nio.file.Paths; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -23,6 +24,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.api.io.TempDir; import org.mockito.MockedStatic; import org.mockito.Mockito; @@ -42,6 +44,9 @@ public final class QLspConnectionProviderTest { + @TempDir + private java.nio.file.Path tempDir; + @RegisterExtension private static ActivatorStaticMockExtension activatorStaticMockExtension = new ActivatorStaticMockExtension(); @@ -98,15 +103,18 @@ void tearDownMocks() { @Test void testConstructorInitializesCorrectly() throws IOException { + String testDir = tempDir.toString(); + String serverPath = Paths.get(testDir, "server.js").toString(); + LspInstallResult lspInstallResultMock = lspManagerProviderStaticMockExtension.getMock(LspInstallResult.class); - Mockito.when(lspInstallResultMock.getServerDirectory()).thenReturn("/test/dir"); + Mockito.when(lspInstallResultMock.getServerDirectory()).thenReturn(testDir); Mockito.when(lspInstallResultMock.getServerCommand()).thenReturn("server.js"); Mockito.when(lspInstallResultMock.getServerCommandArgs()).thenReturn("--test-arg"); var provider = new QLspConnectionProvider(); List expectedCommands = List.of( - "/test/dir/server.js", + serverPath, "--test-arg", "--stdio", "--set-credentials-encryption-key" @@ -114,7 +122,7 @@ void testConstructorInitializesCorrectly() throws IOException { TestProcessConnectionProvider testProcessConnectionProvider = new TestProcessConnectionProvider( expectedCommands, - "/test/dir" + testDir ); assertTrue(testProcessConnectionProvider.equals(provider)); @@ -122,8 +130,10 @@ void testConstructorInitializesCorrectly() throws IOException { @Test void testAddEnvironmentVariablesWithoutProxy() throws IOException { + String testDir = tempDir.toString(); + LspInstallResult lspInstallResultMock = lspManagerProviderStaticMockExtension.getMock(LspInstallResult.class); - Mockito.when(lspInstallResultMock.getServerDirectory()).thenReturn("/test/dir"); + Mockito.when(lspInstallResultMock.getServerDirectory()).thenReturn(testDir); Mockito.when(lspInstallResultMock.getServerCommand()).thenReturn("server.js"); Mockito.when(lspInstallResultMock.getServerCommandArgs()).thenReturn(""); @@ -142,8 +152,10 @@ void testAddEnvironmentVariablesWithoutProxy() throws IOException { @Test void testAddEnvironmentVariablesWithProxy() throws IOException { + String testDir = tempDir.toString(); + LspInstallResult lspInstallResultMock = lspManagerProviderStaticMockExtension.getMock(LspInstallResult.class); - Mockito.when(lspInstallResultMock.getServerDirectory()).thenReturn("/test/dir"); + Mockito.when(lspInstallResultMock.getServerDirectory()).thenReturn(testDir); Mockito.when(lspInstallResultMock.getServerCommand()).thenReturn("server.js"); Mockito.when(lspInstallResultMock.getServerCommandArgs()).thenReturn(""); @@ -163,8 +175,10 @@ void testAddEnvironmentVariablesWithProxy() throws IOException { @Test void testStartInitializesEncryptedCommunication() throws IOException { + String testDir = tempDir.toString(); + LspInstallResult lspInstallResultMock = lspManagerProviderStaticMockExtension.getMock(LspInstallResult.class); - Mockito.when(lspInstallResultMock.getServerDirectory()).thenReturn("/test/dir"); + Mockito.when(lspInstallResultMock.getServerDirectory()).thenReturn(testDir); Mockito.when(lspInstallResultMock.getServerCommand()).thenReturn("server.js"); Mockito.when(lspInstallResultMock.getServerCommandArgs()).thenReturn(""); @@ -187,8 +201,10 @@ void testStartInitializesEncryptedCommunication() throws IOException { @Test void testStartLogsErrorOnException() throws IOException { + String testDir = tempDir.toString(); + LspInstallResult lspInstallResultMock = lspManagerProviderStaticMockExtension.getMock(LspInstallResult.class); - Mockito.when(lspInstallResultMock.getServerDirectory()).thenReturn("/test/dir"); + Mockito.when(lspInstallResultMock.getServerDirectory()).thenReturn(testDir); Mockito.when(lspInstallResultMock.getServerCommand()).thenReturn("server.js"); Mockito.when(lspInstallResultMock.getServerCommandArgs()).thenReturn(""); @@ -215,8 +231,10 @@ void testStartLogsErrorOnException() throws IOException { @Test void testCertInjectionWithUserPreference() throws IOException { + String testDir = tempDir.toString(); + LspInstallResult lspInstallResultMock = lspManagerProviderStaticMockExtension.getMock(LspInstallResult.class); - Mockito.when(lspInstallResultMock.getServerDirectory()).thenReturn("/test/dir"); + Mockito.when(lspInstallResultMock.getServerDirectory()).thenReturn(testDir); Mockito.when(lspInstallResultMock.getServerCommand()).thenReturn("server.js"); Mockito.when(lspInstallResultMock.getServerCommandArgs()).thenReturn(""); @@ -235,8 +253,10 @@ void testCertInjectionWithUserPreference() throws IOException { @Test void testNoCertInjectionWhenNoCertsFound() throws IOException { + String testDir = tempDir.toString(); + LspInstallResult lspInstallResultMock = lspManagerProviderStaticMockExtension.getMock(LspInstallResult.class); - Mockito.when(lspInstallResultMock.getServerDirectory()).thenReturn("/test/dir"); + Mockito.when(lspInstallResultMock.getServerDirectory()).thenReturn(testDir); Mockito.when(lspInstallResultMock.getServerCommand()).thenReturn("server.js"); Mockito.when(lspInstallResultMock.getServerCommandArgs()).thenReturn(""); From cf819deb278e04d9a3b9d2abd643ec517ca341c3 Mon Sep 17 00:00:00 2001 From: Adnan Khan Date: Fri, 7 Nov 2025 16:51:27 -0500 Subject: [PATCH 18/40] Scope down GitHub token permissions for maven.yml (#513) --- .github/workflows/maven.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 88a892a39..595fe0030 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -14,6 +14,10 @@ on: pull_request: branches: [ "main", "feature/*" ] + +permissions: + contents: read + jobs: build: From 7bf189b0195c5f1ac2a72a0459f95a61ce6c7542 Mon Sep 17 00:00:00 2001 From: Jonathan Breedlove Date: Mon, 10 Nov 2025 10:57:30 -0800 Subject: [PATCH 19/40] Serialize code completion requests to LSP (#514) --- .../amazonq/util/QInvocationSession.java | 303 ++++++++++-------- .../amazonq/util/QInvocationSessionTest.java | 78 ++--- 2 files changed, 192 insertions(+), 189 deletions(-) diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/util/QInvocationSession.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/util/QInvocationSession.java index bbe20a821..656d45958 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/util/QInvocationSession.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/util/QInvocationSession.java @@ -28,10 +28,13 @@ import java.util.Map; import java.util.Optional; import java.util.UUID; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.stream.Collectors; import java.util.function.Consumer; @@ -44,6 +47,10 @@ public final class QInvocationSession extends QResource { // Static variable to hold the single instance private static QInvocationSession instance; + private static final ExecutorService SESSION_PROCESSOR = Executors.newSingleThreadExecutor(); + private static volatile UUID pendingRequestId = null; + private static final long REQUEST_TIMEOUT_SECONDS = 5; + private volatile QInvocationSessionState state = QInvocationSessionState.INACTIVE; private CaretMovementReason caretMovementReason = CaretMovementReason.UNEXAMINED; private boolean suggestionAccepted = false; @@ -66,7 +73,6 @@ public final class QInvocationSession extends QResource { private QInlineTerminationListener terminationListener = null; private final boolean isTabOnly = false; private Consumer unsetVerticalIndent; - private final ConcurrentHashMap> unresolvedTasks = new ConcurrentHashMap<>(); private Runnable changeStatusToQuerying; private Runnable changeStatusToIdle; private Runnable changeStatusToPreviewing; @@ -176,121 +182,152 @@ public void invoke() { } private synchronized void queryAsync(final InlineCompletionParams params, final int invocationOffset) { - var uuid = UUID.randomUUID(); - Activator.getLogger().info(uuid + " queried made at " + invocationOffset); - var future = ThreadingUtils.executeAsyncTaskAndReturnFuture(() -> { + UUID requestId = UUID.randomUUID(); + pendingRequestId = requestId; + + SESSION_PROCESSOR.submit(() -> { try { - var session = QInvocationSession.getInstance(); - List newSuggestions = new ArrayList(); - List sessionId = new ArrayList(); - long requestInvocation = System.currentTimeMillis(); - - // request lsp for suggestions - var response = Activator.getLspProvider().getAmazonQServer().get() - .inlineCompletionWithReferences(params); - response.thenAccept(result -> { - sessionId.add(result.getSessionId()); - var suggestions = result.getItems().parallelStream().map(item -> { - if (isTabOnly) { - String sanitizedText = replaceSpacesWithTabs(item.getInsertText(), tabSize); - item.setInsertText(sanitizedText); - } - return item; - }).collect(Collectors.toList()); - newSuggestions.addAll(suggestions); - }).get(); - - Display.getDefault().asyncExec(() -> { - unresolvedTasks.remove(uuid); - - if (newSuggestions == null || newSuggestions.isEmpty() || sessionId.get(0) == null || sessionId.get(0).isEmpty()) { - if (!session.isPreviewingSuggestions()) { - end(); - } - Activator.getLogger().info(uuid + " returned with no result."); - if (params.getContext().getTriggerKind() == InlineCompletionTriggerKind.Invoke) { - Display display = Display.getDefault(); - String message = "Q returned no suggestions"; - QEclipseEditorUtils.showToast(message, display, 2000); - } - return; - } else { - Activator.getLogger().info(uuid + " returned with " + newSuggestions.size() + " results."); + if (requestId.equals(pendingRequestId)) { + CompletableFuture task = CompletableFuture.runAsync(() -> + processRequest(params, invocationOffset, requestId)); + try { + task.get(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS); + } catch (TimeoutException e) { + Activator.getLogger().warn(requestId + " inline completion request task timed out after " + + REQUEST_TIMEOUT_SECONDS + " seconds"); } + } else { + Activator.getLogger().info(requestId + " skipped as newer inline completion request was queued"); + } + } catch (InterruptedException e) { + Activator.getLogger().info(requestId + " inline completion request interrupted"); + } catch (ExecutionException e) { + Activator.getLogger().error("Error executing inline completion request " + requestId, e); + } + }); + } - suggestionsContext.setSessionId(sessionId.get(0)); - suggestionsContext.setRequestedAtEpoch(requestInvocation); - suggestionsContext.getDetails() - .addAll(newSuggestions.stream().map(QSuggestionContext::new).collect(Collectors.toList())); - - initializeSuggestionCompletionResults(); - - // If the caret positions has moved on from the invocation offset, we need to - // see if there exists in the suggestions fetched - // one more suggestions that qualify for what has been typed since the - // invocation. - // Note that we should not remove the ones that have been disqualified by the - // content typed since the user might still want to explore them. - int currentIdxInSuggestion = 0; - boolean hasAMatch = false; - var viewer = session.getViewer(); - if (viewer == null || viewer.getTextWidget() == null || viewer.getTextWidget().getCaretOffset() < invocationOffset) { - // discard all suggestions since the current caret is behind request position - updateCompletionStates(new ArrayList()); - end(); - return; + private void processRequest(final InlineCompletionParams params, final int invocationOffset, final UUID requestId) { + try { + Activator.getLogger().info(requestId + " inline query made at offset " + invocationOffset); + + List newSuggestions = new ArrayList<>(); + String sessionId; + long requestInvocation = System.currentTimeMillis(); + + // Execute the LSP query + var response = Activator.getLspProvider().getAmazonQServer().get() + .inlineCompletionWithReferences(params).get(); + + sessionId = response.getSessionId(); + var suggestions = response.getItems().parallelStream() + .map(item -> { + if (isTabOnly) { + String sanitizedText = replaceSpacesWithTabs(item.getInsertText(), tabSize); + item.setInsertText(sanitizedText); } + return item; + }) + .collect(Collectors.toList()); + newSuggestions.addAll(suggestions); + + Display.getDefault().syncExec(() -> { + if (requestId.equals(pendingRequestId)) { + handleQueryResults(newSuggestions, sessionId, requestInvocation, + new RequestContext(requestId, invocationOffset, params)); + } else { + Activator.getLogger().info(requestId + " skipped rendering as newer inline completion request queued"); + } + }); + + } catch (InterruptedException e) { + Activator.getLogger().info(requestId + " inline completion request interrupted"); + } catch (Exception e) { + Activator.getLogger().error("Error processing inline completion request " + requestId, e); + } + } - if (viewer != null && viewer.getTextWidget() != null && viewer.getTextWidget().getCaretOffset() > invocationOffset) { - var widget = viewer.getTextWidget(); - int currentOffset = widget.getCaretOffset(); - String prefix = widget.getTextRange(invocationOffset, currentOffset - invocationOffset); - // Computes the typed prefix and typeahead length from when user invocation happened to - // before suggestions are first shown in UI - // Note: This computation may change later on but follows the same pattern for consistency across IDEs for now - session.initialTypeaheadLength = Optional.of(prefix.length()); - - for (int i = 0; i < newSuggestions.size(); i++) { - if (newSuggestions.get(i).getInsertText().startsWith(prefix)) { - currentIdxInSuggestion = i; - hasAMatch = true; - break; - } - } - // indicates that typeahead prefix does not match any suggestions - if (invocationOffset != currentOffset && !hasAMatch) { - // all suggestions filtered out, mark them as discarded - updateCompletionStates(new ArrayList()); - end(); - return; - } - - // if typeahead exists, mark all suggestions except for current suggestion index with match as discarded - // As of Jan 25, current logic blocks users from toggling between suggestions when a typeahead exists in QToggleSuggestionsHandler - if (invocationOffset != currentOffset && hasAMatch) { - var currentSuggestion = suggestionsContext.getDetails().get(currentIdxInSuggestion); - var filteredSuggestions = List.of(currentSuggestion.getInlineCompletionItem().getItemId()); - updateCompletionStates(filteredSuggestions); - } - } + private void handleQueryResults(final List newSuggestions, + final String sessionId, + final long requestInvocation, + final RequestContext task) { + if (newSuggestions == null || newSuggestions.isEmpty() || sessionId == null || sessionId.isEmpty()) { + if (!isPreviewingSuggestions()) { + end(); + } + Activator.getLogger().info(task.getUuid() + " returned with no result."); + if (task.getParams().getContext().getTriggerKind() == InlineCompletionTriggerKind.Invoke) { + Display display = Display.getDefault(); + String message = "Q returned no suggestions"; + QEclipseEditorUtils.showToast(message, display, 2000); + } + return; + } - session.invocationOffset = invocationOffset; - suggestionsContext.setCurrentIndex(currentIdxInSuggestion); + // Check if current caret position matches the task's invocation offset + var viewer = getViewer(); + if (viewer == null || viewer.getTextWidget() == null) { + end(); + return; + } - session.transitionToPreviewingState(); - attachListeners(); - session.primeListeners(); - session.getViewer().getTextWidget().redraw(); - }); - } catch (InterruptedException e) { - Activator.getLogger().error("Inline completion interrupted", e); - } catch (Exception e) { - Activator.getLogger().error("Error executing inline completion", e); + int currentCaretOffset = viewer.getTextWidget().getCaretOffset(); + if (currentCaretOffset != task.getInvocationOffset()) { + Activator.getLogger().info(task.getUuid() + + " skipped rendering as caret position changed from " + + task.getInvocationOffset() + " to " + currentCaretOffset); + return; + } + + Activator.getLogger().info(task.getUuid() + " returned with " + newSuggestions.size() + " results."); + + suggestionsContext.setSessionId(sessionId); + suggestionsContext.setRequestedAtEpoch(requestInvocation); + suggestionsContext.getDetails() + .addAll(newSuggestions.stream().map(QSuggestionContext::new).collect(Collectors.toList())); + + initializeSuggestionCompletionResults(); + + // Handle typeahead if it exists + int currentIdxInSuggestion = 0; + boolean hasAMatch = false; + + if (currentCaretOffset > task.getInvocationOffset()) { + var widget = viewer.getTextWidget(); + String prefix = widget.getTextRange(task.getInvocationOffset(), + currentCaretOffset - task.getInvocationOffset()); + // Computes the typed prefix and typeahead length from when user invocation happened to + // before suggestions are first shown in UI + initialTypeaheadLength = Optional.of(prefix.length()); + + for (int i = 0; i < newSuggestions.size(); i++) { + if (newSuggestions.get(i).getInsertText().startsWith(prefix)) { + currentIdxInSuggestion = i; + hasAMatch = true; + break; + } + } + // indicates that typeahead prefix does not match any suggestions + if (!hasAMatch) { + // all suggestions filtered out, mark them as discarded + updateCompletionStates(new ArrayList()); + end(); + return; } - }); - unresolvedTasks.put(uuid, future); - } + // if typeahead exists, mark all suggestions except for current suggestion index with match as discarded + var currentSuggestion = suggestionsContext.getDetails().get(currentIdxInSuggestion); + var filteredSuggestions = List.of(currentSuggestion.getInlineCompletionItem().getItemId()); + updateCompletionStates(filteredSuggestions); + } + + invocationOffset = task.getInvocationOffset(); + suggestionsContext.setCurrentIndex(currentIdxInSuggestion); + transitionToPreviewingState(); + attachListeners(); + primeListeners(); + getViewer().getTextWidget().redraw(); + } /* * Updates completion state of each suggestion in the `suggestionCompletionResult` map, given the updated filtered suggestion list @@ -351,7 +388,7 @@ public void markSuggestionAsSeen() { // Method to end the session public void end() { - if (isActive() && unresolvedTasks.isEmpty()) { + if (isActive()) { if (state == QInvocationSessionState.SUGGESTION_PREVIEWING) { int lastKnownLine = getLastKnownLine(); unsetVerticalIndent(lastKnownLine + 1); @@ -570,20 +607,6 @@ public int getLastKnownLine() { return ((QInlineCaretListener) caretListener).getLastKnownLine(); } - public void awaitAllUnresolvedTasks() throws ExecutionException { - List> tasks = unresolvedTasks.values().stream().toList(); - for (Future future : tasks) { - try { - future.get(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } catch (ExecutionException e) { - // Propagate the execution exception - throw e; - } - } - } - public void assignQueryingCallback(final Runnable runnable) { changeStatusToQuerying = runnable; } @@ -659,7 +682,6 @@ private void resetSessionResultParams() { initialTypeaheadLength = Optional.empty(); } - // Additional methods for the session can be added here @Override public void dispose() { var widget = viewer.getTextWidget(); @@ -675,15 +697,6 @@ public void dispose() { inlineTextFont = null; inlineTextFontBold = null; caretMovementReason = CaretMovementReason.UNEXAMINED; - unresolvedTasks.forEach((uuid, task) -> { - boolean cancelled = task.cancel(true); - if (cancelled) { - Activator.getLogger().info(uuid + " cancelled."); - } else { - Activator.getLogger().error(uuid + " failed to cancel."); - } - }); - unresolvedTasks.clear(); if (inputListener != null) { inputListener.beforeRemoval(); widget.removeVerifyKeyListener(inputListener); @@ -714,4 +727,28 @@ public void dispose() { viewer = null; suggestionAccepted = false; } + + private static final class RequestContext { + private final UUID uuid; + private final int invocationOffset; + private final InlineCompletionParams params; + + RequestContext(final UUID uuid, final int invocationOffset, final InlineCompletionParams params) { + this.uuid = uuid; + this.invocationOffset = invocationOffset; + this.params = params; + } + + public UUID getUuid() { + return uuid; + } + + public int getInvocationOffset() { + return invocationOffset; + } + + public InlineCompletionParams getParams() { + return params; + } + } } diff --git a/plugin/tst/software/aws/toolkits/eclipse/amazonq/util/QInvocationSessionTest.java b/plugin/tst/software/aws/toolkits/eclipse/amazonq/util/QInvocationSessionTest.java index 9c4e06621..3d329c4b5 100644 --- a/plugin/tst/software/aws/toolkits/eclipse/amazonq/util/QInvocationSessionTest.java +++ b/plugin/tst/software/aws/toolkits/eclipse/amazonq/util/QInvocationSessionTest.java @@ -17,13 +17,8 @@ import java.util.Collections; import java.util.List; import java.util.Set; -import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.BlockingQueue; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; import org.eclipse.core.runtime.Platform; import org.eclipse.jface.text.ITextViewer; @@ -186,71 +181,42 @@ void testSessionStartWhileInlineChatIsActive() throws ExecutionException { // - Session should not be ended if there are still requests in flight void testSessionEnd() throws InterruptedException, ExecutionException { threadingUtilsMock = mockStatic(ThreadingUtils.class); + + // Set up synchronous execution of our async tasks threadingUtilsMock.when(() -> ThreadingUtils.executeAsyncTaskAndReturnFuture(any(Runnable.class))) - .thenAnswer(new Answer>() { - @Override - public Future answer(final InvocationOnMock invocation) throws Throwable { - Runnable runnable = invocation.getArgument(0); - Runnable wrapper = () -> { - mockLspProvider(); - mockDisplayAsyncCall(); - mockQEclipseEditorUtils(); - runnable.run(); - }; - ExecutorService executor = Executors.newSingleThreadExecutor(); - return executor.submit(wrapper); - } + .thenAnswer(invocation -> { + Runnable runnable = invocation.getArgument(0); + CompletableFuture future = new CompletableFuture<>(); + // Execute synchronously but wrap in CompletableFuture + mockLspProvider(); + mockDisplayAsyncCall(); + mockQEclipseEditorUtils(); + runnable.run(); + future.complete(null); + return future; }); QInvocationSession session = QInvocationSession.getInstance(); session.start(MOCK_EDITOR); - // We need to mock the Display here because the latter half of the update is - // done on the UI thread - inlineCompletionUtilsMock = mockStatic(InlineCompletionUtils.class); + // Mock StyledText to return the same offset as the invocation + StyledText mockStyledText = mock(StyledText.class); + when(mockStyledText.getCaretOffset()).thenReturn(0); // This is crucial + + ITextViewer viewerMock = mock(ITextViewer.class); + when(viewerMock.getTextWidget()).thenReturn(mockStyledText); + editorUtilsMock.when(() -> QEclipseEditorUtils.getActiveTextViewer(any(ITextEditor.class))) + .thenReturn(viewerMock); // Test case: when there are suggestions received + inlineCompletionUtilsMock = mockStatic(InlineCompletionUtils.class); inlineCompletionUtilsMock.when(() -> InlineCompletionUtils.cwParamsFromContext(any(ITextEditor.class), any(ITextViewer.class), any(Integer.class), any(InlineCompletionTriggerKind.class))) .thenReturn(POTENT_PARAM); - session.invoke(); - session.awaitAllUnresolvedTasks(); - assertTrue(session.isActive()); - session.endImmediately(); - // Test case: when there are not suggestions received - inlineCompletionUtilsMock.when(() -> InlineCompletionUtils.cwParamsFromContext(any(ITextEditor.class), - any(ITextViewer.class), any(Integer.class), any(InlineCompletionTriggerKind.class))) - .thenReturn(IMPOTENT_PARAM); - session.start(MOCK_EDITOR); session.invoke(); - session.awaitAllUnresolvedTasks(); - assertTrue(!session.isActive()); - session.endImmediately(); - - // Test case: calling end when there are still requests in flight - BlockingQueue queue = new ArrayBlockingQueue<>(1); - threadingUtilsMock.when(() -> ThreadingUtils.executeAsyncTaskAndReturnFuture(any(Runnable.class))) - .thenAnswer(new Answer>() { - @Override - public Future answer(final InvocationOnMock invocation) throws Throwable { - Runnable runnable = () -> { - try { - queue.take(); - } catch (InterruptedException e) { - // This will print stack traces from interrupted exception for when it gets terminated forcefully - // It does not mean the test has failed. - e.printStackTrace(); - } - }; - ExecutorService executor = Executors.newSingleThreadExecutor(); - return executor.submit(runnable); - } - }); - session.start(MOCK_EDITOR); - session.invoke(); - session.end(); assertTrue(session.isActive()); + session.endImmediately(); // Test case: force end session.endImmediately(); From bc393720922e80d184e63c92c62ff5e118bc5011 Mon Sep 17 00:00:00 2001 From: Jonathan Breedlove Date: Mon, 10 Nov 2025 12:36:30 -0800 Subject: [PATCH 20/40] Bump version to 2.6.0 (#516) --- feature/feature.xml | 4 ++-- feature/pom.xml | 2 +- plugin/META-INF/MANIFEST.MF | 2 +- plugin/pom.xml | 2 +- pom.xml | 2 +- telemetry/pom.xml | 2 +- updatesite/category.xml | 2 +- updatesite/pom.xml | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/feature/feature.xml b/feature/feature.xml index cdb6a69d1..698956cbf 100644 --- a/feature/feature.xml +++ b/feature/feature.xml @@ -2,7 +2,7 @@ + version="2.6.0.qualifier"> Amazon Q Developer helps users build faster across the entire software development lifecycle by providing tailored responses and code recommendations that conform to their team's internal libraries, proprietary algorithmic techniques, and enterprise code style. @@ -198,6 +198,6 @@ https://github.com/aws/amazon-q-eclipse/blob/main/attribution.xml id="amazon-q-eclipse" download-size="11000" install-size="0" - version="2.5.0.qualifier" + version="2.6.0.qualifier" unpack="false"/> diff --git a/feature/pom.xml b/feature/pom.xml index d95396bdb..33fc4cda4 100644 --- a/feature/pom.xml +++ b/feature/pom.xml @@ -6,7 +6,7 @@ software.aws.toolkits.eclipse amazon-q-eclipse-group - 2.5.0-SNAPSHOT + 2.6.0-SNAPSHOT ../ diff --git a/plugin/META-INF/MANIFEST.MF b/plugin/META-INF/MANIFEST.MF index 33012d0b2..356670825 100644 --- a/plugin/META-INF/MANIFEST.MF +++ b/plugin/META-INF/MANIFEST.MF @@ -4,7 +4,7 @@ Bundle-Name: Amazon Q for Eclipse Bundle-Provider: Amazon Web Services Bundle-RequiredExecutionEnvironment: JavaSE-17 Bundle-SymbolicName: amazon-q-eclipse;singleton:=true -Bundle-Version: 2.5.0.qualifier +Bundle-Version: 2.6.0.qualifier Automatic-Module-Name: amazon.q.eclipse Bundle-ActivationPolicy: lazy Bundle-Activator: software.aws.toolkits.eclipse.amazonq.plugin.Activator diff --git a/plugin/pom.xml b/plugin/pom.xml index 400d7422f..5d061bb51 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -6,7 +6,7 @@ software.aws.toolkits.eclipse amazon-q-eclipse-group - 2.5.0-SNAPSHOT + 2.6.0-SNAPSHOT ../ diff --git a/pom.xml b/pom.xml index bc478310a..6dfd0ab6a 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ software.aws.toolkits.eclipse amazon-q-eclipse-group - 2.5.0-SNAPSHOT + 2.6.0-SNAPSHOT pom diff --git a/telemetry/pom.xml b/telemetry/pom.xml index d4123ec62..474fae438 100644 --- a/telemetry/pom.xml +++ b/telemetry/pom.xml @@ -6,7 +6,7 @@ software.aws.toolkits.eclipse amazon-q-eclipse-group - 2.5.0-SNAPSHOT + 2.6.0-SNAPSHOT ../ diff --git a/updatesite/category.xml b/updatesite/category.xml index 7ec9042d2..486013924 100644 --- a/updatesite/category.xml +++ b/updatesite/category.xml @@ -1,6 +1,6 @@ - + diff --git a/updatesite/pom.xml b/updatesite/pom.xml index 2de4a79b1..c8f4755fe 100644 --- a/updatesite/pom.xml +++ b/updatesite/pom.xml @@ -6,7 +6,7 @@ software.aws.toolkits.eclipse amazon-q-eclipse-group - 2.5.0-SNAPSHOT + 2.6.0-SNAPSHOT ../ From 43b766fe0bcad4659f895634c82fb96ea65c4376 Mon Sep 17 00:00:00 2001 From: Jonathan Breedlove Date: Wed, 19 Nov 2025 13:47:45 -0800 Subject: [PATCH 21/40] Expand Flare version constraint to include all 1.x.x versions (#518) --- .../aws/toolkits/eclipse/amazonq/lsp/manager/LspConstants.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/manager/LspConstants.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/manager/LspConstants.java index 0b1725668..5aad492b8 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/manager/LspConstants.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/manager/LspConstants.java @@ -32,7 +32,7 @@ private LspConstants() { private static VersionRange createVersionRange() { try { - return VersionRange.createFromVersionSpec("[1.0.0, 1.50.0)"); + return VersionRange.createFromVersionSpec("[1.0.0, 2.0.0)"); } catch (InvalidVersionSpecificationException e) { throw new AmazonQPluginException("Failed to parse LSP supported version range", e); } From bf90eaf102bf77466185107109cb4b2840eb1fe6 Mon Sep 17 00:00:00 2001 From: Will Lo <96078566+Will-ShaoHua@users.noreply.github.com> Date: Fri, 21 Nov 2025 11:59:30 -0800 Subject: [PATCH 22/40] fix: inline auto trigger doesnt work when connecting to SAP server through ADT plugin (#519) --- .../amazonq/util/AutoTriggerPartListener.java | 62 +++++++++++++++---- 1 file changed, 50 insertions(+), 12 deletions(-) diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/util/AutoTriggerPartListener.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/util/AutoTriggerPartListener.java index 4faa4fee9..5364b1cf2 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/util/AutoTriggerPartListener.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/util/AutoTriggerPartListener.java @@ -4,11 +4,15 @@ package software.aws.toolkits.eclipse.amazonq.util; import static software.aws.toolkits.eclipse.amazonq.util.QEclipseEditorUtils.getActiveTextEditor; - +import static software.aws.toolkits.eclipse.amazonq.util.QEclipseEditorUtils.getActiveTextViewer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IDocumentListener; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.IPartListener2; +import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.IWorkbenchPartReference; import org.eclipse.ui.texteditor.ITextEditor; @@ -22,34 +26,68 @@ public AutoTriggerPartListener(final T docListener) { this.docListener = docListener; } + static final String SAP_ADT_EDITOR_FQN = "com.sap.adt.programs.ui.internal.programs.editors.ProgramEditor"; + static final List CUSTOM_EDITOR_FQNS = new ArrayList<>(Arrays.asList(SAP_ADT_EDITOR_FQN)); + @Override public void partActivated(final IWorkbenchPartReference partRef) { var part = partRef.getPart(false); - if (!(part instanceof ITextEditor)) { + boolean isEditor = part instanceof ITextEditor; + if (isEditor) { + ITextEditor editor = (ITextEditor) part; + + // We should only have at most one listener listening to one document + // at any given moment. Therefore it would be acceptable to override the + // listener + // This is also assuming an active part cannot be activated again. + attachDocumentListenerAndUpdateActiveDocument(editor); return; } - ITextEditor editor = (ITextEditor) part; - // We should only have at most one listener listening to one document - // at any given moment. Therefore it would be acceptable to override the - // listener - // This is also assuming an active part cannot be activated again. - attachDocumentListenerAndUpdateActiveDocument(editor); + /** + * When users use 3rd party plugins and connect to remote server for example SAP/ADT plugin. + * The file opened is a customized editor defined by SAP package, which cause above logic not executed. + * This is a monkey patch to fix the issue based on how we currently instrument inline auto trigger. + * We might need to add different class names if we see more variants of such editor/file showing. + */ + boolean isCustomizedEditorType = isCustomizedEditorType(part); + if (isCustomizedEditorType) { + ITextEditor e = getActiveTextEditor(); + var viewer = getActiveTextViewer(e); + if (viewer != null) { + var document = viewer.getDocument(); + if (document != null) { + attachDocumentListenerAndUpdateActiveDocument(document); + } + } + } } @Override public void partDeactivated(final IWorkbenchPartReference partRef) { - var part = partRef.getPart(false); - if (!(part instanceof ITextEditor)) { + if (activeDocument == null) { return; } - detachDocumentListenerFromLastActiveDocument(); + + var part = partRef.getPart(false); + boolean isApplicable = part instanceof ITextEditor || isCustomizedEditorType(part); + if (isApplicable) { + detachDocumentListenerFromLastActiveDocument(); + } + } + + private boolean isCustomizedEditorType(final IWorkbenchPart part) { + return CUSTOM_EDITOR_FQNS.contains(part.getClass().getName()); } private void attachDocumentListenerAndUpdateActiveDocument(final ITextEditor editor) { var document = editor.getDocumentProvider().getDocument(editor.getEditorInput()); + attachDocumentListenerAndUpdateActiveDocument(document); + } + + private void attachDocumentListenerAndUpdateActiveDocument(final IDocument document) { document.addDocumentListener(docListener); - activeDocument = document; + setActiveDocument(document); } private void detachDocumentListenerFromLastActiveDocument() { From 71bdc722729af37e62e6a0283c982e429cd42571 Mon Sep 17 00:00:00 2001 From: Jonathan Breedlove Date: Fri, 21 Nov 2025 12:09:26 -0800 Subject: [PATCH 23/40] Bump version to 2.6.1 (#520) --- feature/feature.xml | 4 ++-- feature/pom.xml | 2 +- plugin/META-INF/MANIFEST.MF | 2 +- plugin/pom.xml | 2 +- pom.xml | 2 +- telemetry/pom.xml | 2 +- updatesite/category.xml | 2 +- updatesite/pom.xml | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/feature/feature.xml b/feature/feature.xml index 698956cbf..22c64020d 100644 --- a/feature/feature.xml +++ b/feature/feature.xml @@ -2,7 +2,7 @@ + version="2.6.1.qualifier"> Amazon Q Developer helps users build faster across the entire software development lifecycle by providing tailored responses and code recommendations that conform to their team's internal libraries, proprietary algorithmic techniques, and enterprise code style. @@ -198,6 +198,6 @@ https://github.com/aws/amazon-q-eclipse/blob/main/attribution.xml id="amazon-q-eclipse" download-size="11000" install-size="0" - version="2.6.0.qualifier" + version="2.6.1.qualifier" unpack="false"/> diff --git a/feature/pom.xml b/feature/pom.xml index 33fc4cda4..86c2b03c8 100644 --- a/feature/pom.xml +++ b/feature/pom.xml @@ -6,7 +6,7 @@ software.aws.toolkits.eclipse amazon-q-eclipse-group - 2.6.0-SNAPSHOT + 2.6.1-SNAPSHOT ../ diff --git a/plugin/META-INF/MANIFEST.MF b/plugin/META-INF/MANIFEST.MF index 356670825..aa33901d3 100644 --- a/plugin/META-INF/MANIFEST.MF +++ b/plugin/META-INF/MANIFEST.MF @@ -4,7 +4,7 @@ Bundle-Name: Amazon Q for Eclipse Bundle-Provider: Amazon Web Services Bundle-RequiredExecutionEnvironment: JavaSE-17 Bundle-SymbolicName: amazon-q-eclipse;singleton:=true -Bundle-Version: 2.6.0.qualifier +Bundle-Version: 2.6.1.qualifier Automatic-Module-Name: amazon.q.eclipse Bundle-ActivationPolicy: lazy Bundle-Activator: software.aws.toolkits.eclipse.amazonq.plugin.Activator diff --git a/plugin/pom.xml b/plugin/pom.xml index 5d061bb51..eeba48c26 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -6,7 +6,7 @@ software.aws.toolkits.eclipse amazon-q-eclipse-group - 2.6.0-SNAPSHOT + 2.6.1-SNAPSHOT ../ diff --git a/pom.xml b/pom.xml index 6dfd0ab6a..e62e74750 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ software.aws.toolkits.eclipse amazon-q-eclipse-group - 2.6.0-SNAPSHOT + 2.6.1-SNAPSHOT pom diff --git a/telemetry/pom.xml b/telemetry/pom.xml index 474fae438..0e8f1a721 100644 --- a/telemetry/pom.xml +++ b/telemetry/pom.xml @@ -6,7 +6,7 @@ software.aws.toolkits.eclipse amazon-q-eclipse-group - 2.6.0-SNAPSHOT + 2.6.1-SNAPSHOT ../ diff --git a/updatesite/category.xml b/updatesite/category.xml index 486013924..5b8b28878 100644 --- a/updatesite/category.xml +++ b/updatesite/category.xml @@ -1,6 +1,6 @@ - + diff --git a/updatesite/pom.xml b/updatesite/pom.xml index c8f4755fe..75777030f 100644 --- a/updatesite/pom.xml +++ b/updatesite/pom.xml @@ -6,7 +6,7 @@ software.aws.toolkits.eclipse amazon-q-eclipse-group - 2.6.0-SNAPSHOT + 2.6.1-SNAPSHOT ../ From ec6dbc39f0ce425e56b1569e8b9202ae122070a7 Mon Sep 17 00:00:00 2001 From: Jonathan Breedlove Date: Thu, 11 Dec 2025 12:51:36 -0800 Subject: [PATCH 24/40] fix: make ADT custom editor detection more permissive (#524) --- .../eclipse/amazonq/util/AutoTriggerPartListener.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/util/AutoTriggerPartListener.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/util/AutoTriggerPartListener.java index 5364b1cf2..dcbf7358b 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/util/AutoTriggerPartListener.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/util/AutoTriggerPartListener.java @@ -22,13 +22,12 @@ public final class AutoTriggerPartListener CUSTOM_EDITOR_PREFIXES = new ArrayList<>(Arrays.asList("com.sap.adt")); + public AutoTriggerPartListener(final T docListener) { this.docListener = docListener; } - static final String SAP_ADT_EDITOR_FQN = "com.sap.adt.programs.ui.internal.programs.editors.ProgramEditor"; - static final List CUSTOM_EDITOR_FQNS = new ArrayList<>(Arrays.asList(SAP_ADT_EDITOR_FQN)); - @Override public void partActivated(final IWorkbenchPartReference partRef) { var part = partRef.getPart(false); @@ -77,7 +76,8 @@ public void partDeactivated(final IWorkbenchPartReference partRef) { } private boolean isCustomizedEditorType(final IWorkbenchPart part) { - return CUSTOM_EDITOR_FQNS.contains(part.getClass().getName()); + String className = part.getClass().getName(); + return CUSTOM_EDITOR_PREFIXES.stream().anyMatch(prefix -> className.startsWith(prefix)); } private void attachDocumentListenerAndUpdateActiveDocument(final ITextEditor editor) { From 2dba8b8cc9b79d3ff2b7838a80aab9c5326c001f Mon Sep 17 00:00:00 2001 From: Jonathan Breedlove Date: Fri, 12 Dec 2025 11:43:23 -0800 Subject: [PATCH 25/40] Bump version to 2.6.2 (#525) --- feature/feature.xml | 4 ++-- feature/pom.xml | 2 +- plugin/META-INF/MANIFEST.MF | 2 +- plugin/pom.xml | 2 +- pom.xml | 2 +- telemetry/pom.xml | 2 +- updatesite/category.xml | 2 +- updatesite/pom.xml | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/feature/feature.xml b/feature/feature.xml index 22c64020d..0bfaad387 100644 --- a/feature/feature.xml +++ b/feature/feature.xml @@ -2,7 +2,7 @@ + version="2.6.2.qualifier"> Amazon Q Developer helps users build faster across the entire software development lifecycle by providing tailored responses and code recommendations that conform to their team's internal libraries, proprietary algorithmic techniques, and enterprise code style. @@ -198,6 +198,6 @@ https://github.com/aws/amazon-q-eclipse/blob/main/attribution.xml id="amazon-q-eclipse" download-size="11000" install-size="0" - version="2.6.1.qualifier" + version="2.6.2.qualifier" unpack="false"/> diff --git a/feature/pom.xml b/feature/pom.xml index 86c2b03c8..24c95fbe5 100644 --- a/feature/pom.xml +++ b/feature/pom.xml @@ -6,7 +6,7 @@ software.aws.toolkits.eclipse amazon-q-eclipse-group - 2.6.1-SNAPSHOT + 2.6.2-SNAPSHOT ../ diff --git a/plugin/META-INF/MANIFEST.MF b/plugin/META-INF/MANIFEST.MF index aa33901d3..194c2d36d 100644 --- a/plugin/META-INF/MANIFEST.MF +++ b/plugin/META-INF/MANIFEST.MF @@ -4,7 +4,7 @@ Bundle-Name: Amazon Q for Eclipse Bundle-Provider: Amazon Web Services Bundle-RequiredExecutionEnvironment: JavaSE-17 Bundle-SymbolicName: amazon-q-eclipse;singleton:=true -Bundle-Version: 2.6.1.qualifier +Bundle-Version: 2.6.2.qualifier Automatic-Module-Name: amazon.q.eclipse Bundle-ActivationPolicy: lazy Bundle-Activator: software.aws.toolkits.eclipse.amazonq.plugin.Activator diff --git a/plugin/pom.xml b/plugin/pom.xml index eeba48c26..0e36a32a1 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -6,7 +6,7 @@ software.aws.toolkits.eclipse amazon-q-eclipse-group - 2.6.1-SNAPSHOT + 2.6.2-SNAPSHOT ../ diff --git a/pom.xml b/pom.xml index e62e74750..0ebf046e9 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ software.aws.toolkits.eclipse amazon-q-eclipse-group - 2.6.1-SNAPSHOT + 2.6.2-SNAPSHOT pom diff --git a/telemetry/pom.xml b/telemetry/pom.xml index 0e8f1a721..b3a1e6aed 100644 --- a/telemetry/pom.xml +++ b/telemetry/pom.xml @@ -6,7 +6,7 @@ software.aws.toolkits.eclipse amazon-q-eclipse-group - 2.6.1-SNAPSHOT + 2.6.2-SNAPSHOT ../ diff --git a/updatesite/category.xml b/updatesite/category.xml index 5b8b28878..056d00252 100644 --- a/updatesite/category.xml +++ b/updatesite/category.xml @@ -1,6 +1,6 @@ - + diff --git a/updatesite/pom.xml b/updatesite/pom.xml index 75777030f..803d7cac1 100644 --- a/updatesite/pom.xml +++ b/updatesite/pom.xml @@ -6,7 +6,7 @@ software.aws.toolkits.eclipse amazon-q-eclipse-group - 2.6.1-SNAPSHOT + 2.6.2-SNAPSHOT ../ From fdc3e7aea18d04b0ac10340d23003d9f8c68d218 Mon Sep 17 00:00:00 2001 From: Will Lo <96078566+Will-ShaoHua@users.noreply.github.com> Date: Tue, 20 Jan 2026 11:54:40 -0800 Subject: [PATCH 26/40] fix: NPE when file is stored in semantic cache location where rawLocation returns null (#528) --- .../amazonq/util/QEclipseEditorUtils.java | 51 ++++++++++++------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/util/QEclipseEditorUtils.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/util/QEclipseEditorUtils.java index 266c8fa45..d4c3e00ff 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/util/QEclipseEditorUtils.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/util/QEclipseEditorUtils.java @@ -106,7 +106,7 @@ public static boolean shouldIndentVertically(final StyledText textWidget, final public static Optional getOpenFileUri() { try { return getOpenFilePath() - .map(filePath -> Paths.get(filePath).toUri().toString()); + .map(filePath -> Paths.get(filePath).toUri().toString()); } catch (Exception e) { Activator.getLogger().error("Unexpected error when determining open file path", e); return Optional.empty(); @@ -146,7 +146,11 @@ private static String getOpenFilePath(final IEditorInput editorInput) { // rather than the workspace location, so we need to reference the cached file path return AbapUtil.getSemanticCachePath(file.getFullPath().toOSString()); } - return file.getRawLocation().toOSString(); + // Fallback to semantic cache location if rawLocation is null, one of the use + // cases is also file when users connect to a remote SAP project + return (file.getRawLocation() != null) + ? file.getRawLocation().toOSString() + : AbapUtil.getSemanticCachePath(file.getFullPath().toOSString()); } else { throw new AmazonQPluginException("Unexpected editor input type: " + editorInput.getClass().getName()); } @@ -172,7 +176,8 @@ public static Optional getSelectionRange(final ITextEditor editor) { var end = new Position(endLine, endColumn); return Optional.of(new Range(start, end)); } catch (org.eclipse.jface.text.BadLocationException e) { - Activator.getLogger().error("Error occurred while attempting to determine selected text position in editor", e); + Activator.getLogger() + .error("Error occurred while attempting to determine selected text position in editor", e); } } return Optional.empty(); @@ -184,7 +189,8 @@ public static Optional getActiveSelectionRange() { } /* - * Inserts the given text at cursor position and returns cursor position range of the text + * Inserts the given text at cursor position and returns cursor position range + * of the text */ public static Optional insertAtCursor(final String text) { var editor = getActiveTextEditor(); @@ -254,8 +260,9 @@ private static String applyIndentation(final String text, final String indentati StringBuilder indentedText = new StringBuilder(lines.get(0)); for (int i = 1; i < lines.size(); i++) { indentedText.append("\n") - .append(lines.get(i).isEmpty() ? "" : indentation) // Don't apply the gap to empty lines (eg: end of string may end in a newline) - .append(lines.get(i)); + .append(lines.get(i).isEmpty() ? "" : indentation) // Don't apply the gap to empty lines (eg: end of + // string may end in a newline) + .append(lines.get(i)); } return indentedText.toString(); @@ -268,7 +275,8 @@ private static String getIndentation(final IDocument document, final int offset) var content = document.get(lineOffset, offset - lineOffset); if (content.trim().isEmpty()) { - // if current line is blank or contains only whitespace, return line as indentation + // if current line is blank or contains only whitespace, return line as + // indentation return content; } return content.substring(0, content.indexOf(content.trim())); @@ -319,14 +327,18 @@ public static IExecutionListener getAutoTriggerExecutionListener(final Consumer< public void notHandled(final String commandId, final NotHandledException exception) { return; } + @Override - public void postExecuteFailure(final String commandId, final org.eclipse.core.commands.ExecutionException exception) { + public void postExecuteFailure(final String commandId, + final org.eclipse.core.commands.ExecutionException exception) { return; } + @Override public void postExecuteSuccess(final String commandId, final Object returnValue) { return; } + @Override public void preExecute(final String commandId, final ExecutionEvent event) { callback.accept(commandId); @@ -348,17 +360,18 @@ public static IQInlineTypeaheadProcessor getAutoCloseSettings(final ITextEditor return new GenericTypeheadProcessor(); } switch (contentTypeName) { - // TODO: Add more supported file types here: - case "Java Source File": - IEclipsePreferences preferences = InstanceScope.INSTANCE.getNode("org.eclipse.jdt.ui"); - boolean isBracesSetToAutoClose = preferences.getBoolean("closeBraces", true); - boolean isBracketsSetToAutoClose = preferences.getBoolean("closeBrackets", true); - boolean isStringSetToAutoClose = preferences.getBoolean("closeStrings", true); - return new JavaTypeaheadProcessor(editor, isBracesSetToAutoClose, isBracketsSetToAutoClose, isStringSetToAutoClose); - case "JavaScript Source File": - return new JavascriptTypeaheadProcessor(); - default: - return new GenericTypeheadProcessor(); + // TODO: Add more supported file types here: + case "Java Source File": + IEclipsePreferences preferences = InstanceScope.INSTANCE.getNode("org.eclipse.jdt.ui"); + boolean isBracesSetToAutoClose = preferences.getBoolean("closeBraces", true); + boolean isBracketsSetToAutoClose = preferences.getBoolean("closeBrackets", true); + boolean isStringSetToAutoClose = preferences.getBoolean("closeStrings", true); + return new JavaTypeaheadProcessor(editor, isBracesSetToAutoClose, isBracketsSetToAutoClose, + isStringSetToAutoClose); + case "JavaScript Source File": + return new JavascriptTypeaheadProcessor(); + default: + return new GenericTypeheadProcessor(); } } From 46c5207174871837c4c5586b9fe7bf43eccb6684 Mon Sep 17 00:00:00 2001 From: Will Lo <96078566+Will-ShaoHua@users.noreply.github.com> Date: Tue, 20 Jan 2026 15:06:52 -0800 Subject: [PATCH 27/40] Bump version to 2.6.3 (#530) * Bump version to 2.6.3 * patch --- feature/feature.xml | 4 ++-- feature/pom.xml | 2 +- plugin/META-INF/MANIFEST.MF | 2 +- plugin/pom.xml | 2 +- pom.xml | 2 +- telemetry/pom.xml | 2 +- updatesite/category.xml | 2 +- updatesite/pom.xml | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/feature/feature.xml b/feature/feature.xml index 0bfaad387..d0ae054de 100644 --- a/feature/feature.xml +++ b/feature/feature.xml @@ -2,7 +2,7 @@ + version="2.6.3.qualifier"> Amazon Q Developer helps users build faster across the entire software development lifecycle by providing tailored responses and code recommendations that conform to their team's internal libraries, proprietary algorithmic techniques, and enterprise code style. @@ -198,6 +198,6 @@ https://github.com/aws/amazon-q-eclipse/blob/main/attribution.xml id="amazon-q-eclipse" download-size="11000" install-size="0" - version="2.6.2.qualifier" + version="2.6.3.qualifier" unpack="false"/> diff --git a/feature/pom.xml b/feature/pom.xml index 24c95fbe5..72011e1aa 100644 --- a/feature/pom.xml +++ b/feature/pom.xml @@ -6,7 +6,7 @@ software.aws.toolkits.eclipse amazon-q-eclipse-group - 2.6.2-SNAPSHOT + 2.6.3-SNAPSHOT ../ diff --git a/plugin/META-INF/MANIFEST.MF b/plugin/META-INF/MANIFEST.MF index 194c2d36d..6929d313c 100644 --- a/plugin/META-INF/MANIFEST.MF +++ b/plugin/META-INF/MANIFEST.MF @@ -4,7 +4,7 @@ Bundle-Name: Amazon Q for Eclipse Bundle-Provider: Amazon Web Services Bundle-RequiredExecutionEnvironment: JavaSE-17 Bundle-SymbolicName: amazon-q-eclipse;singleton:=true -Bundle-Version: 2.6.2.qualifier +Bundle-Version: 2.6.3.qualifier Automatic-Module-Name: amazon.q.eclipse Bundle-ActivationPolicy: lazy Bundle-Activator: software.aws.toolkits.eclipse.amazonq.plugin.Activator diff --git a/plugin/pom.xml b/plugin/pom.xml index 0e36a32a1..432bdb636 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -6,7 +6,7 @@ software.aws.toolkits.eclipse amazon-q-eclipse-group - 2.6.2-SNAPSHOT + 2.6.3-SNAPSHOT ../ diff --git a/pom.xml b/pom.xml index 0ebf046e9..34afc6b48 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ software.aws.toolkits.eclipse amazon-q-eclipse-group - 2.6.2-SNAPSHOT + 2.6.3-SNAPSHOT pom diff --git a/telemetry/pom.xml b/telemetry/pom.xml index b3a1e6aed..c8eeb9715 100644 --- a/telemetry/pom.xml +++ b/telemetry/pom.xml @@ -6,7 +6,7 @@ software.aws.toolkits.eclipse amazon-q-eclipse-group - 2.6.2-SNAPSHOT + 2.6.3-SNAPSHOT ../ diff --git a/updatesite/category.xml b/updatesite/category.xml index 056d00252..656e6c38c 100644 --- a/updatesite/category.xml +++ b/updatesite/category.xml @@ -1,6 +1,6 @@ - + diff --git a/updatesite/pom.xml b/updatesite/pom.xml index 803d7cac1..2acdb834a 100644 --- a/updatesite/pom.xml +++ b/updatesite/pom.xml @@ -6,7 +6,7 @@ software.aws.toolkits.eclipse amazon-q-eclipse-group - 2.6.2-SNAPSHOT + 2.6.3-SNAPSHOT ../ From cbfd7e56bc5afa7cd9e1a84554e27a8a43ab3180 Mon Sep 17 00:00:00 2001 From: Laxman Reddy <141967714+laileni-aws@users.noreply.github.com> Date: Wed, 8 Apr 2026 16:42:05 -0700 Subject: [PATCH 28/40] fix(amazonq): adding openTabFilepaths to inline completion requests (#538) --- .../lsp/model/InlineCompletionParams.java | 11 ++ .../amazonq/util/InlineCompletionUtils.java | 8 + .../amazonq/util/QEclipseEditorUtils.java | 33 ++++ .../util/InlineCompletionUtilsTest.java | 124 +++++++++++++ ...EditorUtilsGetOpenEditorFilePathsTest.java | 174 ++++++++++++++++++ 5 files changed, 350 insertions(+) create mode 100644 plugin/tst/software/aws/toolkits/eclipse/amazonq/util/InlineCompletionUtilsTest.java create mode 100644 plugin/tst/software/aws/toolkits/eclipse/amazonq/util/QEclipseEditorUtilsGetOpenEditorFilePathsTest.java diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/model/InlineCompletionParams.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/model/InlineCompletionParams.java index bfc6c7520..c99d83b6f 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/model/InlineCompletionParams.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/model/InlineCompletionParams.java @@ -3,11 +3,14 @@ package software.aws.toolkits.eclipse.amazonq.lsp.model; +import java.util.List; + import org.eclipse.lsp4j.TextDocumentPositionAndWorkDoneProgressParams; public class InlineCompletionParams extends TextDocumentPositionAndWorkDoneProgressParams { private InlineCompletionContext context; + private List openTabFilepaths; public final InlineCompletionContext getContext() { return context; @@ -17,4 +20,12 @@ public final void setContext(final InlineCompletionContext context) { this.context = context; } + public final List getOpenTabFilepaths() { + return openTabFilepaths; + } + + public final void setOpenTabFilepaths(final List openTabFilepaths) { + this.openTabFilepaths = openTabFilepaths; + } + } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/util/InlineCompletionUtils.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/util/InlineCompletionUtils.java index 4fab8e1c3..8c207dd71 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/util/InlineCompletionUtils.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/util/InlineCompletionUtils.java @@ -3,6 +3,8 @@ package software.aws.toolkits.eclipse.amazonq.util; +import java.util.List; + import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.ITextViewer; import org.eclipse.lsp4j.Position; @@ -41,6 +43,12 @@ public static InlineCompletionParams cwParamsFromContext(final ITextEditor edito invocationPosition.setLine(startLine); invocationPosition.setCharacter(lineOffset); params.setPosition(invocationPosition); + + List openTabFilepaths = QEclipseEditorUtils.getOpenEditorFilePaths(); + if (!openTabFilepaths.isEmpty()) { + params.setOpenTabFilepaths(openTabFilepaths); + } + return params; } } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/util/QEclipseEditorUtils.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/util/QEclipseEditorUtils.java index d4c3e00ff..c59f3cf27 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/util/QEclipseEditorUtils.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/util/QEclipseEditorUtils.java @@ -6,6 +6,7 @@ import static software.aws.toolkits.eclipse.amazonq.util.QConstants.Q_INLINE_HINT_TEXT_STYLE; import java.nio.file.Paths; +import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.function.Consumer; @@ -37,6 +38,7 @@ import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IEditorReference; import org.eclipse.ui.IFileEditorInput; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchWindow; @@ -156,6 +158,37 @@ private static String getOpenFilePath(final IEditorInput editorInput) { } } + /** + * Returns file paths for all currently open editor tabs, excluding in-memory + * editors. Used to provide supplemental context for inline completions. + */ + public static List getOpenEditorFilePaths() { + List filePaths = new ArrayList<>(); + try { + IWorkbenchPage page = getActivePage(); + if (page == null) { + return filePaths; + } + for (IEditorReference editorRef : page.getEditorReferences()) { + try { + IEditorInput input = editorRef.getEditorInput(); + if (input instanceof InMemoryInput) { + continue; + } + String path = getOpenFilePath(input); + if (path != null && !path.isEmpty()) { + filePaths.add(path); + } + } catch (Exception e) { + Activator.getLogger().warn("Skipping editor tab: unable to resolve file path", e); + } + } + } catch (Exception e) { + Activator.getLogger().error("Error collecting open editor file paths", e); + } + return filePaths; + } + public static Optional getSelectionRange(final ITextEditor editor) { if (editor == null) { return Optional.empty(); diff --git a/plugin/tst/software/aws/toolkits/eclipse/amazonq/util/InlineCompletionUtilsTest.java b/plugin/tst/software/aws/toolkits/eclipse/amazonq/util/InlineCompletionUtilsTest.java new file mode 100644 index 000000000..28554671d --- /dev/null +++ b/plugin/tst/software/aws/toolkits/eclipse/amazonq/util/InlineCompletionUtilsTest.java @@ -0,0 +1,124 @@ +// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.eclipse.amazonq.util; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.ITextViewer; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.texteditor.ITextEditor; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; + +import software.aws.toolkits.eclipse.amazonq.lsp.model.InlineCompletionParams; +import software.aws.toolkits.eclipse.amazonq.lsp.model.InlineCompletionTriggerKind; + +public class InlineCompletionUtilsTest { + + private static MockedStatic editorUtilsMock; + + @BeforeAll + public static void setUp() { + editorUtilsMock = mockStatic(QEclipseEditorUtils.class); + } + + @AfterAll + public static void tearDown() { + if (editorUtilsMock != null) { + editorUtilsMock.close(); + } + } + + @Test + void testCwParamsIncludesOpenTabFilepaths() throws Exception { + ITextEditor editor = mock(ITextEditor.class); + IEditorInput editorInput = mock(IEditorInput.class); + when(editor.getEditorInput()).thenReturn(editorInput); + + ITextViewer viewer = mock(ITextViewer.class); + IDocument document = mock(IDocument.class); + when(viewer.getDocument()).thenReturn(document); + when(document.getLineOfOffset(0)).thenReturn(0); + when(document.getLineOffset(0)).thenReturn(0); + + List mockPaths = Arrays.asList("/project/src/Foo.java", "/project/src/Bar.java"); + editorUtilsMock.when(() -> QEclipseEditorUtils.getOpenFileUri(any(IEditorInput.class))) + .thenReturn(Optional.of("file:///project/src/Active.java")); + editorUtilsMock.when(QEclipseEditorUtils::getOpenEditorFilePaths) + .thenReturn(mockPaths); + + InlineCompletionParams params = InlineCompletionUtils.cwParamsFromContext( + editor, viewer, 0, InlineCompletionTriggerKind.Invoke); + + assertNotNull(params.getOpenTabFilepaths()); + assertEquals(2, params.getOpenTabFilepaths().size()); + assertTrue(params.getOpenTabFilepaths().contains("/project/src/Foo.java")); + assertTrue(params.getOpenTabFilepaths().contains("/project/src/Bar.java")); + } + + @Test + void testCwParamsOmitsOpenTabFilepathsWhenEmpty() throws Exception { + ITextEditor editor = mock(ITextEditor.class); + IEditorInput editorInput = mock(IEditorInput.class); + when(editor.getEditorInput()).thenReturn(editorInput); + + ITextViewer viewer = mock(ITextViewer.class); + IDocument document = mock(IDocument.class); + when(viewer.getDocument()).thenReturn(document); + when(document.getLineOfOffset(0)).thenReturn(0); + when(document.getLineOffset(0)).thenReturn(0); + + editorUtilsMock.when(() -> QEclipseEditorUtils.getOpenFileUri(any(IEditorInput.class))) + .thenReturn(Optional.of("file:///project/src/Active.java")); + editorUtilsMock.when(QEclipseEditorUtils::getOpenEditorFilePaths) + .thenReturn(Collections.emptyList()); + + InlineCompletionParams params = InlineCompletionUtils.cwParamsFromContext( + editor, viewer, 0, InlineCompletionTriggerKind.Invoke); + + assertNull(params.getOpenTabFilepaths()); + } + + @Test + void testCwParamsSetsCorrectPositionAndContext() throws Exception { + ITextEditor editor = mock(ITextEditor.class); + IEditorInput editorInput = mock(IEditorInput.class); + when(editor.getEditorInput()).thenReturn(editorInput); + + ITextViewer viewer = mock(ITextViewer.class); + IDocument document = mock(IDocument.class); + when(viewer.getDocument()).thenReturn(document); + when(document.getLineOfOffset(25)).thenReturn(2); + when(document.getLineOffset(2)).thenReturn(20); + + editorUtilsMock.when(() -> QEclipseEditorUtils.getOpenFileUri(any(IEditorInput.class))) + .thenReturn(Optional.of("file:///project/src/Test.java")); + editorUtilsMock.when(QEclipseEditorUtils::getOpenEditorFilePaths) + .thenReturn(Arrays.asList("/project/src/Foo.java")); + + InlineCompletionParams params = InlineCompletionUtils.cwParamsFromContext( + editor, viewer, 25, InlineCompletionTriggerKind.Automatic); + + assertEquals(2, params.getPosition().getLine()); + assertEquals(5, params.getPosition().getCharacter()); + assertEquals(InlineCompletionTriggerKind.Automatic, params.getContext().getTriggerKind()); + assertNotNull(params.getTextDocument()); + assertEquals("file:///project/src/Test.java", params.getTextDocument().getUri()); + } +} diff --git a/plugin/tst/software/aws/toolkits/eclipse/amazonq/util/QEclipseEditorUtilsGetOpenEditorFilePathsTest.java b/plugin/tst/software/aws/toolkits/eclipse/amazonq/util/QEclipseEditorUtilsGetOpenEditorFilePathsTest.java new file mode 100644 index 000000000..f114e732b --- /dev/null +++ b/plugin/tst/software/aws/toolkits/eclipse/amazonq/util/QEclipseEditorUtilsGetOpenEditorFilePathsTest.java @@ -0,0 +1,174 @@ +// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.eclipse.amazonq.util; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.when; + +import java.net.URI; +import java.util.List; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.runtime.IPath; +import org.eclipse.ui.IEditorReference; +import org.eclipse.ui.IFileEditorInput; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.ide.FileStoreEditorInput; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; + +import software.aws.toolkits.eclipse.amazonq.editor.InMemoryInput; +import software.aws.toolkits.eclipse.amazonq.plugin.Activator; + +public class QEclipseEditorUtilsGetOpenEditorFilePathsTest { + + private static MockedStatic platformUIMock; + private static MockedStatic activatorMock; + private static IWorkbenchPage mockPage; + + @BeforeAll + public static void setUp() { + platformUIMock = mockStatic(PlatformUI.class); + IWorkbench workbench = mock(IWorkbench.class); + IWorkbenchWindow window = mock(IWorkbenchWindow.class); + mockPage = mock(IWorkbenchPage.class); + + platformUIMock.when(PlatformUI::getWorkbench).thenReturn(workbench); + when(workbench.getActiveWorkbenchWindow()).thenReturn(window); + when(window.getActivePage()).thenReturn(mockPage); + + activatorMock = mockStatic(Activator.class); + LoggingService loggingService = mock(LoggingService.class); + activatorMock.when(Activator::getLogger).thenReturn(loggingService); + } + + @AfterAll + public static void tearDown() { + if (platformUIMock != null) { + platformUIMock.close(); + } + if (activatorMock != null) { + activatorMock.close(); + } + } + + @Test + void testReturnsFilePathsFromFileStoreEditors() throws Exception { + FileStoreEditorInput input1 = mock(FileStoreEditorInput.class); + when(input1.getURI()).thenReturn(new URI("file:///project/src/Foo.java")); + + FileStoreEditorInput input2 = mock(FileStoreEditorInput.class); + when(input2.getURI()).thenReturn(new URI("file:///project/src/Bar.java")); + + IEditorReference ref1 = mock(IEditorReference.class); + when(ref1.getEditorInput()).thenReturn(input1); + + IEditorReference ref2 = mock(IEditorReference.class); + when(ref2.getEditorInput()).thenReturn(input2); + + when(mockPage.getEditorReferences()).thenReturn(new IEditorReference[]{ref1, ref2}); + + List paths = QEclipseEditorUtils.getOpenEditorFilePaths(); + + assertNotNull(paths); + assertEquals(2, paths.size()); + assertTrue(paths.contains("/project/src/Foo.java")); + assertTrue(paths.contains("/project/src/Bar.java")); + } + + @Test + void testReturnsFilePathsFromIFileEditorInputs() throws Exception { + IFileEditorInput input = mock(IFileEditorInput.class); + IFile file = mock(IFile.class); + IPath rawLocation = mock(IPath.class); + when(input.getFile()).thenReturn(file); + when(file.getRawLocation()).thenReturn(rawLocation); + when(rawLocation.toOSString()).thenReturn("/project/src/Model.java"); + when(mockPage.findEditor(input)).thenReturn(null); + + IEditorReference ref = mock(IEditorReference.class); + when(ref.getEditorInput()).thenReturn(input); + + when(mockPage.getEditorReferences()).thenReturn(new IEditorReference[]{ref}); + + List paths = QEclipseEditorUtils.getOpenEditorFilePaths(); + + assertNotNull(paths); + assertEquals(1, paths.size()); + assertTrue(paths.contains("/project/src/Model.java")); + } + + @Test + void testSkipsInMemoryEditors() throws Exception { + InMemoryInput inMemoryInput = mock(InMemoryInput.class); + IEditorReference inMemoryRef = mock(IEditorReference.class); + when(inMemoryRef.getEditorInput()).thenReturn(inMemoryInput); + + FileStoreEditorInput fileInput = mock(FileStoreEditorInput.class); + when(fileInput.getURI()).thenReturn(new URI("file:///project/src/Real.java")); + IEditorReference fileRef = mock(IEditorReference.class); + when(fileRef.getEditorInput()).thenReturn(fileInput); + + when(mockPage.getEditorReferences()).thenReturn(new IEditorReference[]{inMemoryRef, fileRef}); + + List paths = QEclipseEditorUtils.getOpenEditorFilePaths(); + + assertEquals(1, paths.size()); + assertTrue(paths.contains("/project/src/Real.java")); + } + + @Test + void testReturnsEmptyListWhenNoEditorsOpen() { + when(mockPage.getEditorReferences()).thenReturn(new IEditorReference[]{}); + + List paths = QEclipseEditorUtils.getOpenEditorFilePaths(); + + assertNotNull(paths); + assertTrue(paths.isEmpty()); + } + + @Test + void testSkipsEditorsWithUnresolvableInput() throws Exception { + IEditorReference badRef = mock(IEditorReference.class); + when(badRef.getEditorInput()).thenThrow(new RuntimeException("Cannot resolve")); + + FileStoreEditorInput goodInput = mock(FileStoreEditorInput.class); + when(goodInput.getURI()).thenReturn(new URI("file:///project/src/Good.java")); + IEditorReference goodRef = mock(IEditorReference.class); + when(goodRef.getEditorInput()).thenReturn(goodInput); + + when(mockPage.getEditorReferences()).thenReturn(new IEditorReference[]{badRef, goodRef}); + + List paths = QEclipseEditorUtils.getOpenEditorFilePaths(); + + assertEquals(1, paths.size()); + assertTrue(paths.contains("/project/src/Good.java")); + } + + @Test + void testReturnsEmptyListWhenActivePageIsNull() { + IWorkbench workbench = mock(IWorkbench.class); + IWorkbenchWindow window = mock(IWorkbenchWindow.class); + platformUIMock.when(PlatformUI::getWorkbench).thenReturn(workbench); + when(workbench.getActiveWorkbenchWindow()).thenReturn(window); + when(window.getActivePage()).thenReturn(null); + + List paths = QEclipseEditorUtils.getOpenEditorFilePaths(); + + assertNotNull(paths); + assertTrue(paths.isEmpty()); + + // Restore for other tests + when(window.getActivePage()).thenReturn(mockPage); + } +} From 489351b092b850c5d78fe9d9e51b755154bc35f0 Mon Sep 17 00:00:00 2001 From: Will Lo <96078566+Will-ShaoHua@users.noreply.github.com> Date: Fri, 10 Apr 2026 10:15:44 -0700 Subject: [PATCH 29/40] feat(chat): wire filterContextCommands to LSP for @ file search (#541) The chat-client's onContextCommandFilter callback sends filterContextCommands via postMessage/ideCommand, but the Java backend had no Command enum value or handler for it. The message was silently dropped, so the @ picker could only search within the initial capped set of context commands. Add FILTER_CONTEXT_COMMANDS to Command enum, AmazonQLspServer interface, action handler routing, and ChatCommunicationManager to forward the request to the LSP and send the response back to the webview. --- .../eclipse/amazonq/chat/ChatCommunicationManager.java | 10 ++++++++++ .../amazonq/chat/models/ChatUIInboundCommandName.java | 1 + .../toolkits/eclipse/amazonq/lsp/AmazonQLspServer.java | 3 +++ .../amazonq/views/AmazonQChatViewActionHandler.java | 1 + .../toolkits/eclipse/amazonq/views/model/Command.java | 1 + 5 files changed, 16 insertions(+) diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatCommunicationManager.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatCommunicationManager.java index d2cd60d3b..703e8f466 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatCommunicationManager.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/ChatCommunicationManager.java @@ -288,6 +288,16 @@ public void sendMessageToChatServer(final Command command, final ChatMessage mes Activator.getLogger().error("Error processing listAvailableModels: " + e); } break; + case FILTER_CONTEXT_COMMANDS: + try { + Object filterResponse = amazonQLspServer.filterContextCommands(message.getData()).get(); + var filterCommand = ChatUIInboundCommand.createCommand(ChatUIInboundCommandName.FilterContextCommands.getValue(), + filterResponse); + Activator.getEventBroker().post(ChatUIInboundCommand.class, filterCommand); + } catch (Exception e) { + Activator.getLogger().error("Error processing filterContextCommands: " + e); + } + break; case PINNED_CONTEXT_ADD: amazonQLspServer.pinnedContextAdd(message.getData()); break; diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/models/ChatUIInboundCommandName.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/models/ChatUIInboundCommandName.java index dc7d41388..6e03572ea 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/models/ChatUIInboundCommandName.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/chat/models/ChatUIInboundCommandName.java @@ -19,6 +19,7 @@ public enum ChatUIInboundCommandName { ListRules("aws/chat/listRules"), RuleClick("aws/chat/ruleClick"), ListAvailableModels("aws/chat/listAvailableModels"), + FilterContextCommands("aws/chat/filterContextCommands"), SendPinnedContext("aws/chat/sendPinnedContext"); private final String value; diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspServer.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspServer.java index 642b90414..df8ca06c3 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspServer.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspServer.java @@ -139,6 +139,9 @@ CompletableFuture> getConfi @JsonRequest("aws/chat/listAvailableModels") CompletableFuture listAvailableModels(Object params); + @JsonRequest("aws/chat/filterContextCommands") + CompletableFuture filterContextCommands(Object params); + @JsonNotification("aws/chat/pinnedContextAdd") void pinnedContextAdd(Object params); diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatViewActionHandler.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatViewActionHandler.java index 982e2866e..7e383652b 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatViewActionHandler.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatViewActionHandler.java @@ -74,6 +74,7 @@ public final void handleCommand(final ParsedCommand parsedCommand, final Browser case PINNED_CONTEXT_ADD: case PINNED_CONTEXT_REMOVE: case LIST_AVAILABLE_MODELS: + case FILTER_CONTEXT_COMMANDS: chatCommunicationManager.sendMessageToChatServer(command, message); break; case CHAT_INFO_LINK_CLICK: diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/model/Command.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/model/Command.java index 052a91050..068c8cd34 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/model/Command.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/model/Command.java @@ -45,6 +45,7 @@ public enum Command { PINNED_CONTEXT_ADD("aws/chat/pinnedContextAdd"), PINNED_CONTEXT_REMOVE("aws/chat/pinnedContextRemove"), LIST_AVAILABLE_MODELS("aws/chat/listAvailableModels"), + FILTER_CONTEXT_COMMANDS("aws/chat/filterContextCommands"), // Auth LOGIN_BUILDER_ID("loginBuilderId"), LOGIN_IDC("loginIdC"), From 9808be9e598c877d6401809a571f77aa297b80e4 Mon Sep 17 00:00:00 2001 From: Will Lo <96078566+Will-ShaoHua@users.noreply.github.com> Date: Fri, 10 Apr 2026 11:51:03 -0700 Subject: [PATCH 30/40] Bump version to 2.7.0 (#542) --- feature/feature.xml | 4 ++-- feature/pom.xml | 2 +- plugin/META-INF/MANIFEST.MF | 2 +- plugin/pom.xml | 2 +- pom.xml | 2 +- telemetry/pom.xml | 2 +- updatesite/category.xml | 2 +- updatesite/pom.xml | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/feature/feature.xml b/feature/feature.xml index d0ae054de..9448c6935 100644 --- a/feature/feature.xml +++ b/feature/feature.xml @@ -2,7 +2,7 @@ + version="2.7.0.qualifier"> Amazon Q Developer helps users build faster across the entire software development lifecycle by providing tailored responses and code recommendations that conform to their team's internal libraries, proprietary algorithmic techniques, and enterprise code style. @@ -198,6 +198,6 @@ https://github.com/aws/amazon-q-eclipse/blob/main/attribution.xml id="amazon-q-eclipse" download-size="11000" install-size="0" - version="2.6.3.qualifier" + version="2.7.0.qualifier" unpack="false"/> diff --git a/feature/pom.xml b/feature/pom.xml index 72011e1aa..1b0797e9c 100644 --- a/feature/pom.xml +++ b/feature/pom.xml @@ -6,7 +6,7 @@ software.aws.toolkits.eclipse amazon-q-eclipse-group - 2.6.3-SNAPSHOT + 2.7.0-SNAPSHOT ../ diff --git a/plugin/META-INF/MANIFEST.MF b/plugin/META-INF/MANIFEST.MF index 6929d313c..38189c395 100644 --- a/plugin/META-INF/MANIFEST.MF +++ b/plugin/META-INF/MANIFEST.MF @@ -4,7 +4,7 @@ Bundle-Name: Amazon Q for Eclipse Bundle-Provider: Amazon Web Services Bundle-RequiredExecutionEnvironment: JavaSE-17 Bundle-SymbolicName: amazon-q-eclipse;singleton:=true -Bundle-Version: 2.6.3.qualifier +Bundle-Version: 2.7.0.qualifier Automatic-Module-Name: amazon.q.eclipse Bundle-ActivationPolicy: lazy Bundle-Activator: software.aws.toolkits.eclipse.amazonq.plugin.Activator diff --git a/plugin/pom.xml b/plugin/pom.xml index 432bdb636..a1a0756c5 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -6,7 +6,7 @@ software.aws.toolkits.eclipse amazon-q-eclipse-group - 2.6.3-SNAPSHOT + 2.7.0-SNAPSHOT ../ diff --git a/pom.xml b/pom.xml index 34afc6b48..c80dd0b86 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ software.aws.toolkits.eclipse amazon-q-eclipse-group - 2.6.3-SNAPSHOT + 2.7.0-SNAPSHOT pom diff --git a/telemetry/pom.xml b/telemetry/pom.xml index c8eeb9715..7c31dd584 100644 --- a/telemetry/pom.xml +++ b/telemetry/pom.xml @@ -6,7 +6,7 @@ software.aws.toolkits.eclipse amazon-q-eclipse-group - 2.6.3-SNAPSHOT + 2.7.0-SNAPSHOT ../ diff --git a/updatesite/category.xml b/updatesite/category.xml index 656e6c38c..532c1e272 100644 --- a/updatesite/category.xml +++ b/updatesite/category.xml @@ -1,6 +1,6 @@ - + diff --git a/updatesite/pom.xml b/updatesite/pom.xml index 2acdb834a..fc7d4c829 100644 --- a/updatesite/pom.xml +++ b/updatesite/pom.xml @@ -6,7 +6,7 @@ software.aws.toolkits.eclipse amazon-q-eclipse-group - 2.6.3-SNAPSHOT + 2.7.0-SNAPSHOT ../ From 990201c4a7afa5cad220c70a528b407d763c34df Mon Sep 17 00:00:00 2001 From: chungjac Date: Tue, 21 Apr 2026 14:01:44 -0700 Subject: [PATCH 31/40] fix: remove workspace index settings and LSP config (#543) --- .../amazonq/lsp/AmazonQLspClientImpl.java | 8 -- .../AmazonQPreferenceInitializer.java | 3 - .../preferences/AmazonQPreferencePage.java | 97 ------------------- .../eclipse/amazonq/util/Constants.java | 4 - 4 files changed, 112 deletions(-) diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspClientImpl.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspClientImpl.java index 6d3cc5f43..dbeda359d 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspClientImpl.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspClientImpl.java @@ -134,14 +134,6 @@ public final CompletableFuture> configuration(final ConfigurationPa qConfig.put(Constants.LSP_CUSTOMIZATION_CONFIGURATION_KEY, Objects.nonNull(storedCustomization) ? storedCustomization.getArn() : null); qConfig.put(Constants.LSP_ENABLE_TELEMETRY_EVENTS_CONFIGURATION_KEY, false); qConfig.put(Constants.LSP_OPT_OUT_TELEMETRY_CONFIGURATION_KEY, !DefaultTelemetryService.telemetryEnabled()); - Map projectContextConfig = new HashMap<>(); - boolean indexingSetting = Activator.getDefault().getPreferenceStore().getBoolean(AmazonQPreferencePage.WORKSPACE_INDEX); - boolean gpuIndexingSetting = Activator.getDefault().getPreferenceStore().getBoolean(AmazonQPreferencePage.USE_GPU_FOR_INDEXING); - int indexThreadsSetting = Activator.getDefault().getPreferenceStore().getInt(AmazonQPreferencePage.INDEX_WORKER_THREADS); - projectContextConfig.put(Constants.LSP_INDEXING_CONFIGURATION_KEY, indexingSetting); - projectContextConfig.put(Constants.LSP_GPU_INDEXING_CONFIGURATION_KEY, gpuIndexingSetting); - projectContextConfig.put(Constants.LSP_INDEX_THREADS_CONFIGURATION_KEY, indexThreadsSetting); - qConfig.put(Constants.LSP_PROJECT_CONTEXT_CONFIGURATION_KEY, projectContextConfig); output.add(qConfig); Activator.getLspProvider().activate(AmazonQLspServer.class); } else if (item.getSection().equals(Constants.LSP_CW_CONFIGURATION_KEY)) { diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/preferences/AmazonQPreferenceInitializer.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/preferences/AmazonQPreferenceInitializer.java index a1cb028b0..fc40c1512 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/preferences/AmazonQPreferenceInitializer.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/preferences/AmazonQPreferenceInitializer.java @@ -18,9 +18,6 @@ public class AmazonQPreferenceInitializer extends AbstractPreferenceInitializer public final void initializeDefaultPreferences() { IPreferenceStore store = Activator.getDefault().getPreferenceStore(); store.setDefault(AmazonQPreferencePage.CODE_REFERENCE_OPT_IN, true); - store.setDefault(AmazonQPreferencePage.WORKSPACE_INDEX, false); - store.setDefault(AmazonQPreferencePage.USE_GPU_FOR_INDEXING, false); - store.setDefault(AmazonQPreferencePage.INDEX_WORKER_THREADS, 0); store.setDefault(AmazonQPreferencePage.TELEMETRY_OPT_IN, true); store.setDefault(AmazonQPreferencePage.Q_DATA_SHARING, true); store.setDefault(AmazonQPreferencePage.HTTPS_PROXY, ""); diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/preferences/AmazonQPreferencePage.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/preferences/AmazonQPreferencePage.java index 9d4c83d1c..7f55c482f 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/preferences/AmazonQPreferencePage.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/preferences/AmazonQPreferencePage.java @@ -36,22 +36,11 @@ public class AmazonQPreferencePage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage { public static final String PREFERENCE_STORE_ID = "software.aws.toolkits.eclipse.preferences"; public static final String CODE_REFERENCE_OPT_IN = "codeReferenceOptIn"; - public static final String WORKSPACE_INDEX = "workspaceIndex"; - public static final String USE_GPU_FOR_INDEXING = "useGpuForIndexing"; - public static final String INDEX_WORKER_THREADS = "indexWorkerThreads"; public static final String TELEMETRY_OPT_IN = "telemetryOptIn"; public static final String Q_DATA_SHARING = "qDataSharing"; public static final String HTTPS_PROXY = "httpsProxy"; public static final String CA_CERT = "customCaCert"; - private Boolean isWorkspaceIndexChecked; - private Boolean isGpuIndexingChecked; - private int indexWorkerThreads; - - private Boolean changedWorkspaceIndexChecked; - private Boolean changedGpuIndexingChecked; - private int changedIndexWorkerThreads; - private Boolean isTelemetryOptInChecked; private Boolean isQDataSharingOptInChecked; @@ -67,12 +56,6 @@ public AmazonQPreferencePage() { @Override public final void init(final IWorkbench workbench) { - isWorkspaceIndexChecked = preferenceStore.getBoolean(WORKSPACE_INDEX); - changedWorkspaceIndexChecked = preferenceStore.getBoolean(WORKSPACE_INDEX); - isGpuIndexingChecked = preferenceStore.getBoolean(USE_GPU_FOR_INDEXING); - changedGpuIndexingChecked = preferenceStore.getBoolean(USE_GPU_FOR_INDEXING); - indexWorkerThreads = preferenceStore.getInt(INDEX_WORKER_THREADS); - changedIndexWorkerThreads = preferenceStore.getInt(INDEX_WORKER_THREADS); isTelemetryOptInChecked = preferenceStore.getBoolean(TELEMETRY_OPT_IN); changedTelemetryOptInChecked = preferenceStore.getBoolean(TELEMETRY_OPT_IN); isQDataSharingOptInChecked = preferenceStore.getBoolean(Q_DATA_SHARING); @@ -87,10 +70,6 @@ protected final void createFieldEditors() { createHorizontalSeparator(); createHeading("Code Suggestions"); createCodeReferenceOptInField(); - createHeading("Workspace Indexing"); - createWorkspaceIndexField(); - createUseGpuForIndexingField(); - createIndexWorkerThreadsField(); createHeading("Data Sharing"); createTelemetryOptInField(); createHorizontalSeparator(); @@ -148,65 +127,6 @@ public void widgetSelected(final SelectionEvent event) { }); } - private void createWorkspaceIndexField() { - Composite workspaceIndexComposite = new Composite(getFieldEditorParent(), SWT.NONE); - workspaceIndexComposite.setLayout(new GridLayout(2, false)); - GridData workspaceIndexCompositeData = new GridData(SWT.FILL, SWT.CENTER, true, false); - workspaceIndexCompositeData.horizontalIndent = 20; - workspaceIndexComposite.setLayoutData(workspaceIndexCompositeData); - - BooleanFieldEditor workspaceIndex = new BooleanFieldEditor(WORKSPACE_INDEX, "Workspace Index", workspaceIndexComposite) { - @Override - protected void valueChanged(final boolean oldValue, final boolean newValue) { - isWorkspaceIndexChecked = newValue; - } - }; - addField(workspaceIndex); - - createLabel(""" - When you add @workspace to your question in Amazon Q chat, Amazon Q will index your workspace files locally\ - \nto use as context for its response. Extra CPU usage is expected while indexing a workspace. This will not\ - \nimpact Amazon Q features or your IDE, but you may manage CPU usage by setting the number of index threads. - """, 20, workspaceIndexComposite); - } - - private void createUseGpuForIndexingField() { - Composite useGpuComposite = new Composite(getFieldEditorParent(), SWT.NONE); - useGpuComposite.setLayout(new GridLayout(2, false)); - GridData useGpuCompositeData = new GridData(SWT.FILL, SWT.CENTER, true, false); - useGpuCompositeData.horizontalIndent = 20; - useGpuComposite.setLayoutData(useGpuCompositeData); - - BooleanFieldEditor useGpuForIndexing = new BooleanFieldEditor(USE_GPU_FOR_INDEXING, "Use GPU for Indexing", useGpuComposite) { - @Override - protected void valueChanged(final boolean oldValue, final boolean newValue) { - isGpuIndexingChecked = newValue; - } - }; - addField(useGpuForIndexing); - - createLabel(""" - Enable GPU to help index your local workspace files. Only applies to Linux and Windows. - """, 20, useGpuComposite); - } - - private void createIndexWorkerThreadsField() { - Composite indexWorkerThreadsComposite = new Composite(getFieldEditorParent(), SWT.NONE); - indexWorkerThreadsComposite.setLayout(new GridLayout(2, false)); - GridData indexWorkerThreadsCompositeData = new GridData(SWT.LEFT, SWT.CENTER, true, false); - indexWorkerThreadsCompositeData.horizontalIndent = 20; - indexWorkerThreadsComposite.setLayoutData(indexWorkerThreadsCompositeData); - - StringFieldEditor indexWorkerThreads = new StringFieldEditor(INDEX_WORKER_THREADS, "Index Worker Threads", 10, indexWorkerThreadsComposite); - addField(indexWorkerThreads); - - createLabel(""" - Number of worker threads of Amazon Q local index process. '0' will use the system default worker threads for balance\ - \nperformance. You may increase this number to more quickly index your workspace, but only up to your hardware's number\ - \nof CPU cores. Please restart Eclipse after changing worker threads. - """, 20, getFieldEditorParent()); - } - private void createTelemetryOptInField() { Composite telemetryOptInComposite = new Composite(getFieldEditorParent(), SWT.NONE); telemetryOptInComposite.setLayout(new GridLayout(2, false)); @@ -351,23 +271,6 @@ private void sendUpdatedPreferences() { isQDataSharingOptInChecked = changedDataSharingOptInChecked; } - if (changedWorkspaceIndexChecked != isWorkspaceIndexChecked) { - AwsTelemetryProvider.emitModifySettingEvent("amazonQ.workspaceIndexing", - changedWorkspaceIndexChecked.toString()); - isWorkspaceIndexChecked = changedWorkspaceIndexChecked; - } - - if (changedGpuIndexingChecked != isGpuIndexingChecked) { - AwsTelemetryProvider.emitModifySettingEvent("amazonQ.gpuIndexing", - changedGpuIndexingChecked.toString()); - isGpuIndexingChecked = changedGpuIndexingChecked; - } - - if (changedIndexWorkerThreads != indexWorkerThreads) { - AwsTelemetryProvider.emitModifySettingEvent("amazonQ.indexThreads", - String.valueOf(changedIndexWorkerThreads)); - indexWorkerThreads = changedIndexWorkerThreads; - } ThreadingUtils.executeAsyncTask(() -> CustomizationUtil.triggerChangeConfigurationNotification()); } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/util/Constants.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/util/Constants.java index e3726514c..83af12c3e 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/util/Constants.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/util/Constants.java @@ -11,10 +11,6 @@ private Constants() { public static final String CUSTOMIZATION_STORAGE_INTERNAL_KEY = "aws.q.customization.eclipse"; public static final String LSP_CUSTOMIZATION_CONFIGURATION_KEY = "customization"; - public static final String LSP_PROJECT_CONTEXT_CONFIGURATION_KEY = "projectContext"; - public static final String LSP_INDEXING_CONFIGURATION_KEY = "enableLocalIndexing"; - public static final String LSP_GPU_INDEXING_CONFIGURATION_KEY = "enableGpuAcceleration"; - public static final String LSP_INDEX_THREADS_CONFIGURATION_KEY = "indexWorkerThreads"; public static final String LSP_ENABLE_TELEMETRY_EVENTS_CONFIGURATION_KEY = "enableTelemetryEventsToDestination"; public static final String LSP_OPT_OUT_TELEMETRY_CONFIGURATION_KEY = "optOutTelemetry"; public static final String LSP_Q_CONFIGURATION_KEY = "aws.q"; From 672450d788a7991fc9993164e49a09337537aeeb Mon Sep 17 00:00:00 2001 From: Will Lo <96078566+Will-ShaoHua@users.noreply.github.com> Date: Tue, 28 Apr 2026 11:22:53 -0700 Subject: [PATCH 32/40] feat: maintenance-mode UI for Amazon Q login (#544) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(auth): add maintenance-mode UI to Amazon Q login webview Mirrors the JetBrains sign-in redesign so Eclipse matches the same look/feel. - qOptions.vue: add centered "Welcome to Amazon Q" header, yellow maintenance warning banner with Learn more link, disabled "Create New Account" button with lock icon, and "existing users" divider. Update Builder ID / Identity Center titles and descriptions and change the primary button label to "Sign in with existing account". - common.scss: tighten shared login sizing to align with the VSCode sign-in view. Base font 15→13px, title 15→14px, section gap 20→12px, login-flow-button 55→30px tall, border-radius 3→4px, bottom margin 100→12px, container max-width 300→260px. - selectableItem.vue: shrink the option card — icons 20→16px, padding 15→10/12px, switch fixed 38px height to min-height 50px, title 13px bold, description 12px with wrapping so the longer Identity Center text doesn't get clipped. - logo.vue: shrink the AMAZONQ logo 100→56px (Toolkit 100→72px), drop wrapper padding-top 75→65px, and center the login-stage logo so it matches the JetBrains/VSCode presentation. * feat(auth): route maintenance banner Learn more through IDE openUrl Add OPEN_URL command so the webview can delegate external link opens to the host, which surfaces the standard Amazon Q confirm dialog before launching the system browser instead of navigating inside JCEF. * fix(auth): show maintenance banner for returning users too Move the Welcome header, maintenance banner, disabled Create New Account button, and existing-users divider above the existing connections list so they always render — not just for new users. Returning users with saved SSO URLs now see the same maintenance messaging. Also rename 'Connect with an existing account' → 'Connect with an account' since the section now appears below the banner context. * fix(auth): flatten login flow and remember last IdC input - Remove separate 'Connect with an account' section from qOptions.vue. Existing connections now appear inline under the 'existing users' divider alongside Builder ID and Identity Center, matching VS Code and JetBrains. - Persist last-used Identity Center startUrl and region to Eclipse preferences after successful IdC login. Pre-populate the SSO form with these values on next load so returning users don't have to re-enter their organization's start URL and region. * fix(auth): use string concatenation for JS object to avoid format issues Replace String.format with concatenation to prevent potential issues if the regions JSON contains percent characters that would be misinterpreted as format specifiers. * fix(auth): force-populate SSO Start URL via $nextTick fallback Vue 3's mounted() hook does not fire reliably for ssoLoginForm inside the SWT Browser, so v-model's initial DOM sync for the Start URL input was being skipped and the persisted IdC URL never appeared in the field on the first visit to the SSO form. Switch the computed get/set pattern to local data() fields synced back to Vuex via watch, and schedule a $nextTick callback from created() that writes the persisted Start URL directly into the input once Vue flushes its render. The callback bails if the user has already typed (el.value non-empty) so it never overwrites user input. * chore: update maintenance banner to end-of-support wording Update banner message from 'maintenance mode' to 'end of support on April 30, 2027' across all login surfaces. * chore: update banner wording — new accounts unavailable starting 5/15 --- .../amazonq/views/LoginViewActionHandler.java | 51 +++-- .../eclipse/amazonq/views/model/Command.java | 3 +- plugin/webview/src/q-ui/assets/common.scss | 14 +- plugin/webview/src/q-ui/components/logo.vue | 14 +- .../webview/src/q-ui/components/qOptions.vue | 174 ++++++++++++++++-- .../src/q-ui/components/selectableItem.vue | 27 ++- .../src/q-ui/components/ssoLoginForm.vue | 61 +++--- 7 files changed, 260 insertions(+), 84 deletions(-) diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/LoginViewActionHandler.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/LoginViewActionHandler.java index f6c41fcf4..0581914ef 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/LoginViewActionHandler.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/LoginViewActionHandler.java @@ -13,6 +13,7 @@ import software.amazon.awssdk.regions.servicemetadata.OidcServiceMetadata; import software.amazon.awssdk.utils.StringUtils; +import software.aws.toolkits.eclipse.amazonq.configuration.DefaultPluginStore; import software.aws.toolkits.eclipse.amazonq.configuration.customization.CustomizationUtil; import software.aws.toolkits.eclipse.amazonq.configuration.profiles.QDeveloperProfileUtil; import software.aws.toolkits.eclipse.amazonq.lsp.auth.model.LoginIdcParams; @@ -21,6 +22,7 @@ import software.aws.toolkits.eclipse.amazonq.plugin.Activator; import software.aws.toolkits.eclipse.amazonq.util.AwsRegion; import software.aws.toolkits.eclipse.amazonq.util.JsonHandler; +import software.aws.toolkits.eclipse.amazonq.util.PluginUtils; import software.aws.toolkits.eclipse.amazonq.util.ThemeDetector; import software.aws.toolkits.eclipse.amazonq.util.ThreadingUtils; import software.aws.toolkits.eclipse.amazonq.views.model.Command; @@ -31,6 +33,8 @@ public class LoginViewActionHandler implements ViewActionHandler { private static final JsonHandler JSON_HANDLER = new JsonHandler(); private static final ThemeDetector THEME_DETECTOR = new ThemeDetector(); + private static final String LAST_IDC_START_URL_KEY = "lastIdcStartUrl"; + private static final String LAST_IDC_REGION_KEY = "lastIdcRegion"; private Future loginTask; private boolean isLoginTaskRunning = false; @@ -57,6 +61,9 @@ public final void handleCommand(final ParsedCommand parsedCommand, final Browser } Activator.getLoginService().login(LoginType.IAM_IDENTITY_CENTER, new LoginParams().setLoginIdcParams(loginIdcParams)).get(); + // Persist last-used IdC info for next login + DefaultPluginStore.getInstance().put(LAST_IDC_START_URL_KEY, url); + DefaultPluginStore.getInstance().put(LAST_IDC_REGION_KEY, region); if (QDeveloperProfileUtil.getInstance().isProfileSelectionRequired()) { Map profilesData = new HashMap<>(); var profiles = QDeveloperProfileUtil.getInstance().getDeveloperProfiles(); @@ -87,21 +94,27 @@ public final void handleCommand(final ParsedCommand parsedCommand, final Browser + oidcMetadata.regions().stream().filter(region -> region.metadata().partition().id().equals("aws")) .map(AwsRegion::from).map(AwsRegion::toString).collect(Collectors.joining(",")) + "]"; - var js = String.format(""" - { - stage: '%s', - regions: %s, - cancellable: false, - idcInfo: { - profileName: '', - startUrl: '', - region: 'us-east-1' - }, - feature: 'q', - existConnections: [], - profiles: [] - } - """, "START", regions).stripIndent(); + String lastStartUrl = DefaultPluginStore.getInstance().get(LAST_IDC_START_URL_KEY); + String lastRegion = DefaultPluginStore.getInstance().get(LAST_IDC_REGION_KEY); + if (lastStartUrl == null) { + lastStartUrl = ""; + } + if (lastRegion == null || lastRegion.isEmpty()) { + lastRegion = "us-east-1"; + } + var js = "{" + + "stage: 'START'," + + "regions: " + regions + "," + + "cancellable: false," + + "idcInfo: {" + + " profileName: ''," + + " startUrl: '" + lastStartUrl + "'," + + " region: '" + lastRegion + "'" + + "}," + + "feature: 'q'," + + "existConnections: []," + + "profiles: []" + + "}"; browser.execute("changeTheme(" + THEME_DETECTOR.isDarkTheme() + ");"); browser.execute(String.format("ideClient.prepareUi(%s)", js)); browser.execute("ideClient.updateAuthorization('')"); @@ -112,6 +125,14 @@ public final void handleCommand(final ParsedCommand parsedCommand, final Browser CustomizationUtil.validateCurrentCustomization(); }); break; + case OPEN_URL: + if (params instanceof Map) { + var urlValue = ((Map) params).get("url"); + if (urlValue instanceof String && !((String) urlValue).isEmpty()) { + PluginUtils.handleExternalLinkClick((String) urlValue); + } + } + break; default: Activator.getLogger() .error("Unexpected command received from Amazon Q Login: " + parsedCommand.getCommand()); diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/model/Command.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/model/Command.java index 068c8cd34..1dcceffed 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/model/Command.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/model/Command.java @@ -51,7 +51,8 @@ public enum Command { LOGIN_IDC("loginIdC"), CANCEL_LOGIN("cancelLogin"), ON_LOAD("onLoad"), - ON_SELECT_PROFILE("onSelectProfile"); + ON_SELECT_PROFILE("onSelectProfile"), + OPEN_URL("openUrl"); private final String commandString; diff --git a/plugin/webview/src/q-ui/assets/common.scss b/plugin/webview/src/q-ui/assets/common.scss index 8d648863a..9cd517d7a 100644 --- a/plugin/webview/src/q-ui/assets/common.scss +++ b/plugin/webview/src/q-ui/assets/common.scss @@ -24,20 +24,20 @@ a { .font-amazon { font-family: "Amazon Ember", sans-serif; user-select: none; - font-size: 15px; + font-size: 13px; } .bottom-small-gap { - margin-bottom: 20px !important; + margin-bottom: 12px !important; } .login-flow-button { width: 100%; - height: 55px; - border-radius: 3px; + height: 30px; + border-radius: 4px; border-width: 0; font-weight: bold; - margin-bottom: 100px; + margin-bottom: 12px; } .continue-button { @@ -54,7 +54,7 @@ a { .title { margin-bottom: 5px; margin-top: 5px; - font-size: 15px; + font-size: 14px; font-weight: bold; } @@ -99,7 +99,7 @@ button:not([disabled]), .item-container { } .centered-with-max-width { - max-width: 300px; + max-width: 260px; margin: 0 auto; } diff --git a/plugin/webview/src/q-ui/components/logo.vue b/plugin/webview/src/q-ui/components/logo.vue index ea290f292..88c2e23fb 100644 --- a/plugin/webview/src/q-ui/components/logo.vue +++ b/plugin/webview/src/q-ui/components/logo.vue @@ -36,8 +36,8 @@ export default defineComponent({
-
-
Connect with an existing account:
-
- -
+
Welcome to Amazon Q
+
+ + + Amazon Q Developer IDE plugins will reach end of support on April 30, 2027. New accounts will no longer available starting 5/15, but existing users can still sign-in below. + Learn more +
+ +
existing users
-
Choose a sign-in option:
+
+ +
-
@@ -88,6 +115,13 @@ export default defineComponent({ this.selectedLoginOption = itemId window.telemetryApi.postClickEvent(itemId + "Option") }, + handleLearnMoreClick() { + window.telemetryApi.postClickEvent("maintenanceLearnMoreLink") + window.ideApi.postMessage({ + command: 'openUrl', + params: { url: 'https://aws.amazon.com/q/developer/' } + }) + }, handleBackButtonClick() { this.$emit('backToMenu') }, @@ -128,4 +162,106 @@ export default defineComponent({ diff --git a/plugin/webview/src/q-ui/components/selectableItem.vue b/plugin/webview/src/q-ui/components/selectableItem.vue index 22a2909db..358ce698b 100644 --- a/plugin/webview/src/q-ui/components/selectableItem.vue +++ b/plugin/webview/src/q-ui/components/selectableItem.vue @@ -6,8 +6,8 @@
.item-container { - padding: 15px; + padding: 10px 12px; display: flex; align-items: center; - height: 38px; + min-height: 50px; + box-sizing: border-box; } .selected { @@ -105,6 +106,7 @@ export default defineComponent({ } .item-title { + font-size: 13px; font-weight: bold; margin-bottom: 2px; } @@ -112,11 +114,20 @@ export default defineComponent({ .text { display: flex; flex-direction: column; - font-size: 15px; + font-size: 12px; + min-width: 0; +} + +.p { + line-height: 1.4; + white-space: normal; } .icon { - margin-right: 15px; + margin-right: 11px; + display: flex; + align-items: center; + flex-shrink: 0; } /* Theme specific styles */ diff --git a/plugin/webview/src/q-ui/components/ssoLoginForm.vue b/plugin/webview/src/q-ui/components/ssoLoginForm.vue index 1dedaf8ba..9a905b3ad 100644 --- a/plugin/webview/src/q-ui/components/ssoLoginForm.vue +++ b/plugin/webview/src/q-ui/components/ssoLoginForm.vue @@ -68,9 +68,41 @@ export default defineComponent({ app: String }, data() { + const info = this.$store.state.lastLoginIdcInfo return { startUrlRegex: /^https:\/\/(([\w-]+(?:\.gamma)?\.awsapps\.com\/start(?:-beta|-alpha)?[\/#]?)|(start\.(?:us-gov-home|us-gov-east-1\.us-gov-home|us-gov-west-1\.us-gov-home)\.awsapps\.com|start\.(?:home|cn-north-1\.home|cn-northwest-1\.home)\.awsapps\.cn)\/directory\/[\w-]+[\/#]?)$/, - issueUrlRegex: /^https:\/\/([\w-]+\.)?identitycenter\.(amazonaws\.com|amazonaws\.com\.cn|us-gov\.amazonaws\.com)\/[\w\/-]+[\/#]?$/ + issueUrlRegex: /^https:\/\/([\w-]+\.)?identitycenter\.(amazonaws\.com|amazonaws\.com\.cn|us-gov\.amazonaws\.com)\/[\w\/-]+[\/#]?$/, + startUrl: info.startUrl || '', + selectedRegion: info.region || 'us-east-1' + } + }, + // SWT Browser + Vue 3 quirk: the mounted() hook on this component does not fire reliably, + // so v-model's initial DOM sync is skipped. Push the persisted Start URL into the input + // from created() via $nextTick — that callback runs after Vue's next render flush and + // does not depend on the mounted lifecycle hook firing. + created() { + const expected = this.startUrl + if (!expected) return + this.$nextTick(() => { + const el = document.getElementById("startUrl") as HTMLInputElement | null + if (!el || el.value) return // don't overwrite if user already typed + el.value = expected + el.dispatchEvent(new Event('input', { bubbles: true })) + el.focus() + }) + }, + watch: { + startUrl(value: string) { + window.ideClient.updateLastLoginIdcInfo({ + ...this.$store.state.lastLoginIdcInfo, + startUrl: value + }) + }, + selectedRegion(value: string) { + window.ideClient.updateLastLoginIdcInfo({ + ...this.$store.state.lastLoginIdcInfo, + region: value + }) } }, computed: { @@ -79,7 +111,7 @@ export default defineComponent({ const otherRegions = this.regions .filter(r => r.id !== 'us-east-1') .sort((a, b) => a.name.localeCompare(b.name)); - + return usEast1 ? [usEast1, ...otherRegions] : otherRegions; }, regions(): Region[] { @@ -88,28 +120,6 @@ export default defineComponent({ feature(): Feature { return this.$store.state.feature }, - startUrl: { - get() { - return this.$store.state.lastLoginIdcInfo.startUrl; - }, - set(value: string) { - window.ideClient.updateLastLoginIdcInfo({ - ...this.$store.state.lastLoginIdcInfo, - startUrl: value - }) - } - }, - selectedRegion: { - get() { - return this.$store.state.lastLoginIdcInfo.region; - }, - set(value: string) { - window.ideClient.updateLastLoginIdcInfo({ - ...this.$store.state.lastLoginIdcInfo, - region: value - }) - } - }, isStartUrlValid: { get() { return this.startUrlRegex.test(this.startUrl) || this.issueUrlRegex.test(this.startUrl) @@ -152,9 +162,6 @@ export default defineComponent({ handleCodeCatalystSignin() { this.$emit('login', new BuilderId()) } - }, - mounted() { - document.getElementById("startUrl")?.focus() } }) From 4061cd954c611cb08da810b64ec11a139455d1c8 Mon Sep 17 00:00:00 2001 From: Will Lo <96078566+Will-ShaoHua@users.noreply.github.com> Date: Tue, 28 Apr 2026 12:20:16 -0700 Subject: [PATCH 33/40] feat(notifications): add startup toast for KTLO message broadcast (#545) * feat(notifications): add one-time Kiro sunset toast on startup Shows a sticky Mylyn notification popup once on plugin startup with Dismiss and Learn more actions. Learn more opens https://kiro.dev in the external browser. Dismissal is persisted in the plugin store so the toast does not reappear on subsequent launches. * chore: update sunset toast to end-of-support wording - Title: 'Amazon Q Developer end of support' - Body: end-of-support April 30 2027, new accounts unavailable 5/15 - Learn more URL: https://aws.amazon.com/q/developer/ --- .../amazonq/lsp/LspStartupActivity.java | 13 ++++ .../eclipse/amazonq/util/Constants.java | 5 ++ .../amazonq/util/KiroSunsetNotification.java | 64 +++++++++++++++++++ 3 files changed, 82 insertions(+) create mode 100644 plugin/src/software/aws/toolkits/eclipse/amazonq/util/KiroSunsetNotification.java diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/LspStartupActivity.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/LspStartupActivity.java index 8e9d0cbac..9b661a14a 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/LspStartupActivity.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/LspStartupActivity.java @@ -26,6 +26,7 @@ import software.aws.toolkits.eclipse.amazonq.util.AutoTriggerPartListener; import software.aws.toolkits.eclipse.amazonq.util.AutoTriggerTopLevelListener; import software.aws.toolkits.eclipse.amazonq.util.Constants; +import software.aws.toolkits.eclipse.amazonq.util.KiroSunsetNotification; import software.aws.toolkits.eclipse.amazonq.util.ThreadingUtils; import software.aws.toolkits.eclipse.amazonq.util.ToolkitNotification; import software.aws.toolkits.eclipse.amazonq.util.UpdateUtils; @@ -80,10 +81,22 @@ private void schedulePostStartupJobs() { Display.getDefault().asyncExec(() -> launchWebview()); } Display.getDefault().asyncExec(() -> attachAutoTriggerListenersIfApplicable()); + Display.getDefault().asyncExec(() -> showKiroSunsetNotification()); checkForUpdates(); }); } + private void showKiroSunsetNotification() { + if (Activator.getPluginStore().get(Constants.KIRO_SUNSET_NOTIFICATION_DISMISSED_KEY) != null) { + return; + } + AbstractNotificationPopup notification = new KiroSunsetNotification(Display.getCurrent(), + Constants.KIRO_SUNSET_NOTIFICATION_TITLE, + Constants.KIRO_SUNSET_NOTIFICATION_BODY, + () -> Activator.getPluginStore().put(Constants.KIRO_SUNSET_NOTIFICATION_DISMISSED_KEY, "true")); + notification.open(); + } + private void checkForUpdates() { Job updateCheckJob = new Job("Check for updates") { @Override diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/util/Constants.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/util/Constants.java index 83af12c3e..ae97455fa 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/util/Constants.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/util/Constants.java @@ -40,6 +40,11 @@ private Constants() { public static final String TELEMETRY_NOTIFICATION_TITLE = "AWS IDE plugins telemetry"; public static final String TELEMETRY_NOTIFICATION_BODY = "Usage metrics are collected by default. This can be changed in the \"Amazon Q\" section" + " of the IDE preferences."; + public static final String KIRO_SUNSET_NOTIFICATION_TITLE = "Amazon Q Developer end of support"; + public static final String KIRO_SUNSET_NOTIFICATION_BODY = "Amazon Q Developer IDE plugins will reach end of support on April 30, 2027." + + " New accounts will no longer available starting 5/15, but existing users can still sign-in below."; + public static final String KIRO_SUNSET_LEARN_MORE_URL = "https://aws.amazon.com/q/developer/"; + public static final String KIRO_SUNSET_NOTIFICATION_DISMISSED_KEY = "kiroSunsetNotificationDismissed"; public static final String RE_AUTHENTICATE_FAILURE_MESSAGE = "An error occurred while attempting to re-authenticate. Please try again."; public static final String AUTHENTICATE_FAILURE_MESSAGE = "An error occurred while attempting to authenticate. Please try again."; public static final String IDE_SSL_HANDSHAKE_TITLE = "SSL Handshake Error"; diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/util/KiroSunsetNotification.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/util/KiroSunsetNotification.java new file mode 100644 index 000000000..a00bb85a1 --- /dev/null +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/util/KiroSunsetNotification.java @@ -0,0 +1,64 @@ +// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.eclipse.amazonq.util; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; + +public final class KiroSunsetNotification extends ToolkitNotification { + + private final Runnable onDismiss; + + public KiroSunsetNotification(final Display display, final String title, + final String description, final Runnable onDismiss) { + super(display, title, description); + this.onDismiss = onDismiss; + } + + @Override + protected void createContentArea(final Composite parent) { + super.createContentArea(parent); + + Composite buttonRow = new Composite(parent, SWT.NONE); + GridLayout layout = new GridLayout(2, false); + layout.marginWidth = 0; + layout.marginHeight = 0; + buttonRow.setLayout(layout); + buttonRow.setLayoutData(new GridData(SWT.END, SWT.CENTER, true, false)); + + Button learnMoreButton = new Button(buttonRow, SWT.PUSH); + learnMoreButton.setText("Learn more"); + learnMoreButton.setLayoutData(new GridData(SWT.END, SWT.CENTER, false, false)); + learnMoreButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(final SelectionEvent e) { + PluginUtils.openWebpage(Constants.KIRO_SUNSET_LEARN_MORE_URL); + dismiss(); + } + }); + + Button dismissButton = new Button(buttonRow, SWT.PUSH); + dismissButton.setText("Dismiss"); + dismissButton.setLayoutData(new GridData(SWT.END, SWT.CENTER, false, false)); + dismissButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(final SelectionEvent e) { + dismiss(); + } + }); + } + + private void dismiss() { + if (onDismiss != null) { + onDismiss.run(); + } + close(); + } +} From 98a7c7075dc866e15be23ae78be7997918b6cb9e Mon Sep 17 00:00:00 2001 From: Will Lo <96078566+Will-ShaoHua@users.noreply.github.com> Date: Wed, 29 Apr 2026 13:11:54 -0700 Subject: [PATCH 34/40] ci: upload plugin JAR and update site as build artifacts (#547) Add upload-artifact steps to the Maven build workflow so the plugin JAR and p2 update site ZIP are downloadable from the Actions tab on every PR and main push. Retained for 30 days. --- .github/workflows/maven.yml | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 595fe0030..1535d84c0 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -1,12 +1,9 @@ -# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time -# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-maven +# This workflow builds the Eclipse plugin with Maven/Tycho and uploads +# the plugin JAR and p2 update site as downloadable artifacts. +# +# Artifacts are available from the Actions tab on every PR and main push. -# This workflow uses actions that are not certified by GitHub. -# They are provided by a third-party and are governed by -# separate terms of service, privacy policy, and support -# documentation. - -name: Maven Sanity Build +name: Maven Build on: push: @@ -14,17 +11,16 @@ on: pull_request: branches: [ "main", "feature/*" ] - permissions: contents: read jobs: build: - runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - name: Setup Maven Action uses: s4u/setup-maven-action@v1.18.0 with: @@ -32,5 +28,24 @@ jobs: java-version: 17 java-distribution: temurin maven-version: 3.9.8 + - name: Build with Maven run: mvn -B package --file pom.xml + + - name: Upload plugin JAR + if: success() + uses: actions/upload-artifact@v4 + with: + name: amazon-q-eclipse-plugin + path: plugin/target/amazon-q-eclipse-*.jar + if-no-files-found: error + retention-days: 30 + + - name: Upload update site + if: success() + uses: actions/upload-artifact@v4 + with: + name: amazon-q-eclipse-update-site + path: updatesite/target/amazon-q-eclipse-update-site-*.zip + if-no-files-found: error + retention-days: 30 From 7b12d468399a51512fe73c8cbbc57e7edf4a1768 Mon Sep 17 00:00:00 2001 From: Will Lo <96078566+Will-ShaoHua@users.noreply.github.com> Date: Wed, 29 Apr 2026 15:16:05 -0700 Subject: [PATCH 35/40] fix: prevent Learn More from suppressing sunset toast and fix popup clipping (#549) * ci: upload plugin JAR and update site as build artifacts Add upload-artifact steps to the Maven build workflow so the plugin JAR and p2 update site ZIP are downloadable from the Actions tab on every PR and main push. Retained for 30 days. * fix(notifications): prevent Learn More from suppressing sunset toast and fix popup clipping - Learn More now opens the URL and closes the popup without persisting the dismissed key, so the notification reappears on next launch - Only the explicit Dismiss button suppresses future appearances - Fix notification popup height calculation to use width-constrained computeSize so wrapped text and button row are not clipped --- .../eclipse/amazonq/util/KiroSunsetNotification.java | 1 - .../toolkits/eclipse/amazonq/util/ToolkitNotification.java | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/util/KiroSunsetNotification.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/util/KiroSunsetNotification.java index a00bb85a1..ad584e6dd 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/util/KiroSunsetNotification.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/util/KiroSunsetNotification.java @@ -40,7 +40,6 @@ protected void createContentArea(final Composite parent) { @Override public void widgetSelected(final SelectionEvent e) { PluginUtils.openWebpage(Constants.KIRO_SUNSET_LEARN_MORE_URL); - dismiss(); } }); diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/util/ToolkitNotification.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/util/ToolkitNotification.java index 1363930b9..1ad9461d9 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/util/ToolkitNotification.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/util/ToolkitNotification.java @@ -72,9 +72,9 @@ protected final String getPopupShellTitle() { @Override protected final void initializeBounds() { Rectangle clArea = getPrimaryClientArea(); - Point initialSize = getShell().computeSize(SWT.DEFAULT, SWT.DEFAULT); - int height = Math.max(initialSize.y, MIN_HEIGHT); - int width = Math.min(initialSize.x, MAX_WIDTH); + int width = Math.min(getShell().computeSize(SWT.DEFAULT, SWT.DEFAULT).x, MAX_WIDTH); + // Recompute height with the constrained width so wrapped text and buttons are accounted for + int height = Math.max(getShell().computeSize(width, SWT.DEFAULT).y, MIN_HEIGHT); Point size = new Point(width, height); // Calculate the position for the new notification int x = clArea.x + clArea.width - size.x - PADDING_EDGE; From 43228a8e0211269a5a82ac7ab7d47cbda6263e64 Mon Sep 17 00:00:00 2001 From: Will Lo <96078566+Will-ShaoHua@users.noreply.github.com> Date: Thu, 30 Apr 2026 12:01:22 -0700 Subject: [PATCH 36/40] feat: update sunset notification message and Learn More URL (#550) - Update message: 'starting May 15, 2026' instead of 'starting 5/15' - Update Learn More URL to https://aws.amazon.com/blogs/devops/amazon-q-developer-end-of-support-announcement/ --- .../software/aws/toolkits/eclipse/amazonq/util/Constants.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/util/Constants.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/util/Constants.java index ae97455fa..822d42c5b 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/util/Constants.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/util/Constants.java @@ -42,8 +42,8 @@ private Constants() { + " of the IDE preferences."; public static final String KIRO_SUNSET_NOTIFICATION_TITLE = "Amazon Q Developer end of support"; public static final String KIRO_SUNSET_NOTIFICATION_BODY = "Amazon Q Developer IDE plugins will reach end of support on April 30, 2027." - + " New accounts will no longer available starting 5/15, but existing users can still sign-in below."; - public static final String KIRO_SUNSET_LEARN_MORE_URL = "https://aws.amazon.com/q/developer/"; + + " New accounts will no longer be available starting May 15, 2026, but existing users can still sign-in below."; + public static final String KIRO_SUNSET_LEARN_MORE_URL = "https://aws.amazon.com/blogs/devops/amazon-q-developer-end-of-support-announcement/"; public static final String KIRO_SUNSET_NOTIFICATION_DISMISSED_KEY = "kiroSunsetNotificationDismissed"; public static final String RE_AUTHENTICATE_FAILURE_MESSAGE = "An error occurred while attempting to re-authenticate. Please try again."; public static final String AUTHENTICATE_FAILURE_MESSAGE = "An error occurred while attempting to authenticate. Please try again."; From 224e11e978c5fec8e3cdf76a715961b501fa5699 Mon Sep 17 00:00:00 2001 From: Will Lo <96078566+Will-ShaoHua@users.noreply.github.com> Date: Thu, 30 Apr 2026 15:40:26 -0700 Subject: [PATCH 37/40] chore: bump version to 2.7.1 (#551) --- feature/feature.xml | 4 ++-- feature/pom.xml | 2 +- plugin/META-INF/MANIFEST.MF | 2 +- plugin/pom.xml | 2 +- pom.xml | 2 +- telemetry/pom.xml | 2 +- updatesite/category.xml | 2 +- updatesite/pom.xml | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/feature/feature.xml b/feature/feature.xml index 9448c6935..076456415 100644 --- a/feature/feature.xml +++ b/feature/feature.xml @@ -2,7 +2,7 @@ + version="2.7.1.qualifier"> Amazon Q Developer helps users build faster across the entire software development lifecycle by providing tailored responses and code recommendations that conform to their team's internal libraries, proprietary algorithmic techniques, and enterprise code style. @@ -198,6 +198,6 @@ https://github.com/aws/amazon-q-eclipse/blob/main/attribution.xml id="amazon-q-eclipse" download-size="11000" install-size="0" - version="2.7.0.qualifier" + version="2.7.1.qualifier" unpack="false"/> diff --git a/feature/pom.xml b/feature/pom.xml index 1b0797e9c..f6f4de743 100644 --- a/feature/pom.xml +++ b/feature/pom.xml @@ -6,7 +6,7 @@ software.aws.toolkits.eclipse amazon-q-eclipse-group - 2.7.0-SNAPSHOT + 2.7.1-SNAPSHOT ../ diff --git a/plugin/META-INF/MANIFEST.MF b/plugin/META-INF/MANIFEST.MF index 38189c395..4cd3c6690 100644 --- a/plugin/META-INF/MANIFEST.MF +++ b/plugin/META-INF/MANIFEST.MF @@ -4,7 +4,7 @@ Bundle-Name: Amazon Q for Eclipse Bundle-Provider: Amazon Web Services Bundle-RequiredExecutionEnvironment: JavaSE-17 Bundle-SymbolicName: amazon-q-eclipse;singleton:=true -Bundle-Version: 2.7.0.qualifier +Bundle-Version: 2.7.1.qualifier Automatic-Module-Name: amazon.q.eclipse Bundle-ActivationPolicy: lazy Bundle-Activator: software.aws.toolkits.eclipse.amazonq.plugin.Activator diff --git a/plugin/pom.xml b/plugin/pom.xml index a1a0756c5..c5d267fb3 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -6,7 +6,7 @@ software.aws.toolkits.eclipse amazon-q-eclipse-group - 2.7.0-SNAPSHOT + 2.7.1-SNAPSHOT ../ diff --git a/pom.xml b/pom.xml index c80dd0b86..448c7fc8c 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ software.aws.toolkits.eclipse amazon-q-eclipse-group - 2.7.0-SNAPSHOT + 2.7.1-SNAPSHOT pom diff --git a/telemetry/pom.xml b/telemetry/pom.xml index 7c31dd584..6b4857375 100644 --- a/telemetry/pom.xml +++ b/telemetry/pom.xml @@ -6,7 +6,7 @@ software.aws.toolkits.eclipse amazon-q-eclipse-group - 2.7.0-SNAPSHOT + 2.7.1-SNAPSHOT ../ diff --git a/updatesite/category.xml b/updatesite/category.xml index 532c1e272..1f72a6e65 100644 --- a/updatesite/category.xml +++ b/updatesite/category.xml @@ -1,6 +1,6 @@ - + diff --git a/updatesite/pom.xml b/updatesite/pom.xml index fc7d4c829..94f367880 100644 --- a/updatesite/pom.xml +++ b/updatesite/pom.xml @@ -6,7 +6,7 @@ software.aws.toolkits.eclipse amazon-q-eclipse-group - 2.7.0-SNAPSHOT + 2.7.1-SNAPSHOT ../ From 91f237e4358998967046b09ec2679d63bb3cbd3a Mon Sep 17 00:00:00 2001 From: Will Lo <96078566+Will-ShaoHua@users.noreply.github.com> Date: Thu, 30 Apr 2026 16:42:48 -0700 Subject: [PATCH 38/40] doc: patch #550 learn more url (#552) * chore: bump version to 2.7.1 * doc: patch #550 --- plugin/webview/src/q-ui/components/qOptions.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/webview/src/q-ui/components/qOptions.vue b/plugin/webview/src/q-ui/components/qOptions.vue index 1a3c042a3..7ce00a24c 100644 --- a/plugin/webview/src/q-ui/components/qOptions.vue +++ b/plugin/webview/src/q-ui/components/qOptions.vue @@ -119,7 +119,7 @@ export default defineComponent({ window.telemetryApi.postClickEvent("maintenanceLearnMoreLink") window.ideApi.postMessage({ command: 'openUrl', - params: { url: 'https://aws.amazon.com/q/developer/' } + params: { url: 'https://aws.amazon.com/blogs/devops/amazon-q-developer-end-of-support-announcement/' } }) }, handleBackButtonClick() { From 5229869c91aae83af112691b98c885a1e8503acf Mon Sep 17 00:00:00 2001 From: Will Lo <96078566+Will-ShaoHua@users.noreply.github.com> Date: Thu, 30 Apr 2026 17:20:31 -0700 Subject: [PATCH 39/40] chore: bump version to 2.7.2 (#553) * chore: bump version to 2.7.1 * doc: patch #550 * chore: bump version to 2.7.2 --- feature/feature.xml | 4 ++-- feature/pom.xml | 2 +- plugin/META-INF/MANIFEST.MF | 2 +- plugin/pom.xml | 2 +- pom.xml | 2 +- telemetry/pom.xml | 2 +- updatesite/category.xml | 2 +- updatesite/pom.xml | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/feature/feature.xml b/feature/feature.xml index 076456415..a8ace23dd 100644 --- a/feature/feature.xml +++ b/feature/feature.xml @@ -2,7 +2,7 @@ + version="2.7.2.qualifier"> Amazon Q Developer helps users build faster across the entire software development lifecycle by providing tailored responses and code recommendations that conform to their team's internal libraries, proprietary algorithmic techniques, and enterprise code style. @@ -198,6 +198,6 @@ https://github.com/aws/amazon-q-eclipse/blob/main/attribution.xml id="amazon-q-eclipse" download-size="11000" install-size="0" - version="2.7.1.qualifier" + version="2.7.2.qualifier" unpack="false"/> diff --git a/feature/pom.xml b/feature/pom.xml index f6f4de743..2c6bf1700 100644 --- a/feature/pom.xml +++ b/feature/pom.xml @@ -6,7 +6,7 @@ software.aws.toolkits.eclipse amazon-q-eclipse-group - 2.7.1-SNAPSHOT + 2.7.2-SNAPSHOT ../ diff --git a/plugin/META-INF/MANIFEST.MF b/plugin/META-INF/MANIFEST.MF index 4cd3c6690..11a60549e 100644 --- a/plugin/META-INF/MANIFEST.MF +++ b/plugin/META-INF/MANIFEST.MF @@ -4,7 +4,7 @@ Bundle-Name: Amazon Q for Eclipse Bundle-Provider: Amazon Web Services Bundle-RequiredExecutionEnvironment: JavaSE-17 Bundle-SymbolicName: amazon-q-eclipse;singleton:=true -Bundle-Version: 2.7.1.qualifier +Bundle-Version: 2.7.2.qualifier Automatic-Module-Name: amazon.q.eclipse Bundle-ActivationPolicy: lazy Bundle-Activator: software.aws.toolkits.eclipse.amazonq.plugin.Activator diff --git a/plugin/pom.xml b/plugin/pom.xml index c5d267fb3..f0959bb5b 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -6,7 +6,7 @@ software.aws.toolkits.eclipse amazon-q-eclipse-group - 2.7.1-SNAPSHOT + 2.7.2-SNAPSHOT ../ diff --git a/pom.xml b/pom.xml index 448c7fc8c..bba891d9d 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ software.aws.toolkits.eclipse amazon-q-eclipse-group - 2.7.1-SNAPSHOT + 2.7.2-SNAPSHOT pom diff --git a/telemetry/pom.xml b/telemetry/pom.xml index 6b4857375..a1224d0b4 100644 --- a/telemetry/pom.xml +++ b/telemetry/pom.xml @@ -6,7 +6,7 @@ software.aws.toolkits.eclipse amazon-q-eclipse-group - 2.7.1-SNAPSHOT + 2.7.2-SNAPSHOT ../ diff --git a/updatesite/category.xml b/updatesite/category.xml index 1f72a6e65..72968e126 100644 --- a/updatesite/category.xml +++ b/updatesite/category.xml @@ -1,6 +1,6 @@ - + diff --git a/updatesite/pom.xml b/updatesite/pom.xml index 94f367880..d4166d91f 100644 --- a/updatesite/pom.xml +++ b/updatesite/pom.xml @@ -6,7 +6,7 @@ software.aws.toolkits.eclipse amazon-q-eclipse-group - 2.7.1-SNAPSHOT + 2.7.2-SNAPSHOT ../ From f7ee5196025ebe9a056e5efc54c4e10e226966a1 Mon Sep 17 00:00:00 2001 From: Will Lo <96078566+Will-ShaoHua@users.noreply.github.com> Date: Thu, 30 Apr 2026 18:59:14 -0700 Subject: [PATCH 40/40] doc: update message in toast notification (#554) * doc: update message in toast notification * chore: bump version to 2.7.3 --- feature/feature.xml | 4 ++-- feature/pom.xml | 2 +- plugin/META-INF/MANIFEST.MF | 2 +- plugin/pom.xml | 2 +- .../software/aws/toolkits/eclipse/amazonq/util/Constants.java | 2 +- pom.xml | 2 +- telemetry/pom.xml | 2 +- updatesite/category.xml | 2 +- updatesite/pom.xml | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/feature/feature.xml b/feature/feature.xml index a8ace23dd..159f0570f 100644 --- a/feature/feature.xml +++ b/feature/feature.xml @@ -2,7 +2,7 @@ + version="2.7.3.qualifier"> Amazon Q Developer helps users build faster across the entire software development lifecycle by providing tailored responses and code recommendations that conform to their team's internal libraries, proprietary algorithmic techniques, and enterprise code style. @@ -198,6 +198,6 @@ https://github.com/aws/amazon-q-eclipse/blob/main/attribution.xml id="amazon-q-eclipse" download-size="11000" install-size="0" - version="2.7.2.qualifier" + version="2.7.3.qualifier" unpack="false"/> diff --git a/feature/pom.xml b/feature/pom.xml index 2c6bf1700..e93a0fff8 100644 --- a/feature/pom.xml +++ b/feature/pom.xml @@ -6,7 +6,7 @@ software.aws.toolkits.eclipse amazon-q-eclipse-group - 2.7.2-SNAPSHOT + 2.7.3-SNAPSHOT ../ diff --git a/plugin/META-INF/MANIFEST.MF b/plugin/META-INF/MANIFEST.MF index 11a60549e..e1a0f2d18 100644 --- a/plugin/META-INF/MANIFEST.MF +++ b/plugin/META-INF/MANIFEST.MF @@ -4,7 +4,7 @@ Bundle-Name: Amazon Q for Eclipse Bundle-Provider: Amazon Web Services Bundle-RequiredExecutionEnvironment: JavaSE-17 Bundle-SymbolicName: amazon-q-eclipse;singleton:=true -Bundle-Version: 2.7.2.qualifier +Bundle-Version: 2.7.3.qualifier Automatic-Module-Name: amazon.q.eclipse Bundle-ActivationPolicy: lazy Bundle-Activator: software.aws.toolkits.eclipse.amazonq.plugin.Activator diff --git a/plugin/pom.xml b/plugin/pom.xml index f0959bb5b..036d1a555 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -6,7 +6,7 @@ software.aws.toolkits.eclipse amazon-q-eclipse-group - 2.7.2-SNAPSHOT + 2.7.3-SNAPSHOT ../ diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/util/Constants.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/util/Constants.java index 822d42c5b..e2b009eb7 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/util/Constants.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/util/Constants.java @@ -42,7 +42,7 @@ private Constants() { + " of the IDE preferences."; public static final String KIRO_SUNSET_NOTIFICATION_TITLE = "Amazon Q Developer end of support"; public static final String KIRO_SUNSET_NOTIFICATION_BODY = "Amazon Q Developer IDE plugins will reach end of support on April 30, 2027." - + " New accounts will no longer be available starting May 15, 2026, but existing users can still sign-in below."; + + " New accounts will no longer be available starting May 15, 2026"; public static final String KIRO_SUNSET_LEARN_MORE_URL = "https://aws.amazon.com/blogs/devops/amazon-q-developer-end-of-support-announcement/"; public static final String KIRO_SUNSET_NOTIFICATION_DISMISSED_KEY = "kiroSunsetNotificationDismissed"; public static final String RE_AUTHENTICATE_FAILURE_MESSAGE = "An error occurred while attempting to re-authenticate. Please try again."; diff --git a/pom.xml b/pom.xml index bba891d9d..b4dfbc735 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ software.aws.toolkits.eclipse amazon-q-eclipse-group - 2.7.2-SNAPSHOT + 2.7.3-SNAPSHOT pom diff --git a/telemetry/pom.xml b/telemetry/pom.xml index a1224d0b4..8ba6a31fe 100644 --- a/telemetry/pom.xml +++ b/telemetry/pom.xml @@ -6,7 +6,7 @@ software.aws.toolkits.eclipse amazon-q-eclipse-group - 2.7.2-SNAPSHOT + 2.7.3-SNAPSHOT ../ diff --git a/updatesite/category.xml b/updatesite/category.xml index 72968e126..e598e880a 100644 --- a/updatesite/category.xml +++ b/updatesite/category.xml @@ -1,6 +1,6 @@ - + diff --git a/updatesite/pom.xml b/updatesite/pom.xml index d4166d91f..1c5d0bb1e 100644 --- a/updatesite/pom.xml +++ b/updatesite/pom.xml @@ -6,7 +6,7 @@ software.aws.toolkits.eclipse amazon-q-eclipse-group - 2.7.2-SNAPSHOT + 2.7.3-SNAPSHOT ../