完成 IP 的支持(还差自动补全)

This commit is contained in:
锦恢 2024-11-11 23:53:14 +08:00
parent 5540b0b1f2
commit c8aa5e2dcc
5 changed files with 200 additions and 83 deletions

View File

@ -200,6 +200,18 @@ impl Port {
let port_desc = port_desc_array.join(" "); let port_desc = port_desc_array.join(" ");
port_desc port_desc
} }
pub fn to_vhdl_description(&self) -> String {
let mut port_desc_array = Vec::<String>::new();
port_desc_array.push(self.name.to_string());
port_desc_array.push(":".to_string());
port_desc_array.push(self.dir_type.to_string());
let width_string = self.width.replace("[", "(").replace("]", ")").replace(":", " downto ");
port_desc_array.push(format!("{}{};", self.net_type.to_lowercase(), width_string));
let port_desc = port_desc_array.join(" ");
port_desc
}
} }
impl Parameter { impl Parameter {
@ -207,6 +219,17 @@ impl Parameter {
let mut param_desc_array = Vec::<String>::new(); let mut param_desc_array = Vec::<String>::new();
param_desc_array.push(format!("parameter {}", self.name)); param_desc_array.push(format!("parameter {}", self.name));
if self.init != "unknown" {
param_desc_array.push("=".to_string());
param_desc_array.push(self.init.to_string());
}
let param_desc = param_desc_array.join(" ");
param_desc
}
pub fn vhdl_to_vlog_description(&self) -> String {
let mut param_desc_array = Vec::<String>::new();
param_desc_array.push(format!("parameter {}", self.name));
if self.init != "unknown" { if self.init != "unknown" {
param_desc_array.push("=".to_string()); param_desc_array.push("=".to_string());
param_desc_array.push(self.init.to_string()); param_desc_array.push(self.init.to_string());
@ -499,6 +522,14 @@ impl HdlParam {
None None
} }
/// 根据 module name 计算出对应的 file type默认为 common
pub fn find_file_type_by_module_name(&self, module_name: &str) -> String {
if let Some((_, file_type, _)) = self.find_module_context_by_name(module_name) {
return file_type;
}
"common".to_string()
}
/// 输入 module 名字,找到 module 定义的文件的路径 /// 输入 module 名字,找到 module 定义的文件的路径
pub fn find_module_definition_path(&self, module_name: &str) -> Option<String> { pub fn find_module_definition_path(&self, module_name: &str) -> Option<String> {
let module_name_to_path = self.module_name_to_path.read().unwrap(); let module_name_to_path = self.module_name_to_path.read().unwrap();

View File

@ -124,7 +124,23 @@ fn goto_instantiation<'a>(
let def_path = server.srcs.hdl_param.find_module_definition_path(&module.name).unwrap(); let def_path = server.srcs.hdl_param.find_module_definition_path(&module.name).unwrap();
let target_uri = Url::from_file_path(def_path).unwrap(); let target_uri = Url::from_file_path(def_path).unwrap();
let target_range = param.range.clone(); let target_range = param.range.clone();
let target_range = target_range.to_lsp_range();
let file_type = server.srcs.hdl_param.find_file_type_by_module_name(&instance.inst_type);
let target_range = match file_type.as_str() {
"common" => {
target_range.to_lsp_range()
}
"ip" => {
let mut target_range = target_range.clone();
target_range.affine(-1, -1).to_lsp_range()
}
"primitives" => {
target_range.to_lsp_range()
}
_ => {
target_range.to_lsp_range()
}
};
let link = vec![LocationLink { let link = vec![LocationLink {
target_uri, target_uri,
@ -153,7 +169,23 @@ fn goto_instantiation<'a>(
let def_path = server.srcs.hdl_param.find_module_definition_path(&module.name).unwrap(); let def_path = server.srcs.hdl_param.find_module_definition_path(&module.name).unwrap();
let target_uri = Url::from_file_path(def_path).unwrap(); let target_uri = Url::from_file_path(def_path).unwrap();
let target_range = port.range.clone(); let target_range = port.range.clone();
let target_range = target_range.to_lsp_range();
let file_type = server.srcs.hdl_param.find_file_type_by_module_name(&instance.inst_type);
let target_range = match file_type.as_str() {
"common" => {
target_range.to_lsp_range()
}
"ip" => {
let mut target_range = target_range.clone();
target_range.affine(-1, -1).to_lsp_range()
}
"primitives" => {
target_range.to_lsp_range()
}
_ => {
target_range.to_lsp_range()
}
};
let link = vec![LocationLink { let link = vec![LocationLink {
target_uri, target_uri,
@ -281,9 +313,8 @@ fn goto_ip_module_declaration_definition(
if pathbuf.exists() { if pathbuf.exists() {
let target_uri = Url::from_file_path(PathBuf::from_str(&def_path).unwrap()).unwrap(); let target_uri = Url::from_file_path(PathBuf::from_str(&def_path).unwrap()).unwrap();
let target_range = module.range.clone(); let mut target_range = module.range.clone();
info!("target range: {:?}", target_range); let target_range = target_range.affine(-1, -1).to_lsp_range();
let target_range = target_range.to_lsp_range();
let link = vec![LocationLink { let link = vec![LocationLink {
target_uri, target_uri,

View File

@ -229,7 +229,8 @@ fn goto_instantiation<'a>(
for param in &module.params { for param in &module.params {
if token_name == param.name { if token_name == param.name {
let hover = make_param_desc_hover(param, range, language_id); let file_type = server.srcs.hdl_param.find_file_type_by_module_name(&instance.inst_type);
let hover = make_param_desc_hover(&file_type, param, range, language_id);
return Some(hover); return Some(hover);
} }
} }
@ -250,7 +251,8 @@ fn goto_instantiation<'a>(
for port in &module.ports { for port in &module.ports {
if token_name == port.name { if token_name == port.name {
let hover = make_port_desc_hover(port, range, language_id); let file_type = server.srcs.hdl_param.find_file_type_by_module_name(&instance.inst_type);
let hover = make_port_desc_hover(&file_type, port, range, language_id);
return Some(hover); return Some(hover);
} }
} }
@ -294,21 +296,50 @@ pub fn hover_position_port_param(
None None
} }
fn make_port_desc_hover(port: &crate::core::hdlparam::Port, range: &Range, language_id: &str) -> Hover { fn make_port_desc_hover(file_type: &str, port: &crate::core::hdlparam::Port, range: &Range, language_id: &str) -> Hover {
let language_string = LanguageString { info!("enter make_port_desc_hover, file_type: {}", file_type);
language: language_id.to_string(),
value: port.to_description() let (language, value) = match file_type {
"common" => {
(language_id.to_string(), port.to_description())
}
"ip" => {
("vhdl".to_string(), port.to_vhdl_description())
}
"primitives" => {
(language_id.to_string(), port.to_description())
}
_ => {
(language_id.to_string(), port.to_description())
}
}; };
let language_string = LanguageString { language, value };
Hover { Hover {
contents: HoverContents::Scalar(MarkedString::LanguageString(language_string)), contents: HoverContents::Scalar(MarkedString::LanguageString(language_string)),
range: Some(range.clone()) range: Some(range.clone())
} }
} }
fn make_param_desc_hover(param: &crate::core::hdlparam::Parameter, range: &Range, language_id: &str) -> Hover { fn make_param_desc_hover(file_type: &str, param: &crate::core::hdlparam::Parameter, range: &Range, language_id: &str) -> Hover {
let value = match file_type {
"common" => {
param.to_description()
}
"ip" => {
param.vhdl_to_vlog_description()
}
"primitives" => {
param.to_description()
}
_ => {
param.to_description()
}
};
let language_string = LanguageString { let language_string = LanguageString {
language: language_id.to_string(), language: language_id.to_string(),
value: param.to_description() value
}; };
Hover { Hover {
contents: HoverContents::Scalar(MarkedString::LanguageString(language_string)), contents: HoverContents::Scalar(MarkedString::LanguageString(language_string)),
@ -654,16 +685,7 @@ pub fn make_entity_profile_code(module: &crate::core::hdlparam::Module) -> Strin
} }
}; };
let width_mapper = |width: &str| {
if width == "1" {
0
} else {
width.len() + 5
}
};
let max_port_length = module.ports.iter().map(|port| port.name.len()).max().unwrap_or(0); let max_port_length = module.ports.iter().map(|port| port.name.len()).max().unwrap_or(0);
let max_net_length = module.ports.iter().map(|port| net_mapper(&port.net_type)).max().unwrap_or(0);
// let max_width_length = module.ports.iter().map(|port| width_mapper(&port.width)).max().unwrap_or(0); // let max_width_length = module.ports.iter().map(|port| width_mapper(&port.width)).max().unwrap_or(0);
let max_dir_length = module.ports.iter().map(|port| port.dir_type.len()).max().unwrap_or(0); let max_dir_length = module.ports.iter().map(|port| port.dir_type.len()).max().unwrap_or(0);
@ -680,7 +702,7 @@ pub fn make_entity_profile_code(module: &crate::core::hdlparam::Module) -> Strin
// std_logic, signed, unsigned 等等 // std_logic, signed, unsigned 等等
if port.net_type != "unknown" { if port.net_type != "unknown" {
port_desc_array.push(port.net_type.to_string()); port_desc_array.push(port.net_type.to_lowercase());
} }
// (57 downto 0) // (57 downto 0)

View File

@ -6,7 +6,7 @@ use flexi_logger::LoggerHandle;
use log::{debug, info, warn}; use log::{debug, info, warn};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::string::ToString; use std::string::ToString;
use std::sync::{Mutex, RwLock}; use std::sync::{Arc, Mutex, RwLock};
use tower_lsp::jsonrpc::Result; use tower_lsp::jsonrpc::Result;
use tower_lsp::lsp_types::*; use tower_lsp::lsp_types::*;
use tower_lsp::{Client, LanguageServer}; use tower_lsp::{Client, LanguageServer};
@ -16,7 +16,7 @@ pub struct LSPServer {
pub key_comps: Vec<CompletionItem>, pub key_comps: Vec<CompletionItem>,
pub sys_tasks: Vec<CompletionItem>, pub sys_tasks: Vec<CompletionItem>,
pub directives: Vec<CompletionItem>, pub directives: Vec<CompletionItem>,
pub conf: RwLock<ProjectConfig>, pub conf: Arc<RwLock<ProjectConfig>>,
#[allow(unused)] #[allow(unused)]
pub log_handle: Mutex<Option<LoggerHandle>>, pub log_handle: Mutex<Option<LoggerHandle>>,
} }
@ -31,7 +31,7 @@ impl LSPServer {
key_comps: keyword_completions(KEYWORDS), key_comps: keyword_completions(KEYWORDS),
sys_tasks: other_completions(SYS_TASKS), sys_tasks: other_completions(SYS_TASKS),
directives: other_completions(DIRECTIVES), directives: other_completions(DIRECTIVES),
conf: RwLock::new(ProjectConfig::default()), conf: Arc::new(RwLock::new(ProjectConfig::default())),
log_handle: Mutex::new(log_handle), log_handle: Mutex::new(log_handle),
} }
} }
@ -284,6 +284,7 @@ impl LanguageServer for Backend {
} }
async fn did_change(&self, params: DidChangeTextDocumentParams) { async fn did_change(&self, params: DidChangeTextDocumentParams) {
info!("file did change: {:?}", params.text_document.uri);
// // 如果文件太大则显示错误 // // 如果文件太大则显示错误
// if CacheManager::uri_is_big_file(&params.text_document.uri) { // if CacheManager::uri_is_big_file(&params.text_document.uri) {
// // self.client.show_message(MessageType::WARNING, "考虑到性能问题,对于大于 1MB 的文件不会主动提供语言服务") // // self.client.show_message(MessageType::WARNING, "考虑到性能问题,对于大于 1MB 的文件不会主动提供语言服务")

View File

@ -8,6 +8,7 @@ use crate::definition::get_scopes_from_syntax_tree;
use crate::definition::get_scopes_from_vhdl_fast; 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::server::ProjectConfig;
use crate::utils::to_escape_path; use crate::utils::to_escape_path;
#[allow(unused)] #[allow(unused)]
use log::info; use log::info;
@ -41,8 +42,6 @@ macro_rules! unwrap_result {
impl LSPServer { impl LSPServer {
pub fn did_open(&self, params: DidOpenTextDocumentParams) -> PublishDiagnosticsParams { pub fn did_open(&self, params: DidOpenTextDocumentParams) -> PublishDiagnosticsParams {
info!("[LSPServer] did open");
let document: TextDocumentItem = params.text_document; let document: TextDocumentItem = params.text_document;
let uri = document.uri.clone(); let uri = document.uri.clone();
@ -58,54 +57,69 @@ impl LSPServer {
}], }],
}); });
} else { } else {
self.srcs.add(document); self.srcs.add(self, document);
} }
// diagnostics // diagnostics
let urls = self.srcs.names.read().unwrap().keys().cloned().collect(); let urls = self.srcs.names.read().unwrap().keys().cloned().collect();
let file_id = self.srcs.get_id(&uri); let file_id = self.srcs.get_id(&uri);
let file = self.srcs.get_file(file_id).unwrap(); if let Some(file) = self.srcs.get_file(file_id) {
let file = file.read().unwrap(); let file = file.read().unwrap();
get_diagnostics(uri, &file.text, urls, &self.conf.read().unwrap()) get_diagnostics(uri, &file.text, urls, &self.conf.read().unwrap())
} else {
PublishDiagnosticsParams {
uri,
diagnostics: Vec::new(),
version: None,
}
}
} }
pub fn did_change(&self, params: DidChangeTextDocumentParams) { pub fn did_change(&self, params: DidChangeTextDocumentParams) {
let file_id = self.srcs.get_id(&params.text_document.uri); let file_id = self.srcs.get_id(&params.text_document.uri);
let file = self.srcs.get_file(file_id).unwrap(); if let Some(file) = self.srcs.get_file(file_id) {
let mut file = file.write().unwrap(); let mut file = file.write().unwrap();
// loop through changes and apply // loop through changes and apply
for change in params.content_changes { for change in params.content_changes {
if change.range.is_none() { if change.range.is_none() {
file.text = Rope::from_str(&change.text); file.text = Rope::from_str(&change.text);
} else { } else {
file.text.apply_change(&change); file.text.apply_change(&change);
}
file.last_change_range = change.range;
} }
file.last_change_range = change.range; file.version = params.text_document.version;
} drop(file);
file.version = params.text_document.version;
drop(file);
// invalidate syntaxtree and wake parse thread // invalidate syntaxtree and wake parse thread
let meta_data = self.srcs.get_meta_data(file_id).unwrap(); let meta_data = self.srcs.get_meta_data(file_id).unwrap();
let (lock, cvar) = &*meta_data.read().unwrap().valid_parse; let (lock, cvar) = &*meta_data.read().unwrap().valid_parse;
let mut valid = lock.lock().unwrap(); let mut valid = lock.lock().unwrap();
*valid = false; *valid = false;
cvar.notify_all(); cvar.notify_all();
}
} }
pub fn did_save(&self, params: DidSaveTextDocumentParams) -> PublishDiagnosticsParams { pub fn did_save(&self, params: DidSaveTextDocumentParams) -> PublishDiagnosticsParams {
info!("[LSPServer] did save");
let urls = self.srcs.names.read().unwrap().keys().cloned().collect(); let urls = self.srcs.names.read().unwrap().keys().cloned().collect();
let file_id = self.srcs.get_id(&params.text_document.uri); let file_id = self.srcs.get_id(&params.text_document.uri);
let file = self.srcs.get_file(file_id).unwrap(); let uri = params.text_document.uri.clone();
let file = file.read().unwrap();
get_diagnostics( if let Some(file) = self.srcs.get_file(file_id) {
params.text_document.uri, let file = file.read().unwrap();
&file.text, get_diagnostics(
urls, uri,
&self.conf.read().unwrap(), &file.text,
) urls,
&self.conf.read().unwrap(),
)
} else {
PublishDiagnosticsParams {
uri,
diagnostics: Vec::new(),
version: None,
}
}
} }
pub fn did_delete_files(&self, params: DeleteFilesParams) { pub fn did_delete_files(&self, params: DeleteFilesParams) {
@ -240,7 +254,7 @@ impl Sources {
} }
/// 增加一个 hdl 文件,并为该文件添加单独的解析线程 /// 增加一个 hdl 文件,并为该文件添加单独的解析线程
pub fn add(&self, doc: TextDocumentItem) { pub fn add(&self, server: &LSPServer, doc: TextDocumentItem) {
// 对于当前的文件增加一个解析线程,不断进行解析和同步 // 对于当前的文件增加一个解析线程,不断进行解析和同步
#[allow(clippy::mutex_atomic)] // https://github.com/rust-lang/rust-clippy/issues/1516 #[allow(clippy::mutex_atomic)] // https://github.com/rust-lang/rust-clippy/issues/1516
let valid_parse = Arc::new((Mutex::new(false), Condvar::new())); let valid_parse = Arc::new((Mutex::new(false), Condvar::new()));
@ -259,6 +273,8 @@ impl Sources {
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();
let conf = server.conf.clone();
info!("launch worker to parse {:?}", doc.uri.to_string()); info!("launch worker to parse {:?}", doc.uri.to_string());
let language_id = doc.language_id.to_string(); let language_id = doc.language_id.to_string();
@ -289,6 +305,7 @@ impl Sources {
match language_id.as_str() { match language_id.as_str() {
"vhdl" => { "vhdl" => {
vhdl_parser_pipeline( vhdl_parser_pipeline(
&conf,
&source_handle, &source_handle,
&scope_handle, &scope_handle,
&hdl_param_handle, &hdl_param_handle,
@ -298,6 +315,7 @@ impl Sources {
}, },
"verilog" | "systemverilog" => { "verilog" | "systemverilog" => {
sv_parser_pipeline( sv_parser_pipeline(
&conf,
&source_handle, &source_handle,
&scope_handle, &scope_handle,
&hdl_param_handle, &hdl_param_handle,
@ -360,15 +378,16 @@ impl Sources {
/// wait for a valid parse /// wait for a valid parse
pub fn wait_parse_ready(&self, id: usize, wait_valid: bool) { pub fn wait_parse_ready(&self, id: usize, wait_valid: bool) {
let file = self.get_file(id).unwrap(); if let Some(file) = self.get_file(id) {
let file = file.read().unwrap(); let file = file.read().unwrap();
if file.parse_ir.is_none() || wait_valid { if file.parse_ir.is_none() || wait_valid {
drop(file); drop(file);
let meta_data = self.get_meta_data(id).unwrap(); let meta_data = self.get_meta_data(id).unwrap();
let (lock, cvar) = &*meta_data.read().unwrap().valid_parse; let (lock, cvar) = &*meta_data.read().unwrap().valid_parse;
let mut valid = lock.lock().unwrap(); let mut valid = lock.lock().unwrap();
while !*valid { while !*valid {
valid = cvar.wait(valid).unwrap(); valid = cvar.wait(valid).unwrap();
}
} }
} }
} }
@ -562,6 +581,7 @@ pub fn recovery_sv_parse(
} }
pub fn sv_parser_pipeline( pub fn sv_parser_pipeline(
conf: &Arc<RwLock<ProjectConfig>>,
source_handle: &Arc<RwLock<Source>>, source_handle: &Arc<RwLock<Source>>,
scope_handle: &Arc<RwLock<Option<GenericScope>>>, scope_handle: &Arc<RwLock<Option<GenericScope>>>,
hdl_param_handle: &Arc<HdlParam>, hdl_param_handle: &Arc<HdlParam>,
@ -641,6 +661,7 @@ pub fn sv_parser_pipeline(
} }
pub fn vhdl_parser_pipeline( pub fn vhdl_parser_pipeline(
conf: &Arc<RwLock<ProjectConfig>>,
source_handle: &Arc<RwLock<Source>>, source_handle: &Arc<RwLock<Source>>,
scope_handle: &Arc<RwLock<Option<GenericScope>>>, scope_handle: &Arc<RwLock<Option<GenericScope>>>,
hdl_param_handle: &Arc<HdlParam>, hdl_param_handle: &Arc<HdlParam>,
@ -665,30 +686,41 @@ pub fn vhdl_parser_pipeline(
return; return;
} }
// TODO: 通过更加精细的方法获取下面的变量 let extension_path = {
let configure = conf.read().unwrap();
configure.extension_path.to_string()
};
let mut file = source_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 scope_tree = if let Some(design_file) = vhdl_parse(&escape_path) {
//let mut design_files = design_file_handle.write().unwrap(); if let Some(mut fast) = make_fast_from_design_file(&design_file) {
if let Some(fast) = make_fast_from_design_file(&design_file) {
let parse_ir = ParseIR::DesignFile(design_file); let parse_ir = ParseIR::DesignFile(design_file);
file.parse_ir = Some(parse_ir); file.parse_ir = Some(parse_ir);
let file_type = {
let fast_map = hdl_param_handle.path_to_hdl_file.read().unwrap();
if let Some(hdl_file) = fast_map.get(escape_path_string) {
hdl_file.fast.file_type.to_string()
} else {
if let Some(relative_path) = escape_path_string.strip_prefix(&extension_path) {
if relative_path.starts_with("/user/ip") || relative_path.starts_with("user/ip") {
"ip".to_string()
} else {
"common".to_string()
}
} else {
"common".to_string()
}
}
};
fast.file_type = file_type;
hdl_param_handle.update_fast(escape_path_string.to_string(), fast.clone()); hdl_param_handle.update_fast(escape_path_string.to_string(), fast.clone());
let scope_tree = get_scopes_from_vhdl_fast(&fast, doc, uri); let scope_tree = get_scopes_from_vhdl_fast(&fast, doc, uri);
scope_tree scope_tree
} else { } else {
None None
} }
// 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 { } else {
file.parse_ir = None; file.parse_ir = None;
None None