226 lines
7.2 KiB
Rust
226 lines
7.2 KiB
Rust
use std::sync::RwLockReadGuard;
|
|
|
|
use crate::definition::*;
|
|
use crate::server::LSPServer;
|
|
use crate::sources::LSPSupport;
|
|
use crate::utils::*;
|
|
use log::info;
|
|
use regex::Regex;
|
|
use ropey::Rope;
|
|
use tower_lsp::lsp_types::*;
|
|
|
|
pub mod feature;
|
|
use feature::*;
|
|
|
|
pub mod vhdl;
|
|
|
|
impl LSPServer {
|
|
pub fn hover(&self, params: HoverParams) -> Option<Hover> {
|
|
let doc: Url = params.text_document_position_params.text_document.uri;
|
|
let pos: Position = params.text_document_position_params.position;
|
|
let file_id: usize = self.srcs.get_id(&doc).to_owned();
|
|
self.srcs.wait_parse_ready(file_id, false);
|
|
let file: std::sync::Arc<std::sync::RwLock<crate::sources::Source>> = self.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(self, &line_text, pos, &language_id) {
|
|
return Some(hover);
|
|
}
|
|
|
|
let scope_tree: RwLockReadGuard<'_, Option<GenericScope>> = self.srcs.scope_tree.read().ok()?;
|
|
let global_scope = scope_tree.as_ref();
|
|
|
|
// info!("get scope tree: {:?}", scope_tree);
|
|
|
|
// match positional port param
|
|
if let Some(hover) = hover_position_port_param(self, &line_text, &doc, pos, &language_id) {
|
|
return Some(hover);
|
|
}
|
|
|
|
// match module name
|
|
if let Some(hover) = hover_module_declaration(self, &token, &language_id) {
|
|
return Some(hover);
|
|
}
|
|
|
|
if let Some(global_scope) = global_scope {
|
|
|
|
// match 正常 symbol
|
|
if let Some(hover) = hover_common_symbol(self, 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) -> 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();
|
|
}
|
|
|
|
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));
|
|
} else {
|
|
// 这行只有代码,没有注释
|
|
comment_markdowns.push(MarkedString::LanguageString(LanguageString {
|
|
language: language_id.to_string(),
|
|
value: line_hover
|
|
}));
|
|
}
|
|
} else {
|
|
// 否则,都是上方的注释
|
|
comment_markdowns.push(MarkedString::String(line_hover));
|
|
}
|
|
}
|
|
|
|
if comment_markdowns.len() > 0 {
|
|
return Some(Hover {
|
|
contents: HoverContents::Array(comment_markdowns),
|
|
range: None
|
|
});
|
|
}
|
|
|
|
None
|
|
}
|
|
|
|
|
|
|
|
/// 计算正常 symbol 的 hover
|
|
fn hover_common_symbol(
|
|
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 的类别进行额外的判断
|
|
|
|
let def_line = file.text.byte_to_line(symbol_definition.byte_idx());
|
|
make_hover_with_comment(&file.text, def_line, &language_id)
|
|
}
|
|
|