完成 modelsim | vivado 的诊断
This commit is contained in:
parent
ae083bdad4
commit
4db6837029
@ -6,9 +6,9 @@ use serde::{Deserialize, Serialize};
|
|||||||
use ropey::Rope;
|
use ropey::Rope;
|
||||||
use tower_lsp::lsp_types::{Diagnostic, DiagnosticSeverity, Position, Range, Url};
|
use tower_lsp::lsp_types::{Diagnostic, DiagnosticSeverity, Position, Range, Url};
|
||||||
|
|
||||||
use crate::{diagnostics::find_non_whitespace_indices, server::LspServer, sources::LSPSupport, utils::command::is_command_valid};
|
use crate::{diagnostics::find_non_whitespace_indices, server::LspServer};
|
||||||
|
|
||||||
use super::{AbstractLinterConfiguration, LinterStatus};
|
use super::AbstractLinterConfiguration;
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct IverilogConfiguration {
|
pub struct IverilogConfiguration {
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
|
use std::process::{Command, Stdio};
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
use log::info;
|
use log::info;
|
||||||
|
use regex::Regex;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use crate::{server::LspServer, utils::command::is_command_valid};
|
use crate::{diagnostics::find_non_whitespace_indices, server::LspServer};
|
||||||
use super::{AbstractLinterConfiguration, LinterStatus};
|
use super::AbstractLinterConfiguration;
|
||||||
use ropey::Rope;
|
use ropey::Rope;
|
||||||
use tower_lsp::lsp_types::{Diagnostic, Url};
|
use tower_lsp::lsp_types::{Diagnostic, DiagnosticSeverity, Position, Range, Url};
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct ModelsimConfiguration {
|
pub struct ModelsimConfiguration {
|
||||||
@ -48,10 +51,67 @@ impl AbstractLinterConfiguration for ModelsimConfiguration {
|
|||||||
fn provide_diagnostics(
|
fn provide_diagnostics(
|
||||||
&self,
|
&self,
|
||||||
uri: &Url,
|
uri: &Url,
|
||||||
|
#[allow(unused)]
|
||||||
rope: &Rope,
|
rope: &Rope,
|
||||||
|
#[allow(unused)]
|
||||||
server: &LspServer
|
server: &LspServer
|
||||||
) -> Option<Vec<Diagnostic>> {
|
) -> Option<Vec<Diagnostic>> {
|
||||||
let mut diagnostics = Vec::<Diagnostic>::new();
|
let mut diagnostics = Vec::<Diagnostic>::new();
|
||||||
|
let invoke_name = self.get_invoke_name();
|
||||||
|
let pathbuf = uri.to_file_path().unwrap();
|
||||||
|
let path_string = pathbuf.to_str().unwrap();
|
||||||
|
|
||||||
|
let child = Command::new(&invoke_name)
|
||||||
|
.stdin(Stdio::piped())
|
||||||
|
.stderr(Stdio::piped())
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.args(&self.linter.args)
|
||||||
|
.arg(path_string)
|
||||||
|
.spawn()
|
||||||
|
.ok()?;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
static REGEX_STORE: std::sync::OnceLock<Regex> = std::sync::OnceLock::new();
|
||||||
|
let regex = REGEX_STORE.get_or_init(|| { Regex::new(r"ERROR: \[VRFC \d+-\d+\] (?P<description>.+) \[(?P<file>.+):(?P<line>\d+)\]").unwrap() });
|
||||||
|
|
||||||
|
for error_line in output_string.lines() {
|
||||||
|
let caps = match regex.captures(error_line) {
|
||||||
|
Some(caps) => caps,
|
||||||
|
None => continue
|
||||||
|
};
|
||||||
|
|
||||||
|
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::<usize>() {
|
||||||
|
// Mentor Modelsim vlog 在报告错误和警告时,行数是从 1 开始计数的。
|
||||||
|
Ok(no) => no - 1,
|
||||||
|
Err(_) => 0
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some((start_char, end_char)) = find_non_whitespace_indices(rope, error_no) {
|
||||||
|
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 }
|
||||||
|
};
|
||||||
|
|
||||||
|
let diagnostic = Diagnostic {
|
||||||
|
range,
|
||||||
|
code: None,
|
||||||
|
severity: Some(DiagnosticSeverity::ERROR),
|
||||||
|
source: Some("Digital IDE: modelsim".to_string()),
|
||||||
|
message: error_description.to_string(),
|
||||||
|
related_information: None,
|
||||||
|
tags: None,
|
||||||
|
code_description: None,
|
||||||
|
data: None
|
||||||
|
};
|
||||||
|
diagnostics.push(diagnostic);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Some(diagnostics)
|
Some(diagnostics)
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,11 @@
|
|||||||
|
use log::info;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use ropey::Rope;
|
use ropey::Rope;
|
||||||
use std::process::{Command, Stdio};
|
use std::process::{Command, Stdio};
|
||||||
use tower_lsp::lsp_types::*;
|
use tower_lsp::lsp_types::*;
|
||||||
|
use crate::server::LspServer;
|
||||||
use crate::{server::LspServer, utils::command::is_command_valid};
|
use super::AbstractLinterConfiguration;
|
||||||
|
|
||||||
use super::{AbstractLinterConfiguration, LinterStatus};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct VeribleConfiguration {
|
pub struct VeribleConfiguration {
|
||||||
@ -58,65 +55,6 @@ impl Default for VeribleFormat {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// syntax checking using verible-verilog-syntax
|
|
||||||
fn verible_syntax(
|
|
||||||
rope: &Rope,
|
|
||||||
verible_syntax_path: &str,
|
|
||||||
verible_syntax_args: &[String],
|
|
||||||
) -> Option<Vec<Diagnostic>> {
|
|
||||||
let mut child = Command::new(verible_syntax_path)
|
|
||||||
.stdin(Stdio::piped())
|
|
||||||
.stderr(Stdio::piped())
|
|
||||||
.stdout(Stdio::piped())
|
|
||||||
.args(verible_syntax_args)
|
|
||||||
.arg("-")
|
|
||||||
.spawn()
|
|
||||||
.ok()?;
|
|
||||||
|
|
||||||
static RE: std::sync::OnceLock<Regex> = std::sync::OnceLock::new();
|
|
||||||
let re = RE.get_or_init(|| {
|
|
||||||
Regex::new(
|
|
||||||
r"^.+:(?P<line>\d*):(?P<startcol>\d*)(?:-(?P<endcol>\d*))?:\s(?P<message>.*)\s.*$",
|
|
||||||
)
|
|
||||||
.unwrap()
|
|
||||||
});
|
|
||||||
// write file to stdin, read output from stdout
|
|
||||||
rope.write_to(child.stdin.as_mut()?).ok()?;
|
|
||||||
|
|
||||||
let output = child.wait_with_output().ok()?;
|
|
||||||
if !output.status.success() {
|
|
||||||
let mut diags: Vec<Diagnostic> = Vec::new();
|
|
||||||
let raw_output = String::from_utf8(output.stdout).ok()?;
|
|
||||||
for error in raw_output.lines() {
|
|
||||||
let caps = re.captures(error)?;
|
|
||||||
let line: u32 = caps.name("line")?.as_str().parse().ok()?;
|
|
||||||
let startcol: u32 = caps.name("startcol")?.as_str().parse().ok()?;
|
|
||||||
let endcol: Option<u32> = match caps.name("endcol").map(|e| e.as_str().parse()) {
|
|
||||||
Some(Ok(e)) => Some(e),
|
|
||||||
None => None,
|
|
||||||
Some(Err(_)) => return None,
|
|
||||||
};
|
|
||||||
let start_pos = Position::new(line - 1, startcol - 1);
|
|
||||||
let end_pos = Position::new(line - 1, endcol.unwrap_or(startcol) - 1);
|
|
||||||
diags.push(Diagnostic::new(
|
|
||||||
Range::new(start_pos, end_pos),
|
|
||||||
Some(DiagnosticSeverity::ERROR),
|
|
||||||
None,
|
|
||||||
Some("verible".to_string()),
|
|
||||||
caps.name("message")?.as_str().to_string(),
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
Some(diags)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
impl AbstractLinterConfiguration for VeribleConfiguration {
|
impl AbstractLinterConfiguration for VeribleConfiguration {
|
||||||
fn new(language_id: &str, invoker: &str, args: Vec<String>) -> Self {
|
fn new(language_id: &str, invoker: &str, args: Vec<String>) -> Self {
|
||||||
VeribleConfiguration {
|
VeribleConfiguration {
|
||||||
@ -131,10 +69,57 @@ impl AbstractLinterConfiguration for VeribleConfiguration {
|
|||||||
uri: &Url,
|
uri: &Url,
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
rope: &Rope,
|
rope: &Rope,
|
||||||
|
#[allow(unused)]
|
||||||
server: &LspServer
|
server: &LspServer
|
||||||
) -> Option<Vec<Diagnostic>> {
|
) -> Option<Vec<Diagnostic>> {
|
||||||
let mut diagnostics = Vec::<Diagnostic>::new();
|
let mut diagnostics = Vec::<Diagnostic>::new();
|
||||||
|
|
||||||
|
let invoke_name = self.get_invoke_name();
|
||||||
|
let pathbuf = uri.to_file_path().unwrap();
|
||||||
|
let path_string = pathbuf.to_str().unwrap();
|
||||||
|
|
||||||
|
let child = Command::new(&invoke_name)
|
||||||
|
.stdin(Stdio::piped())
|
||||||
|
.stderr(Stdio::piped())
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.args(&self.linter.args)
|
||||||
|
.arg(path_string)
|
||||||
|
.spawn()
|
||||||
|
.ok()?;
|
||||||
|
|
||||||
|
let output = child.wait_with_output().ok()?;
|
||||||
|
let output_string = String::from_utf8(output.stdout).ok()?;
|
||||||
|
|
||||||
|
info!("verible linter: {:?}, output:\n{}", path_string, output_string);
|
||||||
|
|
||||||
|
static REGEX_STORE: std::sync::OnceLock<Regex> = std::sync::OnceLock::new();
|
||||||
|
let regex = REGEX_STORE.get_or_init(|| { Regex::new(r"^.+:(?P<line>\d*):(?P<startcol>\d*)(?:-(?P<endcol>\d*))?:\s(?P<message>.*)\s.*$").unwrap() });
|
||||||
|
|
||||||
|
for error_line in output_string.lines() {
|
||||||
|
let caps = regex.captures(error_line)?;
|
||||||
|
let line: u32 = caps.name("line")?.as_str().parse().ok()?;
|
||||||
|
let startcol: u32 = caps.name("startcol")?.as_str().parse().ok()?;
|
||||||
|
let endcol: Option<u32> = match caps.name("endcol").map(|e| e.as_str().parse()) {
|
||||||
|
Some(Ok(e)) => Some(e),
|
||||||
|
None => None,
|
||||||
|
Some(Err(_)) => return None,
|
||||||
|
};
|
||||||
|
let start_pos = Position::new(line - 1, startcol - 1);
|
||||||
|
let end_pos = Position::new(line - 1, endcol.unwrap_or(startcol) - 1);
|
||||||
|
let diagnostic = Diagnostic {
|
||||||
|
range: Range::new(start_pos, end_pos),
|
||||||
|
severity: Some(DiagnosticSeverity::ERROR),
|
||||||
|
code: None,
|
||||||
|
source: Some("Digital IDE: verible".to_string()),
|
||||||
|
message: caps.name("message")?.as_str().to_string(),
|
||||||
|
related_information: None,
|
||||||
|
tags: None,
|
||||||
|
code_description: None,
|
||||||
|
data: None
|
||||||
|
};
|
||||||
|
diagnostics.push(diagnostic);
|
||||||
|
}
|
||||||
|
|
||||||
Some(diagnostics)
|
Some(diagnostics)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,14 +2,10 @@ use log::info;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use ropey::Rope;
|
use ropey::Rope;
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::process::{Command, Stdio};
|
use std::process::{Command, Stdio};
|
||||||
use tower_lsp::lsp_types::*;
|
use tower_lsp::lsp_types::*;
|
||||||
|
|
||||||
use crate::server::LspServer;
|
use crate::server::LspServer;
|
||||||
use crate::utils::command::is_command_valid;
|
use super::AbstractLinterConfiguration;
|
||||||
|
|
||||||
use super::{AbstractLinterConfiguration, LinterStatus};
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct VerilatorConfiguration {
|
pub struct VerilatorConfiguration {
|
||||||
@ -83,12 +79,12 @@ impl AbstractLinterConfiguration for VerilatorConfiguration {
|
|||||||
|
|
||||||
let output = child.wait_with_output().ok()?;
|
let output = child.wait_with_output().ok()?;
|
||||||
let output_string = String::from_utf8(output.stderr).ok()?;
|
let output_string = String::from_utf8(output.stderr).ok()?;
|
||||||
|
let lint_level = server.srcs.get_lsp_configuration_string_value("digital-ide.function.lsp.linter.linter-level").unwrap_or("error".to_string());
|
||||||
|
|
||||||
info!("verilator linter: {:?}, output:\n{}", path_string, output_string);
|
info!("verilator linter: {:?}, output:\n{}", path_string, output_string);
|
||||||
|
|
||||||
static REGEX_STORE: std::sync::OnceLock<Regex> = std::sync::OnceLock::new();
|
static REGEX_STORE: std::sync::OnceLock<Regex> = std::sync::OnceLock::new();
|
||||||
let regex = REGEX_STORE.get_or_init(|| { Regex::new(r"%(?P<severity>Error|Warning)(-(?P<warning_type>[A-Z0-9_]+))?: (?P<filepath>[^:]+):(?P<line>\d+):((?P<col>\d+):)? ?(?P<message>.*)").unwrap() });
|
let regex = REGEX_STORE.get_or_init(|| { Regex::new(r"%(?P<severity>Error|Warning)(-(?P<warning_type>[A-Z0-9_]+))?: (?P<filepath>[^:]+):(?P<line>\d+):((?P<col>\d+):)? ?(?P<message>.*)").unwrap() });
|
||||||
|
|
||||||
// verilator 错误输出样例
|
// verilator 错误输出样例
|
||||||
// %Warning-IMPLICIT: library/Apply/Comm/FDE/AGC/AGC.v:23:12: Signal definition not found, creating implicitly: 'r_rms'
|
// %Warning-IMPLICIT: library/Apply/Comm/FDE/AGC/AGC.v:23:12: Signal definition not found, creating implicitly: 'r_rms'
|
||||||
// : ... Suggested alternative: 'ref_rms'
|
// : ... Suggested alternative: 'ref_rms'
|
||||||
@ -116,31 +112,46 @@ impl AbstractLinterConfiguration for VerilatorConfiguration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for error_tuple in error_tuples {
|
for error_tuple in error_tuples {
|
||||||
let captures = match regex.captures(error_tuple.0.as_str()) {
|
// 对于 NOTFOUNDMODULE 进行过滤
|
||||||
|
// 如果存在于 fast 中,则直接跳过
|
||||||
|
if let Some(module_name) = match_not_module_found(error_tuple.0.as_str()) {
|
||||||
|
if server.srcs.contains_module(&module_name) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用模板进行解析
|
||||||
|
let caps = match regex.captures(error_tuple.0.as_str()) {
|
||||||
Some(caps) => caps,
|
Some(caps) => caps,
|
||||||
None => continue
|
None => continue
|
||||||
};
|
};
|
||||||
// 因为 verilator 会递归检查,因此报错信息中可能会出现非本文件内的信息
|
// 因为 verilator 会递归检查,因此报错信息中可能会出现非本文件内的信息
|
||||||
if captures.name("filepath")?.as_str().replace("\\", "/") != path_string {
|
if caps.name("filepath")?.as_str().replace("\\", "/") != path_string {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 对于 NOTFOUNDMODULE
|
let line: u32 = caps.name("line")?.as_str().to_string().parse().ok()?;
|
||||||
|
let col: u32 = caps.name("col").map_or("1", |m| m.as_str()).parse().ok()?;
|
||||||
let line: u32 = captures.name("line")?.as_str().to_string().parse().ok()?;
|
|
||||||
let col: u32 = captures.name("col").map_or("1", |m| m.as_str()).parse().ok()?;
|
|
||||||
|
|
||||||
// verilator 的诊断索引都是 one index 的
|
// verilator 的诊断索引都是 one index 的
|
||||||
let pos = Position::new(line - 1, col - 1);
|
let pos = Position::new(line - 1, col - 1);
|
||||||
let severity = verilator_severity(captures.name("severity")?.as_str());
|
let severity = verilator_severity(caps.name("severity")?.as_str());
|
||||||
|
|
||||||
let message = match severity {
|
let message = match severity {
|
||||||
Some(DiagnosticSeverity::ERROR) => captures.name("message")?.as_str().to_string(),
|
Some(DiagnosticSeverity::ERROR) => {
|
||||||
Some(DiagnosticSeverity::WARNING) => format!(
|
caps.name("message")?.as_str().to_string()
|
||||||
"{}: {}",
|
},
|
||||||
captures.name("warning_type")?.as_str(),
|
Some(DiagnosticSeverity::WARNING) => {
|
||||||
captures.name("message")?.as_str()
|
// 如果诊断等级为 error ,也就是只显示错误,那么直接跳过
|
||||||
),
|
if lint_level == "error" {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
format!(
|
||||||
|
"{}: {}",
|
||||||
|
caps.name("warning_type")?.as_str(),
|
||||||
|
caps.name("message")?.as_str()
|
||||||
|
)
|
||||||
|
},
|
||||||
_ => "".to_string(),
|
_ => "".to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -176,3 +187,23 @@ impl AbstractLinterConfiguration for VerilatorConfiguration {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// %Warning-DECLFILENAME: /home/dide/project/Digital-Test/DIDEtemp/user/sim/FFT/FFT_IFFT_tb.v:82:5: Filename 'FFT_IFFT_tb' does not match NOTFOUNDMODULE name: 'FFT_IFFT'
|
||||||
|
/// 匹配最后一个 module 的 name
|
||||||
|
fn match_not_module_found(error_line: &str) -> Option<String> {
|
||||||
|
if let Some(start_index) = error_line.find("NOTFOUNDMODULE") {
|
||||||
|
let mut strings = "".to_string();
|
||||||
|
for char in error_line.chars().skip(start_index).skip_while(|x| x.to_string() != "'") {
|
||||||
|
strings.push(char);
|
||||||
|
}
|
||||||
|
|
||||||
|
info!("strings: {:?}", strings);
|
||||||
|
if strings.starts_with("'") && strings.ends_with("'") {
|
||||||
|
strings = strings[1 .. strings.len() - 1].to_string();
|
||||||
|
}
|
||||||
|
return Some(strings);
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
@ -1,11 +1,14 @@
|
|||||||
|
use std::process::{Command, Stdio};
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
use log::info;
|
use log::info;
|
||||||
|
use regex::Regex;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use crate::{server::LspServer, utils::command::is_command_valid};
|
use crate::{diagnostics::find_non_whitespace_indices, server::LspServer};
|
||||||
use ropey::Rope;
|
use ropey::Rope;
|
||||||
use tower_lsp::lsp_types::{Diagnostic, Url};
|
use tower_lsp::lsp_types::{Diagnostic, DiagnosticSeverity, Position, Range, Url};
|
||||||
|
|
||||||
use super::{AbstractLinterConfiguration, LinterStatus};
|
use super::AbstractLinterConfiguration;
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct VivadoConfiguration {
|
pub struct VivadoConfiguration {
|
||||||
@ -48,10 +51,68 @@ impl AbstractLinterConfiguration for VivadoConfiguration {
|
|||||||
uri: &Url,
|
uri: &Url,
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
rope: &Rope,
|
rope: &Rope,
|
||||||
|
#[allow(unused)]
|
||||||
server: &LspServer
|
server: &LspServer
|
||||||
) -> Option<Vec<Diagnostic>> {
|
) -> Option<Vec<Diagnostic>> {
|
||||||
let mut diagnostics = Vec::<Diagnostic>::new();
|
let mut diagnostics = Vec::<Diagnostic>::new();
|
||||||
|
|
||||||
|
let invoke_name = self.get_invoke_name();
|
||||||
|
let pathbuf = uri.to_file_path().unwrap();
|
||||||
|
let path_string = pathbuf.to_str().unwrap();
|
||||||
|
|
||||||
|
let child = Command::new(&invoke_name)
|
||||||
|
.stdin(Stdio::piped())
|
||||||
|
.stderr(Stdio::piped())
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.args(&self.linter.args)
|
||||||
|
.arg(path_string)
|
||||||
|
.spawn()
|
||||||
|
.ok()?;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
static REGEX_STORE: std::sync::OnceLock<Regex> = std::sync::OnceLock::new();
|
||||||
|
let regex = REGEX_STORE.get_or_init(|| { Regex::new(r"ERROR: \[VRFC \d+-\d+\] (?P<description>.+) \[(?P<file>.+):(?P<line>\d+)\]").unwrap() });
|
||||||
|
|
||||||
|
for error_line in output_string.lines() {
|
||||||
|
let caps = match regex.captures(error_line) {
|
||||||
|
Some(caps) => caps,
|
||||||
|
None => continue
|
||||||
|
};
|
||||||
|
|
||||||
|
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::<usize>() {
|
||||||
|
// Xilinx Vivado xvlog 在报告错误和警告时,行数是从 1 开始计数的。
|
||||||
|
Ok(no) => no - 1,
|
||||||
|
Err(_) => 0
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some((start_char, end_char)) = find_non_whitespace_indices(rope, error_no) {
|
||||||
|
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 }
|
||||||
|
};
|
||||||
|
|
||||||
|
let diagnostic = Diagnostic {
|
||||||
|
range,
|
||||||
|
code: None,
|
||||||
|
severity: Some(DiagnosticSeverity::ERROR),
|
||||||
|
source: Some("Digital IDE: vivado".to_string()),
|
||||||
|
message: error_description.to_string(),
|
||||||
|
related_information: None,
|
||||||
|
tags: None,
|
||||||
|
code_description: None,
|
||||||
|
data: None
|
||||||
|
};
|
||||||
|
diagnostics.push(diagnostic);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Some(diagnostics)
|
Some(diagnostics)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user