backport: 22.0.0
This commit is contained in:
parent
ae08287d7e
commit
0162e53b01
33 changed files with 378 additions and 270 deletions
25
CHANGELOG.md
25
CHANGELOG.md
|
@ -17,6 +17,31 @@ Changelog structure reference:
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
## [22.0.0]
|
||||
|
||||
### Added
|
||||
|
||||
- LSP
|
||||
- Error/Warning banner at the top of the editor when ZLS is misconfigured/not running
|
||||
- ZLS version indicator in the zig settings
|
||||
|
||||
- Toolchain
|
||||
- More descriptive error messages when toolchain detection fails
|
||||
|
||||
### Changed
|
||||
|
||||
- Project
|
||||
- !!BREAKING CHANGE!! Changed file format of zig tasks to store command line arguments as strings instead of string lists.
|
||||
This (and newer) versions of the plugin will automatically upgrade tasks from 21.1.0 and before.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Debugging
|
||||
- Breakpoints could not be placed inside zig code in Android Studio
|
||||
|
||||
- Project
|
||||
- Zig run/debug configuration command line arguments would lose quotes around arguments
|
||||
|
||||
## [21.1.0]
|
||||
|
||||
### Added
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
# Code of Merit
|
||||
|
||||
1. The project creators, lead developers, core team, constitute
|
||||
the managing members of the project and have final say in every decision
|
||||
of the project, technical or otherwise, including overruling previous decisions.
|
||||
There are no limitations to this decisional power.
|
||||
|
||||
2. Contributions are an expected result of your membership on the project.
|
||||
Don't expect others to do your work or help you with your work forever.
|
||||
|
||||
3. All members have the same opportunities to seek any challenge they want
|
||||
within the project.
|
||||
|
||||
4. Authority or position in the project will be proportional
|
||||
to the accrued contribution. Seniority must be earned.
|
||||
|
||||
5. Software is evolutive: the better implementations must supersede lesser
|
||||
implementations. Technical advantage is the primary evaluation metric.
|
||||
|
||||
6. This is a space for technical prowess; topics outside of the project
|
||||
will not be tolerated.
|
||||
|
||||
7. Non technical conflicts will be discussed in a separate space. Disruption
|
||||
of the project will not be allowed.
|
||||
|
||||
8. Individual characteristics, including but not limited to,
|
||||
body, sex, sexual preference, race, language, religion, nationality,
|
||||
or political preferences are irrelevant in the scope of the project and
|
||||
will not be taken into account concerning your value or that of your contribution
|
||||
to the project.
|
||||
|
||||
9. Discuss or debate the idea, not the person.
|
||||
|
||||
10. There is no room for ambiguity: Ambiguity will be met with questioning;
|
||||
further ambiguity will be met with silence. It is the responsibility
|
||||
of the originator to provide requested context.
|
||||
|
||||
11. If something is illegal outside the scope of the project, it is illegal
|
||||
in the scope of the project. This Code of Merit does not take precedence over
|
||||
governing law.
|
||||
|
||||
12. This Code of Merit governs the technical procedures of the project not the
|
||||
activities outside of it.
|
||||
|
||||
13. Participation on the project equates to agreement of this Code of Merit.
|
||||
|
||||
14. No objectives beyond the stated objectives of this project are relevant
|
||||
to the project. Any intent to deviate the project from its original purpose
|
||||
of existence will constitute grounds for remedial action which may include
|
||||
expulsion from the project.
|
||||
|
||||
This document is adapted from the Code of Merit, version 1.0.
|
||||
See: https://codeofmerit.org/.
|
|
@ -39,7 +39,7 @@ class ZigExecConfigBinary(project: Project, factory: ConfigurationFactory) : Zig
|
|||
get() = ZigDebugBundle.message("configuration.binary.suggested-name")
|
||||
|
||||
override suspend fun buildCommandLineArgs(debug: Boolean): List<String> {
|
||||
return args.args
|
||||
return args.argsSplit()
|
||||
}
|
||||
|
||||
override fun getConfigurables(): List<ZigConfigurable<*>> {
|
||||
|
|
|
@ -36,6 +36,6 @@ class ZigDebugParametersBinary @Throws(ExecutionException::class) constructor(dr
|
|||
ZigDebugParametersBase<ZigProfileStateBinary>(driverConfiguration, toolchain, profileState) {
|
||||
private val executableFile = profileState.configuration.exePath.path?.toFile() ?: throw ExecutionException(ZigDebugBundle.message("exception.missing-exe-path"))
|
||||
override fun getInstaller(): Installer {
|
||||
return ZigDebugEmitBinaryInstaller(profileState, toolchain, executableFile, profileState.configuration.args.args)
|
||||
return ZigDebugEmitBinaryInstaller(profileState, toolchain, executableFile, profileState.configuration.args.argsSplit())
|
||||
}
|
||||
}
|
|
@ -53,7 +53,7 @@ class ZigDebugParametersBuild(
|
|||
private lateinit var executableFile: File
|
||||
|
||||
override fun getInstaller(): Installer {
|
||||
return ZigDebugEmitBinaryInstaller(profileState, toolchain, executableFile, profileState.configuration.exeArgs.args)
|
||||
return ZigDebugEmitBinaryInstaller(profileState, toolchain, executableFile, profileState.configuration.exeArgs.argsSplit())
|
||||
}
|
||||
|
||||
@Throws(ExecutionException::class)
|
||||
|
|
|
@ -32,6 +32,6 @@ import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriverConfiguration
|
|||
class ZigDebugParametersRun(driverConfiguration: DebuggerDriverConfiguration, toolchain: AbstractZigToolchain, profileState: ZigProfileStateRun) :
|
||||
ZigDebugParametersEmitBinaryBase<ZigProfileStateRun>(driverConfiguration, toolchain, profileState) {
|
||||
override fun getInstaller(): Installer {
|
||||
return ZigDebugEmitBinaryInstaller(profileState, toolchain, executableFile, profileState.configuration.exeArgs.args)
|
||||
return ZigDebugEmitBinaryInstaller(profileState, toolchain, executableFile, profileState.configuration.exeArgs.argsSplit())
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
<idea-plugin package="com.falsepattern.zigbrains.debugger">
|
||||
<depends>com.intellij.nativeDebug</depends>
|
||||
<depends>com.intellij.modules.cidr.debugger</depends>
|
||||
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<configurationType
|
||||
|
|
|
@ -347,14 +347,18 @@ class ArgsConfigurable(
|
|||
@Transient private val serializedName: String,
|
||||
@Transient @Nls private val guiName: String
|
||||
) : ZigConfigurable<ArgsConfigurable>, Cloneable {
|
||||
var args: List<String> = emptyList()
|
||||
var args: String = ""
|
||||
|
||||
override fun readExternal(element: Element) {
|
||||
args = element.readStrings(serializedName) ?: return
|
||||
args = element.readString(serializedName) ?: element.readStrings(serializedName)?.joinToString(separator = " ") { if (it.contains(' ')) "\"$it\"" else it } ?: ""
|
||||
}
|
||||
|
||||
fun argsSplit(): List<String> {
|
||||
return translateCommandline(args)
|
||||
}
|
||||
|
||||
override fun writeExternal(element: Element) {
|
||||
element.writeStrings(serializedName, args)
|
||||
element.writeString(serializedName, args)
|
||||
}
|
||||
|
||||
override fun createEditor(): ZigConfigModule<ArgsConfigurable> {
|
||||
|
@ -376,12 +380,12 @@ class ArgsConfigurable(
|
|||
}
|
||||
|
||||
override fun apply(configurable: ArgsConfigurable): Boolean {
|
||||
configurable.args = translateCommandline(argsField.text)
|
||||
configurable.args = argsField.text ?: ""
|
||||
return true
|
||||
}
|
||||
|
||||
override fun reset(configurable: ArgsConfigurable) {
|
||||
argsField.text = configurable.args.joinToString(separator = " ")
|
||||
argsField.text = configurable.args
|
||||
}
|
||||
|
||||
override fun construct(p: Panel): Unit = with(p) {
|
||||
|
|
|
@ -48,9 +48,10 @@ class ZigExecConfigBuild(project: Project, factory: ConfigurationFactory): ZigEx
|
|||
override suspend fun buildCommandLineArgs(debug: Boolean): List<String> {
|
||||
val result = ArrayList<String>()
|
||||
result.add("build")
|
||||
val argsSplit = buildSteps.argsSplit()
|
||||
val steps = if (debug) {
|
||||
val truncatedSteps = ArrayList<String>()
|
||||
for (step in buildSteps.args) {
|
||||
for (step in argsSplit) {
|
||||
if (step == "run")
|
||||
continue
|
||||
|
||||
|
@ -60,10 +61,10 @@ class ZigExecConfigBuild(project: Project, factory: ConfigurationFactory): ZigEx
|
|||
truncatedSteps.add(step)
|
||||
}
|
||||
truncatedSteps
|
||||
} else buildSteps.args
|
||||
} else argsSplit
|
||||
result.addAll(steps)
|
||||
result.addAll(coloredCliFlags(colored.value, debug))
|
||||
result.addAll(extraArgs.args)
|
||||
result.addAll(extraArgs.argsSplit())
|
||||
return result
|
||||
}
|
||||
|
||||
|
|
|
@ -52,10 +52,10 @@ class ZigExecConfigRun(project: Project, factory: ConfigurationFactory): ZigExec
|
|||
if (!debug || optimization.forced) {
|
||||
result.addAll(listOf("-O", optimization.level.name))
|
||||
}
|
||||
result.addAll(compilerArgs.args)
|
||||
result.addAll(compilerArgs.argsSplit())
|
||||
if (!debug) {
|
||||
result.add("--")
|
||||
result.addAll(exeArgs.args)
|
||||
result.addAll(exeArgs.argsSplit())
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ class ZigExecConfigTest(project: Project, factory: ConfigurationFactory): ZigExe
|
|||
if (!debug || optimization.forced) {
|
||||
result.addAll(listOf("-O", optimization.level.name))
|
||||
}
|
||||
result.addAll(compilerArgs.args)
|
||||
result.addAll(compilerArgs.argsSplit())
|
||||
if (debug) {
|
||||
result.add("--test-no-exec")
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
@ -68,7 +68,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) {
|
||||
|
@ -161,35 +161,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
|
||||
|
|
|
@ -87,7 +87,7 @@ class BuildToolWindowContext(private val project: Project): Disposable {
|
|||
val factory = firstConfigFactory<ZigConfigTypeBuild>()
|
||||
val newConfig = manager.createConfiguration("zig build $stepName", factory)
|
||||
val config = newConfig.configuration as ZigExecConfigBuild
|
||||
config.buildSteps.args = listOf(stepName)
|
||||
config.buildSteps.args = stepName
|
||||
manager.addConfiguration(newConfig)
|
||||
return@run newConfig
|
||||
}
|
||||
|
@ -213,7 +213,7 @@ private fun getViewport(project: Project): JBScrollPane? {
|
|||
private fun getExistingRunConfig(manager: RunManager, stepName: String): RunnerAndConfigurationSettings? {
|
||||
for (config in manager.getConfigurationSettingsList(ZigConfigTypeBuild::class.java)) {
|
||||
val build = config.configuration as? ZigExecConfigBuild ?: continue
|
||||
val steps = build.buildSteps.args
|
||||
val steps = build.buildSteps.argsSplit()
|
||||
if (steps.size != 1)
|
||||
continue
|
||||
if (steps[0] != stepName)
|
||||
|
|
|
@ -131,7 +131,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"
|
||||
}
|
||||
|
||||
|
@ -155,7 +155,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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,52 +23,26 @@
|
|||
package com.falsepattern.zigbrains.project.toolchain.tools
|
||||
|
||||
import com.falsepattern.zigbrains.project.toolchain.AbstractZigToolchain
|
||||
import com.falsepattern.zigbrains.shared.cli.call
|
||||
import com.falsepattern.zigbrains.shared.cli.createCommandLineSafe
|
||||
import com.intellij.execution.configurations.GeneralCommandLine
|
||||
import com.intellij.execution.process.ProcessOutput
|
||||
import com.intellij.util.io.awaitExit
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.runInterruptible
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.coroutines.withTimeoutOrNull
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.isRegularFile
|
||||
|
||||
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
|
||||
|
||||
val (process, exitCode) = withContext(Dispatchers.IO) {
|
||||
val process = cli.createProcess()
|
||||
val exit = withTimeoutOrNull(timeoutMillis) {
|
||||
process.awaitExit()
|
||||
}
|
||||
process to exit
|
||||
}
|
||||
return runInterruptible {
|
||||
ProcessOutput(
|
||||
process.inputStream.bufferedReader().use { it.readText() },
|
||||
process.errorStream.bufferedReader().use { it.readText() },
|
||||
exitCode ?: -1,
|
||||
exitCode == null,
|
||||
false
|
||||
)
|
||||
}
|
||||
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) } }
|
||||
return cli.call(timeoutMillis)
|
||||
}
|
||||
|
||||
private suspend fun createBaseCommandLine(
|
||||
workingDirectory: Path?,
|
||||
vararg parameters: String
|
||||
): GeneralCommandLine? {
|
||||
): Result<GeneralCommandLine> {
|
||||
val exe = toolchain.pathToExecutable(toolName)
|
||||
if (!exe.isRegularFile())
|
||||
return null
|
||||
val cli = GeneralCommandLine()
|
||||
.withExePath(exe.toString())
|
||||
.withWorkingDirectory(workingDirectory)
|
||||
.withParameters(*parameters)
|
||||
.withCharset(Charsets.UTF_8)
|
||||
return toolchain.patchCommandLine(cli)
|
||||
return createCommandLineSafe(workingDirectory, exe, *parameters)
|
||||
.mapCatching { toolchain.patchCommandLine(it) }
|
||||
}
|
||||
}
|
|
@ -23,8 +23,19 @@
|
|||
package com.falsepattern.zigbrains.shared.cli
|
||||
|
||||
import com.falsepattern.zigbrains.ZigBrainsBundle
|
||||
import com.intellij.execution.configurations.GeneralCommandLine
|
||||
import com.intellij.execution.process.ProcessOutput
|
||||
import com.intellij.openapi.options.ConfigurationException
|
||||
import com.intellij.util.io.awaitExit
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.runInterruptible
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.coroutines.withTimeoutOrNull
|
||||
import java.nio.file.Path
|
||||
import java.util.*
|
||||
import kotlin.io.path.exists
|
||||
import kotlin.io.path.isDirectory
|
||||
import kotlin.io.path.pathString
|
||||
|
||||
|
||||
//From Apache Ant
|
||||
|
@ -100,4 +111,40 @@ fun coloredCliFlags(colored: Boolean, debug: Boolean): List<String> {
|
|||
} else {
|
||||
listOf("--color", if (colored) "on" else "off")
|
||||
}
|
||||
}
|
||||
|
||||
fun createCommandLineSafe(
|
||||
workingDirectory: Path?,
|
||||
exe: Path,
|
||||
vararg parameters: String,
|
||||
): Result<GeneralCommandLine> {
|
||||
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 Result.success(cli)
|
||||
}
|
||||
|
||||
suspend fun GeneralCommandLine.call(timeoutMillis: Long = Long.MAX_VALUE): Result<ProcessOutput> {
|
||||
val (process, exitCode) = withContext(Dispatchers.IO) {
|
||||
val process = createProcess()
|
||||
val exit = withTimeoutOrNull(timeoutMillis) {
|
||||
process.awaitExit()
|
||||
}
|
||||
process to exit
|
||||
}
|
||||
return runInterruptible {
|
||||
Result.success(ProcessOutput(
|
||||
process.inputStream.bufferedReader().use { it.readText() },
|
||||
process.errorStream.bufferedReader().use { it.readText() },
|
||||
exitCode ?: -1,
|
||||
exitCode == null,
|
||||
false
|
||||
))
|
||||
}
|
||||
}
|
|
@ -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 zig version
|
||||
settings.project.label.override-std=Override standard library
|
||||
settings.project.label.std-location=Standard library location
|
||||
build.tool.window.tree.steps.label=Steps
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
pluginName=ZigBrains
|
||||
pluginRepositoryUrl=https://github.com/FalsePattern/ZigBrains
|
||||
|
||||
pluginVersion=21.1.0
|
||||
pluginVersion=22.0.0
|
||||
|
||||
pluginSinceBuild=242
|
||||
pluginUntilBuild=242.*
|
||||
|
|
|
@ -26,8 +26,12 @@ import com.falsepattern.zigbrains.direnv.DirenvCmd
|
|||
import com.falsepattern.zigbrains.direnv.emptyEnv
|
||||
import com.falsepattern.zigbrains.direnv.getDirenv
|
||||
import com.falsepattern.zigbrains.lsp.settings.zlsSettings
|
||||
import com.falsepattern.zigbrains.shared.zigCoroutineScope
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.startup.ProjectActivity
|
||||
import com.intellij.ui.EditorNotifications
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.io.path.pathString
|
||||
|
||||
class ZLSStartup: ProjectActivity {
|
||||
|
@ -43,5 +47,16 @@ class ZLSStartup: ProjectActivity {
|
|||
project.zlsSettings.state = zlsState
|
||||
}
|
||||
}
|
||||
project.zigCoroutineScope.launch {
|
||||
var currentState = project.zlsRunningAsync()
|
||||
while (!project.isDisposed) {
|
||||
val running = project.zlsRunningAsync()
|
||||
if (currentState != running) {
|
||||
EditorNotifications.getInstance(project).updateAllNotifications()
|
||||
}
|
||||
currentState = running
|
||||
delay(1000)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -71,15 +71,43 @@ class ZigLanguageServerFactory: LanguageServerFactory, LanguageServerEnablementS
|
|||
return features
|
||||
}
|
||||
|
||||
override fun isEnabled(project: Project): Boolean {
|
||||
return (project.getUserData(ENABLED_KEY) != false) && project.zlsSettings.validate()
|
||||
}
|
||||
override fun isEnabled(project: Project) = project.zlsEnabledSync()
|
||||
|
||||
override fun setEnabled(enabled: Boolean, project: Project) {
|
||||
project.putUserData(ENABLED_KEY, enabled)
|
||||
project.zlsEnabled(enabled)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun Project.zlsEnabledAsync(): Boolean {
|
||||
return (getUserData(ENABLED_KEY) != false) && zlsSettings.validateAsync()
|
||||
}
|
||||
|
||||
fun Project.zlsEnabledSync(): Boolean {
|
||||
return (getUserData(ENABLED_KEY) != false) && zlsSettings.validateSync()
|
||||
}
|
||||
|
||||
fun Project.zlsEnabled(value: Boolean) {
|
||||
putUserData(ENABLED_KEY, value)
|
||||
}
|
||||
|
||||
suspend fun Project.zlsRunningAsync(): Boolean {
|
||||
if (!zlsEnabledAsync())
|
||||
return false
|
||||
return zlsRunningLsp4ij()
|
||||
}
|
||||
|
||||
fun Project.zlsRunningSync(): Boolean {
|
||||
if (!zlsEnabledSync())
|
||||
return false
|
||||
return zlsRunningLsp4ij()
|
||||
}
|
||||
|
||||
private fun Project.zlsRunningLsp4ij(): Boolean {
|
||||
val manager = service<LanguageServerManager>()
|
||||
val status = manager.getServerStatus("ZigBrains")
|
||||
return status == ServerStatus.started || status == ServerStatus.starting
|
||||
}
|
||||
|
||||
class ZLSStarter: LanguageServerStarter {
|
||||
override fun startLSP(project: Project, restart: Boolean) {
|
||||
project.zigCoroutineScope.launch {
|
||||
|
@ -87,7 +115,10 @@ class ZLSStarter: LanguageServerStarter {
|
|||
val status = manager.getServerStatus("ZigBrains")
|
||||
if ((status == ServerStatus.started || status == ServerStatus.starting) && !restart)
|
||||
return@launch
|
||||
manager.start("ZigBrains")
|
||||
manager.stop("ZigBrains")
|
||||
if (project.zlsSettings.validateAsync()) {
|
||||
manager.start("ZigBrains")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* 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.lsp.notification
|
||||
|
||||
import com.falsepattern.zigbrains.lsp.ZLSBundle
|
||||
import com.falsepattern.zigbrains.lsp.settings.zlsSettings
|
||||
import com.falsepattern.zigbrains.lsp.zlsRunningSync
|
||||
import com.falsepattern.zigbrains.zig.ZigFileType
|
||||
import com.falsepattern.zigbrains.zon.ZonFileType
|
||||
import com.intellij.openapi.fileEditor.FileEditor
|
||||
import com.intellij.openapi.project.DumbAware
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.ui.EditorNotificationPanel
|
||||
import com.intellij.ui.EditorNotificationProvider
|
||||
import java.util.function.Function
|
||||
import javax.swing.JComponent
|
||||
|
||||
class ZigEditorNotificationProvider: EditorNotificationProvider, DumbAware {
|
||||
override fun collectNotificationData(
|
||||
project: Project,
|
||||
file: VirtualFile
|
||||
): Function<in FileEditor, out JComponent?>? {
|
||||
when (file.fileType) {
|
||||
ZigFileType, ZonFileType -> {}
|
||||
else -> return null
|
||||
}
|
||||
if (project.zlsRunningSync()) {
|
||||
return null
|
||||
}
|
||||
return Function { editor ->
|
||||
val status: EditorNotificationPanel.Status
|
||||
val message: String
|
||||
if (!project.zlsSettings.validateSync()) {
|
||||
status = EditorNotificationPanel.Status.Error
|
||||
message = ZLSBundle.message("notification.banner.zls-bad-config")
|
||||
} else {
|
||||
status = EditorNotificationPanel.Status.Warning
|
||||
message = ZLSBundle.message("notification.banner.zls-not-running")
|
||||
}
|
||||
EditorNotificationPanel(editor, status).also { it.text = message }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -25,6 +25,7 @@ package com.falsepattern.zigbrains.lsp.settings
|
|||
import com.falsepattern.zigbrains.direnv.emptyEnv
|
||||
import com.falsepattern.zigbrains.direnv.getDirenv
|
||||
import com.falsepattern.zigbrains.lsp.ZLSBundle
|
||||
import com.falsepattern.zigbrains.lsp.startLSP
|
||||
import com.intellij.openapi.components.*
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.io.toNioPathOrNull
|
||||
|
@ -32,9 +33,9 @@ import com.intellij.platform.ide.progress.ModalTaskOwner
|
|||
import com.intellij.platform.ide.progress.runWithModalProgressBlocking
|
||||
import com.intellij.util.application
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import java.nio.file.Path
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
import kotlin.concurrent.withLock
|
||||
import kotlin.io.path.isExecutable
|
||||
import kotlin.io.path.isRegularFile
|
||||
|
||||
|
@ -51,47 +52,45 @@ class ZLSProjectSettingsService(val project: Project): PersistentStateComponent<
|
|||
@Volatile
|
||||
private var valid = false
|
||||
|
||||
private val mutex = ReentrantLock()
|
||||
private val mutex = Mutex()
|
||||
override fun getState(): ZLSSettings {
|
||||
return state.copy()
|
||||
}
|
||||
|
||||
fun setState(value: ZLSSettings) {
|
||||
mutex.withLock {
|
||||
this.state = value
|
||||
dirty = true
|
||||
runBlocking {
|
||||
mutex.withLock {
|
||||
this@ZLSProjectSettingsService.state = value
|
||||
dirty = true
|
||||
}
|
||||
}
|
||||
startLSP(project, true)
|
||||
}
|
||||
|
||||
override fun loadState(state: ZLSSettings) {
|
||||
mutex.withLock {
|
||||
this.state = state
|
||||
dirty = true
|
||||
}
|
||||
setState(state)
|
||||
}
|
||||
|
||||
fun isModified(otherData: ZLSSettings): Boolean {
|
||||
return state != otherData
|
||||
}
|
||||
|
||||
fun validate(): Boolean {
|
||||
suspend fun validateAsync(): Boolean {
|
||||
mutex.withLock {
|
||||
if (dirty) {
|
||||
val state = this.state
|
||||
valid = if (application.isDispatchThread) {
|
||||
runWithModalProgressBlocking(ModalTaskOwner.project(project), ZLSBundle.message("progress.title.validate")) {
|
||||
doValidate(project, state)
|
||||
}
|
||||
} else {
|
||||
runBlocking {
|
||||
doValidate(project, state)
|
||||
}
|
||||
}
|
||||
valid = doValidate(project, state)
|
||||
dirty = false
|
||||
}
|
||||
return valid
|
||||
}
|
||||
}
|
||||
|
||||
fun validateSync() = if (application.isDispatchThread) {
|
||||
runWithModalProgressBlocking(ModalTaskOwner.project(project), ZLSBundle.message("progress.title.validate")) {
|
||||
validateAsync()
|
||||
}
|
||||
} else {
|
||||
runBlocking {
|
||||
validateAsync()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun doValidate(project: Project, state: ZLSSettings): Boolean {
|
||||
|
|
|
@ -38,7 +38,7 @@ data class ZLSSettings(
|
|||
val enable_argument_placeholders: Boolean = true,
|
||||
val completion_label_details: Boolean = true,
|
||||
val enable_build_on_save: Boolean = false,
|
||||
val build_on_save_args: List<String> = emptyList(),
|
||||
val build_on_save_args: String = "",
|
||||
val semantic_tokens: SemanticTokens = SemanticTokens.full,
|
||||
val inlay_hints_show_variable_type_hints: Boolean = true,
|
||||
val inlay_hints_show_struct_literal_field_type: Boolean = true,
|
||||
|
|
|
@ -24,6 +24,7 @@ package com.falsepattern.zigbrains.lsp.settings
|
|||
|
||||
import com.falsepattern.zigbrains.lsp.config.ZLSConfig
|
||||
import com.falsepattern.zigbrains.lsp.config.ZLSConfigProvider
|
||||
import com.falsepattern.zigbrains.shared.cli.translateCommandline
|
||||
import com.intellij.openapi.project.Project
|
||||
|
||||
class ZLSSettingsConfigProvider: ZLSConfigProvider {
|
||||
|
@ -34,7 +35,14 @@ class ZLSSettingsConfigProvider: ZLSConfigProvider {
|
|||
enable_argument_placeholders = state.enable_argument_placeholders,
|
||||
completion_label_details = state.completion_label_details,
|
||||
enable_build_on_save = state.enable_build_on_save,
|
||||
build_on_save_args = state.build_on_save_args,
|
||||
build_on_save_args = run {
|
||||
val args = state.build_on_save_args
|
||||
return@run if (args.isEmpty()) {
|
||||
emptyList()
|
||||
} else {
|
||||
translateCommandline(args).toList()
|
||||
}
|
||||
},
|
||||
semantic_tokens = state.semantic_tokens,
|
||||
inlay_hints_show_variable_type_hints = state.inlay_hints_show_variable_type_hints,
|
||||
inlay_hints_show_struct_literal_field_type = state.inlay_hints_show_struct_literal_field_type,
|
||||
|
|
|
@ -42,11 +42,7 @@ class ZLSSettingsConfigurable(private val project: Project): SubConfigurable {
|
|||
override fun apply() {
|
||||
val data = appSettingsComponent?.data ?: return
|
||||
val settings = project.zlsSettings
|
||||
val reloadZLS = settings.isModified(data)
|
||||
settings.state = data
|
||||
if (reloadZLS) {
|
||||
startLSP(project, true)
|
||||
}
|
||||
}
|
||||
|
||||
override fun reset() {
|
||||
|
|
|
@ -29,24 +29,38 @@ import com.falsepattern.zigbrains.direnv.getDirenv
|
|||
import com.falsepattern.zigbrains.lsp.ZLSBundle
|
||||
import com.falsepattern.zigbrains.lsp.config.SemanticTokens
|
||||
import com.falsepattern.zigbrains.project.settings.ZigProjectConfigurationProvider
|
||||
import com.falsepattern.zigbrains.shared.cli.call
|
||||
import com.falsepattern.zigbrains.shared.cli.createCommandLineSafe
|
||||
import com.falsepattern.zigbrains.shared.coroutine.launchWithEDT
|
||||
import com.falsepattern.zigbrains.shared.coroutine.withEDTContext
|
||||
import com.falsepattern.zigbrains.shared.zigCoroutineScope
|
||||
import com.intellij.execution.processTools.mapFlat
|
||||
import com.intellij.openapi.application.ModalityState
|
||||
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.project.guessProjectDir
|
||||
import com.intellij.openapi.ui.ComboBox
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.openapi.util.io.toNioPathOrNull
|
||||
import com.intellij.openapi.vfs.toNioPathOrNull
|
||||
import com.intellij.platform.ide.progress.ModalTaskOwner
|
||||
import com.intellij.platform.ide.progress.TaskCancellation
|
||||
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.JBTextArea
|
||||
import com.intellij.ui.components.fields.ExtendableTextField
|
||||
import com.intellij.ui.components.textFieldWithBrowseButton
|
||||
import com.intellij.ui.dsl.builder.AlignX
|
||||
import com.intellij.ui.dsl.builder.Panel
|
||||
import com.intellij.ui.dsl.builder.Row
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import org.jetbrains.annotations.PropertyKey
|
||||
import java.lang.IllegalArgumentException
|
||||
import java.util.*
|
||||
import javax.swing.event.DocumentEvent
|
||||
import kotlin.io.path.pathString
|
||||
|
||||
@Suppress("PrivatePropertyName")
|
||||
|
@ -55,13 +69,24 @@ class ZLSSettingsPanel(private val project: Project) : ZigProjectConfigurationPr
|
|||
project,
|
||||
ZLSBundle.message("settings.zls-path.browse.title"),
|
||||
FileChooserDescriptorFactory.createSingleFileDescriptor(),
|
||||
).also { Disposer.register(this, it) }
|
||||
).also {
|
||||
it.textField.document.addDocumentListener(object: DocumentAdapter() {
|
||||
override fun textChanged(p0: DocumentEvent) {
|
||||
dispatchUpdateUI()
|
||||
}
|
||||
})
|
||||
Disposer.register(this, it)
|
||||
}
|
||||
private val zlsConfigPath = textFieldWithBrowseButton(
|
||||
project,
|
||||
ZLSBundle.message("settings.zls-config-path.browse.title"),
|
||||
FileChooserDescriptorFactory.createSingleFileDescriptor()
|
||||
).also { Disposer.register(this, it) }
|
||||
|
||||
private val zlsVersion = JBTextArea().also { it.isEditable = false }
|
||||
|
||||
private var debounce: Job? = null
|
||||
|
||||
private val inlayHints = JBCheckBox()
|
||||
private val enable_snippets = JBCheckBox()
|
||||
private val enable_argument_placeholders = JBCheckBox()
|
||||
|
@ -102,6 +127,9 @@ class ZLSSettingsPanel(private val project: Project) : ZigProjectConfigurationPr
|
|||
cell(direnv)
|
||||
}
|
||||
}
|
||||
row(ZLSBundle.message("settings.zls-version.label")) {
|
||||
cell(zlsVersion)
|
||||
}
|
||||
fancyRow(
|
||||
"settings.zls-config-path.label",
|
||||
"settings.zls-config-path.tooltip"
|
||||
|
@ -207,14 +235,7 @@ class ZLSSettingsPanel(private val project: Project) : ZigProjectConfigurationPr
|
|||
enable_argument_placeholders.isSelected,
|
||||
completion_label_details.isSelected,
|
||||
enable_build_on_save.isSelected,
|
||||
run {
|
||||
val args = build_on_save_args.text ?: ""
|
||||
return@run if (args.isEmpty()) {
|
||||
emptyList()
|
||||
} else {
|
||||
translateCommandline(args).toList()
|
||||
}
|
||||
},
|
||||
build_on_save_args.text,
|
||||
semantic_tokens.item ?: SemanticTokens.full,
|
||||
inlay_hints_show_variable_type_hints.isSelected,
|
||||
inlay_hints_show_struct_literal_field_type.isSelected,
|
||||
|
@ -240,7 +261,7 @@ class ZLSSettingsPanel(private val project: Project) : ZigProjectConfigurationPr
|
|||
enable_argument_placeholders.isSelected = value.enable_argument_placeholders
|
||||
completion_label_details.isSelected = value.completion_label_details
|
||||
enable_build_on_save.isSelected = value.enable_build_on_save
|
||||
build_on_save_args.text = value.build_on_save_args.joinToString(separator = " ") { it }
|
||||
build_on_save_args.text = value.build_on_save_args
|
||||
semantic_tokens.item = value.semantic_tokens
|
||||
inlay_hints_show_variable_type_hints.isSelected = value.inlay_hints_show_variable_type_hints
|
||||
inlay_hints_show_struct_literal_field_type.isSelected = value.inlay_hints_show_struct_literal_field_type
|
||||
|
@ -257,6 +278,7 @@ class ZLSSettingsPanel(private val project: Project) : ZigProjectConfigurationPr
|
|||
builtin_path.text = value.builtin_path ?: ""
|
||||
build_runner_path.text = value.build_runner_path ?: ""
|
||||
global_cache_path.text = value.global_cache_path ?: ""
|
||||
dispatchUpdateUI()
|
||||
}
|
||||
|
||||
private fun dispatchAutodetect(force: Boolean) {
|
||||
|
@ -272,12 +294,14 @@ class ZLSSettingsPanel(private val project: Project) : ZigProjectConfigurationPr
|
|||
getDirenv().findExecutableOnPATH("zls")?.let {
|
||||
if (force || zlsPath.text.isBlank()) {
|
||||
zlsPath.text = it.pathString
|
||||
dispatchUpdateUI()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
debounce?.cancel("Disposed")
|
||||
}
|
||||
|
||||
private suspend fun getDirenv(): Env {
|
||||
|
@ -285,6 +309,42 @@ class ZLSSettingsPanel(private val project: Project) : ZigProjectConfigurationPr
|
|||
return project.getDirenv()
|
||||
return emptyEnv
|
||||
}
|
||||
|
||||
private fun dispatchUpdateUI() {
|
||||
debounce?.cancel("New debounce")
|
||||
debounce = project.zigCoroutineScope.launch {
|
||||
updateUI()
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun updateUI() {
|
||||
if (project.isDefault)
|
||||
return
|
||||
delay(200)
|
||||
val zlsPath = this.zlsPath.text.ifBlank { null }?.toNioPathOrNull()
|
||||
if (zlsPath == null) {
|
||||
withEDTContext(ModalityState.any()) {
|
||||
zlsVersion.text = "[zls path empty or invalid]"
|
||||
}
|
||||
return
|
||||
}
|
||||
val workingDir = project.guessProjectDir()?.toNioPathOrNull()
|
||||
val result = createCommandLineSafe(workingDir, zlsPath, "version")
|
||||
.map { it.withEnvironment(getDirenv().env) }
|
||||
.mapFlat { it.call() }
|
||||
.getOrElse { throwable ->
|
||||
throwable.printStackTrace()
|
||||
withEDTContext(ModalityState.any()) {
|
||||
zlsVersion.text = "[failed to run \"zls version\"]\n${throwable.message}"
|
||||
}
|
||||
return
|
||||
}
|
||||
val version = result.stdout
|
||||
withEDTContext(ModalityState.any()) {
|
||||
zlsVersion.text = version
|
||||
zlsVersion.foreground = JBColor.foreground()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Panel.fancyRow(
|
||||
|
@ -294,80 +354,4 @@ private fun Panel.fancyRow(
|
|||
) = row(ZLSBundle.message(label)) {
|
||||
contextHelp(ZLSBundle.message(tooltip))
|
||||
cb()
|
||||
}
|
||||
|
||||
|
||||
@Throws(Exception::class)
|
||||
private fun translateCommandline(toProcess: String): List<String> {
|
||||
if (toProcess.isEmpty()) {
|
||||
return emptyList()
|
||||
}
|
||||
val normal = 0
|
||||
val inQuote = 1
|
||||
val inDoubleQuote = 2
|
||||
val inEscape = 3
|
||||
var state = normal
|
||||
var escapeState = normal
|
||||
val tok = StringTokenizer(toProcess, "\\\"' ", true)
|
||||
val v = ArrayList<String>()
|
||||
val current = StringBuilder()
|
||||
|
||||
while (tok.hasMoreTokens()) {
|
||||
val nextTok = tok.nextToken()
|
||||
when (state) {
|
||||
inQuote -> if ("'" == nextTok) {
|
||||
state = normal
|
||||
} else if ("\\" == nextTok) {
|
||||
escapeState = inQuote
|
||||
state = inEscape
|
||||
} else {
|
||||
current.append(nextTok)
|
||||
}
|
||||
|
||||
inDoubleQuote -> if ("\"" == nextTok) {
|
||||
state = normal
|
||||
} else if ("\\" == nextTok) {
|
||||
escapeState = inDoubleQuote
|
||||
state = inEscape
|
||||
} else {
|
||||
current.append(nextTok)
|
||||
}
|
||||
|
||||
inEscape -> {
|
||||
current.append(when(nextTok) {
|
||||
"n" -> "\n"
|
||||
"r" -> "\r"
|
||||
"t" -> "\t"
|
||||
else -> nextTok
|
||||
})
|
||||
state = escapeState
|
||||
}
|
||||
|
||||
else -> if ("'" == nextTok) {
|
||||
state = inQuote
|
||||
} else if ("\"" == nextTok) {
|
||||
state = inDoubleQuote
|
||||
} else if (" " == nextTok) {
|
||||
if (current.isNotEmpty()) {
|
||||
v.add(current.toString())
|
||||
current.setLength(0)
|
||||
}
|
||||
} else if ("\\" == nextTok) {
|
||||
escapeState = normal
|
||||
state = inEscape
|
||||
} else {
|
||||
current.append(nextTok)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (current.isNotEmpty()) {
|
||||
v.add(current.toString())
|
||||
}
|
||||
|
||||
if (state != inQuote && state != inDoubleQuote) {
|
||||
return v
|
||||
} else {
|
||||
throw IllegalArgumentException("unbalanced quotes in $toProcess")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -45,6 +45,10 @@
|
|||
/>
|
||||
<postStartupActivity
|
||||
implementation="com.falsepattern.zigbrains.lsp.ZLSStartup"/>
|
||||
|
||||
<editorNotificationProvider
|
||||
implementation="com.falsepattern.zigbrains.lsp.notification.ZigEditorNotificationProvider"
|
||||
/>
|
||||
</extensions>
|
||||
|
||||
<extensions defaultExtensionNs="com.falsepattern.zigbrains">
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
settings.group.title=ZLS Settings
|
||||
settings.zls-path.label=Executable path
|
||||
settings.zls-path.tooltip=Path to the ZLS Binary
|
||||
settings.zls-path.tooltip=Path to the ZLS Binary
|
||||
settings.zls-path.browse.title=Path to the ZLS Binary
|
||||
settings.zls-version.label=Detected ZLS version
|
||||
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.browse.title=Path to the Custom ZLS Config File (Optional)
|
||||
|
@ -59,6 +60,8 @@ notification.message.zls-config-not-exists.content=ZLS config file does not exis
|
|||
notification.message.zls-config-not-file.content=ZLS config file is not a regular file: {0}
|
||||
notification.message.zls-config-path-invalid.content=ZLS config path could not be parted: {0}
|
||||
notification.message.zls-config-autogen-failed.content=Failed to autogenerate ZLS config from toolchain
|
||||
notification.banner.zls-not-running=Zig Language Server is not running. Check the [Language Servers] tool menu!
|
||||
notification.banner.zls-bad-config=Zig Language Server is misconfigured. Check [Settings | Languages \\& Frameworks | Zig]!
|
||||
progress.title.create-connection-provider=Creating ZLS connection provider
|
||||
progress.title.validate=Validating ZLS
|
||||
# suppress inspection "UnusedProperty"
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
<depends config-file="zigbrains-core.xml">com.intellij.modules.platform</depends>
|
||||
<depends config-file="zigbrains-lsp.xml">com.redhat.devtools.lsp4ij</depends>
|
||||
<depends config-file="zigbrains-debugger.xml" optional="true">com.intellij.nativeDebug</depends>
|
||||
<depends config-file="zigbrains-debugger.xml" optional="true">com.intellij.modules.cidr.debugger</depends>
|
||||
<depends config-file="zigbrains-cidr.xml" optional="true">com.intellij.cidr.base</depends>
|
||||
<depends config-file="zigbrains-clion.xml" optional="true">com.intellij.clion</depends>
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue