修复 macro usage 无法跳转的问题

This commit is contained in:
锦恢 2024-12-15 01:35:26 +08:00
parent 9688a330bf
commit 0cf07fd017
12 changed files with 163 additions and 65 deletions

View File

@ -4,7 +4,7 @@ use std::{path::PathBuf, str::FromStr};
use log::info; use log::info;
use ropey::{Rope, RopeSlice}; use ropey::{Rope, RopeSlice};
use tower_lsp::lsp_types::*; use tower_lsp::lsp_types::*;
use crate::{hover::feature::make_vhdl_module_profile_code, utils::{from_lsp_pos, to_escape_path}}; use crate::{hover::feature::make_vhdl_module_profile_code, utils::to_escape_path};
#[allow(unused)] #[allow(unused)]
use crate::{server::LspServer, sources::LSPSupport, utils::get_language_id_by_uri}; use crate::{server::LspServer, sources::LSPSupport, utils::get_language_id_by_uri};
@ -29,7 +29,10 @@ pub fn completion(server: &LspServer, params: &CompletionParams) -> Option<Compl
); );
let project = server.srcs.vhdl_project.read().ok()?; let project = server.srcs.vhdl_project.read().ok()?;
#[allow(unused)]
let global_project = project.as_ref().unwrap(); let global_project = project.as_ref().unwrap();
let path = match PathBuf::from_str(uri.path()) { let path = match PathBuf::from_str(uri.path()) {
Ok(path) => path, Ok(path) => path,
Err(error) => { Err(error) => {
@ -38,6 +41,8 @@ pub fn completion(server: &LspServer, params: &CompletionParams) -> Option<Compl
} }
}; };
let escape_path = to_escape_path(&path); let escape_path = to_escape_path(&path);
#[allow(unused)]
let project_file = escape_path.as_path(); let project_file = escape_path.as_path();
// let Some(source) = global_project.project.get_source(project_file) else { // let Some(source) = global_project.project.get_source(project_file) else {

View File

@ -446,15 +446,19 @@ impl FastHdlparam {
} }
pub struct HdlFile { pub struct HdlFile {
/// 专注于模块树构建和粗粒度 AST 信息的数据结构
pub fast: FastHdlparam, pub fast: FastHdlparam,
pub name_to_module: HashMap<String, Module> /// 名字到 module 映射的 map
pub name_to_module: HashMap<String, Module>,
/// 解析器生成的额外信息
pub parse_result: sv_parser::common::ParseResult
} }
pub struct HdlParam { pub struct HdlParam {
/// 路径到 HdlFile 的映射 /// 路径到 HdlFile 的映射
pub path_to_hdl_file: RwLock<HashMap<String, HdlFile>>, pub path_to_hdl_file: RwLock<HashMap<String, HdlFile>>,
/// 模块名字到其所在的 HdlFile 路径的映射 /// 模块名字到其所在的 HdlFile 路径的映射
pub module_name_to_path: RwLock<HashMap<String, String>> pub module_name_to_path: RwLock<HashMap<String, String>>,
} }
@ -467,8 +471,13 @@ impl HdlParam {
} }
} }
/// 根据 path 更新 fast /// 根据 path 更新 fast 和 parse_result
pub fn update_fast(&self, path: String, fast: FastHdlparam) { pub fn update_hdl_file(
&self,
path: String,
fast: FastHdlparam,
parse_result: sv_parser::common::ParseResult
) {
let mut fast_map = self.path_to_hdl_file.write().unwrap(); let mut fast_map = self.path_to_hdl_file.write().unwrap();
// 构建映射 // 构建映射
let mut name_to_module = HashMap::<String, Module>::new(); let mut name_to_module = HashMap::<String, Module>::new();
@ -481,7 +490,7 @@ impl HdlParam {
} }
} }
let file = HdlFile { fast, name_to_module }; let file = HdlFile { fast, name_to_module, parse_result };
fast_map.insert(path, file); fast_map.insert(path, file);
} }

View File

@ -73,9 +73,7 @@ pub fn sv_parser(path: &str) -> Option<FastHdlparam> {
let uri = Url::from_file_path(&path).unwrap(); let uri = Url::from_file_path(&path).unwrap();
let result = recovery_sv_parse_with_retry(&doc, &uri, &None, &includes); let result = recovery_sv_parse_with_retry(&doc, &uri, &None, &includes);
// println!("result: {result:?}"); if let Some((syntax_tree, _)) = result {
if let Some(syntax_tree) = result {
if let Ok(fast) = make_fast_from_syntaxtree(&syntax_tree, &path) { if let Ok(fast) = make_fast_from_syntaxtree(&syntax_tree, &path) {
return Some(fast); return Some(fast);
} }

View File

@ -74,7 +74,7 @@ pub fn goto_include_definition(uri: &Url, line: &RopeSlice, pos: Position) -> Op
/// 跳转到宏定义 /// 跳转到宏定义
pub fn goto_macro_definition(server: &LspServer, line: &RopeSlice, pos: Position) -> Option<GotoDefinitionResponse> { pub fn goto_macro_definition(server: &LspServer, line: &RopeSlice, pos: Position) -> Option<GotoDefinitionResponse> {
let macro_text_regex = Regex::new(r"[`0-9a-zA-Z]").unwrap(); let macro_text_regex = Regex::new(r"[`_0-9a-zA-Z]").unwrap();
if let Some((macro_text, range)) = get_word_range_at_position(line, pos, macro_text_regex) { if let Some((macro_text, range)) = get_word_range_at_position(line, pos, macro_text_regex) {
if macro_text.starts_with("`") { if macro_text.starts_with("`") {
if let Some((macro_define, define_path)) = server.find_macros(&macro_text) { if let Some((macro_define, define_path)) = server.find_macros(&macro_text) {
@ -84,8 +84,7 @@ pub fn goto_macro_definition(server: &LspServer, line: &RopeSlice, pos: Position
Err(_) => return None Err(_) => return None
}; };
let mut target_range = macro_define.range.clone(); let target_range = macro_define.range.to_lsp_range();
let target_range = target_range.affine(-1, -1).to_lsp_range();
let link = vec![LocationLink { let link = vec![LocationLink {
target_uri, target_uri,
origin_selection_range: Some(range), origin_selection_range: Some(range),

View File

@ -24,7 +24,7 @@ pub fn goto_definition(server: &LspServer, params: &GotoDefinitionParams) -> Opt
return Some(definition); return Some(definition);
} }
// match macro // match macro usage
if let Some(definition) = goto_macro_definition(server, &line_text, pos) { if let Some(definition) = goto_macro_definition(server, &line_text, pos) {
return Some(definition); return Some(definition);
} }

View File

@ -1,11 +1,10 @@
use std::process::{Command, Stdio}; use std::{collections::HashSet, process::{Command, Stdio}};
#[allow(unused)] #[allow(unused)]
use log::info; use log::info;
use regex::{escape, Regex}; use regex::{escape, Regex};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use xml::name; use crate::{diagnostics::find_non_whitespace_indices, server::LspServer, utils::from_uri_to_escape_path_string};
use crate::{diagnostics::find_non_whitespace_indices, server::LspServer};
use ropey::Rope; use ropey::Rope;
use tower_lsp::lsp_types::{Diagnostic, DiagnosticSeverity, NumberOrString, Position, Range, Url}; use tower_lsp::lsp_types::{Diagnostic, DiagnosticSeverity, NumberOrString, Position, Range, Url};
@ -72,7 +71,7 @@ impl AbstractLinterConfiguration for VivadoConfiguration {
}; };
// vivado 比较特殊,需要先分析出当前文件用了哪些其他文件的宏,然后把那部分宏所在的文件加入编译参数中 // vivado 比较特殊,需要先分析出当前文件用了哪些其他文件的宏,然后把那部分宏所在的文件加入编译参数中
let dependence_files = get_all_dependence_files(uri, server);
let child = Command::new(&invoke_name) let child = Command::new(&invoke_name)
.current_dir(cwd) .current_dir(cwd)
@ -80,6 +79,7 @@ impl AbstractLinterConfiguration for VivadoConfiguration {
.stderr(Stdio::piped()) .stderr(Stdio::piped())
.stdout(Stdio::piped()) .stdout(Stdio::piped())
.args(&self.linter.args) .args(&self.linter.args)
.args(dependence_files)
.arg(path_string) .arg(path_string)
.spawn() .spawn()
.ok()?; .ok()?;
@ -107,7 +107,6 @@ impl AbstractLinterConfiguration for VivadoConfiguration {
Err(_) => 0 Err(_) => 0
}; };
if let Some((start_char, end_char)) = find_vivado_suitable_range(rope, error_no, error_description) { if let Some((start_char, end_char)) = find_vivado_suitable_range(rope, error_no, error_description) {
let range = Range { let range = Range {
start: Position { line: error_no as u32, character: start_char as u32 }, start: Position { line: error_no as u32, character: start_char as u32 },
@ -146,6 +145,50 @@ impl AbstractLinterConfiguration for VivadoConfiguration {
} }
} }
/// 计算出当前文件所有用到的别的文件(比如使用了其他文件的宏)
/// 必须把这些文件也编入诊断中,才能基于 vivado 得到合理的结果
fn get_all_dependence_files(
uri: &Url,
server: &LspServer
) -> Vec<String> {
let mut files = HashSet::<String>::new();
let path_string = from_uri_to_escape_path_string(uri).unwrap();
let mut used_macro_names = HashSet::<String>::new();
let hdl_param = server.srcs.hdl_param.clone();
let path_to_hdl_file = hdl_param.path_to_hdl_file.read().unwrap();
if let Some(hdl_file) = path_to_hdl_file.get(&path_string) {
for macro_symbol in &hdl_file.parse_result.symbol_table.macro_usages {
used_macro_names.insert(macro_symbol.name.to_string());
}
}
for (file_path, hdl_file) in path_to_hdl_file.iter() {
if file_path == path_string.as_str() {
// 只看其他文件
continue;
}
for define in hdl_file.fast.fast_macro.defines.iter() {
let macro_name = define.name.to_string();
if used_macro_names.contains(&macro_name) {
used_macro_names.remove(&macro_name);
files.insert(file_path.to_string());
}
}
// 如果 unused_macro_names 都找到了对应的 path直接 break 即可
if used_macro_names.is_empty() {
break;
}
}
// 释放锁
drop(path_to_hdl_file);
files.into_iter().collect()
}
/// 根据 vivado 返回的诊断信息,返回适合的错误在那一行的起始位置 /// 根据 vivado 返回的诊断信息,返回适合的错误在那一行的起始位置
/// 默认是返回该行的第一个非空字符到最后一个非空字符中间的位置,即 find_non_whitespace_indices /// 默认是返回该行的第一个非空字符到最后一个非空字符中间的位置,即 find_non_whitespace_indices
fn find_vivado_suitable_range( fn find_vivado_suitable_range(

View File

@ -193,11 +193,15 @@ fn do_sv_fast(
); );
let sources = &backend.server.srcs; let sources = &backend.server.srcs;
if let Some(syntax_tree) = parse_result { if let Some((syntax_tree, parse_result)) = parse_result {
if let Ok(mut fast) = make_fast_from_syntaxtree(&syntax_tree, &path_buf) { if let Ok(mut fast) = make_fast_from_syntaxtree(&syntax_tree, &path_buf) {
fast.file_type = file_type.to_string(); fast.file_type = file_type.to_string();
let hdl_param = sources.hdl_param.clone(); let hdl_param = sources.hdl_param.clone();
hdl_param.update_fast(path.to_string(), fast.clone()); hdl_param.update_hdl_file(
path.to_string(),
fast.clone(),
parse_result
);
return Ok(fast); return Ok(fast);
} }
} }
@ -252,7 +256,12 @@ fn do_vhdl_fast(
file_type: "ip".to_string(), file_type: "ip".to_string(),
content: fake_content content: fake_content
}; };
hdl_param.update_fast(path.to_string(), ip_fast.clone());
hdl_param.update_hdl_file(
path.to_string(),
ip_fast.clone(),
sv_parser::common::ParseResult::new()
);
return Ok(ip_fast); return Ok(ip_fast);
} else if let Some(vhdl_project) = &mut *vhdl_project.write().unwrap() { } else if let Some(vhdl_project) = &mut *vhdl_project.write().unwrap() {
vhdl_project.config_file_strs.push(format!("{:?}", pathbuf)); vhdl_project.config_file_strs.push(format!("{:?}", pathbuf));
@ -280,7 +289,12 @@ fn do_vhdl_fast(
for module in &mut fast.content { for module in &mut fast.content {
module.instances.clear(); module.instances.clear();
} }
hdl_param.update_fast(path.to_string(), fast.clone()); // 为了兼容 verilog 而制作的空的
hdl_param.update_hdl_file(
path.to_string(),
fast.clone(),
sv_parser::common::ParseResult::new()
);
return Ok(fast); return Ok(fast);
} }
} else { } else {
@ -321,7 +335,11 @@ fn do_vhdl_fast(
// info!("debug, module : {:?}, path: {:?}", module, pathbuf); // info!("debug, module : {:?}, path: {:?}", module, pathbuf);
// } // }
// } // }
hdl_param.update_fast(path.to_string(), fast.clone()); hdl_param.update_hdl_file(
path.to_string(),
fast.clone(),
sv_parser::common::ParseResult::new()
);
return Ok(fast); return Ok(fast);
} }
} }

View File

@ -9,10 +9,10 @@ use crate::core::scope_tree::get_scopes_from_vhdl_fast;
use crate::diagnostics::provide_diagnostics; use crate::diagnostics::provide_diagnostics;
use crate::server::LspServer; use crate::server::LspServer;
use crate::server::LspConfiguration; use crate::server::LspConfiguration;
use crate::utils::from_uri_to_escape_path_string;
use crate::utils::to_escape_path; use crate::utils::to_escape_path;
#[allow(unused)] #[allow(unused)]
use log::info; use log::{info, debug, error};
use log::{debug, error};
use pathdiff::diff_paths; use pathdiff::diff_paths;
use ropey::{Rope, RopeSlice}; use ropey::{Rope, RopeSlice};
use serde_json::Value; use serde_json::Value;
@ -276,7 +276,11 @@ impl Sources {
for (name, template) in primitive_xml.name_to_template { for (name, template) in primitive_xml.name_to_template {
// use "primitive/name" as a fake path // use "primitive/name" as a fake path
let fake_path = "primitive/".to_string() + &name; let fake_path = "primitive/".to_string() + &name;
hdl_param_handle.update_fast(fake_path, template.fast); hdl_param_handle.update_hdl_file(
fake_path,
template.fast,
sv_parser::common::ParseResult::new()
);
primitive_text_handle.update_text(&name, &template.text); primitive_text_handle.update_text(&name, &template.text);
} }
} }
@ -525,9 +529,9 @@ pub fn recovery_sv_parse_with_retry(
uri: &Url, uri: &Url,
last_change_range: &Option<Range>, last_change_range: &Option<Range>,
inc_paths: &[PathBuf], inc_paths: &[PathBuf],
) -> Option<SyntaxTree>{ ) -> Option<(SyntaxTree, sv_parser::common::ParseResult)>{
if let Some(syntax_tree) = recovery_sv_parse(doc, uri, last_change_range, inc_paths, false) { if let Some((syntax_tree, parse_result)) = recovery_sv_parse(doc, uri, last_change_range, inc_paths, false) {
Some(syntax_tree) Some((syntax_tree, parse_result))
} else { } else {
recovery_sv_parse(doc, uri, last_change_range, inc_paths, true) recovery_sv_parse(doc, uri, last_change_range, inc_paths, true)
} }
@ -547,7 +551,7 @@ pub fn recovery_sv_parse(
last_change_range: &Option<Range>, last_change_range: &Option<Range>,
inc_paths: &[PathBuf], inc_paths: &[PathBuf],
allow_incomplete: bool, allow_incomplete: bool,
) -> Option<SyntaxTree> { ) -> Option<(SyntaxTree, sv_parser::common::ParseResult)> {
let mut parse_iterations = 1; let mut parse_iterations = 1;
let mut i = 0; let mut i = 0;
let mut includes: Vec<PathBuf> = inc_paths.to_vec(); let mut includes: Vec<PathBuf> = inc_paths.to_vec();
@ -583,10 +587,10 @@ pub fn recovery_sv_parse(
} }
}; };
// 最多解析 500 // 最多解析 50
while i < parse_iterations && i < 500 { while i < parse_iterations && i < 50 {
i += 1; i += 1;
match parse_sv_str( match make_ast_from_svlog_code(
&text.to_string(), &text.to_string(),
uri.to_file_path().unwrap(), uri.to_file_path().unwrap(),
&defines, &defines,
@ -594,8 +598,8 @@ pub fn recovery_sv_parse(
true, true,
allow_incomplete allow_incomplete
) { ) {
Ok((syntax_tree, _)) => { Ok((syntax_tree, parse_result)) => {
return Some(syntax_tree); return Some((syntax_tree, parse_result));
} }
Err(err) => { Err(err) => {
// println!("err: {err:?}"); // println!("err: {err:?}");
@ -644,6 +648,8 @@ pub fn recovery_sv_parse(
} }
sv_parser::Error::Preprocess(trace) => match trace { sv_parser::Error::Preprocess(trace) => match trace {
Some((_, byte_idx)) => { Some((_, byte_idx)) => {
info!("meet preprocess error, text: {text:?}");
// 把出错的地方替换成空格 // 把出错的地方替换成空格
// println!("text {text:?}"); // println!("text {text:?}");
recover_text_by_byte_idx(byte_idx, &mut reverted_change, &mut text); recover_text_by_byte_idx(byte_idx, &mut reverted_change, &mut text);
@ -690,21 +696,10 @@ pub fn sv_parser_pipeline(
return; return;
} }
let path = match PathBuf::from_str(uri.path()) { let escape_path_string = from_uri_to_escape_path_string(uri).unwrap();
Ok(path) => path, let escape_path = PathBuf::from_str(&escape_path_string).unwrap();
Err(error) => {
info!("error happen in <goto_include_definition>: {:?}", error);
return;
}
};
let escape_path = to_escape_path(&path);
let escape_path_string = escape_path.to_str().unwrap_or("");
if escape_path_string.len() == 0 {
info!("error happen in [sv_parser_pipeline], escape_path_string is empty");
return;
}
let syntax_tree = recovery_sv_parse_with_retry( let ast = recovery_sv_parse_with_retry(
doc, doc,
uri, uri,
last_change_range, last_change_range,
@ -712,17 +707,22 @@ pub fn sv_parser_pipeline(
); );
// 更新 scope tree // 更新 scope tree
let mut scope_tree = match &syntax_tree { let mut scope_tree = match &ast {
Some(tree) => get_scopes_from_syntax_tree(tree, uri), Some((tree, _)) => get_scopes_from_syntax_tree(tree, uri),
None => None, None => None,
}; };
let mut file = source_handle.write().unwrap(); let mut file = source_handle.write().unwrap();
// 加入语法树 & 更新 fast // 加入语法树 & 更新 fast
if let Some(syntax_tree) = syntax_tree { if let Some((syntax_tree, parse_result)) = ast {
if let Ok(fast) = make_fast_from_syntaxtree(&syntax_tree, &escape_path) { if let Ok(fast) = make_fast_from_syntaxtree(&syntax_tree, &escape_path) {
hdl_param_handle.update_fast(escape_path_string.to_string(), fast); info!("update parse_result: {:?}", parse_result);
hdl_param_handle.update_hdl_file(
escape_path_string.to_string(),
fast,
parse_result
);
} }
let parse_ir = ParseIR::SyntaxTree(syntax_tree); let parse_ir = ParseIR::SyntaxTree(syntax_tree);
file.parse_ir = Some(parse_ir); file.parse_ir = Some(parse_ir);
@ -879,7 +879,11 @@ pub fn vhdl_parser_pipeline(
}; };
fast.file_type = file_type; fast.file_type = file_type;
hdl_param_handle.update_fast(escape_path_string.to_string(), fast.clone()); hdl_param_handle.update_hdl_file(
escape_path_string.to_string(),
fast.clone(),
sv_parser::common::ParseResult::new()
);
let scope_tree = get_scopes_from_vhdl_fast(&fast, doc, uri); let scope_tree = get_scopes_from_vhdl_fast(&fast, doc, uri);
scope_tree scope_tree
} else { } else {

View File

@ -332,7 +332,7 @@ mod test_scope_tree {
let result = recovery_sv_parse_with_retry(&doc, &uri, &None, &includes); let result = recovery_sv_parse_with_retry(&doc, &uri, &None, &includes);
// let result = recovery_sv_parse(&doc, &uri, &None, &includes, true); // let result = recovery_sv_parse(&doc, &uri, &None, &includes, true);
if let Some(syntax_tree) = result { if let Some((syntax_tree, _)) = result {
let file_url = format!("file://{}", file_path); let file_url = format!("file://{}", file_path);
let uri = Url::parse(&file_url); let uri = Url::parse(&file_url);
if let Ok(uri) = uri { if let Ok(uri) = uri {

View File

@ -8,13 +8,11 @@ impl LspServer {
/// macro 可以以 ` 开头 /// macro 可以以 ` 开头
pub fn find_macros(&self, macro_name: &str) -> Option<(Define, String)> { pub fn find_macros(&self, macro_name: &str) -> Option<(Define, String)> {
let macro_name = macro_name.replace("`", ""); let macro_name = macro_name.replace("`", "");
let fast_map = self.srcs.hdl_param.path_to_hdl_file.read().unwrap(); let path_to_hdl_file = self.srcs.hdl_param.path_to_hdl_file.read().unwrap();
for path in fast_map.keys() { for (path, hdl_file) in path_to_hdl_file.iter() {
if let Some(hdl_file) = fast_map.get(path) { for define in &hdl_file.fast.fast_macro.defines {
for define in &hdl_file.fast.fast_macro.defines { if define.name == macro_name {
if define.name == macro_name { return Some((define.clone(), path.to_string()));
return Some((define.clone(), path.to_string()));
}
} }
} }
} }

View File

@ -1,5 +1,6 @@
use std::{env::consts::OS, path::{Path, PathBuf}}; use std::{env::consts::OS, path::{Path, PathBuf}, str::FromStr};
use log::info;
use percent_encoding::percent_decode_str; use percent_encoding::percent_decode_str;
use regex::Regex; use regex::Regex;
use ropey::RopeSlice; use ropey::RopeSlice;
@ -176,6 +177,29 @@ pub fn to_escape_path(path: &PathBuf) -> PathBuf {
} }
} }
/// 将 uri 转换为 digital lsp 内部使用的路径字符串
/// 比如 hdlparam 里面有一些以 String 作为主键的 hashmap
/// 它们的 String 如果代表路径,那么都是通过该函数从 uri 转换而来的
pub fn from_uri_to_escape_path_string(
uri: &Url
) -> Option<String> {
let path = match PathBuf::from_str(uri.path()) {
Ok(path) => path,
Err(error) => {
info!("PathBuf::from_str(uri.path()) 发生错误 {:?}", error);
return None;
}
};
let escape_path = to_escape_path(&path);
let escape_path_string = escape_path.to_str().unwrap_or("");
if escape_path_string.len() == 0 {
info!("escape_path_string 为空");
return None;
}
Some(escape_path_string.to_string())
}
pub fn to_lsp_pos(position: vhdl_lang::Position) -> Position { pub fn to_lsp_pos(position: vhdl_lang::Position) -> Position {
Position { Position {
line: position.line, line: position.line,

@ -1 +1 @@
Subproject commit f04201227d9811e595add86510cb3d4cc4191eb5 Subproject commit bf73f0e4ef0ab048704d52b468985d6630f77a1e