Skip to content

Commit 93ede19

Browse files
authored
Merge branch 'aws:main' into doc
2 parents de4afb2 + 3f3726b commit 93ede19

File tree

40 files changed

+1172
-525
lines changed

40 files changed

+1172
-525
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type" : "bugfix",
3+
"description" : "/review: normalize relative file path before unzipping"
4+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type" : "feature",
3+
"description" : "AmazonQ /dev and /doc: Add support for complex workspaces."
4+
}

buildspec/linuxIntegrationTests.yml

-4
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,6 @@ phases:
1515
install:
1616
commands:
1717
- startDocker.sh
18-
# login to DockerHub so we don't get throttled
19-
- export DOCKER_USERNAME=`echo $DOCKER_HUB_TOKEN | jq -r '.username'`
20-
- export DOCKER_PASSWORD=`echo $DOCKER_HUB_TOKEN | jq -r '.password'`
21-
- docker login --username $DOCKER_USERNAME --password $DOCKER_PASSWORD || true
2218
# should probably be managed as an extension/rule in any tests that need a screen available
2319
- /usr/bin/Xvfb :22 -screen 0 1920x1080x24 &
2420

buildspec/linuxUiTests.yml

-5
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,6 @@ phases:
2121
commands:
2222
- dnf install -y marco mate-media e2fsprogs
2323
- startDesktop.sh
24-
25-
# login to DockerHub so we don't get throttled
26-
- export DOCKER_USERNAME=`echo $DOCKER_HUB_TOKEN | jq -r '.username'`
27-
- export DOCKER_PASSWORD=`echo $DOCKER_HUB_TOKEN | jq -r '.password'`
28-
- docker login --username $DOCKER_USERNAME --password $DOCKER_PASSWORD || true
2924
- export PATH="$PATH:$HOME/.dotnet/tools"
3025
- dotnet codeartifact-creds install
3126

plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/common/session/SessionStateTypes.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
package software.aws.toolkits.jetbrains.common.session
55

66
import software.aws.toolkits.jetbrains.common.util.AmazonQCodeGenService
7-
import software.aws.toolkits.jetbrains.services.amazonq.FeatureDevSessionContext
7+
import software.aws.toolkits.jetbrains.services.amazonq.project.FeatureDevSessionContext
88

99
open class SessionStateConfig(
1010
open val conversationId: String,

plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeTest/CodeWhispererUTGChatManager.kt

+16-2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import software.aws.toolkits.core.utils.debug
2929
import software.aws.toolkits.core.utils.error
3030
import software.aws.toolkits.core.utils.getLogger
3131
import software.aws.toolkits.core.utils.info
32+
import software.aws.toolkits.jetbrains.core.credentials.sono.isInternalUser
3233
import software.aws.toolkits.jetbrains.services.amazonq.clients.AmazonQStreamingClient
3334
import software.aws.toolkits.jetbrains.services.amazonqCodeTest.controller.CodeTestChatHelper
3435
import software.aws.toolkits.jetbrains.services.amazonqCodeTest.messages.Button
@@ -559,12 +560,25 @@ class CodeWhispererUTGChatManager(val project: Project, private val cs: Coroutin
559560
e is JsonParseException -> message("testgen.error.generic_technical_error_message")
560561
else -> message("testgen.error.generic_error_message")
561562
}
562-
563+
val buttonList = mutableListOf<Button>()
564+
if (isInternalUser(getStartUrl(project))) {
565+
buttonList.add(
566+
Button(
567+
"utg_feedback",
568+
message("testgen.button.feedback"),
569+
keepCardAfterClick = true,
570+
position = "outside",
571+
status = "info",
572+
icon = "comment"
573+
),
574+
)
575+
}
563576
codeTestChatHelper.addAnswer(
564577
CodeTestChatMessageContent(
565578
message = errorMessage,
566579
type = ChatMessageType.Answer,
567-
canBeVoted = false
580+
canBeVoted = false,
581+
buttons = buttonList
568582
)
569583
)
570584

plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeTest/controller/CodeTestChatController.kt

+44-9
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import com.intellij.diff.DiffManagerEx
99
import com.intellij.diff.requests.SimpleDiffRequest
1010
import com.intellij.ide.BrowserUtil
1111
import com.intellij.openapi.application.ApplicationManager
12+
import com.intellij.openapi.application.runInEdt
1213
import com.intellij.openapi.fileEditor.FileEditorManager
1314
import com.intellij.openapi.project.Project
1415
import com.intellij.openapi.project.guessProjectDir
@@ -53,6 +54,7 @@ import software.aws.toolkits.jetbrains.core.AwsClientManager
5354
import software.aws.toolkits.jetbrains.core.coroutines.EDT
5455
import software.aws.toolkits.jetbrains.core.credentials.ToolkitConnectionManager
5556
import software.aws.toolkits.jetbrains.core.credentials.pinning.QConnection
57+
import software.aws.toolkits.jetbrains.core.credentials.sono.isInternalUser
5658
import software.aws.toolkits.jetbrains.services.amazonq.apps.AmazonQAppInitContext
5759
import software.aws.toolkits.jetbrains.services.amazonq.auth.AuthController
5860
import software.aws.toolkits.jetbrains.services.amazonq.project.RelevantDocument
@@ -92,6 +94,7 @@ import software.aws.toolkits.jetbrains.services.cwc.editor.context.ExtractionTri
9294
import software.aws.toolkits.jetbrains.services.cwc.editor.context.file.FileContext
9395
import software.aws.toolkits.jetbrains.services.cwc.messages.ChatMessageType
9496
import software.aws.toolkits.jetbrains.services.telemetry.TelemetryService
97+
import software.aws.toolkits.jetbrains.ui.feedback.TestGenFeedbackDialog
9598
import software.aws.toolkits.jetbrains.utils.notifyError
9699
import software.aws.toolkits.resources.message
97100
import software.aws.toolkits.telemetry.AmazonqTelemetry
@@ -168,13 +171,14 @@ class CodeTestChatController(
168171
override suspend fun processStartTestGen(message: IncomingCodeTestMessage.StartTestGen) {
169172
codeTestChatHelper.setActiveCodeTestTabId(message.tabId)
170173
val session = codeTestChatHelper.getActiveSession()
174+
if (session.isGeneratingTests) {
175+
return
176+
}
177+
sessionCleanUp(session.tabId)
171178
// check if IDE has active file open, yes return (fileName and filePath) else return null
172179
val project = context.project
173180
val fileInfo = checkActiveFileInIDE(project, message) ?: return
174181
session.programmingLanguage = fileInfo.fileLanguage
175-
if (session.isGeneratingTests === true) {
176-
return
177-
}
178182
session.startTimeOfTestGeneration = Instant.now().toEpochMilli().toDouble()
179183
session.isGeneratingTests = true
180184

@@ -565,7 +569,7 @@ class CodeTestChatController(
565569
session.linesOfCodeGenerated = lineDifference.coerceAtLeast(0)
566570
session.charsOfCodeGenerated = charDifference.coerceAtLeast(0)
567571
session.latencyOfTestGeneration = (Instant.now().toEpochMilli() - session.startTimeOfTestGeneration)
568-
UiTelemetry.click(null as Project?, "unitTestGeneration_viewDiff")
572+
UiTelemetry.click(context.project, "unitTestGeneration_viewDiff")
569573

570574
val buttonList = mutableListOf<Button>()
571575
buttonList.add(
@@ -662,7 +666,7 @@ class CodeTestChatController(
662666
testGenerationEventResponse.responseMetadata().requestId()}"
663667
}
664668

665-
UiTelemetry.click(null as Project?, "unitTestGeneration_acceptDiff")
669+
UiTelemetry.click(context.project, "unitTestGeneration_acceptDiff")
666670

667671
AmazonqTelemetry.utgGenerateTests(
668672
cwsprChatProgrammingLanguage = session.programmingLanguage.languageId,
@@ -691,10 +695,10 @@ class CodeTestChatController(
691695
CodeTestChatMessageContent(
692696
message = message("testgen.message.success"),
693697
type = ChatMessageType.Answer,
694-
canBeVoted = false
698+
canBeVoted = false,
699+
buttons = this.showFeedbackButton()
695700
)
696701
)
697-
sessionCleanUp(session.tabId)
698702
codeTestChatHelper.updateUI(
699703
promptInputDisabledState = false,
700704
promptInputPlaceholder = message("testgen.placeholder.enter_slash_quick_actions"),
@@ -840,7 +844,8 @@ class CodeTestChatController(
840844
CodeTestChatMessageContent(
841845
message = message("testgen.message.success"),
842846
type = ChatMessageType.Answer,
843-
canBeVoted = false
847+
canBeVoted = false,
848+
buttons = this.showFeedbackButton()
844849
)
845850
)
846851
val testGenerationEventResponse = client.sendTestGenerationEvent(
@@ -884,7 +889,10 @@ class CodeTestChatController(
884889
requestId = session.startTestGenerationRequestId,
885890
status = Status.REJECTED,
886891
)
887-
sessionCleanUp(message.tabId)
892+
}
893+
"utg_feedback" -> {
894+
sendFeedback()
895+
UiTelemetry.click(context.project, "unitTestGeneration_provideFeedback")
888896
}
889897
"utg_skip_and_finish" -> {
890898
codeTestChatHelper.addAnswer(
@@ -1373,6 +1381,33 @@ class CodeTestChatController(
13731381
println(message)
13741382
}
13751383

1384+
private fun sendFeedback() {
1385+
runInEdt {
1386+
TestGenFeedbackDialog(
1387+
context.project,
1388+
codeTestChatHelper.getActiveSession().startTestGenerationRequestId,
1389+
codeTestChatHelper.getActiveSession().testGenerationJob
1390+
).show()
1391+
}
1392+
}
1393+
1394+
private fun showFeedbackButton(): MutableList<Button> {
1395+
val buttonList = mutableListOf<Button>()
1396+
if (isInternalUser(getStartUrl(context.project))) {
1397+
buttonList.add(
1398+
Button(
1399+
"utg_feedback",
1400+
message("testgen.button.feedback"),
1401+
keepCardAfterClick = true,
1402+
position = "outside",
1403+
status = "info",
1404+
icon = "comment"
1405+
),
1406+
)
1407+
}
1408+
return buttonList
1409+
}
1410+
13761411
companion object {
13771412
private val LOG = getLogger<CodeTestChatController>()
13781413

plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqDoc/controller/DocController.kt

+8-24
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,6 @@ import com.intellij.diff.util.DiffUserDataKeys
1313
import com.intellij.ide.BrowserUtil
1414
import com.intellij.openapi.application.runInEdt
1515
import com.intellij.openapi.fileEditor.FileEditorManager
16-
import com.intellij.openapi.project.Project
17-
import com.intellij.openapi.roots.ProjectRootManager
18-
import com.intellij.openapi.vfs.LocalFileSystem
1916
import com.intellij.openapi.vfs.VfsUtil
2017
import com.intellij.openapi.wm.ToolWindowManager
2118
import com.intellij.testFramework.LightVirtualFile
@@ -31,9 +28,9 @@ import software.aws.toolkits.core.utils.info
3128
import software.aws.toolkits.core.utils.warn
3229
import software.aws.toolkits.jetbrains.common.util.selectFolder
3330
import software.aws.toolkits.jetbrains.core.coroutines.EDT
34-
import software.aws.toolkits.jetbrains.services.amazonq.RepoSizeError
3531
import software.aws.toolkits.jetbrains.services.amazonq.apps.AmazonQAppInitContext
3632
import software.aws.toolkits.jetbrains.services.amazonq.auth.AuthController
33+
import software.aws.toolkits.jetbrains.services.amazonq.project.RepoSizeError
3734
import software.aws.toolkits.jetbrains.services.amazonq.toolwindow.AmazonQToolWindowFactory
3835
import software.aws.toolkits.jetbrains.services.amazonqDoc.DEFAULT_RETRY_LIMIT
3936
import software.aws.toolkits.jetbrains.services.amazonqDoc.DIAGRAM_SVG_EXT
@@ -81,7 +78,6 @@ import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.util.Cancellat
8178
import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.QFeatureEvent
8279
import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.broadcastQEvent
8380
import software.aws.toolkits.resources.message
84-
import java.nio.file.Paths
8581
import java.util.UUID
8682

8783
enum class DocGenerationStep {
@@ -308,7 +304,7 @@ class DocController(
308304
val session = getSessionInfo(tabId)
309305
val docGenerationTask = docGenerationTasks.getTask(tabId)
310306

311-
val currentSourceFolder = session.context.selectedSourceFolder
307+
val currentSourceFolder = session.context.selectionRoot
312308

313309
try {
314310
messenger.sendFolderConfirmationMessage(
@@ -405,7 +401,7 @@ class DocController(
405401
inMemoryFile.isWritable = false
406402
FileEditorManager.getInstance(context.project).openFile(inMemoryFile, true)
407403
} else {
408-
val existingFile = VfsUtil.findRelativeFile(message.filePath, session.context.selectedSourceFolder)
404+
val existingFile = VfsUtil.findRelativeFile(message.filePath, session.context.addressableRoot)
409405
val leftDiffContent = if (existingFile == null) {
410406
EmptyContent()
411407
} else {
@@ -945,19 +941,9 @@ class DocController(
945941
}
946942
}
947943

948-
private fun isFolderPathInProjectModules(project: Project, folderPath: String): Boolean {
949-
val path = Paths.get(folderPath)
950-
val virtualFile = LocalFileSystem.getInstance().findFileByIoFile(path.toFile()) ?: return false
951-
952-
val projectFileIndex = ProjectRootManager.getInstance(project).fileIndex
953-
954-
return projectFileIndex.isInProject(virtualFile)
955-
}
956-
957944
private suspend fun modifyDefaultSourceFolder(tabId: String) {
958945
val session = getSessionInfo(tabId)
959-
val currentSourceFolder = session.context.selectedSourceFolder
960-
val projectRoot = session.context.projectRoot
946+
val workspaceRoot = session.context.workspaceRoot
961947
val docGenerationTask = docGenerationTasks.getTask(tabId)
962948

963949
withContext(EDT) {
@@ -967,7 +953,7 @@ class DocController(
967953
message = message("amazonqDoc.prompt.choose_folder_to_continue")
968954
)
969955

970-
val selectedFolder = selectFolder(context.project, currentSourceFolder)
956+
val selectedFolder = selectFolder(context.project, workspaceRoot)
971957

972958
// No folder was selected
973959
if (selectedFolder == null) {
@@ -980,9 +966,7 @@ class DocController(
980966
return@withContext
981967
}
982968

983-
val isFolderPathInProject = isFolderPathInProjectModules(context.project, selectedFolder.path)
984-
985-
if (!isFolderPathInProject) {
969+
if (!selectedFolder.path.startsWith(workspaceRoot.path)) {
986970
logger.info { "Selected folder not in workspace: ${selectedFolder.path}" }
987971

988972
messenger.sendAnswer(
@@ -1004,15 +988,15 @@ class DocController(
1004988
return@withContext
1005989
}
1006990

1007-
if (selectedFolder.path == projectRoot.path) {
991+
if (selectedFolder.path == workspaceRoot.path) {
1008992
docGenerationTask.folderLevel = DocFolderLevel.ENTIRE_WORKSPACE
1009993
} else {
1010994
docGenerationTask.folderLevel = DocFolderLevel.SUB_FOLDER
1011995
}
1012996

1013997
logger.info { "Selected correct folder inside workspace: ${selectedFolder.path}" }
1014998

1015-
session.context.selectedSourceFolder = selectedFolder
999+
session.context.selectionRoot = selectedFolder
10161000

10171001
promptForDocTarget(tabId)
10181002

plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqDoc/session/DocSession.kt

+5-7
Original file line numberDiff line numberDiff line change
@@ -107,14 +107,12 @@ class DocSession(val tabID: String, val project: Project) {
107107
* Triggered by the Insert code follow-up button to apply code changes.
108108
*/
109109
fun insertChanges(filePaths: List<NewFileZipInfo>, deletedFiles: List<DeletedFileInfo>) {
110-
val selectedSourceFolder = context.selectedSourceFolder.toNioPath()
110+
filePaths.forEach { resolveAndCreateOrUpdateFile(context.addressableRoot.toNioPath(), it.zipFilePath, it.fileContent) }
111111

112-
filePaths.forEach { resolveAndCreateOrUpdateFile(selectedSourceFolder, it.zipFilePath, it.fileContent) }
113-
114-
deletedFiles.forEach { resolveAndDeleteFile(selectedSourceFolder, it.zipFilePath) }
112+
deletedFiles.forEach { resolveAndDeleteFile(context.addressableRoot.toNioPath(), it.zipFilePath) }
115113

116114
// Taken from https://intellij-support.jetbrains.com/hc/en-us/community/posts/206118439-Refresh-after-external-changes-to-project-structure-and-sources
117-
VfsUtil.markDirtyAndRefresh(true, true, true, context.selectedSourceFolder)
115+
VfsUtil.markDirtyAndRefresh(true, true, true, context.addressableRoot)
118116
}
119117

120118
private fun getFromReportedChanges(filePath: NewFileZipInfo): String? {
@@ -158,7 +156,7 @@ class DocSession(val tabID: String, val project: Project) {
158156
}
159157
} else {
160158
val sourceContent = reportedChange
161-
?: VfsUtil.findRelativeFile(filePath.zipFilePath, context.selectedSourceFolder)?.content()
159+
?: VfsUtil.findRelativeFile(filePath.zipFilePath, context.addressableRoot)?.content()
162160
.orEmpty()
163161
val diffMetrics = getDiffMetrics(sourceContent, content)
164162
totalAddedLines += diffMetrics.insertedLines
@@ -185,7 +183,7 @@ class DocSession(val tabID: String, val project: Project) {
185183
totalAddedChars += content.length
186184
totalAddedLines += content.split('\n').size
187185
} else {
188-
val existingFileContent = VfsUtil.findRelativeFile(filePath.zipFilePath, context.selectedSourceFolder)?.content()
186+
val existingFileContent = VfsUtil.findRelativeFile(filePath.zipFilePath, context.addressableRoot)?.content()
189187
val diffMetrics = getDiffMetrics(existingFileContent.orEmpty(), content)
190188
totalAddedLines += diffMetrics.insertedLines
191189
totalAddedChars += diffMetrics.insertedCharacters

plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqDoc/session/DocSessionContext.kt

+2-26
Original file line numberDiff line numberDiff line change
@@ -4,30 +4,6 @@
44
package software.aws.toolkits.jetbrains.services.amazonqDoc.session
55

66
import com.intellij.openapi.project.Project
7-
import com.intellij.openapi.vfs.VirtualFile
8-
import software.aws.toolkits.jetbrains.services.amazonq.FeatureDevSessionContext
9-
import software.aws.toolkits.jetbrains.services.amazonqDoc.SUPPORTED_DIAGRAM_EXT_SET
10-
import software.aws.toolkits.jetbrains.services.amazonqDoc.SUPPORTED_DIAGRAM_FILE_NAME_SET
7+
import software.aws.toolkits.jetbrains.services.amazonq.project.FeatureDevSessionContext
118

12-
class DocSessionContext(project: Project, maxProjectSizeBytes: Long? = null) : FeatureDevSessionContext(project, maxProjectSizeBytes) {
13-
14-
/**
15-
* Ensure diagram files are not ignored
16-
*/
17-
override fun getAdditionalGitIgnoreBinaryFilesRules(): Set<String> {
18-
val ignoreRules = super.getAdditionalGitIgnoreBinaryFilesRules()
19-
val diagramExtRulesInGitIgnoreFormatSet = SUPPORTED_DIAGRAM_EXT_SET.map { "*.$it" }.toSet()
20-
return ignoreRules - diagramExtRulesInGitIgnoreFormatSet
21-
}
22-
23-
/**
24-
* Ensure diagram files are not filtered
25-
*/
26-
override fun isFileExtensionAllowed(file: VirtualFile): Boolean {
27-
if (super.isFileExtensionAllowed(file)) {
28-
return true
29-
}
30-
31-
return file.extension != null && SUPPORTED_DIAGRAM_FILE_NAME_SET.contains(file.name)
32-
}
33-
}
9+
class DocSessionContext(project: Project, maxProjectSizeBytes: Long? = null) : FeatureDevSessionContext(project, maxProjectSizeBytes)

plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqFeatureDev/FeatureDevExceptions.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
package software.aws.toolkits.jetbrains.services.amazonqFeatureDev
55

6-
import software.aws.toolkits.jetbrains.services.amazonq.RepoSizeError
6+
import software.aws.toolkits.jetbrains.services.amazonq.project.RepoSizeError
77
import software.aws.toolkits.resources.message
88

99
/**

0 commit comments

Comments
 (0)