迁移 lsp 逻辑

This commit is contained in:
锦恢 2024-09-29 22:08:18 +08:00
parent b3d7a9faa1
commit c3e88d63f9
8 changed files with 237 additions and 54 deletions

88
src/completion/feature.rs Normal file
View File

@ -0,0 +1,88 @@
use std::{fs, path::PathBuf, str::FromStr};
use log::info;
use ropey::RopeSlice;
use tower_lsp::lsp_types::{CompletionItem, CompletionItemKind, CompletionList, Position, Url};
use crate::utils::{resolve_path, to_escape_path};
pub fn include_path_completion(uri: &Url, line: &RopeSlice, pos: Position) -> Option<CompletionList> {
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 ..];
}
// 路径转换
let path = match PathBuf::from_str(uri.path()) {
Ok(path) => path,
Err(error) => {
info!("error happen in <goto_include_definition>: {:?}", error);
return None;
}
};
let escape_path = to_escape_path(&path);
let escape_path = escape_path.to_str().unwrap_or("");
if escape_path.len() == 0 {
return None;
}
if let Some(abs_path) = resolve_path(escape_path, path_string) {
// 遍历该路径下所有文件和文件夹,并构建新的自动补全列表
info!("completion active in {:?}", abs_path);
if abs_path.is_dir() {
let mut completion_items = Vec::<CompletionItem>::new();
for entry in fs::read_dir(abs_path).unwrap() {
let entry = entry.unwrap();
let path: PathBuf = entry.path();
let file_name = match path.file_name() {
Some(os_str) => os_str.to_str(),
None => continue
};
if file_name.is_none() {
continue;
}
let file_name = file_name.unwrap();
if path.is_dir() {
completion_items.push(CompletionItem {
label: file_name.to_string(),
kind: Some(CompletionItemKind::FOLDER),
..CompletionItem::default()
});
}
if path.is_file() {
completion_items.push(CompletionItem {
label: file_name.to_string(),
kind: Some(CompletionItemKind::FILE),
..CompletionItem::default()
});
}
}
if !completion_items.is_empty() {
return Some(CompletionList {
is_incomplete: false,
items: completion_items
});
}
}
}
}
}
None
}

View File

@ -133,7 +133,7 @@ pub const KEYWORDS: &[(&str, &str)] = &[
("implies", ""), ("implies", ""),
("import", ""), ("import", ""),
("incdir", ""), ("incdir", ""),
("include", ""), ("include", "`include \"$1\""),
("initial", ""), ("initial", ""),
("inout", ""), ("inout", ""),
("input", ""), ("input", ""),

View File

@ -1,13 +1,18 @@
use crate::server::LSPServer; use crate::server::LSPServer;
use crate::sources::LSPSupport; use crate::sources::LSPSupport;
use feature::include_path_completion;
use log::info;
use ropey::{Rope, RopeSlice}; use ropey::{Rope, RopeSlice};
use tower_lsp::lsp_types::*; use tower_lsp::lsp_types::*;
pub mod keyword; pub mod keyword;
pub mod feature;
impl LSPServer { impl LSPServer {
pub fn completion(&self, params: CompletionParams) -> Option<CompletionResponse> { pub fn completion(&self, params: CompletionParams) -> Option<CompletionResponse> {
let doc = params.text_document_position; let doc = params.text_document_position;
let pos = doc.position;
let file_id = self.srcs.get_id(&doc.text_document.uri).to_owned(); let file_id = self.srcs.get_id(&doc.text_document.uri).to_owned();
self.srcs.wait_parse_ready(file_id, false); self.srcs.wait_parse_ready(file_id, false);
let file = self.srcs.get_file(file_id)?; let file = self.srcs.get_file(file_id)?;
@ -19,6 +24,7 @@ impl LSPServer {
); );
// info!("trigger completion token: {}", token); // info!("trigger completion token: {}", token);
let line_text = file.text.line(pos.line as usize);
let response = match params.context { let response = match params.context {
Some(context) => match context.trigger_kind { Some(context) => match context.trigger_kind {
@ -37,6 +43,14 @@ impl LSPServer {
is_incomplete: false, is_incomplete: false,
items: self.directives.clone(), items: self.directives.clone(),
}), }),
"/" => {
info!("trigger include");
include_path_completion(&doc.text_document.uri, &line_text, pos)
},
"\"" => {
info!("trigger include");
include_path_completion(&doc.text_document.uri, &line_text, pos)
}
_ => None, _ => None,
} }
} }
@ -48,7 +62,6 @@ impl LSPServer {
&doc.text_document.uri, &doc.text_document.uri,
)?; )?;
// complete keywords // complete keywords
comps.items.extend::<Vec<CompletionItem>>( comps.items.extend::<Vec<CompletionItem>>(
self.key_comps self.key_comps
@ -100,6 +113,8 @@ impl LSPServer {
} }
} }
/// get the previous non-whitespace character /// get the previous non-whitespace character
fn prev_char(text: &Rope, pos: &Position) -> char { fn prev_char(text: &Rope, pos: &Position) -> char {
let char_idx = text.pos_to_char(pos); let char_idx = text.pos_to_char(pos);

View File

@ -1,4 +1,5 @@
use crate::sources::LSPSupport; use crate::sources::LSPSupport;
use log::info;
use ropey::Rope; use ropey::Rope;
use tower_lsp::lsp_types::*; use tower_lsp::lsp_types::*;
@ -61,26 +62,25 @@ pub fn copy_scopes(scopes: &[Box<dyn Scope>]) -> Vec<Box<dyn Scope>> {
scope_decs scope_decs
} }
/// A definition of any SystemVerilog variable or construct /// 用于定义一个 Symbol 或者 Scope 的内部变量
pub trait Definition: std::fmt::Debug + Sync + Send { pub trait Definition: std::fmt::Debug + Sync + Send {
// identifier /// symbol 或者 scope 的名字identity 的缩写
fn ident(&self) -> String; fn ident(&self) -> String;
// byte index in file of definition /// 相对于文本的偏移量,可以利用 Rope 的 `byte_to_pos` 函数转换成 Position
fn byte_idx(&self) -> usize; fn byte_idx(&self) -> usize;
// url pointing to the file the definition is in /// 当前 symbol 所在文件的 url
fn url(&self) -> Url; fn url(&self) -> Url;
// cleaned up text of the definition /// 输出 Definition 的字符
fn type_str(&self) -> String; fn type_str(&self) -> String;
// the kind of this definition, for use in completions /// 类别,用于自动补全
fn completion_kind(&self) -> CompletionItemKind; fn completion_kind(&self) -> CompletionItemKind;
// the kind of this definition, for use in showing document symbols /// 类别,用于文件大纲
// for some reason this kind is different than CompletionItemKind
fn symbol_kind(&self) -> SymbolKind; fn symbol_kind(&self) -> SymbolKind;
// the kind of this definition, simplified for internal use /// 类别,内部使用
fn def_type(&self) -> DefinitionType; fn def_type(&self) -> DefinitionType;
// whether the definition identifier starts with the given token /// 等价于 self.ident.starts_with
fn starts_with(&self, token: &str) -> bool; fn starts_with(&self, token: &str) -> bool;
// constructs the completion for this definition /// 构造 completion 的 item
fn completion(&self) -> CompletionItem; fn completion(&self) -> CompletionItem;
fn dot_completion(&self, scope_tree: &GenericScope) -> Vec<CompletionItem>; fn dot_completion(&self, scope_tree: &GenericScope) -> Vec<CompletionItem>;
} }
@ -173,17 +173,6 @@ pub trait Scope: std::fmt::Debug + Definition + Sync + Send {
Vec::new() Vec::new()
} }
/// 获取当前的 scope如果返回 None则说明当前 scope 为 global
// fn get_current_scope(&self, byte_idx: usize, url: &Url) -> Option<Box<dyn Scope>> {
// for scope in self.scopes() {
// if &scope.url() == url && scope.start() <= byte_idx && byte_idx <= scope.end() {
// return scope.get_current_scope(byte_idx, url);
// }
// }
// self
// }
/// 根据输入的 token计算出这个 token 在 scope 中的定义 /// 根据输入的 token计算出这个 token 在 scope 中的定义
/// 比如输入 clock则返回 clock 这个变量在哪里被定义,没有则返回 None /// 比如输入 clock则返回 clock 这个变量在哪里被定义,没有则返回 None
/// - `token`: 需要查找定义的完整的单词 /// - `token`: 需要查找定义的完整的单词
@ -192,6 +181,7 @@ pub trait Scope: std::fmt::Debug + Definition + Sync + Send {
fn get_definition(&self, token: &str, byte_idx: usize, url: &Url) -> Option<GenericDec> { fn get_definition(&self, token: &str, byte_idx: usize, url: &Url) -> Option<GenericDec> {
let mut definition: Option<GenericDec> = None; let mut definition: Option<GenericDec> = None;
// 递归进入最浅层的
for scope in self.scopes() { for scope in self.scopes() {
if &scope.url() == url && scope.start() <= byte_idx && byte_idx <= scope.end() { if &scope.url() == url && scope.start() <= byte_idx && byte_idx <= scope.end() {
definition = scope.get_definition(token, byte_idx, url); definition = scope.get_definition(token, byte_idx, url);
@ -203,6 +193,7 @@ pub trait Scope: std::fmt::Debug + Definition + Sync + Send {
// 优先找 定义,再找 scope // 优先找 定义,再找 scope
for def in self.defs() { for def in self.defs() {
if def.ident() == token { if def.ident() == token {
info!("find definition: {:?}", def);
return Some(GenericDec { return Some(GenericDec {
ident: def.ident(), ident: def.ident(),
byte_idx: def.byte_idx(), byte_idx: def.byte_idx(),
@ -210,7 +201,7 @@ pub trait Scope: std::fmt::Debug + Definition + Sync + Send {
type_str: def.type_str(), type_str: def.type_str(),
completion_kind: def.completion_kind(), completion_kind: def.completion_kind(),
symbol_kind: def.symbol_kind(), symbol_kind: def.symbol_kind(),
def_type: DefinitionType::Net, def_type: def.def_type(),
}); });
} }
} }

View File

@ -109,3 +109,7 @@ pub fn goto_macro_definition(server: &LSPServer, line: &RopeSlice, pos: Position
pub fn goto_instance_definition() { pub fn goto_instance_definition() {
} }
pub fn goto_module_definition() {
}

View File

@ -73,7 +73,7 @@ fn convert_to_sign_unsign<'a>(tag: &'a str, digit_string: &str) -> Option<(Strin
} }
/// 将 1'b1 翻译成 10进制 /// 将 1'b1 翻译成 10进制
pub fn match_format_digit(line: &RopeSlice, pos: Position, language_id: &str) -> Option<Hover> { pub fn hover_format_digit(line: &RopeSlice, pos: Position, language_id: &str) -> Option<Hover> {
let regex = Regex::new(r"[0-9'bho]").unwrap(); let regex = Regex::new(r"[0-9'bho]").unwrap();
let token_result = get_word_range_at_position(line, pos, regex); let token_result = get_word_range_at_position(line, pos, regex);

View File

@ -5,7 +5,7 @@ use crate::server::LSPServer;
use crate::sources::LSPSupport; use crate::sources::LSPSupport;
use crate::utils::*; use crate::utils::*;
use regex::Regex; use regex::Regex;
use ropey::Rope; use ropey::{Rope, RopeSlice};
use tower_lsp::lsp_types::*; use tower_lsp::lsp_types::*;
pub mod feature; pub mod feature;
@ -40,13 +40,13 @@ impl LSPServer {
if let Some(global_scope) = global_scope { if let Some(global_scope) = global_scope {
// match 正常 symbol // match 正常 symbol
if let Some(hover) = match_common_symbol(global_scope, &token, &file, &doc, pos, &language_id) { if let Some(hover) = hover_common_symbol(global_scope, &token, &file, &doc, pos, &language_id) {
return Some(hover); return Some(hover);
} }
} }
// match digit 5'b00110 // match digit 5'b00110
if let Some(hover) = match_format_digit(&line_text, pos, &language_id) { if let Some(hover) = hover_format_digit(&line_text, pos, &language_id) {
return Some(hover); return Some(hover);
} }
@ -57,10 +57,18 @@ impl LSPServer {
/// get the hover information fn make_hover_with_comment(doc: &Rope, line: usize, language_id: &str) -> Option<Hover> {
fn get_hover(doc: &Rope, line: usize) -> String {
if line == 0 { if line == 0 {
return doc.line(line).to_string(); 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 hover: Vec<String> = Vec::new();
let mut multiline: bool = false; let mut multiline: bool = false;
@ -97,24 +105,91 @@ fn get_hover(doc: &Rope, line: usize) -> String {
let multi_space_regex = Regex::new(r"\s+").unwrap(); let multi_space_regex = Regex::new(r"\s+").unwrap();
let line_handler = |line: &str| -> String { let line_handler = |line: &str| -> String {
let line = multi_space_regex.replace_all(line.trim(), " "); let line = multi_space_regex.replace_all(line.trim(), " ");
let line = line.into_owned(); 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); return format!("{}\n", line);
}; };
let mut result: Vec<String> = Vec::new(); let line_comment_extractor = |line: &str| -> Option<(String, String)> {
for line in hover { let line = line.trim();
if let Some(stripped) = line.strip_prefix(&ltrim) { if line.contains("//") {
let line_hover = line_handler(stripped); let comment_start = line.split_once("//");
result.push(line_hover); 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 { } else {
let line_hover = line_handler(&line); return None;
result.push(line_hover);
} }
};
// 所有的 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));
} }
result.join("").trim_end().to_owned()
} }
fn match_common_symbol( if comment_markdowns.len() > 0 {
return Some(Hover {
contents: HoverContents::Array(comment_markdowns),
range: None
});
}
None
}
/// 计算正常 symbol 的 hover
fn hover_common_symbol(
scope_tree: &GenericScope, scope_tree: &GenericScope,
token: &String, token: &String,
file: &RwLockReadGuard<'_, crate::sources::Source>, file: &RwLockReadGuard<'_, crate::sources::Source>,
@ -123,18 +198,26 @@ fn match_common_symbol(
language_id: &String language_id: &String
) -> Option<Hover> { ) -> Option<Hover> {
let def: GenericDec = scope_tree let symbol_definition: GenericDec = scope_tree
.get_definition(token, file.text.pos_to_byte(&pos), doc)?; .get_definition(token, file.text.pos_to_byte(&pos), doc)?;
let def_line = file.text.byte_to_line(def.byte_idx()); // 根据 symbol 的类别进行额外的判断
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), let def_line = file.text.byte_to_line(symbol_definition.byte_idx());
range: None, make_hover_with_comment(&file.text, def_line, &language_id)
}) }
/// 计算 position 赋值的 port 或者 param
/// 比如 .clk ( clk ) 中的 .
fn hover_position_port(line: &RopeSlice, pos: Position, language_id: &str) -> Option<Hover> {
let position_port_regex = Regex::new(r"[.0-9a-zA-Z]").unwrap();
if let Some((port_name, range)) = get_word_range_at_position(line, pos, position_port_regex) {
if port_name.starts_with(".") {
let port_name = &port_name[1..];
}
}
None
} }

View File

@ -196,6 +196,8 @@ impl LanguageServer for Backend {
".".to_string(), ".".to_string(),
"$".to_string(), "$".to_string(),
"`".to_string(), "`".to_string(),
"\"".to_string(),
"/".to_string()
]), ]),
work_done_progress_options: WorkDoneProgressOptions { work_done_progress_options: WorkDoneProgressOptions {
work_done_progress: None, work_done_progress: None,