feat: Descriptive zig environment errors
This commit is contained in:
parent
91ee38e922
commit
d4a1b69172
9 changed files with 60 additions and 47 deletions
|
@ -22,6 +22,9 @@ Changelog structure reference:
|
|||
- LSP
|
||||
- Error/Warning banner at the top of the editor when ZLS is misconfigured/not running
|
||||
|
||||
- Toolchain
|
||||
- More descriptive error messages when toolchain detection fails
|
||||
|
||||
### Fixed
|
||||
|
||||
- Debugging
|
||||
|
|
|
@ -73,11 +73,10 @@ data class ZigProjectConfigurationData(
|
|||
).notify(project)
|
||||
return@indeterminateStep false
|
||||
}
|
||||
val result = zig.callWithArgs(workDir, "init")
|
||||
if (result == null) {
|
||||
val result = zig.callWithArgs(workDir, "init").getOrElse { throwable ->
|
||||
Notification(
|
||||
"zigbrains",
|
||||
"\"zig init\" could not run because the zig executable was missing!",
|
||||
"Failed to run \"zig init\": ${throwable.message}",
|
||||
NotificationType.ERROR
|
||||
).notify(project)
|
||||
return@indeterminateStep false
|
||||
|
|
|
@ -27,11 +27,11 @@ import com.falsepattern.zigbrains.direnv.DirenvCmd
|
|||
import com.falsepattern.zigbrains.project.toolchain.LocalZigToolchain
|
||||
import com.falsepattern.zigbrains.project.toolchain.ZigToolchainProvider
|
||||
import com.falsepattern.zigbrains.shared.coroutine.launchWithEDT
|
||||
import com.falsepattern.zigbrains.shared.coroutine.withEDTContext
|
||||
import com.falsepattern.zigbrains.shared.zigCoroutineScope
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.application.ModalityState
|
||||
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.project.ProjectManager
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.openapi.util.UserDataHolderBase
|
||||
import com.intellij.openapi.util.io.toNioPathOrNull
|
||||
|
@ -41,7 +41,7 @@ import com.intellij.platform.ide.progress.withModalProgress
|
|||
import com.intellij.ui.DocumentAdapter
|
||||
import com.intellij.ui.JBColor
|
||||
import com.intellij.ui.components.JBCheckBox
|
||||
import com.intellij.ui.components.JBLabel
|
||||
import com.intellij.ui.components.JBTextArea
|
||||
import com.intellij.ui.components.textFieldWithBrowseButton
|
||||
import com.intellij.ui.dsl.builder.AlignX
|
||||
import com.intellij.ui.dsl.builder.Panel
|
||||
|
@ -67,7 +67,7 @@ class ZigProjectSettingsPanel(private val project: Project) : ZigProjectConfigur
|
|||
})
|
||||
Disposer.register(this, it)
|
||||
}
|
||||
private val toolchainVersion = JBLabel()
|
||||
private val toolchainVersion = JBTextArea().also { it.isEditable = false }
|
||||
private val stdFieldOverride = JBCheckBox(ZigBrainsBundle.message("settings.project.label.override-std")).apply {
|
||||
addChangeListener {
|
||||
if (isSelected) {
|
||||
|
@ -159,35 +159,39 @@ class ZigProjectSettingsPanel(private val project: Project) : ZigProjectConfigur
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
private suspend fun updateUI() {
|
||||
val pathToToolchain = this.pathToToolchain.text.ifBlank { null }?.toNioPathOrNull()
|
||||
delay(200)
|
||||
val toolchain = pathToToolchain?.let { LocalZigToolchain(it) }
|
||||
val zig = toolchain?.zig
|
||||
if (zig?.path()?.toFile()?.exists() != true) {
|
||||
toolchainVersion.text = "[zig binary not found]"
|
||||
|
||||
if (!stdFieldOverride.isSelected) {
|
||||
pathToStd.text = ""
|
||||
val pathToToolchain = this.pathToToolchain.text.ifBlank { null }?.toNioPathOrNull()
|
||||
if (pathToToolchain == null) {
|
||||
withEDTContext(ModalityState.any()) {
|
||||
toolchainVersion.text = "[toolchain path empty or invalid]"
|
||||
if (!stdFieldOverride.isSelected) {
|
||||
pathToStd.text = ""
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
val env = zig.getEnv(project)
|
||||
if (env == null) {
|
||||
toolchainVersion.text = "[failed to run zig env]"
|
||||
if (!stdFieldOverride.isSelected) {
|
||||
pathToStd.text = ""
|
||||
val toolchain = LocalZigToolchain(pathToToolchain)
|
||||
val zig = toolchain.zig
|
||||
val env = zig.getEnv(project).getOrElse { throwable ->
|
||||
throwable.printStackTrace()
|
||||
withEDTContext(ModalityState.any()) {
|
||||
toolchainVersion.text = "[failed to run \"zig env\"]\n${throwable.message}"
|
||||
if (!stdFieldOverride.isSelected) {
|
||||
pathToStd.text = ""
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
val version = env.version
|
||||
val stdPath = env.stdPath(toolchain, project)
|
||||
toolchainVersion.text = version
|
||||
toolchainVersion.foreground = JBColor.foreground()
|
||||
|
||||
if (!stdFieldOverride.isSelected) {
|
||||
pathToStd.text = stdPath?.pathString ?: ""
|
||||
withEDTContext(ModalityState.any()) {
|
||||
toolchainVersion.text = version
|
||||
toolchainVersion.foreground = JBColor.foreground()
|
||||
if (!stdFieldOverride.isSelected) {
|
||||
pathToStd.text = stdPath?.pathString ?: ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -84,9 +84,12 @@ class ZigStepDiscoveryService(private val project: Project) {
|
|||
project.guessProjectDir()?.toNioPathOrNull(),
|
||||
"build", "-l",
|
||||
timeoutMillis = currentTimeoutSec * 1000L
|
||||
)
|
||||
).getOrElse { throwable ->
|
||||
errorReload(ErrorType.MissingZigExe, throwable.message)
|
||||
null
|
||||
}
|
||||
if (result == null) {
|
||||
errorReload(ErrorType.MissingZigExe)
|
||||
{}
|
||||
} else if (result.checkSuccess(LOG)) {
|
||||
currentTimeoutSec = DEFAULT_TIMEOUT_SEC
|
||||
val lines = result.stdoutLines
|
||||
|
|
|
@ -132,7 +132,7 @@ private fun getName(
|
|||
project: Project
|
||||
): String {
|
||||
val tc = state.toolchain ?: return "Zig"
|
||||
val version = runBlocking { tc.zig.getEnv(project)?.version } ?: return "Zig"
|
||||
val version = runBlocking { tc.zig.getEnv(project) }.mapCatching { it.version }.getOrElse { return "Zig" }
|
||||
return "Zig $version"
|
||||
}
|
||||
|
||||
|
@ -156,7 +156,7 @@ suspend fun getRoot(
|
|||
}
|
||||
}
|
||||
if (toolchain != null) {
|
||||
val stdPath = toolchain.zig.getEnv(project)?.stdPath(toolchain, project) ?: return null
|
||||
val stdPath = toolchain.zig.getEnv(project).mapCatching { it.stdPath(toolchain, project) }.getOrNull() ?: return null
|
||||
val roots = stdPath.refreshAndFindVirtualDirectory() ?: return null
|
||||
return roots
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ 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) {
|
||||
|
@ -37,12 +38,12 @@ class ZigCompilerTool(toolchain: AbstractZigToolchain) : ZigTool(toolchain) {
|
|||
return toolchain.pathToExecutable(toolName)
|
||||
}
|
||||
|
||||
suspend fun getEnv(project: Project?): ZigToolchainEnvironmentSerializable? {
|
||||
val stdout = callWithArgs(toolchain.workingDirectory(project), "env")?.stdout ?: return null
|
||||
suspend fun getEnv(project: Project?): Result<ZigToolchainEnvironmentSerializable> {
|
||||
val stdout = callWithArgs(toolchain.workingDirectory(project), "env").getOrElse { throwable -> return Result.failure(throwable) }.stdout
|
||||
return try {
|
||||
envJson.decodeFromString<ZigToolchainEnvironmentSerializable>(stdout)
|
||||
Result.success(envJson.decodeFromString<ZigToolchainEnvironmentSerializable>(stdout))
|
||||
} catch (e: SerializationException) {
|
||||
null
|
||||
Result.failure(IllegalStateException("could not deserialize zig env", e))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,13 +31,15 @@ import kotlinx.coroutines.runInterruptible
|
|||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.coroutines.withTimeoutOrNull
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.isRegularFile
|
||||
import kotlin.io.path.exists
|
||||
import kotlin.io.path.isDirectory
|
||||
import kotlin.io.path.pathString
|
||||
|
||||
abstract class ZigTool(val toolchain: AbstractZigToolchain) {
|
||||
abstract val toolName: String
|
||||
|
||||
suspend fun callWithArgs(workingDirectory: Path?, vararg parameters: String, timeoutMillis: Long = Long.MAX_VALUE): ProcessOutput? {
|
||||
val cli = createBaseCommandLine(workingDirectory, *parameters) ?: return null
|
||||
suspend fun callWithArgs(workingDirectory: Path?, vararg parameters: String, timeoutMillis: Long = Long.MAX_VALUE): Result<ProcessOutput> {
|
||||
val cli = createBaseCommandLine(workingDirectory, *parameters).let { it.getOrElse { return Result.failure(it) } }
|
||||
|
||||
val (process, exitCode) = withContext(Dispatchers.IO) {
|
||||
val process = cli.createProcess()
|
||||
|
@ -47,28 +49,30 @@ abstract class ZigTool(val toolchain: AbstractZigToolchain) {
|
|||
process to exit
|
||||
}
|
||||
return runInterruptible {
|
||||
ProcessOutput(
|
||||
Result.success(ProcessOutput(
|
||||
process.inputStream.bufferedReader().use { it.readText() },
|
||||
process.errorStream.bufferedReader().use { it.readText() },
|
||||
exitCode ?: -1,
|
||||
exitCode == null,
|
||||
false
|
||||
)
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun createBaseCommandLine(
|
||||
workingDirectory: Path?,
|
||||
vararg parameters: String
|
||||
): GeneralCommandLine? {
|
||||
): Result<GeneralCommandLine> {
|
||||
val exe = toolchain.pathToExecutable(toolName)
|
||||
if (!exe.isRegularFile())
|
||||
return null
|
||||
if (!exe.exists())
|
||||
return Result.failure(IllegalArgumentException("file does not exist: ${exe.pathString}"))
|
||||
if (exe.isDirectory())
|
||||
return Result.failure(IllegalArgumentException("file is a directory: ${exe.pathString}"))
|
||||
val cli = GeneralCommandLine()
|
||||
.withExePath(exe.toString())
|
||||
.withWorkingDirectory(workingDirectory)
|
||||
.withParameters(*parameters)
|
||||
.withCharset(Charsets.UTF_8)
|
||||
return toolchain.patchCommandLine(cli)
|
||||
return Result.success(toolchain.patchCommandLine(cli))
|
||||
}
|
||||
}
|
|
@ -105,7 +105,7 @@ configuration.build.marker-name=Build and Run
|
|||
settings.project.group.title=Zig Settings
|
||||
settings.project.label.direnv=Use direnv
|
||||
settings.project.label.toolchain=Toolchain location
|
||||
settings.project.label.toolchain-version=Toolchain version
|
||||
settings.project.label.toolchain-version=Detected toolchain version
|
||||
settings.project.label.override-std=Override standard library
|
||||
settings.project.label.std-location=Standard library location
|
||||
build.tool.window.tree.steps.label=Steps
|
||||
|
|
|
@ -39,12 +39,11 @@ class ToolchainZLSConfigProvider: SuspendingZLSConfigProvider {
|
|||
var state = svc.state
|
||||
val toolchain = state.toolchain ?: ZigToolchainProvider.suggestToolchain(project, UserDataHolderBase()) ?: return previous
|
||||
|
||||
val env = toolchain.zig.getEnv(project)
|
||||
|
||||
if (env == null) {
|
||||
val env = toolchain.zig.getEnv(project).getOrElse { throwable ->
|
||||
throwable.printStackTrace()
|
||||
Notification(
|
||||
"zigbrains-lsp",
|
||||
"Failed to evaluate zig env",
|
||||
"Failed to evaluate \"zig env\": ${throwable.message}",
|
||||
NotificationType.ERROR
|
||||
).notify(project)
|
||||
return previous
|
||||
|
|
Loading…
Add table
Reference in a new issue