feat!: TTY support for zig processes
This commit is contained in:
parent
da38433eb3
commit
0a5a765eaf
16 changed files with 103 additions and 143 deletions
10
CHANGELOG.md
10
CHANGELOG.md
|
@ -17,6 +17,16 @@ Changelog structure reference:
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- Project, Debugging
|
||||
- TTY support for zig processes
|
||||
|
||||
### Removed
|
||||
|
||||
- Project
|
||||
- "Emulate terminal" and "colored output" config options have been removed from zig run/test/build tasks, as they are no longer required for ZigBrains to work.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Zig
|
||||
|
|
|
@ -57,8 +57,8 @@ class ZigClionDebuggerDriverConfigurationProvider: ZigDebuggerDriverConfiguratio
|
|||
}
|
||||
return when(toolchain.debuggerKind) {
|
||||
CPPDebugger.Kind.BUNDLED_GDB,
|
||||
CPPDebugger.Kind.CUSTOM_GDB -> CLionGDBDriverConfiguration(project, toolchain)
|
||||
CPPDebugger.Kind.BUNDLED_LLDB -> CLionLLDBDriverConfiguration(project, toolchain)
|
||||
CPPDebugger.Kind.CUSTOM_GDB -> CLionGDBDriverConfiguration(project, toolchain, isEmulateTerminal = emulateTerminal)
|
||||
CPPDebugger.Kind.BUNDLED_LLDB -> CLionLLDBDriverConfiguration(project, toolchain, isEmulateTerminal = emulateTerminal)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,9 +22,10 @@
|
|||
|
||||
package com.falsepattern.zigbrains.debugger
|
||||
|
||||
import com.intellij.execution.filters.Filter
|
||||
import com.intellij.execution.filters.TextConsoleBuilder
|
||||
import com.intellij.xdebugger.XDebugSession
|
||||
import com.jetbrains.cidr.execution.RunParameters
|
||||
import com.jetbrains.cidr.execution.debugger.CidrLocalDebugProcess
|
||||
|
||||
class ZigLocalDebugProcess(parameters: RunParameters, session: XDebugSession, consoleBuilder: TextConsoleBuilder) : CidrLocalDebugProcess(parameters, session, consoleBuilder)
|
||||
class ZigLocalDebugProcess(parameters: RunParameters, session: XDebugSession, consoleBuilder: TextConsoleBuilder) : CidrLocalDebugProcess(parameters, session, consoleBuilder, { Filter.EMPTY_ARRAY }, true)
|
|
@ -23,6 +23,7 @@
|
|||
package com.falsepattern.zigbrains.debugger.runner.base
|
||||
|
||||
import com.falsepattern.zigbrains.project.run.ZigProcessHandler
|
||||
import com.falsepattern.zigbrains.shared.cli.startIPCAwareProcess
|
||||
import com.falsepattern.zigbrains.shared.ipc.IPCUtil
|
||||
import com.falsepattern.zigbrains.shared.ipc.ipc
|
||||
import com.intellij.execution.ExecutionException
|
||||
|
@ -48,12 +49,7 @@ class PreLaunchProcessListener(val console: ConsoleView) : ProcessListener {
|
|||
@Throws(ExecutionException::class)
|
||||
suspend fun executeCommandLineWithHook(project: Project, commandLine: GeneralCommandLine): Boolean {
|
||||
return withProgressText(commandLine.commandLineString) {
|
||||
val ipc = IPCUtil.wrapWithIPC(commandLine)
|
||||
val cli = ipc?.cli ?: commandLine
|
||||
val processHandler = ZigProcessHandler(cli)
|
||||
if (ipc != null) {
|
||||
project.ipc?.launchWatcher(ipc, processHandler.process)
|
||||
}
|
||||
val processHandler = commandLine.startIPCAwareProcess(project)
|
||||
this@PreLaunchProcessListener.processHandler = processHandler
|
||||
hook(processHandler)
|
||||
processHandler.startNotify()
|
||||
|
@ -74,10 +70,6 @@ class PreLaunchProcessListener(val console: ConsoleView) : ProcessListener {
|
|||
|
||||
override fun processTerminated(event: ProcessEvent) {
|
||||
if (event.exitCode != 0) {
|
||||
console.print(
|
||||
"Process finished with exit code " + event.exitCode,
|
||||
ConsoleViewContentType.NORMAL_OUTPUT
|
||||
)
|
||||
isBuildFailed = true
|
||||
} else {
|
||||
isBuildFailed = false
|
||||
|
|
|
@ -26,6 +26,7 @@ import com.falsepattern.zigbrains.project.execution.base.ZigProfileState
|
|||
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain
|
||||
import com.falsepattern.zigbrains.shared.zigCoroutineScope
|
||||
import com.intellij.execution.configurations.GeneralCommandLine
|
||||
import com.intellij.execution.configurations.PtyCommandLine
|
||||
import com.jetbrains.cidr.execution.Installer
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.future.asCompletableFuture
|
||||
|
@ -39,7 +40,7 @@ class ZigDebugEmitBinaryInstaller<ProfileState: ZigProfileState<*>>(
|
|||
): Installer {
|
||||
override fun install(): GeneralCommandLine {
|
||||
val cfg = profileState.configuration
|
||||
val cli = GeneralCommandLine().withExePath(executableFile.absolutePath)
|
||||
val cli = PtyCommandLine().withConsoleMode(false).withExePath(executableFile.absolutePath)
|
||||
cfg.workingDirectory.path?.let { x -> cli.withWorkingDirectory(x) }
|
||||
cli.addParameters(exeArgs)
|
||||
cli.withCharset(Charsets.UTF_8)
|
||||
|
|
|
@ -58,7 +58,7 @@ abstract class ZigDebugRunnerBase<ProfileState : ZigProfileState<*>> : ZigProgra
|
|||
val project = environment.project
|
||||
val driverProviders = ZigDebuggerDriverConfigurationProviderBase.EXTENSION_POINT_NAME.extensionList
|
||||
for (provider in driverProviders) {
|
||||
val driver = provider.getDebuggerConfiguration(project, isElevated = false, emulateTerminal = false, DebuggerDriverConfiguration::class.java) ?: continue
|
||||
val driver = provider.getDebuggerConfiguration(project, isElevated = false, emulateTerminal = true, DebuggerDriverConfiguration::class.java) ?: continue
|
||||
return executeWithDriver(state, toolchain, environment, driver) ?: continue
|
||||
}
|
||||
return null
|
||||
|
@ -98,7 +98,7 @@ abstract class ZigDebugRunnerBase<ProfileState : ZigProfileState<*>> : ZigProgra
|
|||
debuggerManager.startSession(environment, object: XDebugProcessStarter() {
|
||||
override fun start(session: XDebugSession): XDebugProcess {
|
||||
val project = session.project
|
||||
val textConsoleBuilder = SharedConsoleBuilder(console)
|
||||
val textConsoleBuilder = state.consoleBuilder
|
||||
val debugProcess = ZigLocalDebugProcess(runParameters, session, textConsoleBuilder)
|
||||
ProcessTerminatedListener.attach(debugProcess.processHandler, project)
|
||||
debugProcess.start()
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* This file is part of ZigBrains.
|
||||
*
|
||||
* Copyright (C) 2023-2025 FalsePattern
|
||||
* All Rights Reserved
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* ZigBrains is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, only version 3 of the License.
|
||||
*
|
||||
* ZigBrains is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with ZigBrains. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.falsepattern.zigbrains.project.execution
|
||||
|
||||
import com.intellij.execution.filters.TextConsoleBuilderImpl
|
||||
import com.intellij.execution.ui.ConsoleView
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.terminal.TerminalExecutionConsole
|
||||
import java.nio.file.Path
|
||||
|
||||
class ZigConsoleBuilder(private val project: Project, private val emulateTerminal: Boolean = false): TextConsoleBuilderImpl(project) {
|
||||
override fun createConsole(): ConsoleView {
|
||||
return if (emulateTerminal)
|
||||
TerminalExecutionConsole(project, null)
|
||||
else
|
||||
super.createConsole()
|
||||
}
|
||||
}
|
|
@ -274,12 +274,6 @@ open class CheckboxConfigurable(
|
|||
}
|
||||
}
|
||||
|
||||
class ColoredConfigurable(serializedName: String): CheckboxConfigurable(serializedName, ZigBrainsBundle.message("exec.option.label.colored-terminal"), true) {
|
||||
override fun clone(): ColoredConfigurable {
|
||||
return super.clone() as ColoredConfigurable
|
||||
}
|
||||
}
|
||||
|
||||
class OptimizationConfigurable(
|
||||
@Transient private val serializedName: String,
|
||||
var level: OptimizationLevel = OptimizationLevel.Debug,
|
||||
|
|
|
@ -43,8 +43,6 @@ import org.jetbrains.annotations.Nls
|
|||
abstract class ZigExecConfig<T: ZigExecConfig<T>>(project: Project, factory: ConfigurationFactory, @Nls name: String): LocatableConfigurationBase<ZigProfileState<T>>(project, factory, name) {
|
||||
var workingDirectory = WorkDirectoryConfigurable("workingDirectory").apply { path = project.guessProjectDir()?.toNioPathOrNull() }
|
||||
private set
|
||||
var pty = CheckboxConfigurable("pty", ZigBrainsBundle.message("exec.option.label.emulate-terminal"), false)
|
||||
private set
|
||||
|
||||
abstract val suggestedName: @ActionText String
|
||||
@Throws(ExecutionException::class)
|
||||
|
@ -73,17 +71,12 @@ abstract class ZigExecConfig<T: ZigExecConfig<T>>(project: Project, factory: Con
|
|||
return commandLine
|
||||
}
|
||||
|
||||
fun emulateTerminal(): Boolean {
|
||||
return pty.value
|
||||
}
|
||||
|
||||
override fun clone(): T {
|
||||
val myClone = super.clone() as ZigExecConfig<*>
|
||||
myClone.workingDirectory = workingDirectory.clone()
|
||||
myClone.pty = pty.clone()
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return myClone as T
|
||||
}
|
||||
|
||||
open fun getConfigurables(): List<ZigConfigurable<*>> = listOf(workingDirectory, pty)
|
||||
open fun getConfigurables(): List<ZigConfigurable<*>> = listOf(workingDirectory)
|
||||
}
|
|
@ -23,9 +23,11 @@
|
|||
package com.falsepattern.zigbrains.project.execution.base
|
||||
|
||||
import com.falsepattern.zigbrains.ZigBrainsBundle
|
||||
import com.falsepattern.zigbrains.project.execution.ZigConsoleBuilder
|
||||
import com.falsepattern.zigbrains.project.run.ZigProcessHandler
|
||||
import com.falsepattern.zigbrains.project.settings.zigProjectSettings
|
||||
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain
|
||||
import com.falsepattern.zigbrains.shared.cli.startIPCAwareProcess
|
||||
import com.falsepattern.zigbrains.shared.coroutine.runModalOrBlocking
|
||||
import com.falsepattern.zigbrains.shared.ipc.IPCUtil
|
||||
import com.falsepattern.zigbrains.shared.ipc.ipc
|
||||
|
@ -35,11 +37,15 @@ import com.intellij.execution.ExecutionException
|
|||
import com.intellij.execution.configurations.CommandLineState
|
||||
import com.intellij.execution.configurations.GeneralCommandLine
|
||||
import com.intellij.execution.configurations.PtyCommandLine
|
||||
import com.intellij.execution.filters.TextConsoleBuilder
|
||||
import com.intellij.execution.process.ProcessHandler
|
||||
import com.intellij.execution.process.ProcessTerminatedListener
|
||||
import com.intellij.execution.runners.ExecutionEnvironment
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.platform.ide.progress.ModalTaskOwner
|
||||
import com.intellij.terminal.TerminalExecutionConsole
|
||||
import com.intellij.util.system.OS
|
||||
import kotlin.collections.contains
|
||||
import kotlin.io.path.pathString
|
||||
|
||||
abstract class ZigProfileState<T: ZigExecConfig<T>> (
|
||||
|
@ -47,6 +53,10 @@ abstract class ZigProfileState<T: ZigExecConfig<T>> (
|
|||
val configuration: T
|
||||
): CommandLineState(environment) {
|
||||
|
||||
init {
|
||||
consoleBuilder = ZigConsoleBuilder(environment.project, true)
|
||||
}
|
||||
|
||||
@Throws(ExecutionException::class)
|
||||
override fun startProcess(): ProcessHandler {
|
||||
return runModalOrBlocking({ModalTaskOwner.project(environment.project)}, {"ZigProfileState.startProcess"}) {
|
||||
|
@ -57,7 +67,7 @@ abstract class ZigProfileState<T: ZigExecConfig<T>> (
|
|||
@Throws(ExecutionException::class)
|
||||
suspend fun startProcessSuspend(): ProcessHandler {
|
||||
val toolchain = environment.project.zigProjectSettings.state.toolchain ?: throw ExecutionException(ZigBrainsBundle.message("exception.zig-profile-state.start-process.no-toolchain"))
|
||||
return startProcess(getCommandLine(toolchain, false), environment.project)
|
||||
return getCommandLine(toolchain, false).startIPCAwareProcess(environment.project, emulateTerminal = true)
|
||||
}
|
||||
|
||||
@Throws(ExecutionException::class)
|
||||
|
@ -65,9 +75,7 @@ abstract class ZigProfileState<T: ZigExecConfig<T>> (
|
|||
val workingDir = configuration.workingDirectory
|
||||
val zigExePath = toolchain.zig.path()
|
||||
|
||||
// TODO remove this check once JetBrains implements colored terminal in the debugger
|
||||
// https://youtrack.jetbrains.com/issue/CPP-11622/ANSI-color-codes-not-honored-in-Debug-Run-Configuration-output-window
|
||||
val cli = if (configuration.emulateTerminal() && !debug) PtyCommandLine().withConsoleMode(true).withParentEnvironmentType(GeneralCommandLine.ParentEnvironmentType.CONSOLE) else GeneralCommandLine()
|
||||
val cli = PtyCommandLine().withConsoleMode(false)
|
||||
cli.withExePath(zigExePath.pathString)
|
||||
workingDir.path?.let { cli.withWorkingDirectory(it) }
|
||||
cli.withCharset(Charsets.UTF_8)
|
||||
|
@ -75,22 +83,3 @@ abstract class ZigProfileState<T: ZigExecConfig<T>> (
|
|||
return configuration.patchCommandLine(cli)
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(ExecutionException::class)
|
||||
suspend fun executeCommandLine(commandLine: GeneralCommandLine, environment: ExecutionEnvironment): DefaultExecutionResult {
|
||||
val handler = startProcess(commandLine, environment.project)
|
||||
val console = BuildTextConsoleView(environment.project, false, emptyList())
|
||||
console.attachToProcess(handler)
|
||||
return DefaultExecutionResult(console, handler)
|
||||
}
|
||||
|
||||
@Throws(ExecutionException::class)
|
||||
suspend fun startProcess(commandLine: GeneralCommandLine, project: Project): ProcessHandler {
|
||||
val ipc = IPCUtil.wrapWithIPC(commandLine)
|
||||
val handler = ZigProcessHandler(ipc?.cli ?: commandLine)
|
||||
ProcessTerminatedListener.attach(handler)
|
||||
if (ipc != null) {
|
||||
project.ipc?.launchWatcher(ipc, handler.process)
|
||||
}
|
||||
return handler
|
||||
}
|
||||
|
|
|
@ -25,7 +25,6 @@ package com.falsepattern.zigbrains.project.execution.build
|
|||
import com.falsepattern.zigbrains.ZigBrainsBundle
|
||||
import com.falsepattern.zigbrains.project.execution.base.*
|
||||
import com.falsepattern.zigbrains.shared.ZBFeatures
|
||||
import com.falsepattern.zigbrains.shared.cli.coloredCliFlags
|
||||
import com.intellij.execution.ExecutionException
|
||||
import com.intellij.execution.Executor
|
||||
import com.intellij.execution.configurations.ConfigurationFactory
|
||||
|
@ -37,8 +36,6 @@ class ZigExecConfigBuild(project: Project, factory: ConfigurationFactory): ZigEx
|
|||
private set
|
||||
var extraArgs = ArgsConfigurable("compilerArgs", ZigBrainsBundle.message("exec.option.label.build.args"))
|
||||
private set
|
||||
var colored = ColoredConfigurable("colored")
|
||||
private set
|
||||
var debugBuildSteps = ArgsConfigurable("debugBuildSteps", ZigBrainsBundle.message("exec.option.label.build.steps-debug"))
|
||||
private set
|
||||
var debugExtraArgs = ArgsConfigurable("debugCompilerArgs", ZigBrainsBundle.message("exec.option.label.build.args-debug"))
|
||||
|
@ -54,7 +51,6 @@ class ZigExecConfigBuild(project: Project, factory: ConfigurationFactory): ZigEx
|
|||
result.add("build")
|
||||
val steps = if (debug) debugBuildSteps.argsSplit() else buildSteps.argsSplit()
|
||||
result.addAll(steps)
|
||||
result.addAll(coloredCliFlags(colored.value, debug))
|
||||
result.addAll(if (debug) debugExtraArgs.argsSplit() else extraArgs.argsSplit())
|
||||
return result
|
||||
}
|
||||
|
@ -66,14 +62,13 @@ class ZigExecConfigBuild(project: Project, factory: ConfigurationFactory): ZigEx
|
|||
val clone = super.clone()
|
||||
clone.buildSteps = buildSteps.clone()
|
||||
clone.exeArgs = exeArgs.clone()
|
||||
clone.colored = colored.clone()
|
||||
clone.exePath = exePath.clone()
|
||||
clone.exeArgs = exeArgs.clone()
|
||||
return clone
|
||||
}
|
||||
|
||||
override fun getConfigurables(): List<ZigConfigurable<*>> {
|
||||
val baseCfg = super.getConfigurables() + listOf(buildSteps, extraArgs, colored)
|
||||
val baseCfg = super.getConfigurables() + listOf(buildSteps, extraArgs)
|
||||
return if (ZBFeatures.debug()) {
|
||||
baseCfg + listOf(debugBuildSteps, debugExtraArgs, exePath, exeArgs)
|
||||
} else {
|
||||
|
|
|
@ -24,7 +24,6 @@ package com.falsepattern.zigbrains.project.execution.run
|
|||
|
||||
import com.falsepattern.zigbrains.ZigBrainsBundle
|
||||
import com.falsepattern.zigbrains.project.execution.base.*
|
||||
import com.falsepattern.zigbrains.shared.cli.coloredCliFlags
|
||||
import com.intellij.execution.ExecutionException
|
||||
import com.intellij.execution.Executor
|
||||
import com.intellij.execution.configurations.ConfigurationFactory
|
||||
|
@ -35,8 +34,6 @@ import kotlin.io.path.pathString
|
|||
class ZigExecConfigRun(project: Project, factory: ConfigurationFactory): ZigExecConfig<ZigExecConfigRun>(project, factory, ZigBrainsBundle.message("exec.type.run.label")) {
|
||||
var filePath = FilePathConfigurable("filePath", ZigBrainsBundle.message("exec.option.label.file-path"))
|
||||
private set
|
||||
var colored = ColoredConfigurable("colored")
|
||||
private set
|
||||
var optimization = OptimizationConfigurable("optimization")
|
||||
private set
|
||||
var compilerArgs = ArgsConfigurable("compilerArgs", ZigBrainsBundle.message("exec.option.label.compiler-args"))
|
||||
|
@ -47,7 +44,6 @@ class ZigExecConfigRun(project: Project, factory: ConfigurationFactory): ZigExec
|
|||
override suspend fun buildCommandLineArgs(debug: Boolean): List<String> {
|
||||
val result = ArrayList<String>()
|
||||
result.add(if (debug) "build-exe" else "run")
|
||||
result.addAll(coloredCliFlags(colored.value, debug))
|
||||
result.add(filePath.path?.pathString ?: throw ExecutionException(ZigBrainsBundle.message("exception.zig.empty-file-path")))
|
||||
if (!debug || optimization.forced) {
|
||||
result.addAll(listOf("-O", optimization.level.name))
|
||||
|
@ -66,7 +62,6 @@ class ZigExecConfigRun(project: Project, factory: ConfigurationFactory): ZigExec
|
|||
override fun clone(): ZigExecConfigRun {
|
||||
val clone = super.clone()
|
||||
clone.filePath = filePath.clone()
|
||||
clone.colored = colored.clone()
|
||||
clone.compilerArgs = compilerArgs.clone()
|
||||
clone.optimization = optimization.clone()
|
||||
clone.exeArgs = exeArgs.clone()
|
||||
|
@ -74,7 +69,7 @@ class ZigExecConfigRun(project: Project, factory: ConfigurationFactory): ZigExec
|
|||
}
|
||||
|
||||
override fun getConfigurables(): List<ZigConfigurable<*>> {
|
||||
return super.getConfigurables() + listOf(filePath, optimization, colored, compilerArgs, exeArgs)
|
||||
return super.getConfigurables() + listOf(filePath, optimization, compilerArgs, exeArgs)
|
||||
}
|
||||
|
||||
override fun getState(executor: Executor, environment: ExecutionEnvironment): ZigProfileState<ZigExecConfigRun> {
|
||||
|
|
|
@ -24,7 +24,6 @@ package com.falsepattern.zigbrains.project.execution.test
|
|||
|
||||
import com.falsepattern.zigbrains.ZigBrainsBundle
|
||||
import com.falsepattern.zigbrains.project.execution.base.*
|
||||
import com.falsepattern.zigbrains.shared.cli.coloredCliFlags
|
||||
import com.intellij.execution.ExecutionException
|
||||
import com.intellij.execution.Executor
|
||||
import com.intellij.execution.configurations.ConfigurationFactory
|
||||
|
@ -35,8 +34,6 @@ import kotlin.io.path.pathString
|
|||
class ZigExecConfigTest(project: Project, factory: ConfigurationFactory): ZigExecConfig<ZigExecConfigTest>(project, factory, ZigBrainsBundle.message("exec.type.test.label")) {
|
||||
var filePath = FilePathConfigurable("filePath", ZigBrainsBundle.message("exec.option.label.file-path"))
|
||||
private set
|
||||
var colored = ColoredConfigurable("colored")
|
||||
private set
|
||||
var optimization = OptimizationConfigurable("optimization")
|
||||
private set
|
||||
var compilerArgs = ArgsConfigurable("compilerArgs", ZigBrainsBundle.message("exec.option.label.compiler-args"))
|
||||
|
@ -46,7 +43,6 @@ class ZigExecConfigTest(project: Project, factory: ConfigurationFactory): ZigExe
|
|||
override suspend fun buildCommandLineArgs(debug: Boolean): List<String> {
|
||||
val result = ArrayList<String>()
|
||||
result.add("test")
|
||||
result.addAll(coloredCliFlags(colored.value, debug))
|
||||
result.add(filePath.path?.pathString ?: throw ExecutionException(ZigBrainsBundle.message("exception.zig.empty-file-path")))
|
||||
if (!debug || optimization.forced) {
|
||||
result.addAll(listOf("-O", optimization.level.name))
|
||||
|
@ -64,14 +60,13 @@ class ZigExecConfigTest(project: Project, factory: ConfigurationFactory): ZigExe
|
|||
override fun clone(): ZigExecConfigTest {
|
||||
val clone = super.clone()
|
||||
clone.filePath = filePath.clone()
|
||||
clone.colored = colored.clone()
|
||||
clone.compilerArgs = compilerArgs.clone()
|
||||
clone.optimization = optimization.clone()
|
||||
return clone
|
||||
}
|
||||
|
||||
override fun getConfigurables(): List<ZigConfigurable<*>> {
|
||||
return super.getConfigurables() + listOf(filePath, optimization, colored, compilerArgs)
|
||||
return super.getConfigurables() + listOf(filePath, optimization, compilerArgs)
|
||||
}
|
||||
|
||||
override fun getState(executor: Executor, environment: ExecutionEnvironment): ZigProfileState<ZigExecConfigTest> {
|
||||
|
|
|
@ -26,11 +26,12 @@ import com.intellij.execution.configurations.GeneralCommandLine
|
|||
import com.intellij.execution.configurations.PtyCommandLine
|
||||
import com.intellij.execution.process.AnsiEscapeDecoder.ColoredTextAcceptor
|
||||
import com.intellij.execution.process.KillableColoredProcessHandler
|
||||
import com.intellij.execution.process.KillableProcessHandler
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.pty4j.PtyProcess
|
||||
import java.nio.charset.Charset
|
||||
|
||||
class ZigProcessHandler : KillableColoredProcessHandler, ColoredTextAcceptor {
|
||||
class ZigProcessHandler : KillableProcessHandler {
|
||||
constructor(commandLine: GeneralCommandLine) : super(commandLine) {
|
||||
setHasPty(commandLine is PtyCommandLine)
|
||||
setShouldDestroyProcessRecursively(!hasPty())
|
||||
|
@ -40,57 +41,4 @@ class ZigProcessHandler : KillableColoredProcessHandler, ColoredTextAcceptor {
|
|||
setHasPty(process is PtyProcess)
|
||||
setShouldDestroyProcessRecursively(!hasPty())
|
||||
}
|
||||
|
||||
override fun coloredTextAvailable(text: String, attributes: Key<*>) {
|
||||
super.coloredTextAvailable(text.translateVT100Escapes(), attributes)
|
||||
}
|
||||
}
|
||||
|
||||
private val VT100_CHARS = CharArray(256).apply {
|
||||
this.fill(' ')
|
||||
this[0x6A] = '┘'
|
||||
this[0x6B] = '┐'
|
||||
this[0x6C] = '┌'
|
||||
this[0x6D] = '└'
|
||||
this[0x6E] = '┼'
|
||||
this[0x71] = '─'
|
||||
this[0x74] = '├'
|
||||
this[0x75] = '┤'
|
||||
this[0x76] = '┴'
|
||||
this[0x77] = '┬'
|
||||
this[0x78] = '│'
|
||||
}
|
||||
|
||||
private const val VT100_BEGIN_SEQ = "\u001B(0"
|
||||
private const val VT100_END_SEQ = "\u001B(B"
|
||||
private const val VT100_BEGIN_SEQ_LENGTH: Int = VT100_BEGIN_SEQ.length
|
||||
private const val VT100_END_SEQ_LENGTH: Int = VT100_END_SEQ.length
|
||||
|
||||
private fun String.translateVT100Escapes(): String {
|
||||
var offset = 0
|
||||
val result = StringBuilder()
|
||||
val textLength = length
|
||||
while (offset < textLength) {
|
||||
val startIndex = indexOf(VT100_BEGIN_SEQ, offset)
|
||||
if (startIndex < 0) {
|
||||
result.append(substring(offset, textLength).replace(VT100_END_SEQ, ""))
|
||||
break
|
||||
}
|
||||
result.append(this, offset, startIndex)
|
||||
val blockOffset = startIndex + VT100_BEGIN_SEQ_LENGTH
|
||||
var endIndex = indexOf(VT100_END_SEQ, blockOffset)
|
||||
if (endIndex < 0) {
|
||||
endIndex = textLength
|
||||
}
|
||||
for (i in blockOffset until endIndex) {
|
||||
val c = this[i].code
|
||||
if (c >= 256) {
|
||||
result.append(c)
|
||||
} else {
|
||||
result.append(VT100_CHARS[c])
|
||||
}
|
||||
}
|
||||
offset = endIndex + VT100_END_SEQ_LENGTH
|
||||
}
|
||||
return result.toString()
|
||||
}
|
|
@ -24,7 +24,6 @@ package com.falsepattern.zigbrains.project.run
|
|||
|
||||
import com.falsepattern.zigbrains.project.execution.base.ZigExecConfig
|
||||
import com.falsepattern.zigbrains.project.execution.base.ZigProfileState
|
||||
import com.falsepattern.zigbrains.project.execution.base.executeCommandLine
|
||||
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain
|
||||
import com.falsepattern.zigbrains.shared.coroutine.withEDTContext
|
||||
import com.intellij.execution.configurations.RunProfile
|
||||
|
@ -37,7 +36,7 @@ import com.intellij.openapi.application.ModalityState
|
|||
class ZigRegularRunner: ZigProgramRunner<ZigProfileState<*>>(DefaultRunExecutor.EXECUTOR_ID) {
|
||||
override suspend fun execute(state: ZigProfileState<*>, toolchain: AbstractZigToolchain, environment: ExecutionEnvironment): RunContentDescriptor? {
|
||||
val cli = state.getCommandLine(toolchain, false)
|
||||
val exec = executeCommandLine(cli, environment)
|
||||
val exec = state.execute(environment.executor, this)
|
||||
return withEDTContext(ModalityState.any()) {
|
||||
val runContentBuilder = RunContentBuilder(exec, environment)
|
||||
runContentBuilder.showRunContent(null)
|
||||
|
|
|
@ -23,13 +23,18 @@
|
|||
package com.falsepattern.zigbrains.shared.cli
|
||||
|
||||
import com.falsepattern.zigbrains.ZigBrainsBundle
|
||||
import com.falsepattern.zigbrains.project.run.ZigProcessHandler
|
||||
import com.falsepattern.zigbrains.shared.ipc.IPCUtil
|
||||
import com.falsepattern.zigbrains.shared.ipc.ipc
|
||||
import com.intellij.execution.ExecutionException
|
||||
import com.intellij.execution.configurations.GeneralCommandLine
|
||||
import com.intellij.execution.process.ProcessHandler
|
||||
import com.intellij.execution.process.ProcessOutput
|
||||
import com.intellij.execution.process.ProcessTerminatedListener
|
||||
import com.intellij.openapi.options.ConfigurationException
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.util.io.awaitExit
|
||||
import com.intellij.util.system.OS
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.runInterruptible
|
||||
import kotlinx.coroutines.withContext
|
||||
|
@ -108,14 +113,6 @@ fun translateCommandline(toProcess: String): List<String> {
|
|||
return result
|
||||
}
|
||||
|
||||
fun coloredCliFlags(colored: Boolean, debug: Boolean): List<String> {
|
||||
return if (debug) {
|
||||
emptyList()
|
||||
} else {
|
||||
listOf("--color", if (colored) "on" else "off")
|
||||
}
|
||||
}
|
||||
|
||||
fun createCommandLineSafe(
|
||||
workingDirectory: Path?,
|
||||
exe: Path,
|
||||
|
@ -133,14 +130,27 @@ fun createCommandLineSafe(
|
|||
return Result.success(cli)
|
||||
}
|
||||
|
||||
suspend fun GeneralCommandLine.call(timeoutMillis: Long = Long.MAX_VALUE, ipcProject: Project? = null): Result<ProcessOutput> {
|
||||
val ipc = if (ipcProject != null) IPCUtil.wrapWithIPC(this) else null
|
||||
@Throws(ExecutionException::class)
|
||||
suspend fun GeneralCommandLine.startIPCAwareProcess(project: Project?, emulateTerminal: Boolean = false): ZigProcessHandler {
|
||||
val ipc = if (project != null && !emulateTerminal) IPCUtil.wrapWithIPC(this) else null
|
||||
val cli = ipc?.cli ?: this
|
||||
val (process, exitCode) = withContext(Dispatchers.IO) {
|
||||
val process = cli.createProcess()
|
||||
if (ipc != null) {
|
||||
ipcProject!!.ipc?.launchWatcher(ipc, process)
|
||||
if (emulateTerminal && OS.CURRENT != OS.Windows && !cli.environment.contains("TERM")) {
|
||||
cli.withEnvironment("TERM", "xterm-256color")
|
||||
}
|
||||
val handler = ZigProcessHandler(cli)
|
||||
ProcessTerminatedListener.attach(handler)
|
||||
|
||||
if (ipc != null) {
|
||||
project!!.ipc?.launchWatcher(ipc, handler.process)
|
||||
}
|
||||
return handler
|
||||
}
|
||||
|
||||
|
||||
suspend fun GeneralCommandLine.call(timeoutMillis: Long = Long.MAX_VALUE, ipcProject: Project? = null): Result<ProcessOutput> {
|
||||
val (process, exitCode) = withContext(Dispatchers.IO) {
|
||||
val handler = startIPCAwareProcess(ipcProject)
|
||||
val process = handler.process
|
||||
val exit = withTimeoutOrNull(timeoutMillis) {
|
||||
process.awaitExit()
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue