实现 include 跳转

This commit is contained in:
锦恢 2024-09-25 23:19:05 +08:00
parent c8ae0f78a4
commit 6afe6a1077
5 changed files with 183 additions and 16 deletions

View File

@ -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::*;
@ -27,9 +30,10 @@ impl LSPServer {
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()?;

View File

@ -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::*;

View File

@ -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<GotoDefinitionResponse> {
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
}

View File

@ -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;

View File

@ -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<dyn std::error::Error>> {
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<PathBuf> = 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<dyn std::error::Error>> {
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<PathBuf> = 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<dyn std::error::Error>> {
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());
}
}
}