From 9688a330bfba1d1e93dc06c507e7a64efa8393b2 Mon Sep 17 00:00:00 2001 From: LSTM-Kirigaya <1193466151@qq.com> Date: Fri, 13 Dec 2024 22:00:40 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=20endmodule=20=E7=9A=84=20in?= =?UTF-8?q?lay=20hints?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 1 + src/completion/directives.rs | 8 +++-- src/core/sv_parser.rs | 51 ++++++++++++++++++--------- src/inlay_hint/sv.rs | 67 +++++++++++++++++++++++++++++------- 4 files changed, 95 insertions(+), 32 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4542a3c..e78357a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1459,6 +1459,7 @@ dependencies = [ name = "sv-parser-pp" version = "0.13.3" dependencies = [ + "log", "nom", "nom-greedyerror", "sv-parser-error", diff --git a/src/completion/directives.rs b/src/completion/directives.rs index 86b78eb..33aca0a 100644 --- a/src/completion/directives.rs +++ b/src/completion/directives.rs @@ -227,7 +227,9 @@ endmodule // resetall 也常用于开始 // 1834-2005中指出,建议的用法是将 `resetall 放在每个源文本文件的开头,后面紧跟着文件中所需的指令。 // 通常可用于源文本开头,避免该文件受到其他文件代码的影响。或放置在末尾,避免编译器设置的进一步传递 -r#"`resetall` 用于重置所有编译器设置为默认值。它通常用于在模块或文件的末尾清除所有编译器设置,以确保后续代码不受之前设置的影响。 +r#"`resetall` 用于重置所有编译器设置为默认值。通常可用于源文本开头,避免该文件受到其他文件代码的影响。或放置在末尾,避免编译器设置的进一步传递。 + +1834-2005中指出,建议的用法是将 `resetall 放在每个源文本文件的开头,后面紧跟着文件中所需的指令。 ```verilog `timescale 1ns/1ps // 设置时间单位和精度 @@ -273,7 +275,7 @@ endmodule // 也即是,仿真时间单位必须大于等于时间精度 // `timescale 1ns/1ns不会报错,但是`timescale 1ps/1ns会报错 // [Synth 8-522] parsing error: error in timescale or timeprecision statement. must be at least as precise as -r#"设置时间单位和精度,设置时间单位和精度,格式为 `timescale 仿真时间单位/时间精度,仿真时间单位 必须大于 时间精度,例如 +r#"设置时间单位和精度,设置时间单位和精度,格式为 `timescale 仿真时间单位/时间精度,仿真时间单位 必须大于等于 时间精度,例如 ```verilog `timescale 1ns/1ps @@ -343,7 +345,7 @@ r#"`nounconnected_drive` 和 `unconnected_drive` 是配合使用的编译指令 "begin_keywords", "`begin_keywords\n$1\n`end_keywords ", // begin_keywords并不是指定Verilog的编译版本,而是指定使用的Verilog关键字版本 -r#"`begin_keywords` 和 `end_keywords` 是用于指定 Verilog 语言版本的编译指令。它们允许设计者在同一个文件中使用不同版本的 Verilog 语法,从而实现兼容性或逐步迁移到新版本。 +r#"`begin_keywords` 和 `end_keywords` 是用于指定 Verilog 关键字版本的编译指令。它们允许设计者在同一个文件中使用不同版本的 Verilog 关键字语法,从而实现兼容性或逐步迁移到新版本。 `begin_keywords` 支持以下 Verilog 版本: diff --git a/src/core/sv_parser.rs b/src/core/sv_parser.rs index c58d9fb..7f3d78f 100644 --- a/src/core/sv_parser.rs +++ b/src/core/sv_parser.rs @@ -76,8 +76,8 @@ pub fn sv_parser(path: &str) -> Option { // println!("result: {result:?}"); if let Some(syntax_tree) = result { - if let Ok(hdlparam) = make_fast_from_syntaxtree(&syntax_tree, &path) { - return Some(hdlparam); + if let Ok(fast) = make_fast_from_syntaxtree(&syntax_tree, &path) { + return Some(fast); } } @@ -91,7 +91,7 @@ pub fn make_fast_from_syntaxtree( // 对不同操作系统文件路径的支持 let path = to_escape_path(path); - let mut hdlparam = FastHdlparam { + let mut fast: FastHdlparam = FastHdlparam { fast_macro: Macro { defines: Vec::new(), errors: Vec::new(), @@ -103,10 +103,11 @@ pub fn make_fast_from_syntaxtree( }; let mut ansi_port_last_dir = ""; - // println!("{:?}", syntax_tree); // &SyntaxTree is iterable let doc = Rope::from_str(syntax_tree.text.text()); + // 上一个 module 可以在 fast 的 content 的最后一个中找到 + for node in syntax_tree { match node { RefNode::TextMacroDefinition(x) => { @@ -148,20 +149,20 @@ pub fn make_fast_from_syntaxtree( }; let define_range = Range { start: start_pos, end: end_pos }; - hdlparam.add_define(name, replacement, define_range, params_vec); + fast.add_define(name, replacement, define_range, params_vec); } } RefNode::ModuleDeclaration(x) => { let start_keyword = unwrap_node!(x, Keyword).unwrap(); let start_keyword = get_identifier(start_keyword).unwrap(); let start_pos = get_position(&doc, start_keyword, 0); - let module_range = get_pp_range(&doc, RefNode::ModuleDeclaration(x)); - let module_range = Range { start: start_pos, end: module_range.end }; + let module_range = Range { start: start_pos.clone(), end: start_pos }; let id = unwrap_node!(x, ModuleIdentifier).unwrap(); let id = get_identifier(id).unwrap(); let name = syntax_tree.get_str(&id).unwrap(); - hdlparam.new_module(name, module_range); + + fast.new_module(name, module_range); } RefNode::ParameterDeclaration(param_dec) => { let mut event_iter = param_dec.into_iter().event(); @@ -192,7 +193,7 @@ pub fn make_fast_from_syntaxtree( }; let end_pos = get_position(&doc, loc.clone(), loc.len); let param_range = Range { start: start_pos, end: end_pos }; - hdlparam.add_parameter(name, net_type, init, param_range); + fast.add_parameter(name, net_type, init, param_range); } } _ => () @@ -235,7 +236,7 @@ pub fn make_fast_from_syntaxtree( let name = syntax_tree.get_str(&id).unwrap(); let port_range = Range { start: start_pos.clone(), end: get_position(&doc, id, id.len) }; - hdlparam.add_port(name, dir_type, net_type, width.as_str(), port_range); + fast.add_port(name, dir_type, net_type, width.as_str(), port_range); } } } @@ -284,7 +285,7 @@ pub fn make_fast_from_syntaxtree( _ => "1".to_string() }; - hdlparam.add_port(name, ansi_port_last_dir, net_type, width.as_str(), port_range); + fast.add_port(name, ansi_port_last_dir, net_type, width.as_str(), port_range); } } RefNode::ModuleInstantiation(x) => { @@ -316,7 +317,7 @@ pub fn make_fast_from_syntaxtree( let inst_port_assignments = get_instance_ports(&syntax_tree, &doc, hier_node.clone()); let port_range = get_pp_range(&doc, hier_node).to_option(); - hdlparam.add_instance( + fast.add_instance( name, inst_type, inst_range, param_range, inst_param_assignments, port_range, inst_port_assignments @@ -342,7 +343,7 @@ pub fn make_fast_from_syntaxtree( let inst_port_assignments = get_instance_ports(&syntax_tree, &doc, gate_node.clone()); let port_range = get_pp_range(&doc, gate_node).to_option(); - hdlparam.add_instance( + fast.add_instance( name, inst_type, inst_range, param_range, Vec::::new(), port_range, inst_port_assignments @@ -351,13 +352,31 @@ pub fn make_fast_from_syntaxtree( _ => () } } + RefNode::Keyword(x) => { + let id = x.nodes.0; + let name = syntax_tree.get_str(&id).unwrap(); + let pos = get_position(&doc, id, name.len()); + + // 根据关键词进行特殊处理 + // 比如找到 endmodule 的位置来确定目前的这个 module 的 end + match name { + "endmodule" => { + // 尝试获取 content 最后一个元素的可变引用,该引用如果存在,说明已经创建了一个 module + if let Some(last_module) = fast.content.last_mut() { + last_module.range.end.line = pos.line; + last_module.range.end.character = pos.line; + } + }, + _ => {} + } + } _ => () } } // update_module_range(&path, &mut hdlparam); - Ok(hdlparam) + Ok(fast) } // 获取 port 或者 param 的 range @@ -705,7 +724,7 @@ fn get_includes(path: &PathBuf) -> Vec { } #[allow(unused)] -fn update_module_range(path: &PathBuf, hdlparam: &mut FastHdlparam) { +fn update_module_range(path: &PathBuf, fast: &mut FastHdlparam) { let file = File::open(path).unwrap(); let reader = BufReader::new(file); @@ -725,7 +744,7 @@ fn update_module_range(path: &PathBuf, hdlparam: &mut FastHdlparam) { module_stack.push(module_name.clone()); } else if re_endmodule.is_match(&line) { if let Some(module_name) = module_stack.pop() { - hdlparam.update_module_range(&module_name, (line_number + 1) as u32, current_offset as u32); + fast.update_module_range(&module_name, (line_number + 1) as u32, current_offset as u32); // println!("Module {} ends.", module_name); } } diff --git a/src/inlay_hint/sv.rs b/src/inlay_hint/sv.rs index 889ea28..bbfbab7 100644 --- a/src/inlay_hint/sv.rs +++ b/src/inlay_hint/sv.rs @@ -30,19 +30,29 @@ pub fn inlay_hint(server: &LspServer, params: &InlayHintParams) -> Option::new(); // 制作例化模块的 hint - for module in &fast.content { + for module in &fast.content { for instance in &module.instances { // 根据在可见视图外面的 range 就不管了 - if is_visible_range(&instance.instparams, &visible_range) { - hints.extend(make_instparam_hints(server, params, instance, rope)); + if let Some(range) = &instance.instparams { + if is_visible_range(&range, &visible_range) { + hints.extend(make_instparam_hints(server, params, instance, rope)); + } } - - if is_visible_range(&instance.instports, &visible_range) { - hints.extend(make_instport_hints(server, params, instance, rope)); + if let Some(range) = &instance.instports { + if is_visible_range(range, &visible_range) { + hints.extend(make_instport_hints(server, params, instance, rope)); + } } } + + // 在 endmodule 后面添加 module xxx + if is_visible_range(&module.range, &visible_range) { + hints.extend(make_endmodule_hints(module)); + } + } + return Some(hints); } @@ -50,16 +60,46 @@ pub fn inlay_hint(server: &LspServer, params: &InlayHintParams) -> Option, + target_range: &core::hdlparam::Range, visible_range: &core::hdlparam::Range ) -> bool { - if let Some(target_range) = target_range { - if target_range.before(visible_range) || target_range.after(visible_range) { - return false; - } - return true; + if target_range.before(visible_range) || target_range.after(visible_range) { + false + } else { + true } - false +} + +fn make_endmodule_hints( + module: &core::hdlparam::Module +) -> Vec { + let mut hints = Vec::::new(); + let end_pos = &module.range.end; + let start_pos = &module.range.start; + if end_pos.character == start_pos.character && end_pos.line == start_pos.line { + // 说明解析器没有找到 endmodule + return hints; + } + + let module_desc = MarkupContent { + kind: MarkupKind::Markdown, + value: format!("module {}", module.name) + }; + + let hint = InlayHint { + position: end_pos.to_lsp_position(), + label: InlayHintLabel::String(format!("module {}", module.name)), + padding_left: Some(true), + padding_right: Some(true), + kind: Some(InlayHintKind::PARAMETER), + text_edits: None, + tooltip: Some(InlayHintTooltip::MarkupContent(module_desc)), + data: None + }; + + hints.push(hint); + + hints } fn make_instparam_hints( @@ -112,6 +152,7 @@ fn find_instport_inlay_hints_position( fn make_instport_hints( server: &LspServer, + #[allow(unused)] params: &InlayHintParams, instance: &core::hdlparam::Instance, rope: &Rope