use crate::core::hdlparam::HdlParam; use crate::core::primitive_parser::PrimitiveText; use crate::core::sv_parser::make_fast_from_syntaxtree; use crate::core::vhdl_parser::make_fast_from_units; use crate::core::vhdl_parser::vhdl_parse_str; use crate::core::scope_tree::common::*; use crate::core::scope_tree::get_scopes_from_syntax_tree; use crate::core::scope_tree::get_scopes_from_vhdl_fast; use crate::diagnostics::provide_diagnostics; use crate::server::LspServer; use crate::server::LspConfiguration; use crate::utils::from_uri_to_escape_path_string; use crate::utils::to_escape_path; #[allow(unused)] 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; #[allow(unused)] use std::ops::Deref; use std::ops::Range as StdRange; use std::path::Path; use std::path::PathBuf; use std::str::FromStr; use std::sync::Arc; use std::sync::RwLockWriteGuard; use std::sync::{Condvar, Mutex, RwLock}; use std::thread; use sv_parser::*; use vhdl_lang::Project; use thread::JoinHandle; use tower_lsp::lsp_types::*; impl LspServer { pub fn did_open(&self, params: DidOpenTextDocumentParams) -> PublishDiagnosticsParams { let document: TextDocumentItem = params.text_document; let uri = document.uri.clone(); let path_string = from_uri_to_escape_path_string(&uri).unwrap(); if self.db.contain_source(&path_string) { // 如果已经存在了,则进行增量更新 self.did_change(DidChangeTextDocumentParams { text_document: VersionedTextDocumentIdentifier::new(document.uri, document.version), content_changes: vec![TextDocumentContentChangeEvent { range: None, range_length: None, text: document.text, }], }); } else { // 如果不存在,直接加入其中 self.db.add(self, document); } // 生成诊断信息 if let Some(source) = self.db.get_source(&path_string) { if let Some(linter_mode) = self.db.get_lsp_configuration_string_value("digital-ide.function.lsp.linter.mode") { if linter_mode != "shutdown" { let source = source.read().unwrap(); let diagnostics = provide_diagnostics( uri, &source.text, &self ); return diagnostics } } } PublishDiagnosticsParams { uri, diagnostics: Vec::new(), version: None, } } pub fn did_change(&self, params: DidChangeTextDocumentParams) { let path_string = from_uri_to_escape_path_string(¶ms.text_document.uri).unwrap(); if let Some(source) = self.db.get_source(&path_string) { let mut source = source.write().unwrap(); // 根据输入的 change 动态更新对应的代码的文本片段 for change in params.content_changes { if change.range.is_none() { source.text = Rope::from_str(&change.text); } else { source.text.apply_change(&change); } source.last_change_range = change.range; } source.version = params.text_document.version; drop(source); // 唤醒解析线程 let source_status = self.db.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(); } } /// 保存时触发的行为 /// - 提供诊断信息(主要是第三方诊断器,因为 save 后磁盘上的内容就和缓冲区中的一致了) pub fn did_save(&self, params: DidSaveTextDocumentParams) -> PublishDiagnosticsParams { let uri = params.text_document.uri; let path_string = from_uri_to_escape_path_string(&uri).unwrap(); if let Some(source) = self.db.get_source(&path_string) { if let Some(linter_mode) = self.db.get_lsp_configuration_string_value("digital-ide.function.lsp.linter.mode") { if linter_mode != "shutdown" { let source = source.read().unwrap(); return provide_diagnostics( uri, &source.text, &self ); } } } PublishDiagnosticsParams { uri, diagnostics: Vec::new(), version: None, } } pub fn did_delete_files(&self, params: DeleteFilesParams) { // 删除 sources 内对应的文件 for file_delete in params.files { 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.db.fast_sync_controller.write().unwrap(); fast_sync_controller.remove(&path_string); } // hdlparam { self.db.hdl_param.delete_file(&path_string); } // 文本缓冲器 { let mut sources = self.db.sources.write().unwrap(); sources.remove(&path_string); } // scope tree { let mut global_scope = self.db.scope_tree.write().unwrap(); match &mut *global_scope { Some(scope) => { scope.defs.retain(|x| x.url() != uri); scope.scopes.retain(|x| x.url() != uri); }, None => {}, } } // vhdl { let mut vhdl_project = self.db.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) .collect::>(); let mut messages = Vec::new(); let config_str = format!( r#" [libraries] digital_lsp.files = [{}] "#, config_file_strs.join(",") ); let config = vhdl_lang::Config::from_str(&config_str, Path::new("")); if let Ok(mut config) = config { config.append(&vhdl_project.std_config, &mut messages); let mut project = Project::from_config(config, &mut messages); project.analyse(); *vhdl_project = VhdlProject { project, std_config: vhdl_project.std_config.clone(), config_file_strs }; } } None => () } } } } } /// The Source struct holds all file specific information pub struct Source { /// 文件的 uri pub uri: Url, /// 代码文本信息 pub text: Rope, /// 版本号 pub version: i32, /// 是否已经完成至少一次解析,用于在初始化相关的函数中使用 pub finish_at_least_once: bool, /// 用于在解析失败时恢复解析的量 pub last_change_range: Option, } /// file metadata, including whether or not the syntax tree is up to date pub struct SourceStatus { /// 当前解析的文件的路径 #[allow(unused)] pub path: String, /// 用于进行控制的锁 pub valid_parse: Arc<(Mutex, Condvar)>, /// 解析当前文件的线程句柄,此处必须让句柄被全局变量持有,否则 /// 解析线程句柄在离开作用域后会被销毁 #[allow(unused)] pub parse_handle: JoinHandle<()>, } pub enum AstLike { #[allow(unused)] Svlog(SyntaxTree), #[allow(unused)] Vhdl(DesignFile) } /// 用于管理和源代码文本副本、AST、Fast、客户端配置信息等相关的 db 对象 pub struct DigitalDataBase { // 用于存储后端中的前端的文本缓冲区的备份 pub sources: Arc>>>>, // 存储类似于线程句柄等数据的结构 pub sources_status: Arc>>>>, /// scope tree 类型的树形结构,用于提供 sv 的 lsp pub scope_tree: Arc>>, // vhdl project, store vhdl design files, do lsp pub vhdl_project: Arc>>, // include directories, passed to parser to resolve `include pub include_dirs: Arc>>, // primitive instance text pub primitive_text: Arc, /// hdlparam 后端实现 pub hdl_param: Arc, // 同步解析线程和发送 fast 请求的 pub fast_sync_controller: Arc>>>>, // lsp 配置相关的 pub lsp_configuration: Arc>> } impl std::default::Default for DigitalDataBase { fn default() -> Self { Self::new() } } impl DigitalDataBase { pub fn new() -> Self { Self { 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())) } } pub fn init_primitive(&self, tool_chain: &str, extension_path: &str) { let primitive_bin = format!("{}/resources/dide-lsp/static/{}/primitive.bin", extension_path, tool_chain); info!("attempt to find primitives in {}", primitive_bin); if let Some(primitive_xml) = crate::core::primitive_parser::load_primitive_bin(&primitive_bin) { let primitive_text_handle = self.primitive_text.clone(); let hdl_param_handle = self.hdl_param.clone(); for (name, template) in primitive_xml.name_to_template { // use "primitive/name" as a fake path let fake_path = "primitive/".to_string() + &name; hdl_param_handle.update_hdl_file( fake_path, template.fast, 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(); match &mut *global_project { Some(_) => (), None => { let std_cfg_path = match PathBuf::from_str(&(extension_path.to_string() + "/resources/dide-lsp/static/vhdl_std_lib/vhdl_ls.toml")) { Ok(path) => path, Err(e) => { info!("error happen in : {:?}", e); return; } }; match vhdl_lang::Config::read_file_path(&std_cfg_path) { Ok(std_config) => { let mut messages = Vec::new(); let mut project = Project::from_config(std_config.clone(), &mut messages); project.analyse(); let config_file_strs = Vec::new(); *global_project = Some(VhdlProject { project, std_config, config_file_strs }); } Err(e) => info!("error happen in : Can't load standard vhdl lib from {std_cfg_path:#?} because {e:#?}") } } } } /// 增加一个 hdl 文件,并为该文件添加单独的解析线程 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())); let valid_parse_handle = valid_parse.clone(); let mut sources = self.sources.write().unwrap(); // 为新加入的文件构建后端文本缓冲器副本 let source_handle = Arc::new(RwLock::new(Source { uri: doc.uri.clone(), text: Rope::from_str(&doc.text), version: doc.version, finish_at_least_once: false, last_change_range: None, })); 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(); info!("launch worker to parse {:?}", doc.uri.to_string()); let language_id = doc.language_id.to_string(); // 新建一个 fast controller 用于控制下方的解析线程和外部的请求函数 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); // 创建一个解析线程 let parse_handle = thread::spawn(move || { // TODO: 检查这里 let (ref lock, ref cvar) = *valid_parse_handle; loop { info!("do parse in {:?}, language_id: {:?}", uri.to_string(), language_id); // 此时 update fast 的地方为 write 会进行阻塞 { let _unused = fast_lock.read().unwrap(); // 从内存中直接获取增量更新系统维护的代码文本 let file = parse_loop_used_source_handle.write().unwrap(); let text = file.text.clone(); let uri = &file.uri.clone(); let range = &file.last_change_range.clone(); match language_id.as_str() { "vhdl" => { vhdl_parser_pipeline( &configuration, file, &scope_handle, &project_handle, &hdl_param_handle, &text, uri, ); }, "verilog" | "systemverilog" => { sv_parser_pipeline( &configuration, file, &scope_handle, &hdl_param_handle, &text, uri, range, &inc_dirs.read().unwrap() ); }, _ => {} } } // 下方基于消费者-生产者模型进行控制 // lock 会尝试去获取互斥锁,如果当前持有互斥锁的线程发生了 panic,则当前 unwrap 操作会失败 let mut valid = lock.lock().unwrap(); *valid = true; // 唤醒所有正在等待条件变量的线程,即所有被下方 cvar.wait 阻塞的线程将被激活 cvar.notify_all(); while *valid { valid = cvar.wait(valid).unwrap(); } } }); // 将新的文件缓冲区数据结构加入 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); } /// 输入路径,获得路径对应的文件的文本缓冲器对象 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 } /// 输入路径,获得路径对应的文件的文本缓冲器对象 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 } /// 等待解析线程创建完成 ? 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(); // 解析线程一轮解析结束后, valid 会变为 true,结束循环 while !*valid { valid = cvar.wait(valid).unwrap(); } } } } /// 根据输入 token,计算当前 pub fn get_completions( &self, token: &str, byte_idx: usize, url: &Url, ) -> Option { Some(CompletionList { is_incomplete: false, items: self .scope_tree .read() .ok()? .as_ref()? .get_completion(token, byte_idx, url), }) } #[allow(unused)] pub fn get_lsp_configuration_string_value(&self, name: &str) -> Option { let lsp_configuration = self.lsp_configuration.read().unwrap(); if let Some(Value::String(value)) = lsp_configuration.get(name) { return Some(value.to_string()); } None } #[allow(unused)] pub fn get_lsp_configuration_i64_value(&self, name: &str) -> Option { let lsp_configuration = self.lsp_configuration.read().unwrap(); if let Some(Value::Number(number)) = lsp_configuration.get(name) { return number.as_i64(); } None } #[allow(unused)] pub fn get_lsp_configuration_bool_value(&self, name: &str) -> Option { let lsp_configuration = self.lsp_configuration.read().unwrap(); if let Some(Value::Bool(value)) = lsp_configuration.get(name) { return Some(*value); } None } /// 同时从 fast 和 primitives 里面找 module /// 在内部判断一个 module 是否存在时会用到 /// - name: 模块的名字 pub fn contains_module(&self, name: &str) -> bool { if let Some(_) = self.hdl_param.find_module_by_name(name) { return true; } // 从 primitives 里面寻找 let primitive_text = self.primitive_text.clone(); let primitive_map = primitive_text.name_to_text.read().unwrap(); primitive_map.contains_key(name) } } pub fn recovery_sv_parse_with_retry( doc: &Rope, uri: &Url, last_change_range: &Option, inc_paths: &[PathBuf], ) -> Option<(SyntaxTree, sv_parser::common::ParseResult)>{ if let Some((syntax_tree, parse_result)) = recovery_sv_parse(doc, uri, last_change_range, inc_paths, false) { Some((syntax_tree, parse_result)) } else { recovery_sv_parse(doc, uri, last_change_range, inc_paths, true) } } fn replace_substring(rope: Rope, substring: &str) -> Rope { let text = rope.to_string(); let replaced_text = text.replace(substring, &" ".repeat(substring.len())); Rope::from(replaced_text) } /// 更加稳定地解析 sv 和 v /// 支持遇到错误进行自动修复,然后再解析 pub fn recovery_sv_parse( doc: &Rope, uri: &Url, _last_change_range: &Option, inc_paths: &[PathBuf], allow_incomplete: bool, ) -> Option<(SyntaxTree, sv_parser::common::ParseResult)> { let mut parse_iterations = 1; let mut i = 0; let mut includes: Vec = inc_paths.to_vec(); let mut defines = HashMap::new(); let mut text = doc.clone(); // TODO: find how to fix it // remove `reset all to avoid parse error text = replace_substring(text, "`resetall"); let recover_text_by_byte_idx = |byte_idx: usize, text: &mut Rope| { let line_start = text.byte_to_line(byte_idx); let line_end = text.byte_to_line(byte_idx) + 1; // info!("byte: {byte_idx}, line_start: {line_start}, line_end: {line_end}"); // 把 last_change 处的地方替换成空格 for line_idx in line_start .. line_end { let line = text.line(line_idx); let start_char = text.line_to_char(line_idx); let line_length = line.len_chars(); text.remove(start_char..(start_char + line_length - 1)); text.insert(start_char, &" ".to_owned().repeat(line_length)); } }; // 最多解析 50 次 while i < parse_iterations && i < 50 { i += 1; match make_ast_from_svlog_code( &text.to_string(), uri.to_file_path().unwrap(), &defines, &includes, true, allow_incomplete ) { Ok((syntax_tree, parse_result)) => { return Some((syntax_tree, parse_result)); } Err(err) => { // println!("err: {err:?}"); match err { // 语法错误 sv_parser::Error::Parse(trace) => match trace { Some((_, byte_idx)) => { // 把出错的地方替换成空格 recover_text_by_byte_idx(byte_idx, &mut text); parse_iterations += 1; } None => return None, }, // 遇到 include 错误,那就把提示中的 include 加入解析中再次解析 sv_parser::Error::Include { source: x } => { if let sv_parser::Error::File { source: _, path: z } = *x { // Include paths have to be relative to the working directory // so we have to convert a source file relative path to a working directory // relative path. This should have been handled by sv-parser let mut inc_path_given = z.clone(); let mut uri_path = uri.to_file_path().unwrap(); uri_path.pop(); let rel_path = diff_paths(uri_path, current_dir().unwrap()).unwrap(); inc_path_given.pop(); let inc_path = rel_path.join(inc_path_given); if !includes.contains(&inc_path) { includes.push(inc_path); } else { error!("parser: include error: {:?}", z); break; } parse_iterations += 1; } } // 宏定义不存在的错误 sv_parser::Error::DefineNotFound(not_found_macro_name) => { let com_define = Define { identifier: not_found_macro_name.to_string(), arguments: Vec::new(), text: Some(DefineText {text: "UNKNOWN_MACRO".to_string(), origin: None}) }; defines.insert(not_found_macro_name, Some(com_define)); parse_iterations += 1; } sv_parser::Error::Preprocess(trace) => match trace { Some((_, byte_idx)) => { info!("meet preprocess error, text: {text:?}"); // 把出错的地方替换成空格 // println!("text {text:?}"); recover_text_by_byte_idx(byte_idx, &mut text); parse_iterations += 1; } None => return None } sv_parser::Error::DefineArgNotFound(trace) => match trace { Some((_, start_byte_idx, _)) => { recover_text_by_byte_idx(start_byte_idx, &mut text); // recover_text_by_byte_idx(end_byte_idx, &mut reverted_change, &mut text); parse_iterations += 1; } _ => return None } sv_parser::Error::DefineNoArgs(trace) => match trace { Some((_, start_byte_idx, _)) => { recover_text_by_byte_idx(start_byte_idx, &mut text); parse_iterations += 1; } _ => return None } _ => error!("parse error, {:?}", err), }; } } } None } pub fn sv_parser_pipeline( #[allow(unused)] conf: &Arc>, mut file_handle: RwLockWriteGuard<'_, Source>, scope_handle: &Arc>>, hdl_param_handle: &Arc, doc: &Rope, uri: &Url, last_change_range: &Option, include_dirs: &Vec ) { if doc.len_chars() == 0 { return; } let escape_path_string = from_uri_to_escape_path_string(uri).unwrap(); let escape_path = PathBuf::from_str(&escape_path_string).unwrap(); let ast = recovery_sv_parse_with_retry( doc, uri, last_change_range, include_dirs ); // 更新 scope tree let mut scope_tree = match &ast { Some((tree, _)) => get_scopes_from_syntax_tree(tree, uri), None => None, }; // 加入语法树 & 更新 fast if let Some((syntax_tree, parse_result)) = ast { if let Ok(fast) = make_fast_from_syntaxtree(&syntax_tree) { info!("update: {:?}", fast.fast_macro); hdl_param_handle.update_hdl_file( escape_path_string.to_string(), fast, parse_result, Some(AstLike::Svlog(syntax_tree)) ); } file_handle.finish_at_least_once = true; } else { file_handle.finish_at_least_once = false; } drop(file_handle); info!("finish parse {:?}", uri.to_string()); // 更新 global_scope,用于 sv 的解析 // global_scope 为全局最大的那个 scope,它的 scopes 和 defs 下的元素和每一个文件一一对应 let mut global_scope = scope_handle.write().unwrap(); match &mut *global_scope { Some(scope) => match &mut scope_tree { Some(tree) => { // 更新所有 uri 为当前 uri 的文件结构 scope.defs.retain(|x| &x.url() != uri); scope.scopes.retain(|x| &x.url() != uri); scope.defs.append(&mut tree.defs); scope.scopes.append(&mut tree.scopes); } None => (), }, // 使用 scope_tree 来更新全局的 scope None => *global_scope = scope_tree, } drop(global_scope); } pub fn vhdl_parser_pipeline( conf: &Arc>, mut file_handle: RwLockWriteGuard<'_, Source>, scope_handle: &Arc>>, project_handle: &Arc>>, hdl_param_handle: &Arc, doc: &Rope, uri: &Url ) { if doc.len_chars() == 0 { return; } let path = match PathBuf::from_str(uri.path()) { Ok(path) => path, Err(error) => { info!("error happen in : {:?}", error); return; } }; let escape_path = to_escape_path(&path); let escape_path_string = escape_path.to_str().unwrap_or(""); if escape_path_string.len() == 0 { info!("error happen in [vhdl_parser_pipeline], escape_path_string is empty"); return; } let extension_path = { let configure = conf.read().unwrap(); configure.extension_path.to_string() }; let mut global_project = project_handle.write().unwrap(); match &mut *global_project { Some(vhdl_project) => { if let Some(source) = vhdl_project.project.get_source(&escape_path) { source.change(None, &doc.to_string()); vhdl_project.project.update_source(&source); vhdl_project.project.analyse(); } else { vhdl_project.config_file_strs.push(format!("{:?}", escape_path)); let mut messages = Vec::new(); let config_str = format!( r#" [libraries] digital_lsp.files = [{}] "#, vhdl_project.config_file_strs.join(",") ); let config = vhdl_lang::Config::from_str(&config_str, Path::new("")); if let Ok(mut config) = config { config.append(&vhdl_project.std_config, &mut messages); let mut project = Project::from_config(config, &mut messages); project.analyse(); *vhdl_project = VhdlProject { project, std_config: vhdl_project.std_config.clone(), config_file_strs: vhdl_project.config_file_strs.clone() }; } } } None => { let std_cfg_path = match PathBuf::from_str(&(extension_path.clone() + "/resources/dide-lsp/static/vhdl_std_lib/vhdl_ls.toml")) { Ok(path) => path, Err(e) => { info!("error happen in : {:?}", e); return; } }; match vhdl_lang::Config::read_file_path(&std_cfg_path) { Ok(std_config) => { let config_str = format!( r#" [libraries] digital_lsp.files = [{:?}] "#, escape_path ); let mut config_file_strs = Vec::new(); config_file_strs.push(format!("{:?}", escape_path)); let config = vhdl_lang::Config::from_str(&config_str, Path::new("")); if let Ok(mut config) = config { let mut messages = Vec::new(); config.append(&std_config, &mut messages); let mut project = Project::from_config(config, &mut messages); project.analyse(); *global_project = Some(VhdlProject { project, std_config, config_file_strs }); } } Err(e) => info!("error happen in : Can't load standard vhdl lib from {std_cfg_path:#?} because {e:#?}") } } } drop(global_project); let mut global_project = project_handle.write().unwrap(); match &mut *global_project { Some(vhdl_project) => { 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) { // for module in &fast.content { // info!("parse port number: {:?}", module.ports.len()); // } 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_hdl_file( escape_path_string.to_string(), fast.clone(), sv_parser::common::ParseResult::new(), Some(AstLike::Vhdl(design_file)) ); let scope_tree = get_scopes_from_vhdl_fast(&fast, doc, uri); scope_tree } else { None } } else { file_handle.finish_at_least_once = false; None }; drop(file_handle); info!("finish parse {:?}", uri.to_string()); // 更新 global_scope,用于 sv 的解析 // global_scope 为全局最大的那个 scope,它的 scopes 和 defs 下的元素和每一个文件一一对应 let mut global_scope = scope_handle.write().unwrap(); match &mut *global_scope { Some(scope) => match &mut scope_tree { Some(tree) => { // 更新所有 uri 为当前 uri 的文件结构 scope.defs.retain(|x| &x.url() != uri); scope.scopes.retain(|x| &x.url() != uri); scope.defs.append(&mut tree.defs); scope.scopes.append(&mut tree.scopes); } None => (), }, // 使用 scope_tree 来更新全局的 scope None => *global_scope = scope_tree, } // eprintln!("{:#?}", *global_scope); drop(global_scope); } _ => () } } //TODO: add bounds checking for utf8<->utf16 conversions /// This trait defines some helper functions to convert between lsp types /// and char/byte positions pub trait LSPSupport { fn pos_to_byte(&self, pos: &Position) -> usize; fn pos_to_char(&self, pos: &Position) -> usize; fn byte_to_pos(&self, byte_idx: usize) -> Position; fn char_to_pos(&self, char_idx: usize) -> Position; fn range_to_char_range(&self, range: &Range) -> StdRange; fn char_range_to_range(&self, range: StdRange) -> Range; fn apply_change(&mut self, change: &TextDocumentContentChangeEvent); } /// Extend ropey's Rope type with lsp convenience functions impl LSPSupport for Rope { fn pos_to_byte(&self, pos: &Position) -> usize { return self.char_to_byte(self.pos_to_char(pos)); } fn pos_to_char(&self, pos: &Position) -> usize { let line_slice = self.line(pos.line as usize); return self.line_to_char(pos.line as usize) + line_slice.utf16_cu_to_char(pos.character as usize); } fn byte_to_pos(&self, byte_idx: usize) -> Position { // info!("byte_idx: {byte_idx}"); let self_len = if self.len_bytes() != 0 { self.len_bytes() - 1 } else { 0 }; return self.char_to_pos(self.byte_to_char(min(byte_idx, self_len))); } fn char_to_pos(&self, char_idx: usize) -> Position { let line = self.char_to_line(char_idx); let line_slice = self.line(line); return Position { line: line as u32, character: line_slice.char_to_utf16_cu(char_idx - self.line_to_char(line)) as u32, }; } fn range_to_char_range(&self, range: &Range) -> StdRange { return self.pos_to_char(&range.start)..self.pos_to_char(&range.end); } fn char_range_to_range(&self, range: StdRange) -> Range { return Range { start: self.char_to_pos(range.start), end: self.char_to_pos(range.end), }; } fn apply_change(&mut self, change: &TextDocumentContentChangeEvent) { if let Some(range) = change.range { let char_range = self.range_to_char_range(&range); self.remove(char_range.clone()); if !change.text.is_empty() { self.insert(char_range.start, &change.text); } } } } impl<'a> LSPSupport for RopeSlice<'a> { fn pos_to_byte(&self, pos: &Position) -> usize { self.char_to_byte(self.pos_to_char(pos)) } fn pos_to_char(&self, pos: &Position) -> usize { let line_slice = self.line(pos.line as usize); self.line_to_char(pos.line as usize) + line_slice.utf16_cu_to_char(pos.character as usize) } fn byte_to_pos(&self, byte_idx: usize) -> Position { // info!("byte_idx: {byte_idx}"); self.char_to_pos(self.byte_to_char(min(byte_idx, self.len_bytes() - 1))) } fn char_to_pos(&self, char_idx: usize) -> Position { let line = self.char_to_line(char_idx); let line_slice = self.line(line); Position { line: line as u32, character: line_slice.char_to_utf16_cu(char_idx - self.line_to_char(line)) as u32, } } fn range_to_char_range(&self, range: &Range) -> StdRange { self.pos_to_char(&range.start)..self.pos_to_char(&range.end) } fn char_range_to_range(&self, range: StdRange) -> Range { Range { start: self.char_to_pos(range.start), end: self.char_to_pos(range.end), } } fn apply_change(&mut self, _: &TextDocumentContentChangeEvent) { panic!("can't edit a rope slice"); } }