digital-lsp-server/src/sources.rs

1016 lines
40 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_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::{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.srcs.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.srcs.add(self, document);
}
// 生成诊断信息
if let Some(source) = self.srcs.get_source(&path_string) {
let source = source.read().unwrap();
let diagnostics = provide_diagnostics(
uri,
&source.text,
&self.configuration.read().unwrap(),
&self
);
diagnostics
} else {
PublishDiagnosticsParams {
uri,
diagnostics: Vec::new(),
version: None,
}
}
}
pub fn did_change(&self, params: DidChangeTextDocumentParams) {
let path_string = from_uri_to_escape_path_string(&params.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() {
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.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();
}
}
/// 保存时触发的行为
/// - 提供诊断信息(主要是第三方诊断器,因为 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.srcs.get_source(&path_string) {
let source = source.read().unwrap();
provide_diagnostics(
uri,
&source.text,
&self.configuration.read().unwrap(),
&self
)
} else {
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.srcs.fast_sync_controller.write().unwrap();
fast_sync_controller.remove(&path_string);
}
// hdlparam
{
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() != 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)
.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 {
/// 文件的 uri
pub uri: Url,
/// 代码文本信息
pub text: Rope,
/// 版本号
pub version: i32,
/// 是否已经完成至少一次解析,用于在初始化相关的函数中使用
pub finish_at_least_once: bool,
/// 用于在解析失败时恢复解析的量
pub last_change_range: Option<Range>,
}
/// file metadata, including whether or not the syntax tree is up to date
pub struct SourceStatus {
/// 当前解析的文件的路径
pub path: String,
/// 用于进行控制的锁
pub valid_parse: Arc<(Mutex<bool>, Condvar)>,
/// 解析当前文件的线程句柄
#[allow(unused)]
pub parse_handle: JoinHandle<()>,
}
pub enum AstLike {
Svlog(SyntaxTree),
Vhdl(DesignFile)
}
/// The Sources struct manages all source files
pub struct Sources {
// 用于存储后端中的前端的文本缓冲区的备份
pub sources: Arc<RwLock<HashMap<String, Arc<RwLock<Source>>>>>,
// 存储类似于线程句柄等数据的结构
pub sources_status: Arc<RwLock<HashMap<String, Arc<RwLock<SourceStatus>>>>>,
/// 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 {
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 <vhdl_parse_pipeline>: {:?}", 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 <init_vhdl_project>: 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_parse2 = valid_parse.clone();
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,
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 || {
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 = parse_loop_used_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(
&configuration,
&parse_loop_used_source_handle,
&scope_handle,
&project_handle,
&hdl_param_handle,
&text,
uri,
);
},
"verilog" | "systemverilog" => {
sv_parser_pipeline(
&configuration,
&parse_loop_used_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();
}
}
});
// 将新的文件缓冲区数据结构加入 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<Arc<RwLock<Source>>> {
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<Arc<RwLock<SourceStatus>>> {
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();
while !*valid {
valid = cvar.wait(valid).unwrap();
}
}
}
}
/// 根据输入 token计算当前
pub fn get_completions(
&self,
token: &str,
byte_idx: usize,
url: &Url,
) -> Option<CompletionList> {
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<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
}
/// 同时从 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<Range>,
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<Range>,
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<PathBuf> = inc_paths.to_vec();
let mut defines = HashMap::new();
let mut reverted_change = false;
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, 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));
}
};
// 最多解析 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 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)) => {
info!("meet preprocess error, text: {text:?}");
// 把出错的地方替换成空格
// 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<LspConfiguration>>,
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 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,
};
let mut file = source_handle.write().unwrap();
// 加入语法树 & 更新 fast
if let Some((syntax_tree, parse_result)) = ast {
if let Ok(fast) = make_fast_from_syntaxtree(&syntax_tree, &escape_path) {
info!("update parse_result: {:?}", parse_result);
hdl_param_handle.update_hdl_file(
escape_path_string.to_string(),
fast,
parse_result,
Some(AstLike::Svlog(syntax_tree))
);
}
file.finish_at_least_once = true;
} else {
file.finish_at_least_once = false;
}
// 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<LspConfiguration>>,
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 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 <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);
let mut global_project = project_handle.write().unwrap();
match &mut *global_project {
Some(vhdl_project) => {
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) {
// 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 {
source.finish_at_least_once = false;
None
};
drop(source);
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<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");
}
}