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_design_file; use crate::core::vhdl_parser::vhdl_parse_str; use crate::definition::def_types::*; 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; use log::{debug, error}; use pathdiff::diff_paths; use ropey::{Rope, RopeSlice}; use serde_json::Value; 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, Condvar, Mutex, RwLock}; use std::thread; use sv_parser::*; use vhdl_lang::Project; use thread::JoinHandle; use tower_lsp::lsp_types::*; use walkdir::WalkDir; 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 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.srcs.add(self, document); } // diagnostics 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(); 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(¶ms.text_document.uri); if let Some(file) = self.srcs.get_file(file_id) { let mut file = file.write().unwrap(); // 根据输入的 change 动态更新对应的代码的文本片段 for change in params.content_changes { if change.range.is_none() { file.text = Rope::from_str(&change.text); } else { file.text.apply_change(&change); } file.last_change_range = change.range; } file.version = params.text_document.version; drop(file); // 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 mut valid = lock.lock().unwrap(); *valid = false; cvar.notify_all(); } } 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(); 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_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 mut fast_sync_controller = self.srcs.fast_sync_controller.write().unwrap(); fast_sync_controller.remove(path_string); } self.srcs.hdl_param.delete_file(path_string); { let mut names = self.srcs.names.write().unwrap(); names.remove(&url); } { 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); }, None => {}, } } { 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) .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 { /// id pub id: usize, /// uri pub uri: Url, /// 代码文本信息 pub text: Rope, /// 版本号 pub version: i32, /// 解析的 IR,分为 DesignFile(VHDL)和 SyntaxTree(SV)两种 pub parse_ir: Option, /// 用于在解析失败时恢复解析的量 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 valid_parse: Arc<(Mutex, Condvar)>, #[allow(unused)] pub parse_handle: JoinHandle<()>, } /// find SystemVerilog/Verilog sources recursively from opened files fn find_src_paths(dirs: &[PathBuf]) -> Vec { let mut paths: Vec = Vec::new(); for dir in dirs { let walker = WalkDir::new(dir).into_iter(); for entry in walker.filter_entry(|e| !is_hidden(e)) { let entry = entry.unwrap(); if entry.file_type().is_file() && entry.path().extension().is_some() { let extension = entry.path().extension().unwrap(); if extension == "sv" || extension == "svh" || extension == "v" || extension == "vh" { let entry_path = entry.path().to_path_buf(); if !paths.contains(&entry_path) { paths.push(entry_path); } } } } } paths } /// 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>>>>, /// 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 Sources { fn default() -> Self { Self::new() } } 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())), 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_fast(fake_path, template.fast); primitive_text_handle.update_text(&name, &template.text); } } } /// 增加一个 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_parse2 = valid_parse.clone(); let mut files = self.files.write().unwrap(); let source = Arc::new(RwLock::new(Source { id: files.len(), uri: doc.uri.clone(), text: Rope::from_str(&doc.text), version: doc.version, parse_ir: None, last_change_range: None, })); let source_handle = source.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 uri = doc.uri.clone(); let conf = server.conf.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(); 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 { info!("do parse in {:?}, language_id: {:?}", uri.to_string(), language_id); // 此时 update fast 的地方为 write 会进行阻塞 { let _unused = fast_lock.read().unwrap(); // 从内存中直接获取增量更新系统维护的代码文本 let file = source_handle.read().unwrap(); let text = file.text.clone(); let uri = &file.uri.clone(); let range = &file.last_change_range.clone(); drop(file); match language_id.as_str() { "vhdl" => { vhdl_parser_pipeline( &conf, &source_handle, &scope_handle, &project_handle, &hdl_param_handle, &text, uri, ); }, "verilog" | "systemverilog" => { sv_parser_pipeline( &conf, &source_handle, &scope_handle, &hdl_param_handle, &text, uri, range, &inc_dirs.read().unwrap() ); }, _ => {} } } // 通过条件锁进行控制 let mut valid = lock.lock().unwrap(); *valid = true; cvar.notify_all(); while *valid { valid = cvar.wait(valid).unwrap(); } } }); 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); } /// 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()); } } 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()); } } 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; let mut valid = lock.lock().unwrap(); while !*valid { valid = cvar.wait(valid).unwrap(); } } } } /// get file id from url pub fn get_id(&self, uri: &Url) -> usize { *self.names.read().unwrap().get(uri).unwrap() } /// compute identifier completions pub fn get_completions( &self, token: &str, byte_idx: usize, url: &Url, ) -> Option { debug!("retrieving identifier completion for token: {}", &token); Some(CompletionList { is_incomplete: false, items: self .scope_tree .read() .ok()? .as_ref()? .get_completion(token, byte_idx, url), }) } /// compute dot completions pub fn get_dot_completions( &self, token: &str, byte_idx: usize, url: &Url, ) -> Option { debug!("retrieving dot completion for token: {}", &token); let tree = self.scope_tree.read().ok()?; Some(CompletionList { is_incomplete: false, items: tree .as_ref()? .get_dot_completion(token, byte_idx, url, tree.as_ref()?), }) } #[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 } } pub fn recovery_sv_parse_with_retry( doc: &Rope, uri: &Url, last_change_range: &Option, inc_paths: &[PathBuf], ) -> Option{ if let Some(syntax_tree) = recovery_sv_parse(doc, uri, last_change_range, inc_paths, false) { Some(syntax_tree) } else { recovery_sv_parse(doc, uri, last_change_range, inc_paths, true) } } /// 更加稳定地解析 sv 和 v /// 支持遇到错误进行自动修复,然后再解析 pub fn recovery_sv_parse( doc: &Rope, uri: &Url, last_change_range: &Option, inc_paths: &[PathBuf], allow_incomplete: bool, ) -> Option { let mut parse_iterations = 1; let mut i = 0; let mut includes: Vec = inc_paths.to_vec(); let mut defines = HashMap::new(); let mut reverted_change = false; let mut text = doc.clone(); let recover_text_by_byte_idx = |byte_idx: usize, reverted_change: &mut bool, text: &mut Rope| { let mut line_start = text.byte_to_line(byte_idx); let mut line_end = text.byte_to_line(byte_idx) + 1; // println!("byte: {byte_idx}, line_start: {line_start}, line_end: {line_end}"); if !(*reverted_change) { if let Some(range) = last_change_range { line_start = range.start.line as usize; line_end = range.end.line as usize + 1; *reverted_change = true; } } // 把 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)); } }; // 最多解析 500 次 while i < parse_iterations && i < 500 { i += 1; match parse_sv_str( &text.to_string(), uri.to_file_path().unwrap(), &defines, &includes, true, allow_incomplete ) { Ok((syntax_tree, _)) => { return Some(syntax_tree); } 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 reverted_change, &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)) => { // 把出错的地方替换成空格 // println!("text {text:?}"); recover_text_by_byte_idx(byte_idx, &mut reverted_change, &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 reverted_change, &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 reverted_change, &mut text); parse_iterations += 1; } _ => return None } _ => error!("parse error, {:?}", err), }; } } } None } pub fn sv_parser_pipeline( #[allow(unused)] conf: &Arc>, source_handle: &Arc>, 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 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 [sv_parser_pipeline], escape_path_string is empty"); return; } info!("debug, parse: {:?}", path); let syntax_tree = recovery_sv_parse_with_retry( doc, uri, last_change_range, include_dirs ); info!("finish parse {:?}", path); // 更新 scope tree let mut scope_tree = match &syntax_tree { Some(tree) => get_scopes_from_syntax_tree(tree, uri), None => None, }; let mut file = source_handle.write().unwrap(); // 加入语法树 & 更新 fast if let Some(syntax_tree) = syntax_tree { if let Ok(fast) = make_fast_from_syntaxtree(&syntax_tree, &escape_path) { hdl_param_handle.update_fast(escape_path_string.to_string(), fast); } let parse_ir = ParseIR::SyntaxTree(syntax_tree); file.parse_ir = Some(parse_ir); } else { file.parse_ir = None; } // file.syntax_tree = syntax_tree; drop(file); 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); } pub fn vhdl_parser_pipeline( conf: &Arc>, source_handle: &Arc>, 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 file = source_handle.write().unwrap(); let text = doc.to_string(); let mut scope_tree = if let Some(design_file) = vhdl_parse_str(&escape_path, &text) { 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); 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_fast(escape_path_string.to_string(), fast.clone()); let scope_tree = get_scopes_from_vhdl_fast(&fast, doc, uri); scope_tree } else { None } } else { file.parse_ir = None; None }; drop(file); 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); 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 + "/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); 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"); } }