diff --git a/src/core/fast_hdlparam.rs b/src/core/fast_hdlparam.rs index c5bca98..43e0285 100644 --- a/src/core/fast_hdlparam.rs +++ b/src/core/fast_hdlparam.rs @@ -24,6 +24,11 @@ pub struct Range { pub end: Position } +pub trait LikePosition { + fn line(&self) -> u32; + fn character(&self) -> u32; +} + impl Range { #[allow(unused)] pub fn to_lsp_range(&self) -> LspRange { @@ -31,6 +36,25 @@ impl Range { let end_pos= self.end.to_lsp_position(); LspRange::new(start_pos, end_pos) } + + /// 判断输入的 position 是否在当前 range 内 + pub fn contains(&self, postion: &LspPosition) -> bool { + compare_pos(&self.start.to_lsp_position(), postion) != 1 && compare_pos(postion, &self.end.to_lsp_position()) != 1 + } +} + +fn compare_pos(pos1: &LspPosition, pos2: &LspPosition) -> i32 { + if pos1.line > pos2.line { + 1 + } else if pos1.line < pos2.line { + -1 + } else if pos1.character > pos2.character { + 1 + } else if pos1.character < pos2.character { + -1 + } else { + 0 + } } #[derive(Debug, Serialize, PartialEq, Deserialize, Clone)] @@ -304,6 +328,15 @@ impl HdlParam { } None - } + } + + pub fn find_module_definition_path(&self, module_name: &str) -> Option { + let module_name_to_path = self.module_name_to_path.read().unwrap(); + if let Some(path) = module_name_to_path.get(module_name) { + return Some(path.to_string()); + } + + None + } } \ No newline at end of file diff --git a/src/definition/feature.rs b/src/definition/feature.rs index 1a65ad2..6989210 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::{server::LSPServer, utils::{get_word_range_at_position, resolve_path, to_escape_path}}; +use crate::{core::fast_hdlparam::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 { @@ -106,6 +106,112 @@ pub fn goto_macro_definition(server: &LSPServer, line: &RopeSlice, pos: Position } +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.srcs.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.srcs.hdl_param.find_module_definition_path(&module.name).unwrap(); + let target_uri = Url::from_file_path(def_path).unwrap(); + let mut target_range = param.range.to_lsp_range(); + target_range.start.line -= 1; + target_range.start.character -= 1; + target_range.end.line -= 1; + target_range.end.character -= 1; + + 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.srcs.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.srcs.hdl_param.find_module_definition_path(&module.name).unwrap(); + let target_uri = Url::from_file_path(def_path).unwrap(); + let mut target_range = port.range.to_lsp_range(); + target_range.start.line -= 1; + target_range.start.character -= 1; + target_range.end.line -= 1; + target_range.end.character -= 1; + + 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.srcs.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_instance_definition() { } diff --git a/src/definition/mod.rs b/src/definition/mod.rs index 4b43c3d..8caccec 100644 --- a/src/definition/mod.rs +++ b/src/definition/mod.rs @@ -44,6 +44,11 @@ impl LSPServer { let scope_tree = self.srcs.scope_tree.read().ok()?; + // match position port & param + if let Some(definition) = goto_position_port_param_definition(self, &line_text, &doc, pos) { + return Some(definition); + } + let byte_idx = file.text.pos_to_byte(&pos); let global_scope = scope_tree.as_ref()?; let def = global_scope.get_definition(&token, byte_idx, &doc)?; diff --git a/src/hover/feature.rs b/src/hover/feature.rs index 883daeb..739aa38 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::fast_hdlparam::Define, server::LSPServer}; +use crate::{core::fast_hdlparam::{Define, FastHdlparam}, server::LSPServer}; -use super::{get_word_range_at_position, resolve_path, to_escape_path}; +use super::{file::get_range_text, get_word_range_at_position, resolve_path, to_escape_path}; /// 将 4'b0011 分解为 ("b", "0011") fn parse_digit_string(digit_string: &str) -> Option<(&str, &str)> { @@ -202,3 +202,187 @@ pub fn hover_macro(server: &LSPServer, line: &RopeSlice, pos: Position, language } None } + + +fn goto_instantiation<'a>( + server: &LSPServer, + fast: &'a FastHdlparam, + token_name: &str, + pos: &Position, + range: &Range, + language_id: &str +) -> 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.srcs.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 hover = make_param_desc_hover(param, range, language_id); + return Some(hover); + } + } + return None; + } + } + + if let Some(port_range) = &instance.instports { + if port_range.contains(pos) { + let module = match server.srcs.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 hover = make_port_desc_hover(port, range, language_id); + return Some(hover); + } + } + return None; + } + } + } + } + + None +} + +/// 计算 position 赋值的 port 或者 param +/// 比如 .clk ( clk ) 中的 .clk +pub fn hover_position_port_param( + server: &LSPServer, + line: &RopeSlice, + url: &Url, + pos: Position, + language_id: &str +) -> 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.srcs.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(hover) = goto_instantiation(server, fast, name, &pos, &range, language_id) { + return Some(hover); + } + } + } + } + + None +} + +fn make_port_desc_hover(port: &crate::core::fast_hdlparam::Port, range: &Range, language_id: &str) -> Hover { + let mut port_desc_array = Vec::::new(); + port_desc_array.push(port.dir_type.to_string()); + if port.net_type != "unknown" { + port_desc_array.push(port.net_type.to_string()); + } + + if port.signed != "unsigned" { + port_desc_array.push("signed".to_string()); + } + + if port.width != "1" { + port_desc_array.push(port.width.to_string()); + } + + port_desc_array.push(port.name.to_string()); + let port_desc = port_desc_array.join(" "); + let language_string = LanguageString { + language: language_id.to_string(), + value: port_desc + }; + Hover { + contents: HoverContents::Scalar(MarkedString::LanguageString(language_string)), + range: Some(range.clone()) + } +} + +fn make_param_desc_hover(param: &crate::core::fast_hdlparam::Parameter, range: &Range, language_id: &str) -> Hover { + let mut param_desc_array = Vec::::new(); + param_desc_array.push(format!("parameter {}", param.name)); + + if param.init != "unknown" { + param_desc_array.push(param.init.to_string()); + } + + let param_desc = param_desc_array.join(" "); + let language_string = LanguageString { + language: language_id.to_string(), + value: param_desc + }; + Hover { + contents: HoverContents::Scalar(MarkedString::LanguageString(language_string)), + range: Some(range.clone()) + } +} + +pub fn hover_module_declaration( + server: &LSPServer, + token_name: &str, + language_id: &str +) -> Option { + if let Some(module) = server.srcs.hdl_param.find_module_by_name(token_name) { + 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 io_desc = format!(" input {input_count} output {output_count} inout {inout_count}"); + let desc_string = format!("{} | {}", port_desc, io_desc); + + let module_path = server.srcs.hdl_param.find_module_definition_path(&module.name).unwrap(); + let module_pathbuf = PathBuf::from_str(module_path.as_str()).unwrap(); + + let mut markdowns = Vec::::new(); + markdowns.push(MarkedString::String(desc_string)); + markdowns.push(MarkedString::String("".to_string())); + markdowns.push(MarkedString::String("---".to_string())); + + info!("module range: {:?}", module.range); + + if let Some(code) = get_range_text(&module_pathbuf, &module.range.to_lsp_range()) { + let code = LanguageString { + language: language_id.to_string(), + value: code + }; + markdowns.push(MarkedString::LanguageString(code)); + } + + let hover = Hover { + contents: HoverContents::Array(markdowns), + range: None + }; + + return Some(hover); + } + + None +} \ No newline at end of file diff --git a/src/hover/mod.rs b/src/hover/mod.rs index 8a120be..227c36b 100644 --- a/src/hover/mod.rs +++ b/src/hover/mod.rs @@ -1,13 +1,12 @@ -use std::collections::HashMap; -use std::{path::PathBuf, str::FromStr, sync::RwLockReadGuard}; +use std::sync::RwLockReadGuard; -use crate::{core::fast_hdlparam::FastHdlparam, definition::*}; +use crate::definition::*; use crate::server::LSPServer; use crate::sources::LSPSupport; use crate::utils::*; use log::info; use regex::Regex; -use ropey::{Rope, RopeSlice}; +use ropey::Rope; use tower_lsp::lsp_types::*; pub mod feature; @@ -48,9 +47,15 @@ impl LSPServer { return Some(hover); } + // match module name + if let Some(hover) = hover_module_declaration(self, &token, &language_id) { + return Some(hover); + } + if let Some(global_scope) = global_scope { + // match 正常 symbol - if let Some(hover) = hover_common_symbol(global_scope, &token, &file, &doc, pos, &language_id) { + if let Some(hover) = hover_common_symbol(self, global_scope, &token, &file, &doc, pos, &language_id) { return Some(hover); } } @@ -200,6 +205,7 @@ fn make_hover_with_comment(doc: &Rope, line: usize, language_id: &str) -> Option /// 计算正常 symbol 的 hover fn hover_common_symbol( + server: &LSPServer, scope_tree: &GenericScope, token: &String, file: &RwLockReadGuard<'_, crate::sources::Source>, @@ -217,193 +223,3 @@ fn hover_common_symbol( make_hover_with_comment(&file.text, def_line, &language_id) } -fn goto_instantiation<'a>( - fast: &'a FastHdlparam, - pos: &Position -) -> Option<&'a crate::core::fast_hdlparam::Instance> { - for module in &fast.content { - for instance in &module.instances { - - if let Some(param_range) = &instance.instparams { - let range = param_range.to_lsp_range(); - // 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 compare_pos(&range.start, pos) != 1 && compare_pos(pos, &range.end) != 1 { - return Some(instance); - } - } - - if let Some(port_range) = &instance.instports { - let range = port_range.to_lsp_range(); - // 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 compare_pos(&range.start, pos) != 1 && compare_pos(pos, &range.end) != 1 { - return Some(instance); - } - } - } - } - - None -} - -/// 计算 position 赋值的 port 或者 param -/// 比如 .clk ( clk ) 中的 .clk -fn hover_position_port_param( - server: &LSPServer, - line: &RopeSlice, - url: &Url, - pos: Position, - language_id: &str -) -> 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.srcs.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; - info!("find hdl file: {:?}", fast); - if let Some(instance) = goto_instantiation(fast, &pos) { - info!("find instance scope: {:?}", instance); - if let Some(hover) = hover_position_param(server, instance, &pos, &range, name, language_id) { - info!("hover_position_param return"); - return Some(hover); - } - if let Some(hover) = hover_position_port(server, instance, &pos, &range, name, language_id) { - info!("hover_position_port return"); - return Some(hover); - } - } - } - } - } - - None -} - -fn make_port_desc_hover(port: &crate::core::fast_hdlparam::Port, range: &Range, language_id: &str) -> Hover { - let mut port_desc_array = Vec::::new(); - port_desc_array.push(port.dir_type.to_string()); - if port.net_type != "unknown" { - port_desc_array.push(port.net_type.to_string()); - } - - if port.signed != "unsigned" { - port_desc_array.push("signed".to_string()); - } - - if port.width != "1" { - port_desc_array.push(port.width.to_string()); - } - - port_desc_array.push(port.name.to_string()); - let port_desc = port_desc_array.join(" "); - let language_string = LanguageString { - language: language_id.to_string(), - value: port_desc - }; - Hover { - contents: HoverContents::Scalar(MarkedString::LanguageString(language_string)), - range: Some(range.clone()) - } -} - -fn make_param_desc_hover(param: &crate::core::fast_hdlparam::Parameter, range: &Range, language_id: &str) -> Hover { - let mut param_desc_array = Vec::::new(); - param_desc_array.push(format!("parameter {}", param.name)); - - if param.init != "unknown" { - param_desc_array.push(param.init.to_string()); - } - - let param_desc = param_desc_array.join(" "); - let language_string = LanguageString { - language: language_id.to_string(), - value: param_desc - }; - Hover { - contents: HoverContents::Scalar(MarkedString::LanguageString(language_string)), - range: Some(range.clone()) - } -} - - -/// pos1 < pos2 -1 -/// pos1 = pos2 0 -/// pos1 > pos2 1 -fn compare_pos(pos1: &Position, pos2: &Position) -> i32 { - if pos1.line > pos2.line { - 1 - } else if pos1.line < pos2.line { - -1 - } else if pos1.character > pos2.character { - 1 - } else if pos1.character < pos2.character { - -1 - } else { - 0 - } -} - -fn hover_position_port( - server: &LSPServer, - instance: &crate::core::fast_hdlparam::Instance, - pos: &Position, - range: &Range, - token_name: &str, - language_id: &str -) -> Option { - - if let Some(inst_port_range) = &instance.instports { - let port_range = inst_port_range.to_lsp_range(); - - if compare_pos(&port_range.start, pos) != 1 && compare_pos(pos, &port_range.end) != 1 { - let module_name = &instance.inst_type; - // 确定当前光标在 inst port 内 - if let Some(module) = server.srcs.hdl_param.find_module_by_name(module_name) { - info!("find original module"); - for port in &module.ports { - if token_name == port.name { - // 制作 port 的 Hover - let hover = make_port_desc_hover(port, range, language_id); - return Some(hover); - } - } - } - } - } - None -} - -fn hover_position_param( - server: &LSPServer, - instance: &crate::core::fast_hdlparam::Instance, - pos: &Position, - range: &Range, - token_name: &str, - language_id: &str -) -> Option { - if let Some(inst_param_range) = &instance.instparams { - let param_range = inst_param_range.to_lsp_range(); - if compare_pos(¶m_range.start, pos) != 1 && compare_pos(pos, ¶m_range.end) != 1 { - let module_name = &instance.inst_type; - // 确定当前光标在 inst port 内 - if let Some(module) = server.srcs.hdl_param.find_module_by_name(module_name) { - for param in &module.params { - if token_name == param.name { - // 制作 port 的 Hover - let hover = make_param_desc_hover(param, range, language_id); - return Some(hover); - } - } - } - } - } - None -} \ No newline at end of file diff --git a/src/request/fsm.rs b/src/request/fsm.rs new file mode 100644 index 0000000..a097fd6 --- /dev/null +++ b/src/request/fsm.rs @@ -0,0 +1,4 @@ + +pub fn fsm() { + +} \ No newline at end of file diff --git a/src/request.rs b/src/request/mod.rs similarity index 100% rename from src/request.rs rename to src/request/mod.rs diff --git a/src/server.rs b/src/server.rs index 8a10ae2..d66d4a5 100644 --- a/src/server.rs +++ b/src/server.rs @@ -258,7 +258,6 @@ impl LanguageServer for Backend { .await; } - async fn completion(&self, params: CompletionParams) -> Result> { Ok(self.server.completion(params)) } diff --git a/src/utils/file.rs b/src/utils/file.rs new file mode 100644 index 0000000..29b0c67 --- /dev/null +++ b/src/utils/file.rs @@ -0,0 +1,29 @@ +use std::{fs, path::PathBuf, str::FromStr}; + +use log::info; +use ropey::Rope; +use tower_lsp::lsp_types::*; + +use crate::sources::LSPSupport; + +use super::to_escape_path; + +pub fn get_range_text(path: &PathBuf, range: &Range) -> Option { + let path = to_escape_path(path); + if let Some(rope) = open_doc_as_rope(&path) { + let start_index = rope.pos_to_byte(&range.start); + let end_index = rope.pos_to_byte(&range.end); + info!("start index: {}, end index: {}", start_index, end_index); + return Some(rope.slice(start_index..end_index).to_string()); + } + None +} + +pub fn open_doc_as_rope(path: &PathBuf) -> Option { + if let std::result::Result::Ok(text) = fs::read_to_string(path) { + let rope = Rope::from_str(text.as_str()); + Some(rope) + } else { + None + } +} \ No newline at end of file diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 043344c..2d83b0c 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -7,6 +7,7 @@ use tower_lsp::lsp_types::*; use vhdl_lang::{ast::ObjectClass, AnyEntKind, Concurrent, Object, Overloaded, SrcPos, Type}; pub mod fast; +pub mod file; /// 根据 pos 获取到当前光标所在的字符串,相当于 getWordRangeAtPosition pub fn get_definition_token(line: &RopeSlice, pos: Position) -> String {