Skip to content

Commit bf817f3

Browse files
author
C Tidd
committed
feat(amazonq): Add support for multi-project workspaces.
1 parent c28033d commit bf817f3

File tree

21 files changed

+519
-478
lines changed

21 files changed

+519
-478
lines changed

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/amazonqDoc/controller/DocController.kt

+7-8
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ import software.aws.toolkits.core.utils.info
3131
import software.aws.toolkits.core.utils.warn
3232
import software.aws.toolkits.jetbrains.common.util.selectFolder
3333
import software.aws.toolkits.jetbrains.core.coroutines.EDT
34-
import software.aws.toolkits.jetbrains.services.amazonq.RepoSizeError
3534
import software.aws.toolkits.jetbrains.services.amazonq.apps.AmazonQAppInitContext
3635
import software.aws.toolkits.jetbrains.services.amazonq.auth.AuthController
36+
import software.aws.toolkits.jetbrains.services.amazonq.project.RepoSizeError
3737
import software.aws.toolkits.jetbrains.services.amazonq.toolwindow.AmazonQToolWindowFactory
3838
import software.aws.toolkits.jetbrains.services.amazonqDoc.DEFAULT_RETRY_LIMIT
3939
import software.aws.toolkits.jetbrains.services.amazonqDoc.DIAGRAM_SVG_EXT
@@ -308,7 +308,7 @@ class DocController(
308308
val session = getSessionInfo(tabId)
309309
val docGenerationTask = docGenerationTasks.getTask(tabId)
310310

311-
val currentSourceFolder = session.context.selectedSourceFolder
311+
val currentSourceFolder = session.context.selectionRoot
312312

313313
try {
314314
messenger.sendFolderConfirmationMessage(
@@ -405,7 +405,7 @@ class DocController(
405405
inMemoryFile.isWritable = false
406406
FileEditorManager.getInstance(context.project).openFile(inMemoryFile, true)
407407
} else {
408-
val existingFile = VfsUtil.findRelativeFile(message.filePath, session.context.selectedSourceFolder)
408+
val existingFile = VfsUtil.findRelativeFile(message.filePath, session.context.addressableRoot)
409409
val leftDiffContent = if (existingFile == null) {
410410
EmptyContent()
411411
} else {
@@ -956,8 +956,7 @@ class DocController(
956956

957957
private suspend fun modifyDefaultSourceFolder(tabId: String) {
958958
val session = getSessionInfo(tabId)
959-
val currentSourceFolder = session.context.selectedSourceFolder
960-
val projectRoot = session.context.projectRoot
959+
val workspaceRoot = session.context.workspaceRoot
961960
val docGenerationTask = docGenerationTasks.getTask(tabId)
962961

963962
withContext(EDT) {
@@ -967,7 +966,7 @@ class DocController(
967966
message = message("amazonqDoc.prompt.choose_folder_to_continue")
968967
)
969968

970-
val selectedFolder = selectFolder(context.project, currentSourceFolder)
969+
val selectedFolder = selectFolder(context.project, workspaceRoot)
971970

972971
// No folder was selected
973972
if (selectedFolder == null) {
@@ -1004,15 +1003,15 @@ class DocController(
10041003
return@withContext
10051004
}
10061005

1007-
if (selectedFolder.path == projectRoot.path) {
1006+
if (selectedFolder.path == workspaceRoot.path) {
10081007
docGenerationTask.folderLevel = DocFolderLevel.ENTIRE_WORKSPACE
10091008
} else {
10101009
docGenerationTask.folderLevel = DocFolderLevel.SUB_FOLDER
10111010
}
10121011

10131012
logger.info { "Selected correct folder inside workspace: ${selectedFolder.path}" }
10141013

1015-
session.context.selectedSourceFolder = selectedFolder
1014+
session.context.selectionRoot = selectedFolder
10161015

10171016
promptForDocTarget(tabId)
10181017

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
/**

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

+8-9
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,10 @@ import software.aws.toolkits.core.utils.info
2929
import software.aws.toolkits.core.utils.warn
3030
import software.aws.toolkits.jetbrains.common.util.selectFolder
3131
import software.aws.toolkits.jetbrains.core.coroutines.EDT
32-
import software.aws.toolkits.jetbrains.services.amazonq.RepoSizeError
3332
import software.aws.toolkits.jetbrains.services.amazonq.apps.AmazonQAppInitContext
3433
import software.aws.toolkits.jetbrains.services.amazonq.auth.AuthController
3534
import software.aws.toolkits.jetbrains.services.amazonq.messages.MessagePublisher
35+
import software.aws.toolkits.jetbrains.services.amazonq.project.RepoSizeError
3636
import software.aws.toolkits.jetbrains.services.amazonq.toolwindow.AmazonQToolWindowFactory
3737
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.CodeIterationLimitException
3838
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.DEFAULT_RETRY_LIMIT
@@ -249,7 +249,7 @@ class FeatureDevController(
249249
when (sessionState) {
250250
is PrepareCodeGenerationState -> {
251251
runInEdt {
252-
val existingFile = VfsUtil.findRelativeFile(message.filePath, session.context.selectedSourceFolder)
252+
val existingFile = VfsUtil.findRelativeFile(message.filePath, session.context.addressableRoot)
253253

254254
val leftDiffContent = if (existingFile == null) {
255255
EmptyContent()
@@ -336,7 +336,7 @@ class FeatureDevController(
336336
var pollAttempt = 0
337337
val pollDelayMs = 10L
338338
while (pollAttempt < 5) {
339-
val file = VfsUtil.findRelativeFile(message.filePath, session.context.selectedSourceFolder)
339+
val file = VfsUtil.findRelativeFile(message.filePath, session.context.addressableRoot)
340340
// Wait for the file to be created and/or updated to the new content:
341341
if (file != null && file.content() == filePaths.find { it.zipFilePath == fileToUpdate }?.fileContent) {
342342
// Open a diff, showing the changes have been applied and the file now has identical left/right state:
@@ -729,7 +729,7 @@ class FeatureDevController(
729729

730730
val codeWhispererSettings = CodeWhispererSettings.getInstance().getAutoBuildSetting()
731731
val hasDevFile = session.context.checkForDevFile()
732-
val isPromptedForAutoBuildFeature = codeWhispererSettings.containsKey(session.context.getWorkspaceRoot())
732+
val isPromptedForAutoBuildFeature = codeWhispererSettings.containsKey(session.context.workspaceRoot.path)
733733

734734
if (hasDevFile && !isPromptedForAutoBuildFeature) {
735735
promptAllowQCommandsConsent(messenger, tabId)
@@ -812,8 +812,7 @@ class FeatureDevController(
812812

813813
private suspend fun modifyDefaultSourceFolder(tabId: String) {
814814
val session = getSessionInfo(tabId)
815-
val currentSourceFolder = session.context.selectedSourceFolder
816-
val projectRoot = session.context.projectRoot
815+
val workspaceRoot = session.context.workspaceRoot
817816

818817
val modifyFolderFollowUp = FollowUp(
819818
pillText = message("amazonqFeatureDev.follow_up.modify_source_folder"),
@@ -825,7 +824,7 @@ class FeatureDevController(
825824
var reason: ModifySourceFolderErrorReason? = null
826825

827826
withContext(EDT) {
828-
val selectedFolder = selectFolder(context.project, currentSourceFolder)
827+
val selectedFolder = selectFolder(context.project, workspaceRoot)
829828
// No folder was selected
830829
if (selectedFolder == null) {
831830
logger.info { "Cancelled dialog and not selected any folder" }
@@ -840,7 +839,7 @@ class FeatureDevController(
840839
}
841840

842841
// The folder is not in the workspace
843-
if (!selectedFolder.path.startsWith(projectRoot.path)) {
842+
if (!selectedFolder.path.startsWith(workspaceRoot.path)) {
844843
logger.info { "Selected folder not in workspace: ${selectedFolder.path}" }
845844

846845
messenger.sendAnswer(
@@ -860,7 +859,7 @@ class FeatureDevController(
860859

861860
logger.info { "Selected correct folder inside workspace: ${selectedFolder.path}" }
862861

863-
session.context.selectedSourceFolder = selectedFolder
862+
session.context.selectionRoot = selectedFolder
864863
result = Result.Succeeded
865864

866865
messenger.sendAnswer(

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ class CodeGenerationState(
9696
var insertedCharacters = 0
9797
codeGenerationResult.newFiles.forEach { file ->
9898
// FIXME: Ideally, the before content should be read from the uploaded context instead of from disk, to avoid drift
99-
val before = config.repoContext.selectedSourceFolder
99+
val before = config.repoContext.workspaceRoot
100100
.toNioPath()
101101
.resolve(file.zipFilePath)
102102
.toFile()

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ class PrepareCodeGenerationState(
4949
messenger.sendAnswerPart(tabId = this.tabID, message = message("amazonqFeatureDev.chat_message.uploading_code"))
5050
messenger.sendUpdatePlaceholder(tabId = this.tabID, newPlaceholder = message("amazonqFeatureDev.chat_message.uploading_code"))
5151

52-
val isAutoBuildFeatureEnabled = CodeWhispererSettings.getInstance().isAutoBuildFeatureEnabled(this.config.repoContext.getWorkspaceRoot())
52+
val isAutoBuildFeatureEnabled = CodeWhispererSettings.getInstance().isAutoBuildFeatureEnabled(this.config.repoContext.workspaceRoot.path)
5353
val repoZipResult = config.repoContext.getProjectZip(isAutoBuildFeatureEnabled = isAutoBuildFeatureEnabled)
5454
val zipFileChecksum = repoZipResult.checksum
5555
zipFileLength = repoZipResult.contentLength

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

+5-10
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ import com.intellij.openapi.project.Project
88
import com.intellij.openapi.vfs.VfsUtil
99
import software.aws.toolkits.jetbrains.common.util.resolveAndCreateOrUpdateFile
1010
import software.aws.toolkits.jetbrains.common.util.resolveAndDeleteFile
11-
import software.aws.toolkits.jetbrains.services.amazonq.FeatureDevSessionContext
1211
import software.aws.toolkits.jetbrains.services.amazonq.messages.MessagePublisher
12+
import software.aws.toolkits.jetbrains.services.amazonq.project.FeatureDevSessionContext
1313
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.CODE_GENERATION_RETRY_LIMIT
1414
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.ConversationIdNotFoundException
1515
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.FEATURE_NAME
@@ -130,14 +130,13 @@ class Session(val tabID: String, val project: Project) {
130130
) {
131131
val newFilePaths = filePaths.filter { !it.rejected && !it.changeApplied }
132132
val newDeletedFiles = deletedFiles.filter { !it.rejected && !it.changeApplied }
133-
val selectedSourceFolder = context.selectedSourceFolder.toNioPath()
134133

135134
runCatching {
136135
var insertedLines = 0
137136
var insertedCharacters = 0
138137
filePaths.forEach { file ->
139138
// FIXME: Ideally, the before content should be read from the uploaded context instead of from disk, to avoid drift
140-
val before = selectedSourceFolder
139+
val before = context.addressableRoot.toNioPath()
141140
.resolve(file.zipFilePath)
142141
.toFile()
143142
.let { f ->
@@ -174,18 +173,16 @@ class Session(val tabID: String, val project: Project) {
174173
ReferenceLogController.addReferenceLog(references, project)
175174

176175
// Taken from https://intellij-support.jetbrains.com/hc/en-us/community/posts/206118439-Refresh-after-external-changes-to-project-structure-and-sources
177-
VfsUtil.markDirtyAndRefresh(true, true, true, context.selectedSourceFolder)
176+
VfsUtil.markDirtyAndRefresh(true, true, true, context.addressableRoot)
178177
}
179178

180179
// Suppressing because insertNewFiles needs to be a suspend function in order to be tested
181180
@Suppress("RedundantSuspendModifier")
182181
suspend fun insertNewFiles(
183182
filePaths: List<NewFileZipInfo>,
184183
) {
185-
val selectedSourceFolder = context.selectedSourceFolder.toNioPath()
186-
187184
filePaths.forEach {
188-
resolveAndCreateOrUpdateFile(selectedSourceFolder, it.zipFilePath, it.fileContent)
185+
resolveAndCreateOrUpdateFile(context.addressableRoot.toNioPath(), it.zipFilePath, it.fileContent)
189186
it.changeApplied = true
190187
}
191188
}
@@ -195,10 +192,8 @@ class Session(val tabID: String, val project: Project) {
195192
suspend fun applyDeleteFiles(
196193
deletedFiles: List<DeletedFileInfo>,
197194
) {
198-
val selectedSourceFolder = context.selectedSourceFolder.toNioPath()
199-
200195
deletedFiles.forEach {
201-
resolveAndDeleteFile(selectedSourceFolder, it.zipFilePath)
196+
resolveAndDeleteFile(context.addressableRoot.toNioPath(), it.zipFilePath)
202197
it.changeApplied = true
203198
}
204199
}

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

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

66
import com.fasterxml.jackson.annotation.JsonValue
7-
import software.aws.toolkits.jetbrains.services.amazonq.FeatureDevSessionContext
7+
import software.aws.toolkits.jetbrains.services.amazonq.project.FeatureDevSessionContext
88
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.util.CancellationTokenSource
99
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.util.FeatureDevService
1010
import software.aws.toolkits.jetbrains.services.cwc.messages.RecommendationContentSpan

0 commit comments

Comments
 (0)