diff --git a/src/completion/vhdl.rs b/src/completion/vhdl.rs index c09b6a4..c2f4bad 100644 --- a/src/completion/vhdl.rs +++ b/src/completion/vhdl.rs @@ -4,7 +4,7 @@ use std::{path::PathBuf, str::FromStr}; use log::info; use ropey::{Rope, RopeSlice}; 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)] use crate::{server::LspServer, sources::LSPSupport, utils::get_language_id_by_uri}; @@ -29,7 +29,10 @@ pub fn completion(server: &LspServer, params: &CompletionParams) -> Option path, Err(error) => { @@ -38,6 +41,8 @@ pub fn completion(server: &LspServer, params: &CompletionParams) -> Option + /// 名字到 module 映射的 map + pub name_to_module: HashMap, + /// 解析器生成的额外信息 + pub parse_result: sv_parser::common::ParseResult } pub struct HdlParam { /// 路径到 HdlFile 的映射 pub path_to_hdl_file: RwLock>, /// 模块名字到其所在的 HdlFile 路径的映射 - pub module_name_to_path: RwLock> + pub module_name_to_path: RwLock>, } @@ -467,8 +471,13 @@ impl HdlParam { } } - /// 根据 path 更新 fast - pub fn update_fast(&self, path: String, fast: FastHdlparam) { + /// 根据 path 更新 fast 和 parse_result + 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 name_to_module = HashMap::::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); } diff --git a/src/core/sv_parser.rs b/src/core/sv_parser.rs index 7f3d78f..02e193b 100644 --- a/src/core/sv_parser.rs +++ b/src/core/sv_parser.rs @@ -72,10 +72,8 @@ pub fn sv_parser(path: &str) -> Option { let doc = Rope::from_str(&text); let uri = Url::from_file_path(&path).unwrap(); 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) { return Some(fast); } diff --git a/src/definition/feature.rs b/src/definition/feature.rs index 776416b..545fba6 100644 --- a/src/definition/feature.rs +++ b/src/definition/feature.rs @@ -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 { - 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 macro_text.starts_with("`") { if let Some((macro_define, define_path)) = server.find_macros(¯o_text) { @@ -84,8 +84,7 @@ pub fn goto_macro_definition(server: &LspServer, line: &RopeSlice, pos: Position Err(_) => return None }; - let mut target_range = macro_define.range.clone(); - let target_range = target_range.affine(-1, -1).to_lsp_range(); + let target_range = macro_define.range.to_lsp_range(); let link = vec![LocationLink { target_uri, origin_selection_range: Some(range), diff --git a/src/definition/sv.rs b/src/definition/sv.rs index c9a3753..b5e70a7 100644 --- a/src/definition/sv.rs +++ b/src/definition/sv.rs @@ -24,7 +24,7 @@ pub fn goto_definition(server: &LspServer, params: &GotoDefinitionParams) -> Opt return Some(definition); } - // match macro + // match macro usage if let Some(definition) = goto_macro_definition(server, &line_text, pos) { return Some(definition); } diff --git a/src/diagnostics/vivado.rs b/src/diagnostics/vivado.rs index 38288ad..37b5c45 100644 --- a/src/diagnostics/vivado.rs +++ b/src/diagnostics/vivado.rs @@ -1,11 +1,10 @@ -use std::process::{Command, Stdio}; +use std::{collections::HashSet, process::{Command, Stdio}}; #[allow(unused)] use log::info; use regex::{escape, Regex}; use serde::{Deserialize, Serialize}; -use xml::name; -use crate::{diagnostics::find_non_whitespace_indices, server::LspServer}; +use crate::{diagnostics::find_non_whitespace_indices, server::LspServer, utils::from_uri_to_escape_path_string}; use ropey::Rope; use tower_lsp::lsp_types::{Diagnostic, DiagnosticSeverity, NumberOrString, Position, Range, Url}; @@ -72,14 +71,15 @@ impl AbstractLinterConfiguration for VivadoConfiguration { }; // vivado 比较特殊,需要先分析出当前文件用了哪些其他文件的宏,然后把那部分宏所在的文件加入编译参数中 + let dependence_files = get_all_dependence_files(uri, server); - let child = Command::new(&invoke_name) .current_dir(cwd) .stdin(Stdio::piped()) .stderr(Stdio::piped()) .stdout(Stdio::piped()) .args(&self.linter.args) + .args(dependence_files) .arg(path_string) .spawn() .ok()?; @@ -107,7 +107,6 @@ impl AbstractLinterConfiguration for VivadoConfiguration { Err(_) => 0 }; - if let Some((start_char, end_char)) = find_vivado_suitable_range(rope, error_no, error_description) { let range = Range { 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 { + let mut files = HashSet::::new(); + let path_string = from_uri_to_escape_path_string(uri).unwrap(); + let mut used_macro_names = HashSet::::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(¯o_name) { + used_macro_names.remove(¯o_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 返回的诊断信息,返回适合的错误在那一行的起始位置 /// 默认是返回该行的第一个非空字符到最后一个非空字符中间的位置,即 find_non_whitespace_indices fn find_vivado_suitable_range( diff --git a/src/request/fast.rs b/src/request/fast.rs index 40491b5..4b831ab 100644 --- a/src/request/fast.rs +++ b/src/request/fast.rs @@ -193,11 +193,15 @@ fn do_sv_fast( ); 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) { fast.file_type = file_type.to_string(); 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); } } @@ -252,7 +256,12 @@ fn do_vhdl_fast( file_type: "ip".to_string(), 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); } else if let Some(vhdl_project) = &mut *vhdl_project.write().unwrap() { vhdl_project.config_file_strs.push(format!("{:?}", pathbuf)); @@ -280,7 +289,12 @@ fn do_vhdl_fast( for module in &mut fast.content { 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); } } else { @@ -321,7 +335,11 @@ fn do_vhdl_fast( // 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); } } diff --git a/src/sources.rs b/src/sources.rs index 9de09dc..b32500c 100644 --- a/src/sources.rs +++ b/src/sources.rs @@ -9,10 +9,10 @@ use crate::core::scope_tree::get_scopes_from_vhdl_fast; use crate::diagnostics::provide_diagnostics; use crate::server::LspServer; use crate::server::LspConfiguration; +use crate::utils::from_uri_to_escape_path_string; use crate::utils::to_escape_path; #[allow(unused)] -use log::info; -use log::{debug, error}; +use log::{info, debug, error}; use pathdiff::diff_paths; use ropey::{Rope, RopeSlice}; use serde_json::Value; @@ -276,7 +276,11 @@ impl Sources { for (name, template) in primitive_xml.name_to_template { // use "primitive/name" as a fake path 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); } } @@ -525,9 +529,9 @@ pub fn recovery_sv_parse_with_retry( uri: &Url, last_change_range: &Option, inc_paths: &[PathBuf], -) -> Option{ - if let Some(syntax_tree) = recovery_sv_parse(doc, uri, last_change_range, inc_paths, false) { - Some(syntax_tree) +) -> Option<(SyntaxTree, sv_parser::common::ParseResult)>{ + if let Some((syntax_tree, parse_result)) = recovery_sv_parse(doc, uri, last_change_range, inc_paths, false) { + Some((syntax_tree, parse_result)) } else { recovery_sv_parse(doc, uri, last_change_range, inc_paths, true) } @@ -547,7 +551,7 @@ pub fn recovery_sv_parse( last_change_range: &Option, inc_paths: &[PathBuf], allow_incomplete: bool, -) -> Option { +) -> Option<(SyntaxTree, sv_parser::common::ParseResult)> { let mut parse_iterations = 1; let mut i = 0; let mut includes: Vec = inc_paths.to_vec(); @@ -583,10 +587,10 @@ pub fn recovery_sv_parse( } }; - // 最多解析 500 次 - while i < parse_iterations && i < 500 { + // 最多解析 50 次 + while i < parse_iterations && i < 50 { i += 1; - match parse_sv_str( + match make_ast_from_svlog_code( &text.to_string(), uri.to_file_path().unwrap(), &defines, @@ -594,8 +598,8 @@ pub fn recovery_sv_parse( true, allow_incomplete ) { - Ok((syntax_tree, _)) => { - return Some(syntax_tree); + Ok((syntax_tree, parse_result)) => { + return Some((syntax_tree, parse_result)); } Err(err) => { // println!("err: {err:?}"); @@ -642,8 +646,10 @@ pub fn recovery_sv_parse( defines.insert(not_found_macro_name, Some(com_define)); parse_iterations += 1; } - sv_parser::Error::Preprocess(trace) => match trace { + sv_parser::Error::Preprocess(trace) => match trace { Some((_, byte_idx)) => { + info!("meet preprocess error, text: {text:?}"); + // 把出错的地方替换成空格 // println!("text {text:?}"); recover_text_by_byte_idx(byte_idx, &mut reverted_change, &mut text); @@ -690,21 +696,10 @@ pub fn sv_parser_pipeline( return; } - let path = match PathBuf::from_str(uri.path()) { - Ok(path) => path, - Err(error) => { - info!("error happen in : {:?}", 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 escape_path_string = from_uri_to_escape_path_string(uri).unwrap(); + let escape_path = PathBuf::from_str(&escape_path_string).unwrap(); - let syntax_tree = recovery_sv_parse_with_retry( + let ast = recovery_sv_parse_with_retry( doc, uri, last_change_range, @@ -712,17 +707,22 @@ pub fn sv_parser_pipeline( ); // 更新 scope tree - let mut scope_tree = match &syntax_tree { - Some(tree) => get_scopes_from_syntax_tree(tree, uri), + let mut scope_tree = match &ast { + Some((tree, _)) => get_scopes_from_syntax_tree(tree, uri), None => None, }; let mut file = source_handle.write().unwrap(); // 加入语法树 & 更新 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) { - 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); file.parse_ir = Some(parse_ir); @@ -879,7 +879,11 @@ pub fn vhdl_parser_pipeline( }; 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); scope_tree } else { diff --git a/src/test/mod.rs b/src/test/mod.rs index 896a358..f415084 100644 --- a/src/test/mod.rs +++ b/src/test/mod.rs @@ -332,7 +332,7 @@ mod test_scope_tree { let result = recovery_sv_parse_with_retry(&doc, &uri, &None, &includes); // 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 uri = Url::parse(&file_url); if let Ok(uri) = uri { diff --git a/src/utils/fast.rs b/src/utils/fast.rs index 80e4926..8c705e6 100644 --- a/src/utils/fast.rs +++ b/src/utils/fast.rs @@ -8,13 +8,11 @@ impl LspServer { /// macro 可以以 ` 开头 pub fn find_macros(&self, macro_name: &str) -> Option<(Define, String)> { let macro_name = macro_name.replace("`", ""); - let fast_map = self.srcs.hdl_param.path_to_hdl_file.read().unwrap(); - for path in fast_map.keys() { - if let Some(hdl_file) = fast_map.get(path) { - for define in &hdl_file.fast.fast_macro.defines { - if define.name == macro_name { - return Some((define.clone(), path.to_string())); - } + let path_to_hdl_file = self.srcs.hdl_param.path_to_hdl_file.read().unwrap(); + for (path, hdl_file) in path_to_hdl_file.iter() { + for define in &hdl_file.fast.fast_macro.defines { + if define.name == macro_name { + return Some((define.clone(), path.to_string())); } } } diff --git a/src/utils/mod.rs b/src/utils/mod.rs index be9429a..9974417 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -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 regex::Regex; 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 { + 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 { Position { line: position.line, diff --git a/sv-parser b/sv-parser index f042012..bf73f0e 160000 --- a/sv-parser +++ b/sv-parser @@ -1 +1 @@ -Subproject commit f04201227d9811e595add86510cb3d4cc4191eb5 +Subproject commit bf73f0e4ef0ab048704d52b468985d6630f77a1e