规范 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 {
|
pub fn from_lsp_position(pos: &LspPosition) -> Position {
|
||||||
Position { line: pos.line, character: pos.character }
|
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)]
|
#[derive(Debug, Clone, Serialize, PartialEq, PartialOrd, Eq, Ord, Deserialize)]
|
||||||
@ -103,6 +109,13 @@ impl Range {
|
|||||||
Some(self.clone())
|
Some(self.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn default() -> Range {
|
||||||
|
Range {
|
||||||
|
start: Position::new(0, 0),
|
||||||
|
end: Position::new(0, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 比较两个 pos 的位置关系
|
/// 比较两个 pos 的位置关系
|
||||||
@ -437,6 +450,30 @@ impl HdlParam {
|
|||||||
None
|
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 定义的文件的路径
|
/// 输入 module 名字,找到 module 定义的文件的路径
|
||||||
pub fn find_module_definition_path(&self, module_name: &str) -> Option<String> {
|
pub fn find_module_definition_path(&self, module_name: &str) -> Option<String> {
|
||||||
let module_name_to_path = self.module_name_to_path.read().unwrap();
|
let module_name_to_path = self.module_name_to_path.read().unwrap();
|
||||||
|
@ -5,7 +5,7 @@ use regex::Regex;
|
|||||||
use ropey::RopeSlice;
|
use ropey::RopeSlice;
|
||||||
use tower_lsp::lsp_types::{GotoDefinitionResponse, LocationLink, Position, Range, Url};
|
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 的文件
|
/// 跳转到 include 的文件
|
||||||
pub fn goto_include_definition(uri: &Url, line: &RopeSlice, pos: Position) -> Option<GotoDefinitionResponse> {
|
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,
|
server: &LSPServer,
|
||||||
token_name: &str
|
token_name: &str
|
||||||
) -> Option<GotoDefinitionResponse> {
|
) -> Option<GotoDefinitionResponse> {
|
||||||
if let Some(module) = server.srcs.hdl_param.find_module_by_name(token_name) {
|
let hdl_param = server.srcs.hdl_param.clone();
|
||||||
let def_path = server.srcs.hdl_param.find_module_definition_path(&module.name).unwrap();
|
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_uri = Url::from_file_path(PathBuf::from_str(&def_path).unwrap()).unwrap();
|
||||||
|
|
||||||
let target_range = module.range.clone();
|
let target_range = module.range.clone();
|
||||||
@ -220,8 +262,49 @@ pub fn goto_module_declaration_definition(
|
|||||||
target_selection_range: target_range
|
target_selection_range: target_range
|
||||||
}];
|
}];
|
||||||
let links = GotoDefinitionResponse::Link(link);
|
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
|
None
|
||||||
}
|
}
|
@ -5,9 +5,9 @@ use regex::Regex;
|
|||||||
use ropey::RopeSlice;
|
use ropey::RopeSlice;
|
||||||
use tower_lsp::lsp_types::{Hover, HoverContents, LanguageString, MarkedString, Position, Range, Url};
|
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")
|
/// 将 4'b0011 分解为 ("b", "0011")
|
||||||
fn parse_digit_string(digit_string: &str) -> Option<(&str, &str)> {
|
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(
|
pub fn hover_module_declaration(
|
||||||
server: &LSPServer,
|
server: &LSPServer,
|
||||||
token_name: &str,
|
token_name: &str,
|
||||||
|
#[allow(unused)]
|
||||||
language_id: &str
|
language_id: &str
|
||||||
) -> Option<Hover> {
|
) -> 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);
|
// info!("token: {:?}, module info: {:?}", token_name, module_info);
|
||||||
|
|
||||||
// let test = server.srcs.hdl_param.module_name_to_path.read().unwrap();
|
// let test = server.srcs.hdl_param.module_name_to_path.read().unwrap();
|
||||||
// info!("module name to path: {:#?}", test);
|
// 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 {
|
fn hover_common_module_declaration(
|
||||||
let path_uri = Url::from_file_path(path_string.to_string()).unwrap().to_string();
|
#[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_row = module.range.start.line;
|
||||||
let def_col = module.range.start.character;
|
let def_col = module.range.start.character;
|
||||||
let define_info = format!("Go to [Definition]({path_uri}#L{def_row}:{def_col})");
|
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(define_info));
|
||||||
markdowns.push(MarkedString::String("---".to_string()));
|
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 module_profile = make_module_profile_code(&module);
|
||||||
let profile_markdown = LanguageString {
|
let profile_markdown = LanguageString {
|
||||||
language: language_id.to_string(),
|
language: language_id.to_string(),
|
||||||
@ -385,8 +417,31 @@ pub fn hover_module_declaration(
|
|||||||
};
|
};
|
||||||
|
|
||||||
return Some(hover);
|
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
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ use crate::core::hdlparam::FastHdlparam;
|
|||||||
use crate::core::sv_parser::make_fast_from_syntaxtree;
|
use crate::core::sv_parser::make_fast_from_syntaxtree;
|
||||||
|
|
||||||
use crate::core::vhdl_parser::{make_fast_from_design_file, vhdl_parse};
|
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::server::Backend;
|
||||||
use crate::sources::recovery_sv_parse_with_retry;
|
use crate::sources::recovery_sv_parse_with_retry;
|
||||||
|
|
||||||
@ -107,7 +107,7 @@ pub fn do_fast(
|
|||||||
if file_type == "ip" && tool_chain == "xilinx" {
|
if file_type == "ip" && tool_chain == "xilinx" {
|
||||||
let pathbuf = PathBuf::from_str(&path).unwrap();
|
let pathbuf = PathBuf::from_str(&path).unwrap();
|
||||||
let basename = pathbuf.file_name().unwrap().to_str().unwrap();
|
let basename = pathbuf.file_name().unwrap().to_str().unwrap();
|
||||||
format!("{}/{}.vho", path, basename)
|
format!("{}/synth/{}.vhd", path, basename)
|
||||||
} else {
|
} else {
|
||||||
path
|
path
|
||||||
}
|
}
|
||||||
@ -222,14 +222,45 @@ fn do_vhdl_fast(
|
|||||||
) -> Result<FastHdlparam> {
|
) -> Result<FastHdlparam> {
|
||||||
let sources = &backend.server.srcs;
|
let sources = &backend.server.srcs;
|
||||||
let pathbuf = PathBuf::from_str(path).unwrap();
|
let pathbuf = PathBuf::from_str(path).unwrap();
|
||||||
if let Some(design_file) = vhdl_parse(&pathbuf) {
|
|
||||||
let hdl_param = sources.hdl_param.clone();
|
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) {
|
if let Some(mut fast) = make_fast_from_design_file(&design_file) {
|
||||||
fast.file_type = file_type.to_string();
|
fast.file_type = file_type.to_string();
|
||||||
hdl_param.update_fast(path.to_string(), fast.clone());
|
hdl_param.update_fast(path.to_string(), fast.clone());
|
||||||
// if file_type == "ip" {
|
|
||||||
// info!("ip debug, path: {}, fast: {:#?}", path, fast);
|
|
||||||
// }
|
|
||||||
return Ok(fast);
|
return Ok(fast);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user