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(&ltrim) {
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)
}