Skip to content

Commit 3263ac9

Browse files
committed
class visibility
2 parents 7b77a6f + 84b0f7d commit 3263ac9

File tree

13 files changed

+204
-16
lines changed

13 files changed

+204
-16
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type" : "feature",
3+
"description" : "/review: Code reviews are now created with additional workspace context to enable grouping of related scans in the backend"
4+
}

CONTRIBUTING.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ reported the issue. Please try to include as much information as you can. Detail
3131
brew install dotnet@6
3232
```
3333
* If Gradle cannot find `dotnet`, run `./gradlew --stop` and `./gradlew projects` to reload the daemon. Note that this should be done in your terminal as invoking Gradle through the IDE will use the IDE's cached PATH.
34+
* It is recommended to [launch your IDE from the terminal](https://www.jetbrains.com/help/idea/working-with-the-ide-features-from-command-line.html) due to a known issue with Gradle/Java 21 where the Gradle daemon does not respect your PATH variable when the IDE is started from the desktop/Toolbox.
3435
3536
### Instructions
3637

plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/codescan/CodeWhispererCodeScanSession.kt

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,21 @@ import com.fasterxml.jackson.databind.DeserializationFeature
77
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
88
import com.fasterxml.jackson.module.kotlin.readValue
99
import com.intellij.openapi.application.ApplicationInfo
10+
import com.intellij.openapi.application.ApplicationManager
1011
import com.intellij.openapi.application.runInEdt
1112
import com.intellij.openapi.application.runReadAction
1213
import com.intellij.openapi.fileEditor.FileDocumentManager
1314
import com.intellij.openapi.project.Project
15+
import com.intellij.openapi.project.modules
16+
import com.intellij.openapi.roots.ModuleRootManager
1417
import com.intellij.openapi.vfs.LocalFileSystem
1518
import kotlinx.coroutines.delay
1619
import kotlinx.coroutines.ensureActive
1720
import kotlinx.coroutines.isActive
1821
import kotlinx.coroutines.time.withTimeout
1922
import kotlinx.coroutines.withContext
23+
import migration.software.aws.toolkits.jetbrains.settings.AwsSettings
24+
import org.apache.commons.codec.digest.DigestUtils
2025
import software.amazon.awssdk.services.codewhisperer.model.ArtifactType
2126
import software.amazon.awssdk.services.codewhisperer.model.CodeScanFindingsSchema
2227
import software.amazon.awssdk.services.codewhisperer.model.CodeScanStatus
@@ -33,12 +38,14 @@ import software.aws.toolkits.core.utils.Waiters.waitUntil
3338
import software.aws.toolkits.core.utils.debug
3439
import software.aws.toolkits.core.utils.getLogger
3540
import software.aws.toolkits.core.utils.info
41+
import software.aws.toolkits.core.utils.toHexString
3642
import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.sessionconfig.CodeScanSessionConfig
3743
import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.sessionconfig.PayloadContext
3844
import software.aws.toolkits.jetbrains.services.codewhisperer.credentials.CodeWhispererClientAdaptor
3945
import software.aws.toolkits.jetbrains.services.codewhisperer.explorer.CodeWhispererExplorerActionManager
4046
import software.aws.toolkits.jetbrains.services.codewhisperer.model.CodeScanResponseContext
4147
import software.aws.toolkits.jetbrains.services.codewhisperer.model.CreateUploadUrlServiceInvocationContext
48+
import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.CodeWhispererTelemetryService
4249
import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants
4350
import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants.CODE_SCAN_POLLING_INTERVAL_IN_SECONDS
4451
import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants.FILE_SCANS_THROTTLING_MESSAGE
@@ -52,12 +59,14 @@ import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhisperer
5259
import software.aws.toolkits.jetbrains.services.codewhisperer.util.getTelemetryErrorMessage
5360
import software.aws.toolkits.jetbrains.utils.assertIsNonDispatchThread
5461
import software.aws.toolkits.resources.message
62+
import software.aws.toolkits.telemetry.CodewhispererCodeScanScope
5563
import software.aws.toolkits.telemetry.CodewhispererLanguage
5664
import java.nio.file.Path
5765
import java.time.Duration
5866
import java.time.Instant
5967
import java.util.UUID
6068
import kotlin.coroutines.coroutineContext
69+
import kotlin.io.path.pathString
6170

6271
class CodeWhispererCodeScanSession(val sessionContext: CodeScanSessionContext) {
6372
private val clientToken: UUID = UUID.randomUUID()
@@ -103,7 +112,7 @@ class CodeWhispererCodeScanSession(val sessionContext: CodeScanSessionContext) {
103112
// 2 & 3. CreateUploadURL and upload the context.
104113
currentCoroutineContext.ensureActive()
105114
val artifactsUploadStartTime = now()
106-
val codeScanName = UUID.randomUUID().toString()
115+
val codeScanName = generateScanName()
107116

108117
val taskType = if (sessionContext.codeAnalysisScope == CodeWhispererConstants.CodeAnalysisScope.PROJECT) {
109118
CodeWhispererConstants.UploadTaskType.SCAN_PROJECT
@@ -366,6 +375,26 @@ class CodeWhispererCodeScanSession(val sessionContext: CodeScanSessionContext) {
366375
private fun isAutoScan(): Boolean =
367376
sessionContext.codeAnalysisScope == CodeWhispererConstants.CodeAnalysisScope.FILE && !sessionContext.sessionConfig.isInitiatedByChat()
368377

378+
private fun generateScanName(): String {
379+
val clientId = AwsSettings.getInstance().clientId
380+
val filePath = sessionContext.sessionConfig.getSelectedFile()?.toNioPath()?.pathString
381+
val scope = CodeWhispererTelemetryService.getInstance().mapToTelemetryScope(
382+
sessionContext.codeAnalysisScope,
383+
sessionContext.sessionConfig.isInitiatedByChat()
384+
)
385+
val projectId = if (scope != CodewhispererCodeScanScope.PROJECT && filePath != null) {
386+
filePath
387+
} else {
388+
ApplicationManager.getApplication().runReadAction<String> {
389+
sessionContext.project.modules.map { module ->
390+
ModuleRootManager.getInstance(module).contentRoots.firstOrNull()?.path
391+
}.joinToString(",")
392+
}
393+
}
394+
395+
return DigestUtils.sha256("$clientId::$projectId::$scope").toHexString()
396+
}
397+
369398
companion object {
370399
private val LOG = getLogger<CodeWhispererCodeScanSession>()
371400
private val MAPPER = jacksonObjectMapper()

plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/settings/CodeWhispererConfigurable.kt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,16 @@ package software.aws.toolkits.jetbrains.services.codewhisperer.settings
55

66
import com.intellij.icons.AllIcons
77
import com.intellij.ide.DataManager
8+
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory
89
import com.intellij.openapi.options.BoundConfigurable
910
import com.intellij.openapi.options.Configurable
1011
import com.intellij.openapi.options.SearchableConfigurable
1112
import com.intellij.openapi.options.ex.Settings
1213
import com.intellij.openapi.project.Project
14+
import com.intellij.openapi.ui.emptyText
1315
import com.intellij.ui.components.ActionLink
1416
import com.intellij.ui.components.fields.ExpandableTextField
17+
import com.intellij.ui.dsl.builder.Align
1518
import com.intellij.ui.dsl.builder.bindIntText
1619
import com.intellij.ui.dsl.builder.bindSelected
1720
import com.intellij.ui.dsl.builder.bindText
@@ -24,6 +27,7 @@ import software.aws.toolkits.jetbrains.services.codewhisperer.credentials.CodeWh
2427
import software.aws.toolkits.jetbrains.services.codewhisperer.explorer.CodeWhispererExplorerActionManager
2528
import software.aws.toolkits.jetbrains.services.codewhisperer.explorer.isCodeWhispererEnabled
2629
import software.aws.toolkits.jetbrains.settings.CodeWhispererSettings
30+
import software.aws.toolkits.jetbrains.settings.LspSettings
2731
import software.aws.toolkits.resources.message
2832
import java.awt.Font
2933
import java.util.concurrent.TimeUnit
@@ -61,6 +65,24 @@ class CodeWhispererConfigurable(private val project: Project) :
6165
}
6266
}
6367

68+
group(message("amazonqFeatureDev.placeholder.lsp")) {
69+
row(message("amazonqFeatureDev.placeholder.select_lsp_artifact")) {
70+
val fileChooserDescriptor = FileChooserDescriptorFactory.createSingleFileDescriptor()
71+
fileChooserDescriptor.isForcedToUseIdeaFileChooser = true
72+
73+
textFieldWithBrowseButton(fileChooserDescriptor = fileChooserDescriptor)
74+
.bindText(
75+
{ LspSettings.getInstance().getArtifactPath() },
76+
{ LspSettings.getInstance().setArtifactPath(it.takeIf { v -> v.isNotBlank() }) }
77+
)
78+
.applyToComponent {
79+
emptyText.text = "Choose a file to upload"
80+
}
81+
.resizableColumn()
82+
.align(Align.FILL)
83+
}
84+
}
85+
6486
group(message("aws.settings.codewhisperer.group.inline_suggestions")) {
6587
row {
6688
checkBox(message("aws.settings.codewhisperer.include_code_with_reference")).apply {

plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/telemetry/CodeWhispererTelemetryService.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ class CodeWhispererTelemetryService {
285285
)
286286
}
287287

288-
private fun mapToTelemetryScope(codeAnalysisScope: CodeWhispererConstants.CodeAnalysisScope, initiatedByChat: Boolean): CodewhispererCodeScanScope =
288+
fun mapToTelemetryScope(codeAnalysisScope: CodeWhispererConstants.CodeAnalysisScope, initiatedByChat: Boolean): CodewhispererCodeScanScope =
289289
when (codeAnalysisScope) {
290290
CodeWhispererConstants.CodeAnalysisScope.FILE -> {
291291
if (initiatedByChat) {

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/dependencies/providers/JavaModuleDependencyProvider.kt

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import com.intellij.openapi.vfs.VfsUtil
1111
import software.aws.toolkits.jetbrains.services.amazonq.lsp.dependencies.ModuleDependencyProvider
1212
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.dependencies.SyncModuleDependenciesParams
1313

14-
class JavaModuleDependencyProvider : ModuleDependencyProvider {
14+
internal class JavaModuleDependencyProvider : ModuleDependencyProvider {
1515
override fun isApplicable(module: Module): Boolean =
1616
ModuleRootManager.getInstance(module).sdk?.sdkType is JavaSdkType
1717

@@ -36,12 +36,11 @@ class JavaModuleDependencyProvider : ModuleDependencyProvider {
3636
)
3737
}
3838

39-
private fun getSourceRoots(module: Module): List<String> {
40-
return ModuleRootManager.getInstance(module).contentEntries
39+
private fun getSourceRoots(module: Module): List<String> =
40+
ModuleRootManager.getInstance(module).contentEntries
4141
.flatMap { contentEntry ->
4242
contentEntry.sourceFolders
4343
.filter { !it.isTestSource }
4444
.mapNotNull { it.file?.path }
4545
}
46-
}
4746
}

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/dependencies/providers/PythonModuleDependencyProvider.kt

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import com.jetbrains.python.sdk.PythonSdkUtil
1010
import software.aws.toolkits.jetbrains.services.amazonq.lsp.dependencies.ModuleDependencyProvider
1111
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.dependencies.SyncModuleDependenciesParams
1212

13-
class PythonModuleDependencyProvider : ModuleDependencyProvider {
13+
internal class PythonModuleDependencyProvider : ModuleDependencyProvider {
1414
override fun isApplicable(module: Module): Boolean =
1515
PythonSdkUtil.findPythonSdk(module) != null
1616

@@ -35,12 +35,11 @@ class PythonModuleDependencyProvider : ModuleDependencyProvider {
3535
)
3636
}
3737

38-
private fun getSourceRoots(module: Module): List<String> {
39-
return ModuleRootManager.getInstance(module).contentEntries
38+
private fun getSourceRoots(module: Module): List<String> =
39+
ModuleRootManager.getInstance(module).contentEntries
4040
.flatMap { contentEntry ->
4141
contentEntry.sourceFolders
4242
.filter { !it.isTestSource }
4343
.mapNotNull { it.file?.path }
4444
}
45-
}
4645
}

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/dependencies/SyncModuleDependenciesParams.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
package software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.dependencies
55

6-
public class SyncModuleDependenciesParams(
6+
class SyncModuleDependenciesParams(
77
val moduleName: String,
88
val programmingLanguage: String,
99
val files: List<String>,
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package software.aws.toolkits.jetbrains.settings
5+
6+
import com.intellij.openapi.components.BaseState
7+
import com.intellij.openapi.components.PersistentStateComponent
8+
import com.intellij.openapi.components.RoamingType
9+
import com.intellij.openapi.components.Service
10+
import com.intellij.openapi.components.State
11+
import com.intellij.openapi.components.Storage
12+
import com.intellij.openapi.components.service
13+
import com.intellij.util.xmlb.annotations.Property
14+
15+
@Service
16+
@State(name = "lspSettings", storages = [Storage("aws.xml", roamingType = RoamingType.DISABLED)])
17+
class LspSettings : PersistentStateComponent<LspConfiguration> {
18+
private var state = LspConfiguration()
19+
20+
override fun getState(): LspConfiguration = state
21+
22+
override fun loadState(state: LspConfiguration) {
23+
this.state = state
24+
}
25+
26+
fun getArtifactPath() = state.artifactPath
27+
28+
fun setArtifactPath(artifactPath: String?) {
29+
if (artifactPath == null) {
30+
state.artifactPath = ""
31+
} else {
32+
state.artifactPath = artifactPath
33+
}
34+
}
35+
36+
companion object {
37+
fun getInstance(): LspSettings = service()
38+
}
39+
}
40+
41+
class LspConfiguration : BaseState() {
42+
@get:Property
43+
var artifactPath: String = ""
44+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package software.aws.toolkits.jetbrains.settings
5+
6+
import com.intellij.util.xmlb.XmlSerializer
7+
import org.assertj.core.api.Assertions.assertThat
8+
import org.jdom.output.XMLOutputter
9+
import org.junit.jupiter.api.BeforeEach
10+
import org.junit.jupiter.api.Test
11+
import software.aws.toolkits.jetbrains.utils.xmlElement
12+
13+
class LspSettingsTest {
14+
private lateinit var lspSettings: LspSettings
15+
16+
@BeforeEach
17+
fun setUp() {
18+
lspSettings = LspSettings()
19+
lspSettings.loadState(LspConfiguration())
20+
}
21+
22+
@Test
23+
fun `artifact path is empty by default`() {
24+
assertThat(lspSettings.getArtifactPath()).isEmpty()
25+
}
26+
27+
@Test
28+
fun `artifact path can be set`() {
29+
lspSettings.setArtifactPath("test\\lsp.js")
30+
assertThat(lspSettings.getArtifactPath()).isNotEmpty()
31+
assertThat(lspSettings.getArtifactPath()).isEqualTo("test\\lsp.js")
32+
}
33+
34+
@Test
35+
fun `artifact path cannot be null`() {
36+
lspSettings.setArtifactPath(null)
37+
assertThat(lspSettings.getArtifactPath()).isEmpty()
38+
}
39+
40+
@Test
41+
fun `serialize settings to ensure backwards compatibility`() {
42+
val element = xmlElement(
43+
"""
44+
<component name="LspSettings">
45+
</component>
46+
""".trimIndent()
47+
)
48+
lspSettings.setArtifactPath("temp\\lsp.js")
49+
50+
XmlSerializer.serializeInto(lspSettings.state, element)
51+
52+
val actual = XMLOutputter().outputString(element)
53+
54+
val expected = "<component name=\"LspSettings\">\n" +
55+
"<option name=\"artifactPath\" value=\"temp\\lsp.js\" /></component>"
56+
57+
assertThat(actual).isEqualTo(expected)
58+
}
59+
60+
@Test
61+
fun `deserialize empty settings to ensure backwards compatibility`() {
62+
val element = xmlElement(
63+
"""
64+
<component name="LspSettings">
65+
</component>
66+
"""
67+
)
68+
val actual = XmlSerializer.deserialize(element, LspConfiguration::class.java)
69+
assertThat(actual.artifactPath).isEmpty()
70+
}
71+
72+
@Test
73+
fun `deserialize existing settings to ensure backwards compatibility`() {
74+
val element = xmlElement(
75+
"""
76+
<component name="LspSettings">
77+
<option name="artifactPath" value='temp\lsp.js'/>
78+
</component>
79+
""".trimIndent()
80+
)
81+
val actual = XmlSerializer.deserialize(element, LspConfiguration::class.java)
82+
assertThat(actual.artifactPath).isNotEmpty()
83+
assertThat(actual.artifactPath).isEqualTo("temp\\lsp.js")
84+
}
85+
}

0 commit comments

Comments
 (0)