From 13c55acabaade5c1a73d7097fc35f7fca52b1226 Mon Sep 17 00:00:00 2001 From: Nicolas Borges Date: Thu, 12 Dec 2024 18:56:46 -0500 Subject: [PATCH 01/13] improved handling of lsp failure state --- plugin/plugin.xml | 12 +++++ .../AbstractQChatEditorActionsHandler.java | 3 +- .../handlers/QOpenLoginViewHandler.java | 9 ++-- .../connection/QLspConnectionProvider.java | 31 ++++++++----- .../eclipse/amazonq/lsp/manager/LspState.java | 10 +++++ .../amazonq/lsp/manager/LspStatusManager.java | 32 ++++++++++++++ .../amazonq/providers/LspProviderImpl.java | 2 + .../amazonq/views/AmazonQChatWebview.java | 3 +- .../amazonq/views/LspStartUpFailedView.java | 44 +++++++++++++++++++ .../amazonq/views/ViewVisibilityManager.java | 16 ++++++- 10 files changed, 144 insertions(+), 18 deletions(-) create mode 100644 plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/manager/LspState.java create mode 100644 plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/manager/LspStatusManager.java create mode 100644 plugin/src/software/aws/toolkits/eclipse/amazonq/views/LspStartUpFailedView.java diff --git a/plugin/plugin.xml b/plugin/plugin.xml index ec24639a..ce099145 100644 --- a/plugin/plugin.xml +++ b/plugin/plugin.xml @@ -33,6 +33,12 @@ icon="icons/AmazonQ.png" class="software.aws.toolkits.eclipse.amazonq.views.ChatAssetMissingView"> + + + + commands = new ArrayList<>(); + commands.add(serverCommand.toString()); + commands.add(lspInstallResult.getServerCommandArgs()); + commands.add("--stdio"); + commands.add("--set-credentials-encryption-key"); + setCommands(commands); + } catch (Exception e) { + LspStatusManager.setToFailed(); + throw(e); + } - var serverCommand = Paths.get(lspInstallResult.getServerDirectory(), lspInstallResult.getServerCommand()); - List commands = new ArrayList<>(); - commands.add(serverCommand.toString()); - commands.add(lspInstallResult.getServerCommandArgs()); - commands.add("--stdio"); - commands.add("--set-credentials-encryption-key"); - setCommands(commands); } @Override @@ -70,10 +77,12 @@ public final void start() throws IOException { lspEncryption.initializeEncryptedCommunication(serverStdIn); } catch (Exception e) { + LspStatusManager.setToFailed(); emitInitFailure(ExceptionMetadata.scrubException(e)); Activator.getLogger().error("Error occured while initializing communication with Amazon Q Lsp Server", e); } } catch (Exception e) { + LspStatusManager.setToFailed(); emitInitFailure(ExceptionMetadata.scrubException(e)); throw e; } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/manager/LspState.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/manager/LspState.java new file mode 100644 index 00000000..32760a03 --- /dev/null +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/manager/LspState.java @@ -0,0 +1,10 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.eclipse.amazonq.lsp.manager; + +public enum LspState { + ACTIVE, + FAILED, + PENDING +} diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/manager/LspStatusManager.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/manager/LspStatusManager.java new file mode 100644 index 00000000..aa0a4752 --- /dev/null +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/manager/LspStatusManager.java @@ -0,0 +1,32 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.eclipse.amazonq.lsp.manager; + +import software.aws.toolkits.eclipse.amazonq.views.ViewVisibilityManager; + +public final class LspStatusManager { + + private LspStatusManager() { + //prevent + } + + private static LspState lspState = LspState.PENDING; + + public static boolean lspFailed() { + return (lspState == LspState.FAILED); + } + public static void setToActive() { + lspState = LspState.ACTIVE; + ViewVisibilityManager.showDefaultView("restart"); + } + public static void setToFailed() { + if (lspState != LspState.FAILED) { + ViewVisibilityManager.showLspStartUpFailedView("update"); + lspState = LspState.FAILED; + } + } + public static LspState getLspState() { + return lspState; + } +} diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/providers/LspProviderImpl.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/providers/LspProviderImpl.java index 3462a2d4..fc2d7f11 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/providers/LspProviderImpl.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/providers/LspProviderImpl.java @@ -11,6 +11,7 @@ import org.eclipse.lsp4j.services.LanguageServer; import software.aws.toolkits.eclipse.amazonq.lsp.AmazonQLspServer; +import software.aws.toolkits.eclipse.amazonq.lsp.manager.LspStatusManager; import software.aws.toolkits.eclipse.amazonq.lsp.manager.fetcher.RecordLspSetupArgs; import software.aws.toolkits.eclipse.amazonq.telemetry.LanguageServerTelemetryProvider; import software.aws.toolkits.telemetry.TelemetryDefinitions.Result; @@ -52,6 +53,7 @@ public void setAmazonQServer(final LanguageServer server) { future.complete(server); } emitInitializeMetric(); + LspStatusManager.setToActive(); } } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatWebview.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatWebview.java index ebe4b33d..ff43b3d7 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatWebview.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatWebview.java @@ -21,6 +21,7 @@ import software.aws.toolkits.eclipse.amazonq.configuration.PluginStoreKeys; import software.aws.toolkits.eclipse.amazonq.lsp.AwsServerCapabiltiesProvider; import software.aws.toolkits.eclipse.amazonq.lsp.auth.model.AuthState; +import software.aws.toolkits.eclipse.amazonq.lsp.manager.LspStatusManager; import software.aws.toolkits.eclipse.amazonq.lsp.model.ChatOptions; import software.aws.toolkits.eclipse.amazonq.lsp.model.QuickActions; import software.aws.toolkits.eclipse.amazonq.lsp.model.QuickActionsCommandGroup; @@ -141,7 +142,7 @@ public final void onEvent(final AuthState authState) { // chat view if (browser != null && !browser.isDisposed() && !chatStateManager.hasPreservedState()) { Optional content = getContent(); - if (!content.isPresent()) { + if (!content.isPresent() && !LspStatusManager.lspFailed()) { canDisposeState = true; ViewVisibilityManager.showChatAssetMissingView("update"); } else { diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/LspStartUpFailedView.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/LspStartUpFailedView.java new file mode 100644 index 00000000..05382877 --- /dev/null +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/LspStartUpFailedView.java @@ -0,0 +1,44 @@ +// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.eclipse.amazonq.views; +import java.util.concurrent.CompletableFuture; + +import software.aws.toolkits.eclipse.amazonq.lsp.manager.LspStatusManager; + +public final class LspStartUpFailedView extends BaseView { + public static final String ID = "software.aws.toolkits.eclipse.amazonq.views.LspStartUpFailedView"; + + private static final String ICON_PATH = "icons/AmazonQ64.png"; + private static final String HEADER_LABEL = "Language Server failed to start."; + private static final String DETAIL_MESSAGE = "Restart Eclipse or review error logs for troubleshooting"; + + /* TODO: After refactor of LSP error handling is completed, + * add logic to base detail_message on error code returned from exception + * */ + @Override + protected String getIconPath() { + return ICON_PATH; + } + + @Override + protected String getHeaderLabel() { + return HEADER_LABEL; + } + + @Override + protected String getDetailMessage() { + return DETAIL_MESSAGE; + } + + @Override + protected void showAlternateView() { + ViewVisibilityManager.showDefaultView("restart"); + } + + @Override + protected CompletableFuture isViewDisplayable() { + return CompletableFuture.completedFuture(LspStatusManager.lspFailed()); + } + +} diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ViewVisibilityManager.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ViewVisibilityManager.java index 49d51ac8..7d8f3e25 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ViewVisibilityManager.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ViewVisibilityManager.java @@ -26,6 +26,7 @@ private ViewVisibilityManager() { private static final String RE_AUTHENTICATE_VIEW = ReauthenticateView.ID; private static final String CHAT_ASSET_MISSING_VIEW = ChatAssetMissingView.ID; private static final String CODE_REFERENCE_VIEW = AmazonQCodeReferenceView.ID; + private static final String LSP_STARTUP_FAILED_VIEW = LspStartUpFailedView.ID; private static final String ERROR_LOG_VIEW = "org.eclipse.pde.runtime.LogView"; private static final Set MUTUALLY_EXCLUSIVE_VIEWS = Set.of( @@ -33,9 +34,18 @@ private ViewVisibilityManager() { CHAT_VIEW, DEPENDENCY_MISSING_VIEW, RE_AUTHENTICATE_VIEW, - CHAT_ASSET_MISSING_VIEW + CHAT_ASSET_MISSING_VIEW, + LSP_STARTUP_FAILED_VIEW ); + public static void showDefaultView(final String source) { + if (Activator.getLoginService().getAuthState().isLoggedIn()) { + showChatView(source); + } else { + showLoginView(source); + } + } + public static void showLoginView(final String source) { showMutuallyExclusiveView(TOOLKIT_LOGIN_VIEW, source); } @@ -56,6 +66,10 @@ public static void showChatAssetMissingView(final String source) { showMutuallyExclusiveView(CHAT_ASSET_MISSING_VIEW, source); } + public static void showLspStartUpFailedView(final String source) { + showMutuallyExclusiveView(LSP_STARTUP_FAILED_VIEW, source); + } + public static void showCodeReferenceView(final String source) { showView(CODE_REFERENCE_VIEW, source); } From 548a9da3b3f7dba868aac657c39d01c1722e9c56 Mon Sep 17 00:00:00 2001 From: Nicolas Borges Date: Fri, 13 Dec 2024 13:03:25 -0500 Subject: [PATCH 02/13] add static mock to lsp connection test case --- .../amazonq/lsp/manager/LspStatusManager.java | 2 +- .../connection/QLspConnectionProviderTest.java | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/manager/LspStatusManager.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/manager/LspStatusManager.java index aa0a4752..28d5872f 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/manager/LspStatusManager.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/manager/LspStatusManager.java @@ -8,7 +8,7 @@ public final class LspStatusManager { private LspStatusManager() { - //prevent + //prevent instantiation } private static LspState lspState = LspState.PENDING; 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 8520b69b..fbada043 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 @@ -3,16 +3,21 @@ package software.aws.toolkits.eclipse.amazonq.lsp.connection; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import org.mockito.MockedStatic; import org.mockito.Mockito; + import software.aws.toolkits.eclipse.amazonq.extensions.implementation.ActivatorStaticMockExtension; import software.aws.toolkits.eclipse.amazonq.extensions.implementation.DefaultLspEncryptionManagerStaticMockExtension; import software.aws.toolkits.eclipse.amazonq.extensions.implementation.LspManagerProviderStaticMockExtension; import software.aws.toolkits.eclipse.amazonq.extensions.implementation.ProxyUtilsStaticMockExtension; import software.aws.toolkits.eclipse.amazonq.lsp.encryption.LspEncryptionManager; import software.aws.toolkits.eclipse.amazonq.lsp.manager.LspInstallResult; +import software.aws.toolkits.eclipse.amazonq.lsp.manager.LspStatusManager; + import org.eclipse.lsp4e.server.ProcessStreamConnectionProvider; import software.aws.toolkits.eclipse.amazonq.util.LoggingService; import software.aws.toolkits.eclipse.amazonq.util.ProxyUtil; @@ -30,6 +35,7 @@ import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mockStatic; import static org.mockito.Mockito.verify; public final class QLspConnectionProviderTest { @@ -48,6 +54,18 @@ public final class QLspConnectionProviderTest { @RegisterExtension private static ProxyUtilsStaticMockExtension proxyUtilsStaticMockExtension = new ProxyUtilsStaticMockExtension(); + private MockedStatic mockLspStatusManager; + + @BeforeEach + void setupBeforeEach() { + mockLspStatusManager = mockStatic(LspStatusManager.class); + } + + @AfterEach + void tearDown() { + mockLspStatusManager.close(); + } + private static final class TestProcessConnectionProvider extends ProcessStreamConnectionProvider { TestProcessConnectionProvider(final List commands, final String workingDirectory) { From 374e549a692005f03c2c79ef6349e9564e467d5b Mon Sep 17 00:00:00 2001 From: Ishan Taldekar Date: Mon, 27 Jan 2025 17:58:09 -0500 Subject: [PATCH 03/13] Integrate Event Broker in LspStatusManager --- plugin/META-INF/MANIFEST.MF | 2 +- .../AbstractQChatEditorActionsHandler.java | 2 +- .../handlers/QOpenLoginViewHandler.java | 3 +- .../connection/QLspConnectionProvider.java | 10 ++-- .../amazonq/lsp/manager/LspStatusManager.java | 29 +++++++++--- .../amazonq/providers/LspProviderImpl.java | 2 +- .../amazonq/views/AmazonQChatWebview.java | 2 +- .../amazonq/views/LspStartUpFailedView.java | 2 +- .../QLspConnectionProviderTest.java | 47 ++++++++++--------- 9 files changed, 59 insertions(+), 40 deletions(-) diff --git a/plugin/META-INF/MANIFEST.MF b/plugin/META-INF/MANIFEST.MF index bb80724d..47a22e50 100644 --- a/plugin/META-INF/MANIFEST.MF +++ b/plugin/META-INF/MANIFEST.MF @@ -28,7 +28,7 @@ Require-Bundle: org.eclipse.core.runtime;bundle-version="3.31.0", org.apache.commons.logging;bundle-version="1.2.0", slf4j.api;bundle-version="2.0.13", org.apache.commons.lang3;bundle-version="3.14.0" -Bundle-Classpath: target/classes/, +Bundle-Classpath: ., target/dependency/annotations-2.28.26.jar, target/dependency/apache-client-2.28.26.jar, target/dependency/auth-2.28.26.jar, diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/handlers/AbstractQChatEditorActionsHandler.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/handlers/AbstractQChatEditorActionsHandler.java index 7e4f3a62..af363a4f 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/handlers/AbstractQChatEditorActionsHandler.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/handlers/AbstractQChatEditorActionsHandler.java @@ -28,7 +28,7 @@ public abstract class AbstractQChatEditorActionsHandler extends AbstractHandler @Override public final boolean isEnabled() { try { - return Activator.getLoginService().getAuthState().isLoggedIn() && !LspStatusManager.lspFailed(); + return Activator.getLoginService().getAuthState().isLoggedIn() && !LspStatusManager.getInstance().lspFailed(); } catch (Exception e) { return false; } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/handlers/QOpenLoginViewHandler.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/handlers/QOpenLoginViewHandler.java index 69c4e4ad..d2741a31 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/handlers/QOpenLoginViewHandler.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/handlers/QOpenLoginViewHandler.java @@ -7,13 +7,12 @@ import org.eclipse.core.commands.ExecutionEvent; import software.aws.toolkits.eclipse.amazonq.lsp.manager.LspStatusManager; - import software.aws.toolkits.eclipse.amazonq.views.ViewVisibilityManager; public class QOpenLoginViewHandler extends AbstractHandler { @Override public final Object execute(final ExecutionEvent event) { - if (LspStatusManager.lspFailed()) { + if (LspStatusManager.getInstance().lspFailed()) { ViewVisibilityManager.showLspStartUpFailedView("statusBar"); } else { ViewVisibilityManager.showDefaultView("statusBar"); diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/connection/QLspConnectionProvider.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/connection/QLspConnectionProvider.java index 2c924c0e..9dbedf3b 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/connection/QLspConnectionProvider.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/connection/QLspConnectionProvider.java @@ -16,13 +16,13 @@ import software.aws.toolkits.eclipse.amazonq.lsp.manager.LspManager; import software.aws.toolkits.eclipse.amazonq.lsp.manager.LspStatusManager; import software.aws.toolkits.eclipse.amazonq.lsp.manager.fetcher.RecordLspSetupArgs; +import software.aws.toolkits.eclipse.amazonq.plugin.Activator; +import software.aws.toolkits.eclipse.amazonq.preferences.AmazonQPreferencePage; import software.aws.toolkits.eclipse.amazonq.providers.LspManagerProvider; import software.aws.toolkits.eclipse.amazonq.telemetry.LanguageServerTelemetryProvider; import software.aws.toolkits.eclipse.amazonq.telemetry.metadata.ExceptionMetadata; import software.aws.toolkits.eclipse.amazonq.util.ProxyUtil; import software.aws.toolkits.telemetry.TelemetryDefinitions.Result; -import software.aws.toolkits.eclipse.amazonq.plugin.Activator; -import software.aws.toolkits.eclipse.amazonq.preferences.AmazonQPreferencePage; public class QLspConnectionProvider extends AbstractLspConnectionProvider { @@ -43,7 +43,7 @@ public QLspConnectionProvider() throws IOException { commands.add("--set-credentials-encryption-key"); setCommands(commands); } catch (Exception e) { - LspStatusManager.setToFailed(); + LspStatusManager.getInstance().setToFailed(); throw(e); } @@ -77,12 +77,12 @@ public final void start() throws IOException { lspEncryption.initializeEncryptedCommunication(serverStdIn); } catch (Exception e) { - LspStatusManager.setToFailed(); + LspStatusManager.getInstance().setToFailed(); emitInitFailure(ExceptionMetadata.scrubException(e)); Activator.getLogger().error("Error occured while initializing communication with Amazon Q Lsp Server", e); } } catch (Exception e) { - LspStatusManager.setToFailed(); + LspStatusManager.getInstance().setToFailed(); emitInitFailure(ExceptionMetadata.scrubException(e)); throw e; } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/manager/LspStatusManager.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/manager/LspStatusManager.java index 28d5872f..fa8c6c80 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/manager/LspStatusManager.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/manager/LspStatusManager.java @@ -3,30 +3,47 @@ package software.aws.toolkits.eclipse.amazonq.lsp.manager; +import software.aws.toolkits.eclipse.amazonq.plugin.Activator; import software.aws.toolkits.eclipse.amazonq.views.ViewVisibilityManager; public final class LspStatusManager { + private static final LspStatusManager INSTANCE; + private LspState lspState; + + static { + INSTANCE = new LspStatusManager(); + } + private LspStatusManager() { - //prevent instantiation + lspState = LspState.PENDING; + Activator.getEventBroker().post(lspState); + } + + public static LspStatusManager getInstance() { + return INSTANCE; } - private static LspState lspState = LspState.PENDING; - public static boolean lspFailed() { + public boolean lspFailed() { return (lspState == LspState.FAILED); } - public static void setToActive() { + + public void setToActive() { lspState = LspState.ACTIVE; + Activator.getEventBroker().post(lspState); ViewVisibilityManager.showDefaultView("restart"); } - public static void setToFailed() { + + public void setToFailed() { if (lspState != LspState.FAILED) { ViewVisibilityManager.showLspStartUpFailedView("update"); lspState = LspState.FAILED; } + + Activator.getEventBroker().post(lspState); } - public static LspState getLspState() { + public LspState getLspState() { return lspState; } } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/providers/LspProviderImpl.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/providers/LspProviderImpl.java index fc2d7f11..15afefa4 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/providers/LspProviderImpl.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/providers/LspProviderImpl.java @@ -53,7 +53,7 @@ public void setAmazonQServer(final LanguageServer server) { future.complete(server); } emitInitializeMetric(); - LspStatusManager.setToActive(); + LspStatusManager.getInstance().setToActive(); } } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatWebview.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatWebview.java index ff43b3d7..3e2b6642 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatWebview.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatWebview.java @@ -142,7 +142,7 @@ public final void onEvent(final AuthState authState) { // chat view if (browser != null && !browser.isDisposed() && !chatStateManager.hasPreservedState()) { Optional content = getContent(); - if (!content.isPresent() && !LspStatusManager.lspFailed()) { + if (!content.isPresent() && !LspStatusManager.getInstance().lspFailed()) { canDisposeState = true; ViewVisibilityManager.showChatAssetMissingView("update"); } else { diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/LspStartUpFailedView.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/LspStartUpFailedView.java index 05382877..db0e82b2 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/LspStartUpFailedView.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/LspStartUpFailedView.java @@ -38,7 +38,7 @@ protected void showAlternateView() { @Override protected CompletableFuture isViewDisplayable() { - return CompletableFuture.completedFuture(LspStatusManager.lspFailed()); + return CompletableFuture.completedFuture(LspStatusManager.getInstance().lspFailed()); } } 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 fbada043..0ef41dd7 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 @@ -3,6 +3,24 @@ package software.aws.toolkits.eclipse.amazonq.lsp.connection; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.verify; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.lsp4e.server.ProcessStreamConnectionProvider; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -17,27 +35,9 @@ import software.aws.toolkits.eclipse.amazonq.lsp.encryption.LspEncryptionManager; import software.aws.toolkits.eclipse.amazonq.lsp.manager.LspInstallResult; import software.aws.toolkits.eclipse.amazonq.lsp.manager.LspStatusManager; - -import org.eclipse.lsp4e.server.ProcessStreamConnectionProvider; import software.aws.toolkits.eclipse.amazonq.util.LoggingService; import software.aws.toolkits.eclipse.amazonq.util.ProxyUtil; -import java.io.IOException; -import java.io.OutputStream; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mockStatic; -import static org.mockito.Mockito.verify; - public final class QLspConnectionProviderTest { @RegisterExtension @@ -54,16 +54,19 @@ public final class QLspConnectionProviderTest { @RegisterExtension private static ProxyUtilsStaticMockExtension proxyUtilsStaticMockExtension = new ProxyUtilsStaticMockExtension(); - private MockedStatic mockLspStatusManager; + private MockedStatic mockStaticLspStatusManager; + private LspStatusManager mockLspStatusManager; @BeforeEach void setupBeforeEach() { - mockLspStatusManager = mockStatic(LspStatusManager.class); + mockStaticLspStatusManager = mockStatic(LspStatusManager.class); + mockLspStatusManager = mock(LspStatusManager.class); + mockStaticLspStatusManager.when(LspStatusManager::getInstance).thenReturn(mockLspStatusManager); } @AfterEach void tearDown() { - mockLspStatusManager.close(); + mockStaticLspStatusManager.close(); } private static final class TestProcessConnectionProvider extends ProcessStreamConnectionProvider { @@ -107,7 +110,7 @@ void testConstructorInitializesCorrectly() throws IOException { "/test/dir" ); - assertTrue(((ProcessStreamConnectionProvider) testProcessConnectionProvider).equals(provider)); + assertTrue(testProcessConnectionProvider.equals(provider)); } @Test From 6a9f8d3e382682835a7dca8429b78cdef3bbe0d6 Mon Sep 17 00:00:00 2001 From: Ishan Taldekar Date: Tue, 28 Jan 2025 14:29:16 -0500 Subject: [PATCH 04/13] Add ViewRouter POC --- .../eclipse/amazonq/views/router/ViewId.java | 9 ++ .../amazonq/views/router/ViewRouter.java | 87 +++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 plugin/src/software/aws/toolkits/eclipse/amazonq/views/router/ViewId.java create mode 100644 plugin/src/software/aws/toolkits/eclipse/amazonq/views/router/ViewRouter.java diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/router/ViewId.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/router/ViewId.java new file mode 100644 index 00000000..8ff6272e --- /dev/null +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/router/ViewId.java @@ -0,0 +1,9 @@ +// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.eclipse.amazonq.views.router; + +public enum ViewId { + TOOLKIT_LOGIN_VIEW, CHAT_VIEW, DEPENDENCY_MISSING_VIEW, RE_AUTHENTICATE_VIEW, CHAT_ASSET_MISSING_VIEW, + CODE_REFERENCE_VIEW, LSP_STARTUP_FAILED_VIEW, LSP_INITIALIZING_VIEW +} diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/router/ViewRouter.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/router/ViewRouter.java new file mode 100644 index 00000000..289b7f0e --- /dev/null +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/router/ViewRouter.java @@ -0,0 +1,87 @@ +// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.eclipse.amazonq.views.router; + +import software.aws.toolkits.eclipse.amazonq.broker.api.EventObserver; +import software.aws.toolkits.eclipse.amazonq.lsp.auth.model.AuthState; +import software.aws.toolkits.eclipse.amazonq.lsp.manager.LspState; +import software.aws.toolkits.eclipse.amazonq.plugin.Activator; + +public final class ViewRouter { + + private final EventObserver authStateObserver = new EventObserver<>() { + @Override + public void onEvent(final AuthState newAuthState) { + authState = newAuthState; + refreshActiveView(); + } + }; + + private final EventObserver lspStateObserver = new EventObserver<>() { + @Override + public void onEvent(final LspState newLspState) { + lspState = newLspState; + refreshActiveView(); + } + }; + + private ViewId activeViewId; + + // this state needs to be maintained to ensure correct resolution in refreshActiveView + private LspState lspState; + private AuthState authState; + + public ViewRouter() { + activeViewId = ViewId.TOOLKIT_LOGIN_VIEW; + lspState = null; + authState = null; + + Activator.getEventBroker().subscribe(AuthState.class, authStateObserver); + Activator.getEventBroker().subscribe(LspState.class, lspStateObserver); + } + + private void refreshActiveView() { + ViewId newActiveViewId = ViewId.TOOLKIT_LOGIN_VIEW; + + if (isDependencyMissing()) { // TODO: dependency missing check logic needs to be implemented + newActiveViewId = ViewId.DEPENDENCY_MISSING_VIEW; + } else if (lspState != null) { + if (lspState == LspState.FAILED) { + newActiveViewId = ViewId.LSP_STARTUP_FAILED_VIEW; + } else if (lspState == LspState.PENDING) { + newActiveViewId = ViewId.LSP_INITIALIZING_VIEW; + } + } else if (isChatUIAssetMissing()) { // TODO: chat missing logic needs to be implemented + newActiveViewId = ViewId.CHAT_ASSET_MISSING_VIEW; + } else if (authState != null) { + if (authState.isLoggedOut()) { + newActiveViewId = ViewId.TOOLKIT_LOGIN_VIEW; + } else if (authState.isExpired()) { + newActiveViewId = ViewId.RE_AUTHENTICATE_VIEW; + } + } else { + newActiveViewId = ViewId.CHAT_VIEW; + } + + if (activeViewId != newActiveViewId) { + activeViewId = newActiveViewId; + notifyActiveViewChange(); + } + } + + private void notifyActiveViewChange() { + Activator.getEventBroker().post(activeViewId); + } + + // TODO: replace with relevant checks + private boolean isDependencyMissing() { + return false; + } + + // TODO: replace with relevant checks + private boolean isChatUIAssetMissing() { + return false; + } + +} From d1746a48a99721a0620015fbc548ca80baddd5f6 Mon Sep 17 00:00:00 2001 From: taldekar Date: Tue, 28 Jan 2025 14:41:34 -0500 Subject: [PATCH 05/13] Fix code formatting bug --- .../aws/toolkits/eclipse/amazonq/views/router/ViewRouter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/router/ViewRouter.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/router/ViewRouter.java index 289b7f0e..b668f185 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/router/ViewRouter.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/router/ViewRouter.java @@ -27,7 +27,7 @@ public void onEvent(final LspState newLspState) { }; private ViewId activeViewId; - + // this state needs to be maintained to ensure correct resolution in refreshActiveView private LspState lspState; private AuthState authState; From 991b319dca6fc0ccbf802457b72e9902f13d2295 Mon Sep 17 00:00:00 2001 From: Nicolas Borges Date: Mon, 27 Jan 2025 12:28:55 -0500 Subject: [PATCH 06/13] commiting POC --- plugin/plugin.xml | 6 ++ .../handlers/QOpenLoginViewHandler.java | 11 +-- .../amazonq/views/AmazonQViewContainer.java | 86 +++++++++++++++++++ .../amazonq/views/BaseAmazonQView.java | 12 +++ .../amazonq/views/SomeOtherTestView.java | 47 ++++++++++ .../eclipse/amazonq/views/SomeTestView.java | 47 ++++++++++ .../eclipse/amazonq/views/ViewConstants.java | 2 + .../amazonq/views/ViewVisibilityManager.java | 8 +- 8 files changed, 213 insertions(+), 6 deletions(-) create mode 100644 plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQViewContainer.java create mode 100644 plugin/src/software/aws/toolkits/eclipse/amazonq/views/BaseAmazonQView.java create mode 100644 plugin/src/software/aws/toolkits/eclipse/amazonq/views/SomeOtherTestView.java create mode 100644 plugin/src/software/aws/toolkits/eclipse/amazonq/views/SomeTestView.java diff --git a/plugin/plugin.xml b/plugin/plugin.xml index ce099145..2a40148c 100644 --- a/plugin/plugin.xml +++ b/plugin/plugin.xml @@ -21,6 +21,12 @@ icon="icons/AmazonQ.png" class="software.aws.toolkits.eclipse.amazonq.views.ReauthenticateView"> + + views = new HashMap<>(); + String activeId; + BaseAmazonQView activeView; + + public AmazonQViewContainer() { + initializeViews(); + } + public void initializeViews() { + //init map containing all views + var testViewA = new SomeTestView(); + var testViewB = new SomeOtherTestView(); + views.put(ViewConstants.TEST_VIEW_A_ID, testViewA); + views.put(ViewConstants.TEST_VIEW_B_ID, testViewB); + } + + public final void createPartControl(final Composite parent) { + // ===== TEMPORARY TEST SETUP - TO BE REPLACED WITH EVENT BUS ===== + // this nested container structure is only for testing view switching functionality + // once event bus + view router implemented: + // 1. remove temporary container and button + // 2. single StackLayout on parent + // 3. view switching handled through event bus subscription + parentComposite = new Composite(parent, SWT.NONE); + GridLayout mainLayout = new GridLayout(1, false); + parentComposite.setLayout(mainLayout); + + // Temporary test button - will be removed + var button = new Button(parentComposite, SWT.PUSH); + button.setText("Switch View"); + button.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, false)); + + // Temporary nested container for StackLayout + Composite stackContainer = new Composite(parentComposite, SWT.NONE); + stackContainer.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + layout = new StackLayout(); + stackContainer.setLayout(layout); + + // Create initial view in stack container + Composite viewAComposite = views.get(ViewConstants.TEST_VIEW_A_ID).setupView(stackContainer); + layout.topControl = viewAComposite; + activeId = ViewConstants.TEST_VIEW_A_ID; + + // Temporary button listener - will be replaced by event bus subscription + button.addListener(SWT.Selection, new Listener() { + @Override + public void handleEvent(final Event event) { + String newViewId = activeId.equals(ViewConstants.TEST_VIEW_A_ID) + ? ViewConstants.TEST_VIEW_B_ID + : ViewConstants.TEST_VIEW_A_ID; + + BaseAmazonQView newView = views.get(newViewId); + layout.topControl = newView.setupView(stackContainer); + activeId = newViewId; + stackContainer.layout(); + } + }); + } + + + @Override + public final void setFocus() { + parentComposite.setFocus(); + + } +} diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/BaseAmazonQView.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/BaseAmazonQView.java new file mode 100644 index 00000000..6e3de5fe --- /dev/null +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/BaseAmazonQView.java @@ -0,0 +1,12 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.eclipse.amazonq.views; + +import org.eclipse.swt.widgets.Composite; + +public interface BaseAmazonQView { + public Composite setupView(Composite parentComposite); + public boolean canDisplay(); + public void dispose(); +} diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/SomeOtherTestView.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/SomeOtherTestView.java new file mode 100644 index 00000000..3510099f --- /dev/null +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/SomeOtherTestView.java @@ -0,0 +1,47 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.eclipse.amazonq.views; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; + +public class SomeOtherTestView implements BaseAmazonQView { + private Font titleFont; // Keep reference to dispose later + + @Override + public Composite setupView(Composite parentComposite) { + // Create a new composite with a GridLayout + Composite container = new Composite(parentComposite, SWT.NONE); + GridLayout layout = new GridLayout(1, false); + container.setLayout(layout); + + // Add a label with some text + Label label = new Label(container, SWT.NONE); + label.setText("This is Test View B!!!!!"); + label.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true)); + + // Optional: Add some styling to make it more visible + titleFont = new Font(container.getDisplay(), "Arial", 14, SWT.BOLD); + label.setFont(titleFont); + + return container; + } + + @Override + public void dispose() { + if (titleFont != null && !titleFont.isDisposed()) { + titleFont.dispose(); + titleFont = null; + } + } + + @Override + public boolean canDisplay() { + return true; + } +} diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/SomeTestView.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/SomeTestView.java new file mode 100644 index 00000000..89f009bb --- /dev/null +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/SomeTestView.java @@ -0,0 +1,47 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.eclipse.amazonq.views; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; + +public class SomeTestView implements BaseAmazonQView { + private Font titleFont; // Keep reference to dispose later + + @Override + public Composite setupView(Composite parentComposite) { + // Create a new composite with a GridLayout + Composite container = new Composite(parentComposite, SWT.NONE); + GridLayout layout = new GridLayout(1, false); + container.setLayout(layout); + + // Add a label with some text + Label label = new Label(container, SWT.NONE); + label.setText("This is Test View A"); + label.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true)); + + // Optional: Add some styling to make it more visible + titleFont = new Font(container.getDisplay(), "Arial", 14, SWT.BOLD); + label.setFont(titleFont); + + return container; + } + + @Override + public void dispose() { + if (titleFont != null && !titleFont.isDisposed()) { + titleFont.dispose(); + titleFont = null; + } + } + + @Override + public boolean canDisplay() { + return true; + } +} diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ViewConstants.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ViewConstants.java index 6cfe4669..8adeed58 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ViewConstants.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ViewConstants.java @@ -10,4 +10,6 @@ private ViewConstants() { public static final String COMMAND_FUNCTION_NAME = "ideCommand"; public static final String PREFERENCE_STORE_PLUGIN_FIRST_STARTUP_KEY = "qEclipseFirstLoad"; + public static final String TEST_VIEW_A_ID = "testViewAId"; + public static final String TEST_VIEW_B_ID = "testViewBId"; } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ViewVisibilityManager.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ViewVisibilityManager.java index 7d8f3e25..9d43fd95 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ViewVisibilityManager.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ViewVisibilityManager.java @@ -28,6 +28,7 @@ private ViewVisibilityManager() { private static final String CODE_REFERENCE_VIEW = AmazonQCodeReferenceView.ID; private static final String LSP_STARTUP_FAILED_VIEW = LspStartUpFailedView.ID; private static final String ERROR_LOG_VIEW = "org.eclipse.pde.runtime.LogView"; + private static final String AMAZON_Q_VIEW_CONTAINER = "software.aws.toolkits.eclipse.amazonq.views.AmazonQViewContainer"; private static final Set MUTUALLY_EXCLUSIVE_VIEWS = Set.of( TOOLKIT_LOGIN_VIEW, @@ -35,7 +36,8 @@ private ViewVisibilityManager() { DEPENDENCY_MISSING_VIEW, RE_AUTHENTICATE_VIEW, CHAT_ASSET_MISSING_VIEW, - LSP_STARTUP_FAILED_VIEW + LSP_STARTUP_FAILED_VIEW, + AMAZON_Q_VIEW_CONTAINER ); public static void showDefaultView(final String source) { @@ -50,6 +52,10 @@ public static void showLoginView(final String source) { showMutuallyExclusiveView(TOOLKIT_LOGIN_VIEW, source); } + public static void showViewContainer(final String source) { + showMutuallyExclusiveView(AMAZON_Q_VIEW_CONTAINER, source); + } + public static void showChatView(final String source) { showMutuallyExclusiveView(CHAT_VIEW, source); } From cca4f5536b09f0b0944c8be3e739a25a948092ee Mon Sep 17 00:00:00 2001 From: Nicolas Borges Date: Tue, 28 Jan 2025 17:06:58 -0500 Subject: [PATCH 07/13] addition of view container and updated base view --- plugin/plugin.xml | 50 +----- .../handlers/QOpenLoginViewHandler.java | 1 - .../eclipse/amazonq/plugin/Activator.java | 2 +- .../amazonq/views/AmazonQViewContainer.java | 144 ++++++++++++------ .../amazonq/views/CallToActionView.java | 92 ++++++++++- .../amazonq/views/ChatAssetMissingView.java | 95 ++++++++---- .../amazonq/views/DependencyMissingView.java | 26 +--- .../amazonq/views/ReauthenticateView.java | 15 +- .../amazonq/views/SomeOtherTestView.java | 47 ------ .../eclipse/amazonq/views/SomeTestView.java | 47 ------ .../eclipse/amazonq/views/ViewConstants.java | 8 +- .../amazonq/views/ViewVisibilityManager.java | 1 + 12 files changed, 273 insertions(+), 255 deletions(-) delete mode 100644 plugin/src/software/aws/toolkits/eclipse/amazonq/views/SomeOtherTestView.java delete mode 100644 plugin/src/software/aws/toolkits/eclipse/amazonq/views/SomeTestView.java diff --git a/plugin/plugin.xml b/plugin/plugin.xml index 2a40148c..645d9bfb 100644 --- a/plugin/plugin.xml +++ b/plugin/plugin.xml @@ -15,30 +15,12 @@ name="Amazon Q" id="amazonq"> - - - - - - - - - - - - diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/handlers/QOpenLoginViewHandler.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/handlers/QOpenLoginViewHandler.java index d595d658..9a6d706b 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/handlers/QOpenLoginViewHandler.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/handlers/QOpenLoginViewHandler.java @@ -6,7 +6,6 @@ import org.eclipse.core.commands.AbstractHandler; import org.eclipse.core.commands.ExecutionEvent; -import software.aws.toolkits.eclipse.amazonq.lsp.manager.LspStatusManager; import software.aws.toolkits.eclipse.amazonq.views.ViewVisibilityManager; public class QOpenLoginViewHandler extends AbstractHandler { 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 b8396a7f..64b7a750 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/plugin/Activator.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/plugin/Activator.java @@ -16,10 +16,10 @@ import software.aws.toolkits.eclipse.amazonq.providers.LspProviderImpl; import software.aws.toolkits.eclipse.amazonq.telemetry.service.DefaultTelemetryService; import software.aws.toolkits.eclipse.amazonq.telemetry.service.TelemetryService; +import software.aws.toolkits.eclipse.amazonq.util.PluginLogger; import software.aws.toolkits.eclipse.amazonq.util.CodeReferenceLoggingService; import software.aws.toolkits.eclipse.amazonq.util.DefaultCodeReferenceLoggingService; import software.aws.toolkits.eclipse.amazonq.util.LoggingService; -import software.aws.toolkits.eclipse.amazonq.util.PluginLogger; public class Activator extends AbstractUIPlugin { diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQViewContainer.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQViewContainer.java index 7af5a9b9..1d7ab2ff 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQViewContainer.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQViewContainer.java @@ -3,84 +3,132 @@ package software.aws.toolkits.eclipse.amazonq.views; -import java.util.HashMap; import java.util.Map; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StackLayout; 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.Event; -import org.eclipse.swt.widgets.Listener; +import org.eclipse.swt.widgets.Display; import org.eclipse.ui.part.ViewPart; +import software.aws.toolkits.eclipse.amazonq.views.actions.AmazonQStaticActions; + + public class AmazonQViewContainer extends ViewPart { private Composite parentComposite; private StackLayout layout; - private Map views = new HashMap<>(); - String activeId; - BaseAmazonQView activeView; + private Map views; + private String activeId; + private BaseAmazonQView activeView; public AmazonQViewContainer() { initializeViews(); + //Activator.getBroker().subscribe(ViewId.class, this); } + + /* + * ViewRouter.Initialize() + * viewcontainer.init(activeViewId) + */ + + //1. showView(viewContainer.ID) + //2. viewContainer.init(currentView) + public void initializeViews() { + //init map containing all views - var testViewA = new SomeTestView(); - var testViewB = new SomeOtherTestView(); - views.put(ViewConstants.TEST_VIEW_A_ID, testViewA); - views.put(ViewConstants.TEST_VIEW_B_ID, testViewB); + var dependencyMissingView = new DependencyMissingView(); + var chatAssetMissingView = new ChatAssetMissingView(); + var reAuthView = new ReauthenticateView(); + views = Map.of( + ViewConstants.CHAT_ASSET_MISSING_VIEW_ID, chatAssetMissingView, + ViewConstants.DEPENDENCY_MISSING_VIEW_ID, dependencyMissingView, + ViewConstants.REAUTHENTICATE_VIEW_ID, reAuthView); + + //default view + activeView = views.get(ViewConstants.REAUTHENTICATE_VIEW_ID); + + //activeView == current activeView + //chatView as defaultView? + + //query router --> grab current activeView + } public final void createPartControl(final Composite parent) { - // ===== TEMPORARY TEST SETUP - TO BE REPLACED WITH EVENT BUS ===== - // this nested container structure is only for testing view switching functionality - // once event bus + view router implemented: - // 1. remove temporary container and button - // 2. single StackLayout on parent - // 3. view switching handled through event bus subscription - parentComposite = new Composite(parent, SWT.NONE); - GridLayout mainLayout = new GridLayout(1, false); - parentComposite.setLayout(mainLayout); - - // Temporary test button - will be removed - var button = new Button(parentComposite, SWT.PUSH); - button.setText("Switch View"); - button.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, false)); - - // Temporary nested container for StackLayout - Composite stackContainer = new Composite(parentComposite, SWT.NONE); - stackContainer.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + parentComposite = parent; layout = new StackLayout(); - stackContainer.setLayout(layout); - - // Create initial view in stack container - Composite viewAComposite = views.get(ViewConstants.TEST_VIEW_A_ID).setupView(stackContainer); - layout.topControl = viewAComposite; - activeId = ViewConstants.TEST_VIEW_A_ID; - - // Temporary button listener - will be replaced by event bus subscription - button.addListener(SWT.Selection, new Listener() { - @Override - public void handleEvent(final Event event) { - String newViewId = activeId.equals(ViewConstants.TEST_VIEW_A_ID) - ? ViewConstants.TEST_VIEW_B_ID - : ViewConstants.TEST_VIEW_A_ID; - - BaseAmazonQView newView = views.get(newViewId); - layout.topControl = newView.setupView(stackContainer); - activeId = newViewId; - stackContainer.layout(); + parent.setLayout(layout); + + //add base stylings + GridLayout gridLayout = new GridLayout(1, false); + gridLayout.marginLeft = 20; + gridLayout.marginRight = 20; + gridLayout.marginTop = 10; + gridLayout.marginBottom = 10; + parent.setLayout(gridLayout); + + setupStaticMenuActions(); + updateChildView(activeView, ViewConstants.REAUTHENTICATE_VIEW_ID); + } + + /* change methodology for setupMenuActions -- move outside of viewContainer? + * will need ability to switch between the two based on which view is displaying && authState + * if viewId = static view --> new AmazonQStaticActions(getViewSite()); + * if viewId = common view --> new AmazonQCommonActions(AuthState, getViewSite()); + */ + private void setupStaticMenuActions() { + new AmazonQStaticActions(getViewSite()); + } + + private void updateChildView(BaseAmazonQView newView, String newViewId) { + Display.getDefault().asyncExec(() -> { + + if (activeView != null) { + activeView.dispose(); + if (layout.topControl != null) { + layout.topControl.dispose(); + } } + + Composite newViewComposite = newView.setupView(parentComposite); + GridData gridData = new GridData(SWT.CENTER, SWT.CENTER, true, true); + newViewComposite.setLayoutData(gridData); + + layout.topControl = newViewComposite; + parentComposite.layout(true, true); + + activeView = newView; + activeId = newViewId; }); } + public void onChangeViewEvent(String newViewId) { + if (activeId != null && activeId.equals(newViewId)) { + return; + } + + if (views.containsKey(newViewId)) { + BaseAmazonQView newView = views.get(newViewId); + if (!parentComposite.isDisposed()) { + updateChildView(newView, newViewId); + } + } + } @Override public final void setFocus() { parentComposite.setFocus(); } + + @Override + public void dispose() { + if (activeView != null) { + activeView.dispose(); + } + super.dispose(); + } } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/CallToActionView.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/CallToActionView.java index fbeb1f70..39efcae8 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/CallToActionView.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/CallToActionView.java @@ -3,27 +3,86 @@ package software.aws.toolkits.eclipse.amazonq.views; +import java.io.IOException; +import java.net.URL; + import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.graphics.Image; 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; +import org.eclipse.swt.widgets.Label; + +import software.aws.toolkits.eclipse.amazonq.plugin.Activator; +import software.aws.toolkits.eclipse.amazonq.util.PluginUtils; -public abstract class CallToActionView extends BaseView { +public abstract class CallToActionView implements BaseAmazonQView { private String buttonLabel; private SelectionListener buttonHandler; + private final String ICON_PATH = getIconPath(); + private final String HEADER_LABEL = getHeaderLabel(); + private final String DETAIL_MESSAGE = getDetailMessage(); + private Image icon; + protected abstract String getButtonLabel(); protected abstract SelectionListener getButtonHandler(); protected abstract void setupButtonFooterContent(Composite composite); @Override - protected final void setupView() { - super.setupView(); + public Composite setupView(Composite parentComposite) { + Composite container = new Composite(parentComposite, SWT.NONE); + GridLayout layout = new GridLayout(1, false); + layout.marginWidth = 10; + layout.marginHeight = 10; + container.setLayout(layout); + + // Center the container itself + container.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true)); + + Label iconLabel = new Label(container, SWT.NONE); + icon = loadImage(ICON_PATH); + if (icon != null) { + iconLabel.setImage(icon); + iconLabel.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, false)); + + iconLabel.addDisposeListener(e -> { + if (icon != null && !icon.isDisposed()) { + icon.dispose(); + } + }); + } + + Label headerLabel = new Label(container, SWT.CENTER | SWT.WRAP); + headerLabel.setText(HEADER_LABEL); + headerLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + + Label detailLabel = new Label(container, SWT.CENTER | SWT.WRAP); + detailLabel.setText(DETAIL_MESSAGE); + detailLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + this.buttonLabel = getButtonLabel(); this.buttonHandler = getButtonHandler(); - setupButton(getContentComposite()); - setupButtonFooterContent(getContentComposite()); + setupButton(container); + setupButtonFooterContent(container); + + return container; + } + + private Image loadImage(final String imagePath) { + Image loadedImage = null; + try { + URL imageUrl = PluginUtils.getResource(imagePath); + if (imageUrl != null) { + loadedImage = new Image(Display.getCurrent(), imageUrl.openStream()); + } + } catch (IOException e) { + Activator.getLogger().warn(e.getMessage(), e); + } + return loadedImage; } private void setupButton(final Composite composite) { @@ -45,4 +104,27 @@ protected void updateButtonStyle(final Button button) { return; } + @Override + public void dispose() { + // Default implementation - subclasses can override if they need to dispose of resources + } + + @Override + public boolean canDisplay() { + // Default implementation - subclasses should override to provide specific display logic + return true; + } + protected String getIconPath() { + // TODO Auto-generated method stub + return null; + } + protected String getHeaderLabel() { + // TODO Auto-generated method stub + return null; + } + protected String getDetailMessage() { + // TODO Auto-generated method stub + return null; + } + } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ChatAssetMissingView.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ChatAssetMissingView.java index 8209a6bf..514337a5 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ChatAssetMissingView.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ChatAssetMissingView.java @@ -3,60 +3,101 @@ package software.aws.toolkits.eclipse.amazonq.views; +import java.io.IOException; +import java.net.URL; + import java.util.Optional; -import java.util.concurrent.CompletableFuture; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Label; import software.aws.toolkits.eclipse.amazonq.plugin.Activator; import software.aws.toolkits.eclipse.amazonq.util.ChatAssetProvider; +import software.aws.toolkits.eclipse.amazonq.util.PluginUtils; +import software.aws.toolkits.eclipse.amazonq.views.actions.AmazonQStaticActions; -public final class ChatAssetMissingView extends BaseView { +public final class ChatAssetMissingView implements BaseAmazonQView { public static final String ID = "software.aws.toolkits.eclipse.amazonq.views.ChatAssetMissingView"; private static final String ICON_PATH = "icons/AmazonQ64.png"; private static final String HEADER_LABEL = "Error loading Q chat."; private static final String DETAIL_MESSAGE = "Restart Eclipse or review error logs for troubleshooting"; private ChatAssetProvider chatAssetProvider; + private Image icon; + private Composite container; public ChatAssetMissingView() { this.chatAssetProvider = new ChatAssetProvider(); } @Override - protected String getIconPath() { - return ICON_PATH; - } + public Composite setupView(Composite parentComposite) { + container = new Composite(parentComposite, SWT.NONE); + GridLayout layout = new GridLayout(1, false); + layout.marginWidth = 20; + layout.marginHeight = 10; + container.setLayout(layout); - @Override - protected String getHeaderLabel() { - return HEADER_LABEL; - } + Label iconLabel = new Label(container, SWT.NONE); + icon = loadImage(ICON_PATH); + if (icon != null) { + iconLabel.setImage(icon); + iconLabel.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, false)); - @Override - protected String getDetailMessage() { - return DETAIL_MESSAGE; + iconLabel.addDisposeListener(e -> { + if (icon != null && !icon.isDisposed()) { + icon.dispose(); + } + }); + } + + Label headerLabel = new Label(container, SWT.CENTER | SWT.WRAP); + headerLabel.setText(HEADER_LABEL); + headerLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + + Label detailLabel = new Label(container, SWT.CENTER | SWT.WRAP); + detailLabel.setText(DETAIL_MESSAGE); + detailLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + + return container; } - @Override - protected CompletableFuture isViewDisplayable() { - return CompletableFuture.supplyAsync(() -> { - try { - Optional chatAsset = chatAssetProvider.get(); - return !chatAsset.isPresent(); - } catch (Exception ex) { - Activator.getLogger().error("Failed to verify Amazon Q chat content is retrievable", ex); - return true; // Safer to display chat asset missing view by default than give access + private Image loadImage(final String imagePath) { + Image loadedImage = null; + try { + URL imageUrl = PluginUtils.getResource(imagePath); + if (imageUrl != null) { + loadedImage = new Image(Display.getCurrent(), imageUrl.openStream()); } - }); + } catch (IOException e) { + Activator.getLogger().warn(e.getMessage(), e); + } + return loadedImage; } @Override - protected void showAlternateView() { - ViewVisibilityManager.showChatView("restart"); + public void dispose() { + if (chatAssetProvider != null) { + chatAssetProvider.dispose(); + chatAssetProvider = null; + } } + //should live in the viewRouter for the time being + @Override - public void dispose() { - chatAssetProvider.dispose(); - super.dispose(); + public boolean canDisplay() { + try { + Optional chatAsset = chatAssetProvider.get(); + return !chatAsset.isPresent(); + } catch (Exception ex) { + Activator.getLogger().error("Failed to verify Amazon Q chat content is retrievable", ex); + return true; // Safer to display chat asset missing view by default + } } } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/DependencyMissingView.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/DependencyMissingView.java index 8a22ac17..5b706046 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/DependencyMissingView.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/DependencyMissingView.java @@ -3,18 +3,14 @@ package software.aws.toolkits.eclipse.amazonq.views; -import java.util.concurrent.CompletableFuture; - import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Link; -import software.aws.toolkits.eclipse.amazonq.controllers.AmazonQViewController; import software.aws.toolkits.eclipse.amazonq.plugin.Activator; import software.aws.toolkits.eclipse.amazonq.util.PluginPlatform; import software.aws.toolkits.eclipse.amazonq.util.PluginUtils; @@ -34,11 +30,9 @@ public final class DependencyMissingView extends CallToActionView { private PluginPlatform platform; - private AmazonQViewController viewController; public DependencyMissingView() { platform = PluginUtils.getPlatform(); - viewController = new AmazonQViewController(); } @Override @@ -105,23 +99,7 @@ private String getDependency() { } @Override - protected CompletableFuture isViewDisplayable() { - return CompletableFuture.supplyAsync(() -> { - try { - Display.getDefault().syncExec(() -> { // Must be executed synchronously to ensure the correct hasWebViewDependency() result - viewController.setupBrowser(getParentComposite()); - viewController.getBrowser().dispose(); - }); - return !viewController.hasWebViewDependency(); - } catch (Exception ex) { - Activator.getLogger().error("Failed to check webview dependency", ex); - return true; // Safer to display dependency missing view by default than give access - } - }); - } - - @Override - protected void showAlternateView() { - ViewVisibilityManager.showChatView("restart"); + public boolean canDisplay() { + return true; } } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ReauthenticateView.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ReauthenticateView.java index 6a48a6e5..5584f765 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ReauthenticateView.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ReauthenticateView.java @@ -3,7 +3,6 @@ package software.aws.toolkits.eclipse.amazonq.views; -import java.util.concurrent.CompletableFuture; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionAdapter; @@ -98,7 +97,6 @@ public void widgetSelected(final SelectionEvent e) { } }); } - @Override public void onEvent(final AuthState authState) { Display.getDefault().asyncExec(() -> { @@ -139,20 +137,17 @@ protected void updateButtonStyle(final Button button) { } @Override - public void dispose() { - authStateSubscription.dispose(); + public boolean canDisplay() { + return true; } - @Override - protected CompletableFuture isViewDisplayable() { - return CompletableFuture.completedFuture(Activator.getLoginService().getAuthState().isExpired()); - } @Override - protected void showAlternateView() { - ViewVisibilityManager.showChatView("restart"); + public void dispose() { + authStateSubscription.dispose(); } + private void resizeButtonFont(final Button button, final int newFontSize) { Font currentFont = button.getFont(); FontData[] fontData = currentFont.getFontData(); diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/SomeOtherTestView.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/SomeOtherTestView.java deleted file mode 100644 index 3510099f..00000000 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/SomeOtherTestView.java +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -package software.aws.toolkits.eclipse.amazonq.views; - -import org.eclipse.swt.SWT; -import org.eclipse.swt.graphics.Font; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Label; - -public class SomeOtherTestView implements BaseAmazonQView { - private Font titleFont; // Keep reference to dispose later - - @Override - public Composite setupView(Composite parentComposite) { - // Create a new composite with a GridLayout - Composite container = new Composite(parentComposite, SWT.NONE); - GridLayout layout = new GridLayout(1, false); - container.setLayout(layout); - - // Add a label with some text - Label label = new Label(container, SWT.NONE); - label.setText("This is Test View B!!!!!"); - label.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true)); - - // Optional: Add some styling to make it more visible - titleFont = new Font(container.getDisplay(), "Arial", 14, SWT.BOLD); - label.setFont(titleFont); - - return container; - } - - @Override - public void dispose() { - if (titleFont != null && !titleFont.isDisposed()) { - titleFont.dispose(); - titleFont = null; - } - } - - @Override - public boolean canDisplay() { - return true; - } -} diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/SomeTestView.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/SomeTestView.java deleted file mode 100644 index 89f009bb..00000000 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/SomeTestView.java +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -package software.aws.toolkits.eclipse.amazonq.views; - -import org.eclipse.swt.SWT; -import org.eclipse.swt.graphics.Font; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Label; - -public class SomeTestView implements BaseAmazonQView { - private Font titleFont; // Keep reference to dispose later - - @Override - public Composite setupView(Composite parentComposite) { - // Create a new composite with a GridLayout - Composite container = new Composite(parentComposite, SWT.NONE); - GridLayout layout = new GridLayout(1, false); - container.setLayout(layout); - - // Add a label with some text - Label label = new Label(container, SWT.NONE); - label.setText("This is Test View A"); - label.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true)); - - // Optional: Add some styling to make it more visible - titleFont = new Font(container.getDisplay(), "Arial", 14, SWT.BOLD); - label.setFont(titleFont); - - return container; - } - - @Override - public void dispose() { - if (titleFont != null && !titleFont.isDisposed()) { - titleFont.dispose(); - titleFont = null; - } - } - - @Override - public boolean canDisplay() { - return true; - } -} diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ViewConstants.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ViewConstants.java index 8adeed58..dc7e9f76 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ViewConstants.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ViewConstants.java @@ -10,6 +10,10 @@ private ViewConstants() { public static final String COMMAND_FUNCTION_NAME = "ideCommand"; public static final String PREFERENCE_STORE_PLUGIN_FIRST_STARTUP_KEY = "qEclipseFirstLoad"; - public static final String TEST_VIEW_A_ID = "testViewAId"; - public static final String TEST_VIEW_B_ID = "testViewBId"; + public static final String CHAT_ASSET_MISSING_VIEW_ID = + "software.aws.toolkits.eclipse.amazonq.views.ChatAssetMissingView"; + public static final String DEPENDENCY_MISSING_VIEW_ID = + "software.aws.toolkits.eclipse.amazonq.views.DependencyMissingView"; + public static final String REAUTHENTICATE_VIEW_ID = + "software.aws.toolkits.eclipse.amazonq.views.ReauthenticateView"; } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ViewVisibilityManager.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ViewVisibilityManager.java index 9d43fd95..511dde9f 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ViewVisibilityManager.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ViewVisibilityManager.java @@ -54,6 +54,7 @@ public static void showLoginView(final String source) { public static void showViewContainer(final String source) { showMutuallyExclusiveView(AMAZON_Q_VIEW_CONTAINER, source); + //put a call here to init viewContainer? } public static void showChatView(final String source) { From 028a7a526c2414d33e74c9a3934771f6798b26b6 Mon Sep 17 00:00:00 2001 From: Nicolas Borges Date: Tue, 28 Jan 2025 17:42:22 -0500 Subject: [PATCH 08/13] convert view container set up to feature viewId enum --- .../handlers/QOpenLoginViewHandler.java | 5 -- .../amazonq/views/AmazonQViewContainer.java | 71 ++++++++++--------- .../eclipse/amazonq/views/ViewConstants.java | 6 -- 3 files changed, 38 insertions(+), 44 deletions(-) diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/handlers/QOpenLoginViewHandler.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/handlers/QOpenLoginViewHandler.java index 9a6d706b..2d9e6362 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/handlers/QOpenLoginViewHandler.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/handlers/QOpenLoginViewHandler.java @@ -11,11 +11,6 @@ public class QOpenLoginViewHandler extends AbstractHandler { @Override public final Object execute(final ExecutionEvent event) { -// if (Activator.getLoginService().getAuthState().isLoggedIn()) { -// ViewVisibilityManager.showChatView("statusBar"); -// } else { -// ViewVisibilityManager.showLoginView("statusBar"); -// } ViewVisibilityManager.showViewContainer("source"); return null; } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQViewContainer.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQViewContainer.java index 1d7ab2ff..424b5f6c 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQViewContainer.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQViewContainer.java @@ -13,48 +13,51 @@ import org.eclipse.swt.widgets.Display; import org.eclipse.ui.part.ViewPart; +import io.reactivex.rxjava3.disposables.Disposable; +import software.aws.toolkits.eclipse.amazonq.broker.api.EventObserver; +import software.aws.toolkits.eclipse.amazonq.plugin.Activator; import software.aws.toolkits.eclipse.amazonq.views.actions.AmazonQStaticActions; +import software.aws.toolkits.eclipse.amazonq.views.router.ViewId; -public class AmazonQViewContainer extends ViewPart { +public class AmazonQViewContainer extends ViewPart implements EventObserver { private Composite parentComposite; private StackLayout layout; - private Map views; - private String activeId; + private Map views; + private ViewId activeId; private BaseAmazonQView activeView; + private Disposable viewChangeEventSubscription; public AmazonQViewContainer() { - initializeViews(); - //Activator.getBroker().subscribe(ViewId.class, this); + viewChangeEventSubscription = Activator.getEventBroker().subscribe(ViewId.class, this); } - /* + /* Router should be initialized, then init view container * ViewRouter.Initialize() * viewcontainer.init(activeViewId) */ - //1. showView(viewContainer.ID) - //2. viewContainer.init(currentView) + /* + * When container is disposed and being reopened, class will be recreated + * need to call showView to reset viewContainer && follow up with init call to container + * 1. showView(viewContainer.ID) + * 2. viewContainer.init(currentView) + */ - public void initializeViews() { + public void initializeViews(ViewId currentActiveViewId) { //init map containing all views var dependencyMissingView = new DependencyMissingView(); var chatAssetMissingView = new ChatAssetMissingView(); var reAuthView = new ReauthenticateView(); views = Map.of( - ViewConstants.CHAT_ASSET_MISSING_VIEW_ID, chatAssetMissingView, - ViewConstants.DEPENDENCY_MISSING_VIEW_ID, dependencyMissingView, - ViewConstants.REAUTHENTICATE_VIEW_ID, reAuthView); - - //default view - activeView = views.get(ViewConstants.REAUTHENTICATE_VIEW_ID); - - //activeView == current activeView - //chatView as defaultView? - - //query router --> grab current activeView + ViewId.CHAT_ASSET_MISSING_VIEW, chatAssetMissingView, + ViewId.DEPENDENCY_MISSING_VIEW, dependencyMissingView, + ViewId.RE_AUTHENTICATE_VIEW, reAuthView); + //default view passed in from router + //possible we'll use chatView as default? + activeView = views.get(currentActiveViewId); } public final void createPartControl(final Composite parent) { @@ -71,7 +74,7 @@ public final void createPartControl(final Composite parent) { parent.setLayout(gridLayout); setupStaticMenuActions(); - updateChildView(activeView, ViewConstants.REAUTHENTICATE_VIEW_ID); + updateChildView(activeView, ViewId.RE_AUTHENTICATE_VIEW); } /* change methodology for setupMenuActions -- move outside of viewContainer? @@ -83,7 +86,7 @@ private void setupStaticMenuActions() { new AmazonQStaticActions(getViewSite()); } - private void updateChildView(BaseAmazonQView newView, String newViewId) { + private void updateChildView(BaseAmazonQView newView, ViewId newViewId) { Display.getDefault().asyncExec(() -> { if (activeView != null) { @@ -105,17 +108,18 @@ private void updateChildView(BaseAmazonQView newView, String newViewId) { }); } - public void onChangeViewEvent(String newViewId) { - if (activeId != null && activeId.equals(newViewId)) { - return; - } - - if (views.containsKey(newViewId)) { - BaseAmazonQView newView = views.get(newViewId); - if (!parentComposite.isDisposed()) { - updateChildView(newView, newViewId); - } - } + @Override + public void onEvent(final ViewId newViewId) { + if (activeId != null && activeId.equals(newViewId)) { + return; + } + + if (views.containsKey(newViewId)) { + BaseAmazonQView newView = views.get(newViewId); + if (!parentComposite.isDisposed()) { + updateChildView(newView, newViewId); + } + } } @Override @@ -126,6 +130,7 @@ public final void setFocus() { @Override public void dispose() { + viewChangeEventSubscription.dispose(); if (activeView != null) { activeView.dispose(); } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ViewConstants.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ViewConstants.java index dc7e9f76..6cfe4669 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ViewConstants.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ViewConstants.java @@ -10,10 +10,4 @@ private ViewConstants() { public static final String COMMAND_FUNCTION_NAME = "ideCommand"; public static final String PREFERENCE_STORE_PLUGIN_FIRST_STARTUP_KEY = "qEclipseFirstLoad"; - public static final String CHAT_ASSET_MISSING_VIEW_ID = - "software.aws.toolkits.eclipse.amazonq.views.ChatAssetMissingView"; - public static final String DEPENDENCY_MISSING_VIEW_ID = - "software.aws.toolkits.eclipse.amazonq.views.DependencyMissingView"; - public static final String REAUTHENTICATE_VIEW_ID = - "software.aws.toolkits.eclipse.amazonq.views.ReauthenticateView"; } From a053f7e4ed18e40514b4157791ee6d51bd8514b4 Mon Sep 17 00:00:00 2001 From: Nicolas Borges Date: Tue, 28 Jan 2025 17:50:35 -0500 Subject: [PATCH 09/13] remove unused import --- .../aws/toolkits/eclipse/amazonq/views/ChatAssetMissingView.java | 1 - 1 file changed, 1 deletion(-) diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ChatAssetMissingView.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ChatAssetMissingView.java index 514337a5..f050500f 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ChatAssetMissingView.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ChatAssetMissingView.java @@ -19,7 +19,6 @@ import software.aws.toolkits.eclipse.amazonq.plugin.Activator; import software.aws.toolkits.eclipse.amazonq.util.ChatAssetProvider; import software.aws.toolkits.eclipse.amazonq.util.PluginUtils; -import software.aws.toolkits.eclipse.amazonq.views.actions.AmazonQStaticActions; public final class ChatAssetMissingView implements BaseAmazonQView { public static final String ID = "software.aws.toolkits.eclipse.amazonq.views.ChatAssetMissingView"; From 8ac51f091b077d43a65a00605c8a1abfbccefc3d Mon Sep 17 00:00:00 2001 From: Nicolas Borges Date: Tue, 28 Jan 2025 18:08:38 -0500 Subject: [PATCH 10/13] checkstyle edits --- .../amazonq/views/AmazonQViewContainer.java | 10 +++--- .../amazonq/views/BaseAmazonQView.java | 6 ++-- .../amazonq/views/CallToActionView.java | 34 +++++++------------ .../amazonq/views/ChatAssetMissingView.java | 2 +- 4 files changed, 22 insertions(+), 30 deletions(-) diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQViewContainer.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQViewContainer.java index 424b5f6c..1db88790 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQViewContainer.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQViewContainer.java @@ -20,7 +20,7 @@ import software.aws.toolkits.eclipse.amazonq.views.router.ViewId; -public class AmazonQViewContainer extends ViewPart implements EventObserver { +public final class AmazonQViewContainer extends ViewPart implements EventObserver { private Composite parentComposite; private StackLayout layout; private Map views; @@ -44,7 +44,7 @@ public AmazonQViewContainer() { * 2. viewContainer.init(currentView) */ - public void initializeViews(ViewId currentActiveViewId) { + public void initializeViews(final ViewId currentActiveViewId) { //init map containing all views var dependencyMissingView = new DependencyMissingView(); @@ -60,7 +60,7 @@ public void initializeViews(ViewId currentActiveViewId) { activeView = views.get(currentActiveViewId); } - public final void createPartControl(final Composite parent) { + public void createPartControl(final Composite parent) { parentComposite = parent; layout = new StackLayout(); parent.setLayout(layout); @@ -86,7 +86,7 @@ private void setupStaticMenuActions() { new AmazonQStaticActions(getViewSite()); } - private void updateChildView(BaseAmazonQView newView, ViewId newViewId) { + private void updateChildView(final BaseAmazonQView newView, final ViewId newViewId) { Display.getDefault().asyncExec(() -> { if (activeView != null) { @@ -123,7 +123,7 @@ public void onEvent(final ViewId newViewId) { } @Override - public final void setFocus() { + public void setFocus() { parentComposite.setFocus(); } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/BaseAmazonQView.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/BaseAmazonQView.java index 6e3de5fe..4886774f 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/BaseAmazonQView.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/BaseAmazonQView.java @@ -6,7 +6,7 @@ import org.eclipse.swt.widgets.Composite; public interface BaseAmazonQView { - public Composite setupView(Composite parentComposite); - public boolean canDisplay(); - public void dispose(); + Composite setupView(Composite parentComposite); + boolean canDisplay(); + void dispose(); } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/CallToActionView.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/CallToActionView.java index 39efcae8..c7448510 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/CallToActionView.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/CallToActionView.java @@ -23,9 +23,9 @@ public abstract class CallToActionView implements BaseAmazonQView { private String buttonLabel; private SelectionListener buttonHandler; - private final String ICON_PATH = getIconPath(); - private final String HEADER_LABEL = getHeaderLabel(); - private final String DETAIL_MESSAGE = getDetailMessage(); + private final String iconPath = getIconPath(); + private final String headerLabel = getHeaderLabel(); + private final String detailMessage = getDetailMessage(); private Image icon; protected abstract String getButtonLabel(); @@ -33,7 +33,7 @@ public abstract class CallToActionView implements BaseAmazonQView { protected abstract void setupButtonFooterContent(Composite composite); @Override - public Composite setupView(Composite parentComposite) { + public final Composite setupView(final Composite parentComposite) { Composite container = new Composite(parentComposite, SWT.NONE); GridLayout layout = new GridLayout(1, false); layout.marginWidth = 10; @@ -44,7 +44,7 @@ public Composite setupView(Composite parentComposite) { container.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true)); Label iconLabel = new Label(container, SWT.NONE); - icon = loadImage(ICON_PATH); + icon = loadImage(iconPath); if (icon != null) { iconLabel.setImage(icon); iconLabel.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, false)); @@ -56,12 +56,12 @@ public Composite setupView(Composite parentComposite) { }); } - Label headerLabel = new Label(container, SWT.CENTER | SWT.WRAP); - headerLabel.setText(HEADER_LABEL); - headerLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + Label header = new Label(container, SWT.CENTER | SWT.WRAP); + header.setText(headerLabel); + header.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); Label detailLabel = new Label(container, SWT.CENTER | SWT.WRAP); - detailLabel.setText(DETAIL_MESSAGE); + detailLabel.setText(detailMessage); detailLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); this.buttonLabel = getButtonLabel(); @@ -109,22 +109,14 @@ public void dispose() { // Default implementation - subclasses can override if they need to dispose of resources } + @SuppressWarnings("DesignForExtension") @Override public boolean canDisplay() { // Default implementation - subclasses should override to provide specific display logic return true; } - protected String getIconPath() { - // TODO Auto-generated method stub - return null; - } - protected String getHeaderLabel() { - // TODO Auto-generated method stub - return null; - } - protected String getDetailMessage() { - // TODO Auto-generated method stub - return null; - } + protected abstract String getIconPath(); + protected abstract String getHeaderLabel(); + protected abstract String getDetailMessage(); } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ChatAssetMissingView.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ChatAssetMissingView.java index f050500f..12d735b8 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ChatAssetMissingView.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ChatAssetMissingView.java @@ -35,7 +35,7 @@ public ChatAssetMissingView() { } @Override - public Composite setupView(Composite parentComposite) { + public Composite setupView(final Composite parentComposite) { container = new Composite(parentComposite, SWT.NONE); GridLayout layout = new GridLayout(1, false); layout.marginWidth = 20; From ceae903c37d7cb37e32d0a301f9746ae7974c35f Mon Sep 17 00:00:00 2001 From: Nicolas Borges Date: Wed, 29 Jan 2025 10:02:35 -0500 Subject: [PATCH 11/13] translated base view to abstract class and changed lsp failure view to extend base view --- .../handlers/QOpenLoginViewHandler.java | 2 +- .../amazonq/views/AmazonQViewContainer.java | 10 ++- .../amazonq/views/BaseAmazonQView.java | 28 +++++++-- .../amazonq/views/CallToActionView.java | 28 +-------- .../amazonq/views/ChatAssetMissingView.java | 37 +---------- .../amazonq/views/DependencyMissingView.java | 5 -- .../amazonq/views/LspStartUpFailedView.java | 63 ++++++++++++------- .../amazonq/views/ReauthenticateView.java | 6 -- 8 files changed, 76 insertions(+), 103 deletions(-) diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/handlers/QOpenLoginViewHandler.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/handlers/QOpenLoginViewHandler.java index 2d9e6362..96cbef9a 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/handlers/QOpenLoginViewHandler.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/handlers/QOpenLoginViewHandler.java @@ -11,7 +11,7 @@ public class QOpenLoginViewHandler extends AbstractHandler { @Override public final Object execute(final ExecutionEvent event) { - ViewVisibilityManager.showViewContainer("source"); + ViewVisibilityManager.showViewContainer("statusBar"); return null; } } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQViewContainer.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQViewContainer.java index 1db88790..a6ea0dfc 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQViewContainer.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQViewContainer.java @@ -30,6 +30,7 @@ public final class AmazonQViewContainer extends ViewPart implements EventObserve public AmazonQViewContainer() { viewChangeEventSubscription = Activator.getEventBroker().subscribe(ViewId.class, this); + initializeViews(null); } /* Router should be initialized, then init view container @@ -50,14 +51,17 @@ public void initializeViews(final ViewId currentActiveViewId) { var dependencyMissingView = new DependencyMissingView(); var chatAssetMissingView = new ChatAssetMissingView(); var reAuthView = new ReauthenticateView(); + var lspFailedView = new LspStartUpFailedView(); views = Map.of( ViewId.CHAT_ASSET_MISSING_VIEW, chatAssetMissingView, ViewId.DEPENDENCY_MISSING_VIEW, dependencyMissingView, - ViewId.RE_AUTHENTICATE_VIEW, reAuthView); + ViewId.RE_AUTHENTICATE_VIEW, reAuthView, + ViewId.LSP_STARTUP_FAILED_VIEW, lspFailedView + ); //default view passed in from router //possible we'll use chatView as default? - activeView = views.get(currentActiveViewId); + activeView = views.get(ViewId.LSP_STARTUP_FAILED_VIEW); } public void createPartControl(final Composite parent) { @@ -110,7 +114,7 @@ private void updateChildView(final BaseAmazonQView newView, final ViewId newView @Override public void onEvent(final ViewId newViewId) { - if (activeId != null && activeId.equals(newViewId)) { + if (activeId != null && activeId.equals(newViewId)) { return; } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/BaseAmazonQView.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/BaseAmazonQView.java index 4886774f..2a8ad679 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/BaseAmazonQView.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/BaseAmazonQView.java @@ -3,10 +3,30 @@ package software.aws.toolkits.eclipse.amazonq.views; +import java.io.IOException; +import java.net.URL; + +import org.eclipse.swt.graphics.Image; import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; + +import software.aws.toolkits.eclipse.amazonq.plugin.Activator; +import software.aws.toolkits.eclipse.amazonq.util.PluginUtils; + +public abstract class BaseAmazonQView { + public abstract Composite setupView(Composite parentComposite); + public abstract void dispose(); -public interface BaseAmazonQView { - Composite setupView(Composite parentComposite); - boolean canDisplay(); - void dispose(); + protected Image loadImage(final String imagePath) { + Image loadedImage = null; + try { + URL imageUrl = PluginUtils.getResource(imagePath); + if (imageUrl != null) { + loadedImage = new Image(Display.getCurrent(), imageUrl.openStream()); + } + } catch (IOException e) { + Activator.getLogger().warn(e.getMessage(), e); + } + return loadedImage; + } } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/CallToActionView.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/CallToActionView.java index c7448510..6f385ec5 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/CallToActionView.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/CallToActionView.java @@ -3,9 +3,6 @@ package software.aws.toolkits.eclipse.amazonq.views; -import java.io.IOException; -import java.net.URL; - import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.graphics.Image; @@ -13,13 +10,9 @@ import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Label; -import software.aws.toolkits.eclipse.amazonq.plugin.Activator; -import software.aws.toolkits.eclipse.amazonq.util.PluginUtils; - -public abstract class CallToActionView implements BaseAmazonQView { +public abstract class CallToActionView extends BaseAmazonQView { private String buttonLabel; private SelectionListener buttonHandler; @@ -72,19 +65,6 @@ public final Composite setupView(final Composite parentComposite) { return container; } - private Image loadImage(final String imagePath) { - Image loadedImage = null; - try { - URL imageUrl = PluginUtils.getResource(imagePath); - if (imageUrl != null) { - loadedImage = new Image(Display.getCurrent(), imageUrl.openStream()); - } - } catch (IOException e) { - Activator.getLogger().warn(e.getMessage(), e); - } - return loadedImage; - } - private void setupButton(final Composite composite) { var button = new Button(composite, SWT.PUSH); updateButtonStyle(button); @@ -109,12 +89,6 @@ public void dispose() { // Default implementation - subclasses can override if they need to dispose of resources } - @SuppressWarnings("DesignForExtension") - @Override - public boolean canDisplay() { - // Default implementation - subclasses should override to provide specific display logic - return true; - } protected abstract String getIconPath(); protected abstract String getHeaderLabel(); protected abstract String getDetailMessage(); diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ChatAssetMissingView.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ChatAssetMissingView.java index 12d735b8..878c3060 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ChatAssetMissingView.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ChatAssetMissingView.java @@ -3,24 +3,15 @@ package software.aws.toolkits.eclipse.amazonq.views; -import java.io.IOException; -import java.net.URL; - -import java.util.Optional; - import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Label; - -import software.aws.toolkits.eclipse.amazonq.plugin.Activator; import software.aws.toolkits.eclipse.amazonq.util.ChatAssetProvider; -import software.aws.toolkits.eclipse.amazonq.util.PluginUtils; -public final class ChatAssetMissingView implements BaseAmazonQView { +public final class ChatAssetMissingView extends BaseAmazonQView { public static final String ID = "software.aws.toolkits.eclipse.amazonq.views.ChatAssetMissingView"; private static final String ICON_PATH = "icons/AmazonQ64.png"; @@ -66,19 +57,6 @@ public Composite setupView(final Composite parentComposite) { return container; } - private Image loadImage(final String imagePath) { - Image loadedImage = null; - try { - URL imageUrl = PluginUtils.getResource(imagePath); - if (imageUrl != null) { - loadedImage = new Image(Display.getCurrent(), imageUrl.openStream()); - } - } catch (IOException e) { - Activator.getLogger().warn(e.getMessage(), e); - } - return loadedImage; - } - @Override public void dispose() { if (chatAssetProvider != null) { @@ -86,17 +64,4 @@ public void dispose() { chatAssetProvider = null; } } - - //should live in the viewRouter for the time being - - @Override - public boolean canDisplay() { - try { - Optional chatAsset = chatAssetProvider.get(); - return !chatAsset.isPresent(); - } catch (Exception ex) { - Activator.getLogger().error("Failed to verify Amazon Q chat content is retrievable", ex); - return true; // Safer to display chat asset missing view by default - } - } } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/DependencyMissingView.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/DependencyMissingView.java index 5b706046..c6f3b993 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/DependencyMissingView.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/DependencyMissingView.java @@ -97,9 +97,4 @@ public void widgetSelected(final SelectionEvent e) { private String getDependency() { return PluginUtils.getPlatform() == PluginPlatform.WINDOWS ? "WebView2" : "WebKit"; } - - @Override - public boolean canDisplay() { - return true; - } } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/LspStartUpFailedView.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/LspStartUpFailedView.java index db0e82b2..f5a1a6e7 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/LspStartUpFailedView.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/LspStartUpFailedView.java @@ -2,43 +2,64 @@ // SPDX-License-Identifier: Apache-2.0 package software.aws.toolkits.eclipse.amazonq.views; -import java.util.concurrent.CompletableFuture; -import software.aws.toolkits.eclipse.amazonq.lsp.manager.LspStatusManager; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; -public final class LspStartUpFailedView extends BaseView { +public final class LspStartUpFailedView extends BaseAmazonQView { public static final String ID = "software.aws.toolkits.eclipse.amazonq.views.LspStartUpFailedView"; private static final String ICON_PATH = "icons/AmazonQ64.png"; private static final String HEADER_LABEL = "Language Server failed to start."; + + // add logic to base detail_message on error code returned from exception private static final String DETAIL_MESSAGE = "Restart Eclipse or review error logs for troubleshooting"; + private Image icon; + private Composite container; - /* TODO: After refactor of LSP error handling is completed, - * add logic to base detail_message on error code returned from exception - * */ - @Override - protected String getIconPath() { - return ICON_PATH; + public LspStartUpFailedView() { + // } @Override - protected String getHeaderLabel() { - return HEADER_LABEL; - } + public Composite setupView(final Composite parentComposite) { + container = new Composite(parentComposite, SWT.NONE); + GridLayout layout = new GridLayout(1, false); + layout.marginWidth = 20; + layout.marginHeight = 10; + container.setLayout(layout); - @Override - protected String getDetailMessage() { - return DETAIL_MESSAGE; - } + Label iconLabel = new Label(container, SWT.NONE); + icon = loadImage(ICON_PATH); + if (icon != null) { + iconLabel.setImage(icon); + iconLabel.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, false)); - @Override - protected void showAlternateView() { - ViewVisibilityManager.showDefaultView("restart"); + iconLabel.addDisposeListener(e -> { + if (icon != null && !icon.isDisposed()) { + icon.dispose(); + } + }); + } + + Label headerLabel = new Label(container, SWT.CENTER | SWT.WRAP); + headerLabel.setText(HEADER_LABEL); + headerLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + + Label detailLabel = new Label(container, SWT.CENTER | SWT.WRAP); + detailLabel.setText(DETAIL_MESSAGE); + detailLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + + return container; } @Override - protected CompletableFuture isViewDisplayable() { - return CompletableFuture.completedFuture(LspStatusManager.getInstance().lspFailed()); + public void dispose() { + //default implementation } } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ReauthenticateView.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ReauthenticateView.java index 5584f765..0c4ab96f 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ReauthenticateView.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ReauthenticateView.java @@ -136,12 +136,6 @@ protected void updateButtonStyle(final Button button) { }); } - @Override - public boolean canDisplay() { - return true; - } - - @Override public void dispose() { authStateSubscription.dispose(); From 24e1b96faa62ad4a29fa981c8465712607adb658 Mon Sep 17 00:00:00 2001 From: Nicolas Borges Date: Wed, 29 Jan 2025 10:17:30 -0500 Subject: [PATCH 12/13] add missing header styling to base views --- .../eclipse/amazonq/views/AmazonQViewContainer.java | 3 +-- .../eclipse/amazonq/views/BaseAmazonQView.java | 10 ++++++++++ .../eclipse/amazonq/views/ChatAssetMissingView.java | 8 ++++++++ .../eclipse/amazonq/views/LspStartUpFailedView.java | 8 ++++++++ 4 files changed, 27 insertions(+), 2 deletions(-) diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQViewContainer.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQViewContainer.java index a6ea0dfc..d62eadad 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQViewContainer.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQViewContainer.java @@ -30,7 +30,6 @@ public final class AmazonQViewContainer extends ViewPart implements EventObserve public AmazonQViewContainer() { viewChangeEventSubscription = Activator.getEventBroker().subscribe(ViewId.class, this); - initializeViews(null); } /* Router should be initialized, then init view container @@ -61,7 +60,7 @@ public void initializeViews(final ViewId currentActiveViewId) { //default view passed in from router //possible we'll use chatView as default? - activeView = views.get(ViewId.LSP_STARTUP_FAILED_VIEW); + activeView = views.get(currentActiveViewId); } public void createPartControl(final Composite parent) { diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/BaseAmazonQView.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/BaseAmazonQView.java index 2a8ad679..e8bbb999 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/BaseAmazonQView.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/BaseAmazonQView.java @@ -6,6 +6,8 @@ import java.io.IOException; import java.net.URL; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.FontData; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; @@ -29,4 +31,12 @@ protected Image loadImage(final String imagePath) { } return loadedImage; } + protected Font magnifyFontSize(final Composite parentComposite, final Font originalFont, final int fontSize) { + FontData[] fontData = originalFont.getFontData(); + for (int i = 0; i < fontData.length; i++) { + fontData[i].setHeight(fontSize); + } + Font magnifiedFont = new Font(parentComposite.getDisplay(), fontData); + return magnifiedFont; + } } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ChatAssetMissingView.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ChatAssetMissingView.java index 878c3060..e6a04380 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ChatAssetMissingView.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ChatAssetMissingView.java @@ -49,6 +49,14 @@ public Composite setupView(final Composite parentComposite) { Label headerLabel = new Label(container, SWT.CENTER | SWT.WRAP); headerLabel.setText(HEADER_LABEL); headerLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + var font = magnifyFontSize(parentComposite, parentComposite.getFont(), 18); + headerLabel.setFont(font); + + headerLabel.addDisposeListener(e -> { + if (font != null && !font.isDisposed()) { + font.dispose(); + } + }); Label detailLabel = new Label(container, SWT.CENTER | SWT.WRAP); detailLabel.setText(DETAIL_MESSAGE); diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/LspStartUpFailedView.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/LspStartUpFailedView.java index f5a1a6e7..f384436f 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/LspStartUpFailedView.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/LspStartUpFailedView.java @@ -49,6 +49,14 @@ public Composite setupView(final Composite parentComposite) { Label headerLabel = new Label(container, SWT.CENTER | SWT.WRAP); headerLabel.setText(HEADER_LABEL); headerLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + var font = magnifyFontSize(parentComposite, parentComposite.getFont(), 18); + headerLabel.setFont(font); + + headerLabel.addDisposeListener(e -> { + if (font != null && !font.isDisposed()) { + font.dispose(); + } + }); Label detailLabel = new Label(container, SWT.CENTER | SWT.WRAP); detailLabel.setText(DETAIL_MESSAGE); From d18421e04a1f1f81a5b417e1b2b586b7e41c9e8f Mon Sep 17 00:00:00 2001 From: Nicolas Borges Date: Wed, 5 Feb 2025 15:35:01 -0500 Subject: [PATCH 13/13] removed old viewId class and update view rendering logic --- .../amazonq/views/AmazonQViewContainer.java | 45 +++++++++---------- .../eclipse/amazonq/views/router/ViewId.java | 9 ---- 2 files changed, 20 insertions(+), 34 deletions(-) delete mode 100644 plugin/src/software/aws/toolkits/eclipse/amazonq/views/router/ViewId.java diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQViewContainer.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQViewContainer.java index d62eadad..cbd8e0d6 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQViewContainer.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQViewContainer.java @@ -13,23 +13,21 @@ import org.eclipse.swt.widgets.Display; import org.eclipse.ui.part.ViewPart; -import io.reactivex.rxjava3.disposables.Disposable; import software.aws.toolkits.eclipse.amazonq.broker.api.EventObserver; import software.aws.toolkits.eclipse.amazonq.plugin.Activator; import software.aws.toolkits.eclipse.amazonq.views.actions.AmazonQStaticActions; -import software.aws.toolkits.eclipse.amazonq.views.router.ViewId; +import software.aws.toolkits.eclipse.amazonq.views.router.AmazonQViewType; -public final class AmazonQViewContainer extends ViewPart implements EventObserver { +public final class AmazonQViewContainer extends ViewPart implements EventObserver { private Composite parentComposite; private StackLayout layout; - private Map views; - private ViewId activeId; + private Map views; + private AmazonQViewType activeId; private BaseAmazonQView activeView; - private Disposable viewChangeEventSubscription; public AmazonQViewContainer() { - viewChangeEventSubscription = Activator.getEventBroker().subscribe(ViewId.class, this); + Activator.getEventBroker().subscribe(AmazonQViewType.class, this); } /* Router should be initialized, then init view container @@ -44,7 +42,7 @@ public AmazonQViewContainer() { * 2. viewContainer.init(currentView) */ - public void initializeViews(final ViewId currentActiveViewId) { + public void initializeViews(final AmazonQViewType currentActiveViewId) { //init map containing all views var dependencyMissingView = new DependencyMissingView(); @@ -52,15 +50,15 @@ public void initializeViews(final ViewId currentActiveViewId) { var reAuthView = new ReauthenticateView(); var lspFailedView = new LspStartUpFailedView(); views = Map.of( - ViewId.CHAT_ASSET_MISSING_VIEW, chatAssetMissingView, - ViewId.DEPENDENCY_MISSING_VIEW, dependencyMissingView, - ViewId.RE_AUTHENTICATE_VIEW, reAuthView, - ViewId.LSP_STARTUP_FAILED_VIEW, lspFailedView + AmazonQViewType.CHAT_ASSET_MISSING_VIEW, chatAssetMissingView, + AmazonQViewType.DEPENDENCY_MISSING_VIEW, dependencyMissingView, + AmazonQViewType.RE_AUTHENTICATE_VIEW, reAuthView, + AmazonQViewType.LSP_STARTUP_FAILED_VIEW, lspFailedView ); //default view passed in from router //possible we'll use chatView as default? - activeView = views.get(currentActiveViewId); + activeId = currentActiveViewId; } public void createPartControl(final Composite parent) { @@ -77,7 +75,7 @@ public void createPartControl(final Composite parent) { parent.setLayout(gridLayout); setupStaticMenuActions(); - updateChildView(activeView, ViewId.RE_AUTHENTICATE_VIEW); + updateChildView(); } /* change methodology for setupMenuActions -- move outside of viewContainer? @@ -89,9 +87,10 @@ private void setupStaticMenuActions() { new AmazonQStaticActions(getViewSite()); } - private void updateChildView(final BaseAmazonQView newView, final ViewId newViewId) { + private void updateChildView() { Display.getDefault().asyncExec(() -> { - + BaseAmazonQView newView = views.get(activeId); + if (activeView != null) { activeView.dispose(); if (layout.topControl != null) { @@ -107,21 +106,18 @@ private void updateChildView(final BaseAmazonQView newView, final ViewId newView parentComposite.layout(true, true); activeView = newView; - activeId = newViewId; }); } @Override - public void onEvent(final ViewId newViewId) { - if (activeId != null && activeId.equals(newViewId)) { + public void onEvent(final AmazonQViewType newViewId) { + if (newViewId.equals(activeId) || !views.containsKey(newViewId)) { return; } + activeId = newViewId; - if (views.containsKey(newViewId)) { - BaseAmazonQView newView = views.get(newViewId); - if (!parentComposite.isDisposed()) { - updateChildView(newView, newViewId); - } + if (!parentComposite.isDisposed()) { + updateChildView(); } } @@ -133,7 +129,6 @@ public void setFocus() { @Override public void dispose() { - viewChangeEventSubscription.dispose(); if (activeView != null) { activeView.dispose(); } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/router/ViewId.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/router/ViewId.java deleted file mode 100644 index 8ff6272e..00000000 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/router/ViewId.java +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -package software.aws.toolkits.eclipse.amazonq.views.router; - -public enum ViewId { - TOOLKIT_LOGIN_VIEW, CHAT_VIEW, DEPENDENCY_MISSING_VIEW, RE_AUTHENTICATE_VIEW, CHAT_ASSET_MISSING_VIEW, - CODE_REFERENCE_VIEW, LSP_STARTUP_FAILED_VIEW, LSP_INITIALIZING_VIEW -}