backport: 24.0.0

This commit is contained in:
FalsePattern 2025-03-27 20:40:47 +01:00
parent 3954ff8ff5
commit db85b56084
Signed by: falsepattern
GPG key ID: E930CDEC50C50E23
30 changed files with 179 additions and 209 deletions

View file

@ -17,6 +17,30 @@ Changelog structure reference:
## [Unreleased] ## [Unreleased]
## [24.0.0]
### 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
- Debugger
- Build errors didn't get shown in the console
- Project
- File path browse buttons in zig run configurations didn't work
- Occasional GUI deadlocks
- Zig
- IPC wrapper wasn't passing exit code
## [23.1.2] ## [23.1.2]
### Fixed ### Fixed

View file

@ -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)
} }
} }
} }

View file

@ -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)

View file

@ -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

View file

@ -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.withWorkDirectory(x.toFile()) } cfg.workingDirectory.path?.let { x -> cli.withWorkDirectory(x.toFile()) }
cli.addParameters(exeArgs) cli.addParameters(exeArgs)
cli.withCharset(Charsets.UTF_8) cli.withCharset(Charsets.UTF_8)

View file

@ -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
@ -93,12 +93,12 @@ abstract class ZigDebugRunnerBase<ProfileState : ZigProfileState<*>> : ZigProgra
} }
} }
} }
return@reportProgress runInterruptibleEDT { return@reportProgress runInterruptibleEDT(ModalityState.any()) {
val debuggerManager = XDebuggerManager.getInstance(environment.project) val debuggerManager = XDebuggerManager.getInstance(environment.project)
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()

View file

@ -62,7 +62,7 @@ class ZigDebugParametersBuild(
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
val commandLine = profileState.getCommandLine(toolchain, true) val commandLine = profileState.getCommandLine(toolchain, true)
if (listener.executeCommandLineWithHook(profileState.environment.project, commandLine)) if (listener.executeCommandLineWithHook(profileState.environment.project, commandLine))
throw ExecutionException(ZigDebugBundle.message("debug.build.compile.failed.generic")) return@withContext
val cfg = profileState.configuration val cfg = profileState.configuration
val workingDir = cfg.workingDirectory.path val workingDir = cfg.workingDirectory.path
val exe = profileState.configuration.exePath.path ?: run { val exe = profileState.configuration.exePath.path ?: run {

View file

@ -31,6 +31,7 @@ import com.falsepattern.zigbrains.shared.coroutine.launchWithEDT
import com.falsepattern.zigbrains.shared.coroutine.runModalOrBlocking import com.falsepattern.zigbrains.shared.coroutine.runModalOrBlocking
import com.falsepattern.zigbrains.shared.zigCoroutineScope import com.falsepattern.zigbrains.shared.zigCoroutineScope
import com.intellij.ide.plugins.PluginManager import com.intellij.ide.plugins.PluginManager
import com.intellij.openapi.application.ModalityState
import com.intellij.openapi.extensions.PluginId import com.intellij.openapi.extensions.PluginId
import com.intellij.openapi.observable.util.whenItemSelected import com.intellij.openapi.observable.util.whenItemSelected
import com.intellij.openapi.ui.ComboBox import com.intellij.openapi.ui.ComboBox
@ -88,7 +89,7 @@ class ZigDebuggerToolchainConfigurableUi : ZigDebuggerUiComponent {
row(ZigDebugBundle.message("settings.debugger.toolchain.debugger.label")) { row(ZigDebugBundle.message("settings.debugger.toolchain.debugger.label")) {
comment = cell(debuggerKindComboBox) comment = cell(debuggerKindComboBox)
.comment("", DEFAULT_COMMENT_WIDTH) { .comment("", DEFAULT_COMMENT_WIDTH) {
zigCoroutineScope.launchWithEDT { zigCoroutineScope.launchWithEDT(ModalityState.any()) {
withModalProgress(ModalTaskOwner.component(debuggerKindComboBox), "Downloading debugger", TaskCancellation.cancellable()) { withModalProgress(ModalTaskOwner.component(debuggerKindComboBox), "Downloading debugger", TaskCancellation.cancellable()) {
downloadDebugger() downloadDebugger()
} }
@ -96,7 +97,7 @@ class ZigDebuggerToolchainConfigurableUi : ZigDebuggerUiComponent {
} }
.applyToComponent { .applyToComponent {
whenItemSelected(null) { whenItemSelected(null) {
zigCoroutineScope.launchWithEDT { zigCoroutineScope.launchWithEDT(ModalityState.any()) {
this@ZigDebuggerToolchainConfigurableUi.update() this@ZigDebuggerToolchainConfigurableUi.update()
} }
} }
@ -111,7 +112,7 @@ class ZigDebuggerToolchainConfigurableUi : ZigDebuggerUiComponent {
cell(useClion) cell(useClion)
} }
} }
zigCoroutineScope.launchWithEDT { zigCoroutineScope.launchWithEDT(ModalityState.any()) {
update() update()
} }
} }

View file

@ -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()
}
}

View file

@ -33,11 +33,11 @@ import com.intellij.openapi.options.SettingsEditor
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.ComboBox import com.intellij.openapi.ui.ComboBox
import com.intellij.openapi.ui.TextBrowseFolderListener import com.intellij.openapi.ui.TextBrowseFolderListener
import com.intellij.openapi.ui.TextFieldWithBrowseButton
import com.intellij.openapi.util.Disposer import com.intellij.openapi.util.Disposer
import com.intellij.openapi.util.io.toNioPathOrNull import com.intellij.openapi.util.io.toNioPathOrNull
import com.intellij.ui.components.JBCheckBox import com.intellij.ui.components.JBCheckBox
import com.intellij.ui.components.JBTextField import com.intellij.ui.components.JBTextField
import com.intellij.ui.components.textFieldWithBrowseButton
import com.intellij.ui.dsl.builder.AlignX import com.intellij.ui.dsl.builder.AlignX
import com.intellij.ui.dsl.builder.Panel import com.intellij.ui.dsl.builder.Panel
import com.intellij.ui.dsl.builder.panel import com.intellij.ui.dsl.builder.panel
@ -161,12 +161,11 @@ class WorkDirectoryConfigurable(@Transient override val serializedName: String)
} }
class WorkDirectoryConfigModule(private val serializedName: String) : PathConfigModule<WorkDirectoryConfigurable>() { class WorkDirectoryConfigModule(private val serializedName: String) : PathConfigModule<WorkDirectoryConfigurable>() {
private val field = TextFieldWithBrowseButton( private val field = textFieldWithBrowseButton(
TextBrowseFolderListener( null,
FileChooserDescriptorFactory.createSingleFolderDescriptor().withTitle(ZigBrainsBundle.message("dialog.title.working-directory")) ZigBrainsBundle.message("dialog.title.working-directory"),
), FileChooserDescriptorFactory.createSingleFolderDescriptor().withTitle(ZigBrainsBundle.message("dialog.title.working-directory"))
this ).also { Disposer.register(this, it) }
)
override var stringValue by field::text override var stringValue by field::text
@ -201,9 +200,10 @@ class FilePathConfigurable(
} }
class FilePathConfigModule(private val serializedName: String, @Nls private val label: String) : PathConfigModule<FilePathConfigurable>() { class FilePathConfigModule(private val serializedName: String, @Nls private val label: String) : PathConfigModule<FilePathConfigurable>() {
private val field = TextFieldWithBrowseButton( private val field = textFieldWithBrowseButton(
TextBrowseFolderListener(FileChooserDescriptorFactory.createSingleFileDescriptor()), null,
this null,
FileChooserDescriptorFactory.createSingleFileDescriptor(),
) )
override var stringValue by field::text override var stringValue by field::text
@ -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,

View file

@ -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)
} }

View file

@ -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.withWorkDirectory(it.toFile()) } workingDir.path?.let { cli.withWorkDirectory(it.toFile()) }
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
}

View file

@ -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 {

View file

@ -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> {

View file

@ -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> {

View file

@ -29,6 +29,7 @@ import com.intellij.ide.util.projectWizard.ModuleBuilder
import com.intellij.ide.util.projectWizard.ModuleWizardStep import com.intellij.ide.util.projectWizard.ModuleWizardStep
import com.intellij.ide.util.projectWizard.WizardContext import com.intellij.ide.util.projectWizard.WizardContext
import com.intellij.openapi.Disposable import com.intellij.openapi.Disposable
import com.intellij.openapi.application.ModalityState
import com.intellij.openapi.module.ModuleType import com.intellij.openapi.module.ModuleType
import com.intellij.openapi.roots.ModifiableRootModel import com.intellij.openapi.roots.ModifiableRootModel
import com.intellij.openapi.util.Disposer import com.intellij.openapi.util.Disposer
@ -58,7 +59,7 @@ class ZigModuleBuilder: ModuleBuilder() {
val root = contentEntry.file ?: return val root = contentEntry.file ?: return
val config = configurationData ?: return val config = configurationData ?: return
config.generateProject(this, rootModel.project, root, forceGitignore) config.generateProject(this, rootModel.project, root, forceGitignore)
withEDTContext { withEDTContext(ModalityState.defaultModalityState()) {
root.refresh(false, true) root.refresh(false, true)
} }
} }

View file

@ -119,10 +119,10 @@ data class ZigProjectConfigurationData(
if (git) { if (git) {
project.zigCoroutineScope.launch { project.zigCoroutineScope.launch {
GitRepositoryInitializer.getInstance()?.initRepository(project, baseDir) GitRepositoryInitializer.getInstance()?.initRepository(project, baseDir)
createGitIgnoreFile(project, baseDir, requestor) createGitIgnoreFile(baseDir, requestor)
} }
} else if (forceGitignore) { } else if (forceGitignore) {
createGitIgnoreFile(project, baseDir, requestor) createGitIgnoreFile(baseDir, requestor)
} }
return@reportProgress true return@reportProgress true
@ -131,7 +131,7 @@ data class ZigProjectConfigurationData(
} }
private suspend fun createGitIgnoreFile(project: Project, projectDir: VirtualFile, requestor: Any) { private suspend fun createGitIgnoreFile(projectDir: VirtualFile, requestor: Any) {
if (projectDir.findChild(".gitignore") != null) { if (projectDir.findChild(".gitignore") != null) {
return return
} }

View file

@ -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()
}

View file

@ -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
@ -36,8 +35,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 exec = state.execute(environment.executor, this)
val exec = executeCommandLine(cli, environment)
return withEDTContext(ModalityState.any()) { return withEDTContext(ModalityState.any()) {
val runContentBuilder = RunContentBuilder(exec, environment) val runContentBuilder = RunContentBuilder(exec, environment)
runContentBuilder.showRunContent(null) runContentBuilder.showRunContent(null)

View file

@ -97,7 +97,7 @@ class ZigProjectSettingsPanel(private val holder: ZigProjectConfigurationProvide
} }
private fun dispatchAutodetect(force: Boolean) { private fun dispatchAutodetect(force: Boolean) {
project.zigCoroutineScope.launchWithEDT { project.zigCoroutineScope.launchWithEDT(ModalityState.any()) {
withModalProgress(ModalTaskOwner.component(pathToToolchain), "Detecting Zig...", TaskCancellation.cancellable()) { withModalProgress(ModalTaskOwner.component(pathToToolchain), "Detecting Zig...", TaskCancellation.cancellable()) {
autodetect(force) autodetect(force)
} }

View file

@ -27,6 +27,7 @@ import com.falsepattern.zigbrains.project.steps.discovery.ZigStepDiscoveryListen
import com.falsepattern.zigbrains.shared.coroutine.withEDTContext import com.falsepattern.zigbrains.shared.coroutine.withEDTContext
import com.falsepattern.zigbrains.shared.zigCoroutineScope import com.falsepattern.zigbrains.shared.zigCoroutineScope
import com.intellij.openapi.Disposable import com.intellij.openapi.Disposable
import com.intellij.openapi.application.ModalityState
import com.intellij.openapi.components.Service import com.intellij.openapi.components.Service
import com.intellij.openapi.components.service import com.intellij.openapi.components.service
import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.diagnostic.Logger
@ -90,7 +91,7 @@ class ZigStepDiscoveryService(private val project: Project) {
null null
} }
if (result == null) { if (result == null) {
{}
} else if (result.checkSuccess(LOG)) { } else if (result.checkSuccess(LOG)) {
currentTimeoutSec = DEFAULT_TIMEOUT_SEC currentTimeoutSec = DEFAULT_TIMEOUT_SEC
val lines = result.stdoutLines val lines = result.stdoutLines
@ -124,7 +125,7 @@ class ZigStepDiscoveryService(private val project: Project) {
} }
private suspend fun dispatchReload() { private suspend fun dispatchReload() {
withEDTContext { withEDTContext(ModalityState.defaultModalityState()) {
FileDocumentManager.getInstance().saveAllDocuments() FileDocumentManager.getInstance().saveAllDocuments()
} }
doReload() doReload()

View file

@ -81,18 +81,18 @@ class BuildToolWindowContext(private val project: Project): Disposable {
val tree = Tree(model).also { it.isRootVisible = false } val tree = Tree(model).also { it.isRootVisible = false }
} }
private val viewPanel = JPanel(VerticalLayout(0)) private val viewPanel = JPanel(VerticalLayout(0))
private val steps = TreeBox() private val stepsBox = TreeBox()
private val build = if (IPCUtil.haveIPC) TreeBox() else null private val buildBox = if (IPCUtil.haveIPC) TreeBox() else null
private var live = AtomicBoolean(true) private var live = AtomicBoolean(true)
init { init {
viewPanel.add(JBLabel(ZigBrainsBundle.message("build.tool.window.tree.steps.label"))) viewPanel.add(JBLabel(ZigBrainsBundle.message("build.tool.window.tree.steps.label")))
viewPanel.add(steps.panel) viewPanel.add(stepsBox.panel)
steps.panel.setNotScanned() stepsBox.panel.setNotScanned()
steps.tree.addMouseListener(object : MouseAdapter() { stepsBox.tree.addMouseListener(object : MouseAdapter() {
override fun mouseClicked(e: MouseEvent) { override fun mouseClicked(e: MouseEvent) {
if (e.clickCount == 2) { if (e.clickCount == 2) {
val node = steps.tree.lastSelectedPathComponent as? DefaultMutableTreeNode ?: return val node = stepsBox.tree.lastSelectedPathComponent as? DefaultMutableTreeNode ?: return
val step = node.userObject as? StepNodeDescriptor ?: return val step = node.userObject as? StepNodeDescriptor ?: return
val stepName = step.element?.name ?: return val stepName = step.element?.name ?: return
@ -112,10 +112,10 @@ class BuildToolWindowContext(private val project: Project): Disposable {
} }
}) })
if (build != null) { if (buildBox != null) {
viewPanel.add(JBLabel(ZigBrainsBundle.message("build.tool.window.tree.build.label"))) viewPanel.add(JBLabel(ZigBrainsBundle.message("build.tool.window.tree.build.label")))
viewPanel.add(build.panel) viewPanel.add(buildBox.panel)
build.panel.setNoBuilds() buildBox.panel.setNoBuilds()
project.zigCoroutineScope.launch { project.zigCoroutineScope.launch {
while (!project.isDisposed && live.get()) { while (!project.isDisposed && live.get()) {
@ -126,14 +126,14 @@ class BuildToolWindowContext(private val project: Project): Disposable {
ipc.mutex.withLock { ipc.mutex.withLock {
withEDTContext(ModalityState.any()) { withEDTContext(ModalityState.any()) {
if (ipc.nodes.isEmpty()) { if (ipc.nodes.isEmpty()) {
build.root.removeAllChildren() buildBox.root.removeAllChildren()
build.panel.setNoBuilds() buildBox.panel.setNoBuilds()
return@withEDTContext return@withEDTContext
} }
val allNodes = ArrayList(ipc.nodes) val allNodes = ArrayList(ipc.nodes)
val existingNodes = ArrayList<ZigIPCService.IPCTreeNode>() val existingNodes = ArrayList<ZigIPCService.IPCTreeNode>()
val removedNodes = ArrayList<ZigIPCService.IPCTreeNode>() val removedNodes = ArrayList<ZigIPCService.IPCTreeNode>()
build.root.children().iterator().forEach { child -> buildBox.root.children().iterator().forEach { child ->
if (child !is ZigIPCService.IPCTreeNode) { if (child !is ZigIPCService.IPCTreeNode) {
return@forEach return@forEach
} }
@ -145,18 +145,18 @@ class BuildToolWindowContext(private val project: Project): Disposable {
} }
val newNodes = ArrayList<MutableTreeNode>(allNodes) val newNodes = ArrayList<MutableTreeNode>(allNodes)
newNodes.removeAll(existingNodes) newNodes.removeAll(existingNodes)
removedNodes.forEach { build.root.remove(it) } removedNodes.forEach { buildBox.root.remove(it) }
newNodes.forEach { build.root.add(it) } newNodes.forEach { buildBox.root.add(it) }
if (removedNodes.isNotEmpty() || newNodes.isNotEmpty()) { if (removedNodes.isNotEmpty() || newNodes.isNotEmpty()) {
build.model.reload(build.root) buildBox.model.reload(buildBox.root)
} }
if (build.root.childCount == 0) { if (buildBox.root.childCount == 0) {
build.panel.setNoBuilds() buildBox.panel.setNoBuilds()
} else { } else {
build.panel.setViewportBody(build.tree) buildBox.panel.setViewportBody(buildBox.tree)
} }
for (bn in allNodes) { for (bn in allNodes) {
expandRecursively(build, bn) expandRecursively(buildBox, bn)
} }
} }
} }
@ -220,28 +220,28 @@ class BuildToolWindowContext(private val project: Project): Disposable {
inner class BuildReloadListener: ZigStepDiscoveryListener { inner class BuildReloadListener: ZigStepDiscoveryListener {
override suspend fun preReload() { override suspend fun preReload() {
steps.panel.setRunningZigBuild() stepsBox.panel.setRunningZigBuild()
} }
override suspend fun postReload(stepInfo: List<Pair<String, String?>>) { override suspend fun postReload(steps: List<Pair<String, String?>>) {
steps.root.removeAllChildren() stepsBox.root.removeAllChildren()
for ((task, description) in stepInfo) { for ((task, description) in steps) {
val icon = when(task) { val icon = when(task) {
"install" -> AllIcons.Actions.Install "install" -> AllIcons.Actions.Install
"uninstall" -> AllIcons.Actions.Uninstall "uninstall" -> AllIcons.Actions.Uninstall
else -> AllIcons.RunConfigurations.TestState.Run else -> AllIcons.RunConfigurations.TestState.Run
} }
steps.root.add(DefaultMutableTreeNode(StepNodeDescriptor(project, task, icon, description))) stepsBox.root.add(DefaultMutableTreeNode(StepNodeDescriptor(project, task, icon, description)))
} }
withEDTContext(ModalityState.any()) { withEDTContext(ModalityState.any()) {
steps.model.reload(steps.root) stepsBox.model.reload(stepsBox.root)
steps.panel.setViewportBody(steps.tree) stepsBox.panel.setViewportBody(stepsBox.tree)
} }
} }
override suspend fun errorReload(type: ZigStepDiscoveryListener.ErrorType, details: String?) { override suspend fun errorReload(type: ZigStepDiscoveryListener.ErrorType, details: String?) {
withEDTContext(ModalityState.any()) { withEDTContext(ModalityState.any()) {
steps.panel.setViewportError(ZigBrainsBundle.message(when(type) { stepsBox.panel.setViewportError(ZigBrainsBundle.message(when(type) {
ZigStepDiscoveryListener.ErrorType.MissingToolchain -> "build.tool.window.status.error.missing-toolchain" ZigStepDiscoveryListener.ErrorType.MissingToolchain -> "build.tool.window.status.error.missing-toolchain"
ZigStepDiscoveryListener.ErrorType.MissingZigExe -> "build.tool.window.status.error.missing-zig-exe" ZigStepDiscoveryListener.ErrorType.MissingZigExe -> "build.tool.window.status.error.missing-zig-exe"
ZigStepDiscoveryListener.ErrorType.MissingBuildZig -> "build.tool.window.status.error.missing-build-zig" ZigStepDiscoveryListener.ErrorType.MissingBuildZig -> "build.tool.window.status.error.missing-build-zig"
@ -252,7 +252,7 @@ class BuildToolWindowContext(private val project: Project): Disposable {
override suspend fun timeoutReload(seconds: Int) { override suspend fun timeoutReload(seconds: Int) {
withEDTContext(ModalityState.any()) { withEDTContext(ModalityState.any()) {
steps.panel.setViewportError(ZigBrainsBundle.message("build.tool.window.status.timeout", seconds), null) stepsBox.panel.setViewportError(ZigBrainsBundle.message("build.tool.window.status.timeout", seconds), null)
} }
} }
} }

View file

@ -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()
} }

View file

@ -39,7 +39,7 @@ inline fun <T> runModalOrBlocking(taskOwnerFactory: () -> ModalTaskOwner, titleF
} }
} }
suspend inline fun <T> withEDTContext(state: ModalityState = ModalityState.defaultModalityState(), noinline block: suspend CoroutineScope.() -> T): T { suspend inline fun <T> withEDTContext(state: ModalityState, noinline block: suspend CoroutineScope.() -> T): T {
return withContext(Dispatchers.EDT + state.asContextElement(), block = block) return withContext(Dispatchers.EDT + state.asContextElement(), block = block)
} }
@ -49,10 +49,10 @@ suspend inline fun <T> withCurrentEDTModalityContext(noinline block: suspend Cor
} }
} }
suspend inline fun <T> runInterruptibleEDT(state: ModalityState = ModalityState.defaultModalityState(), noinline targetAction: () -> T): T { suspend inline fun <T> runInterruptibleEDT(state: ModalityState, noinline targetAction: () -> T): T {
return runInterruptible(Dispatchers.EDT + state.asContextElement(), block = targetAction) return runInterruptible(Dispatchers.EDT + state.asContextElement(), block = targetAction)
} }
fun CoroutineScope.launchWithEDT(state: ModalityState = ModalityState.defaultModalityState(), block: suspend CoroutineScope.() -> Unit): Job { fun CoroutineScope.launchWithEDT(state: ModalityState, block: suspend CoroutineScope.() -> Unit): Job {
return launch(Dispatchers.EDT + state.asContextElement(), block = block) return launch(Dispatchers.EDT + state.asContextElement(), block = block)
} }

View file

@ -74,7 +74,7 @@ object IPCUtil {
val cli = GeneralCommandLine(it) val cli = GeneralCommandLine(it)
val tmpFile = FileUtil.createTempFile("zigbrains-bash-detection", null, true).toPath() val tmpFile = FileUtil.createTempFile("zigbrains-bash-detection", null, true).toPath()
try { try {
cli.addParameters("-c", "exec {var}>${tmpFile.pathString}; echo foo >&\$var; exec {var}>&-") cli.addParameters("-c", "exec {var}>${tmpFile.pathString}; echo foo >&\$var; ZB_EXIT=\$?; exec {var}>&-; exit \$ZB_EXIT")
val process = cli.createProcess() val process = cli.createProcess()
val exitCode = process.awaitExit() val exitCode = process.awaitExit()
if (exitCode != 0) { if (exitCode != 0) {
@ -100,7 +100,7 @@ object IPCUtil {
val (fifoFile, fifo) = info!!.mkfifo.createTemp() ?: return null val (fifoFile, fifo) = info!!.mkfifo.createTemp() ?: return null
//FIFO created, hack cli //FIFO created, hack cli
val exePath = cli.exePath val exePath = cli.exePath
val args = "exec {var}>${fifoFile.pathString}; ZIG_PROGRESS=\$var $exePath ${cli.parametersList.parametersString}; exec {var}>&-" val args = "exec {var}>${fifoFile.pathString}; ZIG_PROGRESS=\$var $exePath ${cli.parametersList.parametersString}; ZB_EXIT=\$?; exec {var}>&-; exit \$ZB_EXIT"
cli.withExePath(info!!.bash) cli.withExePath(info!!.bash)
cli.parametersList.clearAll() cli.parametersList.clearAll()
cli.addParameters("-c", args) cli.addParameters("-c", args)

View file

@ -55,18 +55,16 @@ class ZonBlock(
override fun getChildIndent(): Indent { override fun getChildIndent(): Indent {
val node = this.node val node = this.node
return getIndentBasedOnParentType(node, null, node.elementType, PLACEHOLDER) return getIndentBasedOnParentType(node.elementType, PLACEHOLDER)
} }
override fun getIndent(): Indent { override fun getIndent(): Indent {
val node = this.node val node = this.node
val parent = node.treeParent ?: return noneIndent val parent = node.treeParent ?: return noneIndent
return getIndentBasedOnParentType(parent, node, parent.elementType, node.elementType) return getIndentBasedOnParentType(parent.elementType, node.elementType)
} }
} }
private fun getIndentBasedOnParentType( private fun getIndentBasedOnParentType(
parent: ASTNode,
child: ASTNode?,
parentType: IElementType, parentType: IElementType,
childType: IElementType childType: IElementType
): Indent { ): Indent {

View file

@ -65,11 +65,9 @@ exec.type.run.label=Zig Run
exec.type.test.label=Zig Test exec.type.test.label=Zig Test
exec.type.build.label=Zig Build exec.type.build.label=Zig Build
exec.option.label.working-directory=&Working directory: exec.option.label.working-directory=&Working directory:
exec.option.label.colored-terminal=Colored terminal
exec.option.label.direnv=Use direnv exec.option.label.direnv=Use direnv
exec.option.label.optimization=Optimization level exec.option.label.optimization=Optimization level
exec.option.label.optimization.force=Force even in debug runs exec.option.label.optimization.force=Force even in debug runs
exec.option.label.emulate-terminal=Emulate terminal
exec.option.label.file-path=File Path exec.option.label.file-path=File Path
exec.option.label.compiler-args=Extra compiler command line arguments exec.option.label.compiler-args=Extra compiler command line arguments
exec.option.label.exe-args=Output program command line arguments exec.option.label.exe-args=Output program command line arguments

View file

@ -1,7 +1,7 @@
pluginName=ZigBrains pluginName=ZigBrains
pluginRepositoryUrl=https://github.com/FalsePattern/ZigBrains pluginRepositoryUrl=https://github.com/FalsePattern/ZigBrains
pluginVersion=23.1.2 pluginVersion=24.0.0
pluginSinceBuild=241 pluginSinceBuild=241
pluginUntilBuild=241.* pluginUntilBuild=241.*

View file

@ -279,7 +279,7 @@ class ZLSSettingsPanel(private val project: Project) : ZigProjectConfigurationPr
} }
private fun dispatchAutodetect(force: Boolean) { private fun dispatchAutodetect(force: Boolean) {
project.zigCoroutineScope.launchWithEDT { project.zigCoroutineScope.launchWithEDT(ModalityState.any()) {
withModalProgress(ModalTaskOwner.component(zlsPath), "Detecting ZLS...", TaskCancellation.cancellable()) { withModalProgress(ModalTaskOwner.component(zlsPath), "Detecting ZLS...", TaskCancellation.cancellable()) {
autodetect(force) autodetect(force)
} }

View file

@ -6,7 +6,6 @@ settings.zls-version.label=Detected ZLS version
settings.zls-config-path.label=Config path settings.zls-config-path.label=Config path
settings.zls-config-path.tooltip=Leave empty to use built-in config generated from the settings below settings.zls-config-path.tooltip=Leave empty to use built-in config generated from the settings below
settings.zls-config-path.browse.title=Path to the Custom ZLS Config File (Optional) settings.zls-config-path.browse.title=Path to the Custom ZLS Config File (Optional)
settings.zls-path.use-direnv.label=Use direnv
settings.inlay-hints-group.label=Inlay Hints settings.inlay-hints-group.label=Inlay Hints
settings.inlay-hints-enable.label=Enable settings.inlay-hints-enable.label=Enable
settings.inlay-hints-enable.tooltip=Toggle this to enable/disable all inlay hints settings.inlay-hints-enable.tooltip=Toggle this to enable/disable all inlay hints
@ -50,7 +49,6 @@ settings.build_runner_path.label=Build runner path
settings.build_runner_path.tooltip=Specify a custom build runner to resolve build system information. settings.build_runner_path.tooltip=Specify a custom build runner to resolve build system information.
settings.global_cache_path.label=Global cache path settings.global_cache_path.label=Global cache path
settings.global_cache_path.tooltip=Path to a directory that will be used as zig's cache. Will default to `${KnownFolders.Cache}/zls`. settings.global_cache_path.tooltip=Path to a directory that will be used as zig's cache. Will default to `${KnownFolders.Cache}/zls`.
configurable.name.zls.settings=ZLS Settings
notification.group.zigbrains-lsp=ZigBrains LSP Integration notification.group.zigbrains-lsp=ZigBrains LSP Integration
notification.message.could-not-detect.content=Could not detect ZLS binary, please configure it notification.message.could-not-detect.content=Could not detect ZLS binary, please configure it
notification.message.zls-exe-path-invalid.content=ZLS executable path could not be parsed: {0} notification.message.zls-exe-path-invalid.content=ZLS executable path could not be parsed: {0}