diff --git a/src/core/cache_storage.rs b/src/core/cache_storage.rs index ef3adc2..190b12d 100644 --- a/src/core/cache_storage.rs +++ b/src/core/cache_storage.rs @@ -38,7 +38,7 @@ impl Default for CacheItem { } -struct CacheInfo { +pub struct CacheInfo { // version pub version: Option, /// 解析类型文件缓存的根目录 diff --git a/src/diagnostics/modelsim.rs b/src/diagnostics/modelsim.rs index b75e972..060d3e4 100644 --- a/src/diagnostics/modelsim.rs +++ b/src/diagnostics/modelsim.rs @@ -61,7 +61,18 @@ impl AbstractLinterConfiguration for ModelsimConfiguration { let pathbuf = uri.to_file_path().unwrap(); let path_string = pathbuf.to_str().unwrap(); + let cache_info = server.cache.cache_info.read().unwrap(); + + let cwd = match &cache_info.linter_cache { + Some(pc) => pc, + None => { + info!("缓存系统尚未完成初始化,本次诊断取消"); + return None; + } + }; + let child = Command::new(&invoke_name) + .current_dir(cwd) .stdin(Stdio::piped()) .stderr(Stdio::piped()) .stdout(Stdio::piped()) @@ -73,10 +84,10 @@ impl AbstractLinterConfiguration for ModelsimConfiguration { let output = child.wait_with_output().ok()?; let output_string = String::from_utf8(output.stdout).ok()?; - info!("vivado linter: {:?}, output:\n{}", path_string, output_string); + info!("modelsim linter: {:?}, output:\n{}", path_string, output_string); static REGEX_STORE: std::sync::OnceLock = std::sync::OnceLock::new(); - let regex = REGEX_STORE.get_or_init(|| { Regex::new(r"ERROR: \[VRFC \d+-\d+\] (?P.+) \[(?P.+):(?P\d+)\]").unwrap() }); + let regex = REGEX_STORE.get_or_init(|| { Regex::new(r"\*\* (Error|Warning): \(\S+\) (?P.*?)(\((?P\d+)\))?: (?P.+)").unwrap() }); for error_line in output_string.lines() { let caps = match regex.captures(error_line) { @@ -84,6 +95,8 @@ impl AbstractLinterConfiguration for ModelsimConfiguration { None => continue }; + info!("get caps: {:?}", caps); + let error_description = caps.name("description").unwrap().as_str(); let error_no = caps.name("line").unwrap().as_str(); let error_no = match error_no.parse::() { diff --git a/src/diagnostics/vivado.rs b/src/diagnostics/vivado.rs index f30dc57..0bb74ed 100644 --- a/src/diagnostics/vivado.rs +++ b/src/diagnostics/vivado.rs @@ -2,8 +2,9 @@ use std::process::{Command, Stdio}; #[allow(unused)] use log::info; -use regex::Regex; +use regex::{escape, Regex}; use serde::{Deserialize, Serialize}; +use xml::name; use crate::{diagnostics::find_non_whitespace_indices, server::LspServer}; use ropey::Rope; use tower_lsp::lsp_types::{Diagnostic, DiagnosticSeverity, Position, Range, Url}; @@ -60,7 +61,18 @@ impl AbstractLinterConfiguration for VivadoConfiguration { let pathbuf = uri.to_file_path().unwrap(); let path_string = pathbuf.to_str().unwrap(); + let cache_info = server.cache.cache_info.read().unwrap(); + + let cwd = match &cache_info.linter_cache { + Some(pc) => pc, + None => { + info!("缓存系统尚未完成初始化,本次诊断取消"); + return None; + } + }; + let child = Command::new(&invoke_name) + .current_dir(cwd) .stdin(Stdio::piped()) .stderr(Stdio::piped()) .stdout(Stdio::piped()) @@ -75,7 +87,7 @@ impl AbstractLinterConfiguration for VivadoConfiguration { info!("vivado linter: {:?}, output:\n{}", path_string, output_string); static REGEX_STORE: std::sync::OnceLock = std::sync::OnceLock::new(); - let regex = REGEX_STORE.get_or_init(|| { Regex::new(r"ERROR: \[VRFC \d+-\d+\] (?P.+) \[(?P.+):(?P\d+)\]").unwrap() }); + let regex = REGEX_STORE.get_or_init(|| { Regex::new(r"ERROR: \[VRFC (?P\S+)] (?P.+) \[(?P.+):(?P\d+)\]").unwrap() }); for error_line in output_string.lines() { let caps = match regex.captures(error_line) { @@ -83,6 +95,7 @@ impl AbstractLinterConfiguration for VivadoConfiguration { None => continue }; + let error_code = caps.name("error_code").unwrap().as_str(); let error_description = caps.name("description").unwrap().as_str(); let error_no = caps.name("line").unwrap().as_str(); let error_no = match error_no.parse::() { @@ -91,7 +104,8 @@ impl AbstractLinterConfiguration for VivadoConfiguration { Err(_) => 0 }; - if let Some((start_char, end_char)) = find_non_whitespace_indices(rope, error_no) { + + if let Some((start_char, end_char)) = find_vivado_suitable_range(rope, error_no, error_description) { let range = Range { start: Position { line: error_no as u32, character: start_char as u32 }, end: Position { line: error_no as u32, character: end_char as u32 } @@ -127,4 +141,46 @@ impl AbstractLinterConfiguration for VivadoConfiguration { self.linter.exe_name.to_string() } } +} + +/// 根据 vivado 返回的诊断信息,返回适合的错误在那一行的起始位置 +/// 默认是返回该行的第一个非空字符到最后一个非空字符中间的位置,即 find_non_whitespace_indices +fn find_vivado_suitable_range( + rope: &Rope, + error_no: usize, + error_description: &str +) -> Option<(usize, usize)> { + // 一般 vivado 会把出错的关键词用单引号包起来 + // 只需要提取这个单词,并在行中匹配它,如果任何一步失败,都采用 find_non_whitespace_indices 即可 + static REGEX_STORE: std::sync::OnceLock = std::sync::OnceLock::new(); + let regex = REGEX_STORE.get_or_init(|| { Regex::new(r"'([^']+)'").unwrap() }); + let error_keyword = match regex.captures(error_description) { + Some(caps) => { + caps.get(1).unwrap().as_str() + } + None => { + return find_non_whitespace_indices(rope, error_no) + } + }; + + let error_keyword = escape(error_keyword); + let pattern = format!(r"\b(?i){}\b", error_keyword); + let regex = Regex::new(&pattern).unwrap(); + + info!("find keyword: {:?}", error_keyword); + + if let Some(line_text) = rope.line(error_no).as_str() { + // 处理特殊情况: error_keyword 为 ; + if error_keyword == ";" { + if let Some(index) = line_text.find(";") { + return Some((index, index)); + } + } + if let Some(mat) = regex.find(line_text) { + info!("mat {} {}", mat.start(), mat.end()); + return Some((mat.start(), mat.end())); + } + } + + find_non_whitespace_indices(rope, error_no) } \ No newline at end of file diff --git a/src/request/fast.rs b/src/request/fast.rs index a44e1df..383e34a 100644 --- a/src/request/fast.rs +++ b/src/request/fast.rs @@ -333,7 +333,7 @@ fn do_vhdl_fast( }) } - +/// 将后端新的 fast 同步到前端去 pub fn sync_fast( path: String, file_type: String, @@ -360,6 +360,7 @@ pub fn sync_fast( } let hdl_param = backend.server.srcs.hdl_param.clone(); + // TODO: 检查加锁的有效性,因为前端在请求该方法时,后端可能仍然在计算 let path_to_hdl_file = hdl_param.path_to_hdl_file.read().unwrap(); if let Some(hdl_file) = path_to_hdl_file.get(&path) { let fast = hdl_file.fast.clone(); diff --git a/src/sources.rs b/src/sources.rs index 1d6f7b8..5fe545f 100644 --- a/src/sources.rs +++ b/src/sources.rs @@ -874,9 +874,9 @@ pub fn vhdl_parser_pipeline( 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()); - } + // 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();