实现 endmodule 的 inlay hints

This commit is contained in:
锦恢 2024-12-13 22:00:40 +08:00
parent 1142aa324f
commit 9688a330bf
4 changed files with 95 additions and 32 deletions

1
Cargo.lock generated
View File

@ -1459,6 +1459,7 @@ dependencies = [
name = "sv-parser-pp" name = "sv-parser-pp"
version = "0.13.3" version = "0.13.3"
dependencies = [ dependencies = [
"log",
"nom", "nom",
"nom-greedyerror", "nom-greedyerror",
"sv-parser-error", "sv-parser-error",

View File

@ -227,7 +227,9 @@ endmodule
// resetall 也常用于开始 // resetall 也常用于开始
// 1834-2005中指出建议的用法是将 `resetall 放在每个源文本文件的开头,后面紧跟着文件中所需的指令。 // 1834-2005中指出建议的用法是将 `resetall 放在每个源文本文件的开头,后面紧跟着文件中所需的指令。
// 通常可用于源文本开头,避免该文件受到其他文件代码的影响。或放置在末尾,避免编译器设置的进一步传递 // 通常可用于源文本开头,避免该文件受到其他文件代码的影响。或放置在末尾,避免编译器设置的进一步传递
r#"`resetall` 用于重置所有编译器设置为默认值。它通常用于在模块或文件的末尾清除所有编译器设置,以确保后续代码不受之前设置的影响。 r#"`resetall` 用于重置所有编译器设置为默认值。通常可用于源文本开头,避免该文件受到其他文件代码的影响。或放置在末尾,避免编译器设置的进一步传递。
1834-2005 `resetall
```verilog ```verilog
`timescale 1ns/1ps // 设置时间单位和精度 `timescale 1ns/1ps // 设置时间单位和精度
@ -273,7 +275,7 @@ endmodule
// 也即是,仿真时间单位必须大于等于时间精度 // 也即是,仿真时间单位必须大于等于时间精度
// `timescale 1ns/1ns不会报错但是`timescale 1ps/1ns会报错 // `timescale 1ns/1ns不会报错但是`timescale 1ps/1ns会报错
// [Synth 8-522] parsing error: error in timescale or timeprecision statement. <timeprecision> must be at least as precise as <timeunit> // [Synth 8-522] parsing error: error in timescale or timeprecision statement. <timeprecision> must be at least as precise as <timeunit>
r#"设置时间单位和精度,设置时间单位和精度,格式为 <code> `timescale </code> 仿真时间单位/时间精度,<code>仿真时间单位</code> 必须大于 <code>时间精度</code>,例如 r#"设置时间单位和精度,设置时间单位和精度,格式为 <code> `timescale </code> 仿真时间单位/时间精度,<code>仿真时间单位</code> 必须大于等于 <code>时间精度</code>,例如
```verilog ```verilog
`timescale 1ns/1ps `timescale 1ns/1ps
@ -343,7 +345,7 @@ r#"`nounconnected_drive` 和 `unconnected_drive` 是配合使用的编译指令
"begin_keywords", "begin_keywords",
"`begin_keywords\n$1\n`end_keywords ", "`begin_keywords\n$1\n`end_keywords ",
// begin_keywords并不是指定Verilog的编译版本而是指定使用的Verilog关键字版本 // begin_keywords并不是指定Verilog的编译版本而是指定使用的Verilog关键字版本
r#"`begin_keywords` 和 `end_keywords` 是用于指定 Verilog 语言版本的编译指令。它们允许设计者在同一个文件中使用不同版本的 Verilog 语法,从而实现兼容性或逐步迁移到新版本。 r#"`begin_keywords` 和 `end_keywords` 是用于指定 Verilog 关键字版本的编译指令。它们允许设计者在同一个文件中使用不同版本的 Verilog 关键字语法,从而实现兼容性或逐步迁移到新版本。
`begin_keywords` Verilog `begin_keywords` Verilog

View File

@ -76,8 +76,8 @@ pub fn sv_parser(path: &str) -> Option<FastHdlparam> {
// println!("result: {result:?}"); // println!("result: {result:?}");
if let Some(syntax_tree) = result { if let Some(syntax_tree) = result {
if let Ok(hdlparam) = make_fast_from_syntaxtree(&syntax_tree, &path) { if let Ok(fast) = make_fast_from_syntaxtree(&syntax_tree, &path) {
return Some(hdlparam); return Some(fast);
} }
} }
@ -91,7 +91,7 @@ pub fn make_fast_from_syntaxtree(
// 对不同操作系统文件路径的支持 // 对不同操作系统文件路径的支持
let path = to_escape_path(path); let path = to_escape_path(path);
let mut hdlparam = FastHdlparam { let mut fast: FastHdlparam = FastHdlparam {
fast_macro: Macro { fast_macro: Macro {
defines: Vec::new(), defines: Vec::new(),
errors: Vec::new(), errors: Vec::new(),
@ -103,10 +103,11 @@ pub fn make_fast_from_syntaxtree(
}; };
let mut ansi_port_last_dir = ""; let mut ansi_port_last_dir = "";
// println!("{:?}", syntax_tree);
// &SyntaxTree is iterable // &SyntaxTree is iterable
let doc = Rope::from_str(syntax_tree.text.text()); let doc = Rope::from_str(syntax_tree.text.text());
// 上一个 module 可以在 fast 的 content 的最后一个中找到
for node in syntax_tree { for node in syntax_tree {
match node { match node {
RefNode::TextMacroDefinition(x) => { RefNode::TextMacroDefinition(x) => {
@ -148,20 +149,20 @@ pub fn make_fast_from_syntaxtree(
}; };
let define_range = Range { start: start_pos, end: end_pos }; 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) => { RefNode::ModuleDeclaration(x) => {
let start_keyword = unwrap_node!(x, Keyword).unwrap(); let start_keyword = unwrap_node!(x, Keyword).unwrap();
let start_keyword = get_identifier(start_keyword).unwrap(); let start_keyword = get_identifier(start_keyword).unwrap();
let start_pos = get_position(&doc, start_keyword, 0); 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.clone(), end: start_pos };
let module_range = Range { start: start_pos, end: module_range.end };
let id = unwrap_node!(x, ModuleIdentifier).unwrap(); let id = unwrap_node!(x, ModuleIdentifier).unwrap();
let id = get_identifier(id).unwrap(); let id = get_identifier(id).unwrap();
let name = syntax_tree.get_str(&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) => { RefNode::ParameterDeclaration(param_dec) => {
let mut event_iter = param_dec.into_iter().event(); 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 end_pos = get_position(&doc, loc.clone(), loc.len);
let param_range = Range { start: start_pos, end: end_pos }; 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 name = syntax_tree.get_str(&id).unwrap();
let port_range = Range { start: start_pos.clone(), end: get_position(&doc, id, id.len) }; 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() _ => "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) => { 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 inst_port_assignments = get_instance_ports(&syntax_tree, &doc, hier_node.clone());
let port_range = get_pp_range(&doc, hier_node).to_option(); let port_range = get_pp_range(&doc, hier_node).to_option();
hdlparam.add_instance( fast.add_instance(
name, inst_type, inst_range, name, inst_type, inst_range,
param_range, inst_param_assignments, param_range, inst_param_assignments,
port_range, inst_port_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 inst_port_assignments = get_instance_ports(&syntax_tree, &doc, gate_node.clone());
let port_range = get_pp_range(&doc, gate_node).to_option(); let port_range = get_pp_range(&doc, gate_node).to_option();
hdlparam.add_instance( fast.add_instance(
name, inst_type, inst_range, name, inst_type, inst_range,
param_range, Vec::<InstParameter>::new(), param_range, Vec::<InstParameter>::new(),
port_range, inst_port_assignments 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); // update_module_range(&path, &mut hdlparam);
Ok(hdlparam) Ok(fast)
} }
// 获取 port 或者 param 的 range // 获取 port 或者 param 的 range
@ -705,7 +724,7 @@ fn get_includes(path: &PathBuf) -> Vec<crate::core::hdlparam::Include> {
} }
#[allow(unused)] #[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 file = File::open(path).unwrap();
let reader = BufReader::new(file); let reader = BufReader::new(file);
@ -725,7 +744,7 @@ fn update_module_range(path: &PathBuf, hdlparam: &mut FastHdlparam) {
module_stack.push(module_name.clone()); module_stack.push(module_name.clone());
} else if re_endmodule.is_match(&line) { } else if re_endmodule.is_match(&line) {
if let Some(module_name) = module_stack.pop() { 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); // println!("Module {} ends.", module_name);
} }
} }

View File

@ -33,16 +33,26 @@ pub fn inlay_hint(server: &LspServer, params: &InlayHintParams) -> Option<Vec<In
for module in &fast.content { for module in &fast.content {
for instance in &module.instances { for instance in &module.instances {
// 根据在可见视图外面的 range 就不管了 // 根据在可见视图外面的 range 就不管了
if is_visible_range(&instance.instparams, &visible_range) { if let Some(range) = &instance.instparams {
hints.extend(make_instparam_hints(server, params, instance, rope)); if is_visible_range(&range, &visible_range) {
hints.extend(make_instparam_hints(server, params, instance, rope));
}
} }
if let Some(range) = &instance.instports {
if is_visible_range(&instance.instports, &visible_range) { if is_visible_range(range, &visible_range) {
hints.extend(make_instport_hints(server, params, instance, rope)); 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); return Some(hints);
} }
@ -50,16 +60,46 @@ pub fn inlay_hint(server: &LspServer, params: &InlayHintParams) -> Option<Vec<In
} }
fn is_visible_range( fn is_visible_range(
target_range: &Option<core::hdlparam::Range>, target_range: &core::hdlparam::Range,
visible_range: &core::hdlparam::Range visible_range: &core::hdlparam::Range
) -> bool { ) -> bool {
if let Some(target_range) = target_range { if target_range.before(visible_range) || target_range.after(visible_range) {
if target_range.before(visible_range) || target_range.after(visible_range) { false
return false; } else {
} true
return true;
} }
false }
fn make_endmodule_hints(
module: &core::hdlparam::Module
) -> Vec<InlayHint> {
let mut hints = Vec::<InlayHint>::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( fn make_instparam_hints(
@ -112,6 +152,7 @@ fn find_instport_inlay_hints_position(
fn make_instport_hints( fn make_instport_hints(
server: &LspServer, server: &LspServer,
#[allow(unused)]
params: &InlayHintParams, params: &InlayHintParams,
instance: &core::hdlparam::Instance, instance: &core::hdlparam::Instance,
rope: &Rope rope: &Rope