backport: 23.0.1

This commit is contained in:
FalsePattern 2025-03-19 16:51:57 +01:00
parent ae7657ab7c
commit 2c343eb940
Signed by: falsepattern
GPG key ID: E930CDEC50C50E23
24 changed files with 141 additions and 79 deletions

View file

@ -17,6 +17,14 @@ Changelog structure reference:
## [Unreleased]
## [23.0.1]
### Fixed
- Project
- mkfifo/bash for zig progress visualization is now detected more reliably (fixes error on macOS)
- Deadlock when launching zig build tasks
## [23.0.0]
### Added

View file

@ -24,7 +24,6 @@ package com.falsepattern.zigbrains.clion
import com.falsepattern.zigbrains.debugger.ZigDebuggerDriverConfigurationProvider
import com.falsepattern.zigbrains.debugger.settings.ZigDebuggerSettings
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.SystemInfo

View file

@ -27,6 +27,7 @@ import com.falsepattern.zigbrains.debugger.toolchain.*
import com.falsepattern.zigbrains.debugger.win.MSVCDriverConfiguration
import com.falsepattern.zigbrains.shared.coroutine.withEDTContext
import com.falsepattern.zigbrains.zig.ZigLanguage
import com.intellij.openapi.application.ModalityState
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.DoNotAskOption
import com.intellij.openapi.ui.MessageDialogBuilder
@ -85,7 +86,7 @@ private suspend fun availabilityCheck(project: Project, kind: DebuggerKind): Boo
}
if (downloadDebugger) {
val result = withEDTContext {
val result = withEDTContext(ModalityState.any()) {
service.downloadDebugger(project, kind)
}
if (result is ZigDebuggerToolchainService.DownloadResult.Ok) {
@ -104,7 +105,7 @@ private suspend fun showDialog(project: Project, message: String, action: String
}
}
return withEDTContext {
return withEDTContext(ModalityState.any()) {
MessageDialogBuilder
.okCancel(ZigDebugBundle.message("debugger.run.unavailable"), message)
.yesText(action)

View file

@ -26,11 +26,7 @@ import com.falsepattern.zigbrains.project.run.ZigProcessHandler
import com.falsepattern.zigbrains.shared.zigCoroutineScope
import com.intellij.execution.ExecutionException
import com.intellij.execution.configurations.PtyCommandLine
import com.intellij.execution.process.BaseProcessHandler
import com.intellij.execution.process.ProcessAdapter
import com.intellij.execution.process.ProcessEvent
import com.intellij.execution.process.ProcessListener
import com.intellij.execution.process.ProcessOutputType
import com.intellij.execution.process.*
import com.intellij.openapi.util.Key
import com.intellij.openapi.util.KeyWithDefaultValue
import com.intellij.openapi.util.io.toNioPathOrNull
@ -45,9 +41,12 @@ import com.jetbrains.cidr.execution.debugger.memory.AddressRange
import com.jetbrains.cidr.system.HostMachine
import com.jetbrains.cidr.system.LocalHost
import io.ktor.util.*
import kotlinx.coroutines.*
import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import kotlinx.coroutines.future.asCompletableFuture
import kotlinx.coroutines.future.asDeferred
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import org.eclipse.lsp4j.debug.*
import org.eclipse.lsp4j.debug.services.IDebugProtocolClient
import org.eclipse.lsp4j.debug.services.IDebugProtocolServer
@ -58,8 +57,7 @@ import java.io.ByteArrayOutputStream
import java.io.File
import java.io.OutputStream
import java.io.PipedOutputStream
import java.lang.Exception
import java.util.TreeMap
import java.util.*
import java.util.concurrent.CompletableFuture
import java.util.concurrent.Executors
import kotlin.math.min

View file

@ -40,6 +40,7 @@ import com.intellij.execution.runners.RunContentBuilder
import com.intellij.execution.ui.ConsoleView
import com.intellij.execution.ui.ConsoleViewContentType
import com.intellij.execution.ui.RunContentDescriptor
import com.intellij.openapi.application.ModalityState
import com.intellij.platform.util.progress.reportProgress
import com.intellij.xdebugger.XDebugProcess
import com.intellij.xdebugger.XDebugProcessStarter
@ -86,7 +87,7 @@ abstract class ZigDebugRunnerBase<ProfileState : ZigProfileState<*>> : ZigProgra
}
if (listener.isBuildFailed) {
val executionResult = DefaultExecutionResult(console, listener.processHandler)
return@reportProgress withEDTContext {
return@reportProgress withEDTContext(ModalityState.any()) {
val runContentBuilder = RunContentBuilder(executionResult, environment)
runContentBuilder.showRunContent(null)
}

View file

@ -27,10 +27,8 @@ import com.falsepattern.zigbrains.debugger.settings.MSVCDownloadPermission
import com.falsepattern.zigbrains.debugger.settings.ZigDebuggerSettings
import com.falsepattern.zigbrains.debugger.toolchain.ZigDebuggerToolchainService.Companion.downloadPath
import com.falsepattern.zigbrains.shared.coroutine.withCurrentEDTModalityContext
import com.falsepattern.zigbrains.shared.coroutine.withEDTContext
import com.intellij.notification.Notification
import com.intellij.notification.NotificationType
import com.intellij.openapi.application.ModalityState
import com.intellij.openapi.progress.coroutineToIndicator
import com.intellij.openapi.ui.DialogBuilder
import com.intellij.platform.util.progress.withProgressText

View file

@ -49,7 +49,6 @@ import com.jetbrains.cidr.execution.debugger.backend.lldb.LLDBDriverConfiguratio
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runInterruptible
import kotlinx.coroutines.withContext
import java.io.File
import java.io.IOException
import java.net.URL
import java.nio.file.Path

View file

@ -39,9 +39,8 @@ import org.eclipse.lsp4j.jsonrpc.debug.messages.DebugResponseMessage
import org.eclipse.lsp4j.jsonrpc.messages.Message
import org.eclipse.lsp4j.jsonrpc.services.JsonRequest
import java.io.InputStream
import java.lang.RuntimeException
import java.security.MessageDigest
import java.util.Base64
import java.util.*
import java.util.concurrent.CompletableFuture
import java.util.zip.Inflater

View file

@ -23,8 +23,6 @@
package com.falsepattern.zigbrains
import com.falsepattern.zigbrains.direnv.DirenvCmd
import com.falsepattern.zigbrains.direnv.emptyEnv
import com.falsepattern.zigbrains.direnv.getDirenv
import com.falsepattern.zigbrains.project.settings.zigProjectSettings
import com.falsepattern.zigbrains.project.toolchain.LocalZigToolchain
import com.falsepattern.zigbrains.project.toolchain.ZigToolchainProvider

View file

@ -25,10 +25,14 @@ package com.falsepattern.zigbrains.direnv
import com.intellij.openapi.util.SystemInfo
import com.intellij.openapi.util.io.toNioPathOrNull
import com.intellij.util.EnvironmentUtil
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.flow
import org.jetbrains.annotations.NonNls
import java.io.File
import java.nio.file.Path
import kotlin.io.path.*
import kotlin.io.path.absolute
import kotlin.io.path.isDirectory
import kotlin.io.path.isExecutable
import kotlin.io.path.isRegularFile
data class Env(val env: Map<String, String>) {
private val path get() = getVariable("PATH")?.split(File.pathSeparatorChar)
@ -36,9 +40,11 @@ data class Env(val env: Map<String, String>) {
private fun getVariable(name: @NonNls String) =
env.getOrElse(name) { EnvironmentUtil.getValue(name) }
fun findExecutableOnPATH(exe: @NonNls String): Path? {
suspend fun findExecutableOnPATH(exe: @NonNls String) = findAllExecutablesOnPATH(exe).firstOrNull()
fun findAllExecutablesOnPATH(exe: @NonNls String) = flow {
val exeName = if (SystemInfo.isWindows) "$exe.exe" else exe
val paths = path ?: return null
val paths = path ?: return@flow
for (dir in paths) {
val path = dir.toNioPathOrNull()?.absolute() ?: continue
if (!path.toFile().exists() || !path.isDirectory())
@ -46,9 +52,8 @@ data class Env(val env: Map<String, String>) {
val exePath = path.resolve(exeName).absolute()
if (!exePath.isRegularFile() || !exePath.isExecutable())
continue
return exePath
emit(exePath)
}
return null
}
}

View file

@ -26,8 +26,8 @@ import com.falsepattern.zigbrains.ZigBrainsBundle
import com.falsepattern.zigbrains.project.run.ZigProcessHandler
import com.falsepattern.zigbrains.project.settings.zigProjectSettings
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain
import com.falsepattern.zigbrains.shared.ipc.IPCUtil
import com.falsepattern.zigbrains.shared.coroutine.runModalOrBlocking
import com.falsepattern.zigbrains.shared.ipc.IPCUtil
import com.falsepattern.zigbrains.shared.ipc.ipc
import com.intellij.build.BuildTextConsoleView
import com.intellij.execution.DefaultExecutionResult

View file

@ -23,9 +23,7 @@
package com.falsepattern.zigbrains.project.newproject
import com.falsepattern.zigbrains.project.settings.ZigProjectConfigurationProvider
import com.falsepattern.zigbrains.project.template.ZigExecutableTemplate
import com.falsepattern.zigbrains.project.template.ZigInitTemplate
import com.falsepattern.zigbrains.project.template.ZigLibraryTemplate
import com.falsepattern.zigbrains.project.template.ZigProjectTemplate
import com.intellij.openapi.Disposable
import com.intellij.openapi.actionSystem.ActionToolbarPosition

View file

@ -32,12 +32,13 @@ import com.intellij.execution.executors.DefaultRunExecutor
import com.intellij.execution.runners.ExecutionEnvironment
import com.intellij.execution.runners.RunContentBuilder
import com.intellij.execution.ui.RunContentDescriptor
import com.intellij.openapi.application.ModalityState
class ZigRegularRunner: ZigProgramRunner<ZigProfileState<*>>(DefaultRunExecutor.EXECUTOR_ID) {
override suspend fun execute(state: ZigProfileState<*>, toolchain: AbstractZigToolchain, environment: ExecutionEnvironment): RunContentDescriptor? {
val cli = state.getCommandLine(toolchain, false)
val exec = executeCommandLine(cli, environment)
return withEDTContext {
return withEDTContext(ModalityState.any()) {
val runContentBuilder = RunContentBuilder(exec, environment)
runContentBuilder.showRunContent(null)
}

View file

@ -27,7 +27,6 @@ import com.falsepattern.zigbrains.project.toolchain.ZigToolchainEnvironmentSeria
import com.intellij.openapi.project.Project
import kotlinx.serialization.SerializationException
import kotlinx.serialization.json.Json
import java.lang.IllegalStateException
import java.nio.file.Path
class ZigCompilerTool(toolchain: AbstractZigToolchain) : ZigTool(toolchain) {

View file

@ -0,0 +1,28 @@
/*
* 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.shared.ipc
import com.intellij.execution.configurations.GeneralCommandLine
import java.nio.file.Path
data class IPC(val cli: GeneralCommandLine, val fifoPath: Path, val fifoClose: AutoCloseable)

View file

@ -27,55 +27,105 @@ import com.intellij.execution.configurations.GeneralCommandLine
import com.intellij.openapi.util.SystemInfo
import com.intellij.openapi.util.io.FileUtil
import com.intellij.util.io.awaitExit
import java.io.File
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.runBlocking
import java.nio.charset.Charset
import java.nio.file.Path
import javax.swing.tree.DefaultMutableTreeNode
import kotlin.io.path.deleteIfExists
import kotlin.io.path.inputStream
import kotlin.io.path.pathString
/**
* Zig build progress node IPC glue code
*/
object IPCUtil {
val haveIPC = checkHaveIPC()
private fun checkHaveIPC(): Boolean {
val haveIPC: Boolean get() = info != null
@JvmRecord
data class IPCInfo(val mkfifo: MKFifo, val bash: String)
private val info: IPCInfo? by lazy { runBlocking {
createInfo()
} }
private suspend fun createInfo(): IPCInfo? {
if (SystemInfo.isWindows) {
return false;
return null
}
val mkfifo = emptyEnv.findExecutableOnPATH("mkfifo")
val bash = emptyEnv.findExecutableOnPATH("bash")
return mkfifo != null && bash != null
val mkfifo = emptyEnv
.findAllExecutablesOnPATH("mkfifo")
.map { it.pathString }
.map(::MKFifo)
.toList()
.find { mkfifo ->
val fifo = mkfifo.createTemp() ?: return@find false
fifo.second.close()
true
} ?: return null
val selectedBash = emptyEnv
.findAllExecutablesOnPATH("bash")
.map { it.pathString }
.filter {
val cli = GeneralCommandLine(it)
val tmpFile = FileUtil.createTempFile("zigbrains-bash-detection", null, true).toPath()
try {
cli.addParameters("-c", "exec {var}>${tmpFile.pathString}; echo foo >&\$var; exec {var}>&-")
val process = cli.createProcess()
val exitCode = process.awaitExit()
if (exitCode != 0) {
return@filter false
}
val text = tmpFile.inputStream().use { it.readAllBytes().toString(Charset.defaultCharset()).trim() }
if (text != "foo") {
return@filter false
}
true
} finally {
tmpFile.deleteIfExists()
}
}
.firstOrNull() ?: return null
return IPCInfo(mkfifo, selectedBash)
}
private suspend fun mkfifo(path: Path): AutoCloseable? {
val cli = GeneralCommandLine("mkfifo", path.pathString)
suspend fun wrapWithIPC(cli: GeneralCommandLine): IPC? {
if (!haveIPC)
return null
val (fifoFile, fifo) = info!!.mkfifo.createTemp() ?: return null
//FIFO created, hack cli
val exePath = cli.exePath
val args = "exec {var}>${fifoFile.pathString}; ZIG_PROGRESS=\$var $exePath ${cli.parametersList.parametersString}; exec {var}>&-"
cli.withExePath(info!!.bash)
cli.parametersList.clearAll()
cli.addParameters("-c", args)
return IPC(cli, fifoFile, fifo)
}
@JvmRecord
data class MKFifo(val exe: String) {
suspend fun createTemp(): Pair<Path, AutoCloseable>? {
val fifoFile = FileUtil.createTempFile("zigbrains-ipc-pipe", null, true).toPath()
fifoFile.deleteIfExists()
val fifo = create(fifoFile)
if (fifo == null) {
fifoFile.deleteIfExists()
return null
}
return Pair(fifoFile, fifo)
}
suspend fun create(path: Path): AutoCloseable? {
val cli = GeneralCommandLine(exe, path.pathString)
val process = cli.createProcess()
val exitCode = process.awaitExit()
return if (exitCode == 0) AutoCloseable {
path.deleteIfExists()
} else null
}
data class IPC(val cli: GeneralCommandLine, val fifoPath: Path, val fifoClose: AutoCloseable)
suspend fun wrapWithIPC(cli: GeneralCommandLine): IPC? {
if (!haveIPC)
return null
val fifoFile = FileUtil.createTempFile("zigbrains-ipc-pipe", null, true).toPath()
fifoFile.deleteIfExists()
val fifo = mkfifo(fifoFile)
if (fifo == null) {
fifoFile.deleteIfExists()
return null
}
//FIFO created, hack cli
val exePath = cli.exePath
val args = "exec {var}>${fifoFile.pathString}; ZIG_PROGRESS=\$var $exePath ${cli.parametersList.parametersString}; exec {var}>&-"
cli.withExePath("bash")
cli.parametersList.clearAll()
cli.addParameters("-c", args)
return IPC(cli, fifoFile, fifo)
}
}

View file

@ -26,7 +26,6 @@ import com.falsepattern.zigbrains.project.steps.ui.BaseNodeDescriptor
import com.intellij.openapi.project.Project
import com.intellij.util.asSafely
import java.io.DataInput
import javax.swing.tree.DefaultMutableTreeNode
data class Payload(val completed: UInt, val estimatedTotal: UInt, val name: String, var children: ArrayList<Payload> = ArrayList()) {
companion object {

View file

@ -22,14 +22,11 @@
package com.falsepattern.zigbrains.shared.ipc
import com.falsepattern.zigbrains.Icons
import com.falsepattern.zigbrains.project.steps.ui.BaseNodeDescriptor
import com.falsepattern.zigbrains.shared.coroutine.withEDTContext
import com.falsepattern.zigbrains.shared.ipc.Payload.Companion.readPayload
import com.falsepattern.zigbrains.shared.zigCoroutineScope
import com.google.common.io.LittleEndianDataInputStream
import com.intellij.icons.AllIcons
import com.intellij.openapi.application.ModalityState
import com.intellij.openapi.components.Service
import com.intellij.openapi.components.service
import com.intellij.openapi.project.Project
@ -93,7 +90,7 @@ class ZigIPCService(val project: Project) {
return roots
}
private suspend fun watch(ipc: IPCUtil.IPC, process: Process) {
private suspend fun watch(ipc: IPC, process: Process) {
val currentNode = IPCTreeNode(BaseNodeDescriptor<Any>(project, "pid: ${process.pid()}", AllIcons.Actions.InlayGear))
mutex.withLock {
nodes.add(currentNode)
@ -125,7 +122,7 @@ class ZigIPCService(val project: Project) {
}
}
fun launchWatcher(ipc: IPCUtil.IPC, process: Process) {
fun launchWatcher(ipc: IPC, process: Process) {
project.zigCoroutineScope.launch {
watch(ipc, process)
}

View file

@ -22,8 +22,6 @@
package com.falsepattern.zigbrains.zon.highlighting
import com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter
import com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.Companion
import com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.Companion.BAD_CHAR
import com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.Companion.CHAR
import com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.Companion.COMMENT
@ -35,16 +33,12 @@ import com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.Companion
import com.falsepattern.zigbrains.zig.highlighter.ZigSyntaxHighlighter.Companion.STRING_ESC_V
import com.falsepattern.zigbrains.zig.psi.ZigTypes
import com.falsepattern.zigbrains.zon.lexer.ZonHighlightingLexer
import com.falsepattern.zigbrains.zon.lexer.ZonLexerAdapter
import com.falsepattern.zigbrains.zon.psi.ZonTypes
import com.intellij.openapi.editor.HighlighterColors
import com.intellij.openapi.editor.colors.TextAttributesKey
import com.intellij.openapi.fileTypes.SyntaxHighlighterBase
import com.intellij.psi.StringEscapesTokenTypes
import com.intellij.psi.TokenType
import com.intellij.psi.tree.IElementType
import org.jetbrains.annotations.NonNls
import com.intellij.openapi.editor.DefaultLanguageHighlighterColors as DefaultColors
class ZonSyntaxHighlighter : SyntaxHighlighterBase() {
override fun getHighlightingLexer() = ZonHighlightingLexer()

View file

@ -1,7 +1,7 @@
pluginName=ZigBrains
pluginRepositoryUrl=https://github.com/FalsePattern/ZigBrains
pluginVersion=23.0.0
pluginVersion=23.0.1
pluginSinceBuild=242
pluginUntilBuild=242.*

View file

@ -1,5 +1,3 @@
import org.jetbrains.grammarkit.tasks.GenerateLexerTask
import org.jetbrains.grammarkit.tasks.GenerateParserTask
import org.jetbrains.intellij.platform.gradle.IntelliJPlatformType
plugins {

View file

@ -29,22 +29,16 @@ import com.falsepattern.zigbrains.lsp.settings.zlsSettings
import com.intellij.execution.configurations.GeneralCommandLine
import com.intellij.notification.Notification
import com.intellij.notification.NotificationType
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.guessProjectDir
import com.intellij.openapi.util.SystemInfo
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.util.io.toNioPathOrNull
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.toNioPathOrNull
import com.redhat.devtools.lsp4ij.server.OSProcessStreamConnectionProvider
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.encodeToStream
import org.eclipse.lsp4j.InlayHint
import org.eclipse.lsp4j.jsonrpc.messages.Message
import org.eclipse.lsp4j.jsonrpc.messages.ResponseMessage
import org.eclipse.lsp4j.services.LanguageServer
import java.nio.file.Path
import kotlin.io.path.isExecutable
import kotlin.io.path.isRegularFile

View file

@ -22,7 +22,6 @@
package com.falsepattern.zigbrains.lsp.config
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import org.jetbrains.annotations.NonNls

View file

@ -23,7 +23,6 @@
package com.falsepattern.zigbrains.lsp.settings
import com.falsepattern.zigbrains.lsp.config.SemanticTokens
import com.falsepattern.zigbrains.lsp.config.ZLSConfig
import com.falsepattern.zigbrains.project.settings.ZigProjectConfigurationProvider
import com.intellij.openapi.project.Project
import org.jetbrains.annotations.NonNls