Merge remote-tracking branch 'origin/add_vhdl'

This commit is contained in:
light-ly 2024-10-01 16:23:52 +08:00
commit d39399833b
12 changed files with 650 additions and 30 deletions

2
.gitignore vendored
View File

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

12
Cargo.lock generated
View File

@ -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",

@ -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

@ -158,6 +158,55 @@ pub fn make_fast_from_syntaxtree(syntax_tree: &SyntaxTree, path: &PathBuf) -> Re
}
}
}
RefNode::ParameterDeclaration(x) => {
if let Some(id) = unwrap_node!(x, ParameterIdentifier) {
let id = get_identifier(id).unwrap();
let name = syntax_tree.get_str(&id).unwrap();
match unwrap_node!(x, ParameterDeclarationParam, ParameterPortDeclarationParamList) {
Some(RefNode::ParameterDeclarationParam(param_node)) => {
// println!("{:?}", param_node);
let keyword_locate = param_node.nodes.0.nodes.0;
// println!("keyword {:#?}", keyword_locate);
let (start_line, start_character) = (id.line, get_column_by_offset(&content, keyword_locate.offset) as u32);
let (mut end_line, mut end_character) = (id.line, start_character + id.len as u32);
let net_type = match unwrap_node!(param_node, DataType) {
Some(RefNode::DataType(data_type)) => {
let id = unwrap_node!(data_type, SimpleIdentifier, Keyword);
if id == None {
"wire"
} else {
let id = get_identifier(id.unwrap()).unwrap();
syntax_tree.get_str(&id).unwrap()
}
}
_ => "wire"
};
let init = if let Some(init) = unwrap_node!(param_node, ConstantMintypmaxExpression) {
match init {
RefNode::ConstantMintypmaxExpression(expression) => {
// println!("expression {:?}", x);
let param_init = sv_parser::NeedParseExpression::Parameter(expression.clone());
let (exp, last_locate) = parse_expression(&syntax_tree, &param_init);
(end_line, end_character) = if last_locate != None {
// println!("param {:?} lastlocate {:?}", name, last_locate);
(last_locate.unwrap().line, (get_column_by_offset(&content, last_locate.unwrap().offset) + last_locate.unwrap().len) as u32)
} else {
(end_line, end_character)
};
// println!("end pos {} {}", end_line, end_character);
exp
}
_ => "unknown".to_string()
}
} else {
"unknown".to_string()
};
hdlparam.add_parameter(name, net_type, init.as_str(), start_line, start_character, end_line, end_character);
}
_ => ()
}
}
}
RefNode::PortDeclaration(x) => {
if let Some(id) = unwrap_node!(x, InputDeclaration, OutputDeclaration, InoutDeclaration) {
let id = get_identifier(id).unwrap();

View File

@ -1,6 +1,35 @@
use std::{path::PathBuf, str::FromStr};
use log::info;
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,
params: DocumentHighlightParams,
) -> 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 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, &params.text_document_position_params),
"verilog" | "systemverilog" => sv_document_highlight(
self,
&token,

View File

@ -1,5 +1,49 @@
use std::{path::PathBuf, str::FromStr};
use log::info;
use tower_lsp::lsp_types::*;
pub fn vhdl_document_highlight() -> Option<Vec<DocumentHighlight>> {
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<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 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
},
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,9 +1,46 @@
use std::{path::PathBuf, str::FromStr};
use log::info;
use tower_lsp::lsp_types::*;
use crate::server::LSPServer;
pub fn hover_vhdl(server: &LSPServer, param: &DocumentSymbolParams) -> Option<Hover> {
let design_file = server.srcs.design_file_map.write().unwrap();
use super::{from_lsp_pos, to_escape_path};
pub fn hover_vhdl(server: &LSPServer, params: &TextDocumentPositionParams) -> Option<Hover> {
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()?;
None
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))?;
let value = project.format_declaration(ent)?;
Some(Hover {
contents: HoverContents::Markup(MarkupContent {
kind: MarkupKind::Markdown,
value: format!("```vhdl\n{value}\n```"),
}),
range: None,
})
}

View File

@ -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<RwLock<Option<GenericScope>>>,
/// 存储 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
pub include_dirs: Arc<RwLock<Vec<PathBuf>>>,
// source directories
@ -567,7 +566,7 @@ pub fn sv_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>,
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);
}
}

View File

@ -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;
@ -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()),
}
}