完成自动补全的 output 自动申明 | 完成配置文件的前后端更新系统
This commit is contained in:
parent
290d1aec05
commit
d45c243d62
@ -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();
|
||||||
|
|
||||||
// 遍历 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 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::<String>::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_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),
|
||||||
@ -303,9 +320,37 @@ fn make_module_completions(
|
|||||||
insert_text_mode: Some(InsertTextMode::ADJUST_INDENTATION),
|
insert_text_mode: Some(InsertTextMode::ADJUST_INDENTATION),
|
||||||
..CompletionItem::default()
|
..CompletionItem::default()
|
||||||
};
|
};
|
||||||
module_completioms.push(item);
|
module_completioms.push(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
|
}
|
||||||
}
|
}
|
@ -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);
|
||||||
|
@ -33,6 +33,7 @@ pub mod utils;
|
|||||||
// LSP 服务器
|
// LSP 服务器
|
||||||
pub mod server;
|
pub mod server;
|
||||||
|
|
||||||
|
|
||||||
// 管理所有代码
|
// 管理所有代码
|
||||||
pub mod sources;
|
pub mod sources;
|
||||||
|
|
||||||
|
15
src/main.rs
15
src/main.rs
@ -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
53
src/request/config.rs
Normal 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
266
src/request/fast.rs
Normal 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
|
||||||
|
})
|
||||||
|
}
|
@ -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
37
src/request/primitives.rs
Normal 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
44
src/request/test.rs
Normal 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)
|
||||||
|
}
|
@ -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()
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -259,9 +268,7 @@ impl LanguageServer for Backend {
|
|||||||
async fn initialized(&self, _: InitializedParams) {
|
async fn initialized(&self, _: InitializedParams) {
|
||||||
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 ¶ms.content_changes {
|
|
||||||
info!("change: {:?}", change);
|
|
||||||
}
|
|
||||||
|
|
||||||
// // 如果文件太大则显示错误
|
// // 如果文件太大则显示错误
|
||||||
// if CacheManager::uri_is_big_file(¶ms.text_document.uri) {
|
// if CacheManager::uri_is_big_file(¶ms.text_document.uri) {
|
||||||
// // self.client.show_message(MessageType::WARNING, "考虑到性能问题,对于大于 1MB 的文件不会主动提供语言服务")
|
// // self.client.show_message(MessageType::WARNING, "考虑到性能问题,对于大于 1MB 的文件不会主动提供语言服务")
|
||||||
|
@ -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>>>,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user