230 lines
9.4 KiB
Rust
230 lines
9.4 KiB
Rust
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::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<GotoDefinitionResponse> {
|
|
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 <goto_include_definition>: {:?}", 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<GotoDefinitionResponse> {
|
|
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 mut target_range = macro_define.range.clone();
|
|
let target_range = target_range.affine(-1, -1).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<GotoDefinitionResponse> {
|
|
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.clone();
|
|
let target_range = target_range.affine(-1, -1).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<GotoDefinitionResponse> {
|
|
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_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 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);
|
|
return Some(links);
|
|
}
|
|
|
|
None
|
|
} |