合并 vhdl
This commit is contained in:
parent
65983f7a43
commit
58ef3cafc8
571
Cargo.lock
generated
571
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
2
Cross.toml
Normal file
2
Cross.toml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
[target.x86_64-apple-darwin]
|
||||||
|
image = "ghcr.io/shepherdjerred/macos-cross-compiler:latest"
|
14
build_all.sh
Normal file
14
build_all.sh
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# linux amd64
|
||||||
|
cargo build --release
|
||||||
|
# linux aarch64
|
||||||
|
cross build --release --target aarch64-unknown-linux-gnu
|
||||||
|
# windows amd64
|
||||||
|
cross build --release --target x86_64-pc-windows-gnu
|
||||||
|
# windows aarch64
|
||||||
|
cargo xwin build --release --target aarch64-pc-windows-msvc
|
||||||
|
# MacOS amd64
|
||||||
|
export CARGO_TARGET_X86_64_APPLE_DARWIN_LINKER=rust-lld
|
||||||
|
cross build --release --target x86_64-apple-darwin
|
||||||
|
# MacOS aarch64
|
||||||
|
export CARGO_TARGET_AARCH64_APPLE_DARWIN_LINKER=rust-lld
|
||||||
|
cross build --release --target aarch64-apple-darwin
|
@ -1,231 +1,181 @@
|
|||||||
use std::{path::PathBuf, str::FromStr};
|
#[allow(unused)]
|
||||||
|
|
||||||
use log::info;
|
use log::info;
|
||||||
|
use ropey::{Rope, RopeSlice};
|
||||||
use tower_lsp::lsp_types::*;
|
use tower_lsp::lsp_types::*;
|
||||||
use vhdl_lang::{ast::{Designator, ObjectClass}, kind_str, AnyEntKind, Design, EntRef, InterfaceEnt, Overloaded};
|
#[allow(unused)]
|
||||||
use crate::{server::LSPServer, utils::{from_lsp_pos, to_escape_path}};
|
use crate::{server::LSPServer, sources::LSPSupport, utils::get_language_id_by_uri};
|
||||||
|
|
||||||
/// Called when the client requests a completion.
|
/// Called when the client requests a completion.
|
||||||
/// This function looks in the source code to find suitable options and then returns them
|
/// This function looks in the source code to find suitable options and then returns them
|
||||||
pub fn completion(server: &LSPServer, params: &CompletionParams) -> Option<CompletionResponse> {
|
pub fn completion(server: &LSPServer, params: &CompletionParams) -> Option<CompletionResponse> {
|
||||||
let doc = ¶ms.text_document_position;
|
let doc = ¶ms.text_document_position;
|
||||||
|
let uri = ¶ms.text_document_position.text_document.uri;
|
||||||
// let pos = doc.position;
|
// let pos = doc.position;
|
||||||
|
// let language_id = get_language_id_by_uri(uri);
|
||||||
|
|
||||||
let file_id = server.srcs.get_id(&doc.text_document.uri).to_owned();
|
let file_id = server.srcs.get_id(uri).to_owned();
|
||||||
server.srcs.wait_parse_ready(file_id, false);
|
server.srcs.wait_parse_ready(file_id, false);
|
||||||
let projects = server.srcs.design_file_map.read().ok()?;
|
let file = server.srcs.get_file(file_id)?;
|
||||||
|
let file = file.read().ok()?;
|
||||||
|
let line_text = file.text.line(doc.position.line as usize);
|
||||||
|
let token = get_completion_token(
|
||||||
|
&file.text,
|
||||||
|
line_text.clone(),
|
||||||
|
doc.position,
|
||||||
|
);
|
||||||
|
|
||||||
let path = match PathBuf::from_str(&doc.text_document.uri.path()) {
|
// info!("trigger completion token: {}", token);
|
||||||
Ok(path) => path,
|
// let line_text = file.text.line(pos.line as usize);
|
||||||
Err(error) => {
|
|
||||||
info!("error happen in <goto_include_definition>: {:?}", error);
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let escape_path = to_escape_path(&path);
|
|
||||||
|
|
||||||
let project = match projects.get("VHDLProject") {
|
let response = match ¶ms.context {
|
||||||
Some(project) => project,
|
Some(context) => match context.trigger_kind {
|
||||||
None => return None
|
// CompletionTriggerKind::TRIGGER_CHARACTER => {
|
||||||
};
|
// let trigger_character = context.trigger_character.clone().unwrap();
|
||||||
|
// match trigger_character.as_str() {
|
||||||
// let source = project.get_source(&escape_path)?;
|
// "." => {
|
||||||
|
// info!("trigger dot completion");
|
||||||
let binding = escape_path;
|
// get_dot_completion(server, &line_text, uri, &pos, &language_id)
|
||||||
let file = binding.as_path();
|
// },
|
||||||
// 1) get source position, and source file
|
// "$" => Some(CompletionList {
|
||||||
let Some(source) = project.get_source(file) else {
|
// is_incomplete: false,
|
||||||
// Do not enable completions for files that are not part of the project
|
// items: server.sys_tasks.clone(),
|
||||||
|
// }),
|
||||||
return Some(CompletionResponse::List(CompletionList {
|
// "`" => Some(CompletionList {
|
||||||
..Default::default()
|
// is_incomplete: false,
|
||||||
}));
|
// items: server.directives.clone(),
|
||||||
};
|
// }),
|
||||||
let cursor = from_lsp_pos(params.text_document_position.position);
|
// "/" => {
|
||||||
// 2) Optimization chance: go to last recognizable token before the cursor. For example:
|
// info!("trigger include");
|
||||||
// - Any primary unit (e.g. entity declaration, package declaration, ...)
|
// include_path_completion(&doc.text_document.uri, &line_text, pos)
|
||||||
// => keyword `entity`, `package`, ...
|
// },
|
||||||
// - Any secondary unit (e.g. package body, architecture)
|
// "\"" => {
|
||||||
// => keyword `architecture`, ...
|
// info!("trigger include");
|
||||||
|
// include_path_completion(&doc.text_document.uri, &line_text, pos)
|
||||||
// 3) Run the parser until the point of the cursor. Then exit with possible completions
|
// }
|
||||||
let options = project
|
// _ => None,
|
||||||
.list_completion_options(&source, cursor)
|
// }
|
||||||
.into_iter()
|
|
||||||
.map(|item| completion_item_to_lsp_item(server, item))
|
|
||||||
.collect();
|
|
||||||
Some(CompletionResponse::List(CompletionList {
|
|
||||||
items: options,
|
|
||||||
is_incomplete: true,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn completion_item_to_lsp_item(
|
|
||||||
#[allow(unused)]
|
|
||||||
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
|
// CompletionTriggerKind::TRIGGER_FOR_INCOMPLETE_COMPLETIONS => None,
|
||||||
}
|
CompletionTriggerKind::INVOKED => {
|
||||||
vhdl_lang::CompletionItem::Overloaded(desi, count) => CompletionItem {
|
let mut comps = server.srcs.get_completions(
|
||||||
label: desi.to_string(),
|
&token,
|
||||||
detail: Some(format!("+{count} overloaded")),
|
file.text.pos_to_byte(&doc.position),
|
||||||
kind: match desi {
|
&doc.text_document.uri,
|
||||||
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() {
|
// complete keywords
|
||||||
vec![work_name.to_string(), lib_name.name().to_string()]
|
comps.items.extend::<Vec<CompletionItem>>(
|
||||||
} else {
|
server.key_comps
|
||||||
vec![work_name.to_string()]
|
.iter()
|
||||||
};
|
.filter(|x| x.label.starts_with(&token))
|
||||||
let (region, is_component_instantiation) = match ent.kind() {
|
.cloned()
|
||||||
AnyEntKind::Design(Design::Entity(_, region)) => (region, false),
|
.collect(),
|
||||||
AnyEntKind::Component(region) => (region, true),
|
);
|
||||||
// should never happen but better return some value instead of crashing
|
Some(comps)
|
||||||
_ => return entity_to_completion_item(ent),
|
}
|
||||||
};
|
_ => None,
|
||||||
// TODO: Confirm vscode support
|
},
|
||||||
let template = if true { // if server.client_supports_snippets() {
|
None => {
|
||||||
let mut line = if is_component_instantiation {
|
let trigger = prev_char(&file.text, &doc.position);
|
||||||
format!("${{1:{}_inst}}: {}", ent.designator, ent.designator)
|
match trigger {
|
||||||
} else {
|
// '.' => Some(server.srcs.get_dot_completions(
|
||||||
format!(
|
// token.trim_end_matches('.'),
|
||||||
"${{1:{}_inst}}: entity ${{2|{}|}}.{}",
|
// file.text.pos_to_byte(&doc.position),
|
||||||
ent.designator,
|
// &doc.text_document.uri,
|
||||||
library_names.join(","),
|
// )?),
|
||||||
ent.designator
|
// '$' => Some(CompletionList {
|
||||||
)
|
// is_incomplete: false,
|
||||||
};
|
// items: server.sys_tasks.clone(),
|
||||||
if architectures.len() > 1 {
|
// }),
|
||||||
line.push_str("(${3|");
|
// '`' => Some(CompletionList {
|
||||||
for (i, architecture) in architectures.iter().enumerate() {
|
// is_incomplete: false,
|
||||||
line.push_str(&architecture.designator().to_string());
|
// items: server.directives.clone(),
|
||||||
if i != architectures.len() - 1 {
|
// }),
|
||||||
line.push(',')
|
_ => {
|
||||||
}
|
let mut comps = server.srcs.get_completions(
|
||||||
}
|
&token,
|
||||||
line.push_str("|})");
|
file.text.pos_to_byte(&doc.position),
|
||||||
|
&doc.text_document.uri,
|
||||||
|
)?;
|
||||||
|
comps.items.extend::<Vec<CompletionItem>>(
|
||||||
|
server.key_comps
|
||||||
|
.iter()
|
||||||
|
.filter(|x| x.label.starts_with(&token))
|
||||||
|
.cloned()
|
||||||
|
.collect(),
|
||||||
|
);
|
||||||
|
Some(comps)
|
||||||
}
|
}
|
||||||
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}"),
|
// eprintln!("comp response: {}", now.elapsed().as_millis());
|
||||||
detail: Some(format!("{attribute}")),
|
Some(CompletionResponse::List(response?))
|
||||||
insert_text: Some(format!("{attribute}")),
|
|
||||||
kind: Some(CompletionItemKind::REFERENCE),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn entity_to_completion_item(ent: EntRef) -> CompletionItem {
|
fn prev_char(text: &Rope, pos: &Position) -> char {
|
||||||
CompletionItem {
|
let char_idx = text.pos_to_char(pos);
|
||||||
label: ent.designator.to_string(),
|
if char_idx > 0 {
|
||||||
detail: Some(ent.describe()),
|
for i in (0..char_idx).rev() {
|
||||||
kind: Some(entity_kind_to_completion_kind(ent.kind())),
|
let res = text.char(i);
|
||||||
data: serde_json::to_value(ent.id.to_raw()).ok(),
|
if !res.is_whitespace() {
|
||||||
insert_text: Some(ent.designator.to_string()),
|
return res;
|
||||||
..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,
|
} else {
|
||||||
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,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_completion_token(text: &Rope, line: RopeSlice, pos: Position) -> String {
|
||||||
|
let mut token = String::new();
|
||||||
|
let mut line_iter = line.chars();
|
||||||
|
for _ in 0..(line.utf16_cu_to_char(pos.character as usize)) {
|
||||||
|
line_iter.next();
|
||||||
|
}
|
||||||
|
let mut c = line_iter.prev();
|
||||||
|
//TODO: make this a regex
|
||||||
|
while c.is_some()
|
||||||
|
&& (c.unwrap().is_alphanumeric()
|
||||||
|
|| c.unwrap() == '_'
|
||||||
|
|| c.unwrap() == '.'
|
||||||
|
|| c.unwrap() == '['
|
||||||
|
|| c.unwrap() == ']')
|
||||||
|
{
|
||||||
|
token.push(c.unwrap());
|
||||||
|
c = line_iter.prev();
|
||||||
|
}
|
||||||
|
let mut result: String = token.chars().rev().collect();
|
||||||
|
if result.contains('[') {
|
||||||
|
let l_bracket_offset = result.find('[').unwrap_or(result.len());
|
||||||
|
result.replace_range(l_bracket_offset.., "");
|
||||||
|
}
|
||||||
|
if &result == "." {
|
||||||
|
// probably a instantiation, the token should be what we're instatiating
|
||||||
|
let mut char_iter = text.chars();
|
||||||
|
let mut token = String::new();
|
||||||
|
for _ in 0..text.pos_to_char(&pos) {
|
||||||
|
char_iter.next();
|
||||||
|
}
|
||||||
|
let mut c = char_iter.prev();
|
||||||
|
|
||||||
|
// go to the last semicolon
|
||||||
|
while c.is_some() && (c.unwrap() != ';') {
|
||||||
|
c = char_iter.prev();
|
||||||
|
}
|
||||||
|
// go the the start of the next symbol
|
||||||
|
while c.is_some() && !(c.unwrap().is_alphanumeric() || c.unwrap() == '_') {
|
||||||
|
c = char_iter.next();
|
||||||
|
}
|
||||||
|
// then extract the next symbol
|
||||||
|
while c.is_some() && (c.unwrap().is_alphanumeric() || c.unwrap() == '_') {
|
||||||
|
token.push(c.unwrap());
|
||||||
|
c = char_iter.next();
|
||||||
|
}
|
||||||
|
token
|
||||||
|
} else {
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -68,6 +68,7 @@ pub fn make_fast_from_design_file(design_file: &DesignFile) -> Option<FastHdlpar
|
|||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
fn parse_tokens(tokens: Vec<Token>) -> Vec<Module> {
|
fn parse_tokens(tokens: Vec<Token>) -> Vec<Module> {
|
||||||
let mut modules = Vec::new();
|
let mut modules = Vec::new();
|
||||||
|
let mut last_module_name = String::new();
|
||||||
let mut instance_type = HashSet::new();
|
let mut instance_type = HashSet::new();
|
||||||
|
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
@ -97,7 +98,7 @@ fn parse_tokens(tokens: Vec<Token>) -> Vec<Module> {
|
|||||||
tokens[end].pos.range.end
|
tokens[end].pos.range.end
|
||||||
};
|
};
|
||||||
let module = Module {
|
let module = Module {
|
||||||
name: entity_name,
|
name: entity_name.to_string(),
|
||||||
params: Vec::new(),
|
params: Vec::new(),
|
||||||
ports: Vec::new(),
|
ports: Vec::new(),
|
||||||
instances: Vec::new(),
|
instances: Vec::new(),
|
||||||
@ -112,6 +113,7 @@ fn parse_tokens(tokens: Vec<Token>) -> Vec<Module> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
last_module_name = entity_name.to_string();
|
||||||
modules.push(module);
|
modules.push(module);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -140,7 +142,7 @@ fn parse_tokens(tokens: Vec<Token>) -> Vec<Module> {
|
|||||||
tokens[end].pos.range.end
|
tokens[end].pos.range.end
|
||||||
};
|
};
|
||||||
let module = Module {
|
let module = Module {
|
||||||
name,
|
name: name.to_string(),
|
||||||
params: Vec::new(),
|
params: Vec::new(),
|
||||||
ports: Vec::new(),
|
ports: Vec::new(),
|
||||||
instances: Vec::new(),
|
instances: Vec::new(),
|
||||||
@ -155,6 +157,7 @@ fn parse_tokens(tokens: Vec<Token>) -> Vec<Module> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
last_module_name = name.to_string();
|
||||||
modules.push(module);
|
modules.push(module);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -214,7 +217,9 @@ fn parse_tokens(tokens: Vec<Token>) -> Vec<Module> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
modules.last_mut().unwrap().instances.push(instance);
|
if let Some(module) = modules.iter_mut().find(|module| module.name == last_module_name) {
|
||||||
|
module.instances.push(instance);
|
||||||
|
}
|
||||||
} else if kind_str(tokens[i+1].kind) == "entity" {
|
} else if kind_str(tokens[i+1].kind) == "entity" {
|
||||||
let instance = Instance {
|
let instance = Instance {
|
||||||
name: get_value(&tokens[i-1]),
|
name: get_value(&tokens[i-1]),
|
||||||
@ -232,7 +237,9 @@ fn parse_tokens(tokens: Vec<Token>) -> Vec<Module> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
modules.last_mut().unwrap().instances.push(instance);
|
if let Some(module) = modules.iter_mut().find(|module| module.name == last_module_name) {
|
||||||
|
module.instances.push(instance);
|
||||||
|
}
|
||||||
i += 1;
|
i += 1;
|
||||||
} else {
|
} else {
|
||||||
let name = get_value(&tokens[i-1]);
|
let name = get_value(&tokens[i-1]);
|
||||||
@ -261,7 +268,9 @@ fn parse_tokens(tokens: Vec<Token>) -> Vec<Module> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
modules.last_mut().unwrap().instances.push(instance);
|
if let Some(module) = modules.iter_mut().find(|module| module.name == last_module_name) {
|
||||||
|
module.instances.push(instance);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -271,9 +280,13 @@ fn parse_tokens(tokens: Vec<Token>) -> Vec<Module> {
|
|||||||
if is_map {
|
if is_map {
|
||||||
let start = params.first().unwrap().range.start.clone();
|
let start = params.first().unwrap().range.start.clone();
|
||||||
let end = params.last().unwrap().range.start.clone();
|
let end = params.last().unwrap().range.start.clone();
|
||||||
modules.last_mut().unwrap().instances.last_mut().unwrap().instparams = Some(Range { start, end });
|
if let Some(module) = modules.iter_mut().find(|module| module.name == last_module_name) {
|
||||||
|
module.instances.last_mut().unwrap().instparams = Some(Range { start, end });;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
modules.last_mut().unwrap().params.extend(params);
|
if let Some(module) = modules.iter_mut().find(|module| module.name == last_module_name) {
|
||||||
|
module.params.extend(params);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
i = next_index;
|
i = next_index;
|
||||||
}
|
}
|
||||||
@ -283,9 +296,13 @@ fn parse_tokens(tokens: Vec<Token>) -> Vec<Module> {
|
|||||||
if is_map {
|
if is_map {
|
||||||
let start = ports.first().unwrap().range.start.clone();
|
let start = ports.first().unwrap().range.start.clone();
|
||||||
let end = ports.last().unwrap().range.start.clone();
|
let end = ports.last().unwrap().range.start.clone();
|
||||||
modules.last_mut().unwrap().instances.last_mut().unwrap().instports = Some(Range { start, end });
|
if let Some(module) = modules.iter_mut().find(|module| module.name == last_module_name) {
|
||||||
|
module.instances.last_mut().unwrap().instports = Some(Range { start, end });
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
modules.last_mut().unwrap().ports.extend(ports);
|
if let Some(module) = modules.iter_mut().find(|module| module.name == last_module_name) {
|
||||||
|
module.ports.extend(ports);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
i = next_index;
|
i = next_index;
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
|
use crate::core::hdlparam::{self, FastHdlparam};
|
||||||
use crate::utils::get_language_id_by_uri;
|
use crate::utils::get_language_id_by_uri;
|
||||||
use crate::server::LSPServer;
|
use crate::server::LSPServer;
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
use log::info;
|
use log::info;
|
||||||
|
use ropey::Rope;
|
||||||
use sv_parser::*;
|
use sv_parser::*;
|
||||||
use tower_lsp::lsp_types::*;
|
use tower_lsp::lsp_types::*;
|
||||||
|
|
||||||
@ -199,3 +201,58 @@ pub fn get_scopes_from_syntax_tree(syntax_tree: &SyntaxTree, url: &Url) -> Optio
|
|||||||
Some(global_scope)
|
Some(global_scope)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_scopes_from_vhdl_fast(fast: &FastHdlparam, text: &Rope, url: &Url) -> Option<GenericScope> {
|
||||||
|
let mut scopes: Vec<Box<dyn Scope>> = Vec::new();
|
||||||
|
let mut global_scope: GenericScope = GenericScope::new(url);
|
||||||
|
global_scope.ident = String::from("global");
|
||||||
|
|
||||||
|
for module in &fast.content {
|
||||||
|
let mut scope: GenericScope = GenericScope::new(url);
|
||||||
|
scope.ident = module.name.clone();
|
||||||
|
let mut module_range = module.range.clone();
|
||||||
|
module_range.affine(-1, -1);
|
||||||
|
scope.start = position_to_byte_idx(text, &module_range.start);
|
||||||
|
scope.end = position_to_byte_idx(text, &module_range.end);
|
||||||
|
scope.byte_idx = scope.start + 7;
|
||||||
|
|
||||||
|
for parameter in &module.params {
|
||||||
|
let mut def = GenericDec::new(url);
|
||||||
|
def.ident = parameter.name.clone();
|
||||||
|
let mut parameter_range = parameter.range.clone();
|
||||||
|
parameter_range.affine(-1, -1);
|
||||||
|
def.byte_idx = position_to_byte_idx(text, ¶meter_range.start);
|
||||||
|
def.completion_kind = CompletionItemKind::TYPE_PARAMETER;
|
||||||
|
def.symbol_kind = SymbolKind::TYPE_PARAMETER;
|
||||||
|
scope.defs.push(Box::new(def));
|
||||||
|
}
|
||||||
|
for port in &module.ports {
|
||||||
|
let mut port_def = PortDec::new(url);
|
||||||
|
port_def.ident = port.name.clone();
|
||||||
|
let mut port_range = port.range.clone();
|
||||||
|
port_range.affine(-1, -1);
|
||||||
|
port_def.byte_idx = position_to_byte_idx(text, &port_range.start);
|
||||||
|
port_def.type_str = port.dir_type.clone();
|
||||||
|
scope.defs.push(Box::new(port_def));
|
||||||
|
}
|
||||||
|
for inst in &module.instances {
|
||||||
|
let mut instance = ModInst::new(url);
|
||||||
|
instance.ident = inst.name.clone();
|
||||||
|
let mut inst_range = inst.range.clone();
|
||||||
|
inst_range.affine(-1, -1);
|
||||||
|
instance.byte_idx = position_to_byte_idx(text, &inst_range.start);
|
||||||
|
instance.type_str = inst.inst_type.clone();
|
||||||
|
instance.mod_ident = inst.inst_type.clone();
|
||||||
|
|
||||||
|
scope.defs.push(Box::new(instance));
|
||||||
|
}
|
||||||
|
scopes.push(Box::new(scope));
|
||||||
|
}
|
||||||
|
|
||||||
|
global_scope.scopes.append(&mut scopes);
|
||||||
|
Some(global_scope)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn position_to_byte_idx(text: &Rope, pos: &hdlparam::Position) -> usize {
|
||||||
|
let char = text.line_to_char(pos.line as usize) + pos.character as usize;
|
||||||
|
text.char_to_byte(char)
|
||||||
|
}
|
||||||
|
@ -1,33 +1,52 @@
|
|||||||
use std::{path::PathBuf, str::FromStr};
|
#[allow(unused)]
|
||||||
|
|
||||||
use log::info;
|
use log::info;
|
||||||
use tower_lsp::lsp_types::*;
|
use tower_lsp::lsp_types::*;
|
||||||
use crate::{server::LSPServer, utils::{from_lsp_pos, srcpos_to_location, to_escape_path}};
|
use crate::{server::LSPServer, sources::LSPSupport, utils::get_definition_token};
|
||||||
|
|
||||||
|
use super::{Definition, Scope};
|
||||||
|
|
||||||
pub fn goto_vhdl_definition(server: &LSPServer, params: &GotoDefinitionParams) -> Option<GotoDefinitionResponse> {
|
pub fn goto_vhdl_definition(server: &LSPServer, params: &GotoDefinitionParams) -> Option<GotoDefinitionResponse> {
|
||||||
let uri = ¶ms.text_document_position_params.text_document.uri;
|
let doc = ¶ms.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 = server.srcs.get_id(&uri).to_owned();
|
let file_id = server.srcs.get_id(doc).to_owned();
|
||||||
server.srcs.wait_parse_ready(file_id, false);
|
server.srcs.wait_parse_ready(file_id, false);
|
||||||
let projects = server.srcs.design_file_map.read().ok()?;
|
let file = server.srcs.get_file(file_id)?;
|
||||||
|
let file = file.read().ok()?;
|
||||||
|
|
||||||
let path = match PathBuf::from_str(uri.path()) {
|
let line_text = file.text.line(pos.line as usize);
|
||||||
Ok(path) => path,
|
let token: String = get_definition_token(&line_text, pos);
|
||||||
Err(error) => {
|
|
||||||
info!("error happen in <goto_include_definition>: {:?}", error);
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let escape_path = to_escape_path(&path);
|
|
||||||
|
|
||||||
let project = match projects.get("VHDLProject") {
|
// // match include
|
||||||
Some(project) => project,
|
// if let Some(definition) = goto_include_definition(doc, &line_text, pos) {
|
||||||
None => return None
|
// return Some(definition);
|
||||||
};
|
// }
|
||||||
|
|
||||||
let source = project.get_source(&escape_path)?;
|
// // match macro
|
||||||
|
// if let Some(definition) = goto_macro_definition(server, &line_text, pos) {
|
||||||
|
// return Some(definition);
|
||||||
|
// }
|
||||||
|
|
||||||
let ent = project.find_definition(&source, from_lsp_pos(pos))?;
|
// match instance
|
||||||
let location = srcpos_to_location(ent.decl_pos()?);
|
|
||||||
Some(GotoDefinitionResponse::Scalar(location))
|
let scope_tree = server.srcs.scope_tree.read().ok()?;
|
||||||
|
|
||||||
|
// // match position port & param
|
||||||
|
// if let Some(definition) = goto_position_port_param_definition(server, &line_text, doc, pos) {
|
||||||
|
// return Some(definition);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // match module name
|
||||||
|
// if let Some(definition) = goto_module_declaration_definition(server, &token) {
|
||||||
|
// return Some(definition);
|
||||||
|
// }
|
||||||
|
|
||||||
|
let byte_idx = file.text.pos_to_byte(&pos);
|
||||||
|
let global_scope = scope_tree.as_ref()?;
|
||||||
|
let def = global_scope.get_definition(&token, byte_idx, doc)?;
|
||||||
|
|
||||||
|
let def_pos = file.text.byte_to_pos(def.byte_idx());
|
||||||
|
Some(GotoDefinitionResponse::Scalar(Location::new(
|
||||||
|
def.url(),
|
||||||
|
Range::new(def_pos, def_pos),
|
||||||
|
)))
|
||||||
}
|
}
|
@ -21,10 +21,13 @@ 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,
|
// self,
|
||||||
¶ms.text_document_position_params
|
// &token,
|
||||||
),
|
// &file,
|
||||||
|
// pos,
|
||||||
|
// &uri
|
||||||
|
// ),
|
||||||
|
|
||||||
"verilog" | "systemverilog" => sv::document_highlight(
|
"verilog" | "systemverilog" => sv::document_highlight(
|
||||||
self,
|
self,
|
||||||
|
@ -1,45 +1,55 @@
|
|||||||
use std::{path::PathBuf, str::FromStr};
|
|
||||||
|
|
||||||
use log::info;
|
use log::info;
|
||||||
|
use sv_parser::{RefNode, SyntaxTree};
|
||||||
use tower_lsp::lsp_types::*;
|
use tower_lsp::lsp_types::*;
|
||||||
|
|
||||||
use crate::{server::LSPServer, utils::{from_lsp_pos, to_escape_path, to_lsp_range}};
|
use crate::{definition::{get_ident, Scope}, server::LSPServer, sources::{LSPSupport, ParseIR, Source}};
|
||||||
|
|
||||||
pub fn document_highlight(
|
pub fn document_highlight(
|
||||||
server: &LSPServer,
|
server: &LSPServer,
|
||||||
params: &TextDocumentPositionParams
|
token: &str,
|
||||||
|
file: &Source,
|
||||||
|
pos: Position,
|
||||||
|
uri: &Url
|
||||||
) -> Option<Vec<DocumentHighlight>> {
|
) -> Option<Vec<DocumentHighlight>> {
|
||||||
let uri = ¶ms.text_document.uri;
|
let scope_tree = server.srcs.scope_tree.read().ok()?;
|
||||||
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()) {
|
// use the byte_idx of the definition if possible, otherwise use the cursor
|
||||||
Ok(path) => path,
|
let byte_idx =
|
||||||
Err(error) => {
|
match scope_tree
|
||||||
info!("error happen in <goto_include_definition>: {:?}", error);
|
.as_ref()?
|
||||||
return None;
|
.get_definition(token, file.text.pos_to_byte(&pos), uri)
|
||||||
|
{
|
||||||
|
Some(def) => def.byte_idx,
|
||||||
|
None => file.text.pos_to_byte(&pos),
|
||||||
|
};
|
||||||
|
let syntax_tree = file.parse_ir.as_ref()?;
|
||||||
|
match syntax_tree {
|
||||||
|
ParseIR::SyntaxTree(syntax_tree) => {
|
||||||
|
let references = all_identifiers(&syntax_tree, &token);
|
||||||
|
Some(
|
||||||
|
scope_tree
|
||||||
|
.as_ref()?
|
||||||
|
.document_highlights(&uri, &file.text, references, byte_idx),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
info!("error happen in [vhdl_document_highlight]");
|
||||||
|
None
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
let escape_path = to_escape_path(&path);
|
}
|
||||||
|
|
||||||
let project = match projects.get("VHDLProject") {
|
/// return all identifiers in a syntax tree matching a given token
|
||||||
Some(project) => project,
|
fn all_identifiers(syntax_tree: &SyntaxTree, token: &str) -> Vec<(String, usize)> {
|
||||||
None => return None
|
let mut idents: Vec<(String, usize)> = Vec::new();
|
||||||
};
|
for node in syntax_tree {
|
||||||
|
if let RefNode::Identifier(_) = node {
|
||||||
let source = project.get_source(&escape_path)?;
|
|
||||||
|
let (ident, byte_idx) = get_ident(syntax_tree, node);
|
||||||
let ent = project.find_declaration(&source, from_lsp_pos(params.position))?;
|
if ident == token {
|
||||||
|
idents.push((ident, byte_idx));
|
||||||
Some(
|
}
|
||||||
project
|
}
|
||||||
.find_all_references_in_source(&source, ent)
|
}
|
||||||
.iter()
|
idents
|
||||||
.map(|pos| DocumentHighlight {
|
|
||||||
range: to_lsp_range(pos.range()),
|
|
||||||
kind: Some(DocumentHighlightKind::TEXT),
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
)
|
|
||||||
}
|
}
|
@ -1,105 +1,19 @@
|
|||||||
|
#[allow(unused)]
|
||||||
use log::info;
|
use log::info;
|
||||||
use tower_lsp::lsp_types::*;
|
use tower_lsp::lsp_types::*;
|
||||||
use crate::{server::LSPServer, utils::to_lsp_range};
|
use crate::{definition::Scope, server::LSPServer};
|
||||||
use crate::utils::{to_escape_path, to_symbol_kind};
|
|
||||||
|
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::str::FromStr;
|
|
||||||
use vhdl_lang::{EntHierarchy, Token};
|
|
||||||
|
|
||||||
pub fn document_symbol(server: &LSPServer, params: &DocumentSymbolParams) -> Option<DocumentSymbolResponse> {
|
pub fn document_symbol(server: &LSPServer, params: &DocumentSymbolParams) -> Option<DocumentSymbolResponse> {
|
||||||
|
// info!("enter document symbol");
|
||||||
|
|
||||||
let uri = ¶ms.text_document.uri;
|
let uri = ¶ms.text_document.uri;
|
||||||
let file_id = server.srcs.get_id(&uri).to_owned();
|
let file_id = server.srcs.get_id(&uri).to_owned();
|
||||||
server.srcs.wait_parse_ready(file_id, false);
|
server.srcs.wait_parse_ready(file_id, false);
|
||||||
let projects = server.srcs.design_file_map.read().ok()?;
|
let file = server.srcs.get_file(file_id)?;
|
||||||
|
let file = file.read().ok()?;
|
||||||
|
let scope_tree = server.srcs.scope_tree.read().ok()?;
|
||||||
|
|
||||||
let path = match PathBuf::from_str(uri.path()) {
|
Some(DocumentSymbolResponse::Nested(
|
||||||
Ok(path) => path,
|
scope_tree.as_ref()?.document_symbols(uri, &file.text),
|
||||||
Err(error) => {
|
))
|
||||||
info!("error happen in <goto_include_definition>: {:?}", error);
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let escape_path = to_escape_path(&path);
|
|
||||||
|
|
||||||
let project = match projects.get("VHDLProject") {
|
|
||||||
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,43 +1,288 @@
|
|||||||
use std::{path::PathBuf, str::FromStr};
|
use std::{path::PathBuf, str::FromStr, sync::RwLockReadGuard};
|
||||||
|
|
||||||
use log::info;
|
use log::info;
|
||||||
|
use regex::Regex;
|
||||||
|
use ropey::Rope;
|
||||||
use tower_lsp::lsp_types::*;
|
use tower_lsp::lsp_types::*;
|
||||||
use crate::server::LSPServer;
|
use crate::{core::hdlparam::{Instance, Module}, definition::{Definition, DefinitionType, GenericDec, Scope}, hover::{BracketMatchResult, BracketMatcher}, server::LSPServer, sources::LSPSupport};
|
||||||
|
|
||||||
use super::{from_lsp_pos, to_escape_path};
|
use super::{feature::hover_format_digit, get_definition_token, get_language_id_by_uri, to_escape_path};
|
||||||
|
|
||||||
pub fn hover(server: &LSPServer, params: &HoverParams) -> Option<Hover> {
|
pub fn hover(server: &LSPServer, params: &HoverParams) -> Option<Hover> {
|
||||||
let uri = ¶ms.text_document_position_params.text_document.uri;
|
let doc = ¶ms.text_document_position_params.text_document.uri;
|
||||||
let pos = params.text_document_position_params.position;
|
let pos: Position = params.text_document_position_params.position;
|
||||||
let file_id = server.srcs.get_id(&uri).to_owned();
|
let file_id: usize = server.srcs.get_id(doc).to_owned();
|
||||||
server.srcs.wait_parse_ready(file_id, false);
|
server.srcs.wait_parse_ready(file_id, false);
|
||||||
let projects = server.srcs.design_file_map.read().ok()?;
|
let file: std::sync::Arc<std::sync::RwLock<crate::sources::Source>> = server.srcs.get_file(file_id)?;
|
||||||
|
let file: std::sync::RwLockReadGuard<'_, crate::sources::Source> = file.read().ok()?;
|
||||||
|
|
||||||
let path = match PathBuf::from_str(uri.path()) {
|
let line_text = file.text.line(pos.line as usize);
|
||||||
Ok(path) => path,
|
let token: String = get_definition_token(&line_text, pos);
|
||||||
Err(error) => {
|
let language_id = get_language_id_by_uri(doc);
|
||||||
info!("error happen in <goto_include_definition>: {:?}", error);
|
|
||||||
|
// // match `include
|
||||||
|
// if let Some(hover) = hover_include(doc, &line_text, pos, &language_id) {
|
||||||
|
// return Some(hover);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // match macro
|
||||||
|
// if let Some(hover) = hover_macro(server, &line_text, pos, &language_id) {
|
||||||
|
// return Some(hover);
|
||||||
|
// }
|
||||||
|
|
||||||
|
let scope_tree = server.srcs.scope_tree.read().ok()?;
|
||||||
|
let global_scope = scope_tree.as_ref().unwrap();
|
||||||
|
|
||||||
|
let symbol_definition: GenericDec = global_scope
|
||||||
|
.get_definition(&token, file.text.pos_to_byte(&pos), doc)?;
|
||||||
|
|
||||||
|
// // match positional port param
|
||||||
|
// if let Some(hover) = hover_position_port_param(server, &line_text, doc, pos, &language_id) {
|
||||||
|
// return Some(hover);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // match module name
|
||||||
|
// if let Some(hover) = hover_module_declaration(server, &token, &symbol_definition, &language_id) {
|
||||||
|
// return Some(hover);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// match 正常 symbol
|
||||||
|
if let Some(hover) = hover_common_symbol(server, &token, &symbol_definition, &file, doc, pos, &language_id) {
|
||||||
|
return Some(hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
// match digit 5'b00110
|
||||||
|
if let Some(hover) = hover_format_digit(&line_text, pos, &language_id) {
|
||||||
|
return Some(hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_hover_with_comment(doc: &Rope, line: usize, language_id: &str, exclude_code: bool) -> Option<Hover> {
|
||||||
|
if line == 0 {
|
||||||
|
let language_string = LanguageString {
|
||||||
|
language: language_id.to_string(),
|
||||||
|
value: doc.line(line).to_string()
|
||||||
|
};
|
||||||
|
let markdown = MarkedString::LanguageString(language_string);
|
||||||
|
|
||||||
|
return Some(Hover {
|
||||||
|
contents: HoverContents::Scalar(markdown),
|
||||||
|
range: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let mut hover: Vec<String> = Vec::new();
|
||||||
|
let mut multiline: bool = false;
|
||||||
|
let mut valid: bool = true;
|
||||||
|
let mut current: String = doc.line(line).to_string();
|
||||||
|
|
||||||
|
let ltrim: String = " ".repeat(current.len() - current.trim_start().len());
|
||||||
|
let mut line_idx = line;
|
||||||
|
|
||||||
|
// 寻找周围的注释
|
||||||
|
while valid {
|
||||||
|
hover.push(current.clone());
|
||||||
|
line_idx -= 1;
|
||||||
|
valid = false;
|
||||||
|
if line_idx > 0 {
|
||||||
|
current = doc.line(line_idx).to_string();
|
||||||
|
let currentl = current.clone().trim_start().to_owned();
|
||||||
|
let currentr = current.clone().trim_end().to_owned();
|
||||||
|
if currentl.starts_with("/*") && currentr.ends_with("*/") {
|
||||||
|
valid = true;
|
||||||
|
} else if currentr.ends_with("*/") {
|
||||||
|
multiline = true;
|
||||||
|
valid = true;
|
||||||
|
} else if currentl.starts_with("/*") {
|
||||||
|
multiline = false;
|
||||||
|
valid = true;
|
||||||
|
} else {
|
||||||
|
valid = currentl.starts_with("--") || multiline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hover.reverse();
|
||||||
|
|
||||||
|
let multi_space_regex = Regex::new(r"\s+").unwrap();
|
||||||
|
let line_handler = |line: &str| -> String {
|
||||||
|
let line = multi_space_regex.replace_all(line.trim(), " ");
|
||||||
|
let mut line = line.into_owned();
|
||||||
|
|
||||||
|
if line.starts_with("/*") {
|
||||||
|
line = line.strip_prefix("/*").unwrap().trim().to_string();
|
||||||
|
} else if line.starts_with("--") {
|
||||||
|
line = line.strip_prefix("--").unwrap().trim().to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
if line.ends_with("*/") {
|
||||||
|
line = line.strip_suffix("*/").unwrap().trim().to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
return format!("{}\n", line);
|
||||||
|
};
|
||||||
|
|
||||||
|
let line_comment_extractor = |line: &str| -> Option<(String, String)> {
|
||||||
|
let line = line.trim();
|
||||||
|
if line.contains("--") {
|
||||||
|
let comment_start = line.split_once("--");
|
||||||
|
match comment_start {
|
||||||
|
Some((code, comment)) => {
|
||||||
|
let code = code.trim().to_string();
|
||||||
|
let comment = comment.trim().to_string();
|
||||||
|
Some((code, comment))
|
||||||
|
},
|
||||||
|
None => None
|
||||||
|
}
|
||||||
|
} else if line.contains("/*") {
|
||||||
|
let comment_start = line.split_once("/*");
|
||||||
|
match comment_start {
|
||||||
|
Some((code, comment)) => {
|
||||||
|
let code = code.trim().to_string();
|
||||||
|
let comment = comment.trim().strip_suffix("*/").unwrap_or("").to_string();
|
||||||
|
Some((code, comment))
|
||||||
|
},
|
||||||
|
None => None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let escape_path = to_escape_path(&path);
|
|
||||||
|
|
||||||
let project = match projects.get("VHDLProject") {
|
// 所有的 markdown
|
||||||
Some(project) => project,
|
let mut comment_markdowns = Vec::<MarkedString>::new();
|
||||||
None => return None
|
|
||||||
|
for (line_no, line_text) in hover.iter().enumerate() {
|
||||||
|
let line_hover = if let Some(stripped) = line_text.strip_prefix(<rim) {
|
||||||
|
line_handler(stripped)
|
||||||
|
} else {
|
||||||
|
line_handler(&line_text)
|
||||||
|
};
|
||||||
|
|
||||||
|
if line_no == hover.len() - 1 {
|
||||||
|
// 最后一个为定义所在的一行
|
||||||
|
if let Some((code, comment)) = line_comment_extractor(&line_text) {
|
||||||
|
comment_markdowns.push(MarkedString::LanguageString(LanguageString {
|
||||||
|
language: language_id.to_string(),
|
||||||
|
value: code
|
||||||
|
}));
|
||||||
|
comment_markdowns.insert(0, MarkedString::String(comment.trim().to_string()));
|
||||||
|
} else {
|
||||||
|
// 这行只有代码,没有注释
|
||||||
|
comment_markdowns.push(MarkedString::LanguageString(LanguageString {
|
||||||
|
language: language_id.to_string(),
|
||||||
|
value: line_hover
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 否则,都是上方的注释
|
||||||
|
comment_markdowns.push(MarkedString::String(line_hover.trim().to_string()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 合并其中的 markdown
|
||||||
|
let mut merge_markdowns = Vec::<MarkedString>::new();
|
||||||
|
let mut string_buffer = String::new();
|
||||||
|
for (_, md) in comment_markdowns.iter().enumerate() {
|
||||||
|
match md {
|
||||||
|
MarkedString::String(markdown_string) => {
|
||||||
|
string_buffer.push_str(format!("{}\n", markdown_string.trim()).as_str());
|
||||||
|
}
|
||||||
|
MarkedString::LanguageString(code) => {
|
||||||
|
if !string_buffer.is_empty() {
|
||||||
|
merge_markdowns.push(MarkedString::String(string_buffer.to_string()));
|
||||||
|
string_buffer.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
if !exclude_code {
|
||||||
|
merge_markdowns.push(MarkedString::LanguageString(code.to_owned()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if comment_markdowns.len() > 0 {
|
||||||
|
return Some(Hover {
|
||||||
|
contents: HoverContents::Array(merge_markdowns),
|
||||||
|
range: None
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
fn hover_common_symbol(
|
||||||
|
#[allow(unused)]
|
||||||
|
server: &LSPServer,
|
||||||
|
#[allow(unused)]
|
||||||
|
token: &String,
|
||||||
|
symbol_definition: &GenericDec,
|
||||||
|
file: &RwLockReadGuard<'_, crate::sources::Source>,
|
||||||
|
doc: &Url,
|
||||||
|
#[allow(unused)]
|
||||||
|
pos: Position,
|
||||||
|
language_id: &String
|
||||||
|
) -> Option<Hover> {
|
||||||
|
// 根据 symbol 的类别进行额外的判断
|
||||||
|
match symbol_definition.def_type {
|
||||||
|
DefinitionType::ModuleInstantiation => {
|
||||||
|
let hdlparam = server.srcs.hdl_param.clone();
|
||||||
|
let pathbuf = PathBuf::from_str(doc.path()).unwrap();
|
||||||
|
let pathbuf = to_escape_path(&pathbuf);
|
||||||
|
let path_string = pathbuf.to_str().unwrap().replace("\\", "/");
|
||||||
|
|
||||||
|
let find_name_condition = |_: &Module, instance: &Instance| {
|
||||||
|
symbol_definition.ident == instance.name
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
if let Some(instance) = hdlparam.walk_instantiation(&path_string, find_name_condition) {
|
||||||
|
info!("instance {:?}", instance);
|
||||||
|
let def_line = file.text.byte_to_line(symbol_definition.byte_idx());
|
||||||
|
let mut markdown_comment = match make_hover_with_comment(&file.text, def_line, &language_id, true) {
|
||||||
|
Some(hover) => {
|
||||||
|
match hover.contents {
|
||||||
|
HoverContents::Array(array) => array,
|
||||||
|
_ => Vec::<MarkedString>::new()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => Vec::<MarkedString>::new()
|
||||||
|
};
|
||||||
|
|
||||||
|
// 扫描到右括号
|
||||||
|
let mut current_line = def_line;
|
||||||
|
let mut buffer = String::new();
|
||||||
|
let len_lines = file.text.len_lines();
|
||||||
|
let mut matcher = BracketMatcher::new();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if current_line >= len_lines {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let line_text = file.text.line(current_line).to_string();
|
||||||
|
buffer.push_str(&line_text);
|
||||||
|
match matcher.consume_string(&line_text) {
|
||||||
|
BracketMatchResult::Invalid | BracketMatchResult::Complete => break,
|
||||||
|
BracketMatchResult::Valid => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
current_line += 1;
|
||||||
|
}
|
||||||
|
markdown_comment.push(MarkedString::LanguageString(LanguageString {
|
||||||
|
language: language_id.to_string(),
|
||||||
|
value: buffer
|
||||||
|
}));
|
||||||
|
|
||||||
|
return Some(Hover { contents: HoverContents::Array(markdown_comment), range: None });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => {}
|
||||||
};
|
};
|
||||||
|
|
||||||
let source = project.get_source(&escape_path)?;
|
let def_line = file.text.byte_to_line(symbol_definition.byte_idx());
|
||||||
|
make_hover_with_comment(&file.text, def_line, &language_id, false)
|
||||||
let ent = project.find_declaration(&source, from_lsp_pos(pos))?;
|
|
||||||
|
|
||||||
let value = project.format_declaration(ent)?;
|
|
||||||
|
|
||||||
Some(Hover {
|
|
||||||
contents: HoverContents::Markup(MarkupContent {
|
|
||||||
kind: MarkupKind::Markdown,
|
|
||||||
value: format!("```vhdl\n{value}\n```"),
|
|
||||||
}),
|
|
||||||
range: None,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ use crate::core::vhdl_parser::make_fast_from_design_file;
|
|||||||
use crate::core::vhdl_parser::vhdl_parse;
|
use crate::core::vhdl_parser::vhdl_parse;
|
||||||
use crate::definition::def_types::*;
|
use crate::definition::def_types::*;
|
||||||
use crate::definition::get_scopes_from_syntax_tree;
|
use crate::definition::get_scopes_from_syntax_tree;
|
||||||
|
use crate::definition::get_scopes_from_vhdl_fast;
|
||||||
use crate::diagnostics::{get_diagnostics, is_hidden};
|
use crate::diagnostics::{get_diagnostics, is_hidden};
|
||||||
use crate::server::LSPServer;
|
use crate::server::LSPServer;
|
||||||
use crate::utils::to_escape_path;
|
use crate::utils::to_escape_path;
|
||||||
@ -12,7 +13,6 @@ 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::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;
|
||||||
@ -129,7 +129,7 @@ pub struct Source {
|
|||||||
pub enum ParseIR {
|
pub enum ParseIR {
|
||||||
/// 基于 vhdl-parser 的IR,存放 VHDL
|
/// 基于 vhdl-parser 的IR,存放 VHDL
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
VHDLProject(vhdl_lang::Project),
|
DesignFile(vhdl_lang::ast::DesignFile),
|
||||||
/// 存放 sv 的 IR
|
/// 存放 sv 的 IR
|
||||||
SyntaxTree(sv_parser::SyntaxTree)
|
SyntaxTree(sv_parser::SyntaxTree)
|
||||||
}
|
}
|
||||||
@ -248,7 +248,6 @@ impl Sources {
|
|||||||
}));
|
}));
|
||||||
let source_handle = source.clone();
|
let source_handle = source.clone();
|
||||||
let scope_handle = self.scope_tree.clone();
|
let scope_handle = self.scope_tree.clone();
|
||||||
let design_file_handle = self.design_file_map.clone();
|
|
||||||
let hdl_param_handle = self.hdl_param.clone();
|
let hdl_param_handle = self.hdl_param.clone();
|
||||||
let inc_dirs = self.include_dirs.clone();
|
let inc_dirs = self.include_dirs.clone();
|
||||||
|
|
||||||
@ -270,8 +269,10 @@ impl Sources {
|
|||||||
match language_id.as_str() {
|
match language_id.as_str() {
|
||||||
"vhdl" => {
|
"vhdl" => {
|
||||||
vhdl_parser_pipeline(
|
vhdl_parser_pipeline(
|
||||||
&design_file_handle,
|
&source_handle,
|
||||||
|
&scope_handle,
|
||||||
&hdl_param_handle,
|
&hdl_param_handle,
|
||||||
|
&text,
|
||||||
uri,
|
uri,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -601,8 +602,10 @@ pub fn sv_parser_pipeline(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn vhdl_parser_pipeline(
|
pub fn vhdl_parser_pipeline(
|
||||||
design_file_handle: &Arc<RwLock<HashMap<String, vhdl_lang::Project>>>,
|
source_handle: &Arc<RwLock<Source>>,
|
||||||
|
scope_handle: &Arc<RwLock<Option<GenericScope>>>,
|
||||||
hdl_param_handle: &Arc<HdlParam>,
|
hdl_param_handle: &Arc<HdlParam>,
|
||||||
|
doc: &Rope,
|
||||||
uri: &Url
|
uri: &Url
|
||||||
) {
|
) {
|
||||||
let path = match PathBuf::from_str(uri.path()) {
|
let path = match PathBuf::from_str(uri.path()) {
|
||||||
@ -618,19 +621,58 @@ pub fn vhdl_parser_pipeline(
|
|||||||
info!("error happen in [vhdl_parser_pipeline], escape_path_string is empty");
|
info!("error happen in [vhdl_parser_pipeline], escape_path_string is empty");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if let Some(design_file) = vhdl_parse(&escape_path) {
|
|
||||||
let mut design_files = design_file_handle.write().unwrap();
|
let mut file = source_handle.write().unwrap();
|
||||||
|
|
||||||
|
let mut scope_tree = if let Some(design_file) = vhdl_parse(&escape_path) {
|
||||||
|
//let mut design_files = design_file_handle.write().unwrap();
|
||||||
|
|
||||||
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);
|
let parse_ir = ParseIR::DesignFile(design_file);
|
||||||
}
|
file.parse_ir = Some(parse_ir);
|
||||||
let mut msg_printer = MessagePrinter::default();
|
hdl_param_handle.update_fast(escape_path_string.to_string(), fast.clone());
|
||||||
if let Some(project) = design_files.get_mut("VHDLProject") {
|
let scope_tree = get_scopes_from_vhdl_fast(&fast, doc, uri);
|
||||||
project.digital_lsp_update_config(&escape_path, &mut msg_printer);
|
scope_tree
|
||||||
} else {
|
} else {
|
||||||
let project = vhdl_lang::Project::new_without_config(&escape_path, &mut msg_printer);
|
None
|
||||||
design_files.insert("VHDLProject".to_string(), project);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// let mut msg_printer = MessagePrinter::default();
|
||||||
|
// if let Some(project) = design_files.get_mut("VHDLProject") {
|
||||||
|
// project.digital_lsp_update_config(&escape_path, &mut msg_printer);
|
||||||
|
// } else {
|
||||||
|
// let project = vhdl_lang::Project::new_without_config(&escape_path, &mut msg_printer);
|
||||||
|
// design_files.insert("VHDLProject".to_string(), project);
|
||||||
|
// }
|
||||||
|
} else {
|
||||||
|
file.parse_ir = None;
|
||||||
|
None
|
||||||
|
};
|
||||||
|
drop(file);
|
||||||
|
|
||||||
|
|
||||||
|
info!("finish parse {:?}", uri.to_string());
|
||||||
|
|
||||||
|
// 更新 global_scope,用于 sv 的解析
|
||||||
|
// global_scope 为全局最大的那个 scope,它的 scopes 和 defs 下的元素和每一个文件一一对应
|
||||||
|
let mut global_scope = scope_handle.write().unwrap();
|
||||||
|
match &mut *global_scope {
|
||||||
|
Some(scope) => match &mut scope_tree {
|
||||||
|
Some(tree) => {
|
||||||
|
// 更新所有 uri 为当前 uri 的文件结构
|
||||||
|
scope.defs.retain(|x| &x.url() != uri);
|
||||||
|
scope.scopes.retain(|x| &x.url() != uri);
|
||||||
|
|
||||||
|
scope.defs.append(&mut tree.defs);
|
||||||
|
scope.scopes.append(&mut tree.scopes);
|
||||||
|
}
|
||||||
|
None => (),
|
||||||
|
},
|
||||||
|
// 使用 scope_tree 来更新全局的 scope
|
||||||
|
None => *global_scope = scope_tree,
|
||||||
}
|
}
|
||||||
|
// eprintln!("{:#?}", *global_scope);
|
||||||
|
drop(global_scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: add bounds checking for utf8<->utf16 conversions
|
//TODO: add bounds checking for utf8<->utf16 conversions
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit 1e390e0f4429b5004e63327b64d5095775ef27b5
|
Subproject commit 21f3e497e88a108d15be75ea7e2f0f1a409c6e1d
|
@ -1 +1 @@
|
|||||||
Subproject commit 8593beff8c21bd3d3e417ac893b9f317f5386070
|
Subproject commit e8e746628785df31545fd465489dc75eba5a6c10
|
Loading…
x
Reference in New Issue
Block a user