From 1f39b07a095c51ebc29b363bfa1c3483b0a3a7ed Mon Sep 17 00:00:00 2001 From: hongjr03 Date: Sat, 23 May 2026 23:56:46 +0800 Subject: [PATCH 1/2] feat(qihe): support manual manifest arguments --- src/config/user_config.rs | 54 ++++++++++++++----- src/global_state/qihe.rs | 108 +++++++++++++++++++++++++++++++------- 2 files changed, 131 insertions(+), 31 deletions(-) diff --git a/src/config/user_config.rs b/src/config/user_config.rs index fcf9a409..419adfe2 100644 --- a/src/config/user_config.rs +++ b/src/config/user_config.rs @@ -124,6 +124,7 @@ fn default_true() -> bool { #[derive(Debug, Clone, PartialEq, Eq)] pub(crate) struct QiheConfig { pub(crate) command: String, + pub(crate) auto_configure_args_from_manifest: bool, pub(crate) compile_args: Vec, pub(crate) run_args: Vec, } @@ -194,6 +195,7 @@ config_data! { signature_help_params_only: bool = false, qihe_command: String = DEFAULT_QIHE_COMMAND.to_string(), + qihe_autoConfigureArgsFromManifest: bool = true, qihe_compileArgs: Vec = vec![], qihe_runArgs: Vec = DEFAULT_QIHE_RUN_ARGS.iter().map(|arg| (*arg).to_string()).collect(), @@ -220,6 +222,25 @@ impl UserConfig { } .with_fresh_revision() } + + pub(crate) fn qihe(&self) -> QiheConfig { + let command = Some(self.qihe_command.trim()) + .filter(|cmd| !cmd.is_empty()) + .unwrap_or(DEFAULT_QIHE_COMMAND) + .to_string(); + + let run_args = + Some(&self.qihe_runArgs).filter(|args| !args.is_empty()).cloned().unwrap_or_else( + || DEFAULT_QIHE_RUN_ARGS.iter().map(|arg| (*arg).to_string()).collect(), + ); + + QiheConfig { + command, + auto_configure_args_from_manifest: self.qihe_autoConfigureArgsFromManifest, + compile_args: self.qihe_compileArgs.clone(), + run_args, + } + } } impl DiagnosticRuleUserConfig { @@ -322,19 +343,7 @@ impl Config { } pub(crate) fn qihe(&self) -> QiheConfig { - let command = Some(self.user_config.qihe_command.trim()) - .filter(|cmd| !cmd.is_empty()) - .unwrap_or(DEFAULT_QIHE_COMMAND) - .to_string(); - - let run_args = Some(&self.user_config.qihe_runArgs) - .filter(|args| !args.is_empty()) - .cloned() - .unwrap_or_else(|| { - DEFAULT_QIHE_RUN_ARGS.iter().map(|arg| (*arg).to_string()).collect() - }); - - QiheConfig { command, compile_args: self.user_config.qihe_compileArgs.clone(), run_args } + self.user_config.qihe() } } @@ -372,3 +381,22 @@ fn parses_nested_diagnostics_config() { assert_eq!(config.slang.warnings, ["default", "no-unused"]); assert_eq!(config.slang.rules.len(), 2); } + +#[test] +fn parses_qihe_manifest_arg_configuration() { + let json = serde_json::json!({ + "qihe": { + "autoConfigureArgsFromManifest": false, + "compileArgs": ["--mode", "sv2017", "--", "-I", "vendor/include"], + "runArgs": ["-g", "std", "--foo"], + } + }); + let mut errors = vec![]; + let user_cfg = UserConfig::from_json(json, &mut errors); + assert!(errors.is_empty(), "{errors:?}"); + + let qihe = user_cfg.qihe(); + assert!(!qihe.auto_configure_args_from_manifest); + assert_eq!(qihe.compile_args, ["--mode", "sv2017", "--", "-I", "vendor/include"]); + assert_eq!(qihe.run_args, ["-g", "std", "--foo"]); +} diff --git a/src/global_state/qihe.rs b/src/global_state/qihe.rs index 134eff62..bfb22e5a 100644 --- a/src/global_state/qihe.rs +++ b/src/global_state/qihe.rs @@ -266,7 +266,7 @@ fn run_qihe_request( run_qihe_commands(&qihe_config, &cwd, &compile_input, &ir_path, &storage_root, i18n, log_sink)?; let diagnostics = load_latest_diagnostics(&storage_root, i18n)?; - let resolution_base = if compile_input.project_mode { + let resolution_base = if compile_input.uses_manifest() { cwd.as_path() } else { active_path @@ -289,8 +289,20 @@ fn qihe_working_directory(params_cwd: Option, root_path: &AbsPath) -> P #[derive(Debug, Clone, PartialEq, Eq)] struct QiheCompileInput { files: Vec, - slang_args: Vec, - project_mode: bool, + manifest_slang_args: Vec, + source: QiheCompileInputSource, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum QiheCompileInputSource { + SingleFile, + Manifest, +} + +impl QiheCompileInput { + fn uses_manifest(&self) -> bool { + matches!(self.source, QiheCompileInputSource::Manifest) + } } fn qihe_compile_input( @@ -319,8 +331,8 @@ fn qihe_compile_input( fn single_file_qihe_compile_input(active_path: &AbsPath) -> QiheCompileInput { QiheCompileInput { files: vec![active_path.to_path_buf().into()], - slang_args: Vec::new(), - project_mode: false, + manifest_slang_args: Vec::new(), + source: QiheCompileInputSource::SingleFile, } } @@ -349,7 +361,11 @@ fn qihe_compile_input_from_plan( slang_args.push(format!("-D{define}")); } - QiheCompileInput { files, slang_args, project_mode: true } + QiheCompileInput { + files, + manifest_slang_args: slang_args, + source: QiheCompileInputSource::Manifest, + } } fn qihe_run_paths(active_path: &AbsPath) -> Result<(PathBuf, PathBuf)> { @@ -397,14 +413,19 @@ fn prepare_qihe_compile_command( ) { let (qihe_args, user_slang_args) = split_compile_args(&qihe_config.compile_args); command.args(&qihe_args); - if compile_input.project_mode && !has_compile_mode(&qihe_args) { + let auto_configure_manifest_args = + qihe_config.auto_configure_args_from_manifest && compile_input.uses_manifest(); + if auto_configure_manifest_args && !has_compile_mode(&qihe_args) { command.args(["--mode", "sv"]); } command.args(&compile_input.files).arg("-o").arg(ir_path); - let has_slang_args = !user_slang_args.is_empty() || !compile_input.slang_args.is_empty(); + let manifest_slang_args = auto_configure_manifest_args + .then_some(compile_input.manifest_slang_args.as_slice()) + .unwrap_or(&[]); + let has_slang_args = !user_slang_args.is_empty() || !manifest_slang_args.is_empty(); if has_slang_args { - command.arg("--").args(&user_slang_args).args(&compile_input.slang_args); + command.arg("--").args(&user_slang_args).args(manifest_slang_args); } } @@ -782,9 +803,10 @@ mod tests { use utils::paths::AbsPathBuf; use super::{ - QiheCompileInput, QiheLogSink, command_line, has_compile_mode, join_command_output, - parse_source_loc, prepare_qihe_compile_command, qihe_compile_input_from_plan, - qihe_working_directory, split_compile_args, stream_command_output, strip_ansi, + QiheCompileInput, QiheCompileInputSource, QiheLogSink, command_line, has_compile_mode, + join_command_output, parse_source_loc, prepare_qihe_compile_command, + qihe_compile_input_from_plan, qihe_working_directory, split_compile_args, + stream_command_output, strip_ansi, }; use crate::{ config::user_config::QiheConfig, @@ -889,19 +911,20 @@ mod tests { fn project_compile_command_synthesizes_sv_mode_and_slang_args() { let config = QiheConfig { command: "qihe".to_owned(), + auto_configure_args_from_manifest: true, compile_args: vec!["--flag".to_owned(), "--".to_owned(), "--lint".to_owned()], run_args: Vec::new(), }; let input = QiheCompileInput { files: vec![PathBuf::from("/repo/rtl/a.sv"), PathBuf::from("/repo/rtl/b.sv")], - slang_args: vec![ + manifest_slang_args: vec![ "--top".to_owned(), "top".to_owned(), "-I".to_owned(), "/repo/include".to_owned(), "-DDEBUG".to_owned(), ], - project_mode: true, + source: QiheCompileInputSource::Manifest, }; let mut command = Command::new("qihe"); @@ -934,17 +957,66 @@ mod tests { ); } + #[test] + fn project_compile_command_can_disable_manifest_args() { + let config = QiheConfig { + command: "qihe".to_owned(), + auto_configure_args_from_manifest: false, + compile_args: vec![ + "--mode".to_owned(), + "custom".to_owned(), + "--".to_owned(), + "--lint".to_owned(), + ], + run_args: Vec::new(), + }; + let input = QiheCompileInput { + files: vec![PathBuf::from("/repo/rtl/a.sv"), PathBuf::from("/repo/rtl/b.sv")], + manifest_slang_args: vec![ + "--top".to_owned(), + "top".to_owned(), + "-I".to_owned(), + "/repo/include".to_owned(), + "-DDEBUG".to_owned(), + ], + source: QiheCompileInputSource::Manifest, + }; + let mut command = Command::new("qihe"); + + prepare_qihe_compile_command( + &mut command, + &config, + &input, + PathBuf::from("/tmp/in.qh").as_path(), + ); + + assert_eq!( + command_args(&command), + [ + "--mode", + "custom", + "/repo/rtl/a.sv", + "/repo/rtl/b.sv", + "-o", + "/tmp/in.qh", + "--", + "--lint", + ] + ); + } + #[test] fn single_file_compile_command_does_not_force_sv_mode() { let config = QiheConfig { command: "qihe".to_owned(), + auto_configure_args_from_manifest: true, compile_args: Vec::new(), run_args: Vec::new(), }; let input = QiheCompileInput { files: vec![PathBuf::from("/repo/top.sv")], - slang_args: Vec::new(), - project_mode: false, + manifest_slang_args: Vec::new(), + source: QiheCompileInputSource::SingleFile, }; let mut command = Command::new("qihe"); @@ -973,8 +1045,8 @@ mod tests { input, QiheCompileInput { files: vec![active_path.into()], - slang_args: Vec::new(), - project_mode: false, + manifest_slang_args: Vec::new(), + source: QiheCompileInputSource::SingleFile, } ); } From 49f6ea723f8d52be1f56c13253ac25216ca4c3ed Mon Sep 17 00:00:00 2001 From: hongjr03 Date: Sat, 23 May 2026 23:57:08 +0800 Subject: [PATCH 2/2] feat(vscode): expose Qihe manifest args setting --- editors/vscode/package.json | 5 +++++ editors/vscode/package.nls.json | 1 + editors/vscode/package.nls.zh-cn.json | 1 + editors/vscode/src/profiling.ts | 2 ++ editors/vscode/test/configuration.test.ts | 1 + 5 files changed, 10 insertions(+) diff --git a/editors/vscode/package.json b/editors/vscode/package.json index 72928fa3..8a10d9ba 100644 --- a/editors/vscode/package.json +++ b/editors/vscode/package.json @@ -127,6 +127,11 @@ "default": "qihe", "description": "%configuration.qihe.command.description%" }, + "vizsla.qihe.autoConfigureArgsFromManifest": { + "type": "boolean", + "default": true, + "description": "%configuration.qihe.autoConfigureArgsFromManifest.description%" + }, "vizsla.qihe.compileArgs": { "type": "array", "items": { diff --git a/editors/vscode/package.nls.json b/editors/vscode/package.nls.json index 60050341..766c24fe 100644 --- a/editors/vscode/package.nls.json +++ b/editors/vscode/package.nls.json @@ -6,6 +6,7 @@ "configuration.server.cwd.description": "Working directory for the Vizsla language server process. Defaults to the first workspace folder.", "configuration.server.additionalArgs.description": "Additional arguments appended when launching the Vizsla language server.", "configuration.qihe.command.description": "Command used to invoke Qihe. It must be available in the environment PATH seen by VS Code.", + "configuration.qihe.autoConfigureArgsFromManifest.description": "Automatically add Qihe compile mode and forwarded slang options from the Vizsla project manifest. Disable this to provide those options manually through Qihe compile and run arguments.", "configuration.qihe.compileArgs.description": "Arguments inserted after `qihe compile`. Use this for compile mode selection or forwarded `slang` options such as `--mode sv -- -I include`.", "configuration.qihe.runArgs.description": "Arguments inserted after `qihe run` when the Qihe analysis button is used.", "configuration.files.excludeDirs.markdownDescription": "Workspace-relative directories ignored by Vizsla. Globs are not supported; add matching patterns to VS Code `files.watcherExclude` if needed.", diff --git a/editors/vscode/package.nls.zh-cn.json b/editors/vscode/package.nls.zh-cn.json index ade1f6cb..a27b40cf 100644 --- a/editors/vscode/package.nls.zh-cn.json +++ b/editors/vscode/package.nls.zh-cn.json @@ -6,6 +6,7 @@ "configuration.server.cwd.description": "Vizsla 语言服务器进程的工作目录。默认使用第一个工作区文件夹。", "configuration.server.additionalArgs.description": "启动 Vizsla 语言服务器时追加的额外参数。", "configuration.qihe.command.description": "用于调用 Qihe 的命令。它必须在 VS Code 可见的环境 PATH 中可用。", + "configuration.qihe.autoConfigureArgsFromManifest.description": "根据 Vizsla 项目配置文件自动添加 Qihe 编译模式和转发给 slang 的选项。关闭后可通过 Qihe 编译和运行参数手动提供这些选项。", "configuration.qihe.compileArgs.description": "插入到 `qihe compile` 之后的参数。可用于选择编译模式,或转发 `slang` 选项,例如 `--mode sv -- -I include`。", "configuration.qihe.runArgs.description": "使用 Qihe 分析按钮时,插入到 `qihe run` 之后的参数。", "configuration.files.excludeDirs.markdownDescription": "Vizsla 忽略的工作区相对目录。不支持 glob;如需匹配模式,请添加到 VS Code `files.watcherExclude`。", diff --git a/editors/vscode/src/profiling.ts b/editors/vscode/src/profiling.ts index 7dffdb82..020c4165 100644 --- a/editors/vscode/src/profiling.ts +++ b/editors/vscode/src/profiling.ts @@ -383,6 +383,8 @@ function readVizslaInitializationOptions(): Record { }, signature_help_params_only: config.get('signature.help.params.only') ?? false, qihe_command: config.get('qihe.command') ?? 'qihe', + qihe_autoConfigureArgsFromManifest: + config.get('qihe.autoConfigureArgsFromManifest') ?? true, qihe_compileArgs: config.get('qihe.compileArgs') ?? [], qihe_runArgs: config.get('qihe.runArgs') ?? ['-g', 'std'], }; diff --git a/editors/vscode/test/configuration.test.ts b/editors/vscode/test/configuration.test.ts index b4791409..67875635 100644 --- a/editors/vscode/test/configuration.test.ts +++ b/editors/vscode/test/configuration.test.ts @@ -83,6 +83,7 @@ test('contributes settings for the complete Vizsla user configuration surface', 'vizsla.server.cwd', 'vizsla.server.additionalArgs', 'vizsla.qihe.command', + 'vizsla.qihe.autoConfigureArgsFromManifest', 'vizsla.qihe.compileArgs', 'vizsla.qihe.runArgs', 'vizsla.files.excludeDirs',