2024-09-26 20:09:42 +08:00

159 lines
5.3 KiB
Rust

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<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) = match_include(&doc, &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();
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<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;
// 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<String> = Vec::new();
for i in hover {
if let Some(stripped) = i.strip_prefix(&ltrim) {
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<Hover> {
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<Hover> {
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,
})
}