diff --git a/src/core/hdlparam.rs b/src/core/hdlparam.rs index 4aea7eb..e621af2 100644 --- a/src/core/hdlparam.rs +++ b/src/core/hdlparam.rs @@ -21,6 +21,12 @@ impl Position { pub fn from_lsp_position(pos: &LspPosition) -> Position { Position { line: pos.line, character: pos.character } } + + pub fn new(line: u32, character: u32) -> Position { + Position { + line, character + } + } } #[derive(Debug, Clone, Serialize, PartialEq, PartialOrd, Eq, Ord, Deserialize)] @@ -103,6 +109,13 @@ impl Range { Some(self.clone()) } } + + pub fn default() -> Range { + Range { + start: Position::new(0, 0), + end: Position::new(0, 0) + } + } } /// 比较两个 pos 的位置关系 @@ -428,7 +441,7 @@ impl HdlParam { if let Some(path) = module_name_to_path.get(name) { let fast_map = self.path_to_hdl_file.read().unwrap(); if let Some(hdl_file) = fast_map.get(path) { - if let Some(module) = hdl_file.name_to_module.get(name) { + if let Some(module) = hdl_file.name_to_module.get(name) { return Some(module.clone()); } } @@ -437,6 +450,30 @@ impl HdlParam { None } + /// 相比于 find_module_by_name,该方法会返回更多有关 该 module 的必要上下文, + /// 避免重复获取锁,提升性能 + /// 返回三元组 (module, file_type, def_path) + pub fn find_module_context_by_name(&self, name: &str) -> Option<(Module, String, String)> { + // 获取 module_name_to_path 的读锁并查找路径 + let module_name_to_path = self.module_name_to_path.read().unwrap(); + if let Some(path) = module_name_to_path.get(name) { + // 获取 path_to_hdl_file 的读锁并查找 HdlFile + let fast_map = self.path_to_hdl_file.read().unwrap(); + if let Some(hdl_file) = fast_map.get(path) { + // 查找模块 + if let Some(module) = hdl_file.name_to_module.get(name) { + return Some(( + module.clone(), + hdl_file.fast.file_type.to_string(), + path.to_string() + )); + } + } + } + + None + } + /// 输入 module 名字,找到 module 定义的文件的路径 pub fn find_module_definition_path(&self, module_name: &str) -> Option { let module_name_to_path = self.module_name_to_path.read().unwrap(); diff --git a/src/definition/feature.rs b/src/definition/feature.rs index 22c06d5..cdd5b1a 100644 --- a/src/definition/feature.rs +++ b/src/definition/feature.rs @@ -5,7 +5,7 @@ use regex::Regex; use ropey::RopeSlice; use tower_lsp::lsp_types::{GotoDefinitionResponse, LocationLink, Position, Range, Url}; -use crate::{core::hdlparam::FastHdlparam, server::LSPServer, utils::{get_word_range_at_position, resolve_path, to_escape_path}}; +use crate::{core::{self, hdlparam::{self, FastHdlparam}}, server::LSPServer, utils::{get_word_range_at_position, resolve_path, to_escape_path}}; /// 跳转到 include 的文件 pub fn goto_include_definition(uri: &Url, line: &RopeSlice, pos: Position) -> Option { @@ -206,8 +206,77 @@ pub fn goto_module_declaration_definition( server: &LSPServer, token_name: &str ) -> Option { - if let Some(module) = server.srcs.hdl_param.find_module_by_name(token_name) { - let def_path = server.srcs.hdl_param.find_module_definition_path(&module.name).unwrap(); + let hdl_param = server.srcs.hdl_param.clone(); + if let Some((module, file_type, def_path)) = hdl_param.find_module_context_by_name(token_name) { + match file_type.as_str() { + "common" => { + goto_common_module_declaration_definition( + server, + token_name, + &module, + &def_path + ) + }, + "ip" => { + goto_ip_module_declaration_definition( + server, + token_name, + &module, + &def_path + ) + }, + "primitives" => { + goto_primitives_module_declaration_definition( + server, + token_name, + &module, + &def_path + ) + }, + _ => None + } + } else { + None + } +} + +fn goto_common_module_declaration_definition( + #[allow(unused)] + server: &LSPServer, + #[allow(unused)] + token_name: &str, + #[allow(unused)] + module: &core::hdlparam::Module, + #[allow(unused)] + def_path: &str +) -> Option { + let target_uri = Url::from_file_path(PathBuf::from_str(&def_path).unwrap()).unwrap(); + + let target_range = module.range.clone(); + let target_range = target_range.to_lsp_range(); + + let link = vec![LocationLink { + target_uri, + origin_selection_range: None, + target_range: target_range, + target_selection_range: target_range + }]; + let links = GotoDefinitionResponse::Link(link); + Some(links) +} + +fn goto_ip_module_declaration_definition( + #[allow(unused)] + server: &LSPServer, + #[allow(unused)] + token_name: &str, + #[allow(unused)] + module: &core::hdlparam::Module, + #[allow(unused)] + def_path: &str +) -> Option { + let pathbuf = PathBuf::from_str(def_path).unwrap(); + if pathbuf.exists() { let target_uri = Url::from_file_path(PathBuf::from_str(&def_path).unwrap()).unwrap(); let target_range = module.range.clone(); @@ -220,8 +289,22 @@ pub fn goto_module_declaration_definition( target_selection_range: target_range }]; let links = GotoDefinitionResponse::Link(link); - return Some(links); + Some(links) + } else { + None } +} + +fn goto_primitives_module_declaration_definition( + #[allow(unused)] + server: &LSPServer, + #[allow(unused)] + token_name: &str, + #[allow(unused)] + module: &core::hdlparam::Module, + #[allow(unused)] + def_path: &str +) -> Option { None } \ No newline at end of file diff --git a/src/hover/feature.rs b/src/hover/feature.rs index d81ca1e..d43ffa8 100644 --- a/src/hover/feature.rs +++ b/src/hover/feature.rs @@ -5,9 +5,9 @@ use regex::Regex; use ropey::RopeSlice; use tower_lsp::lsp_types::{Hover, HoverContents, LanguageString, MarkedString, Position, Range, Url}; -use crate::{core::hdlparam::{Define, FastHdlparam}, definition::{DefinitionType, GenericDec}, server::LSPServer}; +use crate::{core::{self, hdlparam::{Define, FastHdlparam}}, definition::{DefinitionType, GenericDec}, server::LSPServer}; -use super::{get_word_range_at_position, resolve_path, to_escape_path}; +use super::{get_language_id_by_path_str, get_word_range_at_position, resolve_path, to_escape_path}; /// 将 4'b0011 分解为 ("b", "0011") fn parse_digit_string(digit_string: &str) -> Option<(&str, &str)> { @@ -319,75 +319,130 @@ fn make_param_desc_hover(param: &crate::core::hdlparam::Parameter, range: &Range pub fn hover_module_declaration( server: &LSPServer, token_name: &str, + #[allow(unused)] language_id: &str ) -> Option { - - let module_info = { - let search_result = || { - if let Some(module) = server.srcs.hdl_param.find_module_by_name(token_name) { - let path_string = server.srcs.hdl_param.find_module_definition_path(&module.name).unwrap_or("unknown".to_string()); - return Some((module, path_string)); - } - None - }; - search_result() - }; - // info!("token: {:?}, module info: {:?}", token_name, module_info); // let test = server.srcs.hdl_param.module_name_to_path.read().unwrap(); // info!("module name to path: {:#?}", test); - - if let Some((module, path_string)) = module_info { - let path_uri = Url::from_file_path(path_string.to_string()).unwrap().to_string(); - let def_row = module.range.start.line; - let def_col = module.range.start.character; - let define_info = format!("Go to [Definition]({path_uri}#L{def_row}:{def_col})"); - - let port_num = module.ports.len(); - let param_num = module.params.len(); - let instance_num = module.instances.len(); - - let port_desc = format!("`port` {port_num}, `param` {param_num}, `instantiation` {instance_num}"); - - // 统计 dir - let mut input_count = 0 as u32; - let mut output_count = 0 as u32; - let mut inout_count = 0 as u32; - - for port in &module.ports { - match port.dir_type.as_str() { - "input" => input_count += 1, - "output" => output_count += 1, - "inout" => inout_count += 1, - _ => {} - } + let hdl_param = server.srcs.hdl_param.clone(); + if let Some((module, file_type, def_path)) = hdl_param.find_module_context_by_name(token_name) { + match file_type.as_str() { + "common" => { + hover_common_module_declaration( + server, + token_name, + &module, + &def_path + ) + }, + "ip" => { + hover_ip_module_declaration( + server, + token_name, + &module, + &def_path + ) + }, + "primitives" => { + hover_primitives_module_declaration( + server, + token_name, + &module, + &def_path + ) + }, + _ => None } + } else { + None + } +} - let io_desc = format!("`input` {input_count}, `output` {output_count}, `inout` {inout_count}"); +fn hover_common_module_declaration( + #[allow(unused)] + server: &LSPServer, + #[allow(unused)] + token_name: &str, + #[allow(unused)] + module: &core::hdlparam::Module, + #[allow(unused)] + def_path: &str +) -> Option { + let path_uri = Url::from_file_path(def_path.to_string()).unwrap().to_string(); + let def_row = module.range.start.line; + let def_col = module.range.start.character; + let define_info = format!("Go to [Definition]({path_uri}#L{def_row}:{def_col})"); - let mut markdowns = Vec::::new(); - markdowns.push(MarkedString::String(port_desc)); - markdowns.push(MarkedString::String(io_desc)); - markdowns.push(MarkedString::String(define_info)); - markdowns.push(MarkedString::String("---".to_string())); + let port_num = module.ports.len(); + let param_num = module.params.len(); + let instance_num = module.instances.len(); - let module_profile = make_module_profile_code(&module); - let profile_markdown = LanguageString { - language: language_id.to_string(), - value: module_profile - }; - markdowns.push(MarkedString::LanguageString(profile_markdown)); + let port_desc = format!("`port` {port_num}, `param` {param_num}, `instantiation` {instance_num}"); - let hover = Hover { - contents: HoverContents::Array(markdowns), - range: None - }; + // 统计 dir + let mut input_count = 0 as u32; + let mut output_count = 0 as u32; + let mut inout_count = 0 as u32; - return Some(hover); + for port in &module.ports { + match port.dir_type.as_str() { + "input" => input_count += 1, + "output" => output_count += 1, + "inout" => inout_count += 1, + _ => {} + } } - None + let io_desc = format!("`input` {input_count}, `output` {output_count}, `inout` {inout_count}"); + + let mut markdowns = Vec::::new(); + markdowns.push(MarkedString::String(port_desc)); + markdowns.push(MarkedString::String(io_desc)); + markdowns.push(MarkedString::String(define_info)); + markdowns.push(MarkedString::String("---".to_string())); + + let language_id = get_language_id_by_path_str(def_path); + let module_profile = make_module_profile_code(&module); + let profile_markdown = LanguageString { + language: language_id.to_string(), + value: module_profile + }; + markdowns.push(MarkedString::LanguageString(profile_markdown)); + + let hover = Hover { + contents: HoverContents::Array(markdowns), + range: None + }; + + return Some(hover); +} + +fn hover_ip_module_declaration( + #[allow(unused)] + server: &LSPServer, + #[allow(unused)] + token_name: &str, + #[allow(unused)] + module: &core::hdlparam::Module, + #[allow(unused)] + def_path: &str +) -> Option { + None +} + +fn hover_primitives_module_declaration( + #[allow(unused)] + server: &LSPServer, + #[allow(unused)] + token_name: &str, + #[allow(unused)] + module: &core::hdlparam::Module, + #[allow(unused)] + def_path: &str +) -> Option { + None } diff --git a/src/request/mod.rs b/src/request/mod.rs index e34a4c6..d41fbc4 100644 --- a/src/request/mod.rs +++ b/src/request/mod.rs @@ -15,7 +15,7 @@ use crate::core::hdlparam::FastHdlparam; use crate::core::sv_parser::make_fast_from_syntaxtree; use crate::core::vhdl_parser::{make_fast_from_design_file, vhdl_parse}; -use crate::utils::*; +use crate::{core, utils::*}; use crate::server::Backend; use crate::sources::recovery_sv_parse_with_retry; @@ -107,7 +107,7 @@ pub fn do_fast( if file_type == "ip" && tool_chain == "xilinx" { let pathbuf = PathBuf::from_str(&path).unwrap(); let basename = pathbuf.file_name().unwrap().to_str().unwrap(); - format!("{}/{}.vho", path, basename) + format!("{}/synth/{}.vhd", path, basename) } else { path } @@ -222,14 +222,45 @@ fn do_vhdl_fast( ) -> Result { let sources = &backend.server.srcs; let pathbuf = PathBuf::from_str(path).unwrap(); + let hdl_param = sources.hdl_param.clone(); + + // TODO: 支持对于 synth 下的 vhdl 文件的解析,从而提供更加丰富的 IP 支持 + if file_type == "ip" { + match tool_chain { + "xilinx" => { + let ip_name = pathbuf.file_name().unwrap().to_str().unwrap(); + let ip_name = ip_name.strip_suffix(".vhd").unwrap(); + info!("get ip_name: {}", ip_name); + let fake_content = vec![ + core::hdlparam::Module { + name: ip_name.to_string(), + params: vec![], + ports: vec![], + instances: vec![], + range: core::hdlparam::Range::default() + } + ]; + let ip_fast = core::hdlparam::FastHdlparam { + fast_macro: core::hdlparam::Macro { + includes: vec![], + defines: vec![], + errors: vec![], + invalid: vec![] + }, + file_type: "ip".to_string(), + content: fake_content + }; + hdl_param.update_fast(path.to_string(), ip_fast.clone()); + return Ok(ip_fast); + }, + _ => {} + } + } + if let Some(design_file) = vhdl_parse(&pathbuf) { - let hdl_param = sources.hdl_param.clone(); if let Some(mut fast) = make_fast_from_design_file(&design_file) { fast.file_type = file_type.to_string(); hdl_param.update_fast(path.to_string(), fast.clone()); - // if file_type == "ip" { - // info!("ip debug, path: {}, fast: {:#?}", path, fast); - // } return Ok(fast); } }