1
+ package cc.unitmesh.devti.provider
2
+
3
+ import cc.unitmesh.devti.AutoDevBundle
4
+ import com.intellij.execution.ExecutionException
5
+ import com.intellij.execution.ExecutionManager
6
+ import com.intellij.execution.OutputListener
7
+ import com.intellij.execution.RunnerAndConfigurationSettings
8
+ import com.intellij.execution.actions.ConfigurationContext
9
+ import com.intellij.execution.executors.DefaultRunExecutor
10
+ import com.intellij.execution.impl.ExecutionManagerImpl
11
+ import com.intellij.execution.process.ProcessAdapter
12
+ import com.intellij.execution.process.ProcessEvent
13
+ import com.intellij.execution.process.ProcessListener
14
+ import com.intellij.execution.process.ProcessOutputType
15
+ import com.intellij.execution.runners.ExecutionEnvironmentBuilder
16
+ import com.intellij.execution.runners.ProgramRunner
17
+ import com.intellij.execution.testframework.sm.runner.SMTRunnerEventsAdapter
18
+ import com.intellij.execution.testframework.sm.runner.SMTRunnerEventsListener
19
+ import com.intellij.execution.testframework.sm.runner.SMTestProxy
20
+ import com.intellij.openapi.application.invokeAndWaitIfNeeded
21
+ import com.intellij.openapi.application.runInEdt
22
+ import com.intellij.openapi.progress.ProgressIndicator
23
+ import com.intellij.openapi.project.Project
24
+ import com.intellij.openapi.util.Disposer
25
+ import com.intellij.openapi.util.Key
26
+ import com.intellij.openapi.vfs.VirtualFile
27
+ import com.intellij.psi.PsiElement
28
+ import com.intellij.util.messages.MessageBusConnection
29
+ import java.util.concurrent.CountDownLatch
30
+
31
+ class RunServiceTask (
32
+ private val project : Project ,
33
+ private val virtualFile : VirtualFile ,
34
+ private val testElement : PsiElement ? ,
35
+ private val runService : RunService
36
+ ) : com.intellij.openapi.progress.Task.Backgroundable(
37
+ project,
38
+ AutoDevBundle .message("progress.run.task"),
39
+ true
40
+ ) {
41
+ override fun run (indicator : ProgressIndicator ) {
42
+ doRun(indicator)
43
+ }
44
+
45
+ private fun doRun (indicator : ProgressIndicator ): String? {
46
+ var settings: RunnerAndConfigurationSettings ? = runService.createRunSettings(project, virtualFile)
47
+ if (settings == null ) {
48
+ settings = createDefaultTestConfigurations(project, testElement ? : return null ) ? : return null
49
+ }
50
+
51
+ settings.isActivateToolWindowBeforeRun = false
52
+
53
+ val stderr = StringBuilder ()
54
+ val processListener = object : OutputListener () {
55
+ override fun onTextAvailable (event : ProcessEvent , outputType : Key <* >) {
56
+ val text = event.text
57
+ if (text != null && ProcessOutputType .isStderr(outputType)) {
58
+ stderr.append(text)
59
+ }
60
+ }
61
+ }
62
+
63
+ val testRoots = mutableListOf<SMTestProxy .SMRootTestProxy >()
64
+ val testEventsListener = object : SMTRunnerEventsAdapter () {
65
+ override fun onTestingStarted (testsRoot : SMTestProxy .SMRootTestProxy ) {
66
+ testRoots + = testsRoot
67
+ }
68
+ }
69
+
70
+ executeRunConfigures(project, settings, processListener, testEventsListener)
71
+
72
+ @Suppress(" UnstableApiUsage" )
73
+ invokeAndWaitIfNeeded {}
74
+ // val testResults = testRoots.map { it.toCheckResult() }
75
+
76
+ val output = processListener.output
77
+ val errorOutput = output.stderr
78
+
79
+ if (output.exitCode != 0 ) {
80
+ return errorOutput
81
+ }
82
+
83
+ val outputString = output.stdout
84
+ return outputString
85
+ }
86
+
87
+
88
+ fun executeRunConfigures (
89
+ project : Project ,
90
+ settings : RunnerAndConfigurationSettings ,
91
+ processListener : OutputListener ,
92
+ testEventsListener : SMTRunnerEventsAdapter
93
+ ) {
94
+ val connection = project.messageBus.connect()
95
+ try {
96
+ return executeRunConfigurations(connection, settings, processListener, testEventsListener)
97
+ } finally {
98
+ // connection.disconnect()
99
+ }
100
+ }
101
+
102
+ private fun executeRunConfigurations (
103
+ connection : MessageBusConnection ,
104
+ configurations : RunnerAndConfigurationSettings ,
105
+ processListener : ProcessListener ? ,
106
+ testEventsListener : SMTRunnerEventsListener ?
107
+ ) {
108
+ testEventsListener?.let {
109
+ connection.subscribe(SMTRunnerEventsListener .TEST_STATUS , it)
110
+ }
111
+ val context = Context (processListener, null , CountDownLatch (1 ))
112
+ Disposer .register(connection, context)
113
+
114
+ runInEdt {
115
+ connection.subscribe(
116
+ ExecutionManager .EXECUTION_TOPIC ,
117
+ CheckExecutionListener (DefaultRunExecutor .EXECUTOR_ID , context)
118
+ )
119
+
120
+ configurations.startRunConfigurationExecution(context)
121
+ }
122
+
123
+ // if run in Task, Disposer.dispose(context)
124
+ }
125
+
126
+
127
+ /* *
128
+ * Returns `true` if configuration execution is started successfully, `false` otherwise
129
+ */
130
+ @Throws(ExecutionException ::class )
131
+ private fun RunnerAndConfigurationSettings.startRunConfigurationExecution (context : Context ): Boolean {
132
+ val runner = ProgramRunner .getRunner(DefaultRunExecutor .EXECUTOR_ID , configuration)
133
+ val env =
134
+ ExecutionEnvironmentBuilder .create(DefaultRunExecutor .getRunExecutorInstance(), this )
135
+ .activeTarget()
136
+ .build(callback(context))
137
+
138
+ if (runner == null || env.state == null ) {
139
+ context.latch.countDown()
140
+ return false
141
+ }
142
+
143
+ context.environments.add(env)
144
+ runner.execute(env)
145
+ return true
146
+ }
147
+
148
+ fun callback (context : Context ) = ProgramRunner .Callback { descriptor ->
149
+ // Descriptor can be null in some cases.
150
+ // For example, IntelliJ Rust's test runner provides null here if compilation fails
151
+ if (descriptor == null ) {
152
+ context.latch.countDown()
153
+ return @Callback
154
+ }
155
+
156
+ Disposer .register(context) {
157
+ ExecutionManagerImpl .stopProcess(descriptor)
158
+ }
159
+ val processHandler = descriptor.processHandler
160
+ if (processHandler != null ) {
161
+ processHandler.addProcessListener(object : ProcessAdapter () {
162
+ override fun processTerminated (event : ProcessEvent ) {
163
+ context.latch.countDown()
164
+ }
165
+ })
166
+ context.processListener?.let { processHandler.addProcessListener(it) }
167
+ }
168
+ }
169
+
170
+
171
+ fun createDefaultTestConfigurations (project : Project , element : PsiElement ): RunnerAndConfigurationSettings ? {
172
+ return ConfigurationContext (element).configurationsFromContext?.firstOrNull()?.configurationSettings
173
+ }
174
+ }
0 commit comments