完成 linter 的诊断 pipeline 架构实现

This commit is contained in:
锦恢 2024-12-09 19:33:25 +08:00
parent e726fffd99
commit 723ee40a4e
14 changed files with 382 additions and 129 deletions

View File

@ -26,11 +26,32 @@ pub struct CacheItem {
pub cache_name: String pub cache_name: String
} }
impl Default for CacheItem {
fn default() -> Self {
CacheItem {
file_name: "".to_string(),
size: 0,
version: "".to_string(),
cache_name: "".to_string()
}
}
}
struct CacheInfo {
// version
pub version: Option<String>,
/// 解析类型文件缓存的根目录
pub parser_cache: Option<PathBuf>,
/// 第三方 linter 文件缓存的根目录
pub linter_cache: Option<PathBuf>,
}
/// 用于进行高效 IR 缓存的模块 /// 用于进行高效 IR 缓存的模块
pub struct CacheManager { pub struct CacheManager {
/// 缓存文件夹根目录 /// 缓存文件夹根目录
pub root_dir: PathBuf, pub root_dir: PathBuf,
pub cache_info: Arc<RwLock<CacheInfo>>,
/// meta 文件内容 /// meta 文件内容
pub meta_name: String, pub meta_name: String,
/// meta 内容 /// meta 内容
@ -40,23 +61,52 @@ pub struct CacheManager {
impl CacheManager { impl CacheManager {
pub fn new(root_dir: PathBuf) -> Self { pub fn new(root_dir: PathBuf) -> Self {
// 读入 meta 文件
let meta_name = "index.cache"; let meta_name = "index.cache";
let meta_path = root_dir.join(meta_name); let cache_info = Arc::new(RwLock::new(CacheInfo {
let meta = get_or_init_meta(&meta_path); version: None,
parser_cache: None,
// 如果不存在 root dir则创建 linter_cache: None,
if !root_dir.exists() { }));
match fs::create_dir_all(&root_dir) {
Ok(_) => {},
Err(err) => info!("error happen when create {root_dir:?}: {err:?}")
}
}
CacheManager { CacheManager {
root_dir, root_dir,
cache_info,
meta_name: meta_name.to_string(), meta_name: meta_name.to_string(),
meta: Arc::new(RwLock::new(meta)) meta: Arc::new(RwLock::new(HashMap::<String, CacheItem>::new()))
}
}
pub fn start(&self, version: &str) {
let root_dir = &self.root_dir;
let mut cache_info = self.cache_info.write().unwrap();
cache_info.version = Some(version.to_string());
let linter_cache = root_dir.join(version).join("lc");
let parser_cache = root_dir.join(version).join("pc");
// 如果不存在指定的缓存文件夹,则创建
// ~/digital-ide/{版本号}/
// 📁 lc linter的cache
// 📁 pc parser的cache
CacheManager::check_dir(&linter_cache);
CacheManager::check_dir(&parser_cache);
// 读入 meta 文件
let meta_path = parser_cache.join(&self.meta_name);
let meta = get_or_init_meta(&meta_path);
let mut cache_meta = self.meta.write().unwrap();
for (key, value) in meta.into_iter() {
cache_meta.insert(key, value);
}
info!("缓存系统初始化完成pc: {:?}, lc: {:?}", parser_cache, linter_cache);
cache_info.parser_cache = Some(parser_cache);
cache_info.linter_cache = Some(linter_cache);
}
fn check_dir(dir: &PathBuf) {
if !dir.exists() {
fs::create_dir_all(dir);
} }
} }
@ -123,6 +173,7 @@ impl CacheManager {
hash_string hash_string
} }
/// 更新缓存,并写入磁盘
pub fn update_cache(&self, path: &PathBuf, fast: FastHdlparam) { pub fn update_cache(&self, path: &PathBuf, fast: FastHdlparam) {
let path_string = path.to_str().unwrap(); let path_string = path.to_str().unwrap();
let version = self.get_version(path); let version = self.get_version(path);
@ -139,10 +190,13 @@ impl CacheManager {
let mut meta_handle = self.meta.write().unwrap(); let mut meta_handle = self.meta.write().unwrap();
meta_handle.insert(path_string.to_string(), cache_item); meta_handle.insert(path_string.to_string(), cache_item);
let cache_info = self.cache_info.read().unwrap();
// 准备必要的独立数据塞入线程进行调度 // 准备必要的独立数据塞入线程进行调度
if let Some(parser_cache) = &cache_info.parser_cache {
let meta = (&*meta_handle).clone(); let meta = (&*meta_handle).clone();
let meta_save_path = self.root_dir.join(self.meta_name.clone()); let meta_save_path = parser_cache.join(&self.meta_name);
let cache_save_path = self.root_dir.join(cache_name); let cache_save_path = parser_cache.join(cache_name);
std::thread::spawn(move || { std::thread::spawn(move || {
info!("save meta to {meta_save_path:?}"); info!("save meta to {meta_save_path:?}");
@ -152,6 +206,7 @@ impl CacheManager {
let _ = k_serialize(&cache_save_path, fast); let _ = k_serialize(&cache_save_path, fast);
}); });
} }
}
} }

View File

@ -1,6 +1,9 @@
use std::{path::PathBuf, str::FromStr}; use std::{path::PathBuf, str::FromStr};
#[allow(unused)]
use log::info;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use ropey::Rope;
use tower_lsp::lsp_types::{Diagnostic, Url};
#[derive(Deserialize, Serialize, Debug)] #[derive(Deserialize, Serialize, Debug)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
@ -22,19 +25,21 @@ impl Default for LinterStatus {
} }
pub trait AbstractLinterConfiguration { pub trait AbstractLinterConfiguration {
fn new(language_id: &str) -> Self; /// - language_id: 语言 id
/// - invoker: 调用可执行文件的名字,比如 `iverilog``xvlog``verible-verilog-syntax`
/// 提供一次诊断 /// - args: 调用执行器进行诊断的命令行参数(不包含文件路径)
fn provide_diagnostics(&self); fn new(language_id: &str, invoker: &str, args: Vec<String>) -> Self;
/// 提供一次诊断目前基于 uri 中的地址读取真实路径来获取诊断结果
fn provide_diagnostics(&self, uri: &Url, rope: &Rope) -> Vec<Diagnostic>;
/// 获取工具在 Windows 下的后缀,比如 iverilog 就是 exe xvlog 就是 bat /// 获取工具在 Windows 下的后缀,比如 iverilog 就是 exe xvlog 就是 bat
fn get_windows_extension(&self) -> &str; fn get_windows_extension(&self) -> &str;
/// 获取 linter 安装路径,内部实现,内部调用
fn get_linter_path(&self) -> &str; fn get_linter_path(&self) -> &str;
/// 获取真实调用路径,比如 /// 获取真实调用路径,比如
///
/// iverilog.exe 或者 /path/to/verilog.exe /// iverilog.exe 或者 /path/to/verilog.exe
fn get_invoke_name(&self, execute_name: &str) -> String { fn get_invoke_name(&self, execute_name: &str) -> String {
let suffix = { let suffix = {

View File

@ -1,12 +1,14 @@
use std::{path::PathBuf, str::FromStr}; use std::process::{Command, Stdio};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use ropey::Rope;
use tower_lsp::lsp_types::{Diagnostic, Url};
use crate::utils::command::is_command_valid; use crate::utils::command::is_command_valid;
use super::{AbstractLinterConfiguration, LinterStatus}; use super::{AbstractLinterConfiguration, LinterStatus};
#[derive(Default, Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct IverilogConfiguration { pub struct IverilogConfiguration {
pub language_id: String, pub language_id: String,
pub linter: IverilogLinter pub linter: IverilogLinter
@ -15,33 +17,44 @@ pub struct IverilogConfiguration {
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct IverilogLinter { pub struct IverilogLinter {
pub name: String, pub name: String,
pub exe_name: String,
/// 目前是否启动 /// 目前是否启动
pub enabled: bool, pub enabled: bool,
pub path: String, pub path: String,
pub args: Vec<String>, pub args: Vec<String>,
} }
impl Default for IverilogLinter { impl IverilogLinter {
fn default() -> Self { fn new(invoker: &str, args: Vec<String>) -> Self {
Self { IverilogLinter {
name: "iverilog".to_string(), name: "iverilog".to_string(),
exe_name: invoker.to_string(),
enabled: false, enabled: false,
path: "iverilog".to_string(), path: invoker.to_string(),
args: Vec::new(), args
} }
} }
} }
impl AbstractLinterConfiguration for IverilogConfiguration { impl AbstractLinterConfiguration for IverilogConfiguration {
fn new(language_id: &str) -> Self { fn new(language_id: &str, invoker: &str, args: Vec<String>) -> Self {
IverilogConfiguration { IverilogConfiguration {
language_id: language_id.to_string(), language_id: language_id.to_string(),
linter: IverilogLinter::default() linter: IverilogLinter::new(invoker, args)
} }
} }
fn provide_diagnostics(&self) { fn provide_diagnostics(
&self,
uri: &Url,
#[allow(unused)]
rope: &Rope
) -> Vec<Diagnostic> {
let mut diagnostics = Vec::<Diagnostic>::new();
diagnostics
} }
fn get_windows_extension(&self) -> &str { fn get_windows_extension(&self) -> &str {

View File

@ -26,7 +26,6 @@ pub use modelsim::*;
/// 诊断模块的每一个子诊断器都需要实现如下的函数 /// 诊断模块的每一个子诊断器都需要实现如下的函数
/// - provide_diagnostics: 提供一次诊断 /// - provide_diagnostics: 提供一次诊断
/// - /// -
/// 获取诊断核心函数 /// 获取诊断核心函数
pub fn provide_diagnostics( pub fn provide_diagnostics(
uri: Url, uri: Url,
@ -58,27 +57,46 @@ pub fn provide_diagnostics(
// 根据配置决定使用哪一个诊断器 // 根据配置决定使用哪一个诊断器
// 外层代码需要保证只有一个 linter.enable 为 true // 外层代码需要保证只有一个 linter.enable 为 true
if linter_configuration.verilator.linter.enabled { match linter_configuration {
info!("verilator linter enter"); config if config.iverilog.linter.enabled => {
info!("iverilog linter enter");
} else if linter_configuration.verible.linter.enabled { diagnostics.append(
info!("verible linter enter"); &mut config.iverilog.provide_diagnostics(&uri, rope)
);
} else if linter_configuration.modelsim.linter.enabled { }
info!("modelsim linter enter"); config if config.verilator.linter.enabled => {
info!("verilator linter enter");
} else if linter_configuration.vivado.linter.enabled { diagnostics.append(
info!("vivado linter enter"); &mut config.verilator.provide_diagnostics(&uri, rope)
);
}
config if config.verible.linter.enabled => {
info!("verible linter enter");
diagnostics.append(
&mut config.verible.provide_diagnostics(&uri, rope)
);
}
config if config.modelsim.linter.enabled => {
info!("modelsim linter enter");
diagnostics.append(
&mut config.modelsim.provide_diagnostics(&uri, rope)
);
}
config if config.vivado.linter.enabled => {
info!("vivado linter enter");
diagnostics.append(
&mut config.vivado.provide_diagnostics(&uri, rope)
);
}
_ => {}
} }
PublishDiagnosticsParams { PublishDiagnosticsParams {
uri, diagnostics, uri, diagnostics,
version: None version: None
} }
} }
/// 根据输入的名字选择诊断器 /// 根据输入的名字选择诊断器,并更新所有诊断器的基本路径
/// - `linter_name` 为 `"vivado" | "modelsim" | "verilator" | "verible" | "iverilog"` /// - `linter_name` 为 `"vivado" | "modelsim" | "verilator" | "verible" | "iverilog"`
/// - `language_id` 为 `"vhdl" | "verilog" | "systemverilog"` /// - `language_id` 为 `"vhdl" | "verilog" | "systemverilog"`
/// - `linter_path` 为第三方的可执行文件的路径 /// - `linter_path` 为第三方的可执行文件的路径
@ -114,27 +132,44 @@ pub fn update_diagnostics_configuration(
"iverilog" => { "iverilog" => {
linter_configuration.iverilog.linter.enabled = true; linter_configuration.iverilog.linter.enabled = true;
linter_configuration.iverilog.linter.path = linter_path.to_string(); linter_configuration.iverilog.linter.path = linter_path.to_string();
info!("{} 诊断器 {} 已经激活, 工作负载为 {}", language_id, linter_name, linter_configuration.iverilog.linter.path); let invoke_name = linter_configuration.iverilog.get_invoke_name("iverilog");
info!("{} 诊断器 {} 已经激活, 工作负载为 {}", language_id, linter_name, invoke_name);
} }
"verilator" => { "verilator" => {
linter_configuration.verilator.linter.enabled = true; linter_configuration.verilator.linter.enabled = true;
linter_configuration.verilator.linter.path = linter_path.to_string(); linter_configuration.verilator.linter.path = linter_path.to_string();
info!("{} 诊断器 {} 已经激活, 工作负载为 {}", language_id, linter_name, linter_configuration.verilator.linter.path); let invoke_name = linter_configuration.verilator.get_invoke_name("verilator");
info!("{} 诊断器 {} 已经激活, 工作负载为 {}", language_id, linter_name, invoke_name);
} }
"verible" => { "verible" => {
linter_configuration.verible.linter.enabled = true; linter_configuration.verible.linter.enabled = true;
linter_configuration.verible.linter.path = linter_path.to_string(); linter_configuration.verible.linter.path = linter_path.to_string();
info!("{} 诊断器 {} 已经激活, 工作负载为 {}", language_id, linter_name, linter_configuration.verible.linter.path); let invoke_name = linter_configuration.verible.get_invoke_name("verible-verilog-syntax");
info!("{} 诊断器 {} 已经激活, 工作负载为 {}", language_id, linter_name, invoke_name);
} }
"modelsim" => { "modelsim" => {
linter_configuration.modelsim.linter.enabled = true; linter_configuration.modelsim.linter.enabled = true;
linter_configuration.modelsim.linter.path = linter_path.to_string(); linter_configuration.modelsim.linter.path = linter_path.to_string();
info!("{} 诊断器 {} 已经激活, 工作负载为 {}", language_id, linter_name, linter_configuration.modelsim.linter.path); let invoke_name = {
if language_id == "vhdl" {
linter_configuration.modelsim.get_invoke_name("vcom")
} else {
linter_configuration.modelsim.get_invoke_name("vlog")
}
};
info!("{} 诊断器 {} 已经激活, 工作负载为 {}", language_id, linter_name, invoke_name);
} }
"vivado" => { "vivado" => {
linter_configuration.vivado.linter.enabled = true; linter_configuration.vivado.linter.enabled = true;
linter_configuration.vivado.linter.path = linter_path.to_string(); linter_configuration.vivado.linter.path = linter_path.to_string();
info!("{} 诊断器 {} 已经激活, 工作负载为 {}", language_id, linter_name, linter_configuration.vivado.linter.path); let invoke_name = {
if language_id == "vhdl" {
linter_configuration.vivado.get_invoke_name("xvhdl")
} else {
linter_configuration.vivado.get_invoke_name("xvlog")
}
};
info!("{} 诊断器 {} 已经激活, 工作负载为 {}", language_id, linter_name, invoke_name);
} }
_ => { _ => {
info!("未找到匹配的诊断器: {}", linter_name); info!("未找到匹配的诊断器: {}", linter_name);
@ -172,12 +207,128 @@ pub struct DigitalLinterConfiguration {
impl DigitalLinterConfiguration { impl DigitalLinterConfiguration {
pub fn new(language_id: &str) -> Self { pub fn new(language_id: &str) -> Self {
match language_id {
"vhdl" => {
DigitalLinterConfiguration { DigitalLinterConfiguration {
iverilog: IverilogConfiguration::new(language_id), iverilog: IverilogConfiguration::new(
verible: VeribleConfiguration::new(language_id), language_id,
verilator: VerilatorConfiguration::new(language_id), "iverilog",
modelsim: ModelsimConfiguration::new(language_id), vec![
vivado: VivadoConfiguration::new(language_id) "-t null".to_string()
]
),
verible: VeribleConfiguration::new(
language_id,
"verible-verilog-syntax",
vec![]
),
verilator: VerilatorConfiguration::new(
language_id,
"verilator",
vec![]
),
modelsim: ModelsimConfiguration::new(
language_id,
"vcom",
vec![
"-quiet".to_string(),
"-nologo".to_string(),
"-2008".to_string()
]
),
vivado: VivadoConfiguration::new(
language_id,
"xvhdl",
vec![
"--nolog".to_string()
]
)
}
}
"systemverilog" => {
DigitalLinterConfiguration {
iverilog: IverilogConfiguration::new(
language_id,
"iverilog",
vec![
"-g2012".to_string()
]
),
verible: VeribleConfiguration::new(
language_id,
"verible-verilog-syntax",
vec![]
),
verilator: VerilatorConfiguration::new(
language_id,
"verilator",
vec![
"--lint-only".to_string(),
"-sv".to_string(),
"-Wall".to_string()
]
),
modelsim: ModelsimConfiguration::new(
language_id,
"vlog",
vec![
"-quiet".to_string(),
"-nologo".to_string(),
"-sv".to_string()
]
),
vivado: VivadoConfiguration::new(
language_id,
"xvlog",
vec![
"--sv".to_string(),
"--nolog".to_string()
]
)
}
}
// 默认为 verilog
_ => {
DigitalLinterConfiguration {
iverilog: IverilogConfiguration::new(
language_id,
"iverilog",
vec![
"-t null".to_string()
]
),
verible: VeribleConfiguration::new(
language_id,
"verible-verilog-syntax",
vec![]
),
verilator: VerilatorConfiguration::new(
language_id,
"verilator",
vec![
"--lint-only".to_string(),
"-Wall".to_string()
]
),
modelsim: ModelsimConfiguration::new(
language_id,
"vlog",
vec![
"-quiet".to_string(),
"-nologo".to_string()
]
),
vivado: VivadoConfiguration::new(
language_id,
"xvlog",
vec![
"--nolog".to_string()
]
)
}
}
} }
} }
} }

View File

@ -1,12 +1,12 @@
use std::{path::PathBuf, str::FromStr}; #[allow(unused)]
use log::info;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::utils::command::is_command_valid; use crate::utils::command::is_command_valid;
use super::{AbstractLinterConfiguration, LinterStatus}; use super::{AbstractLinterConfiguration, LinterStatus};
use ropey::Rope;
use tower_lsp::lsp_types::{Diagnostic, Url};
#[derive(Default, Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct ModelsimConfiguration { pub struct ModelsimConfiguration {
pub language_id: String, pub language_id: String,
pub linter: ModelsimLinter pub linter: ModelsimLinter
@ -16,6 +16,7 @@ pub struct ModelsimConfiguration {
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct ModelsimLinter { pub struct ModelsimLinter {
pub name: String, pub name: String,
pub exe_name: String,
/// 目前是否启动 /// 目前是否启动
pub enabled: bool, pub enabled: bool,
pub path: String, pub path: String,
@ -23,28 +24,36 @@ pub struct ModelsimLinter {
} }
impl Default for ModelsimLinter { impl ModelsimLinter {
fn default() -> Self { fn new(invoker: &str, args: Vec<String>) -> Self {
Self { Self {
name: "modelsim".to_string(), name: "modelsim".to_string(),
exe_name: invoker.to_string(),
enabled: false, enabled: false,
path: "Modelsim-verilog-syntax".to_string(), path: invoker.to_string(),
args: Vec::new(), args,
} }
} }
} }
impl AbstractLinterConfiguration for ModelsimConfiguration { impl AbstractLinterConfiguration for ModelsimConfiguration {
fn new(language_id: &str) -> Self { fn new(language_id: &str, invoker: &str, args: Vec<String>) -> Self {
ModelsimConfiguration { ModelsimConfiguration {
language_id: language_id.to_string(), language_id: language_id.to_string(),
linter: ModelsimLinter::default() linter: ModelsimLinter::new(invoker, args)
} }
} }
fn provide_diagnostics(&self) { fn provide_diagnostics(
&self,
uri: &Url,
#[allow(unused)]
rope: &Rope
) -> Vec<Diagnostic> {
let mut diagnostics = Vec::<Diagnostic>::new();
diagnostics
} }
fn get_windows_extension(&self) -> &str { fn get_windows_extension(&self) -> &str {

View File

@ -10,7 +10,7 @@ use super::{AbstractLinterConfiguration, LinterStatus};
#[derive(Default, Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct VeribleConfiguration { pub struct VeribleConfiguration {
pub language_id: String, pub language_id: String,
pub linter: VeribleLinter, pub linter: VeribleLinter,
@ -20,6 +20,7 @@ pub struct VeribleConfiguration {
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct VeribleLinter { pub struct VeribleLinter {
pub name: String, pub name: String,
pub exe_name: String,
/// 目前是否启动 /// 目前是否启动
pub enabled: bool, pub enabled: bool,
pub path: String, pub path: String,
@ -33,13 +34,14 @@ pub struct VeribleFormat {
pub args: Vec<String>, pub args: Vec<String>,
} }
impl Default for VeribleLinter { impl VeribleLinter {
fn default() -> Self { fn new(invoker: &str, args: Vec<String>) -> Self {
Self { Self {
name: "verible".to_string(), name: "verible".to_string(),
exe_name: invoker.to_string(),
enabled: false, enabled: false,
path: "verible-verilog-syntax".to_string(), path: invoker.to_string(),
args: Vec::new(), args,
} }
} }
} }
@ -116,16 +118,23 @@ fn verible_syntax(
impl AbstractLinterConfiguration for VeribleConfiguration { impl AbstractLinterConfiguration for VeribleConfiguration {
fn new(language_id: &str) -> Self { fn new(language_id: &str, invoker: &str, args: Vec<String>) -> Self {
VeribleConfiguration { VeribleConfiguration {
language_id: language_id.to_string(), language_id: language_id.to_string(),
linter: VeribleLinter::default(), linter: VeribleLinter::new(invoker, args),
format: VeribleFormat::default() format: VeribleFormat::default()
} }
} }
fn provide_diagnostics(&self) { fn provide_diagnostics(
&self,
uri: &Url,
#[allow(unused)]
rope: &Rope
) -> Vec<Diagnostic> {
let mut diagnostics = Vec::<Diagnostic>::new();
diagnostics
} }
fn get_linter_path(&self) -> &str { fn get_linter_path(&self) -> &str {

View File

@ -9,7 +9,7 @@ use crate::utils::command::is_command_valid;
use super::{AbstractLinterConfiguration, LinterStatus}; use super::{AbstractLinterConfiguration, LinterStatus};
#[derive(Debug, Default, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct VerilatorConfiguration { pub struct VerilatorConfiguration {
pub language_id: String, pub language_id: String,
pub linter: VerilatorLinter, pub linter: VerilatorLinter,
@ -18,23 +18,21 @@ pub struct VerilatorConfiguration {
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct VerilatorLinter { pub struct VerilatorLinter {
pub name: String, pub name: String,
pub exe_name: String,
/// 目前是否启动 /// 目前是否启动
pub enabled: bool, pub enabled: bool,
pub path: String, pub path: String,
pub args: Vec<String>, pub args: Vec<String>,
} }
impl Default for VerilatorLinter { impl VerilatorLinter {
fn default() -> Self { fn new(invoker: &str, args: Vec<String>) -> Self {
Self { Self {
name: "verilator".to_string(), name: "verilator".to_string(),
exe_name: invoker.to_string(),
enabled: false, enabled: false,
path: "verilator".to_string(), path: invoker.to_string(),
args: vec![ args,
"--lint-only".to_string(),
"--sv".to_string(),
"-Wall".to_string(),
],
} }
} }
} }
@ -125,15 +123,22 @@ fn verilator_syntax(
impl AbstractLinterConfiguration for VerilatorConfiguration { impl AbstractLinterConfiguration for VerilatorConfiguration {
fn new(language_id: &str) -> Self { fn new(language_id: &str, invoker: &str, args: Vec<String>) -> Self {
VerilatorConfiguration { VerilatorConfiguration {
language_id: language_id.to_string(), language_id: language_id.to_string(),
linter: VerilatorLinter::default() linter: VerilatorLinter::new(invoker, args)
} }
} }
fn provide_diagnostics(&self) { fn provide_diagnostics(
&self,
uri: &Url,
#[allow(unused)]
rope: &Rope
) -> Vec<Diagnostic> {
let mut diagnostics = Vec::<Diagnostic>::new();
diagnostics
} }
fn get_linter_path(&self) -> &str { fn get_linter_path(&self) -> &str {

View File

@ -1,10 +1,13 @@
#[allow(unused)]
use log::info;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::utils::command::is_command_valid; use crate::utils::command::is_command_valid;
use ropey::Rope;
use tower_lsp::lsp_types::{Diagnostic, Url};
use super::{AbstractLinterConfiguration, LinterStatus}; use super::{AbstractLinterConfiguration, LinterStatus};
#[derive(Debug, Default, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct VivadoConfiguration { pub struct VivadoConfiguration {
pub language_id: String, pub language_id: String,
pub linter: VivadoLinter, pub linter: VivadoLinter,
@ -13,37 +16,42 @@ pub struct VivadoConfiguration {
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct VivadoLinter { pub struct VivadoLinter {
pub name: String, pub name: String,
pub exe_name: String,
/// 目前是否启动 /// 目前是否启动
pub enabled: bool, pub enabled: bool,
pub path: String, pub path: String,
pub args: Vec<String>, pub args: Vec<String>,
} }
impl Default for VivadoLinter { impl VivadoLinter {
fn default() -> Self { fn new(invoker: &str, args: Vec<String>) -> Self {
Self { Self {
name: "vivado".to_string(), name: "vivado".to_string(),
exe_name: invoker.to_string(),
enabled: false, enabled: false,
path: "Vivado".to_string(), path: invoker.to_string(),
args: vec![ args,
"--lint-only".to_string(),
"--sv".to_string(),
"-Wall".to_string(),
],
} }
} }
} }
impl AbstractLinterConfiguration for VivadoConfiguration { impl AbstractLinterConfiguration for VivadoConfiguration {
fn new(language_id: &str) -> Self { fn new(language_id: &str, invoker: &str, args: Vec<String>) -> Self {
VivadoConfiguration { VivadoConfiguration {
language_id: language_id.to_string(), language_id: language_id.to_string(),
linter: VivadoLinter::default() linter: VivadoLinter::new(invoker, args)
} }
} }
fn provide_diagnostics(&self) { fn provide_diagnostics(
&self,
uri: &Url,
#[allow(unused)]
rope: &Rope
) -> Vec<Diagnostic> {
let mut diagnostics = Vec::<Diagnostic>::new();
diagnostics
} }
fn get_linter_path(&self) -> &str { fn get_linter_path(&self) -> &str {

View File

@ -69,9 +69,10 @@ fn update_configuration(
let linter_name_configuration = &configs[0]; let linter_name_configuration = &configs[0];
let linter_path_configuration = &configs[1]; let linter_path_configuration = &configs[1];
// something like digital-ide.function.lsp.linter.vlog.diagnostor // linter_name_config_name 形如 digital-ide.function.lsp.linter.vlog.diagnostor
let linter_name_config_name = &linter_name_configuration.name; let linter_name_config_name = &linter_name_configuration.name;
// 同步到全局配置中
lsp_configuration.insert( lsp_configuration.insert(
linter_name_config_name.to_string(), linter_name_config_name.to_string(),
linter_name_configuration.value.clone() linter_name_configuration.value.clone()
@ -82,12 +83,7 @@ fn update_configuration(
// linter_name_config_name 形如 digital-ide.function.lsp.linter.vlog.diagnostor // linter_name_config_name 形如 digital-ide.function.lsp.linter.vlog.diagnostor
let mut cookies = linter_name_config_name.split("."); let mut cookies = linter_name_config_name.split(".");
let name = cookies.nth(4).unwrap(); let name = cookies.nth(4).unwrap();
match name { name
"vlog" => "verilog",
"vhdl" => "vhdl",
"svlog" => "systemverilog",
_ => return
}
}; };
let linter_name = linter_name_configuration.value.as_str().unwrap(); let linter_name = linter_name_configuration.value.as_str().unwrap();

View File

@ -46,7 +46,6 @@ fn get_linter_status(
linter_path: &str linter_path: &str
) -> Result<LinterStatus> { ) -> Result<LinterStatus> {
let configuration = backend.server.configuration.read().unwrap(); let configuration = backend.server.configuration.read().unwrap();
// 获取对应语言的配置项目 // 获取对应语言的配置项目
let configuration = match language_id { let configuration = match language_id {
"verilog" => &configuration.vlog_linter_configuration, "verilog" => &configuration.vlog_linter_configuration,
@ -86,7 +85,7 @@ fn get_linter_status(
_ => { _ => {
return Err(tower_lsp::jsonrpc::Error { return Err(tower_lsp::jsonrpc::Error {
code: tower_lsp::jsonrpc::ErrorCode::InvalidRequest, code: tower_lsp::jsonrpc::ErrorCode::InvalidRequest,
message: Cow::Owned(format!("无效的语言 ID {}", language_id)), message: Cow::Owned(format!("无效的诊断器 {}", linter_name)),
data: None data: None
}); });
} }

View File

@ -131,11 +131,7 @@ impl Default for LspConfiguration {
impl LanguageServer for Backend { impl LanguageServer for Backend {
async fn initialize(&self, params: InitializeParams) -> Result<InitializeResult> { async fn initialize(&self, params: InitializeParams) -> Result<InitializeResult> {
// 申明 LSP 的基本信息和提供的能力 // 申明 LSP 的基本信息和提供的能力
let server_info = Some(ServerInfo { let mut version = "0.4.0".to_string();
name: "Digital IDE 专用 LSP 后端服务器".to_string(),
version: Some("0.4.0".to_string())
});
let root_uri = &params.root_uri; let root_uri = &params.root_uri;
let mut configure = self.server.configuration.write().unwrap(); let mut configure = self.server.configuration.write().unwrap();
@ -144,10 +140,16 @@ impl LanguageServer for Backend {
if let Some(serde_json::Value::Object(options)) = params.initialization_options { if let Some(serde_json::Value::Object(options)) = params.initialization_options {
let extension_path = options.get("extensionPath").unwrap().as_str().unwrap(); let extension_path = options.get("extensionPath").unwrap().as_str().unwrap();
let tool_chain = options.get("toolChain").unwrap().as_str().unwrap(); let tool_chain = options.get("toolChain").unwrap().as_str().unwrap();
version = options.get("version").unwrap().as_str().unwrap().to_string();
configure.tool_chain = tool_chain.to_string(); configure.tool_chain = tool_chain.to_string();
configure.extension_path = extension_path.to_string(); configure.extension_path = extension_path.to_string();
} }
let server_info = Some(ServerInfo {
name: "Digital IDE 专用 LSP 后端服务器".to_string(),
version: Some(version.to_string())
});
info!("当前客户端初始化结果"); info!("当前客户端初始化结果");
info!("workspaceFolder: {:?}", configure.workspace_folder); info!("workspaceFolder: {:?}", configure.workspace_folder);
info!("extensionPath: {:?}", configure.extension_path); info!("extensionPath: {:?}", configure.extension_path);
@ -159,8 +161,8 @@ impl LanguageServer for Backend {
&configure.extension_path &configure.extension_path
); );
// 初始化诊断器 // 初始化系统缓存路径
self.server.cache.start(&version);
let text_document_sync = TextDocumentSyncCapability::Options( let text_document_sync = TextDocumentSyncCapability::Options(
TextDocumentSyncOptions { TextDocumentSyncOptions {

View File

@ -31,7 +31,6 @@ use sv_parser::*;
use vhdl_lang::Project; use vhdl_lang::Project;
use thread::JoinHandle; use thread::JoinHandle;
use tower_lsp::lsp_types::*; use tower_lsp::lsp_types::*;
use walkdir::WalkDir;
impl LspServer { impl LspServer {
pub fn did_open(&self, params: DidOpenTextDocumentParams) -> PublishDiagnosticsParams { pub fn did_open(&self, params: DidOpenTextDocumentParams) -> PublishDiagnosticsParams {
@ -100,6 +99,8 @@ impl LspServer {
} }
} }
/// 保存时触发的行为
/// - 提供诊断信息(主要是第三方诊断器,因为 save 后磁盘上的内容就和缓冲区中的一致了)
pub fn did_save(&self, params: DidSaveTextDocumentParams) -> PublishDiagnosticsParams { pub fn did_save(&self, params: DidSaveTextDocumentParams) -> PublishDiagnosticsParams {
let urls = self.srcs.names.read().unwrap().keys().cloned().collect(); let urls = self.srcs.names.read().unwrap().keys().cloned().collect();
let file_id = self.srcs.get_id(&params.text_document.uri); let file_id = self.srcs.get_id(&params.text_document.uri);

View File

@ -3,7 +3,7 @@ use std::process::Command;
pub fn is_command_valid(command: &str) -> bool { pub fn is_command_valid(command: &str) -> bool {
// 尝试执行命令 // 尝试执行命令
match Command::new(command).output() { match Command::new(command).output() {
Ok(output) => output.status.success(), Ok(_) => true,
Err(_) => false, Err(_) => false,
} }
} }

@ -1 +1 @@
Subproject commit b6ae8b8a1ff22609e2a837b12dd798226f299f71 Subproject commit ba95abd436b0a032e0cfdcb9400ce2cfb678792f