refactor vhdl lsp service

This commit is contained in:
light-ly 2024-11-27 03:25:55 +08:00
parent c40e66f3df
commit 2fcfed4674
9 changed files with 373 additions and 30 deletions

102
Cargo.lock generated
View File

@ -286,6 +286,15 @@ version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
name = "crossbeam-channel"
version = "0.5.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.5"
@ -348,6 +357,7 @@ dependencies = [
"tokio",
"tower-lsp",
"vhdl_lang",
"vhdl_ls",
"walkdir",
"xml",
]
@ -426,6 +436,29 @@ dependencies = [
"syn 2.0.79",
]
[[package]]
name = "env_filter"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab"
dependencies = [
"log",
"regex",
]
[[package]]
name = "env_logger"
version = "0.11.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d"
dependencies = [
"anstream",
"anstyle",
"env_filter",
"humantime",
"log",
]
[[package]]
name = "equivalent"
version = "1.0.1"
@ -544,6 +577,15 @@ dependencies = [
"slab",
]
[[package]]
name = "fuzzy-matcher"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54614a3312934d066701a80f20f15fa3b56d67ac7722b39eea5b4c9dd1d66c94"
dependencies = [
"thread_local",
]
[[package]]
name = "getrandom"
version = "0.2.15"
@ -609,6 +651,12 @@ version = "1.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946"
[[package]]
name = "humantime"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
name = "iana-time-zone"
version = "0.1.61"
@ -720,6 +768,18 @@ version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
[[package]]
name = "lsp-server"
version = "0.7.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "550446e84739dcaf6d48a4a093973850669e13e8a34d8f8d64851041be267cd9"
dependencies = [
"crossbeam-channel",
"log",
"serde",
"serde_json",
]
[[package]]
name = "lsp-types"
version = "0.94.1"
@ -733,6 +793,19 @@ dependencies = [
"url",
]
[[package]]
name = "lsp-types"
version = "0.95.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e34d33a8e9b006cd3fc4fe69a921affa097bae4bb65f76271f4644f9a334365"
dependencies = [
"bitflags 1.3.2",
"serde",
"serde_json",
"serde_repr",
"url",
]
[[package]]
name = "memchr"
version = "2.7.4"
@ -1462,6 +1535,16 @@ dependencies = [
"syn 2.0.79",
]
[[package]]
name = "thread_local"
version = "1.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
dependencies = [
"cfg-if",
"once_cell",
]
[[package]]
name = "tinyvec"
version = "1.8.0"
@ -1578,7 +1661,7 @@ dependencies = [
"dashmap",
"futures",
"httparse",
"lsp-types",
"lsp-types 0.94.1",
"memchr",
"serde",
"serde_json",
@ -1736,6 +1819,23 @@ dependencies = [
"syn 2.0.79",
]
[[package]]
name = "vhdl_ls"
version = "0.83.0"
dependencies = [
"clap 4.5.19",
"env_logger",
"fnv",
"fuzzy-matcher",
"log",
"lsp-server",
"lsp-types 0.95.1",
"serde",
"serde_json",
"tower-lsp",
"vhdl_lang",
]
[[package]]
name = "walkdir"
version = "2.5.0"

View File

@ -6,6 +6,7 @@ edition = "2018"
[dependencies]
sv-parser = { version = "0.13.3", path = "sv-parser/sv-parser"}
vhdl_ls = { version = "^0.83.0", path = "vhdl-parser/vhdl_ls" }
vhdl_lang = { version = "^0.83.0", path = "vhdl-parser/vhdl_lang" }
dirs-next = "2.0"
bincode = "1.3"

View File

@ -1,8 +1,10 @@
use std::{path::PathBuf, str::FromStr};
#[allow(unused)]
use log::info;
use ropey::{Rope, RopeSlice};
use tower_lsp::lsp_types::*;
use crate::hover::feature::make_vhdl_module_profile_code;
use crate::{hover::feature::make_vhdl_module_profile_code, utils::{from_lsp_pos, to_escape_path}};
#[allow(unused)]
use crate::{server::LSPServer, sources::LSPSupport, utils::get_language_id_by_uri};
@ -26,6 +28,27 @@ pub fn completion(server: &LSPServer, params: &CompletionParams) -> Option<Compl
doc.position,
);
let project = server.srcs.vhdl_project.read().ok()?;
let global_project = project.as_ref().unwrap();
let path = match PathBuf::from_str(uri.path()) {
Ok(path) => path,
Err(error) => {
info!("error happen in vhdl <hover>: {:?}", error);
return None;
}
};
let escape_path = to_escape_path(&path);
let project_file = escape_path.as_path();
let Some(source) = global_project.project.get_source(project_file) else {
return None
};
let cursor = from_lsp_pos(params.text_document_position.position);
let vhdl_project_completion_items = global_project.project
.list_completion_options(&source, cursor)
.into_iter()
.map(|item| vhdl_ls::VHDLServer::completion_item_to_tower_lsp_item(item))
.collect::<Vec<CompletionItem>>();
let response = match &params.context {
Some(context) => match context.trigger_kind {
@ -65,6 +88,8 @@ pub fn completion(server: &LSPServer, params: &CompletionParams) -> Option<Compl
make_module_completions(server, &token, &language_id)
);
completion_items.items.extend(vhdl_project_completion_items);
// 去重
completion_items.items.dedup_by_key(|i| i.label.to_string());
Some(completion_items)
@ -100,6 +125,9 @@ pub fn completion(server: &LSPServer, params: &CompletionParams) -> Option<Compl
.cloned()
.collect(),
);
comps.items.extend(vhdl_project_completion_items);
comps.items.dedup_by_key(|i| i.label.clone());
Some(comps)
}

View File

@ -1,8 +1,12 @@
use crate::sources::LSPSupport;
use std::ffi::OsStr;
#[allow(unused)]
use log::info;
use ropey::Rope;
use tower_lsp::lsp_types::*;
use vhdl_lang::Project;
use crate::sources::LSPSupport;
/// cleanup the text of a definition so it can be included in completions
pub fn clean_type_str(type_str: &str, ident: &str) -> String {
@ -749,6 +753,12 @@ impl Definition for ModInst {
}
}
pub struct VhdlProject {
pub project: Project,
pub std_config: vhdl_lang::Config,
pub config_file_strs: Vec<String>
}
#[derive(Debug)]
pub struct GenericScope {
pub ident: String,

View File

@ -1,7 +1,9 @@
use std::{path::PathBuf, str::FromStr};
#[allow(unused)]
use log::info;
use tower_lsp::lsp_types::*;
use crate::{server::LSPServer, sources::LSPSupport, utils::get_definition_token};
use crate::{server::LSPServer, utils::{from_lsp_pos, get_definition_token, srcpos_to_location, to_escape_path}};
use super::{Definition, Scope};
@ -16,6 +18,29 @@ pub fn goto_vhdl_definition(server: &LSPServer, params: &GotoDefinitionParams) -
let line_text = file.text.line(pos.line as usize);
let token: String = get_definition_token(&line_text, pos);
let project = server.srcs.vhdl_project.read().ok()?;
let global_project = project.as_ref().unwrap();
let path = match PathBuf::from_str(doc.path()) {
Ok(path) => path,
Err(error) => {
info!("error happen in vhdl <hover>: {:?}", error);
return None;
}
};
let escape_path = to_escape_path(&path);
let project_file = escape_path.as_path();
let Some(source) = global_project.project.get_source(project_file) else {
return None
};
let ents = global_project.project.find_implementation(&source, from_lsp_pos(pos));
Some(GotoDefinitionResponse::Array(
ents.into_iter()
.filter_map(|ent| ent.decl_pos().map(srcpos_to_location))
.collect(),
))
// // match include
// if let Some(definition) = goto_include_definition(doc, &line_text, pos) {
// return Some(definition);
@ -28,7 +53,7 @@ pub fn goto_vhdl_definition(server: &LSPServer, params: &GotoDefinitionParams) -
// match instance
let scope_tree = server.srcs.scope_tree.read().ok()?;
// let scope_tree = server.srcs.scope_tree.read().ok()?;
// // match position port & param
// if let Some(definition) = goto_position_port_param_definition(server, &line_text, doc, pos) {
@ -40,13 +65,13 @@ pub fn goto_vhdl_definition(server: &LSPServer, params: &GotoDefinitionParams) -
// return Some(definition);
// }
let byte_idx = file.text.pos_to_byte(&pos);
let global_scope = scope_tree.as_ref()?;
let def = global_scope.get_definition(&token, byte_idx, doc)?;
// let byte_idx = file.text.pos_to_byte(&pos);
// let global_scope = scope_tree.as_ref()?;
// let def = global_scope.get_definition(&token, byte_idx, doc)?;
let def_pos = file.text.byte_to_pos(def.byte_idx());
Some(GotoDefinitionResponse::Scalar(Location::new(
def.url(),
Range::new(def_pos, def_pos),
)))
// let def_pos = file.text.byte_to_pos(def.byte_idx());
// Some(GotoDefinitionResponse::Scalar(Location::new(
// def.url(),
// Range::new(def_pos, def_pos),
// )))
}

View File

@ -1,7 +1,10 @@
use std::{path::PathBuf, str::FromStr};
#[allow(unused)]
use log::info;
use tower_lsp::lsp_types::*;
use crate::{definition::Scope, server::LSPServer};
use vhdl_lang::{EntHierarchy, Token};
use crate::{definition::Scope, server::LSPServer, utils::{to_escape_path, to_lsp_range, to_symbol_kind}};
pub fn document_symbol(server: &LSPServer, params: &DocumentSymbolParams) -> Option<DocumentSymbolResponse> {
// info!("enter document symbol");
@ -13,7 +16,66 @@ pub fn document_symbol(server: &LSPServer, params: &DocumentSymbolParams) -> Opt
let file = file.read().ok()?;
let scope_tree = server.srcs.scope_tree.read().ok()?;
Some(DocumentSymbolResponse::Nested(
scope_tree.as_ref()?.document_symbols(uri, &file.text),
))
let project = server.srcs.vhdl_project.read().ok()?;
let global_project = project.as_ref().unwrap();
let path = match PathBuf::from_str(uri.path()) {
Ok(path) => path,
Err(error) => {
info!("error happen in vhdl <hover>: {:?}", error);
return None;
}
};
let escape_path = to_escape_path(&path);
let project_file = escape_path.as_path();
let Some(source) = global_project.project.get_source(project_file) else {
return None
};
// Some files are mapped to multiple libraries, only use the first library for document symbols
let library_name = global_project.project
.library_mapping_of(&source)
.into_iter()
.next()?;
fn to_document_symbol(
EntHierarchy { ent, children }: EntHierarchy,
ctx: &Vec<Token>,
) -> DocumentSymbol {
// Use the declaration position, if it exists,
// else the position of the first source range token.
// The latter is applicable for unnamed elements, e.g., processes or loops.
let selection_pos = ent.decl_pos().unwrap_or(ent.src_span.start_token.pos(ctx));
let src_range = ent.src_span.pos(ctx).range();
#[allow(deprecated)]
DocumentSymbol {
name: ent.describe(),
kind: to_symbol_kind(ent.kind()),
tags: None,
detail: None,
selection_range: to_lsp_range(selection_pos.range),
range: to_lsp_range(src_range),
children: if !children.is_empty() {
Some(
children
.into_iter()
.map(|hierarchy| to_document_symbol(hierarchy, ctx))
.collect(),
)
} else {
None
},
deprecated: None,
}
}
Some(DocumentSymbolResponse::Nested(
global_project.project
.document_symbols(&library_name, &source)
.into_iter()
.map(|(hierarchy, tokens)| to_document_symbol(hierarchy, tokens))
.collect(),
))
// Some(DocumentSymbolResponse::Nested(
// scope_tree.as_ref()?.document_symbols(uri, &file.text),
// ))
}

View File

@ -6,7 +6,7 @@ use ropey::Rope;
use tower_lsp::lsp_types::*;
use crate::{core::hdlparam::{Instance, Module}, definition::{Definition, DefinitionType, GenericDec, Scope}, hover::{BracketMatchResult, BracketMatcher}, server::LSPServer, sources::LSPSupport};
use super::{feature::hover_format_digit, get_definition_token, get_language_id_by_uri, to_escape_path};
use super::{from_lsp_pos, get_definition_token, get_language_id_by_uri, to_escape_path};
pub fn hover(server: &LSPServer, params: &HoverParams) -> Option<Hover> {
let doc = &params.text_document_position_params.text_document.uri;
@ -20,6 +20,30 @@ pub fn hover(server: &LSPServer, params: &HoverParams) -> Option<Hover> {
let token: String = get_definition_token(&line_text, pos);
let language_id = get_language_id_by_uri(doc);
let project = server.srcs.vhdl_project.read().ok()?;
let global_project = project.as_ref().unwrap();
let path = match PathBuf::from_str(doc.path()) {
Ok(path) => path,
Err(error) => {
info!("error happen in vhdl <hover>: {:?}", error);
return None;
}
};
let escape_path = to_escape_path(&path);
let source = global_project.project.get_source(&escape_path)?;
let ent = global_project.project.find_declaration(&source, from_lsp_pos(pos))?;
let value = global_project.project.format_declaration(ent)?;
Some(Hover {
contents: HoverContents::Markup(MarkupContent {
kind: MarkupKind::Markdown,
value: format!("```vhdl\n{value}\n```"),
}),
range: None,
})
// // match `include
// if let Some(hover) = hover_include(doc, &line_text, pos, &language_id) {
// return Some(hover);
@ -30,11 +54,11 @@ pub fn hover(server: &LSPServer, params: &HoverParams) -> Option<Hover> {
// return Some(hover);
// }
let scope_tree = server.srcs.scope_tree.read().ok()?;
let global_scope = scope_tree.as_ref().unwrap();
// let scope_tree = server.srcs.scope_tree.read().ok()?;
// let global_scope = scope_tree.as_ref().unwrap();
let symbol_definition: GenericDec = global_scope
.get_definition(&token, file.text.pos_to_byte(&pos), doc)?;
// let symbol_definition: GenericDec = global_scope
// .get_definition(&token, file.text.pos_to_byte(&pos), doc)?;
// // match positional port param
// if let Some(hover) = hover_position_port_param(server, &line_text, doc, pos, &language_id) {
@ -47,16 +71,16 @@ pub fn hover(server: &LSPServer, params: &HoverParams) -> Option<Hover> {
// }
// match 正常 symbol
if let Some(hover) = hover_common_symbol(server, &token, &symbol_definition, &file, doc, pos, &language_id) {
return Some(hover);
}
// if let Some(hover) = hover_common_symbol(server, &token, &symbol_definition, &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);
}
// // match digit 5'b00110
// if let Some(hover) = hover_format_digit(&line_text, pos, &language_id) {
// return Some(hover);
// }
None
// None
}
fn make_hover_with_comment(doc: &Rope, line: usize, language_id: &str, exclude_code: bool) -> Option<Hover> {

View File

@ -22,11 +22,13 @@ use std::env::current_dir;
#[allow(unused)]
use std::ops::Deref;
use std::ops::Range as StdRange;
use std::path::Path;
use std::path::PathBuf;
use std::str::FromStr;
use std::sync::{Arc, Condvar, Mutex, RwLock};
use std::thread;
use sv_parser::*;
use vhdl_lang::Project;
use thread::JoinHandle;
use tower_lsp::lsp_types::*;
use walkdir::WalkDir;
@ -144,6 +146,32 @@ impl LSPServer {
}
}
{
let mut vhdl_project = self.srcs.vhdl_project.write().unwrap();
match &mut *vhdl_project {
Some(vhdl_project) => {
let config_file_strs = vhdl_project.config_file_strs.clone()
.into_iter()
.filter(|x| x != path_string)
.collect::<Vec<String>>();
let mut messages = Vec::new();
let config_str = format!(
r#"
[libraries]
digital_lsp.files = [{}]
"#, config_file_strs.join(",")
);
let config = vhdl_lang::Config::from_str(&config_str, Path::new(""));
if let Ok(mut config) = config {
config.append(&vhdl_project.std_config, &mut messages);
let mut project = Project::from_config(config, &mut messages);
project.analyse();
*vhdl_project = VhdlProject { project, std_config: vhdl_project.std_config.clone(), config_file_strs };
}
}
None => ()
}
}
}
}
}
@ -214,6 +242,8 @@ pub struct Sources {
pub meta: Arc<RwLock<Vec<Arc<RwLock<SourceMeta>>>>>,
/// scope tree 类型的树形结构,用于提供 sv 的 lsp
pub scope_tree: Arc<RwLock<Option<GenericScope>>>,
// vhdl project, store vhdl design files, do lsp
pub vhdl_project: Arc<RwLock<Option<VhdlProject>>>,
// include directories, passed to parser to resolve `include
pub include_dirs: Arc<RwLock<Vec<PathBuf>>>,
// primitive instance text
@ -239,6 +269,7 @@ impl Sources {
names: Arc::new(RwLock::new(HashMap::new())),
meta: Arc::new(RwLock::new(Vec::new())),
scope_tree: Arc::new(RwLock::new(None)),
vhdl_project: Arc::new(RwLock::new(None)),
include_dirs: Arc::new(RwLock::new(Vec::new())),
primitive_text: Arc::new(PrimitiveText::new()),
hdl_param: Arc::new(HdlParam::new()),
@ -280,6 +311,7 @@ impl Sources {
}));
let source_handle = source.clone();
let scope_handle = self.scope_tree.clone();
let project_handle = self.vhdl_project.clone();
let hdl_param_handle = self.hdl_param.clone();
let inc_dirs = self.include_dirs.clone();
@ -321,6 +353,7 @@ impl Sources {
&conf,
&source_handle,
&scope_handle,
&project_handle,
&hdl_param_handle,
&text,
uri,
@ -707,6 +740,7 @@ pub fn vhdl_parser_pipeline(
conf: &Arc<RwLock<ProjectConfig>>,
source_handle: &Arc<RwLock<Source>>,
scope_handle: &Arc<RwLock<Option<GenericScope>>>,
project_handle: &Arc<RwLock<Option<VhdlProject>>>,
hdl_param_handle: &Arc<HdlParam>,
doc: &Rope,
uri: &Url
@ -797,6 +831,65 @@ pub fn vhdl_parser_pipeline(
None => *global_scope = scope_tree,
}
// eprintln!("{:#?}", *global_scope);
let mut global_project = project_handle.write().unwrap();
match &mut *global_project {
Some(vhdl_project) => {
if let Some(source) = vhdl_project.project.get_source(&escape_path) {
source.change(None, &doc.to_string());
vhdl_project.project.update_source(&source);
vhdl_project.project.analyse();
} else {
vhdl_project.config_file_strs.push(format!("{:?}", escape_path));
let mut messages = Vec::new();
let config_str = format!(
r#"
[libraries]
digital_lsp.files = [{}]
"#, vhdl_project.config_file_strs.join(",")
);
let config = vhdl_lang::Config::from_str(&config_str, Path::new(""));
if let Ok(mut config) = config {
config.append(&vhdl_project.std_config, &mut messages);
let mut project = Project::from_config(config, &mut messages);
project.analyse();
*vhdl_project = VhdlProject { project, std_config: vhdl_project.std_config.clone(), config_file_strs: vhdl_project.config_file_strs.clone() };
}
}
}
None => {
let std_cfg_path = match PathBuf::from_str(&(extension_path + "/resources/dide-lsp/static/vhdl_libraries/vhdl_ls.toml")) {
Ok(path) => path,
Err(e) => {
info!("error happen in <vhdl_parse_pipeline>: {:?}", e);
return;
}
};
match vhdl_lang::Config::read_file_path(&std_cfg_path) {
Ok(std_config) => {
let config_str = format!(
r#"
[libraries]
digital_lsp.files = [{:?}]
"#, escape_path
);
let mut config_file_strs = Vec::new();
config_file_strs.push(format!("{:?}", escape_path));
let config = vhdl_lang::Config::from_str(&config_str, Path::new(""));
if let Ok(mut config) = config {
let mut messages = Vec::new();
config.append(&std_config, &mut messages);
let mut project = Project::from_config(config, &mut messages);
project.analyse();
*global_project = Some(VhdlProject { project, std_config, config_file_strs });
}
}
Err(e) => info!("error happen in <vhdl_parse_pipeline>: Can't load standard vhdl lib from {std_cfg_path:#?} because {e:#?}")
}
}
}
drop(global_project);
drop(global_scope);
}

@ -1 +1 @@
Subproject commit eb7ea05f13a133a175352da8ba537ac1717a35cf
Subproject commit 2446bd0a812d1667ad7033f10cd27a03967fa15e