Merge branch 'add_vhdl' of https://github.com/Digital-EDA/digital-lsp-server
This commit is contained in:
commit
dc368a508f
2
.gitignore
vendored
2
.gitignore
vendored
@ -11,3 +11,5 @@ log_files
|
||||
test.txt
|
||||
|
||||
build.bat
|
||||
|
||||
.DS_Store
|
12
Cargo.lock
generated
12
Cargo.lock
generated
@ -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",
|
||||
|
2
rust_hdl
2
rust_hdl
@ -1 +1 @@
|
||||
Subproject commit f881ca161ac60accdf65295851ded9abf413da75
|
||||
Subproject commit d3f01fe8ec8d6d48031e3eaf043a65244297ca9c
|
@ -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 = ¶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 <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,
|
||||
}
|
||||
}
|
@ -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 = ¶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 <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()?))
|
||||
}
|
@ -12,7 +12,7 @@ impl LSPServer {
|
||||
&self,
|
||||
params: DocumentHighlightParams,
|
||||
) -> Option<Vec<DocumentHighlight>> {
|
||||
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,
|
||||
|
@ -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 = ¶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 <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(),
|
||||
)
|
||||
}
|
@ -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 = ¶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 <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(),
|
||||
// ))
|
||||
// }
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
121
src/utils/mod.rs
121
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;
|
||||
@ -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()),
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user