diff --git a/.gitignore b/.gitignore index 4dcd28e..074a64a 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,6 @@ tests_rtl log_files test.txt -build.bat \ No newline at end of file +build.bat + +.DS_Store \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index a86bf14..03d8c33 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -281,6 +281,15 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +[[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" @@ -343,6 +352,7 @@ dependencies = [ "tokio", "tower-lsp", "vhdl_lang", + "vhdl_ls", "walkdir", "which", ] @@ -400,6 +410,29 @@ dependencies = [ "syn 2.0.28", ] +[[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.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c012a26a7f605efc424dd53697843a72be7dc86ad2d01f7814337794a12231d" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "humantime", + "log", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -528,6 +561,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.14" @@ -608,6 +650,12 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +[[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.57" @@ -725,6 +773,18 @@ version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" +[[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" @@ -738,6 +798,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.5.0" @@ -1203,18 +1276,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.179" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a5bf42b8d227d4abf38a1ddb08602e229108a517cd4e5bb28f9c7eaafdce5c0" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.179" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "741e124f5485c7e60c03b043f79f320bff3527f4bbf12cf3831750dc46a0ec2c" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", @@ -1223,9 +1296,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.104" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" +checksum = "cb0652c533506ad7a2e353cce269330d6afd8bdfb6d75e0ace5b35aacbd7b9e9" dependencies = [ "itoa", "ryu", @@ -1483,6 +1556,16 @@ dependencies = [ "syn 2.0.28", ] +[[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.6.0" @@ -1602,7 +1685,7 @@ dependencies = [ "dashmap", "futures", "httparse", - "lsp-types", + "lsp-types 0.94.1", "memchr", "serde", "serde_json", @@ -1760,6 +1843,22 @@ dependencies = [ "syn 2.0.28", ] +[[package]] +name = "vhdl_ls" +version = "0.83.0" +dependencies = [ + "clap 4.4.18", + "env_logger", + "fnv", + "fuzzy-matcher", + "log", + "lsp-server", + "lsp-types 0.95.1", + "serde", + "serde_json", + "vhdl_lang", +] + [[package]] name = "walkdir" version = "2.3.3" diff --git a/Cargo.toml b/Cargo.toml index 29788d0..f399886 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ edition = "2018" [dependencies] sv-parser = { version = "0.13.3", path = "sv-parser/sv-parser"} vhdl_lang = { version = "^0.83.0", path = "rust_hdl/vhdl_lang" } +vhdl_ls = { version = "^0.83.0", path = "rust_hdl/vhdl_ls" } once_cell = "1.8" percent-encoding = "2.1.0" log = "0.4.19" diff --git a/rust_hdl b/rust_hdl index f881ca1..d3f01fe 160000 --- a/rust_hdl +++ b/rust_hdl @@ -1 +1 @@ -Subproject commit f881ca161ac60accdf65295851ded9abf413da75 +Subproject commit d3f01fe8ec8d6d48031e3eaf043a65244297ca9c diff --git a/src/document_symbol/vhdl.rs b/src/document_symbol/vhdl.rs index bb8fb17..1701cb7 100644 --- a/src/document_symbol/vhdl.rs +++ b/src/document_symbol/vhdl.rs @@ -1,6 +1,109 @@ +use log::info; use tower_lsp::lsp_types::*; -use crate::server::LSPServer; +use crate::{server::LSPServer, utils::to_lsp_range}; +use crate::utils::{to_escape_path, to_symbol_kind}; -pub fn vhdl_document_symbol(server: &LSPServer, param: &DocumentSymbolParams) -> Option { - None +use std::path::PathBuf; +use std::str::FromStr; +use vhdl_lang::{EntHierarchy, Token}; + +pub fn vhdl_document_symbol(server: &LSPServer, params: &DocumentSymbolParams) -> Option { + let uri = ¶ms.text_document.uri; + let file_id = server.srcs.get_id(&uri).to_owned(); + server.srcs.wait_parse_ready(file_id, false); + let projects = server.srcs.design_file_map.read().ok()?; + + let path = match PathBuf::from_str(uri.path()) { + Ok(path) => path, + Err(error) => { + info!("error happen in : {:?}", error); + return None; + } + }; + let escape_path = to_escape_path(&path); + let escape_path_string = escape_path.to_str().unwrap_or(""); + if escape_path_string.len() == 0 { + info!("error happen in [vhdl_parser_pipeline], escape_path_string is empty"); + return None; + } + let project = match projects.get(escape_path_string) { + Some(project) => project, + None => return None + }; + + let source = project.get_source(&escape_path)?; + + // Some files are mapped to multiple libraries, only use the first library for document symbols + let library_name = project + .library_mapping_of(&source) + .into_iter() + .next()?; + + // TODO: Confirm vscode support + // if server.client_has_hierarchical_document_symbol_support() { + fn to_document_symbol( + EntHierarchy { ent, children }: EntHierarchy, + ctx: &Vec, + ) -> 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( + project + .document_symbols(&library_name, &source) + .into_iter() + .map(|(hierarchy, tokens)| to_document_symbol(hierarchy, tokens)) + .collect(), + )) + // } else { + // #[allow(clippy::ptr_arg)] + // fn to_symbol_information(ent: EntRef, ctx: &Vec) -> SymbolInformation { + // let selection_pos = ent.decl_pos().unwrap_or(ent.src_span.start_token.pos(ctx)); + // #[allow(deprecated)] + // SymbolInformation { + // name: ent.describe(), + // kind: to_symbol_kind(ent.kind()), + // tags: None, + // location: srcpos_to_location(selection_pos), + // deprecated: None, + // container_name: ent.parent_in_same_source().map(|ent| ent.describe()), + // } + // } + + // Some(DocumentSymbolResponse::Flat( + // project + // .document_symbols(&library_name, &source) + // .into_iter() + // .flat_map(|(a, ctx)| { + // a.into_flat() + // .into_iter() + // .map(|hierarchy| to_symbol_information(hierarchy, ctx)) + // }) + // .collect(), + // )) + // } } \ No newline at end of file diff --git a/src/sources.rs b/src/sources.rs index d0b4a3e..646a310 100644 --- a/src/sources.rs +++ b/src/sources.rs @@ -1,4 +1,3 @@ -use crate::core::fast_hdlparam::FastHdlparam; use crate::core::fast_hdlparam::HdlParam; use crate::core::sv_parser::make_fast_from_syntaxtree; use crate::core::vhdl_parser::make_fast_from_design_file; @@ -13,7 +12,7 @@ use log::info; use log::{debug, error}; use pathdiff::diff_paths; use ropey::{Rope, RopeSlice}; -use vhdl_lang::ast::DesignFile; +use vhdl_lang::MessagePrinter; use std::cmp::min; use std::collections::HashMap; use std::env::current_dir; @@ -130,7 +129,7 @@ pub struct Source { pub enum ParseIR { /// 基于 rust_hdl 的IR,存放 VHDL #[allow(unused)] - DesignFile(vhdl_lang::ast::DesignFile), + VHDLProject(vhdl_lang::Project), /// 存放 sv 的 IR SyntaxTree(sv_parser::SyntaxTree) } @@ -178,7 +177,7 @@ pub struct Sources { /// scope tree 类型的树形结构,用于提供 sv 的 lsp pub scope_tree: Arc>>, /// 存储 vhdl design file ir 的对象 - pub design_file_map: Arc>>, + pub design_file_map: Arc>>, // include directories, passed to parser to resolve `include pub include_dirs: Arc>>, // source directories @@ -567,7 +566,7 @@ pub fn sv_parser_pipeline( } pub fn vhdl_parser_pipeline( - design_file_handle: &Arc>>, + design_file_handle: &Arc>>, hdl_param_handle: &Arc, uri: &Url ) { @@ -589,7 +588,9 @@ pub fn vhdl_parser_pipeline( if let Some(fast) = make_fast_from_design_file(&design_file) { hdl_param_handle.update_fast(escape_path_string.to_string(), fast); } - design_files.insert(escape_path_string.to_string(), design_file); + let mut msg_printer = MessagePrinter::default(); + let project = vhdl_lang::Project::new_without_config(&escape_path, &mut msg_printer, None); + design_files.insert(escape_path_string.to_string(), project); } } diff --git a/src/utils/mod.rs b/src/utils/mod.rs index fc80ac5..823ef49 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -4,6 +4,7 @@ use percent_encoding::percent_decode_str; use regex::Regex; use ropey::RopeSlice; use tower_lsp::lsp_types::*; +use vhdl_lang::{ast::ObjectClass, AnyEntKind, Object, Overloaded, Type, Concurrent}; pub mod fast; @@ -139,4 +140,116 @@ pub fn to_escape_path(path: &PathBuf) -> PathBuf { PathBuf::from(decoded_path_str) }, } +} + +pub fn to_lsp_pos(position: vhdl_lang::Position) -> Position { + Position { + line: position.line, + character: position.character, + } +} + +pub fn to_lsp_range(range: vhdl_lang::Range) -> Range { + Range { + start: to_lsp_pos(range.start), + end: to_lsp_pos(range.end), + } +} + +pub fn to_symbol_kind(kind: &AnyEntKind) -> SymbolKind { + match kind { + AnyEntKind::ExternalAlias { class, .. } => object_class_kind(ObjectClass::from(*class)), + AnyEntKind::ObjectAlias { base_object, .. } => object_kind(base_object.object()), + AnyEntKind::Object(o) => object_kind(o), + AnyEntKind::LoopParameter(_) => SymbolKind::CONSTANT, + AnyEntKind::PhysicalLiteral(_) => SymbolKind::CONSTANT, + AnyEntKind::DeferredConstant(_) => SymbolKind::CONSTANT, + AnyEntKind::File { .. } => SymbolKind::FILE, + AnyEntKind::InterfaceFile { .. } => SymbolKind::INTERFACE, + AnyEntKind::Component(_) => SymbolKind::CLASS, + AnyEntKind::Attribute(_) => SymbolKind::PROPERTY, + AnyEntKind::Overloaded(o) => overloaded_kind(o), + AnyEntKind::Type(t) => type_kind(t), + AnyEntKind::ElementDeclaration(_) => SymbolKind::FIELD, + AnyEntKind::Sequential(_) => SymbolKind::NAMESPACE, + AnyEntKind::Concurrent(Some(Concurrent::Instance)) => SymbolKind::MODULE, + AnyEntKind::Concurrent(_) => SymbolKind::NAMESPACE, + AnyEntKind::Library => SymbolKind::NAMESPACE, + AnyEntKind::View(_) => SymbolKind::INTERFACE, + AnyEntKind::Design(d) => match d { + vhdl_lang::Design::Entity(_, _) => SymbolKind::MODULE, + vhdl_lang::Design::Architecture(..) => SymbolKind::MODULE, + vhdl_lang::Design::Configuration => SymbolKind::MODULE, + vhdl_lang::Design::Package(_, _) => SymbolKind::PACKAGE, + vhdl_lang::Design::PackageBody(..) => SymbolKind::PACKAGE, + vhdl_lang::Design::UninstPackage(_, _) => SymbolKind::PACKAGE, + vhdl_lang::Design::PackageInstance(_) => SymbolKind::PACKAGE, + vhdl_lang::Design::InterfacePackageInstance(_) => SymbolKind::PACKAGE, + vhdl_lang::Design::Context(_) => SymbolKind::NAMESPACE, + }, + } +} + +pub fn type_kind(t: &Type) -> SymbolKind { + match t { + vhdl_lang::Type::Array { .. } => SymbolKind::ARRAY, + vhdl_lang::Type::Enum(_) => SymbolKind::ENUM, + vhdl_lang::Type::Integer => SymbolKind::NUMBER, + vhdl_lang::Type::Real => SymbolKind::NUMBER, + vhdl_lang::Type::Physical => SymbolKind::NUMBER, + vhdl_lang::Type::Access(_) => SymbolKind::ENUM, + vhdl_lang::Type::Record(_) => SymbolKind::STRUCT, + vhdl_lang::Type::Incomplete => SymbolKind::NULL, + vhdl_lang::Type::Subtype(t) => type_kind(t.type_mark().kind()), + vhdl_lang::Type::Protected(_, _) => SymbolKind::CLASS, + vhdl_lang::Type::File => SymbolKind::FILE, + vhdl_lang::Type::Interface => SymbolKind::TYPE_PARAMETER, + vhdl_lang::Type::Alias(t) => type_kind(t.kind()), + vhdl_lang::Type::Universal(_) => SymbolKind::NUMBER, + } +} + +pub fn overloaded_kind(overloaded: &Overloaded) -> SymbolKind { + match overloaded { + Overloaded::SubprogramDecl(_) => SymbolKind::FUNCTION, + Overloaded::Subprogram(_) => SymbolKind::FUNCTION, + Overloaded::UninstSubprogramDecl(..) => SymbolKind::FUNCTION, + Overloaded::UninstSubprogram(..) => SymbolKind::FUNCTION, + Overloaded::InterfaceSubprogram(_) => SymbolKind::FUNCTION, + Overloaded::EnumLiteral(_) => SymbolKind::ENUM_MEMBER, + Overloaded::Alias(o) => overloaded_kind(o.kind()), + } +} + +pub fn object_kind(object: &Object) -> SymbolKind { + if matches!(object.subtype.type_mark().kind(), Type::Protected(..)) { + SymbolKind::OBJECT + } else if object.iface.is_some() { + SymbolKind::INTERFACE + } else { + object_class_kind(object.class) + } +} + +pub fn object_class_kind(class: ObjectClass) -> SymbolKind { + match class { + ObjectClass::Signal => SymbolKind::EVENT, + ObjectClass::Constant => SymbolKind::CONSTANT, + ObjectClass::Variable => SymbolKind::VARIABLE, + ObjectClass::SharedVariable => SymbolKind::VARIABLE, + } +} + +pub fn from_lsp_pos(position: Position) -> vhdl_lang::Position { + vhdl_lang::Position { + line: position.line, + character: position.character, + } +} + +pub fn from_lsp_range(range: Range) -> vhdl_lang::Range { + vhdl_lang::Range { + start: from_lsp_pos(range.start), + end: from_lsp_pos(range.end), + } } \ No newline at end of file