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

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

View File

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

View File

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

View File

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

View File

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

View File

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