From 6afe6a1077b34123fa0e7289c16db30cb45a03a9 Mon Sep 17 00:00:00 2001 From: LSTM-Kirigaya <1193466151@qq.com> Date: Wed, 25 Sep 2024 23:19:05 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=20include=20=E8=B7=B3?= =?UTF-8?q?=E8=BD=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/definition.rs | 12 ++-- src/definition/extract_defs.rs | 1 + src/definition/feature.rs | 63 ++++++++++++++++++- src/sources.rs | 15 +++-- src/test/mod.rs | 108 +++++++++++++++++++++++++++++++-- 5 files changed, 183 insertions(+), 16 deletions(-) diff --git a/src/definition.rs b/src/definition.rs index 00de7be..cb8cb2c 100644 --- a/src/definition.rs +++ b/src/definition.rs @@ -10,6 +10,9 @@ use tower_lsp::lsp_types::*; pub mod def_types; pub use def_types::*; +pub mod feature; +pub use feature::*; + mod extract_defs; use extract_defs::*; @@ -22,14 +25,15 @@ impl LSPServer { self.srcs.wait_parse_ready(file_id, false); let file = self.srcs.get_file(file_id)?; let file = file.read().ok()?; - + let line_text = file.text.line(pos.line as usize); let token: String = get_definition_token(&line_text, pos); info!("definition token: {:?}", token); - - - + let include_definition = goto_include_definition(&doc, &line_text, pos); + if include_definition.is_some() { + return include_definition; + } let scope_tree = self.srcs.scope_tree.read().ok()?; diff --git a/src/definition/extract_defs.rs b/src/definition/extract_defs.rs index bc68377..478edd7 100644 --- a/src/definition/extract_defs.rs +++ b/src/definition/extract_defs.rs @@ -1,5 +1,6 @@ use crate::definition::def_types::*; use crate::definition::match_definitions; +use log::info; use sv_parser::*; use tower_lsp::lsp_types::*; diff --git a/src/definition/feature.rs b/src/definition/feature.rs index 807125c..cd4ad61 100644 --- a/src/definition/feature.rs +++ b/src/definition/feature.rs @@ -1,3 +1,62 @@ -pub fn goto_include_definition() { - +use std::path::{Path, PathBuf}; + +use ropey::RopeSlice; +use tower_lsp::lsp_types::{GotoDefinitionResponse, LocationLink, Position, Range, Url}; + +/// 跳转到定义 +pub fn goto_include_definition(uri: &Url, line: &RopeSlice, pos: Position) -> Option { + let line_text = line.as_str().unwrap_or(""); + if line_text.trim().starts_with("`include") { + let character = pos.character as usize; + let first_quote_idx = line_text.find("\""); + let last_quote_idx = line_text.rfind("\""); + + if first_quote_idx.is_none() || last_quote_idx.is_none() { + return None; + } + + let first_quote_idx = first_quote_idx.unwrap(); + let last_quote_idx = last_quote_idx.unwrap(); + if character >= first_quote_idx && character <= last_quote_idx { + let mut path_string = &line_text[(first_quote_idx + 1) .. last_quote_idx]; + if path_string.starts_with("./") || path_string.starts_with(".\\") { + path_string = &path_string[2 ..]; + } + // 此处得到路径,根据是否为绝对路径进行判断 + let path = Path::new(path_string); + let mut abs_path: PathBuf; + if path.is_absolute() { + abs_path = PathBuf::from(path); + } else { + let base = Path::new(uri.path()).parent()?; + abs_path = base.join(path); + } + + if abs_path.exists() { + let target_uri = match Url::from_file_path(abs_path.as_path()) { + Ok(uri) => uri, + Err(_) => return None + }; + + let origin_selection_range = Range::new( + Position { line: pos.line, character: first_quote_idx as u32 }, + Position { line: pos.line, character: last_quote_idx as u32 } + ); + + let target_position = Position { line: 0, character: 0 }; + let target_range = Range::new(target_position, target_position); + + let link = vec![LocationLink { + target_uri, + origin_selection_range: Some(origin_selection_range), + target_range, + target_selection_range: target_range + }]; + let links = GotoDefinitionResponse::Link(link); + return Some(links); + } + } + } + + None } \ No newline at end of file diff --git a/src/sources.rs b/src/sources.rs index 0b7fb1e..f42d233 100644 --- a/src/sources.rs +++ b/src/sources.rs @@ -1,10 +1,7 @@ -use crate::core::sv_parser::make_fast_from_syntaxtree; -use crate::custom_request::update_fast_to_client; use crate::definition::def_types::*; use crate::definition::get_scopes; use crate::diagnostics::{get_diagnostics, is_hidden}; use crate::server::LSPServer; -use futures::executor::block_on; #[allow(unused)] use log::info; use log::{debug, error}; @@ -18,9 +15,11 @@ use std::fs; use std::ops::Deref; use std::ops::Range as StdRange; use std::path::PathBuf; +use std::str::FromStr; use std::sync::{Arc, Condvar, Mutex, RwLock}; use std::thread; use sv_parser::*; +use sv_parser::sv_parser_pp_range; use thread::JoinHandle; use tower_lsp::lsp_types::*; use walkdir::WalkDir; @@ -34,6 +33,11 @@ macro_rules! unwrap_result { }; } +struct CustomRange { + pub begin: usize, + pub end: usize, +} + impl LSPServer { pub fn did_open(&self, params: DidOpenTextDocumentParams) -> PublishDiagnosticsParams { info!("[LSPServer] did open"); @@ -480,10 +484,13 @@ pub fn parse( // 宏定义不存在的错误 sv_parser::Error::DefineNotFound(not_found_macro_name) => { + let range = sv_parser::sv_parser_pp_range::Range { begin: 0, end: 0 }; + let pathbuf = PathBuf::from_str(uri.as_str()).unwrap(); + let origin = Some((pathbuf, range)); let com_define = Define { identifier: not_found_macro_name.to_string(), arguments: Vec::new(), - text: Some(DefineText {text: "undefined".to_string(), origin: None}) + text: Some(DefineText {text: "undefined".to_string(), origin}) }; defines.insert(not_found_macro_name, Some(com_define)); parse_iterations += 1; diff --git a/src/test/mod.rs b/src/test/mod.rs index 34ad40a..f442a47 100644 --- a/src/test/mod.rs +++ b/src/test/mod.rs @@ -4,6 +4,13 @@ const TESTFILES_DIR: &str = "testfiles"; #[allow(unused)] const DIGTIAL_IDE_TEST: &str = "/home/dide/project/Digital-Test/Digital-IDE-test/user"; +#[allow(unused)] +const TEST_FILE: &str = "/home/dide/project/Digital-Test/Digital-IDE-test/user/src/svlog/tools/ivtest/br_gh330.sv"; + +#[allow(unused)] +const INCOMPLETE_EXAMPLE: &str = "/home/dide/project/Digital-Test/Digital-IDE-test/user/src/incomplete-example"; + + #[allow(unused)] macro_rules! unwrap_result { ($expr:expr) => { @@ -61,6 +68,19 @@ mod test_fast { } } + #[test] + fn test_incomplete_example() { + let path = Path::new(INCOMPLETE_EXAMPLE); + if path.exists() && path.is_dir() { + // 递归遍历文件夹 + if let Err(e) = traverse_directory(path) { + eprintln!("Error: {}", e); + } + } else { + eprintln!("Path does not exist or is not a directory"); + } + } + fn traverse_directory(dir: &Path) -> Result<(), Box> { if dir.is_dir() { for entry in fs::read_dir(dir)? { @@ -89,7 +109,8 @@ mod test_fast { continue; } let file_path = path.to_str().unwrap(); - let _ = sv_parser(file_path); + let result = sv_parser(file_path); + assert!(result.is_some()); } } } @@ -101,17 +122,19 @@ mod test_fast { #[cfg(test)] mod test_svparse { - use std::{fs, path::PathBuf}; + use std::{fs, path::{Path, PathBuf}}; use ropey::Rope; use tower_lsp::lsp_types::Url; - use crate::sources::parse; + use super::{INCOMPLETE_EXAMPLE, TEST_FILE}; + #[test] + /// 测试单独的文件 fn test_mycpu() { let includes: Vec = Vec::new(); - let path = "/home/dide/project/Digital-Test/MipsDesign/src/MyCpu.v"; + let path = TEST_FILE; let text = match fs::read_to_string(path) { Ok(text) => text, @@ -122,14 +145,73 @@ mod test_svparse { let uri = Url::from_file_path(&path).unwrap(); let result = parse(&doc, &uri, &None, &includes); match result { - Some(syntax_tree) => { + Some(_) => { println!("success"); }, None => { eprintln!("gen None"); } } + } + #[test] + /// 测试语法不完整的例子 + fn test_incomplete_example() { + let path = Path::new(INCOMPLETE_EXAMPLE); + if path.exists() && path.is_dir() { + // 递归遍历文件夹 + if let Err(e) = traverse_directory(path) { + eprintln!("Error: {}", e); + } + } else { + eprintln!("Path does not exist or is not a directory"); + } + } + + fn traverse_directory(dir: &Path) -> Result<(), Box> { + if dir.is_dir() { + for entry in fs::read_dir(dir)? { + let entry = entry?; + let path = entry.path(); + + if path.is_dir() { + // 递归遍历子文件夹 + traverse_directory(&path)?; + } else if path.is_file() { + // 检查文件扩展名 + if let Some(ext) = path.extension() { + if ext == "v" || ext == "sv" { + println!("Test file: {:?}", path); + // TODO: Check Stack Overflow tests + if path.to_str().unwrap() == "/home/dide/project/Digital-Test/Digital-IDE-test/user/src/svlog/tools/ivtest/comp1001.sv" { + continue; + } + if path.to_str().unwrap() == "/home/dide/project/Digital-Test/Digital-IDE-test/user/src/svlog/tools/ivtest/comp1000.sv" { + continue; + } + if path.to_str().unwrap() == "/home/dide/project/Digital-Test/Digital-IDE-test/user/src/svlog/tools/ivtest/br_gh330.sv" { + continue; + } + if path.to_str().unwrap() == "/home/dide/project/Digital-Test/Digital-IDE-test/user/src/svlog/tools/hdlconv/pri_encoder_using_assign.sv" { + continue; + } + let file_path = path.to_str().unwrap(); + let text = match fs::read_to_string(file_path) { + Ok(text) => text, + Err(_) => continue + }; + + let includes: Vec = Vec::new(); + let doc = Rope::from_str(&text); + let uri = Url::from_file_path(file_path).unwrap(); + let result = parse(&doc, &uri, &None, &includes); + assert!(result.is_some()); + } + } + } + } + } + Ok(()) } } @@ -191,6 +273,19 @@ mod test_scope_tree { } } + #[test] + fn test_incomplete_example() { + let path = Path::new(INCOMPLETE_EXAMPLE); + if path.exists() && path.is_dir() { + // 递归遍历文件夹 + if let Err(e) = traverse_directory(path) { + eprintln!("Error: {}", e); + } + } else { + eprintln!("Path does not exist or is not a directory"); + } + } + fn traverse_directory(dir: &Path) -> Result<(), Box> { if dir.is_dir() { for entry in fs::read_dir(dir)? { @@ -219,7 +314,8 @@ mod test_scope_tree { continue; } let file_path = path.to_str().unwrap(); - let _ = get_scope_tree(file_path); + let scope = get_scope_tree(file_path); + assert!(scope.is_some()); } } }