Skip to content

Commit 14cd758

Browse files
authored
Merge branch 'main' into API-refactor
2 parents ec4d7cb + 3f3726b commit 14cd758

File tree

33 files changed

+648
-516
lines changed

33 files changed

+648
-516
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+
}

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
@@ -30,6 +30,7 @@ import software.aws.toolkits.core.utils.debug
3030
import software.aws.toolkits.core.utils.error
3131
import software.aws.toolkits.core.utils.getLogger
3232
import software.aws.toolkits.core.utils.info
33+
import software.aws.toolkits.jetbrains.core.credentials.sono.isInternalUser
3334
import software.aws.toolkits.jetbrains.services.amazonq.clients.AmazonQStreamingClient
3435
import software.aws.toolkits.jetbrains.services.amazonqCodeTest.controller.CodeTestChatHelper
3536
import software.aws.toolkits.jetbrains.services.amazonqCodeTest.messages.Button
@@ -501,12 +502,25 @@ Please see the unit tests generated below. Click 'View Diff' to review the chang
501502
e is JsonParseException -> message("testgen.error.generic_technical_error_message")
502503
else -> message("testgen.error.generic_error_message")
503504
}
504-
505+
val buttonList = mutableListOf<Button>()
506+
if (isInternalUser(getStartUrl(project))) {
507+
buttonList.add(
508+
Button(
509+
"utg_feedback",
510+
message("testgen.button.feedback"),
511+
keepCardAfterClick = true,
512+
position = "outside",
513+
status = "info",
514+
icon = "comment"
515+
),
516+
)
517+
}
505518
codeTestChatHelper.addAnswer(
506519
CodeTestChatMessageContent(
507520
message = errorMessage,
508521
type = ChatMessageType.Answer,
509-
canBeVoted = false
522+
canBeVoted = false,
523+
buttons = buttonList
510524
)
511525
)
512526

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(
@@ -663,7 +667,7 @@ class CodeTestChatController(
663667
testGenerationEventResponse.responseMetadata().requestId()}"
664668
}
665669

666-
UiTelemetry.click(null as Project?, "unitTestGeneration_acceptDiff")
670+
UiTelemetry.click(context.project, "unitTestGeneration_acceptDiff")
667671

668672
AmazonqTelemetry.utgGenerateTests(
669673
cwsprChatProgrammingLanguage = session.programmingLanguage.languageId,
@@ -692,10 +696,10 @@ class CodeTestChatController(
692696
CodeTestChatMessageContent(
693697
message = message("testgen.message.success"),
694698
type = ChatMessageType.Answer,
695-
canBeVoted = false
699+
canBeVoted = false,
700+
buttons = this.showFeedbackButton()
696701
)
697702
)
698-
sessionCleanUp(session.tabId)
699703
codeTestChatHelper.updateUI(
700704
promptInputDisabledState = false,
701705
promptInputPlaceholder = message("testgen.placeholder.enter_slash_quick_actions"),
@@ -841,7 +845,8 @@ class CodeTestChatController(
841845
CodeTestChatMessageContent(
842846
message = message("testgen.message.success"),
843847
type = ChatMessageType.Answer,
844-
canBeVoted = false
848+
canBeVoted = false,
849+
buttons = this.showFeedbackButton()
845850
)
846851
)
847852
val testGenerationEventResponse = client.sendTestGenerationEvent(
@@ -885,7 +890,10 @@ class CodeTestChatController(
885890
requestId = session.startTestGenerationRequestId,
886891
status = Status.REJECTED,
887892
)
888-
sessionCleanUp(message.tabId)
893+
}
894+
"utg_feedback" -> {
895+
sendFeedback()
896+
UiTelemetry.click(context.project, "unitTestGeneration_provideFeedback")
889897
}
890898
"utg_skip_and_finish" -> {
891899
codeTestChatHelper.addAnswer(
@@ -1374,6 +1382,33 @@ class CodeTestChatController(
13741382
println(message)
13751383
}
13761384

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

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)