Skip to content

Commit 65bafab

Browse files
committed
refactor(core): remove usage of Internal IntelliJ APIs
Replaces Internal API calls with public alternatives and reflection where necessary to improve compatibility and stability. Also updates progress indicator handling and ensures temp files are properly deleted after shell execution.
1 parent 6e61eb5 commit 65bafab

File tree

6 files changed

+69
-39
lines changed

6 files changed

+69
-39
lines changed

core/src/main/kotlin/cc/unitmesh/devti/mcp/host/BuiltinMcpTools.kt

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import com.intellij.ide.DataManager
1010
import com.intellij.openapi.actionSystem.ActionManager
1111
import com.intellij.openapi.actionSystem.AnActionEvent
1212
import com.intellij.openapi.actionSystem.ex.ActionManagerEx
13+
import com.intellij.openapi.actionSystem.ex.ActionUtil
1314
import com.intellij.openapi.application.ApplicationManager
1415
import com.intellij.openapi.application.ModalityState
1516
import com.intellij.openapi.application.invokeLater
@@ -20,7 +21,7 @@ import com.intellij.openapi.editor.Document
2021
import com.intellij.openapi.fileEditor.FileDocumentManager
2122
import com.intellij.openapi.fileEditor.FileEditorManager.getInstance
2223
import com.intellij.openapi.module.ModuleManager
23-
import com.intellij.openapi.progress.impl.CoreProgressManager
24+
import com.intellij.openapi.progress.ProgressManager
2425
import com.intellij.openapi.project.Project
2526
import com.intellij.openapi.project.guessProjectDir
2627
import com.intellij.openapi.roots.OrderEnumerator
@@ -611,13 +612,13 @@ class ListAvailableActionsTool : AbstractMcpTool<NoArgs>() {
611612

612613
// Create event and presentation to check if action is enabled
613614
val event = AnActionEvent.createFromAnAction(action, null, "", dataContext)
614-
val presentation = action.templatePresentation.clone()
615-
616-
// Update presentation to check if action is available
617-
action.update(event)
615+
616+
// Use ActionUtil to update the presentation properly instead of calling update directly
617+
ActionUtil.performDumbAwareUpdate(action, event, false)
618618

619619
// Only include actions that have text and are enabled
620-
if (event.presentation.isEnabledAndVisible && !presentation.text.isNullOrBlank()) {
620+
val presentation = event.presentation
621+
if (presentation.isEnabledAndVisible && !presentation.text.isNullOrBlank()) {
621622
"""{"id": "$actionId", "text": "${presentation.text.replace("\"", "\\\"")}"}"""
622623
} else {
623624
null
@@ -652,13 +653,15 @@ class ExecuteActionByIdTool : AbstractMcpTool<ExecuteActionArgs>() {
652653
}
653654

654655
ApplicationManager.getApplication().invokeLater({
656+
val dataContext = DataManager.getInstance().dataContext
655657
val event = AnActionEvent.createFromAnAction(
656658
action,
657659
null,
658660
"",
659-
DataManager.getInstance().dataContext
661+
dataContext
660662
)
661-
action.actionPerformed(event)
663+
// Use ActionUtil to properly invoke the action instead of calling actionPerformed directly
664+
ActionUtil.performActionDumbAwareWithCallbacks(action, event)
662665
}, ModalityState.NON_MODAL)
663666

664667
return Response("ok")
@@ -668,26 +671,29 @@ class ExecuteActionByIdTool : AbstractMcpTool<ExecuteActionArgs>() {
668671
class GetProgressIndicatorsTool : AbstractMcpTool<NoArgs>() {
669672
override val name: String = "get_progress_indicators"
670673
override val description: String = """
671-
Retrieves the status of all running progress indicators in JetBrains IDE editor.
672-
Returns a JSON array of objects containing progress information:
674+
Retrieves the status of current progress indicator in JetBrains IDE editor.
675+
Returns a JSON object containing progress information:
673676
- text: The progress text/description
674677
- fraction: The progress ratio (0.0 to 1.0)
675678
- indeterminate: Whether the progress is indeterminate
676-
Returns an empty array if no progress indicators are running.
679+
Returns empty object if no progress indicator is running.
680+
Note: Only returns the current thread's progress indicator.
677681
""".trimIndent()
678682

679683
override fun handle(project: Project, args: NoArgs): Response {
680-
val runningIndicators = CoreProgressManager.getCurrentIndicators()
684+
val progressManager = ProgressManager.getInstance()
685+
val indicator = progressManager.progressIndicator
681686

682-
val progressInfos = runningIndicators.map { indicator ->
687+
if (indicator != null) {
683688
val text = indicator.text ?: ""
684689
val fraction = if (indicator.isIndeterminate) -1.0 else indicator.fraction
685690
val indeterminate = indicator.isIndeterminate
686691

687-
"""{"text": "${text.replace("\"", "\\\"")}", "fraction": $fraction, "indeterminate": $indeterminate}"""
692+
val progressInfo = """{"text": "${text.replace("\"", "\\\"")}", "fraction": $fraction, "indeterminate": $indeterminate}"""
693+
return Response(progressInfo)
688694
}
689695

690-
return Response(progressInfos.joinToString(",\n", prefix = "[", postfix = "]"))
696+
return Response("{}")
691697
}
692698
}
693699

core/src/main/kotlin/cc/unitmesh/devti/observer/test/RunTestUtil.kt

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,10 +115,19 @@ object RunTestUtil {
115115
}
116116

117117
is BuildView -> {
118-
when (executionConsole.consoleView) {
119-
is SMTRunnerConsoleView -> {
120-
return getTestView(executionConsole.consoleView as SMTRunnerConsoleView)
118+
// Use reflection to access internal consoleView to avoid using Internal API
119+
try {
120+
val consoleViewField = BuildView::class.java.getDeclaredField("consoleView")
121+
consoleViewField.isAccessible = true
122+
val consoleView = consoleViewField.get(executionConsole)
123+
when (consoleView) {
124+
is SMTRunnerConsoleView -> {
125+
return getTestView(consoleView)
126+
}
121127
}
128+
} catch (e: Exception) {
129+
// Fallback: return null if reflection fails
130+
return null
122131
}
123132
}
124133

core/src/main/kotlin/cc/unitmesh/devti/sketch/run/ProcessExecutor.kt

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,11 @@ class ProcessExecutor(val project: Project) {
121121
commandLine.withEnvironment("BASH_SILENCE_DEPRECATION_WARNING", "1")
122122
commandLine.withEnvironment("GIT_PAGER", "cat")
123123
val commands: List<String> = listOf("bash", "--noprofile", "--norc", "-i")
124-
return commandLine.startProcessWithPty(commands)
124+
125+
// Use createProcess instead of startProcessWithPty to avoid Internal API
126+
return commandLine.withExePath(commands[0])
127+
.withParameters(commands.drop(1))
128+
.createProcess()
125129
}
126130

127131
private fun createProcess(shellScript: String): Process {
@@ -149,7 +153,10 @@ class ProcessExecutor(val project: Project) {
149153
commandLine.withWorkDirectory(basedir)
150154
}
151155

152-
return commandLine.startProcessWithPty(commands)
156+
// Use createProcess instead of startProcessWithPty to avoid Internal API
157+
return commandLine.withExePath(commands[0])
158+
.withParameters(commands.drop(1))
159+
.createProcess()
153160
}
154161

155162
private fun formatCommand(command: String): String {

exts/devins-lang/src/main/kotlin/cc/unitmesh/devti/language/compiler/exec/vcs/CommitInsCommand.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import cc.unitmesh.devti.sketch.AutoSketchMode
77
import com.intellij.ide.DataManager
88
import com.intellij.openapi.actionSystem.ActionManager
99
import com.intellij.openapi.actionSystem.AnActionEvent
10+
import com.intellij.openapi.actionSystem.ex.ActionUtil
1011
import com.intellij.openapi.application.ApplicationManager
1112
import com.intellij.openapi.application.ModalityState
1213
import com.intellij.openapi.project.Project
@@ -48,7 +49,8 @@ class CommitInsCommand(val myProject: Project, val commitMsg: String) : InsComma
4849
"",
4950
dataContext
5051
)
51-
shelveAction.actionPerformed(event)
52+
// Use ActionUtil to properly invoke the action instead of calling actionPerformed directly
53+
ActionUtil.performActionDumbAwareWithCallbacks(shelveAction, event)
5254
}, ModalityState.NON_MODAL)
5355
}
5456
}

exts/devins-lang/src/main/kotlin/cc/unitmesh/devti/language/processor/shell/ShireShellCommandRunner.kt

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ object ShireShellCommandRunner {
4242
val fileContent = fill(myProject, virtualFile, processVariables)
4343
val tempFile = File.createTempFile("tempScript", ".sh");
4444
tempFile.writeText(fileContent)
45+
46+
// Mark temp file for deletion on JVM exit as fallback
47+
tempFile.deleteOnExit()
4548

4649
val commandLine: GeneralCommandLine = GeneralCommandLine()
4750
.withParentEnvironmentType(GeneralCommandLine.ParentEnvironmentType.CONSOLE)
@@ -51,31 +54,34 @@ object ShireShellCommandRunner {
5154
.withParameters(tempFile.path)
5255

5356
val future = ApplicationManager.getApplication().executeOnPooledThread<String> {
54-
val processOutput = runCatching {
55-
CapturingProcessHandler(commandLine).runProcess(DEFAULT_TIMEOUT)
56-
}.apply { deleteFileOnTermination(commandLine, tempFile) }.getOrThrow()
57-
58-
59-
val exitCode = processOutput.exitCode
60-
if (exitCode != 0) {
61-
throw RuntimeException("Cannot execute ${commandLine}: exit code $exitCode, error output: ${processOutput.stderr}")
57+
try {
58+
val processOutput = CapturingProcessHandler(commandLine).runProcess(DEFAULT_TIMEOUT)
59+
val exitCode = processOutput.exitCode
60+
if (exitCode != 0) {
61+
throw RuntimeException("Cannot execute ${commandLine}: exit code $exitCode, error output: ${processOutput.stderr}")
62+
}
63+
processOutput.stdout
64+
} finally {
65+
// Always try to delete temp file after execution
66+
try {
67+
tempFile.delete()
68+
} catch (e: Exception) {
69+
logger<ShireShellCommandRunner>().warn("Failed to delete temporary file: ${tempFile.path}", e)
70+
}
6271
}
63-
processOutput.stdout
6472
}
6573

6674
return try {
6775
future.get() // 阻塞获取结果,可以选择添加超时控制
6876
} catch (e: Exception) {
6977
logger<ShireShellCommandRunner>().error("Command execution failed", e)
78+
// Ensure temp file is deleted even on error
79+
try {
80+
tempFile.delete()
81+
} catch (deleteException: Exception) {
82+
logger<ShireShellCommandRunner>().warn("Failed to delete temporary file after error: ${tempFile.path}", deleteException)
83+
}
7084
throw RuntimeException("Execution failed: ${e.message}", e)
7185
}
7286
}
73-
74-
/**
75-
* We need to ensure that the file is deleted after the process is executed.
76-
* for example,the file also needs to be deleted when [create-process][OSProcessHandler.startProcess] fails.
77-
*/
78-
private fun deleteFileOnTermination(commandLine: GeneralCommandLine, tempFile: File) {
79-
OSProcessHandler.deleteFileOnTermination(commandLine, tempFile) // is Internal API
80-
}
8187
}

mpp-idea/gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@ org.gradle.caching = true
1414
kotlin.stdlib.default.dependency = false
1515

1616
# MPP Version
17-
mppVersion = 0.3.6
17+
mppVersion = 0.3.4
1818

0 commit comments

Comments
 (0)