991 lines
37 KiB
Rust
991 lines
37 KiB
Rust
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::{provide_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);
|
||
}
|
||
|
||
// 生成诊断信息
|
||
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();
|
||
let diagnostics = provide_diagnostics(
|
||
uri,
|
||
&file.text,
|
||
urls,
|
||
&self.conf.read().unwrap()
|
||
);
|
||
diagnostics
|
||
} 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();
|
||
provide_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,分为 DesignFile(VHDL)和 SyntaxTree(SV)两种
|
||
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");
|
||
}
|
||
}
|