148 lines
5.1 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 std::process::{Command, Stdio};
use log::info;
use regex::Regex;
use serde::{Deserialize, Serialize};
use ropey::Rope;
use tower_lsp::lsp_types::{Diagnostic, DiagnosticSeverity, Position, Range, Url};
use crate::{diagnostics::find_non_whitespace_indices, server::LspServer};
use super::AbstractLinterConfiguration;
#[derive(Debug, Serialize, Deserialize)]
pub struct IverilogConfiguration {
pub language_id: String,
pub linter: IverilogLinter
}
#[derive(Debug, Serialize, Deserialize)]
pub struct IverilogLinter {
pub name: String,
pub exe_name: String,
/// 目前是否启动
pub enabled: bool,
pub path: String,
pub args: Vec<String>,
}
impl IverilogLinter {
fn new(invoker: &str, args: Vec<String>) -> Self {
IverilogLinter {
name: "iverilog".to_string(),
exe_name: invoker.to_string(),
enabled: false,
path: invoker.to_string(),
args
}
}
}
impl AbstractLinterConfiguration for IverilogConfiguration {
fn new(language_id: &str, invoker: &str, args: Vec<String>) -> Self {
IverilogConfiguration {
language_id: language_id.to_string(),
linter: IverilogLinter::new(invoker, args)
}
}
fn provide_diagnostics(
&self,
uri: &Url,
rope: &Rope,
server: &LspServer
) -> Option<Vec<Diagnostic>> {
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.stderr).ok()?;
info!("iverilog linter: {:?}, output:\n{}", path_string, output_string);
// 初始化一个正则捕获器
static REGEX_STORE: std::sync::OnceLock<Regex> = std::sync::OnceLock::new();
// {filename}:{errorno}: {error tag}(:{error description})
let regex = REGEX_STORE.get_or_init(|| { Regex::new(r"^(.*?)\s*:\s*(\d+)\s*:\s*(\w+)\s*(?::\s*(.*))?$").unwrap() });
for error_line in output_string.lines() {
let captures = match regex.captures(error_line) {
Some(cap) => cap,
None => continue
};
let error_no = captures.get(2).unwrap().as_str().trim();
let error_tag = captures.get(3).unwrap().as_str().trim();
let error_description = captures.get(4).map_or("", |m| m.as_str()).trim();
let error_no = match error_no.parse::<usize>() {
// Icarus Verilog 在报告错误和警告时,行数是从 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 }
};
// 特殊处理错误信息,比如
// /home/dide/project/Digital-Test/DIDEtemp/user/sim/FFT/FFT_IFFT_tb.v:81: error: Unknown module type: FFT_IFFT
// 找不到模块 XXX此时 error_description 就是 Unknown module type: 找不到的模块名,去 fast 里寻找
if error_description.starts_with("Unknown module type") {
let mut groups = error_description.split(":");
if let Some(unknown_module_name) = groups.nth(1) {
let unknown_module_name = unknown_module_name.trim();
info!("包含 {} ? {}", unknown_module_name, server.db.contains_module(unknown_module_name));
if server.db.contains_module(unknown_module_name) {
continue;
}
}
}
let diagnostic = Diagnostic {
range,
code: None,
severity: Some(DiagnosticSeverity::ERROR),
source: Some("Digital IDE: iverilog".to_string()),
message: format!("{} {}", error_tag, error_description),
related_information: None,
tags: None,
code_description: None,
data: None
};
diagnostics.push(diagnostic);
}
}
Some(diagnostics)
}
fn get_linter_path(&self) -> &str {
&self.linter.path
}
fn get_exe_name(&self) -> String {
if std::env::consts::OS == "windows" {
format!("{}.exe", self.linter.exe_name)
} else {
self.linter.exe_name.to_string()
}
}
}