完成自动补全的 output 自动申明 | 完成配置文件的前后端更新系统

This commit is contained in:
锦恢 2024-11-13 22:46:00 +08:00
parent 290d1aec05
commit d45c243d62
11 changed files with 517 additions and 391 deletions

View File

@ -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 log::info;
use ropey::{Rope, RopeSlice}; use ropey::{Rope, RopeSlice};
use tower_lsp::lsp_types::*; use tower_lsp::lsp_types::*;
@ -261,26 +263,40 @@ fn make_instantiation_code(module: &crate::core::hdlparam::Module) -> String {
snippet_codes.join("") snippet_codes.join("")
} }
/// 自动补全例化模块
fn make_module_completions( fn make_module_completions(
server: &LSPServer, server: &LSPServer,
token: &str, token: &str,
language_id: &str language_id: &str
) -> Vec<CompletionItem> { ) -> Vec<CompletionItem> {
let mut module_completioms = Vec::<CompletionItem>::new(); let mut module_completioms = Vec::<CompletionItem>::new();
let hdl_param = server.srcs.hdl_param.clone();
let prefix = token.to_string().to_lowercase(); let prefix = token.to_string().to_lowercase();
let path_to_files = server.srcs.hdl_param.path_to_hdl_file.read().unwrap(); let module_name_to_path = hdl_param.module_name_to_path.read().unwrap();
// 获取和自动补全相关的配置
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 // 遍历 hdlparam 中所有的 modules
for (path_string, hdl_file) in path_to_files.iter() { for module_name in module_name_to_path.keys() {
for module in &hdl_file.fast.content { if !module_name.to_string().to_lowercase().starts_with(&prefix) {
if !module.name.to_string().to_lowercase().starts_with(&prefix) {
continue; continue;
} }
let insert_text = make_instantiation_code(module); if let Some((module, file_type, def_path)) = hdl_param.find_module_context_by_name(&module_name) {
let module_profile = make_module_profile_code(module); let mut insert_text = Vec::<String>::new();
if auto_add_output_declaration {
if let Some(declaration_string) = make_output_declaration(&module) {
insert_text.push(declaration_string);
}
}
let path_uri = Url::from_file_path(path_string.to_string()).unwrap().to_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_row = module.range.start.line;
let def_col = module.range.start.character; let def_col = module.range.start.character;
let define_info = format!("Go to [Definition]({path_uri}#L{def_row}:{def_col})"); 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) value: format!("```{}\n{}\n```\n{}", language_id, module_profile, define_info)
}; };
let detail = format!("module instantiation ({})", file_type);
let item = CompletionItem { let item = CompletionItem {
label: module.name.to_string(), label: module.name.to_string(),
detail: Some("module instantiation".to_string()), detail: Some(detail),
documentation: Some(Documentation::MarkupContent(module_profile)), documentation: Some(Documentation::MarkupContent(module_profile)),
kind: Some(CompletionItemKind::CLASS), kind: Some(CompletionItemKind::CLASS),
insert_text: Some(insert_text), insert_text: Some(insert_text),
@ -309,3 +326,31 @@ fn make_module_completions(
module_completioms module_completioms
} }
fn make_output_declaration(
module: &core::hdlparam::Module
) -> Option<String> {
let mut output_declaration = Vec::<String>::new();
for port in &module.ports {
if port.dir_type == "output" || port.dir_type == "out" {
let mut declaration = Vec::<String>::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
}
}

View File

@ -780,14 +780,6 @@ pub fn make_entity_profile_code(module: &crate::core::hdlparam::Module) -> Strin
if module.ports.len() > 0 { if module.ports.len() > 0 {
codes.push("\tport (".to_string()); 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_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_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);

View File

@ -33,6 +33,7 @@ pub mod utils;
// LSP 服务器 // LSP 服务器
pub mod server; pub mod server;
// 管理所有代码 // 管理所有代码
pub mod sources; pub mod sources;

View File

@ -1,6 +1,12 @@
#![recursion_limit = "256"] #![recursion_limit = "256"]
use request::{ CustomParamRequest, CustomRequest, DoFastApi }; use request::{
test::CustomParamRequest,
test::CustomRequest,
fast::DoFastApi,
config::UpdateConfigurationApi,
primitives::DoPrimitivesJudgeApi
};
use log::info; use log::info;
use std::sync::Arc; use std::sync::Arc;
@ -43,10 +49,11 @@ async fn main() {
let backend = Arc::new(Backend::new(client, log_handle)); let backend = Arc::new(Backend::new(client, log_handle));
backend backend
}) })
.custom_method("custom/request", CustomRequest) .custom_method("custom/request", CustomRequest) // for test
.custom_method("custom/paramRequest", CustomParamRequest) .custom_method("custom/paramRequest", CustomParamRequest) // for test
.custom_method("api/fast", DoFastApi) .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(); .finish();
Server::new(stdin, stdout, socket) Server::new(stdin, stdout, socket)

53
src/request/config.rs Normal file
View File

@ -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<UpdateConfigurationItem>,
#[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<Backend>, (UpdateConfigurationParams, ), Result<()>> for UpdateConfigurationApi {
type Future = future::Ready<Result<()>>;
fn invoke(&self, _server: &'a Arc<Backend>, _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<UpdateConfigurationItem>,
backend: &Arc<Backend>
) {
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);
}
}

266
src/request/fast.rs Normal file
View File

@ -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<Backend>, (DoFastApiRequestParams, ), Result<FastHdlparam>> for DoFastApi {
type Future = future::Ready<Result<FastHdlparam>>;
fn invoke(&self, _server: &'a Arc<Backend>, _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<TextDocumentItem> {
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<Backend>
) -> Result<FastHdlparam> {
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<Backend>
) -> Result<FastHdlparam> {
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<PathBuf> = 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<Backend>
) -> Result<FastHdlparam> {
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
})
}

View File

@ -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; pub mod notification;
pub mod config;
#[derive(Clone)] pub mod primitives;
pub struct CustomRequest; pub mod fast;
pub mod test;
impl <'a>tower_lsp::jsonrpc::Method<&'a Arc<Backend>, (), Result<i32>> for CustomRequest {
type Future = future::Ready<Result<i32>>;
fn invoke(&self, _server: &'a Arc<Backend>, _params: ()) -> Self::Future {
future::ready(custom_request())
}
}
pub fn custom_request() -> Result<i32> {
Ok(123)
}
#[derive(Deserialize, Serialize, Debug)]
pub struct CustomParamRequestParams {
param: String,
}
#[derive(Clone)]
pub struct CustomParamRequest;
impl <'a>tower_lsp::jsonrpc::Method<&'a Arc<Backend>, (CustomParamRequestParams, ), Result<i32>> for CustomParamRequest {
type Future = future::Ready<Result<i32>>;
fn invoke(&self, _server: &'a Arc<Backend>, _params: (CustomParamRequestParams, )) -> Self::Future {
future::ready(custom_param_request(_params.0.param))
}
}
pub fn custom_param_request(param: String) -> Result<i32> {
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<Backend>, (DoFastApiRequestParams, ), Result<FastHdlparam>> for DoFastApi {
type Future = future::Ready<Result<FastHdlparam>>;
fn invoke(&self, _server: &'a Arc<Backend>, _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<TextDocumentItem> {
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<Backend>
) -> Result<FastHdlparam> {
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<Backend>
) -> Result<FastHdlparam> {
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<PathBuf> = 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<Backend>
) -> Result<FastHdlparam> {
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<Backend>) -> 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<Backend>, (DoFastApiRequestParams, ), Result<FastHdlparam>> for UpdateFastApi {
// type Future = future::Ready<Result<FastHdlparam>>;
// fn invoke(&self, _server: &'a Arc<Backend>, _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<Backend>) -> Result<FastHdlparam> {
// {
// 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)
// }
// }

37
src/request/primitives.rs Normal file
View File

@ -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<Backend>, (DoPrimitivesJudgeParams, ), Result<bool>> for DoPrimitivesJudgeApi {
type Future = future::Ready<Result<bool>>;
fn invoke(&self, _server: &'a Arc<Backend>, _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<Backend>
) -> 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)
}

44
src/request/test.rs Normal file
View File

@ -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<Backend>, (), Result<i32>> for CustomRequest {
type Future = future::Ready<Result<i32>>;
fn invoke(&self, _server: &'a Arc<Backend>, _params: ()) -> Self::Future {
future::ready(custom_request())
}
}
pub fn custom_request() -> Result<i32> {
Ok(123)
}
#[derive(Deserialize, Serialize, Debug)]
pub struct CustomParamRequestParams {
param: String,
}
#[derive(Clone)]
pub struct CustomParamRequest;
impl <'a>tower_lsp::jsonrpc::Method<&'a Arc<Backend>, (CustomParamRequestParams, ), Result<i32>> for CustomParamRequest {
type Future = future::Ready<Result<i32>>;
fn invoke(&self, _server: &'a Arc<Backend>, _params: (CustomParamRequestParams, )) -> Self::Future {
future::ready(custom_param_request(_params.0.param))
}
}
pub fn custom_param_request(param: String) -> Result<i32> {
info!("receive param: {:?}", param);
Ok(123)
}

View File

@ -238,6 +238,14 @@ impl LanguageServer for Backend {
completion_item: None, completion_item: None,
}; };
let workspace = WorkspaceServerCapabilities {
workspace_folders: Some(WorkspaceFoldersServerCapabilities {
supported: Some(true),
change_notifications: Some(OneOf::Left(true)),
}),
..Default::default()
};
let capabilities = ServerCapabilities { let capabilities = ServerCapabilities {
text_document_sync: Some(text_document_sync), text_document_sync: Some(text_document_sync),
completion_provider: Some(completion_provider), completion_provider: Some(completion_provider),
@ -246,6 +254,7 @@ impl LanguageServer for Backend {
inlay_hint_provider: Some(OneOf::Left(true)), inlay_hint_provider: Some(OneOf::Left(true)),
document_symbol_provider: Some(OneOf::Left(true)), document_symbol_provider: Some(OneOf::Left(true)),
document_highlight_provider: Some(OneOf::Left(true)), document_highlight_provider: Some(OneOf::Left(true)),
workspace: Some(workspace),
..ServerCapabilities::default() ..ServerCapabilities::default()
}; };
@ -260,8 +269,6 @@ impl LanguageServer for Backend {
self.client self.client
.log_message(MessageType::INFO, "Digital LSP initialized!") .log_message(MessageType::INFO, "Digital LSP initialized!")
.await; .await;
// self.client.send_notification::<StringNotification>(StringNotification { content: "hello from lsp server".to_string() }).await;
} }
async fn shutdown(&self) -> Result<()> { async fn shutdown(&self) -> Result<()> {
@ -286,11 +293,6 @@ 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);
for change in &params.content_changes {
info!("change: {:?}", change);
}
// // 如果文件太大则显示错误 // // 如果文件太大则显示错误
// 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

@ -15,6 +15,7 @@ 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 serde_json::Value;
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;
@ -30,15 +31,6 @@ use thread::JoinHandle;
use tower_lsp::lsp_types::*; use tower_lsp::lsp_types::*;
use walkdir::WalkDir; use walkdir::WalkDir;
macro_rules! unwrap_result {
($expr:expr) => {
match $expr {
Ok(e) => e,
Err(_) => return
}
};
}
impl LSPServer { impl LSPServer {
pub fn did_open(&self, params: DidOpenTextDocumentParams) -> PublishDiagnosticsParams { pub fn did_open(&self, params: DidOpenTextDocumentParams) -> PublishDiagnosticsParams {
let document: TextDocumentItem = params.text_document; let document: TextDocumentItem = params.text_document;
@ -180,6 +172,7 @@ pub enum ParseIR {
SyntaxTree(sv_parser::SyntaxTree) SyntaxTree(sv_parser::SyntaxTree)
} }
/// file metadata, including whether or not the syntax tree is up to date /// file metadata, including whether or not the syntax tree is up to date
pub struct SourceMeta { pub struct SourceMeta {
pub id: usize, pub id: usize,
@ -229,7 +222,9 @@ pub struct Sources {
/// hdlparam 后端实现 /// hdlparam 后端实现
pub hdl_param: Arc<HdlParam>, pub hdl_param: Arc<HdlParam>,
// 同步解析线程和发送 fast 请求的 // 同步解析线程和发送 fast 请求的
pub fast_sync_controller: Arc<RwLock<HashMap<String, Arc<RwLock<bool>>>>> pub fast_sync_controller: Arc<RwLock<HashMap<String, Arc<RwLock<bool>>>>>,
// lsp 配置相关的
pub lsp_configuration: Arc<RwLock<HashMap<String, Value>>>
} }
impl std::default::Default for Sources { impl std::default::Default for Sources {
@ -248,7 +243,8 @@ impl Sources {
include_dirs: Arc::new(RwLock::new(Vec::new())), include_dirs: Arc::new(RwLock::new(Vec::new())),
primitive_text: Arc::new(PrimitiveText::new()), primitive_text: Arc::new(PrimitiveText::new()),
hdl_param: Arc::new(HdlParam::new()), hdl_param: Arc::new(HdlParam::new()),
fast_sync_controller: Arc::new(RwLock::new(HashMap::<String, Arc<RwLock<bool>>>::new())) fast_sync_controller: Arc::new(RwLock::new(HashMap::<String, Arc<RwLock<bool>>>::new())),
lsp_configuration: Arc::new(RwLock::new(HashMap::<String, Value>::new()))
} }
} }
@ -448,6 +444,30 @@ impl Sources {
.get_dot_completion(token, byte_idx, url, tree.as_ref()?), .get_dot_completion(token, byte_idx, url, tree.as_ref()?),
}) })
} }
pub fn get_lsp_configuration_string_value(&self, name: &str) -> Option<String> {
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<i64> {
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<bool> {
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( pub fn sv_parser_pipeline(
#[allow(unused)]
conf: &Arc<RwLock<ProjectConfig>>, 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>>>,