use std::{fmt::format, sync::RwLockReadGuard}; use crate::definition::*; use crate::server::LSPServer; use crate::sources::LSPSupport; use crate::utils::*; use ropey::{Rope, RopeSlice}; use tower_lsp::lsp_types::*; pub mod feature; use feature::*; 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) = match_include(&doc, &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(); if let Some(global_scope) = global_scope { // match 正常 symbol if let Some(hover) = match_common_symbol(global_scope, &token, &file, &doc, pos, &language_id) { return Some(hover); } } // match digit 5'b00110 if let Some(hover) = match_format_digit(&line_text, pos) { return Some(hover); } None } } /// get the hover information fn get_hover(doc: &Rope, line: usize) -> String { if line == 0 { return doc.line(line).to_string(); } 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; // iterate upwards from the definition, and grab the comments 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 mut result: Vec = Vec::new(); for i in hover { if let Some(stripped) = i.strip_prefix(<rim) { result.push(stripped.to_owned()); } else { result.push(i); } } result.join("").trim_end().to_owned() } fn match_include(uri: &Url, line: &RopeSlice, pos: Position, language_id: &String) -> Option { 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 ..]; } if let Some(abs_path) = resolve_path(uri.path(), path_string) { let content = format!("{:?}", abs_path); let language_string = LanguageString { language: language_id.to_string(), value: content }; let markdown = MarkedString::LanguageString(language_string); return Some(Hover { contents: HoverContents::Scalar(markdown), range: None }) } } } None } fn match_common_symbol( scope_tree: &GenericScope, token: &String, file: &RwLockReadGuard<'_, crate::sources::Source>, doc: &Url, pos: Position, language_id: &String ) -> Option { let def: GenericDec = scope_tree .get_definition(token, file.text.pos_to_byte(&pos), doc)?; let def_line = file.text.byte_to_line(def.byte_idx()); let language_string = LanguageString { language: language_id.to_string(), value: get_hover(&file.text, def_line) }; let markdown = MarkedString::LanguageString(language_string); Some(Hover { contents: HoverContents::Scalar(markdown), range: None, }) }