From 4c62409bcb8a3f0540c0bf5eb46188b756049ac8 Mon Sep 17 00:00:00 2001 From: Nicolas Date: Thu, 21 Nov 2024 14:22:01 -0500 Subject: [PATCH 1/3] Move eclipse executeCommand metric into Toolkit (#254) --- .../definitions/commonDefinitions.json | 42 +++++++++---------- .../AbstractQChatEditorActionsHandler.java | 10 ++--- .../telemetry/EclipseTelemetryProvider.java | 32 -------------- .../telemetry/ToolkitTelemetryProvider.java | 15 ++++++- 4 files changed, 40 insertions(+), 59 deletions(-) delete mode 100644 plugin/src/software/aws/toolkits/eclipse/amazonq/telemetry/EclipseTelemetryProvider.java diff --git a/plugin/codegen-resources/definitions/commonDefinitions.json b/plugin/codegen-resources/definitions/commonDefinitions.json index d49cacb5..47364dcf 100644 --- a/plugin/codegen-resources/definitions/commonDefinitions.json +++ b/plugin/codegen-resources/definitions/commonDefinitions.json @@ -6223,26 +6223,6 @@ } ] }, - { - "name": "eclipse_executeCommand", - "description": "Emitted whenever a registered Eclipse command is executed", - "passive": true, - "metadata": [ - { - "type": "command" - }, - { - "type": "duration" - }, - { - "type": "result" - }, - { - "type": "reason", - "required": false - } - ] - }, { "name": "rds_createConnectionConfiguration", "description": "Called when creating a new database connection configuration to for a RDS database. In Datagrip we do not get this infromation if it is created directly, so this is only counts actions.", @@ -6968,6 +6948,26 @@ ], "passive": true }, + { + "name": "toolkit_execute", + "description": "Emitted whenever a registered command is executed", + "passive": true, + "metadata": [ + { + "type": "command" + }, + { + "type": "duration" + }, + { + "type": "result" + }, + { + "type": "reason", + "required": false + } + ] + }, { "name": "toolkit_featureState", "description": "Represents the current enabled state of a feature. Used to track user journey through a feature. Emitted after feature-specific operations of interest in the Toolkit.", @@ -7221,4 +7221,4 @@ ] } ] -} \ No newline at end of file +} 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 5e445061..8a8ff233 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/handlers/AbstractQChatEditorActionsHandler.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/handlers/AbstractQChatEditorActionsHandler.java @@ -16,7 +16,7 @@ import software.aws.toolkits.eclipse.amazonq.chat.models.SendToPromptParams; import software.aws.toolkits.eclipse.amazonq.chat.models.TriggerType; import software.aws.toolkits.eclipse.amazonq.plugin.Activator; -import software.aws.toolkits.eclipse.amazonq.telemetry.EclipseTelemetryProvider; +import software.aws.toolkits.eclipse.amazonq.telemetry.ToolkitTelemetryProvider; import software.aws.toolkits.eclipse.amazonq.util.QEclipseEditorUtils; import software.aws.toolkits.eclipse.amazonq.views.ViewVisibilityManager; import software.aws.toolkits.telemetry.TelemetryDefinitions.Result; @@ -39,7 +39,7 @@ protected final void executeGenericCommand(final String genericCommandVerb) { getSelectedText().ifPresentOrElse( selection -> { sendGenericCommand(selection, genericCommandVerb); - emitExecuteCommand(genericCommandVerb, start, Result.SUCCEEDED, ""); + emitExecuteCommand(genericCommandVerb, start, Result.SUCCEEDED, null); }, () -> { Activator.getLogger().info("No text was retrieved when fetching selected text"); @@ -59,7 +59,7 @@ protected final void executeSendToPromptCommand() { getSelectedText().ifPresentOrElse( selection -> { sendToPromptCommand(selection); - emitExecuteCommand("sendToPrompt", start, Result.SUCCEEDED, ""); + emitExecuteCommand("sendToPrompt", start, Result.SUCCEEDED, null); }, () -> { Activator.getLogger().info("No text was retrieved when fetching selected text"); @@ -112,8 +112,8 @@ public void run() { } private void emitExecuteCommand(final String command, final Instant start, final Result result, final String reason) { double duration = Duration.between(start, Instant.now()).toMillis(); - var params = new EclipseTelemetryProvider.Params(command, duration, result, reason); - EclipseTelemetryProvider.emitExecuteCommandMetric(params); + var params = new ToolkitTelemetryProvider.ExecuteParams(command, duration, result, reason); + ToolkitTelemetryProvider.emitExecuteCommandMetric(params); return; } } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/telemetry/EclipseTelemetryProvider.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/telemetry/EclipseTelemetryProvider.java deleted file mode 100644 index 0b2f5048..00000000 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/telemetry/EclipseTelemetryProvider.java +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -package software.aws.toolkits.eclipse.amazonq.telemetry; - -import java.time.Instant; -import software.aws.toolkits.eclipse.amazonq.plugin.Activator; -import software.aws.toolkits.telemetry.EclipseTelemetry; -import software.aws.toolkits.telemetry.TelemetryDefinitions.Result; - -public final class EclipseTelemetryProvider { - - private EclipseTelemetryProvider() { - //prevent instantiation - } - - public static void emitExecuteCommandMetric(final Params params) { - var metadata = EclipseTelemetry.ExecuteCommandEvent() - .command(params.command()) - .duration(params.duration()) - .result(params.result()) - .reason(params.reason()) - .passive(false) - .createTime(Instant.now()) - .value(1.0) - .build(); - Activator.getTelemetryService().emitMetric(metadata); - } - - public record Params(String command, double duration, Result result, String reason) { }; - -} diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/telemetry/ToolkitTelemetryProvider.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/telemetry/ToolkitTelemetryProvider.java index 8fa32d83..9dbcbcea 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/telemetry/ToolkitTelemetryProvider.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/telemetry/ToolkitTelemetryProvider.java @@ -15,6 +15,19 @@ private ToolkitTelemetryProvider() { //prevent instantiation } + public static void emitExecuteCommandMetric(final ExecuteParams params) { + var metadata = ToolkitTelemetry.ExecuteEvent() + .command(params.command()) + .duration(params.duration()) + .result(params.result()) + .reason(params.reason()) + .passive(false) + .createTime(Instant.now()) + .value(1.0) + .build(); + Activator.getTelemetryService().emitMetric(metadata); + } + public static void emitOpenModuleEventMetric(final String module, final String source, final String failureReason) { Result result = Result.SUCCEEDED; boolean isPassive = (!source.equals("ellipsesMenu") && !source.equals("statusBar") && !source.equals("shortcut")); @@ -57,5 +70,5 @@ private static String mapModuleId(final String viewId) { return page; } } - + public record ExecuteParams(String command, double duration, Result result, String reason) { }; } From ef661341842f6a6bca57c31f7a75fbd3caad59de Mon Sep 17 00:00:00 2001 From: Felix Ding Date: Thu, 21 Nov 2024 12:35:05 -0800 Subject: [PATCH 2/3] Derives which line to unset vertical indent during typing mismatch (#250) --- .../eclipse/amazonq/util/QInlineInputListener.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/util/QInlineInputListener.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/util/QInlineInputListener.java index ada1f0c1..b53276fe 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/util/QInlineInputListener.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/util/QInlineInputListener.java @@ -188,8 +188,7 @@ public void beforeRemoval() { currentOffset); int lineNumber = doc.getLineOfOffset(expandedCurrentOffset); int startLineOffset = doc.getLineOffset(lineNumber); - int invocationOffset = session.getInvocationOffset(); - int curLineInDoc = widget.getLineAtOffset(invocationOffset); + int curLineInDoc = widget.getLineAtOffset(currentOffset); int lineIdx = expandedCurrentOffset - startLineOffset; String contentInLine = widget.getLine(curLineInDoc) + widget.getLineDelimiter(); String currentRightCtx = "\n"; @@ -323,9 +322,13 @@ public void documentChanged(final DocumentEvent event) { boolean isOutOfBounds = distanceTraversed + input.length() >= currentSuggestion.length() || distanceTraversed < 0; if (isOutOfBounds || !isInputAMatch(currentSuggestion, distanceTraversed, input)) { - distanceTraversed++; - session.transitionToDecisionMade(); + distanceTraversed += input.length(); event.getDocument().removeDocumentListener(this); + StyledText widget = session.getViewer().getTextWidget(); + int caretLine = widget.getLineAtOffset(widget.getCaretOffset()); + int linesOfInput = input.split(widget.getLineDelimiter()).length; + int lineToUnsetIndent = caretLine + linesOfInput; + session.transitionToDecisionMade(lineToUnsetIndent); Display.getCurrent().asyncExec(() -> { if (session.isActive()) { session.end(); From 028e27b3479875eee17dcd032d6e5addab5b30e7 Mon Sep 17 00:00:00 2001 From: Jonathan Breedlove Date: Thu, 21 Nov 2024 12:36:30 -0800 Subject: [PATCH 3/3] Add configuration for showing code references (#255) --- .../amazonq/lsp/AmazonQLspClientImpl.java | 4 ++ .../amazonq/lsp/auth/DefaultLoginService.java | 3 ++ .../AmazonQPreferenceInitializer.java | 1 + .../preferences/AmazonQPreferencePage.java | 50 ++++++++++++++++--- .../eclipse/amazonq/util/Constants.java | 1 + 5 files changed, 51 insertions(+), 8 deletions(-) diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspClientImpl.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspClientImpl.java index 6bca6ee9..183d131b 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspClientImpl.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspClientImpl.java @@ -24,6 +24,7 @@ import software.amazon.awssdk.services.toolkittelemetry.model.Sentiment; import software.aws.toolkits.eclipse.amazonq.chat.ChatCommunicationManager; import software.aws.toolkits.eclipse.amazonq.lsp.auth.model.AuthState; +import software.aws.toolkits.eclipse.amazonq.lsp.auth.model.LoginType; import software.aws.toolkits.eclipse.amazonq.lsp.auth.model.SsoTokenChangedKind; import software.aws.toolkits.eclipse.amazonq.lsp.auth.model.SsoTokenChangedParams; import software.aws.toolkits.eclipse.amazonq.lsp.model.ConnectionMetadata; @@ -75,7 +76,10 @@ public final CompletableFuture> configuration(final ConfigurationPa } else if (item.getSection().equals(Constants.LSP_CW_CONFIGURATION_KEY)) { Map cwConfig = new HashMap<>(); boolean shareContentSetting = Activator.getDefault().getPreferenceStore().getBoolean(AmazonQPreferencePage.Q_DATA_SHARING); + boolean referencesEnabled = Activator.getDefault().getPreferenceStore().getBoolean(AmazonQPreferencePage.CODE_REFERENCE_OPT_IN) + && Activator.getLoginService().getAuthState().loginType().equals(LoginType.BUILDER_ID); cwConfig.put(Constants.LSP_CW_OPT_OUT_KEY, shareContentSetting); + cwConfig.put(Constants.LSP_CODE_REFERENCES_OPT_OUT_KEY, referencesEnabled); output.add(cwConfig); } }); diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/auth/DefaultLoginService.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/auth/DefaultLoginService.java index d0b5b728..52f78b27 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/auth/DefaultLoginService.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/auth/DefaultLoginService.java @@ -9,6 +9,7 @@ import software.aws.toolkits.eclipse.amazonq.configuration.PluginStore; +import software.aws.toolkits.eclipse.amazonq.customization.CustomizationUtil; import software.aws.toolkits.eclipse.amazonq.exception.AmazonQPluginException; import software.aws.toolkits.eclipse.amazonq.lsp.auth.model.AuthState; import software.aws.toolkits.eclipse.amazonq.lsp.auth.model.InvalidateSsoTokenParams; @@ -18,6 +19,7 @@ import software.aws.toolkits.eclipse.amazonq.lsp.encryption.LspEncryptionManager; import software.aws.toolkits.eclipse.amazonq.providers.LspProvider; import software.aws.toolkits.eclipse.amazonq.util.AuthUtil; +import software.aws.toolkits.eclipse.amazonq.util.ThreadingUtils; import software.aws.toolkits.eclipse.amazonq.plugin.Activator; /** @@ -165,6 +167,7 @@ CompletableFuture processLogin(final LoginType loginType, final LoginParam .thenRun(() -> { authStateManager.toLoggedIn(loginType, loginParams, ssoTokenId.get()); Activator.getLogger().info("Successfully logged in"); + ThreadingUtils.executeAsyncTask(() -> CustomizationUtil.triggerChangeConfigurationNotification()); }) .exceptionally(throwable -> { throw new AmazonQPluginException("Failed to process log in", throwable); diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/preferences/AmazonQPreferenceInitializer.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/preferences/AmazonQPreferenceInitializer.java index f5bdb1dc..00aed9f5 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/preferences/AmazonQPreferenceInitializer.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/preferences/AmazonQPreferenceInitializer.java @@ -17,6 +17,7 @@ public class AmazonQPreferenceInitializer extends AbstractPreferenceInitializer @Override public final void initializeDefaultPreferences() { IPreferenceStore store = Activator.getDefault().getPreferenceStore(); + store.setDefault(AmazonQPreferencePage.CODE_REFERENCE_OPT_IN, true); store.setDefault(AmazonQPreferencePage.TELEMETRY_OPT_IN, true); store.setDefault(AmazonQPreferencePage.Q_DATA_SHARING, true); store.addPropertyChangeListener(event -> { diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/preferences/AmazonQPreferencePage.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/preferences/AmazonQPreferencePage.java index 97caaf71..b7ed3295 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/preferences/AmazonQPreferencePage.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/preferences/AmazonQPreferencePage.java @@ -18,6 +18,7 @@ import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbenchPreferencePage; +import software.aws.toolkits.eclipse.amazonq.lsp.auth.model.LoginType; import software.aws.toolkits.eclipse.amazonq.lsp.model.GetConfigurationFromServerParams; import software.aws.toolkits.eclipse.amazonq.plugin.Activator; import software.aws.toolkits.eclipse.amazonq.telemetry.AwsTelemetryProvider; @@ -26,7 +27,8 @@ public class AmazonQPreferencePage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage { public static final String PREFERENCE_STORE_ID = "software.aws.toolkits.eclipse.preferences"; - public static final String TELEMETRY_OPT_IN = "telemtryOptIn"; + public static final String CODE_REFERENCE_OPT_IN = "codeReferenceOptIn"; + public static final String TELEMETRY_OPT_IN = "telemetryOptIn"; public static final String Q_DATA_SHARING = "qDataSharing"; private boolean isTelemetryOptInChecked; @@ -49,7 +51,9 @@ public final void init(final IWorkbench workbench) { @Override protected final void createFieldEditors() { createHorizontalSeparator(); - createDataSharingLabel(); + createHeading("Inline Suggestions"); + createCodeReferenceOptInField(); + createHeading("Data Sharing"); createTelemetryOptInField(); createHorizontalSeparator(); createQDataSharingField(); @@ -64,14 +68,44 @@ private void createHorizontalSeparator() { new Label(getFieldEditorParent(), SWT.HORIZONTAL); } - private void createDataSharingLabel() { + private void createHeading(final String text) { Label dataSharing = new Label(getFieldEditorParent(), SWT.NONE); dataSharing.setFont(JFaceResources.getFontRegistry().getBold(JFaceResources.HEADER_FONT)); - dataSharing.setText("Data Sharing"); + dataSharing.setText(text); dataSharing.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); new Label(getFieldEditorParent(), SWT.HORIZONTAL | SWT.SEPARATOR).setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); } + private void createCodeReferenceOptInField() { + Composite codeReferenceOptInComposite = new Composite(getFieldEditorParent(), SWT.NONE); + codeReferenceOptInComposite.setLayout(new GridLayout(2, false)); + GridData telemetryOptInCompositeData = new GridData(SWT.FILL, SWT.CENTER, true, false); + telemetryOptInCompositeData.horizontalIndent = 20; + codeReferenceOptInComposite.setLayoutData(telemetryOptInCompositeData); + + BooleanFieldEditor codeReferenceOptIn = new BooleanFieldEditor(CODE_REFERENCE_OPT_IN, + "Show inline code suggestions with code references", codeReferenceOptInComposite); + addField(codeReferenceOptIn); + + if (Activator.getLoginService().getAuthState().loginType().equals(LoginType.IAM_IDENTITY_CENTER)) { + codeReferenceOptIn.setEnabled(false, codeReferenceOptInComposite); + } + + Link codeReferenceLink = createLink(""" + Amazon Q creates a code reference when you insert a code suggestion from Amazon Q that is similar to training data.\ + \nWhen unchecked, Amazon Q will not show code suggestions that have code references. If you authenticate through IAM\ + \nIdentity Center, this setting is controlled by your Amazon Q administrator. \ + \nLearn more + """, 20, codeReferenceOptInComposite); + codeReferenceLink.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(final SelectionEvent event) { + UiTelemetryProvider.emitClickEventMetric("preferences_codeReferences"); + PluginUtils.openWebpage(event.text); + } + }); + } + private void createTelemetryOptInField() { Composite telemetryOptInComposite = new Composite(getFieldEditorParent(), SWT.NONE); telemetryOptInComposite.setLayout(new GridLayout(2, false)); @@ -104,10 +138,10 @@ private void createQDataSharingField() { addField(qDataSharing); Link dataSharingLink = createLink(""" - When checked, your content processed by Amazon Q may be used for service improvement (except for content processed by the \ - Amazon Q Developer Pro tier).\nUnchecking this box will cause AWS to delete any of your content used for that purpose. The \ - information used to provide the Amazon Q service to you will not be affected.\nSee the \ - Service Terms for more detail. + When checked, your content processed by Amazon Q may be used for service improvement (except for content processed\ + \nby the Amazon Q Developer Pro tier). Unchecking this box will cause AWS to delete any of your content used for that\ + \npurpose. The information used to provide the Amazon Q service to you will not be affected.\ + \nSee the Service Terms for more detail. """, 20, qDataSharingComposite); dataSharingLink.addSelectionListener(new SelectionAdapter() { @Override diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/util/Constants.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/util/Constants.java index 08a97250..ebe0b905 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/util/Constants.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/util/Constants.java @@ -16,6 +16,7 @@ private Constants() { public static final String LSP_Q_CONFIGURATION_KEY = "aws.q"; public static final String LSP_CW_CONFIGURATION_KEY = "aws.codeWhisperer"; public static final String LSP_CW_OPT_OUT_KEY = "shareCodeWhispererContentWithAWS"; + public static final String LSP_CODE_REFERENCES_OPT_OUT_KEY = "includeSuggestionsWithCodeReferences"; public static final String IDE_CUSTOMIZATION_NOTIFICATION_TITLE = "Amazon Q Customization"; public static final String IDE_CUSTOMIZATION_NOTIFICATION_BODY_TEMPLATE = "Amazon Q inline suggestions are now coming from the %s"; public static final String DEFAULT_Q_FOUNDATION_DISPLAY_NAME = "Amazon Q foundation (Default)";