Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cwspr crossfile all #4787

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ class CodeWhispererC private constructor() : CodeWhispererProgrammingLanguage()

override fun isAutoFileScanSupported(): Boolean = true

override fun isSupplementalContextSupported() = true

companion object {
const val ID = "c"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ class CodeWhispererCpp private constructor() : CodeWhispererProgrammingLanguage(

override fun isAutoFileScanSupported(): Boolean = true

override fun isSupplementalContextSupported() = true

companion object {
const val ID = "cpp"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import software.aws.toolkits.jetbrains.services.codewhisperer.language.CodeWhispererProgrammingLanguage
import software.aws.toolkits.telemetry.CodewhispererLanguage

class CodeWhispererCsharp private constructor() : CodeWhispererProgrammingLanguage() {

Check warning on line 9 in plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/language/languages/CodeWhispererCsharp.kt

View workflow job for this annotation

GitHub Actions / qodana

Extension class should be final and non-public

Extension class should not be public
override val languageId: String = ID

override fun toTelemetryType(): CodewhispererLanguage = CodewhispererLanguage.Csharp
Expand All @@ -17,7 +17,9 @@

override fun isAutoFileScanSupported(): Boolean = true

override fun isSupplementalContextSupported() = true

companion object {

Check warning on line 22 in plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/language/languages/CodeWhispererCsharp.kt

View workflow job for this annotation

GitHub Actions / qodana

Companion object in extensions

Companion objects in IDE extension implementations may only contain a logger and constants
const val ID = "csharp"

val INSTANCE = CodeWhispererCsharp()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ class CodeWhispererGo private constructor() : CodeWhispererProgrammingLanguage()

override fun isAutoFileScanSupported(): Boolean = true

override fun isSupplementalContextSupported() = true

companion object {
const val ID = "go"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ class CodeWhispererKotlin private constructor() : CodeWhispererProgrammingLangua

override fun isCodeCompletionSupported(): Boolean = true

override fun isSupplementalContextSupported() = true

companion object {
const val ID = "kotlin"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ class CodeWhispererPhp private constructor() : CodeWhispererProgrammingLanguage(

override fun isAutoFileScanSupported(): Boolean = true

override fun isSupplementalContextSupported() = true

companion object {
const val ID = "php"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ class CodeWhispererRuby private constructor() : CodeWhispererProgrammingLanguage

override fun isAutoFileScanSupported(): Boolean = true

override fun isSupplementalContextSupported() = true

companion object {
const val ID = "ruby"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ class CodeWhispererRust private constructor() : CodeWhispererProgrammingLanguage

override fun isCodeCompletionSupported(): Boolean = true

override fun isSupplementalContextSupported() = true

companion object {
const val ID = "rust"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ class CodeWhispererScala private constructor() : CodeWhispererProgrammingLanguag

override fun isCodeCompletionSupported(): Boolean = true

override fun isSupplementalContextSupported() = true

companion object {
const val ID = "scala"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ class CodeWhispererShell private constructor() : CodeWhispererProgrammingLanguag

override fun isCodeCompletionSupported(): Boolean = true

override fun isSupplementalContextSupported() = true

companion object {
const val ID = "shell"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,19 @@

fun getCustomizationArnOverride(): String = getFeatureValueForKey(CUSTOMIZATION_ARN_OVERRIDE_NAME).stringValue()

fun getCrissfileConfig(): String {
// TODO: use key instead of random
// val key = getFeatureValueForKey(CROSSFILE_KEY).stringValue()
val randomNum = Math.random()
val group = if (randomNum < 1 / 2.0) {
"control"
} else {
"experiment"
}

return group
}

// Get the feature value for the given key.
// In case of a misconfiguration, it will return a default feature value of Boolean true.
private fun getFeatureValueForKey(name: String): FeatureValue =
Expand All @@ -92,6 +105,7 @@
fun getInstance(): CodeWhispererFeatureConfigService = service()
private const val TEST_FEATURE_NAME = "testFeature"
const val CUSTOMIZATION_ARN_OVERRIDE_NAME = "customizationArnOverride"
const val CROSSFILE_KEY = "crossfile"

Check warning on line 108 in plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/service/CodeWhispererFeatureConfigService.kt

View workflow job for this annotation

GitHub Actions / qodana

Unused symbol

Property "CROSSFILE_KEY" is never used
private val LOG = getLogger<CodeWhispererFeatureConfigService>()

// TODO: add real feature later
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiFile
import com.intellij.util.concurrency.annotations.RequiresEdt
import com.intellij.util.messages.Topic
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.TimeoutCancellationException
import kotlinx.coroutines.async
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
Expand All @@ -43,7 +45,6 @@ import software.amazon.awssdk.services.codewhispererruntime.model.ThrottlingExce
import software.aws.toolkits.core.utils.debug
import software.aws.toolkits.core.utils.getLogger
import software.aws.toolkits.core.utils.info
import software.aws.toolkits.jetbrains.core.coroutines.disposableCoroutineScope
import software.aws.toolkits.jetbrains.core.coroutines.projectCoroutineScope
import software.aws.toolkits.jetbrains.core.credentials.ToolkitConnection
import software.aws.toolkits.jetbrains.core.credentials.ToolkitConnectionManager
Expand Down Expand Up @@ -91,7 +92,7 @@ import software.aws.toolkits.telemetry.CodewhispererTriggerType
import java.util.concurrent.TimeUnit

@Service
class CodeWhispererService : Disposable {
class CodeWhispererService(private val coroutineScope: CoroutineScope) : Disposable {
private val codeInsightSettingsFacade = CodeInsightsSettingsFacade()
private var refreshFailure: Int = 0

Expand Down Expand Up @@ -193,7 +194,6 @@ class CodeWhispererService : Disposable {
// from CodeWhispererPopupManager.cancelPopup() and CodeWhispererPopupManager.closePopup().
// It's possible and ok that coroutine will keep running until the next time we check it's state.
// As long as we don't show to the user extra info we are good.
val coroutineScope = disposableCoroutineScope(popup)

var states: InvocationContext? = null
var lastRecommendationIndex = -1
Expand Down Expand Up @@ -624,27 +624,29 @@ class CodeWhispererService : Disposable {
val startFetchingTimestamp = System.currentTimeMillis()
val isTstFile = FileContextProvider.getInstance(project).isTestFile(psiFile)
val supplementalContext = runBlocking {
try {
withTimeout(SUPPLEMENTAL_CONTEXT_TIMEOUT) {
FileContextProvider.getInstance(project).extractSupplementalFileContext(psiFile, fileContext)
}
} catch (e: Exception) {
if (e is TimeoutCancellationException) {
LOG.debug {
"Supplemental context fetch timed out in ${System.currentTimeMillis() - startFetchingTimestamp}ms"
coroutineScope.async {
try {
withTimeout(SUPPLEMENTAL_CONTEXT_TIMEOUT) {
FileContextProvider.getInstance(project).extractSupplementalFileContext(psiFile, fileContext)
}
} catch (e: Exception) {
if (e is TimeoutCancellationException) {
LOG.debug {
"Supplemental context fetch timed out in ${System.currentTimeMillis() - startFetchingTimestamp}ms"
}
SupplementalContextInfo(
isUtg = isTstFile,
contents = emptyList(),
latency = System.currentTimeMillis() - startFetchingTimestamp,
targetFileName = fileContext.filename,
strategy = if (isTstFile) UtgStrategy.Empty else CrossFileStrategy.Empty
)
} else {
LOG.debug { "Run into unexpected error when fetching supplemental context, error: ${e.message}" }
null
}
SupplementalContextInfo(
isUtg = isTstFile,
contents = emptyList(),
latency = System.currentTimeMillis() - startFetchingTimestamp,
targetFileName = fileContext.filename,
strategy = if (isTstFile) UtgStrategy.Empty else CrossFileStrategy.Empty
)
} else {
LOG.debug { "Run into unexpected error when fetching supplemental context, error: ${e.message}" }
null
}
}
}.await()
}

// 3. caret position
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import kotlin.reflect.KClass
/**
* Component controlling codewhisperer user group settings
*/
@Deprecated("use CodeWhispererFeatureConfigService instead")
@Service
@State(name = "codewhispererUserGroupSettings", storages = [Storage("aws.xml", roamingType = RoamingType.DISABLED)])
class CodeWhispererUserGroupSettings : PersistentStateComponent<CodeWhispererUserGroupStates> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import com.intellij.ui.JBColor
import software.amazon.awssdk.regions.Region
import software.amazon.awssdk.services.codewhispererruntime.model.AccessDeniedException
import software.amazon.awssdk.services.codewhispererruntime.model.CodeWhispererRuntimeException
import software.aws.toolkits.jetbrains.isDeveloperMode
import software.aws.toolkits.jetbrains.services.codewhisperer.language.languages.CodeWhispererJava
import software.aws.toolkits.telemetry.CodewhispererGettingStartedTask
import java.awt.Font
Expand Down Expand Up @@ -133,9 +134,14 @@ object CodeWhispererConstants {
}
}
object CrossFile {
const val CHUNK_SIZE = 60
const val NUMBER_OF_LINE_IN_CHUNK = 10
val CHUNK_SIZE
get() = if (isDeveloperMode()) 200 else 60
val NUMBER_OF_LINE_IN_CHUNK
get() = if (isDeveloperMode()) 50 else 10

// TODO: 3 -> 10 when service side CR is done
const val NUMBER_OF_CHUNK_TO_FETCH = 3
const val NEIGHBOR_FILES_DISTANCE = 1
}

object Utg {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,30 @@ import software.aws.toolkits.core.utils.debug
import software.aws.toolkits.core.utils.getLogger
import software.aws.toolkits.core.utils.info
import software.aws.toolkits.core.utils.warn
import software.aws.toolkits.jetbrains.isDeveloperMode
import software.aws.toolkits.jetbrains.services.codewhisperer.editor.CodeWhispererEditorUtil
import software.aws.toolkits.jetbrains.services.codewhisperer.language.CodeWhispererProgrammingLanguage
import software.aws.toolkits.jetbrains.services.codewhisperer.language.languages.CodeWhispererC
import software.aws.toolkits.jetbrains.services.codewhisperer.language.languages.CodeWhispererCpp
import software.aws.toolkits.jetbrains.services.codewhisperer.language.languages.CodeWhispererCsharp
import software.aws.toolkits.jetbrains.services.codewhisperer.language.languages.CodeWhispererGo
import software.aws.toolkits.jetbrains.services.codewhisperer.language.languages.CodeWhispererJava
import software.aws.toolkits.jetbrains.services.codewhisperer.language.languages.CodeWhispererJavaScript
import software.aws.toolkits.jetbrains.services.codewhisperer.language.languages.CodeWhispererJsx
import software.aws.toolkits.jetbrains.services.codewhisperer.language.languages.CodeWhispererKotlin
import software.aws.toolkits.jetbrains.services.codewhisperer.language.languages.CodeWhispererPhp
import software.aws.toolkits.jetbrains.services.codewhisperer.language.languages.CodeWhispererPython
import software.aws.toolkits.jetbrains.services.codewhisperer.language.languages.CodeWhispererRuby
import software.aws.toolkits.jetbrains.services.codewhisperer.language.languages.CodeWhispererRust
import software.aws.toolkits.jetbrains.services.codewhisperer.language.languages.CodeWhispererScala
import software.aws.toolkits.jetbrains.services.codewhisperer.language.languages.CodeWhispererShell
import software.aws.toolkits.jetbrains.services.codewhisperer.language.languages.CodeWhispererTsx
import software.aws.toolkits.jetbrains.services.codewhisperer.language.languages.CodeWhispererTypeScript
import software.aws.toolkits.jetbrains.services.codewhisperer.language.programmingLanguage
import software.aws.toolkits.jetbrains.services.codewhisperer.model.Chunk
import software.aws.toolkits.jetbrains.services.codewhisperer.model.FileContextInfo
import software.aws.toolkits.jetbrains.services.codewhisperer.model.SupplementalContextInfo
import software.aws.toolkits.jetbrains.services.codewhisperer.service.CodeWhispererFeatureConfigService
import software.aws.toolkits.jetbrains.services.codewhisperer.service.CodeWhispererUserGroup
import software.aws.toolkits.jetbrains.services.codewhisperer.service.CodeWhispererUserGroupSettings
import java.io.DataInput
Expand Down Expand Up @@ -114,7 +126,8 @@ class DefaultCodeWhispererFileContextProvider(private val project: Project) : Fi
val language = targetContext.programmingLanguage
val group = CodeWhispererUserGroupSettings.getInstance().getUserGroup()

val supplementalContext = if (isTst) {
// if utg is not supported, use crossfile context as fallback
val supplementalContext = if (isTst && language.isUTGSupported()) {
when (shouldFetchUtgContext(language, group)) {
true -> extractSupplementalFileContextForTst(psiFile, targetContext)
false -> SupplementalContextInfo.emptyUtgFileContextInfo(targetContext.filename)
Expand Down Expand Up @@ -174,13 +187,19 @@ class DefaultCodeWhispererFileContextProvider(private val project: Project) : Fi
chunks.addAll(file.toCodeChunk(relativePath))
hasUsed.add(file)
if (chunks.size > CodeWhispererConstants.CrossFile.CHUNK_SIZE) {
LOG.debug { "finish fetching ${CodeWhispererConstants.CrossFile.CHUNK_SIZE} chunks in ${System.currentTimeMillis() - parseFilesStart} ms" }
LOG.debug {
"finish fetching ${CodeWhispererConstants.CrossFile.CHUNK_SIZE} " +
"chunks in ${System.currentTimeMillis() - parseFilesStart} ms from files ${hasUsed.map { it.name }}"
}
return chunks.take(CodeWhispererConstants.CrossFile.CHUNK_SIZE)
}
}
}

LOG.debug { "finish fetching ${CodeWhispererConstants.CrossFile.CHUNK_SIZE} chunks in ${System.currentTimeMillis() - parseFilesStart} ms" }
LOG.debug {
"finish fetching ${CodeWhispererConstants.CrossFile.CHUNK_SIZE} " +
"chunks in ${System.currentTimeMillis() - parseFilesStart} ms from files ${hasUsed.map { it.name }}"
}
return chunks.take(CodeWhispererConstants.CrossFile.CHUNK_SIZE)
}

Expand All @@ -197,7 +216,18 @@ class DefaultCodeWhispererFileContextProvider(private val project: Project) : Fi

// step 1: prepare data
val first60Chunks: List<Chunk> = try {
runReadAction { codewhispererCodeChunksIndex.getFileData(psiFile) }
runReadAction {
// for dev purpose only, not using cache when it's dev mode to monitor performance
if (isDeveloperMode()) {
runBlocking {
val fileCrawler = psiFile.programmingLanguage().fileCrawler
val fileProducers = listOf<suspend (PsiFile) -> List<VirtualFile>> { psiFile -> fileCrawler.listCrossFileCandidate(psiFile) }
FileContextProvider.getInstance(psiFile.project).extractCodeChunksFromFiles(psiFile, fileProducers)
}
} else {
codewhispererCodeChunksIndex.getFileData(psiFile)
}
}
} catch (e: TimeoutCancellationException) {
throw e
}
Expand Down Expand Up @@ -299,6 +329,7 @@ class DefaultCodeWhispererFileContextProvider(private val project: Project) : Fi
}
}

@Suppress("UNUSED_PARAMETER")
fun shouldFetchCrossfileContext(language: CodeWhispererProgrammingLanguage, userGroup: CodeWhispererUserGroup): Boolean? {
if (!language.isSupplementalContextSupported()) {
return null
Expand All @@ -312,7 +343,18 @@ class DefaultCodeWhispererFileContextProvider(private val project: Project) : Fi
is CodeWhispererJsx,
is CodeWhispererTsx -> true

else -> userGroup == CodeWhispererUserGroup.CrossFile
is CodeWhispererC,
is CodeWhispererGo,
is CodeWhispererPhp,
is CodeWhispererRust,
is CodeWhispererKotlin,
is CodeWhispererCpp,
is CodeWhispererCsharp,
is CodeWhispererRuby,
is CodeWhispererShell,
is CodeWhispererScala -> CodeWhispererFeatureConfigService.getInstance().getCrissfileConfig() == "experiment"

else -> false
}
}
}
Expand Down
Loading
Loading