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]
|
## [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
|
### Fixed
|
||||||
|
|
||||||
- Zig
|
- Zig
|
||||||
|
|
|
@ -57,8 +57,8 @@ class ZigClionDebuggerDriverConfigurationProvider: ZigDebuggerDriverConfiguratio
|
||||||
}
|
}
|
||||||
return when(toolchain.debuggerKind) {
|
return when(toolchain.debuggerKind) {
|
||||||
CPPDebugger.Kind.BUNDLED_GDB,
|
CPPDebugger.Kind.BUNDLED_GDB,
|
||||||
CPPDebugger.Kind.CUSTOM_GDB -> CLionGDBDriverConfiguration(project, toolchain)
|
CPPDebugger.Kind.CUSTOM_GDB -> CLionGDBDriverConfiguration(project, toolchain, isEmulateTerminal = emulateTerminal)
|
||||||
CPPDebugger.Kind.BUNDLED_LLDB -> CLionLLDBDriverConfiguration(project, toolchain)
|
CPPDebugger.Kind.BUNDLED_LLDB -> CLionLLDBDriverConfiguration(project, toolchain, isEmulateTerminal = emulateTerminal)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,9 +22,10 @@
|
||||||
|
|
||||||
package com.falsepattern.zigbrains.debugger
|
package com.falsepattern.zigbrains.debugger
|
||||||
|
|
||||||
|
import com.intellij.execution.filters.Filter
|
||||||
import com.intellij.execution.filters.TextConsoleBuilder
|
import com.intellij.execution.filters.TextConsoleBuilder
|
||||||
import com.intellij.xdebugger.XDebugSession
|
import com.intellij.xdebugger.XDebugSession
|
||||||
import com.jetbrains.cidr.execution.RunParameters
|
import com.jetbrains.cidr.execution.RunParameters
|
||||||
import com.jetbrains.cidr.execution.debugger.CidrLocalDebugProcess
|
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
|
package com.falsepattern.zigbrains.debugger.runner.base
|
||||||
|
|
||||||
import com.falsepattern.zigbrains.project.run.ZigProcessHandler
|
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.IPCUtil
|
||||||
import com.falsepattern.zigbrains.shared.ipc.ipc
|
import com.falsepattern.zigbrains.shared.ipc.ipc
|
||||||
import com.intellij.execution.ExecutionException
|
import com.intellij.execution.ExecutionException
|
||||||
|
@ -48,12 +49,7 @@ class PreLaunchProcessListener(val console: ConsoleView) : ProcessListener {
|
||||||
@Throws(ExecutionException::class)
|
@Throws(ExecutionException::class)
|
||||||
suspend fun executeCommandLineWithHook(project: Project, commandLine: GeneralCommandLine): Boolean {
|
suspend fun executeCommandLineWithHook(project: Project, commandLine: GeneralCommandLine): Boolean {
|
||||||
return withProgressText(commandLine.commandLineString) {
|
return withProgressText(commandLine.commandLineString) {
|
||||||
val ipc = IPCUtil.wrapWithIPC(commandLine)
|
val processHandler = commandLine.startIPCAwareProcess(project)
|
||||||
val cli = ipc?.cli ?: commandLine
|
|
||||||
val processHandler = ZigProcessHandler(cli)
|
|
||||||
if (ipc != null) {
|
|
||||||
project.ipc?.launchWatcher(ipc, processHandler.process)
|
|
||||||
}
|
|
||||||
this@PreLaunchProcessListener.processHandler = processHandler
|
this@PreLaunchProcessListener.processHandler = processHandler
|
||||||
hook(processHandler)
|
hook(processHandler)
|
||||||
processHandler.startNotify()
|
processHandler.startNotify()
|
||||||
|
@ -74,10 +70,6 @@ class PreLaunchProcessListener(val console: ConsoleView) : ProcessListener {
|
||||||
|
|
||||||
override fun processTerminated(event: ProcessEvent) {
|
override fun processTerminated(event: ProcessEvent) {
|
||||||
if (event.exitCode != 0) {
|
if (event.exitCode != 0) {
|
||||||
console.print(
|
|
||||||
"Process finished with exit code " + event.exitCode,
|
|
||||||
ConsoleViewContentType.NORMAL_OUTPUT
|
|
||||||
)
|
|
||||||
isBuildFailed = true
|
isBuildFailed = true
|
||||||
} else {
|
} else {
|
||||||
isBuildFailed = false
|
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.project.toolchain.AbstractZigToolchain
|
||||||
import com.falsepattern.zigbrains.shared.zigCoroutineScope
|
import com.falsepattern.zigbrains.shared.zigCoroutineScope
|
||||||
import com.intellij.execution.configurations.GeneralCommandLine
|
import com.intellij.execution.configurations.GeneralCommandLine
|
||||||
|
import com.intellij.execution.configurations.PtyCommandLine
|
||||||
import com.jetbrains.cidr.execution.Installer
|
import com.jetbrains.cidr.execution.Installer
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.future.asCompletableFuture
|
import kotlinx.coroutines.future.asCompletableFuture
|
||||||
|
@ -39,7 +40,7 @@ class ZigDebugEmitBinaryInstaller<ProfileState: ZigProfileState<*>>(
|
||||||
): Installer {
|
): Installer {
|
||||||
override fun install(): GeneralCommandLine {
|
override fun install(): GeneralCommandLine {
|
||||||
val cfg = profileState.configuration
|
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) }
|
cfg.workingDirectory.path?.let { x -> cli.withWorkingDirectory(x) }
|
||||||
cli.addParameters(exeArgs)
|
cli.addParameters(exeArgs)
|
||||||
cli.withCharset(Charsets.UTF_8)
|
cli.withCharset(Charsets.UTF_8)
|
||||||
|
|
|
@ -58,7 +58,7 @@ abstract class ZigDebugRunnerBase<ProfileState : ZigProfileState<*>> : ZigProgra
|
||||||
val project = environment.project
|
val project = environment.project
|
||||||
val driverProviders = ZigDebuggerDriverConfigurationProviderBase.EXTENSION_POINT_NAME.extensionList
|
val driverProviders = ZigDebuggerDriverConfigurationProviderBase.EXTENSION_POINT_NAME.extensionList
|
||||||
for (provider in driverProviders) {
|
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 executeWithDriver(state, toolchain, environment, driver) ?: continue
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
|
@ -98,7 +98,7 @@ abstract class ZigDebugRunnerBase<ProfileState : ZigProfileState<*>> : ZigProgra
|
||||||
debuggerManager.startSession(environment, object: XDebugProcessStarter() {
|
debuggerManager.startSession(environment, object: XDebugProcessStarter() {
|
||||||
override fun start(session: XDebugSession): XDebugProcess {
|
override fun start(session: XDebugSession): XDebugProcess {
|
||||||
val project = session.project
|
val project = session.project
|
||||||
val textConsoleBuilder = SharedConsoleBuilder(console)
|
val textConsoleBuilder = state.consoleBuilder
|
||||||
val debugProcess = ZigLocalDebugProcess(runParameters, session, textConsoleBuilder)
|
val debugProcess = ZigLocalDebugProcess(runParameters, session, textConsoleBuilder)
|
||||||
ProcessTerminatedListener.attach(debugProcess.processHandler, project)
|
ProcessTerminatedListener.attach(debugProcess.processHandler, project)
|
||||||
debugProcess.start()
|
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(
|
class OptimizationConfigurable(
|
||||||
@Transient private val serializedName: String,
|
@Transient private val serializedName: String,
|
||||||
var level: OptimizationLevel = OptimizationLevel.Debug,
|
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) {
|
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() }
|
var workingDirectory = WorkDirectoryConfigurable("workingDirectory").apply { path = project.guessProjectDir()?.toNioPathOrNull() }
|
||||||
private set
|
private set
|
||||||
var pty = CheckboxConfigurable("pty", ZigBrainsBundle.message("exec.option.label.emulate-terminal"), false)
|
|
||||||
private set
|
|
||||||
|
|
||||||
abstract val suggestedName: @ActionText String
|
abstract val suggestedName: @ActionText String
|
||||||
@Throws(ExecutionException::class)
|
@Throws(ExecutionException::class)
|
||||||
|
@ -73,17 +71,12 @@ abstract class ZigExecConfig<T: ZigExecConfig<T>>(project: Project, factory: Con
|
||||||
return commandLine
|
return commandLine
|
||||||
}
|
}
|
||||||
|
|
||||||
fun emulateTerminal(): Boolean {
|
|
||||||
return pty.value
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun clone(): T {
|
override fun clone(): T {
|
||||||
val myClone = super.clone() as ZigExecConfig<*>
|
val myClone = super.clone() as ZigExecConfig<*>
|
||||||
myClone.workingDirectory = workingDirectory.clone()
|
myClone.workingDirectory = workingDirectory.clone()
|
||||||
myClone.pty = pty.clone()
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
return myClone as T
|
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
|
package com.falsepattern.zigbrains.project.execution.base
|
||||||
|
|
||||||
import com.falsepattern.zigbrains.ZigBrainsBundle
|
import com.falsepattern.zigbrains.ZigBrainsBundle
|
||||||
|
import com.falsepattern.zigbrains.project.execution.ZigConsoleBuilder
|
||||||
import com.falsepattern.zigbrains.project.run.ZigProcessHandler
|
import com.falsepattern.zigbrains.project.run.ZigProcessHandler
|
||||||
import com.falsepattern.zigbrains.project.settings.zigProjectSettings
|
import com.falsepattern.zigbrains.project.settings.zigProjectSettings
|
||||||
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain
|
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.coroutine.runModalOrBlocking
|
||||||
import com.falsepattern.zigbrains.shared.ipc.IPCUtil
|
import com.falsepattern.zigbrains.shared.ipc.IPCUtil
|
||||||
import com.falsepattern.zigbrains.shared.ipc.ipc
|
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.CommandLineState
|
||||||
import com.intellij.execution.configurations.GeneralCommandLine
|
import com.intellij.execution.configurations.GeneralCommandLine
|
||||||
import com.intellij.execution.configurations.PtyCommandLine
|
import com.intellij.execution.configurations.PtyCommandLine
|
||||||
|
import com.intellij.execution.filters.TextConsoleBuilder
|
||||||
import com.intellij.execution.process.ProcessHandler
|
import com.intellij.execution.process.ProcessHandler
|
||||||
import com.intellij.execution.process.ProcessTerminatedListener
|
import com.intellij.execution.process.ProcessTerminatedListener
|
||||||
import com.intellij.execution.runners.ExecutionEnvironment
|
import com.intellij.execution.runners.ExecutionEnvironment
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.platform.ide.progress.ModalTaskOwner
|
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
|
import kotlin.io.path.pathString
|
||||||
|
|
||||||
abstract class ZigProfileState<T: ZigExecConfig<T>> (
|
abstract class ZigProfileState<T: ZigExecConfig<T>> (
|
||||||
|
@ -47,6 +53,10 @@ abstract class ZigProfileState<T: ZigExecConfig<T>> (
|
||||||
val configuration: T
|
val configuration: T
|
||||||
): CommandLineState(environment) {
|
): CommandLineState(environment) {
|
||||||
|
|
||||||
|
init {
|
||||||
|
consoleBuilder = ZigConsoleBuilder(environment.project, true)
|
||||||
|
}
|
||||||
|
|
||||||
@Throws(ExecutionException::class)
|
@Throws(ExecutionException::class)
|
||||||
override fun startProcess(): ProcessHandler {
|
override fun startProcess(): ProcessHandler {
|
||||||
return runModalOrBlocking({ModalTaskOwner.project(environment.project)}, {"ZigProfileState.startProcess"}) {
|
return runModalOrBlocking({ModalTaskOwner.project(environment.project)}, {"ZigProfileState.startProcess"}) {
|
||||||
|
@ -57,7 +67,7 @@ abstract class ZigProfileState<T: ZigExecConfig<T>> (
|
||||||
@Throws(ExecutionException::class)
|
@Throws(ExecutionException::class)
|
||||||
suspend fun startProcessSuspend(): ProcessHandler {
|
suspend fun startProcessSuspend(): ProcessHandler {
|
||||||
val toolchain = environment.project.zigProjectSettings.state.toolchain ?: throw ExecutionException(ZigBrainsBundle.message("exception.zig-profile-state.start-process.no-toolchain"))
|
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)
|
@Throws(ExecutionException::class)
|
||||||
|
@ -65,32 +75,11 @@ abstract class ZigProfileState<T: ZigExecConfig<T>> (
|
||||||
val workingDir = configuration.workingDirectory
|
val workingDir = configuration.workingDirectory
|
||||||
val zigExePath = toolchain.zig.path()
|
val zigExePath = toolchain.zig.path()
|
||||||
|
|
||||||
// TODO remove this check once JetBrains implements colored terminal in the debugger
|
val cli = PtyCommandLine().withConsoleMode(false)
|
||||||
// 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()
|
|
||||||
cli.withExePath(zigExePath.pathString)
|
cli.withExePath(zigExePath.pathString)
|
||||||
workingDir.path?.let { cli.withWorkingDirectory(it) }
|
workingDir.path?.let { cli.withWorkingDirectory(it) }
|
||||||
cli.withCharset(Charsets.UTF_8)
|
cli.withCharset(Charsets.UTF_8)
|
||||||
cli.addParameters(configuration.buildCommandLineArgs(debug))
|
cli.addParameters(configuration.buildCommandLineArgs(debug))
|
||||||
return configuration.patchCommandLine(cli)
|
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.ZigBrainsBundle
|
||||||
import com.falsepattern.zigbrains.project.execution.base.*
|
import com.falsepattern.zigbrains.project.execution.base.*
|
||||||
import com.falsepattern.zigbrains.shared.ZBFeatures
|
import com.falsepattern.zigbrains.shared.ZBFeatures
|
||||||
import com.falsepattern.zigbrains.shared.cli.coloredCliFlags
|
|
||||||
import com.intellij.execution.ExecutionException
|
import com.intellij.execution.ExecutionException
|
||||||
import com.intellij.execution.Executor
|
import com.intellij.execution.Executor
|
||||||
import com.intellij.execution.configurations.ConfigurationFactory
|
import com.intellij.execution.configurations.ConfigurationFactory
|
||||||
|
@ -37,8 +36,6 @@ class ZigExecConfigBuild(project: Project, factory: ConfigurationFactory): ZigEx
|
||||||
private set
|
private set
|
||||||
var extraArgs = ArgsConfigurable("compilerArgs", ZigBrainsBundle.message("exec.option.label.build.args"))
|
var extraArgs = ArgsConfigurable("compilerArgs", ZigBrainsBundle.message("exec.option.label.build.args"))
|
||||||
private set
|
private set
|
||||||
var colored = ColoredConfigurable("colored")
|
|
||||||
private set
|
|
||||||
var debugBuildSteps = ArgsConfigurable("debugBuildSteps", ZigBrainsBundle.message("exec.option.label.build.steps-debug"))
|
var debugBuildSteps = ArgsConfigurable("debugBuildSteps", ZigBrainsBundle.message("exec.option.label.build.steps-debug"))
|
||||||
private set
|
private set
|
||||||
var debugExtraArgs = ArgsConfigurable("debugCompilerArgs", ZigBrainsBundle.message("exec.option.label.build.args-debug"))
|
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")
|
result.add("build")
|
||||||
val steps = if (debug) debugBuildSteps.argsSplit() else buildSteps.argsSplit()
|
val steps = if (debug) debugBuildSteps.argsSplit() else buildSteps.argsSplit()
|
||||||
result.addAll(steps)
|
result.addAll(steps)
|
||||||
result.addAll(coloredCliFlags(colored.value, debug))
|
|
||||||
result.addAll(if (debug) debugExtraArgs.argsSplit() else extraArgs.argsSplit())
|
result.addAll(if (debug) debugExtraArgs.argsSplit() else extraArgs.argsSplit())
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
@ -66,14 +62,13 @@ class ZigExecConfigBuild(project: Project, factory: ConfigurationFactory): ZigEx
|
||||||
val clone = super.clone()
|
val clone = super.clone()
|
||||||
clone.buildSteps = buildSteps.clone()
|
clone.buildSteps = buildSteps.clone()
|
||||||
clone.exeArgs = exeArgs.clone()
|
clone.exeArgs = exeArgs.clone()
|
||||||
clone.colored = colored.clone()
|
|
||||||
clone.exePath = exePath.clone()
|
clone.exePath = exePath.clone()
|
||||||
clone.exeArgs = exeArgs.clone()
|
clone.exeArgs = exeArgs.clone()
|
||||||
return clone
|
return clone
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getConfigurables(): List<ZigConfigurable<*>> {
|
override fun getConfigurables(): List<ZigConfigurable<*>> {
|
||||||
val baseCfg = super.getConfigurables() + listOf(buildSteps, extraArgs, colored)
|
val baseCfg = super.getConfigurables() + listOf(buildSteps, extraArgs)
|
||||||
return if (ZBFeatures.debug()) {
|
return if (ZBFeatures.debug()) {
|
||||||
baseCfg + listOf(debugBuildSteps, debugExtraArgs, exePath, exeArgs)
|
baseCfg + listOf(debugBuildSteps, debugExtraArgs, exePath, exeArgs)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -24,7 +24,6 @@ package com.falsepattern.zigbrains.project.execution.run
|
||||||
|
|
||||||
import com.falsepattern.zigbrains.ZigBrainsBundle
|
import com.falsepattern.zigbrains.ZigBrainsBundle
|
||||||
import com.falsepattern.zigbrains.project.execution.base.*
|
import com.falsepattern.zigbrains.project.execution.base.*
|
||||||
import com.falsepattern.zigbrains.shared.cli.coloredCliFlags
|
|
||||||
import com.intellij.execution.ExecutionException
|
import com.intellij.execution.ExecutionException
|
||||||
import com.intellij.execution.Executor
|
import com.intellij.execution.Executor
|
||||||
import com.intellij.execution.configurations.ConfigurationFactory
|
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")) {
|
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"))
|
var filePath = FilePathConfigurable("filePath", ZigBrainsBundle.message("exec.option.label.file-path"))
|
||||||
private set
|
private set
|
||||||
var colored = ColoredConfigurable("colored")
|
|
||||||
private set
|
|
||||||
var optimization = OptimizationConfigurable("optimization")
|
var optimization = OptimizationConfigurable("optimization")
|
||||||
private set
|
private set
|
||||||
var compilerArgs = ArgsConfigurable("compilerArgs", ZigBrainsBundle.message("exec.option.label.compiler-args"))
|
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> {
|
override suspend fun buildCommandLineArgs(debug: Boolean): List<String> {
|
||||||
val result = ArrayList<String>()
|
val result = ArrayList<String>()
|
||||||
result.add(if (debug) "build-exe" else "run")
|
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")))
|
result.add(filePath.path?.pathString ?: throw ExecutionException(ZigBrainsBundle.message("exception.zig.empty-file-path")))
|
||||||
if (!debug || optimization.forced) {
|
if (!debug || optimization.forced) {
|
||||||
result.addAll(listOf("-O", optimization.level.name))
|
result.addAll(listOf("-O", optimization.level.name))
|
||||||
|
@ -66,7 +62,6 @@ class ZigExecConfigRun(project: Project, factory: ConfigurationFactory): ZigExec
|
||||||
override fun clone(): ZigExecConfigRun {
|
override fun clone(): ZigExecConfigRun {
|
||||||
val clone = super.clone()
|
val clone = super.clone()
|
||||||
clone.filePath = filePath.clone()
|
clone.filePath = filePath.clone()
|
||||||
clone.colored = colored.clone()
|
|
||||||
clone.compilerArgs = compilerArgs.clone()
|
clone.compilerArgs = compilerArgs.clone()
|
||||||
clone.optimization = optimization.clone()
|
clone.optimization = optimization.clone()
|
||||||
clone.exeArgs = exeArgs.clone()
|
clone.exeArgs = exeArgs.clone()
|
||||||
|
@ -74,7 +69,7 @@ class ZigExecConfigRun(project: Project, factory: ConfigurationFactory): ZigExec
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getConfigurables(): List<ZigConfigurable<*>> {
|
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> {
|
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.ZigBrainsBundle
|
||||||
import com.falsepattern.zigbrains.project.execution.base.*
|
import com.falsepattern.zigbrains.project.execution.base.*
|
||||||
import com.falsepattern.zigbrains.shared.cli.coloredCliFlags
|
|
||||||
import com.intellij.execution.ExecutionException
|
import com.intellij.execution.ExecutionException
|
||||||
import com.intellij.execution.Executor
|
import com.intellij.execution.Executor
|
||||||
import com.intellij.execution.configurations.ConfigurationFactory
|
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")) {
|
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"))
|
var filePath = FilePathConfigurable("filePath", ZigBrainsBundle.message("exec.option.label.file-path"))
|
||||||
private set
|
private set
|
||||||
var colored = ColoredConfigurable("colored")
|
|
||||||
private set
|
|
||||||
var optimization = OptimizationConfigurable("optimization")
|
var optimization = OptimizationConfigurable("optimization")
|
||||||
private set
|
private set
|
||||||
var compilerArgs = ArgsConfigurable("compilerArgs", ZigBrainsBundle.message("exec.option.label.compiler-args"))
|
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> {
|
override suspend fun buildCommandLineArgs(debug: Boolean): List<String> {
|
||||||
val result = ArrayList<String>()
|
val result = ArrayList<String>()
|
||||||
result.add("test")
|
result.add("test")
|
||||||
result.addAll(coloredCliFlags(colored.value, debug))
|
|
||||||
result.add(filePath.path?.pathString ?: throw ExecutionException(ZigBrainsBundle.message("exception.zig.empty-file-path")))
|
result.add(filePath.path?.pathString ?: throw ExecutionException(ZigBrainsBundle.message("exception.zig.empty-file-path")))
|
||||||
if (!debug || optimization.forced) {
|
if (!debug || optimization.forced) {
|
||||||
result.addAll(listOf("-O", optimization.level.name))
|
result.addAll(listOf("-O", optimization.level.name))
|
||||||
|
@ -64,14 +60,13 @@ class ZigExecConfigTest(project: Project, factory: ConfigurationFactory): ZigExe
|
||||||
override fun clone(): ZigExecConfigTest {
|
override fun clone(): ZigExecConfigTest {
|
||||||
val clone = super.clone()
|
val clone = super.clone()
|
||||||
clone.filePath = filePath.clone()
|
clone.filePath = filePath.clone()
|
||||||
clone.colored = colored.clone()
|
|
||||||
clone.compilerArgs = compilerArgs.clone()
|
clone.compilerArgs = compilerArgs.clone()
|
||||||
clone.optimization = optimization.clone()
|
clone.optimization = optimization.clone()
|
||||||
return clone
|
return clone
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getConfigurables(): List<ZigConfigurable<*>> {
|
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> {
|
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.configurations.PtyCommandLine
|
||||||
import com.intellij.execution.process.AnsiEscapeDecoder.ColoredTextAcceptor
|
import com.intellij.execution.process.AnsiEscapeDecoder.ColoredTextAcceptor
|
||||||
import com.intellij.execution.process.KillableColoredProcessHandler
|
import com.intellij.execution.process.KillableColoredProcessHandler
|
||||||
|
import com.intellij.execution.process.KillableProcessHandler
|
||||||
import com.intellij.openapi.util.Key
|
import com.intellij.openapi.util.Key
|
||||||
import com.pty4j.PtyProcess
|
import com.pty4j.PtyProcess
|
||||||
import java.nio.charset.Charset
|
import java.nio.charset.Charset
|
||||||
|
|
||||||
class ZigProcessHandler : KillableColoredProcessHandler, ColoredTextAcceptor {
|
class ZigProcessHandler : KillableProcessHandler {
|
||||||
constructor(commandLine: GeneralCommandLine) : super(commandLine) {
|
constructor(commandLine: GeneralCommandLine) : super(commandLine) {
|
||||||
setHasPty(commandLine is PtyCommandLine)
|
setHasPty(commandLine is PtyCommandLine)
|
||||||
setShouldDestroyProcessRecursively(!hasPty())
|
setShouldDestroyProcessRecursively(!hasPty())
|
||||||
|
@ -40,57 +41,4 @@ class ZigProcessHandler : KillableColoredProcessHandler, ColoredTextAcceptor {
|
||||||
setHasPty(process is PtyProcess)
|
setHasPty(process is PtyProcess)
|
||||||
setShouldDestroyProcessRecursively(!hasPty())
|
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.ZigExecConfig
|
||||||
import com.falsepattern.zigbrains.project.execution.base.ZigProfileState
|
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.project.toolchain.AbstractZigToolchain
|
||||||
import com.falsepattern.zigbrains.shared.coroutine.withEDTContext
|
import com.falsepattern.zigbrains.shared.coroutine.withEDTContext
|
||||||
import com.intellij.execution.configurations.RunProfile
|
import com.intellij.execution.configurations.RunProfile
|
||||||
|
@ -37,7 +36,7 @@ import com.intellij.openapi.application.ModalityState
|
||||||
class ZigRegularRunner: ZigProgramRunner<ZigProfileState<*>>(DefaultRunExecutor.EXECUTOR_ID) {
|
class ZigRegularRunner: ZigProgramRunner<ZigProfileState<*>>(DefaultRunExecutor.EXECUTOR_ID) {
|
||||||
override suspend fun execute(state: ZigProfileState<*>, toolchain: AbstractZigToolchain, environment: ExecutionEnvironment): RunContentDescriptor? {
|
override suspend fun execute(state: ZigProfileState<*>, toolchain: AbstractZigToolchain, environment: ExecutionEnvironment): RunContentDescriptor? {
|
||||||
val cli = state.getCommandLine(toolchain, false)
|
val cli = state.getCommandLine(toolchain, false)
|
||||||
val exec = executeCommandLine(cli, environment)
|
val exec = state.execute(environment.executor, this)
|
||||||
return withEDTContext(ModalityState.any()) {
|
return withEDTContext(ModalityState.any()) {
|
||||||
val runContentBuilder = RunContentBuilder(exec, environment)
|
val runContentBuilder = RunContentBuilder(exec, environment)
|
||||||
runContentBuilder.showRunContent(null)
|
runContentBuilder.showRunContent(null)
|
||||||
|
|
|
@ -23,13 +23,18 @@
|
||||||
package com.falsepattern.zigbrains.shared.cli
|
package com.falsepattern.zigbrains.shared.cli
|
||||||
|
|
||||||
import com.falsepattern.zigbrains.ZigBrainsBundle
|
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.IPCUtil
|
||||||
import com.falsepattern.zigbrains.shared.ipc.ipc
|
import com.falsepattern.zigbrains.shared.ipc.ipc
|
||||||
|
import com.intellij.execution.ExecutionException
|
||||||
import com.intellij.execution.configurations.GeneralCommandLine
|
import com.intellij.execution.configurations.GeneralCommandLine
|
||||||
|
import com.intellij.execution.process.ProcessHandler
|
||||||
import com.intellij.execution.process.ProcessOutput
|
import com.intellij.execution.process.ProcessOutput
|
||||||
|
import com.intellij.execution.process.ProcessTerminatedListener
|
||||||
import com.intellij.openapi.options.ConfigurationException
|
import com.intellij.openapi.options.ConfigurationException
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.util.io.awaitExit
|
import com.intellij.util.io.awaitExit
|
||||||
|
import com.intellij.util.system.OS
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.runInterruptible
|
import kotlinx.coroutines.runInterruptible
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
@ -108,14 +113,6 @@ fun translateCommandline(toProcess: String): List<String> {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
fun coloredCliFlags(colored: Boolean, debug: Boolean): List<String> {
|
|
||||||
return if (debug) {
|
|
||||||
emptyList()
|
|
||||||
} else {
|
|
||||||
listOf("--color", if (colored) "on" else "off")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun createCommandLineSafe(
|
fun createCommandLineSafe(
|
||||||
workingDirectory: Path?,
|
workingDirectory: Path?,
|
||||||
exe: Path,
|
exe: Path,
|
||||||
|
@ -133,14 +130,27 @@ fun createCommandLineSafe(
|
||||||
return Result.success(cli)
|
return Result.success(cli)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun GeneralCommandLine.call(timeoutMillis: Long = Long.MAX_VALUE, ipcProject: Project? = null): Result<ProcessOutput> {
|
@Throws(ExecutionException::class)
|
||||||
val ipc = if (ipcProject != null) IPCUtil.wrapWithIPC(this) else null
|
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 cli = ipc?.cli ?: this
|
||||||
|
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 (process, exitCode) = withContext(Dispatchers.IO) {
|
||||||
val process = cli.createProcess()
|
val handler = startIPCAwareProcess(ipcProject)
|
||||||
if (ipc != null) {
|
val process = handler.process
|
||||||
ipcProject!!.ipc?.launchWatcher(ipc, process)
|
|
||||||
}
|
|
||||||
val exit = withTimeoutOrNull(timeoutMillis) {
|
val exit = withTimeoutOrNull(timeoutMillis) {
|
||||||
process.awaitExit()
|
process.awaitExit()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue