[evaluation-plugin] LME-610 The refactoring to get rid of a big number of useless thread blocks

Merge-request: IJ-MR-179004
Merged-by: Roman Vasiliev <Roman.Vasiliev@jetbrains.com>

GitOrigin-RevId: 2469fe2d5af7f5286bf08356cc496daad690fe55
This commit is contained in:
Roman Vasiliev
2025-10-24 13:21:45 +00:00
committed by intellij-monorepo-bot
parent 2be04f6162
commit 2bca8e145b
39 changed files with 183 additions and 176 deletions
@@ -2,11 +2,11 @@
package com.intellij.cce.actions
import com.intellij.cce.core.CodeFragment
import com.intellij.cce.processor.GenerateActionsProcessor
import com.intellij.cce.processor.ActionGenerator
import com.intellij.cce.util.FileTextUtil.computeChecksum
class ActionsGenerator(private val processor: GenerateActionsProcessor) {
fun generate(code: CodeFragment): FileActions {
class ActionsGenerator(private val processor: ActionGenerator) {
suspend fun generate(code: CodeFragment): FileActions {
val actions = processor.buildActions(code)
return FileActions(code.path, computeChecksum(code.text), actions.count { it is CallFeature }, actions)
}
@@ -25,7 +25,7 @@ class FusLogsSaver(private val finalStorageDir: Path, private val allowedGroups:
require(finalStorageDir.exists())
}
override fun <T> invokeRememberingLogs(action: () -> T): T {
override suspend fun <T> invokeRememberingLogs(action: suspend () -> T): T {
val logFilter: (LogEvent) -> Boolean = if (allowedGroups != null) { log -> log.group.id in allowedGroups }
else { _ -> true }
@@ -12,8 +12,10 @@ import java.util.concurrent.CopyOnWriteArrayList
import java.util.concurrent.Executor
object MockFUSCollector {
fun <T> collectLogEvents(parentDisposable: Disposable,
action: () -> T): Pair<List<LogEvent>, T> {
suspend fun <T> collectLogEvents(
parentDisposable: Disposable,
action: suspend () -> T,
): Pair<List<LogEvent>, T> {
val mockLoggerProvider = MockStatisticsEventLoggerProvider("ML")
val llmcLoggerProvider = MockStatisticsEventLoggerProvider("LLMC")
@@ -81,7 +83,7 @@ private class MockStatisticsEventLoggerProvider(recorderId: String) : Statistics
}
}
fun <T> collectingFusLogs(logEventFilter: (LogEvent) -> Boolean, action: () -> T): Pair<T, List<LogEvent>> = Disposer.newDisposable().use { lifetime ->
suspend fun <T> collectingFusLogs(logEventFilter: (LogEvent) -> Boolean, action: suspend () -> T): Pair<T, List<LogEvent>> = Disposer.newDisposable().use { lifetime ->
val (allFusLogs, result) = MockFUSCollector.collectLogEvents(lifetime, action)
val mlFusLogs: List<LogEvent> = allFusLogs.filter { logEventFilter(it) }
result to mlFusLogs
@@ -11,7 +11,7 @@ class ActionInvokingInterpreter(private val invokersFactory: InvokersFactory,
private val filter: InterpretFilter,
private val order: InterpretationOrder) {
fun interpret(fileActions: FileActions, sessionHandler: (Session) -> Unit): List<Session> {
suspend fun interpret(fileActions: FileActions, sessionHandler: (Session) -> Unit): List<Session> {
val actionsInvoker = invokersFactory.createActionsInvoker()
val featureInvoker = invokersFactory.createFeatureInvoker()
val fileOpener = FileOpener(fileActions.path, actionsInvoker)
@@ -40,7 +40,7 @@ class ActionInvokingInterpreter(private val invokersFactory: InvokersFactory,
}
is CallFeature -> {
if (shouldCompleteToken) {
val session = featureInvoker.callFeature(action.expectedText, action.offset, action.nodeProperties, action.sessionId.id)
val session = featureInvoker.call(action.expectedText, action.offset, action.nodeProperties, action.sessionId.id)
sessions.add(session)
sessionHandler(session)
}
@@ -10,7 +10,7 @@ interface ActionsInvoker {
fun delay(seconds: Int)
fun openFile(file: String): String
fun closeFile(file: String)
fun optimiseImports(file: String)
suspend fun optimiseImports(file: String)
fun isOpen(file: String): Boolean
fun save()
fun getText(): String
@@ -10,14 +10,12 @@ import com.intellij.cce.evaluation.data.EvalDataDescription
/**
* Feature invoker that add an abstraction layer over evaluation data storage format.
*/
interface BindingFeatureInvoker : FeatureInvoker {
fun invoke(properties: TokenProperties): BoundEvalData
interface BindingFeatureInvoker : AsyncFeatureInvoker {
suspend fun invoke(properties: TokenProperties): BoundEvalData
override fun callFeature(expectedText: String, offset: Int, properties: TokenProperties, sessionId: String): Session =
override suspend fun call(expectedText: String, offset: Int, properties: TokenProperties, sessionId: String): Session =
invoke(properties).session(expectedText, offset, properties, sessionId)
override fun comparator(generated: String, expected: String): Boolean = true
infix fun <T, B : Bindable<T>> B.bind(value: T): Binding<B> = Binding.create(this, value)
}
@@ -3,8 +3,16 @@ package com.intellij.cce.interpreter
import com.intellij.cce.core.Session
import com.intellij.cce.core.TokenProperties
interface FeatureInvoker {
interface AsyncFeatureInvoker {
suspend fun call(expectedText: String, offset: Int, properties: TokenProperties, sessionId: String): Session
}
interface FeatureInvoker : AsyncFeatureInvoker {
fun callFeature(expectedText: String, offset: Int, properties: TokenProperties, sessionId: String): Session
fun comparator(generated: String, expected: String, ): Boolean
override suspend fun call(expectedText: String, offset: Int, properties: TokenProperties, sessionId: String): Session {
return callFeature(expectedText, offset, properties, sessionId)
}
}
@@ -2,5 +2,5 @@ package com.intellij.cce.interpreter
interface InvokersFactory {
fun createActionsInvoker(): ActionsInvoker
fun createFeatureInvoker(): FeatureInvoker
fun createFeatureInvoker(): AsyncFeatureInvoker
}
@@ -11,8 +11,7 @@ import com.intellij.cce.report.CardLayout
* The invocation result is easily presentable as a card in an evaluation report.
*/
interface PresentableFeatureInvoker : BindingFeatureInvoker {
// TODO suspend to get rid of runBlockingCancellable everywhere
override fun invoke(properties: TokenProperties): PresentableEvalData
override suspend fun invoke(properties: TokenProperties): PresentableEvalData
}
data class PresentableEvalData(
@@ -6,5 +6,5 @@ data class LLMJudgeResult(
)
interface LLMJudge {
fun computeLLMJudgeScoreSync(question: String, aiaResponse: String, reference: String): LLMJudgeResult
suspend fun computeLLMJudgeScoreSync(question: String, aiaResponse: String, reference: String): LLMJudgeResult
}
@@ -4,5 +4,5 @@ package com.intellij.cce.processor
import com.intellij.cce.core.CodeFragment
interface CodeFragmentProcessor {
fun process(code: CodeFragment) = Unit
suspend fun processFragment(code: CodeFragment): Unit = Unit
}
@@ -10,7 +10,7 @@ abstract class EvaluationRootProcessor : CodeFragmentProcessor {
class DefaultEvaluationRootProcessor : EvaluationRootProcessor() {
private var evaluationRoot: CodeFragment? = null
override fun process(code: CodeFragment) {
override suspend fun processFragment(code: CodeFragment) {
evaluationRoot = code
}
@@ -20,7 +20,7 @@ class DefaultEvaluationRootProcessor : EvaluationRootProcessor() {
class EvaluationRootByRangeProcessor(private val startOffset: Int, private val endOffset: Int) : EvaluationRootProcessor() {
private var evaluationRoot: CodeFragment? = null
override fun process(code: CodeFragment) {
override suspend fun processFragment(code: CodeFragment) {
evaluationRoot = CodeFragment(startOffset, endOffset - startOffset)
evaluationRoot?.path = code.path
evaluationRoot?.text = code.text
@@ -5,16 +5,32 @@ import com.intellij.cce.actions.Action
import com.intellij.cce.actions.ActionsBuilder
import com.intellij.cce.core.CodeFragment
abstract class GenerateActionsProcessor : CodeFragmentProcessor {
private lateinit var actionsBuilder: ActionsBuilder
abstract class ActionGenerator : CodeFragmentProcessor {
protected lateinit var actionsBuilder: ActionsBuilder
suspend fun buildActions(code: CodeFragment): List<Action> {
actionsBuilder = ActionsBuilder()
processFragment(code)
return actionsBuilder.build()
}
}
abstract class AsyncActionGenerator : ActionGenerator() {
open suspend fun process(code: CodeFragment): Unit = Unit
final override suspend fun processFragment(code: CodeFragment): Unit = process(code)
protected suspend fun actions(init: suspend ActionsBuilder.() -> Unit) {
init(actionsBuilder)
}
}
abstract class GenerateActionsProcessor : ActionGenerator() {
open fun process(code: CodeFragment): Unit = Unit
final override suspend fun processFragment(code: CodeFragment): Unit = process(code)
protected fun actions(init: ActionsBuilder.() -> Unit) {
init(actionsBuilder)
}
fun buildActions(code: CodeFragment): List<Action> {
actionsBuilder = ActionsBuilder()
process(code)
return actionsBuilder.build()
}
}
}
@@ -7,9 +7,10 @@ interface Progress {
fun finish() = Unit
fun isCanceled(): Boolean
fun wrapWithProgress(block: (Progress) -> Unit) {
suspend fun <T> wrapWithProgress(block: suspend (Progress) -> T): T {
start()
block(this)
val result = block(this)
finish()
return result
}
}
@@ -1,13 +1,13 @@
package com.intellij.cce.workspace.storages
interface LogsSaver {
fun <T> invokeRememberingLogs(action: () -> T): T
suspend fun <T> invokeRememberingLogs(action: suspend () -> T): T
fun save(languageName: String?, trainingPercentage: Int)
}
class NoLogsSaver : LogsSaver {
override fun <T> invokeRememberingLogs(action: () -> T): T = action()
override suspend fun <T> invokeRememberingLogs(action: suspend () -> T): T = action()
override fun save(languageName: String?, trainingPercentage: Int) = Unit
}
@@ -17,7 +17,7 @@ fun logsSaverIf(condition: Boolean, createSaver: () -> LogsSaver): LogsSaver = i
private fun makeNestingCollector(saverA: LogsSaver,
saverB: LogsSaver
) = object : LogsSaver {
override fun <T> invokeRememberingLogs(action: () -> T): T {
override suspend fun <T> invokeRememberingLogs(action: suspend () -> T): T {
return saverA.invokeRememberingLogs {
saverB.invokeRememberingLogs(action)
}
@@ -15,7 +15,7 @@ class StatLogsSaver(private val logsTemporaryStoragePath: Path, private val fina
private val formatter = SimpleDateFormat("dd_MM_yyyy")
private val sessionIds = linkedSetOf<String>()
override fun <T> invokeRememberingLogs(action: () -> T): T = action()
override suspend fun <T> invokeRememberingLogs(action: suspend () -> T): T = action()
override fun save(languageName: String?, trainingPercentage: Int) {
val logsDir = logsTemporaryStoragePath.toFile()
@@ -23,10 +23,10 @@ abstract class CodeFragmentBuilder {
}
}
protected fun findRoot(code: CodeFragment, rootProcessor: EvaluationRootProcessor): CodeFragment {
rootProcessor.process(code)
protected suspend fun findRoot(code: CodeFragment, rootProcessor: EvaluationRootProcessor): CodeFragment {
rootProcessor.processFragment(code)
return rootProcessor.getRoot() ?: throw NullRootException(code.path)
}
abstract fun build(file: VirtualFile, rootProcessor: EvaluationRootProcessor, featureName: String): CodeFragment?
abstract suspend fun build(file: VirtualFile, rootProcessor: EvaluationRootProcessor, featureName: String): CodeFragment?
}
@@ -19,7 +19,7 @@ open class CodeFragmentFromPsiBuilder(private val project: Project, val language
open fun getVisitors(): List<EvaluationVisitor> = EvaluationVisitor.EP_NAME.extensionList
override fun build(file: VirtualFile, rootProcessor: EvaluationRootProcessor, featureName: String): CodeFragment? {
override suspend fun build(file: VirtualFile, rootProcessor: EvaluationRootProcessor, featureName: String): CodeFragment? {
val psiFile = dumbService.runReadActionInSmartMode<PsiFile?> {
PsiManager.getInstance(project).findFile(file)
}
@@ -8,7 +8,7 @@ import com.intellij.cce.util.text
import com.intellij.openapi.vfs.VirtualFile
class CodeFragmentFromTextBuilder : CodeFragmentBuilder() {
override fun build(file: VirtualFile, rootProcessor: EvaluationRootProcessor, featureName: String): CodeFragment {
override suspend fun build(file: VirtualFile, rootProcessor: EvaluationRootProcessor, featureName: String): CodeFragment {
val text = file.text()
val codeFragment = CodeFragment(0, text.length)
codeFragment.text = text
@@ -20,12 +20,12 @@ import com.intellij.cce.evaluation.FinishEvaluationStep
import com.intellij.cce.evaluation.allPreliminarySteps
import com.intellij.cce.evaluation.step.ReportGenerationStep
import com.intellij.cce.evaluation.step.SetupStatsCollectorStep
import com.intellij.cce.evaluation.step.runInIntellij
import com.intellij.cce.evaluation.step.run
import com.intellij.cce.util.ExceptionsUtil.stackTraceToString
import com.intellij.cce.workspace.Config
import com.intellij.cce.workspace.ConfigFactory
import com.intellij.cce.workspace.EvaluationWorkspace
import com.intellij.openapi.application.ApplicationStarter
import com.intellij.openapi.application.ModernApplicationStarter
import com.intellij.openapi.application.ex.ApplicationEx.FORCE_EXIT
import com.intellij.openapi.application.ex.ApplicationManagerEx
import com.intellij.openapi.diagnostic.currentClassLogger
@@ -35,12 +35,9 @@ import kotlin.io.path.exists
import kotlin.io.path.isDirectory
import kotlin.system.exitProcess
internal class CompletionEvaluationStarter : ApplicationStarter {
override val requiredModality: Int
get() = ApplicationStarter.NOT_IN_EDT
override fun main(args: List<String>) {
fun run() = MainEvaluationCommand("ml-evaluate")
internal class CompletionEvaluationStarter : ModernApplicationStarter() {
override suspend fun start(args: List<String>) {
val command = MainEvaluationCommand("ml-evaluate")
.subcommands(
FullCommand(),
GenerateActionsCommand(),
@@ -55,11 +52,16 @@ internal class CompletionEvaluationStarter : ApplicationStarter {
it.extend(command)
}
}
.main(args.toList().subList(1, args.size))
val startTimestamp = System.currentTimeMillis()
try {
run()
command.main(args.toList().subList(1, args.size))
for (subcommand in command.registeredSubcommands()) {
if (subcommand is EvaluationCommand) {
subcommand.postponedRun()
}
}
val delta = 5_000 - (System.currentTimeMillis() - startTimestamp)
if (delta > 0) {
Thread.sleep(delta) // for graceful shutdown
@@ -78,6 +80,8 @@ internal class CompletionEvaluationStarter : ApplicationStarter {
protected val featureName by argument(name = "Feature name").default("rename")
private var postponed: Boolean = false
protected fun <T : EvaluationStrategy> loadConfig(configPath: Path, strategySerializer: StrategySerializer<T>): Config {
try {
println("Load config: $configPath")
@@ -91,25 +95,40 @@ internal class CompletionEvaluationStarter : ApplicationStarter {
}
}
protected fun runPreliminarySteps(feature: EvaluableFeature<*>, workspace: EvaluationWorkspace) {
abstract suspend fun asyncRun()
final override fun run() {
// Clikt doesn't support async execution. And there is no way to get the selected command.
// So in this blocking method we only save the intention to run the command.
// Actual execution should be performed later with the postponedRun method.
postponed = true
}
suspend fun postponedRun() {
if (postponed) {
asyncRun()
}
}
protected suspend fun runPreliminarySteps(feature: EvaluableFeature<*>, workspace: EvaluationWorkspace) {
for (step in allPreliminarySteps(feature)) {
println("Starting preliminary step: ${step.name}")
step.runInIntellij(null, workspace)
step.run(workspace)
}
}
}
class MainEvaluationCommand(name: String) : EvaluationCommand(name, "Evaluate code completion quality in headless mode") {
override fun run() = Unit
override suspend fun asyncRun() = Unit
}
abstract class EvaluationCommandBase(name: String, help: String) : EvaluationCommand(name, help) {
private val configPath by argument(name = "config-path", help = "Path to config").default(ConfigFactory.DEFAULT_CONFIG_NAME)
override fun run() {
override suspend fun asyncRun() {
val feature = EvaluableFeature.forFeature(featureName) ?: throw Exception("No support for the $featureName")
val config = loadConfig(Paths.get(configPath), feature.getStrategySerializer())
val workspace = EvaluationWorkspace.create(config, SetupStatsCollectorStep.statsCollectorLogsDirectory)
val workspace = EvaluationWorkspace.create(config, SetupStatsCollectorStep.statsCollectorLogsDirectory, debug = true)
val datasetContext = DatasetContext(workspace, workspace, configPath)
runPreliminarySteps(feature, workspace)
feature.prepareEnvironment(config, workspace).use { environment ->
@@ -147,7 +166,7 @@ internal class CompletionEvaluationStarter : ApplicationStarter {
private val generateReport by option(names = arrayOf("--generate-report", "-r"), help = "Generate report").flag()
private val reorderElements by option(names = arrayOf("--reorder-elements", "-e"), help = "Reorder elements").flag()
override fun run() {
override suspend fun asyncRun() {
val feature = EvaluableFeature.forFeature(featureName) ?: throw Exception("No support for the feature")
val workspace = EvaluationWorkspace.open(workspacePath, SetupStatsCollectorStep.statsCollectorLogsDirectory)
val datasetContext = DatasetContext(workspace, workspace, null)
@@ -170,7 +189,7 @@ internal class CompletionEvaluationStarter : ApplicationStarter {
abstract fun getWorkspaces(): List<String>
override fun run() {
override suspend fun asyncRun() {
val workspacesToCompare = getWorkspaces()
val feature = EvaluableFeature.forFeature(featureName) ?: throw Exception("No support for the feature")
val config = workspacesToCompare.map { EvaluationWorkspace.open(it, SetupStatsCollectorStep.statsCollectorLogsDirectory) }.buildMultipleEvaluationsConfig(
@@ -207,7 +226,7 @@ internal class CompletionEvaluationStarter : ApplicationStarter {
help = "Generate merged report for all evaluation workspaces in a directory") {
private val root by argument(name = "directory", help = "Root directory for evaluation workspaces")
override fun run() {
override suspend fun asyncRun() {
val workspacesToMerge = readWorkspacesFromDirectory(root)
val feature = EvaluableFeature.forFeature(featureName) ?: throw Exception("No support for the feature")
val config = workspacesToMerge.map { EvaluationWorkspace.open(it, SetupStatsCollectorStep.statsCollectorLogsDirectory) }.buildMultipleEvaluationsConfig(
@@ -234,7 +253,7 @@ internal class CompletionEvaluationStarter : ApplicationStarter {
feature,
)
step.runInIntellij(null, outputWorkspace)
step.run(outputWorkspace)
}
/**
@@ -18,7 +18,8 @@ import com.intellij.cce.filter.EvaluationFilter
import com.intellij.cce.filter.EvaluationFilterReader
import com.intellij.cce.interpreter.FeatureInvoker
import com.intellij.cce.metric.Metric
import com.intellij.cce.processor.GenerateActionsProcessor
import com.intellij.cce.processor.ActionGenerator
import com.intellij.cce.processor.AsyncActionGenerator
import com.intellij.cce.report.GeneratorDirectories
import com.intellij.cce.report.MultiLineFileReportGenerator
import com.intellij.cce.util.FilesHelper
@@ -29,6 +30,7 @@ import com.intellij.cce.workspace.ConfigFactory
import com.intellij.cce.workspace.EvaluationWorkspace
import com.intellij.cce.workspace.storages.FeaturesStorage
import com.intellij.cce.workspace.storages.FullLineLogsStorage
import com.intellij.openapi.application.readAction
import com.intellij.openapi.application.runInEdt
import com.intellij.openapi.application.runReadAction
import com.intellij.openapi.editor.Document
@@ -52,7 +54,7 @@ internal class ContextCollectionEvaluationCommand : CompletionEvaluationStarter.
help = "Path to config"
).default(ConfigFactory.DEFAULT_CONFIG_NAME)
override fun run() {
override suspend fun asyncRun() {
val feature = EvaluableFeature.forFeature(featureName) ?: error("There is no support for the $featureName")
val config = loadConfig(Paths.get(configPath), feature.getStrategySerializer())
val workspace = EvaluationWorkspace.create(config, SetupStatsCollectorStep.statsCollectorLogsDirectory)
@@ -73,10 +75,10 @@ internal class ContextCollectionEvaluationCommand : CompletionEvaluationStarter.
feature.name,
environment.featureInvoker
) {
override fun prepareDataset(datasetContext: DatasetContext, progress: Progress) {
val files = runReadAction {
FilesHelper.getFilesOfLanguage(project, actions.evaluationRoots, actions.ignoreFileNames, actions.language)
}.sortedBy { it.name }
override suspend fun prepareDataset(datasetContext: DatasetContext, progress: Progress) {
val files = readAction {
FilesHelper.getFilesOfLanguage(project, actions.evaluationRoots, actions.ignoreFileNames, actions.language).sortedBy { it.name }
}
val strategy = config.strategy as CompletionContextCollectionStrategy
val sampled = files.shuffled(Random(strategy.samplingSeed)).take(strategy.samplesCount).sortedBy { it.name }
generateActions(datasetContext, actions.language, sampled, evaluationRootInfo, progress)
@@ -142,12 +144,12 @@ private class ContextCollectionActionsInvoker(
}
}
private class ContextCollectionMultiLineProcessor(private val strategy: CompletionContextCollectionStrategy) : GenerateActionsProcessor() {
override fun process(code: CodeFragment) {
private class ContextCollectionMultiLineProcessor(private val strategy: CompletionContextCollectionStrategy) : AsyncActionGenerator() {
override suspend fun process(code: CodeFragment) {
actions {
runReadAction {
readAction {
check(code is CodeFragmentWithPsi)
val file = code.psi.dereference() as? PsiFile ?: return@runReadAction
val file = code.psi.dereference() as? PsiFile ?: return@readAction
val project = file.project
val document = PsiDocumentManager.getInstance(project).getDocument(file)
checkNotNull(document) { "There should've been a document instance for $file (${file.virtualFile})" }
@@ -278,7 +280,7 @@ private class ContextCollectionStrategySerializer : StrategySerializer<Completio
}
internal class ContextCollectionFeature : EvaluableFeatureBase<CompletionContextCollectionStrategy>("completion-context") {
override fun getGenerateActionsProcessor(strategy: CompletionContextCollectionStrategy, project: Project): GenerateActionsProcessor {
override fun getGenerateActionsProcessor(strategy: CompletionContextCollectionStrategy, project: Project): ActionGenerator {
return ContextCollectionMultiLineProcessor(strategy)
}
@@ -12,12 +12,12 @@ class CsvEnvironment(
override val datasetRef: DatasetRef,
private val chunkSize: Int,
private val targetField: String,
private val featureInvoker: FeatureInvoker,
private val featureInvoker: AsyncFeatureInvoker,
) : SimpleFileEnvironment {
override val preparationDescription: String = "Checking that CSV file exists"
override fun initialize(datasetContext: DatasetContext) {
override suspend fun initialize(datasetContext: DatasetContext) {
super.initialize(datasetContext)
val datasetPath = datasetContext.path(datasetRef)
@@ -49,7 +49,7 @@ class CsvEnvironment(
val (target, features) = props.value
val call = callFeature(target, props.offset, features)
ChunkHelper.Result(
featureInvoker.callFeature(call.expectedText, call.offset, call.nodeProperties, call.sessionId.id),
featureInvoker.call(call.expectedText, call.offset, call.nodeProperties, call.sessionId.id),
"$target <- ${features.toList().joinToString(", ") { "${it.first} = ${it.second}" }}",
call
)
@@ -11,7 +11,7 @@ sealed interface DatasetRef {
val name: String
fun prepare(datasetContext: DatasetContext)
suspend fun prepare(datasetContext: DatasetContext)
fun resultPath(datasetContext: DatasetContext): Path = datasetContext.path(name)
@@ -58,7 +58,7 @@ sealed interface DatasetRef {
internal data class AbsoluteRef(val relativePath: String) : DatasetRef {
override val name: String = Path.of(relativePath).name.toString()
override fun prepare(datasetContext: DatasetContext) {
override suspend fun prepare(datasetContext: DatasetContext) {
val path = resultPath(datasetContext)
check(path.exists()) {
"Path ${relativePath} doesn't exist: ${path.absolutePathString()}"
@@ -71,7 +71,7 @@ internal data class AbsoluteRef(val relativePath: String) : DatasetRef {
internal data class ConfigRelativeRef(val relativePath: String) : DatasetRef {
override val name: String = Path.of(relativePath).normalize().toString()
override fun prepare(datasetContext: DatasetContext) {
override suspend fun prepare(datasetContext: DatasetContext) {
val path = resultPath(datasetContext)
check(path.exists()) {
@@ -90,7 +90,7 @@ internal data class ConfigRelativeRef(val relativePath: String) : DatasetRef {
}
internal data class ExistingRef(override val name: String) : DatasetRef {
override fun prepare(datasetContext: DatasetContext) {
override suspend fun prepare(datasetContext: DatasetContext) {
val path = datasetContext.path(name)
check(path.exists()) {
@@ -112,7 +112,7 @@ internal data class RemoteFileRef(private val url: String) : DatasetRef {
}
}
override fun prepare(datasetContext: DatasetContext) {
override suspend fun prepare(datasetContext: DatasetContext) {
val path = datasetContext.path(name)
if (path.exists()) {
@@ -145,7 +145,7 @@ internal data class RemoteFileRef(private val url: String) : DatasetRef {
}
internal data class AiPlatformFileRef(override val name: String): DatasetRef {
override fun prepare(datasetContext: DatasetContext) { }
override suspend fun prepare(datasetContext: DatasetContext) { }
}
private val LOG = fileLogger()
@@ -4,15 +4,11 @@ package com.intellij.cce.actions
import com.intellij.cce.core.*
import com.intellij.cce.evaluable.EvaluationStrategy
import com.intellij.cce.evaluable.common.CommonActionsInvoker
import com.intellij.cce.evaluation.EvaluationChunk
import com.intellij.cce.evaluation.EvaluationEnvironment
import com.intellij.cce.evaluation.EvaluationRootInfo
import com.intellij.cce.evaluation.EvaluationStep
import com.intellij.cce.evaluation.step.runInIntellij
import com.intellij.cce.evaluation.*
import com.intellij.cce.interpreter.*
import com.intellij.cce.processor.ActionGenerator
import com.intellij.cce.processor.DefaultEvaluationRootProcessor
import com.intellij.cce.processor.EvaluationRootByRangeProcessor
import com.intellij.cce.processor.GenerateActionsProcessor
import com.intellij.cce.util.ExceptionsUtil.stackTraceToString
import com.intellij.cce.util.FilesHelper
import com.intellij.cce.util.Progress
@@ -20,12 +16,11 @@ import com.intellij.cce.util.Summary
import com.intellij.cce.util.text
import com.intellij.cce.visitor.CodeFragmentBuilder
import com.intellij.cce.workspace.Config
import com.intellij.cce.workspace.EvaluationWorkspace
import com.intellij.cce.workspace.info.FileErrorInfo
import com.intellij.cce.workspace.storages.storage.ActionsSingleFileStorage
import com.intellij.configurationStore.StoreUtil.saveSettings
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.ReadAction
import com.intellij.openapi.application.readAction
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
@@ -39,10 +34,10 @@ open class ProjectActionsEnvironment(
private val sessionsLimit: Int?,
private val evaluationRootInfo: EvaluationRootInfo,
val project: Project,
val processor: GenerateActionsProcessor,
val processor: ActionGenerator,
override val setupSteps: List<EvaluationStep>,
private val featureName: String,
val featureInvoker: FeatureInvoker,
val featureInvoker: AsyncFeatureInvoker,
) : EvaluationEnvironment {
private val datasetRef = config.sourceFile.run {
val sf = this ?: ""
@@ -56,17 +51,17 @@ open class ProjectActionsEnvironment(
override val preparationDescription: String = "Generating actions by selected files"
override fun initialize(datasetContext: DatasetContext) {
override suspend fun initialize(datasetContext: DatasetContext) {
datasetRef?.prepare(datasetContext)
}
override fun prepareDataset(datasetContext: DatasetContext, progress: Progress) {
override suspend fun prepareDataset(datasetContext: DatasetContext, progress: Progress) {
if (datasetRef != null) {
val finalPath = DatasetRefConverter().convert(datasetRef, datasetContext, project) ?: datasetContext.path(datasetRef)
datasetContext.replaceActionsStorage(ActionsSingleFileStorage(finalPath))
}
else {
val filesForEvaluation = ReadAction.compute<List<VirtualFile>, Throwable> {
val filesForEvaluation = readAction {
FilesHelper.getFilesOfLanguage(project, config.evaluationRoots, config.ignoreFileNames, config.language)
}
@@ -99,7 +94,7 @@ open class ProjectActionsEnvironment(
}
}
protected fun generateActions(
protected suspend fun generateActions(
datasetContext: DatasetContext,
languageName: String?,
files: Collection<VirtualFile>,
@@ -171,9 +166,6 @@ open class ProjectActionsEnvironment(
actionsSummarizer.save(datasetContext)
}
override fun execute(step: EvaluationStep, workspace: EvaluationWorkspace): EvaluationWorkspace? =
step.runInIntellij(project, workspace)
override fun close() {
ProjectOpeningUtils.closeProject(project)
@@ -248,7 +240,7 @@ open class ProjectActionsEnvironment(
override val name: String = fileActions.path
override val sessionsExist: Boolean = fileActions.sessionsCount > 0
override fun evaluate(
override suspend fun evaluate(
handler: InterpretationHandler,
filter: InterpretFilter,
order: InterpretationOrder,
@@ -256,7 +248,7 @@ open class ProjectActionsEnvironment(
): EvaluationChunk.Result {
val factory = object : InvokersFactory {
override fun createActionsInvoker(): ActionsInvoker = CommonActionsInvoker(project)
override fun createFeatureInvoker(): FeatureInvoker = featureInvoker
override fun createFeatureInvoker(): AsyncFeatureInvoker = featureInvoker
}
val actionInterpreter = ActionInvokingInterpreter(factory, handler, filter, order)
return EvaluationChunk.Result(
@@ -28,14 +28,14 @@ class ChunkHelper(
fun <T> chunks(
chunkSize: Int,
entities: Sequence<T>,
evaluate: ChunkHelper.(Props<T>) -> Result,
evaluate: suspend ChunkHelper.(Props<T>) -> Result,
): Sequence<EvaluationChunk> {
return entities.chunked(chunkSize).mapIndexed { index, values ->
object : EvaluationChunk {
override val datasetName: String = this@ChunkHelper.datasetName
override val name: String = "${chunkNamePrefix}:${chunkSize * index}-${chunkSize * index + values.size - 1}"
override fun evaluate(
override suspend fun evaluate(
handler: InterpretationHandler,
filter: InterpretFilter,
order: InterpretationOrder,
@@ -81,7 +81,7 @@ class ChunkHelper(
layoutManager: LayoutManager,
chunkSize: Int,
entities: Sequence<T>,
evaluate: ChunkHelper.(Props<T>) -> PresentableResult,
evaluate: suspend ChunkHelper.(Props<T>) -> PresentableResult,
): Sequence<EvaluationChunk> {
return chunks(chunkSize, entities) { props ->
var call: CallFeature? = null
@@ -111,7 +111,7 @@ class ChunkHelper(
fun presentableChunk(
layoutManager: LayoutManager,
evaluate: ChunkHelper.(Props<Unit>) -> PresentableResult
evaluate: suspend ChunkHelper.(Props<Unit>) -> PresentableResult
): Sequence<EvaluationChunk> = presentableChunks(layoutManager, 1, sequenceOf(Unit)) { evaluate(it) }
fun callFeature(target: String, offset: Int, features: Map<String, String>): CallFeature {
@@ -10,8 +10,8 @@ import com.intellij.cce.evaluation.SetupSdkPreferences
import com.intellij.cce.evaluation.SetupSdkStepFactory
import com.intellij.cce.evaluation.step.CheckProjectSdkStep
import com.intellij.cce.evaluation.step.DropProjectSdkStep
import com.intellij.cce.interpreter.FeatureInvoker
import com.intellij.cce.processor.GenerateActionsProcessor
import com.intellij.cce.interpreter.AsyncFeatureInvoker
import com.intellij.cce.processor.ActionGenerator
import com.intellij.cce.report.BasicFileReportGenerator
import com.intellij.cce.report.FileReportGenerator
import com.intellij.cce.report.GeneratorDirectories
@@ -30,12 +30,12 @@ abstract class EvaluableFeatureBase<T : EvaluationStrategy>(override val name: S
/**
* how to prepare the context before the feature invocation
*/
abstract fun getGenerateActionsProcessor(strategy: T, project: Project): GenerateActionsProcessor
abstract fun getGenerateActionsProcessor(strategy: T, project: Project): ActionGenerator
/**
* how to call the feature
*/
abstract fun getFeatureInvoker(project: Project, language: Language, strategy: T): FeatureInvoker
abstract fun getFeatureInvoker(project: Project, language: Language, strategy: T): AsyncFeatureInvoker
abstract fun getEvaluationSteps(language: Language, strategy: T): List<EvaluationStep>
@@ -12,7 +12,6 @@ import com.intellij.cce.report.FileReportGenerator
import com.intellij.cce.report.GeneratorDirectories
import com.intellij.cce.workspace.Config
import com.intellij.cce.workspace.EvaluationWorkspace
import com.intellij.openapi.progress.runBlockingCancellable
import java.util.concurrent.atomic.AtomicReference
/**
@@ -56,11 +55,9 @@ class LayoutManager(
) {
private val globalLayout = AtomicReference(workspace.layout)
fun processData(f: suspend () -> PresentableEvalData): PresentableEvalData {
val data = runBlockingCancellable {
PresentableEvalData.augment(augmenters) {
f()
}
suspend fun processData(f: suspend () -> PresentableEvalData): PresentableEvalData {
val data = PresentableEvalData.augment(augmenters) {
f()
}
try {
processData(data)
@@ -16,7 +16,6 @@ import com.intellij.openapi.editor.impl.TrailingSpacesStripper
import com.intellij.openapi.fileEditor.FileDocumentManager
import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.openapi.fileEditor.OpenFileDescriptor
import com.intellij.openapi.progress.runBlockingCancellable
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.text.StringUtil
import com.intellij.openapi.vfs.LocalFileSystem
@@ -29,6 +28,7 @@ import com.intellij.util.progress.sleepCancellable
import java.io.File
import java.nio.file.Paths
// TODO rewrite without blocking
class CommonActionsInvoker(private val project: Project) : ActionsInvoker {
init {
TestModeFlags.set(CompletionAutoPopupHandler.ourTestingAutopopup, true)
@@ -126,10 +126,10 @@ class CommonActionsInvoker(private val project: Project) : ActionsInvoker {
FileEditorManager.getInstance(project).closeFile(virtualFile)
}
override fun optimiseImports(file: String) {
override suspend fun optimiseImports(file: String) {
LOG.info("Optimise imports in file: $file")
val virtualFile = LocalFileSystem.getInstance().findFileByIoFile(File(fullPath(file)))!!
return runBlockingCancellable { ImportOptimiser.optimiseImports(project, virtualFile) }
return ImportOptimiser.optimiseImports(project, virtualFile)
}
override fun isOpen(file: String): Boolean = readActionInSmartMode(project) {
@@ -36,7 +36,7 @@ class ConflictEnvironment(
override val name: String = "${fileConflict.hash} - ${fileConflict.fileName}"
override val datasetName: String = datasetRef.name
override fun evaluate(
override suspend fun evaluate(
handler: InterpretationHandler,
filter: InterpretFilter, // TODO should we use it somehow?
order: InterpretationOrder, // TODO should we use somehow?
@@ -33,7 +33,7 @@ class ActionsInterpretationHandler(
logsSaverIf(config.interpret.saveFusLogs) { workspace.fusLogsSaver }
).asCompositeLogsSaver()
fun invoke(environment: EvaluationEnvironment, workspace: EvaluationWorkspace, indicator: Progress) {
suspend fun invoke(environment: EvaluationEnvironment, workspace: EvaluationWorkspace, indicator: Progress) {
var sessionsCount: Int
val computingTime = measureTimeMillis {
sessionsCount = environment.sessionCount(datasetContext)
@@ -14,7 +14,7 @@ interface EvaluationChunk {
val sessionsExist: Boolean get() = true
fun evaluate(
suspend fun evaluate(
handler: InterpretationHandler,
filter: InterpretFilter,
order: InterpretationOrder,
@@ -4,9 +4,7 @@ import com.intellij.cce.actions.DatasetContext
import com.intellij.cce.actions.DatasetRef
import com.intellij.cce.evaluation.data.Bindable
import com.intellij.cce.evaluation.data.Binding
import com.intellij.cce.evaluation.step.runInIntellij
import com.intellij.cce.util.Progress
import com.intellij.cce.workspace.EvaluationWorkspace
/**
* Environment represents resources needed for an evaluation.
@@ -19,17 +17,15 @@ interface EvaluationEnvironment : AutoCloseable {
val preparationDescription: String
fun initialize(datasetContext: DatasetContext)
suspend fun initialize(datasetContext: DatasetContext)
fun prepareDataset(datasetContext: DatasetContext, progress: Progress)
suspend fun prepareDataset(datasetContext: DatasetContext, progress: Progress)
fun sessionCount(datasetContext: DatasetContext): Int
// TODO should return something closeable for large files
fun chunks(datasetContext: DatasetContext): Sequence<EvaluationChunk>
fun execute(step: EvaluationStep, workspace: EvaluationWorkspace): EvaluationWorkspace?
// place here just for convenience, should be final protected by meaning
infix fun <T, B : Bindable<T>> B.bind(value: T): Binding<B> = Binding.create(this, value)
}
@@ -42,16 +38,13 @@ interface SimpleFileEnvironment : EvaluationEnvironment {
override val preparationDescription: String get() = "Checking that dataset file is available"
override fun initialize(datasetContext: DatasetContext) {
override suspend fun initialize(datasetContext: DatasetContext) {
datasetRef.prepare(datasetContext)
}
override fun prepareDataset(datasetContext: DatasetContext, progress: Progress) {
override suspend fun prepareDataset(datasetContext: DatasetContext, progress: Progress) {
}
override fun execute(step: EvaluationStep, workspace: EvaluationWorkspace): EvaluationWorkspace? =
step.runInIntellij(null, workspace)
override fun close() {
}
}
@@ -1,25 +1,25 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.cce.evaluation
import com.intellij.cce.evaluation.step.run
import com.intellij.cce.workspace.EvaluationWorkspace
import com.intellij.openapi.application.ApplicationManager
import kotlin.system.measureTimeMillis
class EvaluationProcess private constructor (
private val environment: EvaluationEnvironment,
private val steps: List<EvaluationStep>,
private val finalStep: FinishEvaluationStep?
) {
companion object {
fun build(environment: EvaluationEnvironment, stepFactory: StepFactory, init: Builder.() -> Unit): EvaluationProcess {
suspend fun build(environment: EvaluationEnvironment, stepFactory: StepFactory, init: Builder.() -> Unit): EvaluationProcess {
environment.initialize(stepFactory.datasetContext)
val builder = Builder()
builder.init()
return builder.build(environment, stepFactory)
return builder.build(stepFactory)
}
}
fun start(workspace: EvaluationWorkspace): EvaluationWorkspace {
suspend fun start(workspace: EvaluationWorkspace): EvaluationWorkspace {
val stats = mutableMapOf<String, Long>()
var currentWorkspace = workspace
var hasError = false
@@ -27,7 +27,7 @@ class EvaluationProcess private constructor (
if (hasError && step !is UndoableEvaluationStep.UndoStep) continue
println("Starting step: ${step.name} (${step.description})")
val duration = measureTimeMillis {
val result = environment.execute(step, currentWorkspace)
val result = step.run(currentWorkspace)
if (result == null) {
hasError = true
} else {
@@ -47,7 +47,7 @@ class EvaluationProcess private constructor (
var shouldGenerateReports: Boolean = false
var shouldReorderElements: Boolean = false
fun build(environment: EvaluationEnvironment, factory: StepFactory): EvaluationProcess {
fun build(factory: StepFactory): EvaluationProcess {
val steps = mutableListOf<EvaluationStep>()
val isTestingEnvironment = ApplicationManager.getApplication().isUnitTestMode
@@ -92,7 +92,7 @@ class EvaluationProcess private constructor (
steps.add(step.undoStep())
}
return EvaluationProcess(environment, steps, factory.finishEvaluationStep().takeIf { !isTestingEnvironment })
return EvaluationProcess(steps, factory.finishEvaluationStep().takeIf { !isTestingEnvironment })
}
}
}
@@ -18,7 +18,7 @@ class ActionsInterpretationStep(
override val description: String = "Interpretation of generated actions"
override fun runInBackground(workspace: EvaluationWorkspace, progress: Progress): EvaluationWorkspace {
override suspend fun runInBackground(workspace: EvaluationWorkspace, progress: Progress): EvaluationWorkspace {
val resultWorkspace =
if (newWorkspace) EvaluationWorkspace.create(config, SetupStatsCollectorStep.statsCollectorLogsDirectory)
else workspace
@@ -9,41 +9,29 @@ import com.intellij.cce.util.Progress
import com.intellij.cce.util.TeamcityProgress
import com.intellij.cce.util.isUnderTeamCity
import com.intellij.cce.workspace.EvaluationWorkspace
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.progress.Task
import com.intellij.openapi.progress.impl.BackgroundableProcessIndicator
import com.intellij.openapi.project.Project
import com.intellij.util.concurrency.FutureResult
import kotlin.coroutines.cancellation.CancellationException
interface BackgroundEvaluationStep : EvaluationStep {
fun runInBackground(workspace: EvaluationWorkspace, progress: Progress): EvaluationWorkspace
suspend fun runInBackground(workspace: EvaluationWorkspace, progress: Progress): EvaluationWorkspace
}
fun EvaluationStep.runInIntellij(project: Project?, workspace: EvaluationWorkspace): EvaluationWorkspace? {
suspend fun EvaluationStep.run(workspace: EvaluationWorkspace): EvaluationWorkspace? {
return when (this) {
is ForegroundEvaluationStep -> start(workspace)
is BackgroundEvaluationStep -> {
val result = FutureResult<EvaluationWorkspace?>()
val task = object : Task.Backgroundable(project, name, true) {
override fun run(indicator: ProgressIndicator) {
createProgress(title).wrapWithProgress {
result.set(runInBackground(workspace, it))
}
createProgress(name).wrapWithProgress {
try {
runInBackground(workspace, it)
}
override fun onCancel() {
evaluationAbortedHandler.onCancel(this.title)
result.set(null)
catch (e: CancellationException) {
evaluationAbortedHandler.onCancel(name)
throw e
}
override fun onThrowable(error: Throwable) {
evaluationAbortedHandler.onError(error, this.title)
result.set(null)
catch (e: Throwable) {
evaluationAbortedHandler.onError(e, name)
null
}
}
ProgressManager.getInstance().runProcessWithProgressAsynchronously(task, BackgroundableProcessIndicator(task))
return result.get()
}
else -> throw IllegalStateException("Unexpected type of `$this`")
}
@@ -11,7 +11,7 @@ abstract class CreateWorkspaceStep(
private val handler: TwoWorkspaceHandler
) : BackgroundEvaluationStep {
override fun runInBackground(workspace: EvaluationWorkspace, progress: Progress): EvaluationWorkspace {
override suspend fun runInBackground(workspace: EvaluationWorkspace, progress: Progress): EvaluationWorkspace {
val newWorkspace = EvaluationWorkspace.create(config, SetupStatsCollectorStep.statsCollectorLogsDirectory)
handler.invoke(workspace, newWorkspace, progress)
return newWorkspace
@@ -13,7 +13,7 @@ class DatasetPreparationStep(
override val description: String = environment.preparationDescription
override fun runInBackground(workspace: EvaluationWorkspace, progress: Progress): EvaluationWorkspace {
override suspend fun runInBackground(workspace: EvaluationWorkspace, progress: Progress): EvaluationWorkspace {
environment.prepareDataset(datasetContext, progress)
return workspace
}
@@ -43,7 +43,7 @@ class ReportGenerationStep<T : EvaluationStrategy>(
else emptyList()
private val sessionLookupFilter: SessionLookupsFilter = SessionLookupsFilter(lookupFilters)
override fun runInBackground(workspace: EvaluationWorkspace, progress: Progress): EvaluationWorkspace {
override suspend fun runInBackground(workspace: EvaluationWorkspace, progress: Progress): EvaluationWorkspace {
val workspaces = inputWorkspaces ?: listOf(workspace)
val configs = workspaces.map { it.readConfig(feature.getStrategySerializer()) }
val evaluationTitles = configs.map { it.reports.evaluationTitle }
@@ -1,6 +1,5 @@
package com.intellij.cce.util
import com.intellij.openapi.progress.runBlockingCancellable
import io.ktor.client.HttpClient
import io.ktor.client.engine.java.Java
import io.ktor.client.plugins.HttpRequestRetry
@@ -36,7 +35,7 @@ private val httpClient: HttpClient by lazy {
}
}
private suspend fun httpGetSuspend(url: String, authToken: String?): ByteArray {
suspend fun httpGet(url: String, authToken: String?): ByteArray {
val response = httpClient.get(url) {
headers {
if (authToken != null) {
@@ -51,10 +50,3 @@ private suspend fun httpGetSuspend(url: String, authToken: String?): ByteArray {
return response.bodyAsBytes()
}
fun httpGet(url: String, authToken: String?): ByteArray {
//todo refac eval framework to make it work with suspend funs
return runBlockingCancellable {
httpGetSuspend(url, authToken)
}
}