digital-lsp-server/src/sources.rs
2024-12-01 22:24:49 +08:00

984 lines
37 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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(&params.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(&params.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::<Vec<String>>();
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分为 DesignFileVHDL和 SyntaxTreeSV两种
pub parse_ir: Option<ParseIR>,
/// 用于在解析失败时恢复解析的量
pub last_change_range: Option<Range>,
}
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<bool>, Condvar)>,
#[allow(unused)]
pub parse_handle: JoinHandle<()>,
}
/// find SystemVerilog/Verilog sources recursively from opened files
fn find_src_paths(dirs: &[PathBuf]) -> Vec<PathBuf> {
let mut paths: Vec<PathBuf> = 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<RwLock<Vec<Arc<RwLock<Source>>>>>,
// map file urls to id
pub names: Arc<RwLock<HashMap<Url, usize>>>,
// file metadata
pub meta: Arc<RwLock<Vec<Arc<RwLock<SourceMeta>>>>>,
/// scope tree 类型的树形结构,用于提供 sv 的 lsp
pub scope_tree: Arc<RwLock<Option<GenericScope>>>,
// vhdl project, store vhdl design files, do lsp
pub vhdl_project: Arc<RwLock<Option<VhdlProject>>>,
// include directories, passed to parser to resolve `include
pub include_dirs: Arc<RwLock<Vec<PathBuf>>>,
// primitive instance text
pub primitive_text: Arc<PrimitiveText>,
/// hdlparam 后端实现
pub hdl_param: Arc<HdlParam>,
// 同步解析线程和发送 fast 请求的
pub fast_sync_controller: Arc<RwLock<HashMap<String, Arc<RwLock<bool>>>>>,
// lsp 配置相关的
pub lsp_configuration: Arc<RwLock<HashMap<String, Value>>>
}
impl std::default::Default for Sources {
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::<String, Arc<RwLock<bool>>>::new())),
lsp_configuration: Arc::new(RwLock::new(HashMap::<String, Value>::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<Arc<RwLock<Source>>> {
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<Arc<RwLock<SourceMeta>>> {
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<CompletionList> {
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<CompletionList> {
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<String> {
let lsp_configuration = self.lsp_configuration.read().unwrap();
if let Some(Value::String(value)) = lsp_configuration.get(name) {
return Some(value.to_string());
}
None
}
#[allow(unused)]
pub fn get_lsp_configuration_i64_value(&self, name: &str) -> Option<i64> {
let lsp_configuration = self.lsp_configuration.read().unwrap();
if let Some(Value::Number(number)) = lsp_configuration.get(name) {
return number.as_i64();
}
None
}
#[allow(unused)]
pub fn get_lsp_configuration_bool_value(&self, name: &str) -> Option<bool> {
let lsp_configuration = self.lsp_configuration.read().unwrap();
if let Some(Value::Bool(value)) = lsp_configuration.get(name) {
return Some(*value);
}
None
}
}
pub fn recovery_sv_parse_with_retry(
doc: &Rope,
uri: &Url,
last_change_range: &Option<Range>,
inc_paths: &[PathBuf],
) -> Option<SyntaxTree>{
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<Range>,
inc_paths: &[PathBuf],
allow_incomplete: bool,
) -> Option<SyntaxTree> {
let mut parse_iterations = 1;
let mut i = 0;
let mut includes: Vec<PathBuf> = 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<RwLock<ProjectConfig>>,
source_handle: &Arc<RwLock<Source>>,
scope_handle: &Arc<RwLock<Option<GenericScope>>>,
hdl_param_handle: &Arc<HdlParam>,
doc: &Rope,
uri: &Url,
last_change_range: &Option<Range>,
include_dirs: &Vec<PathBuf>
) {
if doc.len_chars() == 0 {
return;
}
let path = match PathBuf::from_str(uri.path()) {
Ok(path) => path,
Err(error) => {
info!("error happen in <goto_include_definition>: {:?}", 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<RwLock<ProjectConfig>>,
source_handle: &Arc<RwLock<Source>>,
scope_handle: &Arc<RwLock<Option<GenericScope>>>,
project_handle: &Arc<RwLock<Option<VhdlProject>>>,
hdl_param_handle: &Arc<HdlParam>,
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 <goto_include_definition>: {:?}", 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 <vhdl_parse_pipeline>: {:?}", 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 <vhdl_parse_pipeline>: 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<usize>;
fn char_range_to_range(&self, range: StdRange<usize>) -> 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<usize> {
return self.pos_to_char(&range.start)..self.pos_to_char(&range.end);
}
fn char_range_to_range(&self, range: StdRange<usize>) -> 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<usize> {
self.pos_to_char(&range.start)..self.pos_to_char(&range.end)
}
fn char_range_to_range(&self, range: StdRange<usize>) -> 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");
}
}