This commit is contained in:
锦恢 2024-10-01 14:33:33 +08:00
commit dc368a508f
10 changed files with 559 additions and 25 deletions

2
.gitignore vendored
View File

@ -11,3 +11,5 @@ log_files
test.txt test.txt
build.bat build.bat
.DS_Store

12
Cargo.lock generated
View File

@ -1203,18 +1203,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.179" version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a5bf42b8d227d4abf38a1ddb08602e229108a517cd4e5bb28f9c7eaafdce5c0" checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.179" version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "741e124f5485c7e60c03b043f79f320bff3527f4bbf12cf3831750dc46a0ec2c" checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -1223,9 +1223,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.104" version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" checksum = "cb0652c533506ad7a2e353cce269330d6afd8bdfb6d75e0ace5b35aacbd7b9e9"
dependencies = [ dependencies = [
"itoa", "itoa",
"ryu", "ryu",

@ -1 +1 @@
Subproject commit f881ca161ac60accdf65295851ded9abf413da75 Subproject commit d3f01fe8ec8d6d48031e3eaf043a65244297ca9c

View File

@ -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<CompletionList> {
let doc = &params.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 <goto_include_definition>: {:?}", 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<InterfaceEnt>, 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,
}
}

View File

@ -1,6 +1,35 @@
use std::{path::PathBuf, str::FromStr};
use log::info;
use tower_lsp::lsp_types::*; use tower_lsp::lsp_types::*;
use crate::server::LSPServer; use crate::{server::LSPServer, utils::{from_lsp_pos, srcpos_to_location, to_escape_path}};
pub fn goto_vhdl_definition() { pub fn goto_vhdl_definition(server: &LSPServer, params: TextDocumentPositionParams) -> Option<Location> {
let uri = &params.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 <goto_include_definition>: {:?}", 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()?))
} }

View File

@ -12,7 +12,7 @@ impl LSPServer {
&self, &self,
params: DocumentHighlightParams, params: DocumentHighlightParams,
) -> Option<Vec<DocumentHighlight>> { ) -> Option<Vec<DocumentHighlight>> {
let uri = params.text_document_position_params.text_document.uri; let uri = &params.text_document_position_params.text_document.uri;
let pos = params.text_document_position_params.position; let pos = params.text_document_position_params.position;
let file_id = self.srcs.get_id(&uri).to_owned(); let file_id = self.srcs.get_id(&uri).to_owned();
self.srcs.wait_parse_ready(file_id, false); self.srcs.wait_parse_ready(file_id, false);
@ -24,7 +24,7 @@ impl LSPServer {
let language_id = get_language_id_by_uri(&uri); let language_id = get_language_id_by_uri(&uri);
match language_id.as_str() { match language_id.as_str() {
"vhdl" => vhdl_document_highlight(), "vhdl" => vhdl_document_highlight(self, &params.text_document_position_params),
"verilog" | "systemverilog" => sv_document_highlight( "verilog" | "systemverilog" => sv_document_highlight(
self, self,
&token, &token,

View File

@ -1,5 +1,49 @@
use std::{path::PathBuf, str::FromStr};
use log::info;
use tower_lsp::lsp_types::*; use tower_lsp::lsp_types::*;
pub fn vhdl_document_highlight() -> Option<Vec<DocumentHighlight>> { use crate::{server::LSPServer, utils::{from_lsp_pos, to_escape_path, to_lsp_range}};
None
pub fn vhdl_document_highlight(
server: &LSPServer,
params: &TextDocumentPositionParams
) -> Option<Vec<DocumentHighlight>> {
let uri = &params.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 <goto_include_definition>: {:?}", 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(),
)
} }

View File

@ -1,6 +1,109 @@
use log::info;
use tower_lsp::lsp_types::*; 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<DocumentSymbolResponse> { use std::path::PathBuf;
use std::str::FromStr;
use vhdl_lang::{EntHierarchy, Token};
pub fn vhdl_document_symbol(server: &LSPServer, params: &DocumentSymbolParams) -> Option<DocumentSymbolResponse> {
let uri = &params.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 <goto_include_definition>: {:?}", 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<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 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<Token>) -> 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(),
// ))
// }
} }

View File

@ -1,4 +1,3 @@
use crate::core::fast_hdlparam::FastHdlparam;
use crate::core::fast_hdlparam::HdlParam; use crate::core::fast_hdlparam::HdlParam;
use crate::core::sv_parser::make_fast_from_syntaxtree; use crate::core::sv_parser::make_fast_from_syntaxtree;
use crate::core::vhdl_parser::make_fast_from_design_file; use crate::core::vhdl_parser::make_fast_from_design_file;
@ -13,7 +12,7 @@ use log::info;
use log::{debug, error}; use log::{debug, error};
use pathdiff::diff_paths; use pathdiff::diff_paths;
use ropey::{Rope, RopeSlice}; use ropey::{Rope, RopeSlice};
use vhdl_lang::ast::DesignFile; use vhdl_lang::MessagePrinter;
use std::cmp::min; use std::cmp::min;
use std::collections::HashMap; use std::collections::HashMap;
use std::env::current_dir; use std::env::current_dir;
@ -130,7 +129,7 @@ pub struct Source {
pub enum ParseIR { pub enum ParseIR {
/// 基于 rust_hdl 的IR存放 VHDL /// 基于 rust_hdl 的IR存放 VHDL
#[allow(unused)] #[allow(unused)]
DesignFile(vhdl_lang::ast::DesignFile), VHDLProject(vhdl_lang::Project),
/// 存放 sv 的 IR /// 存放 sv 的 IR
SyntaxTree(sv_parser::SyntaxTree) SyntaxTree(sv_parser::SyntaxTree)
} }
@ -178,7 +177,7 @@ pub struct Sources {
/// scope tree 类型的树形结构,用于提供 sv 的 lsp /// scope tree 类型的树形结构,用于提供 sv 的 lsp
pub scope_tree: Arc<RwLock<Option<GenericScope>>>, pub scope_tree: Arc<RwLock<Option<GenericScope>>>,
/// 存储 vhdl design file ir 的对象 /// 存储 vhdl design file ir 的对象
pub design_file_map: Arc<RwLock<HashMap<String, DesignFile>>>, pub design_file_map: Arc<RwLock<HashMap<String, vhdl_lang::Project>>>,
// include directories, passed to parser to resolve `include // include directories, passed to parser to resolve `include
pub include_dirs: Arc<RwLock<Vec<PathBuf>>>, pub include_dirs: Arc<RwLock<Vec<PathBuf>>>,
// source directories // source directories
@ -567,7 +566,7 @@ pub fn sv_parser_pipeline(
} }
pub fn vhdl_parser_pipeline( pub fn vhdl_parser_pipeline(
design_file_handle: &Arc<RwLock<HashMap<String, DesignFile>>>, design_file_handle: &Arc<RwLock<HashMap<String, vhdl_lang::Project>>>,
hdl_param_handle: &Arc<HdlParam>, hdl_param_handle: &Arc<HdlParam>,
uri: &Url uri: &Url
) { ) {
@ -589,7 +588,9 @@ pub fn vhdl_parser_pipeline(
if let Some(fast) = make_fast_from_design_file(&design_file) { if let Some(fast) = make_fast_from_design_file(&design_file) {
hdl_param_handle.update_fast(escape_path_string.to_string(), fast); 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);
} }
} }

View File

@ -4,6 +4,7 @@ use percent_encoding::percent_decode_str;
use regex::Regex; use regex::Regex;
use ropey::RopeSlice; use ropey::RopeSlice;
use tower_lsp::lsp_types::*; use tower_lsp::lsp_types::*;
use vhdl_lang::{ast::ObjectClass, AnyEntKind, Concurrent, Object, Overloaded, SrcPos, Type};
pub mod fast; pub mod fast;
pub mod file; pub mod file;
@ -141,3 +142,123 @@ pub fn to_escape_path(path: &PathBuf) -> PathBuf {
}, },
} }
} }
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()),
}
}