294 lines
10 KiB
Rust
294 lines
10 KiB
Rust
use log::info;
|
|
use regex::Regex;
|
|
use ropey::Rope;
|
|
use tower_lsp::lsp_types::*;
|
|
use crate::{core::hdlparam::{HdlFile, Instance, Module}, hover::{to_escape_path, BracketMatchResult, BracketMatcher}, server::LSPServer, sources::LSPSupport};
|
|
use super::feature::*;
|
|
use std::{path::PathBuf, str::FromStr, sync::RwLockReadGuard};
|
|
|
|
use crate::definition::*;
|
|
|
|
use super::{get_definition_token, get_language_id_by_uri};
|
|
|
|
pub fn hover(server: &LSPServer, params: &HoverParams) -> Option<Hover> {
|
|
let doc = ¶ms.text_document_position_params.text_document.uri;
|
|
let pos: Position = params.text_document_position_params.position;
|
|
let file_id: usize = server.srcs.get_id(doc).to_owned();
|
|
server.srcs.wait_parse_ready(file_id, false);
|
|
let file: std::sync::Arc<std::sync::RwLock<crate::sources::Source>> = server.srcs.get_file(file_id)?;
|
|
let file: std::sync::RwLockReadGuard<'_, crate::sources::Source> = file.read().ok()?;
|
|
|
|
let line_text = file.text.line(pos.line as usize);
|
|
let token: String = get_definition_token(&line_text, pos);
|
|
let language_id = get_language_id_by_uri(doc);
|
|
|
|
// match `include
|
|
if let Some(hover) = hover_include(doc, &line_text, pos, &language_id) {
|
|
return Some(hover);
|
|
}
|
|
|
|
// match macro
|
|
if let Some(hover) = hover_macro(server, &line_text, pos, &language_id) {
|
|
return Some(hover);
|
|
}
|
|
|
|
let scope_tree = server.srcs.scope_tree.read().ok()?;
|
|
let global_scope = scope_tree.as_ref();
|
|
|
|
// match positional port param
|
|
if let Some(hover) = hover_position_port_param(server, &line_text, doc, pos, &language_id) {
|
|
return Some(hover);
|
|
}
|
|
|
|
// match module name
|
|
if let Some(hover) = hover_module_declaration(server, &token, &language_id) {
|
|
return Some(hover);
|
|
}
|
|
|
|
if let Some(global_scope) = global_scope {
|
|
|
|
// match 正常 symbol
|
|
if let Some(hover) = hover_common_symbol(server, global_scope, &token, &file, doc, pos, &language_id) {
|
|
return Some(hover);
|
|
}
|
|
}
|
|
|
|
// match digit 5'b00110
|
|
if let Some(hover) = hover_format_digit(&line_text, pos, &language_id) {
|
|
return Some(hover);
|
|
}
|
|
|
|
None
|
|
}
|
|
|
|
fn make_hover_with_comment(doc: &Rope, line: usize, language_id: &str, exclude_code: bool) -> Option<Hover> {
|
|
if line == 0 {
|
|
let language_string = LanguageString {
|
|
language: language_id.to_string(),
|
|
value: doc.line(line).to_string()
|
|
};
|
|
let markdown = MarkedString::LanguageString(language_string);
|
|
|
|
return Some(Hover {
|
|
contents: HoverContents::Scalar(markdown),
|
|
range: None,
|
|
});
|
|
}
|
|
let mut hover: Vec<String> = Vec::new();
|
|
let mut multiline: bool = false;
|
|
let mut valid: bool = true;
|
|
let mut current: String = doc.line(line).to_string();
|
|
|
|
let ltrim: String = " ".repeat(current.len() - current.trim_start().len());
|
|
let mut line_idx = line;
|
|
|
|
// 寻找周围的注释
|
|
while valid {
|
|
hover.push(current.clone());
|
|
line_idx -= 1;
|
|
valid = false;
|
|
if line_idx > 0 {
|
|
current = doc.line(line_idx).to_string();
|
|
let currentl = current.clone().trim_start().to_owned();
|
|
let currentr = current.clone().trim_end().to_owned();
|
|
if currentl.starts_with("/*") && currentr.ends_with("*/") {
|
|
valid = true;
|
|
} else if currentr.ends_with("*/") {
|
|
multiline = true;
|
|
valid = true;
|
|
} else if currentl.starts_with("/*") {
|
|
multiline = false;
|
|
valid = true;
|
|
} else {
|
|
valid = currentl.starts_with("//") || multiline;
|
|
}
|
|
}
|
|
}
|
|
hover.reverse();
|
|
|
|
let multi_space_regex = Regex::new(r"\s+").unwrap();
|
|
let line_handler = |line: &str| -> String {
|
|
let line = multi_space_regex.replace_all(line.trim(), " ");
|
|
let mut line = line.into_owned();
|
|
|
|
if line.starts_with("/*") {
|
|
line = line.strip_prefix("/*").unwrap().trim().to_string();
|
|
} else if line.starts_with("//") {
|
|
line = line.strip_prefix("//").unwrap().trim().to_string();
|
|
}
|
|
|
|
if line.ends_with("*/") {
|
|
line = line.strip_suffix("*/").unwrap().trim().to_string();
|
|
}
|
|
|
|
return format!("{}\n", line);
|
|
};
|
|
|
|
let line_comment_extractor = |line: &str| -> Option<(String, String)> {
|
|
let line = line.trim();
|
|
if line.contains("//") {
|
|
let comment_start = line.split_once("//");
|
|
match comment_start {
|
|
Some((code, comment)) => {
|
|
let code = code.trim().to_string();
|
|
let comment = comment.trim().to_string();
|
|
Some((code, comment))
|
|
},
|
|
None => None
|
|
}
|
|
} else if line.contains("/*") {
|
|
let comment_start = line.split_once("/*");
|
|
match comment_start {
|
|
Some((code, comment)) => {
|
|
let code = code.trim().to_string();
|
|
let comment = comment.trim().strip_suffix("*/").unwrap_or("").to_string();
|
|
Some((code, comment))
|
|
},
|
|
None => None
|
|
}
|
|
} else {
|
|
return None;
|
|
}
|
|
};
|
|
|
|
// 所有的 markdown
|
|
let mut comment_markdowns = Vec::<MarkedString>::new();
|
|
|
|
for (line_no, line_text) in hover.iter().enumerate() {
|
|
let line_hover = if let Some(stripped) = line_text.strip_prefix(<rim) {
|
|
line_handler(stripped)
|
|
} else {
|
|
line_handler(&line_text)
|
|
};
|
|
|
|
if line_no == hover.len() - 1 {
|
|
// 最后一个为定义所在的一行
|
|
if let Some((code, comment)) = line_comment_extractor(&line_text) {
|
|
comment_markdowns.push(MarkedString::LanguageString(LanguageString {
|
|
language: language_id.to_string(),
|
|
value: code
|
|
}));
|
|
comment_markdowns.insert(0, MarkedString::String(comment.trim().to_string()));
|
|
} else {
|
|
// 这行只有代码,没有注释
|
|
comment_markdowns.push(MarkedString::LanguageString(LanguageString {
|
|
language: language_id.to_string(),
|
|
value: line_hover
|
|
}));
|
|
}
|
|
} else {
|
|
// 否则,都是上方的注释
|
|
comment_markdowns.push(MarkedString::String(line_hover.trim().to_string()));
|
|
}
|
|
}
|
|
|
|
// 合并其中的 markdown
|
|
let mut merge_markdowns = Vec::<MarkedString>::new();
|
|
let mut string_buffer = String::new();
|
|
for (_, md) in comment_markdowns.iter().enumerate() {
|
|
match md {
|
|
MarkedString::String(markdown_string) => {
|
|
string_buffer.push_str(format!("{}\n", markdown_string.trim()).as_str());
|
|
}
|
|
MarkedString::LanguageString(code) => {
|
|
if !string_buffer.is_empty() {
|
|
merge_markdowns.push(MarkedString::String(string_buffer.to_string()));
|
|
string_buffer.clear();
|
|
}
|
|
|
|
if !exclude_code {
|
|
merge_markdowns.push(MarkedString::LanguageString(code.to_owned()));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if comment_markdowns.len() > 0 {
|
|
return Some(Hover {
|
|
contents: HoverContents::Array(merge_markdowns),
|
|
range: None
|
|
});
|
|
}
|
|
|
|
None
|
|
}
|
|
|
|
|
|
|
|
/// 计算正常 symbol 的 hover
|
|
fn hover_common_symbol(
|
|
#[allow(unused)]
|
|
server: &LSPServer,
|
|
scope_tree: &GenericScope,
|
|
token: &String,
|
|
file: &RwLockReadGuard<'_, crate::sources::Source>,
|
|
doc: &Url,
|
|
pos: Position,
|
|
language_id: &String
|
|
) -> Option<Hover> {
|
|
|
|
let symbol_definition: GenericDec = scope_tree
|
|
.get_definition(token, file.text.pos_to_byte(&pos), doc)?;
|
|
|
|
// 根据 symbol 的类别进行额外的判断
|
|
match symbol_definition.def_type {
|
|
DefinitionType::ModuleInstantiation => {
|
|
let hdlparam = server.srcs.hdl_param.clone();
|
|
let pathbuf = PathBuf::from_str(doc.path()).unwrap();
|
|
let pathbuf = to_escape_path(&pathbuf);
|
|
let path_string = pathbuf.to_str().unwrap().replace("\\", "/");
|
|
|
|
let find_name_condition = |_: &Module, instance: &Instance| {
|
|
symbol_definition.ident == instance.name
|
|
};
|
|
|
|
|
|
if let Some(instance) = hdlparam.walk_instantiation(&path_string, find_name_condition) {
|
|
info!("instance {:?}", instance);
|
|
let def_line = file.text.byte_to_line(symbol_definition.byte_idx());
|
|
let mut markdown_comment = match make_hover_with_comment(&file.text, def_line, &language_id, true) {
|
|
Some(hover) => {
|
|
match hover.contents {
|
|
HoverContents::Array(array) => array,
|
|
_ => Vec::<MarkedString>::new()
|
|
}
|
|
},
|
|
None => Vec::<MarkedString>::new()
|
|
};
|
|
|
|
// 扫描到右括号
|
|
let mut current_line = def_line;
|
|
let mut buffer = String::new();
|
|
let len_lines = file.text.len_lines();
|
|
let mut matcher = BracketMatcher::new();
|
|
|
|
loop {
|
|
if current_line >= len_lines {
|
|
break;
|
|
}
|
|
let line_text = file.text.line(current_line).to_string();
|
|
buffer.push_str(&line_text);
|
|
match matcher.consume_string(&line_text) {
|
|
BracketMatchResult::Invalid | BracketMatchResult::Complete => break,
|
|
BracketMatchResult::Valid => {}
|
|
}
|
|
|
|
current_line += 1;
|
|
}
|
|
markdown_comment.push(MarkedString::LanguageString(LanguageString {
|
|
language: language_id.to_string(),
|
|
value: buffer
|
|
}));
|
|
|
|
return Some(Hover { contents: HoverContents::Array(markdown_comment), range: None });
|
|
}
|
|
}
|
|
|
|
_ => {}
|
|
};
|
|
|
|
let def_line = file.text.byte_to_line(symbol_definition.byte_idx());
|
|
make_hover_with_comment(&file.text, def_line, &language_id, false)
|
|
}
|
|
|