diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/settings/CodeWhispererConfigurable.kt b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/settings/CodeWhispererConfigurable.kt index 94bcc93f5ca..91cff463cd0 100644 --- a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/settings/CodeWhispererConfigurable.kt +++ b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/settings/CodeWhispererConfigurable.kt @@ -5,13 +5,16 @@ package software.aws.toolkits.jetbrains.services.codewhisperer.settings import com.intellij.icons.AllIcons import com.intellij.ide.DataManager +import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory import com.intellij.openapi.options.BoundConfigurable import com.intellij.openapi.options.Configurable import com.intellij.openapi.options.SearchableConfigurable import com.intellij.openapi.options.ex.Settings import com.intellij.openapi.project.Project +import com.intellij.openapi.ui.emptyText import com.intellij.ui.components.ActionLink import com.intellij.ui.components.fields.ExpandableTextField +import com.intellij.ui.dsl.builder.Align import com.intellij.ui.dsl.builder.bindIntText import com.intellij.ui.dsl.builder.bindSelected import com.intellij.ui.dsl.builder.bindText @@ -24,6 +27,7 @@ import software.aws.toolkits.jetbrains.services.codewhisperer.credentials.CodeWh import software.aws.toolkits.jetbrains.services.codewhisperer.explorer.CodeWhispererExplorerActionManager import software.aws.toolkits.jetbrains.services.codewhisperer.explorer.isCodeWhispererEnabled import software.aws.toolkits.jetbrains.settings.CodeWhispererSettings +import software.aws.toolkits.jetbrains.settings.LspSettings import software.aws.toolkits.resources.message import java.awt.Font import java.util.concurrent.TimeUnit @@ -61,6 +65,24 @@ class CodeWhispererConfigurable(private val project: Project) : } } + group(message("amazonqFeatureDev.placeholder.lsp")) { + row(message("amazonqFeatureDev.placeholder.select_lsp_artifact")) { + val fileChooserDescriptor = FileChooserDescriptorFactory.createSingleFileDescriptor() + fileChooserDescriptor.isForcedToUseIdeaFileChooser = true + + textFieldWithBrowseButton(fileChooserDescriptor = fileChooserDescriptor) + .bindText( + { LspSettings.getInstance().getArtifactPath() }, + { LspSettings.getInstance().setArtifactPath(it.takeIf { v -> v.isNotBlank() }) } + ) + .applyToComponent { + emptyText.text = "Choose a file to upload" + } + .resizableColumn() + .align(Align.FILL) + } + } + group(message("aws.settings.codewhisperer.group.inline_suggestions")) { row { checkBox(message("aws.settings.codewhisperer.include_code_with_reference")).apply { diff --git a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/settings/LspSettings.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/settings/LspSettings.kt new file mode 100644 index 00000000000..8ac9d527651 --- /dev/null +++ b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/settings/LspSettings.kt @@ -0,0 +1,44 @@ +// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.settings + +import com.intellij.openapi.components.BaseState +import com.intellij.openapi.components.PersistentStateComponent +import com.intellij.openapi.components.RoamingType +import com.intellij.openapi.components.Service +import com.intellij.openapi.components.State +import com.intellij.openapi.components.Storage +import com.intellij.openapi.components.service +import com.intellij.util.xmlb.annotations.Property + +@Service +@State(name = "lspSettings", storages = [Storage("aws.xml", roamingType = RoamingType.DISABLED)]) +class LspSettings : PersistentStateComponent { + private var state = LspConfiguration() + + override fun getState(): LspConfiguration = state + + override fun loadState(state: LspConfiguration) { + this.state = state + } + + fun getArtifactPath() = state.artifactPath + + fun setArtifactPath(artifactPath: String?) { + if (artifactPath == null) { + state.artifactPath = "" + } else { + state.artifactPath = artifactPath + } + } + + companion object { + fun getInstance(): LspSettings = service() + } +} + +class LspConfiguration : BaseState() { + @get:Property + var artifactPath: String = "" +} diff --git a/plugins/amazonq/shared/jetbrains-community/tst/software/aws/toolkits/jetbrains/settings/LspSettingsTest.kt b/plugins/amazonq/shared/jetbrains-community/tst/software/aws/toolkits/jetbrains/settings/LspSettingsTest.kt new file mode 100644 index 00000000000..3d1e240beb7 --- /dev/null +++ b/plugins/amazonq/shared/jetbrains-community/tst/software/aws/toolkits/jetbrains/settings/LspSettingsTest.kt @@ -0,0 +1,85 @@ +// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.settings + +import com.intellij.util.xmlb.XmlSerializer +import org.assertj.core.api.Assertions.assertThat +import org.jdom.output.XMLOutputter +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import software.aws.toolkits.jetbrains.utils.xmlElement + +class LspSettingsTest { + private lateinit var lspSettings: LspSettings + + @BeforeEach + fun setUp() { + lspSettings = LspSettings() + lspSettings.loadState(LspConfiguration()) + } + + @Test + fun `artifact path is empty by default`() { + assertThat(lspSettings.getArtifactPath()).isEmpty() + } + + @Test + fun `artifact path can be set`() { + lspSettings.setArtifactPath("test\\lsp.js") + assertThat(lspSettings.getArtifactPath()).isNotEmpty() + assertThat(lspSettings.getArtifactPath()).isEqualTo("test\\lsp.js") + } + + @Test + fun `artifact path cannot be null`() { + lspSettings.setArtifactPath(null) + assertThat(lspSettings.getArtifactPath()).isEmpty() + } + + @Test + fun `serialize settings to ensure backwards compatibility`() { + val element = xmlElement( + """ + + + """.trimIndent() + ) + lspSettings.setArtifactPath("temp\\lsp.js") + + XmlSerializer.serializeInto(lspSettings.state, element) + + val actual = XMLOutputter().outputString(element) + + val expected = "\n" + + "" + + assertThat(actual).isEqualTo(expected) + } + + @Test + fun `deserialize empty settings to ensure backwards compatibility`() { + val element = xmlElement( + """ + + + """ + ) + val actual = XmlSerializer.deserialize(element, LspConfiguration::class.java) + assertThat(actual.artifactPath).isEmpty() + } + + @Test + fun `deserialize existing settings to ensure backwards compatibility`() { + val element = xmlElement( + """ + + + """.trimIndent() + ) + val actual = XmlSerializer.deserialize(element, LspConfiguration::class.java) + assertThat(actual.artifactPath).isNotEmpty() + assertThat(actual.artifactPath).isEqualTo("temp\\lsp.js") + } +} diff --git a/plugins/core/resources/resources/software/aws/toolkits/resources/MessagesBundle.properties b/plugins/core/resources/resources/software/aws/toolkits/resources/MessagesBundle.properties index 2d756cb51b5..6fd97883b49 100644 --- a/plugins/core/resources/resources/software/aws/toolkits/resources/MessagesBundle.properties +++ b/plugins/core/resources/resources/software/aws/toolkits/resources/MessagesBundle.properties @@ -143,8 +143,10 @@ amazonqFeatureDev.placeholder.closed_session=Open a new chat tab to continue amazonqFeatureDev.placeholder.context_gathering_complete=Gathering context... amazonqFeatureDev.placeholder.downloading_and_extracting_lsp_artifacts=Downloading and Extracting Lsp Artifacts... amazonqFeatureDev.placeholder.generating_code=Generating code... +amazonqFeatureDev.placeholder.lsp=LSP amazonqFeatureDev.placeholder.new_plan=Describe your task or issue in as much detail as possible amazonqFeatureDev.placeholder.provide_code_feedback=Provide feedback or comments +amazonqFeatureDev.placeholder.select_lsp_artifact=Select LSP Artifact amazonqFeatureDev.placeholder.write_new_prompt=Write a new prompt apprunner.action.configure=Configure Service apprunner.action.create.service=Create Service...