Skip to content

Commit c79176a

Browse files
authored
Amazon Q: Greatly reduce indexing time for at workspace feature (#4784)
* Amazon Q: Greatly reduce indexing time for at workspace feature 1. Optimize the file traversing logic to do early pruning and remove redundant checks 2. Add .venv to the ignore pattern 3. Only add files to the indexing results not directories 4. In a testing project reduced the indexing time from 4m30s to 15s 5. Also did the same optimiztaion to /dev zip file traversing. * addressing comments * detektMain and addressing comments
1 parent 723a877 commit c79176a

File tree

3 files changed

+49
-29
lines changed

3 files changed

+49
-29
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type" : "bugfix",
3+
"description" : "Amazon Q: Optimized the workspace indexing logic which makes the indexing time now only 5-10% of what it was before."
4+
}

plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/editor/context/project/ProjectContextProvider.kt

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@ import com.fasterxml.jackson.module.kotlin.readValue
1010
import com.intellij.openapi.Disposable
1111
import com.intellij.openapi.project.Project
1212
import com.intellij.openapi.project.guessProjectDir
13-
import com.intellij.openapi.vcs.changes.ChangeListManager
14-
import com.intellij.openapi.vfs.VfsUtil
13+
import com.intellij.openapi.vfs.VfsUtilCore
14+
import com.intellij.openapi.vfs.VirtualFile
15+
import com.intellij.openapi.vfs.VirtualFileVisitor
16+
import com.intellij.openapi.vfs.isFile
1517
import kotlinx.coroutines.delay
1618
import kotlinx.coroutines.launch
1719
import kotlinx.coroutines.runBlocking
@@ -155,6 +157,7 @@ class ProjectContextProvider(val project: Project, private val encoderServer: En
155157
logger.info { "project context index response code: ${connection.responseCode} for ${project.name}" }
156158
val duration = (System.currentTimeMillis() - indexStartTime).toDouble()
157159
val startUrl = getStartUrl(project)
160+
logger.debug { "project context index time: ${duration}ms" }
158161
if (connection.responseCode == 200) {
159162
val usage = getUsage()
160163
TelemetryHelper.recordIndexWorkspace(duration, filesResult.files.size, filesResult.fileSize, true, usage?.memoryUsage, usage?.cpuUsage, startUrl)
@@ -261,23 +264,36 @@ class ProjectContextProvider(val project: Project, private val encoderServer: En
261264
}
262265

263266
private fun isBuildOrBin(filePath: String): Boolean {
264-
val regex = Regex("""[/\\](bin|build|node_modules|venv|env|\.idea)[/\\]""", RegexOption.IGNORE_CASE)
267+
val regex = Regex("""[/\\](bin|build|node_modules|venv|.venv|env|\.idea|.conda)[/\\]""", RegexOption.IGNORE_CASE)
265268
return regex.find(filePath) != null
266269
}
267270

268271
private fun collectFiles(): FileCollectionResult {
269272
val collectedFiles = mutableListOf<String>()
270273
var currentTotalFileSize = 0L
271-
val changeListManager = ChangeListManager.getInstance(project)
272274
val featureDevSessionContext = FeatureDevSessionContext(project)
273-
val allFiles = project.guessProjectDir()?.let {
274-
VfsUtil.collectChildrenRecursively(it).filter { child ->
275-
!changeListManager.isIgnoredFile(child) &&
276-
!isBuildOrBin(child.path) &&
277-
runBlocking { !featureDevSessionContext.ignoreFile(child, scope) } &&
278-
child.length <= 10 * 1024 * 1024
279-
}
280-
}.orEmpty()
275+
val allFiles = mutableListOf<VirtualFile>()
276+
project.guessProjectDir()?.let {
277+
VfsUtilCore.visitChildrenRecursively(
278+
it,
279+
object : VirtualFileVisitor<Unit>(NO_FOLLOW_SYMLINKS) {
280+
// TODO: refactor this along with /dev & codescan file traversing logic
281+
override fun visitFile(file: VirtualFile): Boolean {
282+
if ((file.isDirectory && isBuildOrBin(file.name)) ||
283+
runBlocking { featureDevSessionContext.ignoreFile(file.name, scope) } ||
284+
(file.isFile && file.length > 10 * 1024 * 1024)
285+
) {
286+
return false
287+
}
288+
if (file.isFile) {
289+
allFiles.add(file)
290+
return false
291+
}
292+
return true
293+
}
294+
}
295+
)
296+
}
281297

282298
for (file in allFiles) {
283299
if (willExceedPayloadLimit(currentTotalFileSize, file.length)) {

plugins/core/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/FeatureDevSessionContext.kt

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -89,15 +89,6 @@ class FeatureDevSessionContext(val project: Project, val maxProjectSizeBytes: Lo
8989
return ZipCreationResult(zippedProject, checkSum256, zippedProject.length())
9090
}
9191

92-
private suspend fun ignoreFile(file: File, scope: CoroutineScope): Boolean = with(scope) {
93-
val deferredResults = ignorePatternsWithGitIgnore.map { pattern ->
94-
async {
95-
pattern.containsMatchIn(file.path)
96-
}
97-
}
98-
deferredResults.any { it.await() }
99-
}
100-
10192
fun isFileExtensionAllowed(file: VirtualFile): Boolean {
10293
// if it is a directory, it is allowed
10394
if (file.isDirectory) return true
@@ -110,7 +101,16 @@ class FeatureDevSessionContext(val project: Project, val maxProjectSizeBytes: Lo
110101
return !isFileExtensionAllowed(file)
111102
}
112103

113-
suspend fun ignoreFile(file: VirtualFile, scope: CoroutineScope): Boolean = ignoreFile(File(file.path), scope)
104+
suspend fun ignoreFile(file: VirtualFile, scope: CoroutineScope): Boolean = ignoreFile(file.path, scope)
105+
106+
suspend fun ignoreFile(path: String, scope: CoroutineScope): Boolean = with(scope) {
107+
val deferredResults = ignorePatternsWithGitIgnore.map { pattern ->
108+
async {
109+
pattern.containsMatchIn(path)
110+
}
111+
}
112+
deferredResults.any { it.await() }
113+
}
114114

115115
suspend fun zipFiles(projectRoot: VirtualFile): File = withContext(getCoroutineBgContext()) {
116116
val files = mutableListOf<VirtualFile>()
@@ -121,26 +121,26 @@ class FeatureDevSessionContext(val project: Project, val maxProjectSizeBytes: Lo
121121
projectRoot,
122122
object : VirtualFileVisitor<Unit>() {
123123
override fun visitFile(file: VirtualFile): Boolean {
124-
val isFileIgnoredByPattern = runBlocking { ignoreFile(file, this) }
125124
val isFileIgnoredByExtension = runBlocking { ignoreFileByExtension(file, this) }
126-
val isFileIgnored = isFileIgnoredByExtension || isFileIgnoredByPattern
127-
128125
if (isFileIgnoredByExtension) {
129126
val extension = file.extension.orEmpty()
130127
ignoredExtensionMap[extension] = (ignoredExtensionMap[extension] ?: 0) + 1
128+
return false
129+
}
130+
val isFileIgnoredByPattern = runBlocking { ignoreFile(file.name, this) }
131+
if (isFileIgnoredByPattern) {
132+
return false
131133
}
132134

133-
if (file.isFile && !isFileIgnored) {
135+
if (file.isFile) {
134136
totalSize += file.length
135137
files.add(file)
136138

137139
if (maxProjectSizeBytes != null && totalSize > maxProjectSizeBytes) {
138140
throw RepoSizeLimitError(message("amazonqFeatureDev.content_length.error_text"))
139141
}
140-
return true
141-
} else {
142-
return !isFileIgnored
143142
}
143+
return true
144144
}
145145
}
146146
)

0 commit comments

Comments
 (0)