fix: Only split args when passing them to a cmdline, store them as fixed strings

This commit is contained in:
FalsePattern 2025-03-13 14:10:05 +01:00
parent 05ff125c1d
commit 91ee38e922
Signed by: falsepattern
GPG key ID: E930CDEC50C50E23
13 changed files with 38 additions and 105 deletions

View file

@ -27,6 +27,9 @@ Changelog structure reference:
- Debugging - Debugging
- Breakpoints could not be placed inside zig code in Android Studio - 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] ## [21.1.0]
### Added ### Added

View file

@ -39,7 +39,7 @@ class ZigExecConfigBinary(project: Project, factory: ConfigurationFactory) : Zig
get() = ZigDebugBundle.message("configuration.binary.suggested-name") get() = ZigDebugBundle.message("configuration.binary.suggested-name")
override suspend fun buildCommandLineArgs(debug: Boolean): List<String> { override suspend fun buildCommandLineArgs(debug: Boolean): List<String> {
return args.args return args.argsSplit()
} }
override fun getConfigurables(): List<ZigConfigurable<*>> { override fun getConfigurables(): List<ZigConfigurable<*>> {

View file

@ -36,6 +36,6 @@ class ZigDebugParametersBinary @Throws(ExecutionException::class) constructor(dr
ZigDebugParametersBase<ZigProfileStateBinary>(driverConfiguration, toolchain, profileState) { ZigDebugParametersBase<ZigProfileStateBinary>(driverConfiguration, toolchain, profileState) {
private val executableFile = profileState.configuration.exePath.path?.toFile() ?: throw ExecutionException(ZigDebugBundle.message("exception.missing-exe-path")) private val executableFile = profileState.configuration.exePath.path?.toFile() ?: throw ExecutionException(ZigDebugBundle.message("exception.missing-exe-path"))
override fun getInstaller(): Installer { override fun getInstaller(): Installer {
return ZigDebugEmitBinaryInstaller(profileState, toolchain, executableFile, profileState.configuration.args.args) return ZigDebugEmitBinaryInstaller(profileState, toolchain, executableFile, profileState.configuration.args.argsSplit())
} }
} }

View file

@ -53,7 +53,7 @@ class ZigDebugParametersBuild(
private lateinit var executableFile: File private lateinit var executableFile: File
override fun getInstaller(): Installer { 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) @Throws(ExecutionException::class)

View file

@ -32,6 +32,6 @@ import com.jetbrains.cidr.execution.debugger.backend.DebuggerDriverConfiguration
class ZigDebugParametersRun(driverConfiguration: DebuggerDriverConfiguration, toolchain: AbstractZigToolchain, profileState: ZigProfileStateRun) : class ZigDebugParametersRun(driverConfiguration: DebuggerDriverConfiguration, toolchain: AbstractZigToolchain, profileState: ZigProfileStateRun) :
ZigDebugParametersEmitBinaryBase<ZigProfileStateRun>(driverConfiguration, toolchain, profileState) { ZigDebugParametersEmitBinaryBase<ZigProfileStateRun>(driverConfiguration, toolchain, profileState) {
override fun getInstaller(): Installer { override fun getInstaller(): Installer {
return ZigDebugEmitBinaryInstaller(profileState, toolchain, executableFile, profileState.configuration.exeArgs.args) return ZigDebugEmitBinaryInstaller(profileState, toolchain, executableFile, profileState.configuration.exeArgs.argsSplit())
} }
} }

View file

@ -347,14 +347,18 @@ class ArgsConfigurable(
@Transient private val serializedName: String, @Transient private val serializedName: String,
@Transient @Nls private val guiName: String @Transient @Nls private val guiName: String
) : ZigConfigurable<ArgsConfigurable>, Cloneable { ) : ZigConfigurable<ArgsConfigurable>, Cloneable {
var args: List<String> = emptyList() var args: String = ""
override fun readExternal(element: Element) { 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) { override fun writeExternal(element: Element) {
element.writeStrings(serializedName, args) element.writeString(serializedName, args)
} }
override fun createEditor(): ZigConfigModule<ArgsConfigurable> { override fun createEditor(): ZigConfigModule<ArgsConfigurable> {
@ -376,12 +380,12 @@ class ArgsConfigurable(
} }
override fun apply(configurable: ArgsConfigurable): Boolean { override fun apply(configurable: ArgsConfigurable): Boolean {
configurable.args = translateCommandline(argsField.text) configurable.args = argsField.text ?: ""
return true return true
} }
override fun reset(configurable: ArgsConfigurable) { override fun reset(configurable: ArgsConfigurable) {
argsField.text = configurable.args.joinToString(separator = " ") argsField.text = configurable.args
} }
override fun construct(p: Panel): Unit = with(p) { override fun construct(p: Panel): Unit = with(p) {

View file

@ -48,9 +48,10 @@ class ZigExecConfigBuild(project: Project, factory: ConfigurationFactory): ZigEx
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("build") result.add("build")
val argsSplit = buildSteps.argsSplit()
val steps = if (debug) { val steps = if (debug) {
val truncatedSteps = ArrayList<String>() val truncatedSteps = ArrayList<String>()
for (step in buildSteps.args) { for (step in argsSplit) {
if (step == "run") if (step == "run")
continue continue
@ -60,10 +61,10 @@ class ZigExecConfigBuild(project: Project, factory: ConfigurationFactory): ZigEx
truncatedSteps.add(step) truncatedSteps.add(step)
} }
truncatedSteps truncatedSteps
} else buildSteps.args } else argsSplit
result.addAll(steps) result.addAll(steps)
result.addAll(coloredCliFlags(colored.value, debug)) result.addAll(coloredCliFlags(colored.value, debug))
result.addAll(extraArgs.args) result.addAll(extraArgs.argsSplit())
return result return result
} }

View file

@ -52,10 +52,10 @@ class ZigExecConfigRun(project: Project, factory: ConfigurationFactory): ZigExec
if (!debug || optimization.forced) { if (!debug || optimization.forced) {
result.addAll(listOf("-O", optimization.level.name)) result.addAll(listOf("-O", optimization.level.name))
} }
result.addAll(compilerArgs.args) result.addAll(compilerArgs.argsSplit())
if (!debug) { if (!debug) {
result.add("--") result.add("--")
result.addAll(exeArgs.args) result.addAll(exeArgs.argsSplit())
} }
return result return result
} }

View file

@ -51,7 +51,7 @@ class ZigExecConfigTest(project: Project, factory: ConfigurationFactory): ZigExe
if (!debug || optimization.forced) { if (!debug || optimization.forced) {
result.addAll(listOf("-O", optimization.level.name)) result.addAll(listOf("-O", optimization.level.name))
} }
result.addAll(compilerArgs.args) result.addAll(compilerArgs.argsSplit())
if (debug) { if (debug) {
result.add("--test-no-exec") result.add("--test-no-exec")
} }

View file

@ -87,7 +87,7 @@ class BuildToolWindowContext(private val project: Project): Disposable {
val factory = firstConfigFactory<ZigConfigTypeBuild>() val factory = firstConfigFactory<ZigConfigTypeBuild>()
val newConfig = manager.createConfiguration("zig build $stepName", factory) val newConfig = manager.createConfiguration("zig build $stepName", factory)
val config = newConfig.configuration as ZigExecConfigBuild val config = newConfig.configuration as ZigExecConfigBuild
config.buildSteps.args = listOf(stepName) config.buildSteps.args = stepName
manager.addConfiguration(newConfig) manager.addConfiguration(newConfig)
return@run newConfig return@run newConfig
} }
@ -213,7 +213,7 @@ private fun getViewport(project: Project): JBScrollPane? {
private fun getExistingRunConfig(manager: RunManager, stepName: String): RunnerAndConfigurationSettings? { private fun getExistingRunConfig(manager: RunManager, stepName: String): RunnerAndConfigurationSettings? {
for (config in manager.getConfigurationSettingsList(ZigConfigTypeBuild::class.java)) { for (config in manager.getConfigurationSettingsList(ZigConfigTypeBuild::class.java)) {
val build = config.configuration as? ZigExecConfigBuild ?: continue val build = config.configuration as? ZigExecConfigBuild ?: continue
val steps = build.buildSteps.args val steps = build.buildSteps.argsSplit()
if (steps.size != 1) if (steps.size != 1)
continue continue
if (steps[0] != stepName) if (steps[0] != stepName)

View file

@ -38,7 +38,7 @@ data class ZLSSettings(
val enable_argument_placeholders: Boolean = true, val enable_argument_placeholders: Boolean = true,
val completion_label_details: Boolean = true, val completion_label_details: Boolean = true,
val enable_build_on_save: Boolean = false, 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 semantic_tokens: SemanticTokens = SemanticTokens.full,
val inlay_hints_show_variable_type_hints: Boolean = true, val inlay_hints_show_variable_type_hints: Boolean = true,
val inlay_hints_show_struct_literal_field_type: Boolean = true, val inlay_hints_show_struct_literal_field_type: Boolean = true,

View file

@ -24,6 +24,7 @@ package com.falsepattern.zigbrains.lsp.settings
import com.falsepattern.zigbrains.lsp.config.ZLSConfig import com.falsepattern.zigbrains.lsp.config.ZLSConfig
import com.falsepattern.zigbrains.lsp.config.ZLSConfigProvider import com.falsepattern.zigbrains.lsp.config.ZLSConfigProvider
import com.falsepattern.zigbrains.shared.cli.translateCommandline
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
class ZLSSettingsConfigProvider: ZLSConfigProvider { class ZLSSettingsConfigProvider: ZLSConfigProvider {
@ -34,7 +35,14 @@ class ZLSSettingsConfigProvider: ZLSConfigProvider {
enable_argument_placeholders = state.enable_argument_placeholders, enable_argument_placeholders = state.enable_argument_placeholders,
completion_label_details = state.completion_label_details, completion_label_details = state.completion_label_details,
enable_build_on_save = state.enable_build_on_save, 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, semantic_tokens = state.semantic_tokens,
inlay_hints_show_variable_type_hints = state.inlay_hints_show_variable_type_hints, 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, inlay_hints_show_struct_literal_field_type = state.inlay_hints_show_struct_literal_field_type,

View file

@ -207,14 +207,7 @@ class ZLSSettingsPanel(private val project: Project) : ZigProjectConfigurationPr
enable_argument_placeholders.isSelected, enable_argument_placeholders.isSelected,
completion_label_details.isSelected, completion_label_details.isSelected,
enable_build_on_save.isSelected, enable_build_on_save.isSelected,
run { build_on_save_args.text,
val args = build_on_save_args.text ?: ""
return@run if (args.isEmpty()) {
emptyList()
} else {
translateCommandline(args).toList()
}
},
semantic_tokens.item ?: SemanticTokens.full, semantic_tokens.item ?: SemanticTokens.full,
inlay_hints_show_variable_type_hints.isSelected, inlay_hints_show_variable_type_hints.isSelected,
inlay_hints_show_struct_literal_field_type.isSelected, inlay_hints_show_struct_literal_field_type.isSelected,
@ -240,7 +233,7 @@ class ZLSSettingsPanel(private val project: Project) : ZigProjectConfigurationPr
enable_argument_placeholders.isSelected = value.enable_argument_placeholders enable_argument_placeholders.isSelected = value.enable_argument_placeholders
completion_label_details.isSelected = value.completion_label_details completion_label_details.isSelected = value.completion_label_details
enable_build_on_save.isSelected = value.enable_build_on_save 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 semantic_tokens.item = value.semantic_tokens
inlay_hints_show_variable_type_hints.isSelected = value.inlay_hints_show_variable_type_hints 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 inlay_hints_show_struct_literal_field_type.isSelected = value.inlay_hints_show_struct_literal_field_type
@ -295,79 +288,3 @@ private fun Panel.fancyRow(
contextHelp(ZLSBundle.message(tooltip)) contextHelp(ZLSBundle.message(tooltip))
cb() 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")
}
}