in plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/util/CodeWhispererFileContextProvider.kt [222:328]
suspend fun extractSupplementalFileContextForSrc(psiFile: PsiFile, targetContext: FileContextInfo): SupplementalContextInfo {
if (!targetContext.programmingLanguage.isSupplementalContextSupported()) {
return SupplementalContextInfo.emptyCrossFileContextInfo(targetContext.filename)
}
val query = generateQuery(targetContext)
val contexts = withContext(coroutineContext) {
val projectContextDeferred1 = async {
val timedCodemapContext = measureTimedValue { fetchProjectContext(query, psiFile, targetContext) }
val codemapContext = timedCodemapContext.value
LOG.debug {
buildString {
append("time elapse for fetching project context=${timedCodemapContext.duration.inWholeMilliseconds}ms; ")
append("numberOfChunks=${codemapContext.contents.size}; ")
append("totalLength=${codemapContext.contentLength}")
}
}
codemapContext
}
val openTabsContextDeferred1 = async {
val timedOpentabContext = measureTimedValue { fetchOpenTabsContext(query, psiFile, targetContext) }
val opentabContext = timedOpentabContext.value
LOG.debug {
buildString {
append("time elapse for open tabs context=${timedOpentabContext.duration.inWholeMilliseconds}ms; ")
append("numberOfChunks=${opentabContext.contents.size}; ")
append("totalLength=${opentabContext.contentLength}")
}
}
opentabContext
}
awaitAll(projectContextDeferred1, openTabsContextDeferred1)
}
val projectContext = contexts.find { it.strategy == CrossFileStrategy.Codemap }
val openTabsContext = contexts.find { it.strategy == CrossFileStrategy.OpenTabsBM25 }
/**
* We're using both codemap and opentabs context
* 1. If both are present, codemap should live in the first of supplemental context list, i.e [codemap, opentabs_0, opentabs_1...] with strategy name codemap
* 2. If only one is present, return the one present with corresponding strategy name, either codemap or opentabs
* 3. If none is present, return empty list with strategy name empty
*
* Service will throw 400 error when context length is greater than 20480, drop the last chunk until the total length fits in the cap
*/
val contextBeforeTruncation = when {
projectContext == null && openTabsContext == null -> SupplementalContextInfo.emptyCrossFileContextInfo(targetContext.filename)
projectContext != null && openTabsContext != null -> {
val context1 = projectContext.contents
val context2 = openTabsContext.contents
val mergedContext = (context1 + context2).filter { it.content.isNotEmpty() }
val strategy = if (projectContext.contentLength != 0 && openTabsContext.contentLength != 0) {
CrossFileStrategy.Codemap
} else if (projectContext.contentLength != 0) {
CrossFileStrategy.Codemap
} else if (openTabsContext.contentLength != 0) {
CrossFileStrategy.OpenTabsBM25
} else {
CrossFileStrategy.Empty
}
SupplementalContextInfo(
isUtg = false,
contents = mergedContext,
targetFileName = targetContext.filename,
strategy = strategy
)
}
projectContext != null -> {
return if (projectContext.contentLength == 0) {
SupplementalContextInfo.emptyCrossFileContextInfo(targetContext.filename)
} else {
SupplementalContextInfo(
isUtg = false,
contents = projectContext.contents,
targetFileName = targetContext.filename,
strategy = CrossFileStrategy.Codemap
)
}
}
openTabsContext != null -> {
return if (openTabsContext.contentLength == 0) {
SupplementalContextInfo.emptyCrossFileContextInfo(targetContext.filename)
} else {
SupplementalContextInfo(
isUtg = false,
contents = openTabsContext.contents,
targetFileName = targetContext.filename,
strategy = CrossFileStrategy.OpenTabsBM25
)
}
}
else -> SupplementalContextInfo.emptyCrossFileContextInfo(targetContext.filename)
}
return truncateContext(contextBeforeTruncation)
}