规范 module 的 hover 和 definition 接口
This commit is contained in:
parent
fc34d22c82
commit
b14f8bd17f
@ -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();
|
||||
|
@ -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,77 @@ 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();
|
||||
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<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();
|
||||
@ -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<GotoDefinitionResponse> {
|
||||
|
||||
None
|
||||
}
|
@ -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,74 +319,129 @@ 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);
|
||||
|
||||
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<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})");
|
||||
|
||||
let mut markdowns = Vec::<MarkedString>::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,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let io_desc = format!("`input` {input_count}, `output` {output_count}, `inout` {inout_count}");
|
||||
|
||||
let mut markdowns = Vec::<MarkedString>::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<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
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user