2024-12-07 15:47:46 +08:00

370 lines
13 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use std::borrow::Cow;
use std::str::FromStr;
use std::{fs, future};
use std::path::{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 vhdl_lang::Project;
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_units;
use crate::definition::VhdlProject;
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)
}
}
#[derive(Deserialize, Serialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct SyncFastApiRequestParams {
path: String,
file_type: String,
tool_chain: String
}
#[derive(Clone)]
pub struct SyncFastApi;
impl <'a>tower_lsp::jsonrpc::Method<&'a Arc<Backend>, (SyncFastApiRequestParams, ), Result<FastHdlparam>> for SyncFastApi {
type Future = future::Ready<Result<FastHdlparam>>;
fn invoke(&self, _server: &'a Arc<Backend>, _params: (SyncFastApiRequestParams, )) -> 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 = sync_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);
// 从 pathbuf 中读取文本并作为 TextDocumentItem 打开
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();
let vhdl_project = sources.vhdl_project.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(),
arch_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(vhdl_project) = &mut *vhdl_project.write().unwrap() {
vhdl_project.config_file_strs.push(format!("{:?}", pathbuf));
let mut messages = Vec::new();
let config_str = format!(
r#"
[libraries]
digital_lsp.files = [{}]
"#, vhdl_project.config_file_strs.join(",")
);
let config = vhdl_lang::Config::from_str(&config_str, Path::new(""));
if let Ok(mut config) = config {
config.append(&vhdl_project.std_config, &mut messages);
let mut project = Project::from_config(config, &mut messages);
project.analyse();
*vhdl_project = VhdlProject { project, std_config: vhdl_project.std_config.clone(), config_file_strs: vhdl_project.config_file_strs.clone() };
}
let arch_and_entity = vhdl_project.project.get_analyzed_units(&pathbuf);
if let Some(mut fast) = make_fast_from_units(arch_and_entity) {
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(vhdl_project) = &mut *vhdl_project.write().unwrap() {
vhdl_project.config_file_strs.push(format!("{:?}", pathbuf));
let mut messages = Vec::new();
let config_str = format!(
r#"
[libraries]
digital_lsp.files = [{}]
"#, vhdl_project.config_file_strs.join(",")
);
let config = vhdl_lang::Config::from_str(&config_str, Path::new(""));
if let Ok(mut config) = config {
config.append(&vhdl_project.std_config, &mut messages);
let mut project = Project::from_config(config, &mut messages);
project.analyse();
*vhdl_project = VhdlProject { project, std_config: vhdl_project.std_config.clone(), config_file_strs: vhdl_project.config_file_strs.clone() };
}
let arch_and_entity = vhdl_project.project.get_analyzed_units(&pathbuf);
if let Some(mut fast) = make_fast_from_units(arch_and_entity) {
fast.file_type = file_type.to_string();
// for module in &fast.content {
// if module.name == "None" {
// info!("debug, module : {:?}, path: {:?}", module, pathbuf);
// }
// }
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
})
}
pub fn sync_fast(
path: String,
file_type: String,
tool_chain: String,
backend: &Arc<Backend>
) -> Result<FastHdlparam> {
// 根据 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 uri = Url::from_file_path(path.to_string()).unwrap();
let file_id = backend.server.srcs.get_id(&uri);
if let Some(file) = backend.server.srcs.get_file(file_id) {
let _unused = file.read().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, file_type, tool_chain, backend)
}
}