修复 macro usage 无法跳转的问题
This commit is contained in:
parent
9688a330bf
commit
0cf07fd017
@ -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<Compl
|
||||
);
|
||||
|
||||
let project = server.srcs.vhdl_project.read().ok()?;
|
||||
|
||||
#[allow(unused)]
|
||||
let global_project = project.as_ref().unwrap();
|
||||
|
||||
let path = match PathBuf::from_str(uri.path()) {
|
||||
Ok(path) => path,
|
||||
Err(error) => {
|
||||
@ -38,6 +41,8 @@ pub fn completion(server: &LspServer, params: &CompletionParams) -> Option<Compl
|
||||
}
|
||||
};
|
||||
let escape_path = to_escape_path(&path);
|
||||
|
||||
#[allow(unused)]
|
||||
let project_file = escape_path.as_path();
|
||||
|
||||
// let Some(source) = global_project.project.get_source(project_file) else {
|
||||
|
@ -446,15 +446,19 @@ impl FastHdlparam {
|
||||
}
|
||||
|
||||
pub struct HdlFile {
|
||||
/// 专注于模块树构建和粗粒度 AST 信息的数据结构
|
||||
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 {
|
||||
/// 路径到 HdlFile 的映射
|
||||
pub path_to_hdl_file: RwLock<HashMap<String, 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
|
||||
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::<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);
|
||||
}
|
||||
|
||||
|
@ -73,9 +73,7 @@ pub fn sv_parser(path: &str) -> Option<FastHdlparam> {
|
||||
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);
|
||||
}
|
||||
|
@ -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> {
|
||||
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),
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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,7 +71,7 @@ impl AbstractLinterConfiguration for VivadoConfiguration {
|
||||
};
|
||||
|
||||
// vivado 比较特殊,需要先分析出当前文件用了哪些其他文件的宏,然后把那部分宏所在的文件加入编译参数中
|
||||
|
||||
let dependence_files = get_all_dependence_files(uri, server);
|
||||
|
||||
let child = Command::new(&invoke_name)
|
||||
.current_dir(cwd)
|
||||
@ -80,6 +79,7 @@ impl AbstractLinterConfiguration for VivadoConfiguration {
|
||||
.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<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(¯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(
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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<Range>,
|
||||
inc_paths: &[PathBuf],
|
||||
) -> Option<SyntaxTree>{
|
||||
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<Range>,
|
||||
inc_paths: &[PathBuf],
|
||||
allow_incomplete: bool,
|
||||
) -> Option<SyntaxTree> {
|
||||
) -> Option<(SyntaxTree, sv_parser::common::ParseResult)> {
|
||||
let mut parse_iterations = 1;
|
||||
let mut i = 0;
|
||||
let mut includes: Vec<PathBuf> = 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:?}");
|
||||
@ -644,6 +648,8 @@ pub fn recovery_sv_parse(
|
||||
}
|
||||
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 <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 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 {
|
||||
|
@ -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 {
|
||||
|
@ -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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<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 {
|
||||
Position {
|
||||
line: position.line,
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit f04201227d9811e595add86510cb3d4cc4191eb5
|
||||
Subproject commit bf73f0e4ef0ab048704d52b468985d6630f77a1e
|
Loading…
x
Reference in New Issue
Block a user