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 { 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> = 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> = 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 { 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 = 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::::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 { 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) }