规范 module 的 hover 和 definition 接口

This commit is contained in:
锦恢 2024-11-08 23:11:27 +08:00
parent fc34d22c82
commit b14f8bd17f
4 changed files with 274 additions and 68 deletions

View File

@ -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 的位置关系
@ -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<String> {
let module_name_to_path = self.module_name_to_path.read().unwrap();

View File

@ -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<GotoDefinitionResponse> {
@ -206,8 +206,50 @@ pub fn goto_module_declaration_definition(
server: &LSPServer,
token_name: &str
) -> Option<GotoDefinitionResponse> {
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<GotoDefinitionResponse> {
let target_uri = Url::from_file_path(PathBuf::from_str(&def_path).unwrap()).unwrap();
let target_range = module.range.clone();
@ -220,8 +262,49 @@ pub fn goto_module_declaration_definition(
target_selection_range: target_range
}];
let links = GotoDefinitionResponse::Link(link);
return Some(links);
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<GotoDefinitionResponse> {
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();
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)
} 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<GotoDefinitionResponse> {
None
}

View File

@ -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,27 +319,58 @@ 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<Hover> {
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);
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
}
}
if let Some((module, path_string)) = module_info {
let path_uri = Url::from_file_path(path_string.to_string()).unwrap().to_string();
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<Hover> {
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})");
@ -372,6 +403,7 @@ pub fn hover_module_declaration(
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(),
@ -387,6 +419,29 @@ pub fn hover_module_declaration(
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<Hover> {
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<Hover> {
None
}

View File

@ -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<FastHdlparam> {
let sources = &backend.server.srcs;
let pathbuf = PathBuf::from_str(path).unwrap();
if let Some(design_file) = vhdl_parse(&pathbuf) {
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) {
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);
}
}