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..cb468f2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1203,18 +1203,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 +1223,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", 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/completion/vhdl.rs b/src/completion/vhdl.rs index e69de29..c24f3af 100644 --- a/src/completion/vhdl.rs +++ b/src/completion/vhdl.rs @@ -0,0 +1,234 @@ +use std::{path::PathBuf, str::FromStr}; + +use log::info; +use tower_lsp::lsp_types::*; +use vhdl_lang::{ast::{Designator, ObjectClass}, kind_str, AnyEntKind, Design, EntRef, InterfaceEnt, Overloaded}; +use crate::{server::LSPServer, utils::{from_lsp_pos, to_escape_path}}; + +/// Called when the client requests a completion. +/// This function looks in the source code to find suitable options and then returns them +pub fn request_completion(server: &LSPServer, params: &CompletionParams) -> Option { + let doc = ¶ms.text_document_position; + // let pos = doc.position; + + let file_id = server.srcs.get_id(&doc.text_document.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(&doc.text_document.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)?; + + let binding = escape_path; + let file = binding.as_path(); + // 1) get source position, and source file + let Some(source) = project.get_source(file) else { + // Do not enable completions for files that are not part of the project + return Some(CompletionList { + ..Default::default() + }); + }; + let cursor = from_lsp_pos(params.text_document_position.position); + // 2) Optimization chance: go to last recognizable token before the cursor. For example: + // - Any primary unit (e.g. entity declaration, package declaration, ...) + // => keyword `entity`, `package`, ... + // - Any secondary unit (e.g. package body, architecture) + // => keyword `architecture`, ... + + // 3) Run the parser until the point of the cursor. Then exit with possible completions + let options = project + .list_completion_options(&source, cursor) + .into_iter() + .map(|item| completion_item_to_lsp_item(server, item)) + .collect(); + + Some(CompletionList { + items: options, + is_incomplete: true, + }) +} + +fn completion_item_to_lsp_item( + server: &LSPServer, + item: vhdl_lang::CompletionItem, +) -> CompletionItem { + match item { + vhdl_lang::CompletionItem::Simple(ent) => entity_to_completion_item(ent), + vhdl_lang::CompletionItem::Work => CompletionItem { + label: "work".to_string(), + detail: Some("work library".to_string()), + kind: Some(CompletionItemKind::MODULE), + insert_text: Some("work".to_string()), + ..Default::default() + }, + vhdl_lang::CompletionItem::Formal(ent) => { + let mut item = entity_to_completion_item(ent); + // TODO: Confirm vscode support + // if server.client_supports_snippets() { + item.insert_text_format = Some(InsertTextFormat::SNIPPET); + item.insert_text = Some(format!("{} => $1,", item.insert_text.unwrap())); + // } + item + } + vhdl_lang::CompletionItem::Overloaded(desi, count) => CompletionItem { + label: desi.to_string(), + detail: Some(format!("+{count} overloaded")), + kind: match desi { + Designator::Identifier(_) => Some(CompletionItemKind::FUNCTION), + Designator::OperatorSymbol(_) => Some(CompletionItemKind::OPERATOR), + _ => None, + }, + insert_text: Some(desi.to_string()), + ..Default::default() + }, + vhdl_lang::CompletionItem::Keyword(kind) => CompletionItem { + label: kind_str(kind).to_string(), + detail: Some(kind_str(kind).to_string()), + insert_text: Some(kind_str(kind).to_string()), + kind: Some(CompletionItemKind::KEYWORD), + ..Default::default() + }, + vhdl_lang::CompletionItem::Instantiation(ent, architectures) => { + let work_name = "work"; + + let library_names = if let Some(lib_name) = ent.library_name() { + vec![work_name.to_string(), lib_name.name().to_string()] + } else { + vec![work_name.to_string()] + }; + let (region, is_component_instantiation) = match ent.kind() { + AnyEntKind::Design(Design::Entity(_, region)) => (region, false), + AnyEntKind::Component(region) => (region, true), + // should never happen but better return some value instead of crashing + _ => return entity_to_completion_item(ent), + }; + // TODO: Confirm vscode support + let template = if true { // if server.client_supports_snippets() { + let mut line = if is_component_instantiation { + format!("${{1:{}_inst}}: {}", ent.designator, ent.designator) + } else { + format!( + "${{1:{}_inst}}: entity ${{2|{}|}}.{}", + ent.designator, + library_names.join(","), + ent.designator + ) + }; + if architectures.len() > 1 { + line.push_str("(${3|"); + for (i, architecture) in architectures.iter().enumerate() { + line.push_str(&architecture.designator().to_string()); + if i != architectures.len() - 1 { + line.push(',') + } + } + line.push_str("|})"); + } + let (ports, generics) = region.ports_and_generics(); + let mut idx = 4; + let mut interface_ent = |elements: Vec, purpose: &str| { + line += &*format!("\n {} map(\n", purpose); + for (i, generic) in elements.iter().enumerate() { + line += &*format!( + " {} => ${{{}:{}}}", + generic.designator, idx, generic.designator + ); + idx += 1; + if i != elements.len() - 1 { + line += "," + } + line += "\n"; + } + line += ")"; + }; + if !generics.is_empty() { + interface_ent(generics, "generic"); + } + if !ports.is_empty() { + interface_ent(ports, "port"); + } + line += ";"; + line + } else { + format!("{}", ent.designator) + }; + CompletionItem { + label: format!("{} instantiation", ent.designator), + insert_text: Some(template), + insert_text_format: Some(InsertTextFormat::SNIPPET), + kind: Some(CompletionItemKind::MODULE), + ..Default::default() + } + } + vhdl_lang::CompletionItem::Attribute(attribute) => CompletionItem { + label: format!("{attribute}"), + detail: Some(format!("{attribute}")), + insert_text: Some(format!("{attribute}")), + kind: Some(CompletionItemKind::REFERENCE), + ..Default::default() + }, + } +} + +fn entity_to_completion_item(ent: EntRef) -> CompletionItem { + CompletionItem { + label: ent.designator.to_string(), + detail: Some(ent.describe()), + kind: Some(entity_kind_to_completion_kind(ent.kind())), + data: serde_json::to_value(ent.id.to_raw()).ok(), + insert_text: Some(ent.designator.to_string()), + ..Default::default() + } +} + + +fn entity_kind_to_completion_kind(kind: &AnyEntKind) -> CompletionItemKind { + match kind { + AnyEntKind::ExternalAlias { .. } | AnyEntKind::ObjectAlias { .. } => { + CompletionItemKind::FIELD + } + AnyEntKind::File(_) | AnyEntKind::InterfaceFile(_) => CompletionItemKind::FILE, + AnyEntKind::Component(_) => CompletionItemKind::MODULE, + AnyEntKind::Attribute(_) => CompletionItemKind::REFERENCE, + AnyEntKind::Overloaded(overloaded) => match overloaded { + Overloaded::SubprogramDecl(_) + | Overloaded::Subprogram(_) + | Overloaded::UninstSubprogramDecl(..) + | Overloaded::UninstSubprogram(..) + | Overloaded::InterfaceSubprogram(_) => CompletionItemKind::FUNCTION, + Overloaded::EnumLiteral(_) => CompletionItemKind::ENUM_MEMBER, + Overloaded::Alias(_) => CompletionItemKind::FIELD, + }, + AnyEntKind::Type(_) => CompletionItemKind::TYPE_PARAMETER, + AnyEntKind::ElementDeclaration(_) => CompletionItemKind::FIELD, + AnyEntKind::Concurrent(_) => CompletionItemKind::MODULE, + AnyEntKind::Sequential(_) => CompletionItemKind::MODULE, + AnyEntKind::Object(object) => match object.class { + ObjectClass::Signal => CompletionItemKind::EVENT, + ObjectClass::Constant => CompletionItemKind::CONSTANT, + ObjectClass::Variable | ObjectClass::SharedVariable => CompletionItemKind::VARIABLE, + }, + AnyEntKind::LoopParameter(_) => CompletionItemKind::MODULE, + AnyEntKind::PhysicalLiteral(_) => CompletionItemKind::UNIT, + AnyEntKind::DeferredConstant(_) => CompletionItemKind::CONSTANT, + AnyEntKind::Library => CompletionItemKind::MODULE, + AnyEntKind::Design(_) => CompletionItemKind::MODULE, + AnyEntKind::View(_) => CompletionItemKind::INTERFACE, + } +} diff --git a/src/definition/vhdl.rs b/src/definition/vhdl.rs index 399c669..06a423c 100644 --- a/src/definition/vhdl.rs +++ b/src/definition/vhdl.rs @@ -1,6 +1,35 @@ -use tower_lsp::lsp_types::*; -use crate::server::LSPServer; +use std::{path::PathBuf, str::FromStr}; -pub fn goto_vhdl_definition() { - +use log::info; +use tower_lsp::lsp_types::*; +use crate::{server::LSPServer, utils::{from_lsp_pos, srcpos_to_location, to_escape_path}}; + +pub fn goto_vhdl_definition(server: &LSPServer, params: TextDocumentPositionParams) -> 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)?; + + let ent = project.find_definition(&source, from_lsp_pos(params.position))?; + Some(srcpos_to_location(ent.decl_pos()?)) } \ No newline at end of file diff --git a/src/document_highlight/mod.rs b/src/document_highlight/mod.rs index ea9f358..1530789 100644 --- a/src/document_highlight/mod.rs +++ b/src/document_highlight/mod.rs @@ -12,7 +12,7 @@ impl LSPServer { &self, params: DocumentHighlightParams, ) -> Option> { - let uri = params.text_document_position_params.text_document.uri; + let uri = ¶ms.text_document_position_params.text_document.uri; let pos = params.text_document_position_params.position; let file_id = self.srcs.get_id(&uri).to_owned(); self.srcs.wait_parse_ready(file_id, false); @@ -24,7 +24,7 @@ impl LSPServer { let language_id = get_language_id_by_uri(&uri); match language_id.as_str() { - "vhdl" => vhdl_document_highlight(), + "vhdl" => vhdl_document_highlight(self, ¶ms.text_document_position_params), "verilog" | "systemverilog" => sv_document_highlight( self, &token, diff --git a/src/document_highlight/vhdl.rs b/src/document_highlight/vhdl.rs index 6eeca88..e62de6b 100644 --- a/src/document_highlight/vhdl.rs +++ b/src/document_highlight/vhdl.rs @@ -1,5 +1,49 @@ +use std::{path::PathBuf, str::FromStr}; + +use log::info; use tower_lsp::lsp_types::*; -pub fn vhdl_document_highlight() -> Option> { - None +use crate::{server::LSPServer, utils::{from_lsp_pos, to_escape_path, to_lsp_range}}; + +pub fn vhdl_document_highlight( + server: &LSPServer, + params: &TextDocumentPositionParams +) -> 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)?; + + let ent = project.find_declaration(&source, from_lsp_pos(params.position))?; + + Some( + project + .find_all_references_in_source(&source, ent) + .iter() + .map(|pos| DocumentHighlight { + range: to_lsp_range(pos.range()), + kind: Some(DocumentHighlightKind::TEXT), + }) + .collect(), + ) } \ No newline at end of file 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 5573a7f..7f60bbf 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 e96ec5a..2d83b0c 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, Concurrent, Object, Overloaded, SrcPos, Type}; pub mod fast; pub mod file; @@ -140,4 +141,124 @@ 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), + } +} + +pub fn srcpos_to_location(pos: &SrcPos) -> Location { + let uri = Url::from_file_path(pos.source.file_name()).unwrap(); + Location { + uri, + range: to_lsp_range(pos.range()), + } } \ No newline at end of file