use std::{path::PathBuf, str::FromStr}; use log::info; use regex::Regex; use ropey::RopeSlice; use tower_lsp::lsp_types::{GotoDefinitionResponse, LocationLink, Position, Range, Url}; 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 { let line_text = line.as_str().unwrap_or(""); if line_text.trim().starts_with("`include") { let character = pos.character as usize; let first_quote_idx = line_text.find("\""); let last_quote_idx = line_text.rfind("\""); if first_quote_idx.is_none() || last_quote_idx.is_none() { return None; } let first_quote_idx = first_quote_idx.unwrap(); let last_quote_idx = last_quote_idx.unwrap(); if character >= first_quote_idx && character <= last_quote_idx { let mut path_string = &line_text[(first_quote_idx + 1) .. last_quote_idx]; if path_string.starts_with("./") || path_string.starts_with(".\\") { path_string = &path_string[2 ..]; } // 路径转换 let path = match PathBuf::from_str(uri.path()) { Ok(path) => path, Err(error) => { info!("error happen in : {:?}", error); return None; } }; let escape_path = to_escape_path(&path); let escape_path = escape_path.to_str().unwrap_or(""); if escape_path.len() == 0 { return None; } if let Some(abs_path) = resolve_path(escape_path, path_string) { let target_uri = match Url::from_file_path(abs_path.as_path()) { Ok(uri) => uri, Err(_) => return None }; let origin_selection_range = Range::new( Position { line: pos.line, character: first_quote_idx as u32 }, Position { line: pos.line, character: (last_quote_idx + 1) as u32 } ); let target_position = Position { line: 0, character: 0 }; let target_range = Range::new(target_position, target_position); let link = vec![LocationLink { target_uri, origin_selection_range: Some(origin_selection_range), target_range, target_selection_range: target_range }]; let links = GotoDefinitionResponse::Link(link); return Some(links); } } } None } /// 跳转到宏定义 pub fn goto_macro_definition(server: &LspServer, line: &RopeSlice, pos: Position) -> Option { 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) { let define_path = PathBuf::from_str(&define_path).unwrap(); let target_uri = match Url::from_file_path(define_path) { Ok(uri) => uri, Err(_) => return None }; let target_range = macro_define.range.to_lsp_range(); let link = vec![LocationLink { target_uri, origin_selection_range: Some(range), target_range: target_range, target_selection_range: target_range }]; let links = GotoDefinitionResponse::Link(link); return Some(links); } } } None } fn goto_instantiation<'a>( server: &LspServer, fast: &'a FastHdlparam, token_name: &str, pos: &Position, range: &Range ) -> Option { for module in &fast.content { for instance in &module.instances { if let Some(param_range) = &instance.instparams { // let in_scope = compare_pos(&range.start, pos) != 1 && compare_pos(pos, &range.end) != 1; // info!("pos: {pos:?}, param_range: {range:?}, in_scope: {in_scope:?}"); if param_range.contains(pos) { let module = match server.db.hdl_param.find_module_by_name(&instance.inst_type) { Some(module) => module, None => return None }; for param in &module.params { if token_name == param.name { let def_path = server.db.hdl_param.find_module_definition_path(&module.name).unwrap(); let target_uri = Url::from_file_path(def_path).unwrap(); let target_range = param.range.clone(); let file_type = server.db.hdl_param.find_file_type_by_module_name(&instance.inst_type); let target_range = match file_type.as_str() { "common" => { target_range.to_lsp_range() } "ip" => { let mut target_range = target_range.clone(); target_range.affine(-1, -1).to_lsp_range() } "primitives" => { target_range.to_lsp_range() } _ => { target_range.to_lsp_range() } }; let link = vec![LocationLink { target_uri, origin_selection_range: Some(range.clone()), target_range: target_range, target_selection_range: target_range }]; let links = GotoDefinitionResponse::Link(link); return Some(links); } } return None; } } if let Some(port_range) = &instance.instports { // let in_scope = compare_pos(&range.start, pos) != 1 && compare_pos(pos, &range.end) != 1; // info!("pos: {pos:?}, port_range: {range:?}, in_scope: {in_scope:?}"); if port_range.contains(pos) { let module = match server.db.hdl_param.find_module_by_name(&instance.inst_type) { Some(module) => module, None => return None }; for port in &module.ports { if token_name == port.name { let def_path = server.db.hdl_param.find_module_definition_path(&module.name).unwrap(); let target_uri = Url::from_file_path(def_path).unwrap(); let target_range = port.range.clone(); let file_type = server.db.hdl_param.find_file_type_by_module_name(&instance.inst_type); let target_range = match file_type.as_str() { "common" => { target_range.to_lsp_range() } "ip" => { let mut target_range = target_range.clone(); target_range.affine(-1, -1).to_lsp_range() } "primitives" => { target_range.to_lsp_range() } _ => { target_range.to_lsp_range() } }; let link = vec![LocationLink { target_uri, origin_selection_range: Some(range.clone()), target_range: target_range, target_selection_range: target_range }]; let links = GotoDefinitionResponse::Link(link); return Some(links); } } return None; } } } } None } pub fn goto_position_port_param_definition( server: &LspServer, line: &RopeSlice, url: &Url, pos: Position ) -> Option { let position_port_regex = Regex::new(r"[._0-9a-zA-Z]").unwrap(); if let Some((name, range)) = get_word_range_at_position(line, pos, position_port_regex) { if name.starts_with(".") { let name = &name[1..]; // 进入最近的 scope 寻找 let fast_map = server.db.hdl_param.path_to_hdl_file.read().unwrap(); let path = PathBuf::from_str(url.path()).unwrap(); let path = to_escape_path(&path); let path_string = path.to_str().unwrap(); if let Some(hdl_file) = fast_map.get(path_string) { // 先找到属于哪一个 module let fast = &hdl_file.fast; if let Some(definition) = goto_instantiation(server, fast, name, &pos, &range) { return Some(definition); } } } } None } pub fn goto_module_declaration_definition( server: &LspServer, token_name: &str ) -> Option { let hdl_param = server.db.hdl_param.clone(); info!("get into goto_module_declaration_definition"); 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 mut target_range = module.range.clone(); let target_range = target_range.affine(-1, -1).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) } 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 }