Skip to content

Commit f029612

Browse files
committed
feat(devtools): improve shell command execution and output display
- Refactor ProcessExecutor to return a structured result object- Update ShellRunService to handle execution results more effectively - Enhance TerminalSketchProvider to display and format execution results - Improve CollapsiblePanel functionality for better UI interaction
1 parent 50c6caf commit f029612

File tree

4 files changed

+82
-28
lines changed

4 files changed

+82
-28
lines changed

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

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,22 @@ import java.io.Writer
4343
import java.util.concurrent.CompletableFuture
4444
import java.util.concurrent.TimeUnit
4545

46+
data class ProcessExecutorResult(
47+
val exitCode: Int,
48+
val stdOutput: String,
49+
val errOutput: String
50+
)
51+
4652
class ProcessExecutor(val project: Project) {
47-
fun executeCode(code: String): String? {
53+
fun executeCode(code: String): ProcessExecutorResult {
4854
val taskExecutor = PooledThreadExecutor.INSTANCE
49-
val future: CompletableFuture<String?> = CompletableFuture()
55+
val future: CompletableFuture<ProcessExecutorResult> = CompletableFuture()
5056
val task = object : Task.Backgroundable(project, "Running shell command") {
5157
override fun run(indicator: ProgressIndicator) {
5258
runBlocking(taskExecutor.asCoroutineDispatcher()) {
5359
val executor = ProcessExecutor(project)
5460
val result = executor.executeCode(code, taskExecutor.asCoroutineDispatcher())
55-
future.complete(result ?: "")
61+
future.complete(result)
5662
}
5763
}
5864
}
@@ -63,7 +69,7 @@ class ProcessExecutor(val project: Project) {
6369
return future.get(120, TimeUnit.SECONDS)
6470
}
6571

66-
suspend fun executeCode(code: String, dispatcher: CoroutineDispatcher): String? {
72+
suspend fun executeCode(code: String, dispatcher: CoroutineDispatcher): ProcessExecutorResult {
6773
val outputWriter = StringWriter()
6874
val errWriter = StringWriter()
6975

@@ -72,15 +78,19 @@ class ProcessExecutor(val project: Project) {
7278
val stdOutput = outputWriter.toString()
7379
val errOutput = errWriter.toString()
7480

75-
return if (exitCode == 0) {
81+
if (exitCode == 0) {
7682
AutoDevNotifications.notify(project, "Shell command $code executed successfully")
77-
stdOutput
7883
} else {
7984
AutoDevToolWindowFactory.Companion.sendToSketchToolWindow(project, ChatActionType.SKETCH) { ui, _ ->
8085
ui.putText("Error executing shell command: \n```bash\n$errOutput\n```")
8186
}
82-
"Error executing shell command: $errOutput"
8387
}
88+
89+
return ProcessExecutorResult(
90+
exitCode = exitCode,
91+
stdOutput = stdOutput,
92+
errOutput = errOutput
93+
)
8494
}
8595
}
8696

@@ -178,4 +188,4 @@ class ProcessExecutor(val project: Project) {
178188
} while (process.isAlive || line != null)
179189
}
180190
}
181-
}
191+
}

exts/devins-lang/src/main/kotlin/cc/unitmesh/devti/language/compiler/service/ShellRunService.kt

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,6 @@ import com.intellij.execution.configurations.RunConfiguration
88
import com.intellij.execution.configurations.RunProfile
99
import com.intellij.openapi.application.ApplicationManager
1010
import com.intellij.openapi.application.runReadAction
11-
import com.intellij.openapi.progress.ProgressIndicator
12-
import com.intellij.openapi.progress.ProgressManager
13-
import com.intellij.openapi.progress.Task
14-
import com.intellij.openapi.progress.impl.BackgroundableProcessIndicator
1511
import com.intellij.openapi.project.Project
1612
import com.intellij.openapi.vfs.VirtualFile
1713
import com.intellij.psi.PsiElement
@@ -20,11 +16,6 @@ import com.intellij.sh.psi.ShFile
2016
import com.intellij.sh.run.ShConfigurationType
2117
import com.intellij.sh.run.ShRunConfiguration
2218
import com.intellij.sh.run.ShRunner
23-
import kotlinx.coroutines.asCoroutineDispatcher
24-
import kotlinx.coroutines.runBlocking
25-
import org.jetbrains.ide.PooledThreadExecutor
26-
import java.util.concurrent.CompletableFuture
27-
import java.util.concurrent.TimeUnit
2819

2920
class ShellRunService : RunService {
3021
override fun isApplicable(project: Project, file: VirtualFile) = file.extension == "sh" || file.extension == "bash"
@@ -39,7 +30,13 @@ class ShellRunService : RunService {
3930

4031
val code = virtualFile.readText()
4132
if (isFromToolAction) {
42-
return ProcessExecutor(project).executeCode(code)
33+
return ProcessExecutor(project).executeCode(code).let { result ->
34+
return if (result.exitCode != 0) {
35+
"Error: ${result.errOutput}"
36+
} else {
37+
"Output: ${result.stdOutput}"
38+
}
39+
}
4340
}
4441

4542
val shRunner = ApplicationManager.getApplication().getService(ShRunner::class.java)

exts/ext-terminal/src/main/kotlin/cc/unitmesh/terminal/sketch/CollapsiblePanel.kt

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ import java.awt.event.MouseAdapter
1010
import java.awt.event.MouseEvent
1111
import javax.swing.JPanel
1212

13-
class CollapsiblePanel(title: String, content: JPanel, initiallyCollapsed: Boolean = false) :
13+
class CollapsiblePanel(title: String, private val contentPanel: JPanel, initiallyCollapsed: Boolean = false) :
1414
JBPanel<CollapsiblePanel>(BorderLayout()) {
1515

1616
private var isCollapsed = initiallyCollapsed
1717
private val headerPanel = JBPanel<JBPanel<*>>(BorderLayout())
18-
private val titleLabel = JBLabel(title)
18+
val titleLabel = JBLabel(title)
1919
private val toggleLabel = JBLabel()
2020

2121
init {
@@ -28,31 +28,47 @@ class CollapsiblePanel(title: String, content: JPanel, initiallyCollapsed: Boole
2828

2929
headerPanel.addMouseListener(object : MouseAdapter() {
3030
override fun mouseClicked(e: MouseEvent?) {
31-
toggleContent(content)
31+
toggleContent()
3232
}
3333
})
3434

3535
add(headerPanel, BorderLayout.NORTH)
36-
add(content, BorderLayout.CENTER)
36+
add(contentPanel, BorderLayout.CENTER)
3737

38-
content.isVisible = !isCollapsed
38+
contentPanel.isVisible = !isCollapsed
3939
}
4040

4141
private fun updateToggleIcon() {
4242
toggleLabel.icon = if (isCollapsed) AllIcons.General.ArrowRight else AllIcons.General.ArrowDown
4343
}
4444

45-
private fun toggleContent(content: JPanel) {
46-
setCollapsed(!isCollapsed, content)
45+
private fun toggleContent() {
46+
setCollapsed(!isCollapsed)
4747
}
4848

49-
fun setCollapsed(collapsed: Boolean, content: JPanel) {
49+
fun setCollapsed(collapsed: Boolean) {
5050
isCollapsed = collapsed
51-
content.isVisible = !isCollapsed
51+
contentPanel.isVisible = !isCollapsed
5252
updateToggleIcon()
5353
revalidate()
5454
repaint()
5555
}
5656

57+
fun setCollapsed(collapsed: Boolean, content: JPanel) {
58+
setCollapsed(collapsed)
59+
}
60+
61+
fun expand() {
62+
setCollapsed(false)
63+
}
64+
65+
fun collapse() {
66+
setCollapsed(true)
67+
}
68+
5769
fun isCollapsed(): Boolean = isCollapsed
70+
71+
fun setTitle(title: String) {
72+
titleLabel.text = title
73+
}
5874
}

exts/ext-terminal/src/main/kotlin/cc/unitmesh/terminal/sketch/TerminalSketchProvider.kt

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,16 @@ class TerminalSketchProvider : LanguageSketchProvider {
6161
add(codeSketch.getComponent(), BorderLayout.CENTER)
6262
}
6363

64+
// Add result display component
65+
val resultSketch = CodeHighlightSketch(project, "", CodeFence.findLanguage("bash"))
66+
val resultPanel = JPanel(BorderLayout()).apply {
67+
add(resultSketch.getComponent(), BorderLayout.CENTER)
68+
}
69+
6470
val isSingleLine = content.lines().filter { it.trim().isNotEmpty() }.size <= 1
6571
val collapsibleCodePanel = CollapsiblePanel("Shell Code", codePanel, initiallyCollapsed = isSingleLine)
72+
73+
val collapsibleResultPanel = CollapsiblePanel("Execution Results", resultPanel, initiallyCollapsed = true)
6674

6775
val toolbarPanel = JPanel(BorderLayout()).apply {
6876
add(titleLabel, BorderLayout.WEST)
@@ -82,10 +90,13 @@ class TerminalSketchProvider : LanguageSketchProvider {
8290
}
8391

8492
codeSketch.getComponent().border = JBUI.Borders.empty()
93+
resultSketch.getComponent().border = JBUI.Borders.empty()
94+
8595
mainPanel = object : JPanel(VerticalLayout(JBUI.scale(0))) {
8696
init {
8797
add(toolbarWrapper)
8898
add(collapsibleCodePanel)
99+
add(collapsibleResultPanel)
89100
add(terminalWidget!!.component)
90101
}
91102
}
@@ -98,7 +109,26 @@ class TerminalSketchProvider : LanguageSketchProvider {
98109
val executeAction = object :
99110
AnAction("Execute", AutoDevBundle.message("sketch.terminal.execute"), AllIcons.Actions.Execute) {
100111
override fun actionPerformed(e: AnActionEvent) {
101-
ProcessExecutor(project).executeCode(getViewText())
112+
val runResult = ProcessExecutor(project).executeCode(getViewText())
113+
ApplicationManager.getApplication().invokeLater {
114+
val resultText = if (runResult.exitCode != 0) {
115+
"${runResult.stdOutput}\n${runResult.errOutput}".trim()
116+
} else {
117+
runResult.stdOutput
118+
}
119+
120+
resultSketch.updateViewText(resultText, true)
121+
122+
if (collapsibleResultPanel.isCollapsed()) {
123+
collapsibleResultPanel.expand()
124+
}
125+
126+
if (runResult.exitCode != 0) {
127+
collapsibleResultPanel.setTitle("Execution Results (Error: ${runResult.exitCode})")
128+
} else {
129+
collapsibleResultPanel.setTitle("Execution Results")
130+
}
131+
}
102132
}
103133
}
104134

@@ -195,6 +225,7 @@ class TerminalSketchProvider : LanguageSketchProvider {
195225
override fun updateLanguage(language: Language?, originLanguage: String?) {}
196226
override fun dispose() {
197227
codeSketch.dispose()
228+
resultSketch.dispose() // Make sure to dispose resultSketch
198229
}
199230
}
200231
}

0 commit comments

Comments
 (0)