From d45c243d624d1500a8f7f88d1d71b9b38fe62b28 Mon Sep 17 00:00:00 2001 From: LSTM-Kirigaya <1193466151@qq.com> Date: Wed, 13 Nov 2024 22:46:00 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=E8=87=AA=E5=8A=A8=E8=A1=A5?= =?UTF-8?q?=E5=85=A8=E7=9A=84=20output=20=E8=87=AA=E5=8A=A8=E7=94=B3?= =?UTF-8?q?=E6=98=8E=20|=20=E5=AE=8C=E6=88=90=E9=85=8D=E7=BD=AE=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E7=9A=84=E5=89=8D=E5=90=8E=E7=AB=AF=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/completion/sv.rs | 73 ++++++-- src/hover/feature.rs | 8 - src/lib.rs | 1 + src/main.rs | 15 +- src/request/config.rs | 53 ++++++ src/request/fast.rs | 266 +++++++++++++++++++++++++++++ src/request/mod.rs | 350 +------------------------------------- src/request/primitives.rs | 37 ++++ src/request/test.rs | 44 +++++ src/server.rs | 18 +- src/sources.rs | 43 +++-- 11 files changed, 517 insertions(+), 391 deletions(-) create mode 100644 src/request/config.rs create mode 100644 src/request/fast.rs create mode 100644 src/request/primitives.rs create mode 100644 src/request/test.rs diff --git a/src/completion/sv.rs b/src/completion/sv.rs index 0d87213..fd270c9 100644 --- a/src/completion/sv.rs +++ b/src/completion/sv.rs @@ -1,4 +1,6 @@ -use crate::{completion::feature::{get_dot_completion, include_path_completion}, hover::feature::make_module_profile_code, server::LSPServer, sources::LSPSupport, utils::get_language_id_by_uri}; +use std::collections::HashSet; + +use crate::{completion::feature::{get_dot_completion, include_path_completion}, core, hover::feature::make_module_profile_code, server::LSPServer, sources::LSPSupport, utils::get_language_id_by_uri}; use log::info; use ropey::{Rope, RopeSlice}; use tower_lsp::lsp_types::*; @@ -261,26 +263,40 @@ fn make_instantiation_code(module: &crate::core::hdlparam::Module) -> String { snippet_codes.join("") } +/// 自动补全例化模块 fn make_module_completions( server: &LSPServer, token: &str, language_id: &str ) -> Vec { let mut module_completioms = Vec::::new(); + let hdl_param = server.srcs.hdl_param.clone(); let prefix = token.to_string().to_lowercase(); - let path_to_files = server.srcs.hdl_param.path_to_hdl_file.read().unwrap(); - - // 遍历 hdlparam 中所有的 modules - for (path_string, hdl_file) in path_to_files.iter() { - for module in &hdl_file.fast.content { - if !module.name.to_string().to_lowercase().starts_with(&prefix) { - continue; - } - let insert_text = make_instantiation_code(module); - let module_profile = make_module_profile_code(module); + let module_name_to_path = hdl_param.module_name_to_path.read().unwrap(); - let path_uri = Url::from_file_path(path_string.to_string()).unwrap().to_string(); + // 获取和自动补全相关的配置 + let auto_add_output_declaration = server.srcs.get_lsp_configuration_bool_value("digital-ide.function.lsp.completion.vlog.auto-add-output-declaration").unwrap_or(true); + + // 遍历 hdlparam 中所有的 modules + for module_name in module_name_to_path.keys() { + if !module_name.to_string().to_lowercase().starts_with(&prefix) { + continue; + } + if let Some((module, file_type, def_path)) = hdl_param.find_module_context_by_name(&module_name) { + let mut insert_text = Vec::::new(); + if auto_add_output_declaration { + if let Some(declaration_string) = make_output_declaration(&module) { + insert_text.push(declaration_string); + } + } + + insert_text.push(make_instantiation_code(&module)); + + let insert_text = insert_text.join("\n"); + let module_profile = make_module_profile_code(&module); + + let path_uri = Url::from_file_path(def_path).unwrap().to_string(); let def_row = module.range.start.line; let def_col = module.range.start.character; let define_info = format!("Go to [Definition]({path_uri}#L{def_row}:{def_col})"); @@ -290,10 +306,11 @@ fn make_module_completions( value: format!("```{}\n{}\n```\n{}", language_id, module_profile, define_info) }; + let detail = format!("module instantiation ({})", file_type); let item = CompletionItem { label: module.name.to_string(), - detail: Some("module instantiation".to_string()), + detail: Some(detail), documentation: Some(Documentation::MarkupContent(module_profile)), kind: Some(CompletionItemKind::CLASS), insert_text: Some(insert_text), @@ -303,9 +320,37 @@ fn make_module_completions( insert_text_mode: Some(InsertTextMode::ADJUST_INDENTATION), ..CompletionItem::default() }; - module_completioms.push(item); + module_completioms.push(item); } } module_completioms +} + +fn make_output_declaration( + module: &core::hdlparam::Module +) -> Option { + let mut output_declaration = Vec::::new(); + for port in &module.ports { + if port.dir_type == "output" || port.dir_type == "out" { + let mut declaration = Vec::::new(); + if port.net_type == "reg" || port.net_type == "wire" { + declaration.push(port.net_type.to_string()); + } else { + declaration.push("wire".to_string()); + } + if port.width != "1" { + declaration.push(port.width.to_string()); + } + declaration.push(port.name.to_string()); + output_declaration.push(format!("{};", declaration.join(" "))); + } + } + + if output_declaration.len() > 0 { + let output_declaration = format!("// output declaration of module {}\n{}\n", module.name, output_declaration.join("\n")); + Some(output_declaration) + } else { + None + } } \ No newline at end of file diff --git a/src/hover/feature.rs b/src/hover/feature.rs index 3637c2e..2d1b556 100644 --- a/src/hover/feature.rs +++ b/src/hover/feature.rs @@ -780,14 +780,6 @@ pub fn make_entity_profile_code(module: &crate::core::hdlparam::Module) -> Strin if module.ports.len() > 0 { codes.push("\tport (".to_string()); - let net_mapper = |net_type: &str| { - if net_type == "unknown" { - 0 - } else { - net_type.len() - } - }; - let max_port_length = module.ports.iter().map(|port| port.name.len()).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); diff --git a/src/lib.rs b/src/lib.rs index 4ab6e0f..1be98ca 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -33,6 +33,7 @@ pub mod utils; // LSP 服务器 pub mod server; + // 管理所有代码 pub mod sources; diff --git a/src/main.rs b/src/main.rs index 6de7f73..94ce59b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,12 @@ #![recursion_limit = "256"] -use request::{ CustomParamRequest, CustomRequest, DoFastApi }; +use request::{ + test::CustomParamRequest, + test::CustomRequest, + fast::DoFastApi, + config::UpdateConfigurationApi, + primitives::DoPrimitivesJudgeApi +}; use log::info; use std::sync::Arc; @@ -43,10 +49,11 @@ async fn main() { let backend = Arc::new(Backend::new(client, log_handle)); backend }) - .custom_method("custom/request", CustomRequest) - .custom_method("custom/paramRequest", CustomParamRequest) + .custom_method("custom/request", CustomRequest) // for test + .custom_method("custom/paramRequest", CustomParamRequest) // for test .custom_method("api/fast", DoFastApi) - // .custom_method("api/update-fast", UpdateFastApi) + .custom_method("api/do-primitives-judge", DoPrimitivesJudgeApi) + .custom_method("api/update-fast", UpdateConfigurationApi) .finish(); Server::new(stdin, stdout, socket) diff --git a/src/request/config.rs b/src/request/config.rs new file mode 100644 index 0000000..75bcc94 --- /dev/null +++ b/src/request/config.rs @@ -0,0 +1,53 @@ +use std::sync::Arc; +use std::future; +use log::info; +use serde::{Deserialize, Serialize}; +use serde_json::Value; +use tower_lsp::jsonrpc::Result; + +use crate::server::Backend; + +#[derive(Clone)] +pub struct UpdateConfigurationApi; + +#[derive(Deserialize, Serialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct UpdateConfigurationParams { + configs: Vec, + #[serde(rename = "configType")] + config_type: String +} + +#[derive(Deserialize, Serialize, Debug)] +#[serde(rename_all = "camelCase")] +struct UpdateConfigurationItem { + name: String, + value: Value +} + +impl <'a>tower_lsp::jsonrpc::Method<&'a Arc, (UpdateConfigurationParams, ), Result<()>> for UpdateConfigurationApi { + type Future = future::Ready>; + + fn invoke(&self, _server: &'a Arc, _params: (UpdateConfigurationParams, )) -> Self::Future { + let request_param = _params.0; + let configs = request_param.configs; + + // 用于未来进行配置分区 + #[allow(unused)] + let config_type = request_param.config_type; + + update_configuration(configs, &_server); + future::ready(Ok(())) + } +} + +fn update_configuration( + configs: Vec, + backend: &Arc +) { + let mut lsp_configuration = backend.server.srcs.lsp_configuration.write().unwrap(); + for config in configs { + info!("name: {}, value: {}", config.name, config.value); + lsp_configuration.insert(config.name, config.value); + } +} \ No newline at end of file diff --git a/src/request/fast.rs b/src/request/fast.rs new file mode 100644 index 0000000..4dbfaf3 --- /dev/null +++ b/src/request/fast.rs @@ -0,0 +1,266 @@ +use std::borrow::Cow; +use std::str::FromStr; +use std::{fs, future}; +use std::path::PathBuf; +use std::sync::Arc; +use log::info; +use ropey::Rope; +use serde::{Deserialize, Serialize}; +use tower_lsp::jsonrpc::Result; +use tower_lsp::lsp_types::*; + +use crate::core::cache_storage::CacheResult; +use crate::core::hdlparam::FastHdlparam; +use crate::core::sv_parser::make_fast_from_syntaxtree; + +use crate::core::vhdl_parser::{make_fast_from_design_file, vhdl_parse}; +use crate::{core, utils::*}; +use crate::server::Backend; +use crate::sources::recovery_sv_parse_with_retry; + + +#[derive(Deserialize, Serialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct DoFastApiRequestParams { + path: String, + file_type: String, + tool_chain: String +} + + +#[derive(Clone)] +pub struct DoFastApi; + +impl <'a>tower_lsp::jsonrpc::Method<&'a Arc, (DoFastApiRequestParams, ), Result> for DoFastApi { + type Future = future::Ready>; + + fn invoke(&self, _server: &'a Arc, _params: (DoFastApiRequestParams, )) -> Self::Future { + let request_param = _params.0; + let path = request_param.path; + let file_type = request_param.file_type; + let tool_chain = request_param.tool_chain; + let hdlparam = do_fast(path, file_type, tool_chain, _server); + future::ready(hdlparam) + } +} + +fn make_textdocumenitem_from_path(path_buf: &PathBuf) -> Option { + if let Ok(url) = Url::from_file_path(path_buf) { + if let Ok(text) = fs::read_to_string(path_buf) { + let language_id = get_language_id_by_uri(&url); + return Some(TextDocumentItem::new(url, language_id, -1, text)); + } + } + None +} + +/// 前端交互接口: do_fast,输入文件路径,计算出对应的 fast 结构 +pub fn do_fast( + path: String, + file_type: String, + tool_chain: String, + backend: &Arc +) -> Result { + info!("parse fast {:?}, type: {:?}, toolchain: {:?}", path, file_type, tool_chain); + + // 根据 file_type 和 tool_chain 计算正确的 path + let path = { + if file_type == "ip" && tool_chain == "xilinx" { + let pathbuf = PathBuf::from_str(&path).unwrap(); + let basename = pathbuf.file_name().unwrap().to_str().unwrap(); + format!("{}/synth/{}.vhd", path, basename) + } else { + path + } + }; + + let language_id = get_language_id_by_path_str(&path); + + let parse_fast_by_language_id = |language_id: &str| { + match language_id { + "vhdl" => { + do_vhdl_fast( + &path, + &file_type, + &tool_chain, + backend + ) + } + + "verilog" | "systemverilog" => { + do_sv_fast( + &path, + &file_type, + &tool_chain, + backend + ) + } + + _ => Err(tower_lsp::jsonrpc::Error { + code: tower_lsp::jsonrpc::ErrorCode::InvalidRequest, + message: Cow::Owned(format!("invalid file: {path}, expect vhdl, verilog or system verilog!")), + data: None + }) + } + }; + + let path_buf = PathBuf::from_str(&path).unwrap(); + // 做缓存优化 + let cache = &backend.server.cache; + match cache.try_get_fast_cache(&path_buf) { + // 找到缓存,直接返回 + CacheResult::Ok(fast) => { + return Ok(fast); + } + + // cache 没找到,那么就需要计算并存入 + CacheResult::CacheNotFound => { + match parse_fast_by_language_id(&language_id) { + Ok(fast) => { + cache.update_cache(&path_buf, fast.clone()); + return Ok(fast); + }, + Err(err) => Err(err) + } + } + + // 不需要缓存的文件正常进行 fast 计算即可 + _ => { + parse_fast_by_language_id(&language_id) + } + } +} + +fn do_sv_fast( + path: &str, + #[allow(unused)] + file_type: &str, + #[allow(unused)] + tool_chain: &str, + backend: &Arc +) -> Result { + let path_buf = PathBuf::from(&path); + + let doc = match make_textdocumenitem_from_path(&path_buf) { + Some(doc) => doc, + None => { + let api_error = tower_lsp::jsonrpc::Error { + code: tower_lsp::jsonrpc::ErrorCode::InvalidParams, + message: Cow::Owned(format!("cannot make doc from path : {path}")), + data: None + }; + + return Err(api_error); + } + }; + + let uri = doc.uri; + let text = Rope::from(doc.text); + // fast 解析不需要 include + let includes: Vec = Vec::new(); + + let parse_result = recovery_sv_parse_with_retry( + &text, + &uri, + &None, + &includes + ); + + let sources = &backend.server.srcs; + if let Some(syntax_tree) = parse_result { + if let Ok(mut fast) = make_fast_from_syntaxtree(&syntax_tree, &path_buf) { + fast.file_type = file_type.to_string(); + let hdl_param = sources.hdl_param.clone(); + hdl_param.update_fast(path.to_string(), fast.clone()); + return Ok(fast); + } + } + + Err(tower_lsp::jsonrpc::Error { + code: tower_lsp::jsonrpc::ErrorCode::ParseError, + message: Cow::Owned(format!("error happen when parse {path} [do_sv_fast]")), + data: None + }) +} + +fn do_vhdl_fast( + path: &str, + #[allow(unused)] + file_type: &str, + #[allow(unused)] + tool_chain: &str, + backend: &Arc +) -> Result { + let sources = &backend.server.srcs; + let pathbuf = PathBuf::from_str(path).unwrap(); + let hdl_param = sources.hdl_param.clone(); + + // TODO: 支持对于 synth 下的 vhdl 文件的解析,从而提供更加丰富的 IP 支持 + if file_type == "ip" { + match tool_chain { + // 此时的 pathbuf 类似 {ip_name}/synth/{ip_name}.vhd + "xilinx" => { + // 如果 ip 描述文件存在,则解析它,否则,创建空的写入 + // IP 描述文件一般都不会很大,所以不需要缓存 + if !pathbuf.exists() { + let ip_name = pathbuf.file_name().unwrap().to_str().unwrap(); + let ip_name = ip_name.strip_suffix(".vhd").unwrap(); + let fake_content = vec![ + core::hdlparam::Module { + name: ip_name.to_string(), + params: vec![], + ports: vec![], + instances: vec![], + range: core::hdlparam::Range::default() + } + ]; + let ip_fast = core::hdlparam::FastHdlparam { + fast_macro: core::hdlparam::Macro { + includes: vec![], + defines: vec![], + errors: vec![], + invalid: vec![] + }, + file_type: "ip".to_string(), + content: fake_content + }; + hdl_param.update_fast(path.to_string(), ip_fast.clone()); + return Ok(ip_fast); + } else if let Some(design_file) = vhdl_parse(&pathbuf) { + if let Some(mut fast) = make_fast_from_design_file(&design_file) { + fast.file_type = file_type.to_string(); + // IP 不需要内部的 instance,其实现是加密的,只需要暴露 module 的接口即可 + // 所以此处需要清空所有的 module 中的 instance + for module in &mut fast.content { + module.instances.clear(); + } + hdl_param.update_fast(path.to_string(), fast.clone()); + return Ok(fast); + } + } else { + return Err(tower_lsp::jsonrpc::Error { + code: tower_lsp::jsonrpc::ErrorCode::ParseError, + message: Cow::Owned(format!("error happen when parse {path} in [do_vhdl_fast]")), + data: None + }); + } + }, + _ => {} + } + } + + // 没有特殊情况,则正常解析并写入 + if let Some(design_file) = vhdl_parse(&pathbuf) { + if let Some(mut fast) = make_fast_from_design_file(&design_file) { + fast.file_type = file_type.to_string(); + hdl_param.update_fast(path.to_string(), fast.clone()); + return Ok(fast); + } + } + + Err(tower_lsp::jsonrpc::Error { + code: tower_lsp::jsonrpc::ErrorCode::ParseError, + message: Cow::Owned(format!("error happen when parse {path} in [do_vhdl_fast]")), + data: None + }) +} diff --git a/src/request/mod.rs b/src/request/mod.rs index 9ab8109..d42d4ce 100644 --- a/src/request/mod.rs +++ b/src/request/mod.rs @@ -1,347 +1,5 @@ -use std::borrow::Cow; -use std::str::FromStr; -use std::{fs, future}; -use std::path::PathBuf; -use std::sync::Arc; - -use log::info; -use ropey::Rope; -use serde::{Deserialize, Serialize}; -use tower_lsp::jsonrpc::Result; -use tower_lsp::lsp_types::*; - -use crate::core::cache_storage::CacheResult; -use crate::core::hdlparam::FastHdlparam; -use crate::core::sv_parser::make_fast_from_syntaxtree; - -use crate::core::vhdl_parser::{make_fast_from_design_file, vhdl_parse}; -use crate::{core, utils::*}; -use crate::server::Backend; -use crate::sources::recovery_sv_parse_with_retry; - pub mod notification; - -#[derive(Clone)] -pub struct CustomRequest; - -impl <'a>tower_lsp::jsonrpc::Method<&'a Arc, (), Result> for CustomRequest { - type Future = future::Ready>; - - fn invoke(&self, _server: &'a Arc, _params: ()) -> Self::Future { - future::ready(custom_request()) - } -} - -pub fn custom_request() -> Result { - Ok(123) -} - -#[derive(Deserialize, Serialize, Debug)] -pub struct CustomParamRequestParams { - param: String, -} - -#[derive(Clone)] -pub struct CustomParamRequest; - -impl <'a>tower_lsp::jsonrpc::Method<&'a Arc, (CustomParamRequestParams, ), Result> for CustomParamRequest { - type Future = future::Ready>; - - fn invoke(&self, _server: &'a Arc, _params: (CustomParamRequestParams, )) -> Self::Future { - future::ready(custom_param_request(_params.0.param)) - } -} - -pub fn custom_param_request(param: String) -> Result { - info!("receive param: {:?}", param); - Ok(123) -} - - -#[derive(Deserialize, Serialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct DoFastApiRequestParams { - path: String, - file_type: String, - tool_chain: String -} - - -#[derive(Clone)] -pub struct DoFastApi; - -impl <'a>tower_lsp::jsonrpc::Method<&'a Arc, (DoFastApiRequestParams, ), Result> for DoFastApi { - type Future = future::Ready>; - - fn invoke(&self, _server: &'a Arc, _params: (DoFastApiRequestParams, )) -> Self::Future { - let request_param = _params.0; - let path = request_param.path; - let file_type = request_param.file_type; - let tool_chain = request_param.tool_chain; - let hdlparam = do_fast(path, file_type, tool_chain, _server); - future::ready(hdlparam) - } -} - -fn make_textdocumenitem_from_path(path_buf: &PathBuf) -> Option { - if let Ok(url) = Url::from_file_path(path_buf) { - if let Ok(text) = fs::read_to_string(path_buf) { - let language_id = get_language_id_by_uri(&url); - return Some(TextDocumentItem::new(url, language_id, -1, text)); - } - } - None -} - -/// 前端交互接口: do_fast,输入文件路径,计算出对应的 fast 结构 -pub fn do_fast( - path: String, - file_type: String, - tool_chain: String, - backend: &Arc -) -> Result { - info!("parse fast {:?}, type: {:?}, toolchain: {:?}", path, file_type, tool_chain); - - // 根据 file_type 和 tool_chain 计算正确的 path - let path = { - if file_type == "ip" && tool_chain == "xilinx" { - let pathbuf = PathBuf::from_str(&path).unwrap(); - let basename = pathbuf.file_name().unwrap().to_str().unwrap(); - format!("{}/synth/{}.vhd", path, basename) - } else { - path - } - }; - - let language_id = get_language_id_by_path_str(&path); - - let parse_fast_by_language_id = |language_id: &str| { - match language_id { - "vhdl" => { - do_vhdl_fast( - &path, - &file_type, - &tool_chain, - backend - ) - } - - "verilog" | "systemverilog" => { - do_sv_fast( - &path, - &file_type, - &tool_chain, - backend - ) - } - - _ => Err(tower_lsp::jsonrpc::Error { - code: tower_lsp::jsonrpc::ErrorCode::InvalidRequest, - message: Cow::Owned(format!("invalid file: {path}, expect vhdl, verilog or system verilog!")), - data: None - }) - } - }; - - let path_buf = PathBuf::from_str(&path).unwrap(); - // 做缓存优化 - let cache = &backend.server.cache; - match cache.try_get_fast_cache(&path_buf) { - // 找到缓存,直接返回 - CacheResult::Ok(fast) => { - return Ok(fast); - } - - // cache 没找到,那么就需要计算并存入 - CacheResult::CacheNotFound => { - match parse_fast_by_language_id(&language_id) { - Ok(fast) => { - cache.update_cache(&path_buf, fast.clone()); - return Ok(fast); - }, - Err(err) => Err(err) - } - } - - // 不需要缓存的文件正常进行 fast 计算即可 - _ => { - parse_fast_by_language_id(&language_id) - } - } -} - -fn do_sv_fast( - path: &str, - #[allow(unused)] - file_type: &str, - #[allow(unused)] - tool_chain: &str, - backend: &Arc -) -> Result { - let path_buf = PathBuf::from(&path); - - let doc = match make_textdocumenitem_from_path(&path_buf) { - Some(doc) => doc, - None => { - let api_error = tower_lsp::jsonrpc::Error { - code: tower_lsp::jsonrpc::ErrorCode::InvalidParams, - message: Cow::Owned(format!("cannot make doc from path : {path}")), - data: None - }; - - return Err(api_error); - } - }; - - let uri = doc.uri; - let text = Rope::from(doc.text); - // fast 解析不需要 include - let includes: Vec = Vec::new(); - - let parse_result = recovery_sv_parse_with_retry( - &text, - &uri, - &None, - &includes - ); - - let sources = &backend.server.srcs; - if let Some(syntax_tree) = parse_result { - if let Ok(mut fast) = make_fast_from_syntaxtree(&syntax_tree, &path_buf) { - fast.file_type = file_type.to_string(); - let hdl_param = sources.hdl_param.clone(); - hdl_param.update_fast(path.to_string(), fast.clone()); - return Ok(fast); - } - } - - Err(tower_lsp::jsonrpc::Error { - code: tower_lsp::jsonrpc::ErrorCode::ParseError, - message: Cow::Owned(format!("error happen when parse {path} [do_sv_fast]")), - data: None - }) -} - -fn do_vhdl_fast( - path: &str, - #[allow(unused)] - file_type: &str, - #[allow(unused)] - tool_chain: &str, - backend: &Arc -) -> Result { - let sources = &backend.server.srcs; - let pathbuf = PathBuf::from_str(path).unwrap(); - let hdl_param = sources.hdl_param.clone(); - - // TODO: 支持对于 synth 下的 vhdl 文件的解析,从而提供更加丰富的 IP 支持 - if file_type == "ip" { - match tool_chain { - // 此时的 pathbuf 类似 {ip_name}/synth/{ip_name}.vhd - "xilinx" => { - // 如果 ip 描述文件存在,则解析它,否则,创建空的写入 - // IP 描述文件一般都不会很大,所以不需要缓存 - if !pathbuf.exists() { - let ip_name = pathbuf.file_name().unwrap().to_str().unwrap(); - let ip_name = ip_name.strip_suffix(".vhd").unwrap(); - let fake_content = vec![ - core::hdlparam::Module { - name: ip_name.to_string(), - params: vec![], - ports: vec![], - instances: vec![], - range: core::hdlparam::Range::default() - } - ]; - let ip_fast = core::hdlparam::FastHdlparam { - fast_macro: core::hdlparam::Macro { - includes: vec![], - defines: vec![], - errors: vec![], - invalid: vec![] - }, - file_type: "ip".to_string(), - content: fake_content - }; - hdl_param.update_fast(path.to_string(), ip_fast.clone()); - return Ok(ip_fast); - } else if let Some(design_file) = vhdl_parse(&pathbuf) { - if let Some(mut fast) = make_fast_from_design_file(&design_file) { - fast.file_type = file_type.to_string(); - // IP 不需要内部的 instance,其实现是加密的,只需要暴露 module 的接口即可 - // 所以此处需要清空所有的 module 中的 instance - for module in &mut fast.content { - module.instances.clear(); - } - hdl_param.update_fast(path.to_string(), fast.clone()); - return Ok(fast); - } - } else { - return Err(tower_lsp::jsonrpc::Error { - code: tower_lsp::jsonrpc::ErrorCode::ParseError, - message: Cow::Owned(format!("error happen when parse {path} in [do_vhdl_fast]")), - data: None - }); - } - }, - _ => {} - } - } - - // 没有特殊情况,则正常解析并写入 - if let Some(design_file) = vhdl_parse(&pathbuf) { - if let Some(mut fast) = make_fast_from_design_file(&design_file) { - fast.file_type = file_type.to_string(); - hdl_param.update_fast(path.to_string(), fast.clone()); - return Ok(fast); - } - } - - Err(tower_lsp::jsonrpc::Error { - code: tower_lsp::jsonrpc::ErrorCode::ParseError, - message: Cow::Owned(format!("error happen when parse {path} in [do_vhdl_fast]")), - data: None - }) -} - -fn do_primitives_judge(name: &str, backend: &Arc) -> bool { - let sources = &backend.server.srcs; - let primitive_text = sources.primitive_text.clone(); - let primitive_map = primitive_text.name_to_text.read().unwrap(); - primitive_map.contains_key(name) -} - -// #[derive(Clone)] -// pub struct UpdateFastApi; - -// impl <'a>tower_lsp::jsonrpc::Method<&'a Arc, (DoFastApiRequestParams, ), Result> for UpdateFastApi { -// type Future = future::Ready>; - -// fn invoke(&self, _server: &'a Arc, _params: (DoFastApiRequestParams, )) -> Self::Future { -// let request_param = _params.0; -// let path = request_param.path; -// let hdlparam = update_fast(path, _server); -// future::ready(hdlparam) -// } -// } - -// pub fn update_fast(path: String, backend: &Arc) -> Result { -// { -// let fast_sync_controller = backend.server.srcs.fast_sync_controller.read().unwrap(); -// if let Some(fast_lock) = fast_sync_controller.get(&path) { -// let fast_lock = fast_lock.clone(); -// drop(fast_sync_controller); -// let _unused = fast_lock.write().unwrap(); -// } -// } - -// // 去缓存中寻找 -// let hdl_param = backend.server.srcs.hdl_param.clone(); -// let path_to_hdl_file = hdl_param.path_to_hdl_file.read().unwrap(); -// if let Some(hdl_file) = path_to_hdl_file.get(&path) { -// let fast = hdl_file.fast.clone(); -// Ok(fast) -// } else { -// do_fast(path, backend) -// } -// } \ No newline at end of file +pub mod config; +pub mod primitives; +pub mod fast; +pub mod test; \ No newline at end of file diff --git a/src/request/primitives.rs b/src/request/primitives.rs new file mode 100644 index 0000000..d41bdd1 --- /dev/null +++ b/src/request/primitives.rs @@ -0,0 +1,37 @@ +use std::future; +use std::sync::Arc; +use serde::{Deserialize, Serialize}; +use tower_lsp::jsonrpc::Result; +use crate::server::Backend; + +#[derive(Deserialize, Serialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct DoPrimitivesJudgeParams { + name: String +} + +#[derive(Clone)] +pub struct DoPrimitivesJudgeApi; + +impl <'a>tower_lsp::jsonrpc::Method<&'a Arc, (DoPrimitivesJudgeParams, ), Result> for DoPrimitivesJudgeApi { + type Future = future::Ready>; + + fn invoke(&self, _server: &'a Arc, _params: (DoPrimitivesJudgeParams, )) -> Self::Future { + let request_param = _params.0; + let primitive_name = request_param.name; + let is_primitive = do_primitives_judge(&primitive_name, &_server); + future::ready(Ok(is_primitive)) + } +} + +fn do_primitives_judge( + name: &str, + backend: &Arc +) -> bool { + let sources = &backend.server.srcs; + let primitive_text = sources.primitive_text.clone(); + let primitive_map = primitive_text.name_to_text.read().unwrap(); + primitive_map.contains_key(name) +} + + diff --git a/src/request/test.rs b/src/request/test.rs new file mode 100644 index 0000000..3753a84 --- /dev/null +++ b/src/request/test.rs @@ -0,0 +1,44 @@ +use std::future; +use std::sync::Arc; + +use log::info; +use serde::{Deserialize, Serialize}; +use tower_lsp::jsonrpc::Result; +use crate::server::Backend; + + +#[derive(Clone)] +pub struct CustomRequest; + +impl <'a>tower_lsp::jsonrpc::Method<&'a Arc, (), Result> for CustomRequest { + type Future = future::Ready>; + + fn invoke(&self, _server: &'a Arc, _params: ()) -> Self::Future { + future::ready(custom_request()) + } +} + +pub fn custom_request() -> Result { + Ok(123) +} + +#[derive(Deserialize, Serialize, Debug)] +pub struct CustomParamRequestParams { + param: String, +} + +#[derive(Clone)] +pub struct CustomParamRequest; + +impl <'a>tower_lsp::jsonrpc::Method<&'a Arc, (CustomParamRequestParams, ), Result> for CustomParamRequest { + type Future = future::Ready>; + + fn invoke(&self, _server: &'a Arc, _params: (CustomParamRequestParams, )) -> Self::Future { + future::ready(custom_param_request(_params.0.param)) + } +} + +pub fn custom_param_request(param: String) -> Result { + info!("receive param: {:?}", param); + Ok(123) +} \ No newline at end of file diff --git a/src/server.rs b/src/server.rs index 71a7a33..44a051f 100644 --- a/src/server.rs +++ b/src/server.rs @@ -238,6 +238,14 @@ impl LanguageServer for Backend { completion_item: None, }; + let workspace = WorkspaceServerCapabilities { + workspace_folders: Some(WorkspaceFoldersServerCapabilities { + supported: Some(true), + change_notifications: Some(OneOf::Left(true)), + }), + ..Default::default() + }; + let capabilities = ServerCapabilities { text_document_sync: Some(text_document_sync), completion_provider: Some(completion_provider), @@ -246,6 +254,7 @@ impl LanguageServer for Backend { inlay_hint_provider: Some(OneOf::Left(true)), document_symbol_provider: Some(OneOf::Left(true)), document_highlight_provider: Some(OneOf::Left(true)), + workspace: Some(workspace), ..ServerCapabilities::default() }; @@ -259,9 +268,7 @@ impl LanguageServer for Backend { async fn initialized(&self, _: InitializedParams) { self.client .log_message(MessageType::INFO, "Digital LSP initialized!") - .await; - - // self.client.send_notification::(StringNotification { content: "hello from lsp server".to_string() }).await; + .await; } async fn shutdown(&self) -> Result<()> { @@ -286,11 +293,6 @@ impl LanguageServer for Backend { } async fn did_change(&self, params: DidChangeTextDocumentParams) { - info!("file did change: {:?}", params.text_document.uri); - for change in ¶ms.content_changes { - info!("change: {:?}", change); - } - // // 如果文件太大则显示错误 // if CacheManager::uri_is_big_file(¶ms.text_document.uri) { // // self.client.show_message(MessageType::WARNING, "考虑到性能问题,对于大于 1MB 的文件不会主动提供语言服务") diff --git a/src/sources.rs b/src/sources.rs index 31f3882..9a01801 100644 --- a/src/sources.rs +++ b/src/sources.rs @@ -15,6 +15,7 @@ use log::info; use log::{debug, error}; use pathdiff::diff_paths; use ropey::{Rope, RopeSlice}; +use serde_json::Value; use std::cmp::min; use std::collections::HashMap; use std::env::current_dir; @@ -30,15 +31,6 @@ use thread::JoinHandle; use tower_lsp::lsp_types::*; use walkdir::WalkDir; -macro_rules! unwrap_result { - ($expr:expr) => { - match $expr { - Ok(e) => e, - Err(_) => return - } - }; -} - impl LSPServer { pub fn did_open(&self, params: DidOpenTextDocumentParams) -> PublishDiagnosticsParams { let document: TextDocumentItem = params.text_document; @@ -180,6 +172,7 @@ pub enum ParseIR { SyntaxTree(sv_parser::SyntaxTree) } + /// file metadata, including whether or not the syntax tree is up to date pub struct SourceMeta { pub id: usize, @@ -229,7 +222,9 @@ pub struct Sources { /// hdlparam 后端实现 pub hdl_param: Arc, // 同步解析线程和发送 fast 请求的 - pub fast_sync_controller: Arc>>>> + pub fast_sync_controller: Arc>>>>, + // lsp 配置相关的 + pub lsp_configuration: Arc>> } impl std::default::Default for Sources { @@ -248,7 +243,8 @@ impl Sources { include_dirs: Arc::new(RwLock::new(Vec::new())), primitive_text: Arc::new(PrimitiveText::new()), hdl_param: Arc::new(HdlParam::new()), - fast_sync_controller: Arc::new(RwLock::new(HashMap::>>::new())) + fast_sync_controller: Arc::new(RwLock::new(HashMap::>>::new())), + lsp_configuration: Arc::new(RwLock::new(HashMap::::new())) } } @@ -448,6 +444,30 @@ impl Sources { .get_dot_completion(token, byte_idx, url, tree.as_ref()?), }) } + + pub fn get_lsp_configuration_string_value(&self, name: &str) -> Option { + let lsp_configuration = self.lsp_configuration.read().unwrap(); + if let Some(Value::String(value)) = lsp_configuration.get(name) { + return Some(value.to_string()); + } + None + } + + pub fn get_lsp_configuration_i64_value(&self, name: &str) -> Option { + let lsp_configuration = self.lsp_configuration.read().unwrap(); + if let Some(Value::Number(number)) = lsp_configuration.get(name) { + return number.as_i64(); + } + None + } + + pub fn get_lsp_configuration_bool_value(&self, name: &str) -> Option { + let lsp_configuration = self.lsp_configuration.read().unwrap(); + if let Some(Value::Bool(value)) = lsp_configuration.get(name) { + return Some(*value); + } + None + } } @@ -597,6 +617,7 @@ pub fn recovery_sv_parse( } pub fn sv_parser_pipeline( + #[allow(unused)] conf: &Arc>, source_handle: &Arc>, scope_handle: &Arc>>,