159 lines
5.3 KiB
Rust
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(<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<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,
|
|
})
|
|
}
|