From fa7b42f09b2b06d63ae97dac6a929dcb2f43e0fa Mon Sep 17 00:00:00 2001 From: LSTM-Kirigaya <1193466151@qq.com> Date: Mon, 16 Dec 2024 00:44:33 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=9C=AC=E7=BC=93?= =?UTF-8?q?=E5=86=B2=E5=8C=BA=E5=A4=87=E4=BB=BD=E7=9A=84=E7=B4=A2=E5=BC=95?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F=20|=20=E5=B0=86=20AST=20=E7=9A=84=E5=AD=98?= =?UTF-8?q?=E5=82=A8=E5=9C=B0=E7=82=B9=E4=BB=8E=20Sources=20=E4=B8=AD?= =?UTF-8?q?=E7=A7=BB=E5=8A=A8=E5=88=B0=20HdlFile=20=E5=86=85=E9=83=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/completion/sv.rs | 20 +-- src/completion/vhdl.rs | 58 ++----- src/core/hdlparam.rs | 11 +- src/definition/sv.rs | 29 ++-- src/definition/vhdl.rs | 22 ++- src/diagnostics/mod.rs | 2 - src/document_highlight/mod.rs | 18 ++- src/document_highlight/sv.rs | 37 +++-- src/document_highlight/vhdl.rs | 33 ++-- src/document_symbol/sv.rs | 16 +- src/document_symbol/vhdl.rs | 15 +- src/hover/sv.rs | 29 ++-- src/hover/vhdl.rs | 21 +-- src/inlay_hint/sv.rs | 17 +-- src/request/fast.rs | 19 ++- src/sources.rs | 269 +++++++++++++++++---------------- 16 files changed, 297 insertions(+), 319 deletions(-) diff --git a/src/completion/sv.rs b/src/completion/sv.rs index 3409fa6..5a3444f 100644 --- a/src/completion/sv.rs +++ b/src/completion/sv.rs @@ -1,4 +1,4 @@ -use crate::{completion::feature::{get_dot_completion, include_path_completion}, core, hover::feature::make_module_profile_code, server::LspServer, sources::LSPSupport, utils::{get_definition_token, get_language_id_by_uri, is_character_ordered_match}}; +use crate::{completion::feature::{get_dot_completion, include_path_completion}, core, hover::feature::make_module_profile_code, server::LspServer, sources::LSPSupport, utils::{from_uri_to_escape_path_string, get_definition_token, get_language_id_by_uri, is_character_ordered_match}}; #[allow(unused)] use log::info; use tower_lsp::lsp_types::*; @@ -12,14 +12,16 @@ pub fn completion(server: &LspServer, params: &CompletionParams) -> Option match context.trigger_kind { @@ -65,7 +67,7 @@ pub fn completion(server: &LspServer, params: &CompletionParams) -> Option Option Option Option Option None, }, - None => { - let trigger = prev_char(&file.text, &doc.position); - match trigger { - // '.' => Some(server.srcs.get_dot_completions( - // token.trim_end_matches('.'), - // file.text.pos_to_byte(&doc.position), - // &doc.text_document.uri, - // )?), - // '$' => Some(CompletionList { - // is_incomplete: false, - // items: server.sys_tasks.clone(), - // }), - // '`' => Some(CompletionList { - // is_incomplete: false, - // items: server.directives.clone(), - // }), - _ => { - let mut comps = server.srcs.get_completions( - &token, - file.text.pos_to_byte(&doc.position), - &doc.text_document.uri, - )?; - comps.items.extend::>( - server.vhdl_keyword_completiom_items - .iter() - .filter(|x| x.label.starts_with(&token)) - .cloned() - .collect(), - ); - - // comps.items.extend(vhdl_project_completion_items); - - comps.items.dedup_by_key(|i| i.label.clone()); - Some(comps) - } - } - } + None => None }; // eprintln!("comp response: {}", now.elapsed().as_millis()); Some(CompletionResponse::List(response?)) diff --git a/src/core/hdlparam.rs b/src/core/hdlparam.rs index a2c150e..9838719 100644 --- a/src/core/hdlparam.rs +++ b/src/core/hdlparam.rs @@ -6,6 +6,8 @@ use serde::{Deserialize, Serialize}; use tower_lsp::lsp_types::Position as LspPosition; use tower_lsp::lsp_types::Range as LspRange; +use crate::sources::AstLike; + #[derive(Debug, Clone, Serialize, PartialEq, PartialOrd, Eq, Ord, Deserialize)] pub struct Position { pub line: u32, @@ -451,7 +453,9 @@ pub struct HdlFile { /// 名字到 module 映射的 map pub name_to_module: HashMap, /// 解析器生成的额外信息 - pub parse_result: sv_parser::common::ParseResult + pub parse_result: sv_parser::common::ParseResult, + /// 解析器生成的 AST 或者类似 AST 的数据结构 + pub ast_like: Option } pub struct HdlParam { @@ -476,7 +480,8 @@ impl HdlParam { &self, path: String, fast: FastHdlparam, - parse_result: sv_parser::common::ParseResult + parse_result: sv_parser::common::ParseResult, + ast_like: Option ) { let mut fast_map = self.path_to_hdl_file.write().unwrap(); // 构建映射 @@ -490,7 +495,7 @@ impl HdlParam { } } - let file = HdlFile { fast, name_to_module, parse_result }; + let file = HdlFile { fast, name_to_module, parse_result, ast_like }; fast_map.insert(path, file); } diff --git a/src/definition/sv.rs b/src/definition/sv.rs index b5e70a7..d467e5b 100644 --- a/src/definition/sv.rs +++ b/src/definition/sv.rs @@ -1,7 +1,7 @@ use crate::core::scope_tree::common::{Definition, Scope}; -use crate::utils::get_definition_token; -use crate::server::LspServer; use crate::sources::LSPSupport; +use crate::utils::{from_uri_to_escape_path_string, get_definition_token}; +use crate::server::LspServer; #[allow(unused)] use log::info; @@ -9,18 +9,21 @@ use tower_lsp::lsp_types::*; use super::feature::*; pub fn goto_definition(server: &LspServer, params: &GotoDefinitionParams) -> Option { - let doc = ¶ms.text_document_position_params.text_document.uri; + let uri = ¶ms.text_document_position_params.text_document.uri; let pos = params.text_document_position_params.position; - let file_id = server.srcs.get_id(doc).to_owned(); - server.srcs.wait_parse_ready(file_id, false); - let file = server.srcs.get_file(file_id)?; - let file = file.read().ok()?; - let line_text = file.text.line(pos.line as usize); + let path_string = from_uri_to_escape_path_string(uri).unwrap(); + + // 等待解析完成 + server.srcs.wait_parse_ready(&path_string, false); + let source = server.srcs.get_source(&path_string)?; + let source = source.read().ok()?; + + let line_text = source.text.line(pos.line as usize); let token: String = get_definition_token(&line_text, pos); // match include - if let Some(definition) = goto_include_definition(doc, &line_text, pos) { + if let Some(definition) = goto_include_definition(uri, &line_text, pos) { return Some(definition); } @@ -33,7 +36,7 @@ pub fn goto_definition(server: &LspServer, params: &GotoDefinitionParams) -> Opt let scope_tree = server.srcs.scope_tree.read().ok()?; // match position port & param - if let Some(definition) = goto_position_port_param_definition(server, &line_text, doc, pos) { + if let Some(definition) = goto_position_port_param_definition(server, &line_text, uri, pos) { return Some(definition); } @@ -42,11 +45,11 @@ pub fn goto_definition(server: &LspServer, params: &GotoDefinitionParams) -> Opt return Some(definition); } - let byte_idx = file.text.pos_to_byte(&pos); + let byte_idx = source.text.pos_to_byte(&pos); let global_scope = scope_tree.as_ref()?; - let def = global_scope.get_definition(&token, byte_idx, doc)?; + let def = global_scope.get_definition(&token, byte_idx, uri)?; - let def_pos = file.text.byte_to_pos(def.byte_idx()); + let def_pos = source.text.byte_to_pos(def.byte_idx()); Some(GotoDefinitionResponse::Scalar(Location::new( def.url(), Range::new(def_pos, def_pos), diff --git a/src/definition/vhdl.rs b/src/definition/vhdl.rs index 1ec1834..6eea0eb 100644 --- a/src/definition/vhdl.rs +++ b/src/definition/vhdl.rs @@ -3,22 +3,28 @@ use std::{path::PathBuf, str::FromStr}; #[allow(unused)] use log::info; use tower_lsp::lsp_types::*; -use crate::{server::LspServer, utils::{from_lsp_pos, get_definition_token, srcpos_to_location, to_escape_path}}; +use crate::{server::LspServer, utils::{from_lsp_pos, from_uri_to_escape_path_string, get_definition_token, srcpos_to_location, to_escape_path}}; pub fn goto_vhdl_definition(server: &LspServer, params: &GotoDefinitionParams) -> Option { - let doc = ¶ms.text_document_position_params.text_document.uri; + let uri = ¶ms.text_document_position_params.text_document.uri; let pos = params.text_document_position_params.position; - let file_id = server.srcs.get_id(doc).to_owned(); - server.srcs.wait_parse_ready(file_id, false); - let file = server.srcs.get_file(file_id)?; - let file = file.read().ok()?; - let line_text = file.text.line(pos.line as usize); + let path_string = from_uri_to_escape_path_string(uri).unwrap(); + + // 等待解析完成 + server.srcs.wait_parse_ready(&path_string, false); + let source = server.srcs.get_source(&path_string)?; + let source = source.read().ok()?; + + let line_text = source.text.line(pos.line as usize); + + #[allow(unused)] let token: String = get_definition_token(&line_text, pos); let project = server.srcs.vhdl_project.read().ok()?; let global_project = project.as_ref().unwrap(); - let path = match PathBuf::from_str(doc.path()) { + + let path = match PathBuf::from_str(uri.path()) { Ok(path) => path, Err(error) => { info!("error happen in vhdl : {:?}", error); diff --git a/src/diagnostics/mod.rs b/src/diagnostics/mod.rs index 399794b..487a17a 100644 --- a/src/diagnostics/mod.rs +++ b/src/diagnostics/mod.rs @@ -35,8 +35,6 @@ pub use modelsim::*; pub fn provide_diagnostics( uri: Url, rope: &Rope, - #[allow(unused_variables)] - files: Vec, configuration: &LspConfiguration, server: &LspServer ) -> PublishDiagnosticsParams { diff --git a/src/document_highlight/mod.rs b/src/document_highlight/mod.rs index ff46154..de80e5b 100644 --- a/src/document_highlight/mod.rs +++ b/src/document_highlight/mod.rs @@ -1,4 +1,4 @@ -use crate::{server::LspServer, utils::{get_definition_token, get_language_id_by_uri}}; +use crate::{server::LspServer, utils::{from_uri_to_escape_path_string, get_definition_token, get_language_id_by_uri}}; use tower_lsp::lsp_types::*; mod sv; @@ -11,11 +11,15 @@ impl LspServer { ) -> Option> { let uri = ¶ms.text_document_position_params.text_document.uri; let pos = params.text_document_position_params.position; - let file_id = self.srcs.get_id(&uri).to_owned(); - self.srcs.wait_parse_ready(file_id, false); - let file = self.srcs.get_file(file_id)?; - let file = file.read().ok()?; - let line_text = file.text.line(pos.line as usize); + + let path_string = from_uri_to_escape_path_string(uri).unwrap(); + + // 等待解析完成 + self.srcs.wait_parse_ready(&path_string, false); + let source = self.srcs.get_source(&path_string)?; + let source = source.read().ok()?; + + let line_text = source.text.line(pos.line as usize); let token = get_definition_token(&line_text, pos); let language_id = get_language_id_by_uri(&uri); @@ -32,7 +36,7 @@ impl LspServer { "verilog" | "systemverilog" => sv::document_highlight( self, &token, - &file, + &source, pos, &uri ), diff --git a/src/document_highlight/sv.rs b/src/document_highlight/sv.rs index 84a88cd..039ce45 100644 --- a/src/document_highlight/sv.rs +++ b/src/document_highlight/sv.rs @@ -3,7 +3,7 @@ use log::info; use sv_parser::{RefNode, SyntaxTree}; use tower_lsp::lsp_types::*; -use crate::{server::LspServer, sources::{LSPSupport, ParseIR, Source}}; +use crate::{server::LspServer, sources::{AstLike, LSPSupport, Source}, utils::from_uri_to_escape_path_string}; use crate::core::scope_tree::parse::get_ident; use crate::core::scope_tree::common::Scope; @@ -16,29 +16,26 @@ pub fn document_highlight( ) -> Option> { let scope_tree = server.srcs.scope_tree.read().ok()?; + let path_string = from_uri_to_escape_path_string(uri).unwrap(); + // use the byte_idx of the definition if possible, otherwise use the cursor - let byte_idx = - match scope_tree - .as_ref()? - .get_definition(token, file.text.pos_to_byte(&pos), uri) - { - Some(def) => def.byte_idx, - None => file.text.pos_to_byte(&pos), - }; - let syntax_tree = file.parse_ir.as_ref()?; - match syntax_tree { - ParseIR::SyntaxTree(syntax_tree) => { + let byte_idx = match scope_tree.as_ref()?.get_definition(token, file.text.pos_to_byte(&pos), uri) { + Some(def) => def.byte_idx, + None => file.text.pos_to_byte(&pos), + }; + + // 获取对应的 AST + let path_to_hdl_file = server.srcs.hdl_param.path_to_hdl_file.read().unwrap(); + if let Some(hdl_file) = path_to_hdl_file.get(&path_string) { + if let Some(AstLike::Svlog(syntax_tree)) = &hdl_file.ast_like { let references = all_identifiers(&syntax_tree, &token); - Some( - scope_tree - .as_ref()? - .document_highlights(&uri, &file.text, references, byte_idx), - ) - }, - _ => { - info!("error happen in [sv_document_highlight]"); + let highlights = scope_tree.as_ref()?.document_highlights(&uri, &file.text, references, byte_idx); + Some(highlights) + } else { None } + } else { + None } } diff --git a/src/document_highlight/vhdl.rs b/src/document_highlight/vhdl.rs index 307ec4d..10b08c2 100644 --- a/src/document_highlight/vhdl.rs +++ b/src/document_highlight/vhdl.rs @@ -2,7 +2,7 @@ use log::info; use sv_parser::{RefNode, SyntaxTree}; use tower_lsp::lsp_types::*; -use crate::{server::LspServer, sources::{LSPSupport, ParseIR, Source}}; +use crate::{server::LspServer, sources::{LSPSupport, Source}}; use crate::core::scope_tree::parse::get_ident; use crate::core::scope_tree::common::Scope; @@ -16,29 +16,14 @@ pub fn document_highlight( let scope_tree = server.srcs.scope_tree.read().ok()?; // use the byte_idx of the definition if possible, otherwise use the cursor - let byte_idx = - match scope_tree - .as_ref()? - .get_definition(token, file.text.pos_to_byte(&pos), uri) - { - Some(def) => def.byte_idx, - None => file.text.pos_to_byte(&pos), - }; - let syntax_tree = file.parse_ir.as_ref()?; - match syntax_tree { - ParseIR::SyntaxTree(syntax_tree) => { - let references = all_identifiers(&syntax_tree, &token); - Some( - scope_tree - .as_ref()? - .document_highlights(&uri, &file.text, references, byte_idx), - ) - }, - _ => { - info!("error happen in [vhdl_document_highlight]"); - None - } - } + let byte_idx = match scope_tree.as_ref()?.get_definition(token, file.text.pos_to_byte(&pos), uri) { + Some(def) => def.byte_idx, + None => file.text.pos_to_byte(&pos), + }; + + // TODO: 完成剩余部分 + + None } /// return all identifiers in a syntax tree matching a given token diff --git a/src/document_symbol/sv.rs b/src/document_symbol/sv.rs index d5e62c8..8dbf7aa 100644 --- a/src/document_symbol/sv.rs +++ b/src/document_symbol/sv.rs @@ -1,4 +1,4 @@ -use crate::{core::scope_tree::common::Scope, server::LspServer}; +use crate::{core::scope_tree::common::Scope, server::LspServer, utils::from_uri_to_escape_path_string}; use tower_lsp::lsp_types::*; pub fn document_symbol( @@ -6,13 +6,17 @@ pub fn document_symbol( params: &DocumentSymbolParams ) -> Option { let uri = ¶ms.text_document.uri; - let file_id = server.srcs.get_id(uri).to_owned(); - server.srcs.wait_parse_ready(file_id, false); - let file = server.srcs.get_file(file_id)?; - let file = file.read().ok()?; + + let path_string = from_uri_to_escape_path_string(uri).unwrap(); + + // 等待解析完成 + server.srcs.wait_parse_ready(&path_string, false); + let source = server.srcs.get_source(&path_string)?; + let source = source.read().ok()?; + let scope_tree = server.srcs.scope_tree.read().ok()?; Some(DocumentSymbolResponse::Nested( - scope_tree.as_ref()?.document_symbols(uri, &file.text), + scope_tree.as_ref()?.document_symbols(uri, &source.text), )) } \ No newline at end of file diff --git a/src/document_symbol/vhdl.rs b/src/document_symbol/vhdl.rs index 07f3803..fa88edc 100644 --- a/src/document_symbol/vhdl.rs +++ b/src/document_symbol/vhdl.rs @@ -4,16 +4,17 @@ use std::{path::PathBuf, str::FromStr}; use log::info; use tower_lsp::lsp_types::*; use vhdl_lang::{EntHierarchy, Token}; -use crate::{server::LspServer, utils::{to_escape_path, to_lsp_range, to_symbol_kind}}; +use crate::{server::LspServer, utils::{from_uri_to_escape_path_string, to_escape_path, to_lsp_range, to_symbol_kind}}; pub fn document_symbol(server: &LspServer, params: &DocumentSymbolParams) -> Option { - // info!("enter document symbol"); - let uri = ¶ms.text_document.uri; - let file_id = server.srcs.get_id(&uri).to_owned(); - server.srcs.wait_parse_ready(file_id, false); - let file = server.srcs.get_file(file_id)?; - let file = file.read().ok()?; + let path_string = from_uri_to_escape_path_string(uri).unwrap(); + + // 等待解析完成 + server.srcs.wait_parse_ready(&path_string, false); + // let source_handle = server.srcs.get_source(&path_string)?; + // let source_handle = source_handle.read().ok()?; + let scope_tree = server.srcs.scope_tree.read().ok()?; let project = server.srcs.vhdl_project.read().ok()?; diff --git a/src/hover/sv.rs b/src/hover/sv.rs index 7e1983e..c3ff5e9 100644 --- a/src/hover/sv.rs +++ b/src/hover/sv.rs @@ -4,7 +4,7 @@ use regex::Regex; use ropey::Rope; use tower_lsp::lsp_types::*; use crate::{core::{hdlparam::{Instance, Module}, scope_tree::common::Scope}, hover::{to_escape_path, BracketMatchResult, BracketMatcher}, server::LspServer, sources::LSPSupport}; -use super::feature::*; +use super::{feature::*, from_uri_to_escape_path_string}; use std::{path::PathBuf, str::FromStr, sync::RwLockReadGuard}; use crate::core::scope_tree::common::*; @@ -12,19 +12,22 @@ use crate::core::scope_tree::common::*; use super::{get_definition_token, get_language_id_by_uri}; pub fn hover(server: &LspServer, params: &HoverParams) -> Option { - let doc = ¶ms.text_document_position_params.text_document.uri; + let uri = ¶ms.text_document_position_params.text_document.uri; let pos: Position = params.text_document_position_params.position; - let file_id: usize = server.srcs.get_id(doc).to_owned(); - server.srcs.wait_parse_ready(file_id, false); - let file: std::sync::Arc> = server.srcs.get_file(file_id)?; - let file: std::sync::RwLockReadGuard<'_, crate::sources::Source> = file.read().ok()?; + + let path_string = from_uri_to_escape_path_string(uri).unwrap(); + + // 等待解析完成 + server.srcs.wait_parse_ready(&path_string, false); + let source = server.srcs.get_source(&path_string)?; + let source = source.read().ok()?; - let line_text = file.text.line(pos.line as usize); + let line_text = source.text.line(pos.line as usize); let token: String = get_definition_token(&line_text, pos); - let language_id = get_language_id_by_uri(doc); + let language_id = get_language_id_by_uri(uri); // match `include - if let Some(hover) = hover_include(doc, &line_text, pos, &language_id) { + if let Some(hover) = hover_include(uri, &line_text, pos, &language_id) { return Some(hover); } @@ -35,13 +38,13 @@ pub fn hover(server: &LspServer, params: &HoverParams) -> Option { // info!("enter hover_position_port_param"); // match positional port param - if let Some(hover) = hover_position_port_param(server, &line_text, doc, pos, &language_id) { + if let Some(hover) = hover_position_port_param(server, &line_text, uri, pos, &language_id) { return Some(hover); } // info!("enter hover_module_declaration"); // match module name - if hover_for_module(server, pos, doc) { + if hover_for_module(server, pos, uri) { if let Some(hover) = hover_module_declaration(server, &token, &language_id) { return Some(hover); } @@ -56,10 +59,10 @@ pub fn hover(server: &LspServer, params: &HoverParams) -> Option { let global_scope = scope_tree.as_ref().unwrap(); let symbol_definition: GenericDec = global_scope - .get_definition(&token, file.text.pos_to_byte(&pos), doc)?; + .get_definition(&token, source.text.pos_to_byte(&pos), uri)?; // match 正常 symbol - if let Some(hover) = hover_common_symbol(server, &token, &symbol_definition, &file, doc, pos, &language_id) { + if let Some(hover) = hover_common_symbol(server, &token, &symbol_definition, &source, uri, pos, &language_id) { return Some(hover); } diff --git a/src/hover/vhdl.rs b/src/hover/vhdl.rs index 2ec654e..bd43b73 100644 --- a/src/hover/vhdl.rs +++ b/src/hover/vhdl.rs @@ -7,24 +7,25 @@ use tower_lsp::lsp_types::*; use crate::{core::hdlparam::{Instance, Module}, hover::{BracketMatchResult, BracketMatcher}, server::LspServer}; use crate::core::scope_tree::common::*; -use super::{from_lsp_pos, get_definition_token, get_language_id_by_uri, to_escape_path}; +use super::{from_lsp_pos, from_uri_to_escape_path_string, get_definition_token, get_language_id_by_uri, to_escape_path}; pub fn hover(server: &LspServer, params: &HoverParams) -> Option { - let doc = ¶ms.text_document_position_params.text_document.uri; + let uri = ¶ms.text_document_position_params.text_document.uri; let pos: Position = params.text_document_position_params.position; - let file_id: usize = server.srcs.get_id(doc).to_owned(); - server.srcs.wait_parse_ready(file_id, false); - let file: std::sync::Arc> = server.srcs.get_file(file_id)?; - let file: std::sync::RwLockReadGuard<'_, crate::sources::Source> = file.read().ok()?; + + let path_string = from_uri_to_escape_path_string(uri).unwrap(); + + // 等待解析完成 + server.srcs.wait_parse_ready(&path_string, false); + let source = server.srcs.get_source(&path_string)?; + let source = source.read().ok()?; - let line_text = file.text.line(pos.line as usize); - let token: String = get_definition_token(&line_text, pos); - let language_id = get_language_id_by_uri(doc); + let line_text = source.text.line(pos.line as usize); let project = server.srcs.vhdl_project.read().ok()?; let global_project = project.as_ref().unwrap(); - let path = match PathBuf::from_str(doc.path()) { + let path = match PathBuf::from_str(uri.path()) { Ok(path) => path, Err(error) => { info!("error happen in vhdl : {:?}", error); diff --git a/src/inlay_hint/sv.rs b/src/inlay_hint/sv.rs index bbfbab7..34e3ba3 100644 --- a/src/inlay_hint/sv.rs +++ b/src/inlay_hint/sv.rs @@ -15,18 +15,15 @@ pub fn inlay_hint(server: &LspServer, params: &InlayHintParams) -> Option::new(); // 制作例化模块的 hint diff --git a/src/request/fast.rs b/src/request/fast.rs index 4b831ab..5debb89 100644 --- a/src/request/fast.rs +++ b/src/request/fast.rs @@ -200,7 +200,8 @@ fn do_sv_fast( hdl_param.update_hdl_file( path.to_string(), fast.clone(), - parse_result + parse_result, + Some(crate::sources::AstLike::Svlog(syntax_tree)) ); return Ok(fast); } @@ -260,7 +261,8 @@ fn do_vhdl_fast( hdl_param.update_hdl_file( path.to_string(), ip_fast.clone(), - sv_parser::common::ParseResult::new() + sv_parser::common::ParseResult::new(), + None ); return Ok(ip_fast); } else if let Some(vhdl_project) = &mut *vhdl_project.write().unwrap() { @@ -293,7 +295,8 @@ fn do_vhdl_fast( hdl_param.update_hdl_file( path.to_string(), fast.clone(), - sv_parser::common::ParseResult::new() + sv_parser::common::ParseResult::new(), + None ); return Ok(fast); } @@ -338,7 +341,8 @@ fn do_vhdl_fast( hdl_param.update_hdl_file( path.to_string(), fast.clone(), - sv_parser::common::ParseResult::new() + sv_parser::common::ParseResult::new(), + None ); return Ok(fast); } @@ -371,9 +375,10 @@ pub fn sync_fast( { 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 path_string = from_uri_to_escape_path_string(&uri).unwrap(); + + if let Some(source_handle) = backend.server.srcs.get_source(&path_string) { + let _unused = source_handle.read().unwrap(); } } diff --git a/src/sources.rs b/src/sources.rs index b32500c..b301e57 100644 --- a/src/sources.rs +++ b/src/sources.rs @@ -16,6 +16,7 @@ use log::{info, debug, error}; use pathdiff::diff_paths; use ropey::{Rope, RopeSlice}; use serde_json::Value; +use vhdl_lang::ast::DesignFile; use std::cmp::min; use std::collections::HashMap; use std::env::current_dir; @@ -25,7 +26,8 @@ use std::ops::Range as StdRange; use std::path::Path; use std::path::PathBuf; use std::str::FromStr; -use std::sync::{Arc, Condvar, Mutex, RwLock}; +use std::sync::Arc; +use std::sync::{Condvar, Mutex, RwLock}; use std::thread; use sv_parser::*; use vhdl_lang::Project; @@ -36,10 +38,10 @@ impl LspServer { pub fn did_open(&self, params: DidOpenTextDocumentParams) -> PublishDiagnosticsParams { let document: TextDocumentItem = params.text_document; let uri = document.uri.clone(); - - // check if doc is already added - if self.srcs.names.read().unwrap().contains_key(&document.uri) { - // convert to a did_change that replace the entire text + let path_string = from_uri_to_escape_path_string(&uri).unwrap(); + + if self.srcs.contain_source(&path_string) { + // 如果已经存在了,则进行增量更新 self.did_change(DidChangeTextDocumentParams { text_document: VersionedTextDocumentIdentifier::new(document.uri, document.version), content_changes: vec![TextDocumentContentChangeEvent { @@ -49,18 +51,17 @@ impl LspServer { }], }); } else { + // 如果不存在,直接加入其中 self.srcs.add(self, document); } - + + // 生成诊断信息 - let urls = self.srcs.names.read().unwrap().keys().cloned().collect(); - let file_id = self.srcs.get_id(&uri); - if let Some(file) = self.srcs.get_file(file_id) { - let file = file.read().unwrap(); + if let Some(source) = self.srcs.get_source(&path_string) { + let source = source.read().unwrap(); let diagnostics = provide_diagnostics( uri, - &file.text, - urls, + &source.text, &self.configuration.read().unwrap(), &self ); @@ -75,25 +76,26 @@ impl LspServer { } pub fn did_change(&self, params: DidChangeTextDocumentParams) { - let file_id = self.srcs.get_id(¶ms.text_document.uri); - if let Some(file) = self.srcs.get_file(file_id) { - let mut file = file.write().unwrap(); + let path_string = from_uri_to_escape_path_string(¶ms.text_document.uri).unwrap(); + + if let Some(source) = self.srcs.get_source(&path_string) { + let mut source = source.write().unwrap(); // 根据输入的 change 动态更新对应的代码的文本片段 for change in params.content_changes { if change.range.is_none() { - file.text = Rope::from_str(&change.text); + source.text = Rope::from_str(&change.text); } else { - file.text.apply_change(&change); + source.text.apply_change(&change); } - file.last_change_range = change.range; + source.last_change_range = change.range; } - file.version = params.text_document.version; - drop(file); + source.version = params.text_document.version; + drop(source); - // invalidate syntaxtree and wake parse thread - let meta_data = self.srcs.get_meta_data(file_id).unwrap(); - let (lock, cvar) = &*meta_data.read().unwrap().valid_parse; + // 唤醒解析线程 + let source_status = self.srcs.get_source_status(&path_string).unwrap(); + let (lock, cvar) = &*source_status.read().unwrap().valid_parse; let mut valid = lock.lock().unwrap(); *valid = false; cvar.notify_all(); @@ -103,16 +105,14 @@ impl LspServer { /// 保存时触发的行为 /// - 提供诊断信息(主要是第三方诊断器,因为 save 后磁盘上的内容就和缓冲区中的一致了) pub fn did_save(&self, params: DidSaveTextDocumentParams) -> PublishDiagnosticsParams { - let urls = self.srcs.names.read().unwrap().keys().cloned().collect(); - let file_id = self.srcs.get_id(¶ms.text_document.uri); - let uri = params.text_document.uri.clone(); + let uri = params.text_document.uri; + let path_string = from_uri_to_escape_path_string(&uri).unwrap(); - if let Some(file) = self.srcs.get_file(file_id) { - let file = file.read().unwrap(); + if let Some(source) = self.srcs.get_source(&path_string) { + let source = source.read().unwrap(); provide_diagnostics( uri, - &file.text, - urls, + &source.text, &self.configuration.read().unwrap(), &self ) @@ -128,41 +128,46 @@ impl LspServer { pub fn did_delete_files(&self, params: DeleteFilesParams) { // 删除 sources 内对应的文件 for file_delete in params.files { - let url = Url::parse(&file_delete.uri).unwrap(); - let pathbuf = PathBuf::from_str(url.path()).unwrap(); - let pathbuf = to_escape_path(&pathbuf); - let path_string = pathbuf.to_str().unwrap(); + let uri = Url::parse(&file_delete.uri).unwrap(); + let path_string = from_uri_to_escape_path_string(&uri).unwrap(); + // 同步 { let mut fast_sync_controller = self.srcs.fast_sync_controller.write().unwrap(); - fast_sync_controller.remove(path_string); + fast_sync_controller.remove(&path_string); } - self.srcs.hdl_param.delete_file(path_string); - + // hdlparam { - let mut names = self.srcs.names.write().unwrap(); - names.remove(&url); + self.srcs.hdl_param.delete_file(&path_string); } + // 文本缓冲器 + { + let mut sources = self.srcs.sources.write().unwrap(); + sources.remove(&path_string); + } + + // scope tree { let mut global_scope = self.srcs.scope_tree.write().unwrap(); match &mut *global_scope { Some(scope) => { - scope.defs.retain(|x| x.url() != url); - scope.scopes.retain(|x| x.url() != url); + scope.defs.retain(|x| x.url() != uri); + scope.scopes.retain(|x| x.url() != uri); }, None => {}, } } + // vhdl { let mut vhdl_project = self.srcs.vhdl_project.write().unwrap(); match &mut *vhdl_project { Some(vhdl_project) => { let config_file_strs = vhdl_project.config_file_strs.clone() .into_iter() - .filter(|x| x != path_string) + .filter(|x| x != &path_string) .collect::>(); let mut messages = Vec::new(); let config_str = format!( @@ -188,46 +193,40 @@ impl LspServer { /// The Source struct holds all file specific information pub struct Source { - /// id - pub id: usize, - /// uri + /// 文件的 uri pub uri: Url, /// 代码文本信息 pub text: Rope, /// 版本号 pub version: i32, - /// 解析的 IR,分为 DesignFile(VHDL)和 SyntaxTree(SV)两种 - pub parse_ir: Option, + /// 是否已经完成至少一次解析,用于在初始化相关的函数中使用 + pub finish_at_least_once: bool, /// 用于在解析失败时恢复解析的量 pub last_change_range: Option, } -pub enum ParseIR { - /// 基于 vhdl-parser 的IR,存放 VHDL - #[allow(unused)] - DesignFile(vhdl_lang::ast::DesignFile), - /// 存放 sv 的 IR - SyntaxTree(sv_parser::SyntaxTree) -} - - /// file metadata, including whether or not the syntax tree is up to date -pub struct SourceMeta { - pub id: usize, +pub struct SourceStatus { + /// 当前解析的文件的路径 + pub path: String, + /// 用于进行控制的锁 pub valid_parse: Arc<(Mutex, Condvar)>, + /// 解析当前文件的线程句柄 #[allow(unused)] pub parse_handle: JoinHandle<()>, } +pub enum AstLike { + Svlog(SyntaxTree), + Vhdl(DesignFile) +} /// The Sources struct manages all source files pub struct Sources { - // all files - pub files: Arc>>>>, - // map file urls to id - pub names: Arc>>, - // file metadata - pub meta: Arc>>>>, + // 用于存储后端中的前端的文本缓冲区的备份 + pub sources: Arc>>>>, + // 存储类似于线程句柄等数据的结构 + pub sources_status: Arc>>>>, /// scope tree 类型的树形结构,用于提供 sv 的 lsp pub scope_tree: Arc>>, // vhdl project, store vhdl design files, do lsp @@ -253,16 +252,15 @@ impl std::default::Default for Sources { impl Sources { pub fn new() -> Self { Self { - files: Arc::new(RwLock::new(Vec::new())), - names: Arc::new(RwLock::new(HashMap::new())), - meta: Arc::new(RwLock::new(Vec::new())), + sources: Arc::new(RwLock::new(HashMap::new())), + sources_status: Arc::new(RwLock::new(HashMap::new())), scope_tree: Arc::new(RwLock::new(None)), vhdl_project: Arc::new(RwLock::new(None)), include_dirs: Arc::new(RwLock::new(Vec::new())), primitive_text: Arc::new(PrimitiveText::new()), hdl_param: Arc::new(HdlParam::new()), - fast_sync_controller: Arc::new(RwLock::new(HashMap::>>::new())), - lsp_configuration: Arc::new(RwLock::new(HashMap::::new())) + fast_sync_controller: Arc::new(RwLock::new(HashMap::new())), + lsp_configuration: Arc::new(RwLock::new(HashMap::new())) } } @@ -279,13 +277,20 @@ impl Sources { hdl_param_handle.update_hdl_file( fake_path, template.fast, - sv_parser::common::ParseResult::new() + sv_parser::common::ParseResult::new(), + None ); primitive_text_handle.update_text(&name, &template.text); } } } + /// 判断当前文本缓冲器中是否存在指定路径的文件的备份 + pub fn contain_source(&self, path_string: &str) -> bool { + let sources = self.sources.read().unwrap(); + sources.contains_key(path_string) + } + pub fn init_vhdl_project(&self, extension_path: &str) { let project_handle = self.vhdl_project.clone(); let mut global_project = project_handle.write().unwrap(); @@ -319,25 +324,30 @@ impl Sources { #[allow(clippy::mutex_atomic)] // https://github.com/rust-lang/rust-clippy/issues/1516 let valid_parse = Arc::new((Mutex::new(false), Condvar::new())); let valid_parse2 = valid_parse.clone(); - let mut files = self.files.write().unwrap(); - let source = Arc::new(RwLock::new(Source { - id: files.len(), + + let mut sources = self.sources.write().unwrap(); + + // uri 转换为标准文件路径 + let path_string = from_uri_to_escape_path_string(&doc.uri).unwrap(); + + // 为新加入的文件构建后端文本缓冲器副本 + let source_handle = Arc::new(RwLock::new(Source { uri: doc.uri.clone(), text: Rope::from_str(&doc.text), version: doc.version, - parse_ir: None, + finish_at_least_once: false, last_change_range: None, })); - let source_handle = source.clone(); + + let parse_loop_used_source_handle = source_handle.clone(); let scope_handle = self.scope_tree.clone(); let project_handle = self.vhdl_project.clone(); let hdl_param_handle = self.hdl_param.clone(); let inc_dirs = self.include_dirs.clone(); + let configuration = server.configuration.clone(); let uri = doc.uri.clone(); - let conf = server.configuration.clone(); - info!("launch worker to parse {:?}", doc.uri.to_string()); let language_id = doc.language_id.to_string(); @@ -345,12 +355,14 @@ impl Sources { let pathbuf = PathBuf::from_str(&doc.uri.path()).unwrap(); let pathbuf = to_escape_path(&pathbuf); let path_string = pathbuf.to_str().unwrap(); + + // 用于进行 fast 的同步(和 request 那边的同步) let mut fast_controller = self.fast_sync_controller.write().unwrap(); let fast_lock = Arc::new(RwLock::new(true)); fast_controller.insert(path_string.to_string(), fast_lock.clone()); drop(fast_controller); - // spawn parse thread + // 创建一个解析线程 let parse_handle = thread::spawn(move || { let (lock, cvar) = &*valid_parse2; loop { @@ -360,7 +372,7 @@ impl Sources { { let _unused = fast_lock.read().unwrap(); // 从内存中直接获取增量更新系统维护的代码文本 - let file = source_handle.read().unwrap(); + let file = parse_loop_used_source_handle.read().unwrap(); let text = file.text.clone(); let uri = &file.uri.clone(); @@ -369,8 +381,8 @@ impl Sources { match language_id.as_str() { "vhdl" => { vhdl_parser_pipeline( - &conf, - &source_handle, + &configuration, + &parse_loop_used_source_handle, &scope_handle, &project_handle, &hdl_param_handle, @@ -380,8 +392,8 @@ impl Sources { }, "verilog" | "systemverilog" => { sv_parser_pipeline( - &conf, - &source_handle, + &configuration, + &parse_loop_used_source_handle, &scope_handle, &hdl_param_handle, &text, @@ -403,52 +415,47 @@ impl Sources { } } }); - files.push(source); - let fid = files.len() - 1; - self.meta - .write() - .unwrap() - .push(Arc::new(RwLock::new(SourceMeta { - id: fid, - valid_parse, - parse_handle, - }))); - self.names.write().unwrap().insert(doc.uri, fid); + // 将新的文件缓冲区数据结构加入 sources 和 status 中 + sources.insert(path_string.to_string(), source_handle); + + let mut source_status = self.sources_status.write().unwrap(); + let status_handle = Arc::new(RwLock::new(SourceStatus { + path: path_string.to_string(), + valid_parse, + parse_handle, + })); + source_status.insert(path_string.to_string(), status_handle); } - /// get file by id - pub fn get_file(&self, id: usize) -> Option>> { - let files = self.files.read().ok()?; - for file in files.iter() { - let source = file.read().ok()?; - if source.id == id { - return Some(file.clone()); - } + /// 输入路径,获得路径对应的文件的文本缓冲器对象 source + pub fn get_source(&self, path_string: &str) -> Option>> { + let sources = self.sources.read().unwrap(); + if let Some(source_handle) = sources.get(path_string) { + return Some(source_handle.clone()); } + None } - /// get metadata by file id - pub fn get_meta_data(&self, id: usize) -> Option>> { - let meta = self.meta.read().ok()?; - for data in meta.iter() { - let i = data.read().ok()?; - if i.id == id { - return Some(data.clone()); - } + /// 输入路径,获得路径对应的文件的文本缓冲器对象 source 的运行时状态 + pub fn get_source_status(&self, path_string: &str) -> Option>> { + let source_status = self.sources_status.read().unwrap(); + if let Some(status_handle) = source_status.get(path_string) { + return Some(status_handle.clone()); } + None } - /// wait for a valid parse - pub fn wait_parse_ready(&self, id: usize, wait_valid: bool) { - if let Some(file) = self.get_file(id) { - let file = file.read().unwrap(); - if file.parse_ir.is_none() || wait_valid { - drop(file); - let meta_data = self.get_meta_data(id).unwrap(); - let (lock, cvar) = &*meta_data.read().unwrap().valid_parse; + /// 等待解析线程创建完成 ? + pub fn wait_parse_ready(&self, path_string: &str, wait_valid: bool) { + if let Some(source_handle) = self.get_source(path_string) { + let source = source_handle.read().unwrap(); + if !source.finish_at_least_once || wait_valid { + drop(source); + let source_status_handle = self.get_source_status(path_string).unwrap(); // status 和 source 是同步创建的,要有一起有 + let (lock, cvar) = &*source_status_handle.read().unwrap().valid_parse; let mut valid = lock.lock().unwrap(); while !*valid { valid = cvar.wait(valid).unwrap(); @@ -457,11 +464,6 @@ impl Sources { } } - /// get file id from url - pub fn get_id(&self, uri: &Url) -> usize { - *self.names.read().unwrap().get(uri).unwrap() - } - /// 根据输入 token,计算当前 pub fn get_completions( &self, @@ -721,13 +723,14 @@ pub fn sv_parser_pipeline( hdl_param_handle.update_hdl_file( escape_path_string.to_string(), fast, - parse_result + parse_result, + Some(AstLike::Svlog(syntax_tree)) ); } - let parse_ir = ParseIR::SyntaxTree(syntax_tree); - file.parse_ir = Some(parse_ir); + + file.finish_at_least_once = true; } else { - file.parse_ir = None; + file.finish_at_least_once = false; } // file.syntax_tree = syntax_tree; drop(file); @@ -849,14 +852,11 @@ pub fn vhdl_parser_pipeline( let mut global_project = project_handle.write().unwrap(); match &mut *global_project { Some(vhdl_project) => { - let mut file = source_handle.write().unwrap(); + let mut source = source_handle.write().unwrap(); let text = doc.to_string(); let mut scope_tree = if let Some(design_file) = vhdl_parse_str(&escape_path, &text) { let arch_and_entity = vhdl_project.project.get_analyzed_units(&escape_path); if let Some(mut fast) = make_fast_from_units(arch_and_entity) { - let parse_ir = ParseIR::DesignFile(design_file); - file.parse_ir = Some(parse_ir); - // for module in &fast.content { // info!("parse port number: {:?}", module.ports.len()); // } @@ -882,7 +882,8 @@ pub fn vhdl_parser_pipeline( hdl_param_handle.update_hdl_file( escape_path_string.to_string(), fast.clone(), - sv_parser::common::ParseResult::new() + sv_parser::common::ParseResult::new(), + Some(AstLike::Vhdl(design_file)) ); let scope_tree = get_scopes_from_vhdl_fast(&fast, doc, uri); scope_tree @@ -890,10 +891,10 @@ pub fn vhdl_parser_pipeline( None } } else { - file.parse_ir = None; + source.finish_at_least_once = false; None }; - drop(file); + drop(source); info!("finish parse {:?}", uri.to_string());