-
Notifications
You must be signed in to change notification settings - Fork 125
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
find refs: add unit test; fix kcl_pos bug
- Loading branch information
1 parent
367dc7a
commit b8cdc93
Showing
13 changed files
with
407 additions
and
149 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,116 +1,244 @@ | ||
use crate::from_lsp::kcl_pos; | ||
use crate::goto_def::goto_definition; | ||
use crate::util::{build_word_index, parse_param_and_compile, Param}; | ||
use anyhow; | ||
use std::collections::HashMap; | ||
use crate::{ | ||
util::{build_word_index, parse_param_and_compile, Param}, | ||
state::{LanguageServerSnapshot, Task, log_message}, | ||
from_lsp::{self, file_path_from_url, kcl_pos}, | ||
goto_def::{goto_definition, find_def,}, | ||
}; | ||
use lsp_types; | ||
use crossbeam_channel::Sender; | ||
use kclvm_config::modfile::get_pkg_root; | ||
use kclvm_ast::ast::Stmt; | ||
use lsp_types::Location; | ||
|
||
|
||
pub(crate) fn find_references ( | ||
snapshot: LanguageServerSnapshot, | ||
params: lsp_types::ReferenceParams, | ||
sender: Sender<Task>, | ||
) -> anyhow::Result<Option<Vec<lsp_types::Location>>> { | ||
// 1. find definition of current token | ||
let file = file_path_from_url(¶ms.text_document_position.text_document.uri)?; | ||
let path = from_lsp::abs_path(¶ms.text_document_position.text_document.uri)?; | ||
let db = snapshot.get_db(&path.clone().into())?; | ||
let pos = kcl_pos(&file, params.text_document_position.position); | ||
|
||
if let Some(def_resp) = goto_definition(&db.prog, &pos, &db.scope) { | ||
match def_resp { | ||
lsp_types::GotoDefinitionResponse::Scalar(def_loc) => { | ||
// get the def location | ||
if let Some(def_name) = match db.prog.pos_to_stmt(&pos) { | ||
Some(node) => match node.node { | ||
Stmt::Import(_) => None, | ||
_ => match find_def(node.clone(), &pos, &db.scope) { | ||
Some(def) => Some(def.get_name()), | ||
None => None, | ||
}, | ||
}, | ||
None => None, | ||
} { | ||
// 2. find all occurrence of current token | ||
// todo: decide the scope by the workspace root and the kcl.mod both, use the narrower scope | ||
if let Some(root) = get_pkg_root(path.display().to_string().as_str()) { | ||
match build_word_index(root) { | ||
Ok(word_index) => { | ||
return find_refs(def_loc, def_name, word_index); | ||
}, | ||
Err(_) => { | ||
let _ = log_message("build word index failed".to_string(), &sender); | ||
return anyhow::Ok(None); | ||
} | ||
} | ||
} else { | ||
return Ok(None) | ||
} | ||
pub(crate) fn find_refs<F: Fn(String) -> Result<(), anyhow::Error>>( | ||
def_loc: Location, | ||
name: String, | ||
cursor_path: String, | ||
logger: F, | ||
) -> anyhow::Result<Option<Vec<Location>>> { | ||
// todo: decide the scope by the workspace root and the kcl.mod both, use the narrower scope | ||
// todo: should use the current file path | ||
if let Some(root) = get_pkg_root(def_loc.uri.path()) { | ||
match build_word_index(root) { | ||
std::result::Result::Ok(word_index) => { | ||
if let Some(locs) = word_index.get(name.as_str()).cloned() { | ||
return anyhow::Ok(Some( | ||
locs.into_iter() | ||
.filter(|ref_loc| { | ||
// from location to real def | ||
// return if the real def location matches the def_loc | ||
let file_path = ref_loc.uri.path().to_string(); | ||
match parse_param_and_compile( | ||
Param { | ||
file: file_path.clone(), | ||
}, | ||
None, | ||
) { | ||
Ok((prog, scope, _)) => { | ||
let ref_pos = kcl_pos(&file_path, ref_loc.range.start); | ||
// find def from the ref_pos | ||
if let Some(real_def) = | ||
goto_definition(&prog, &ref_pos, &scope) | ||
{ | ||
match real_def { | ||
lsp_types::GotoDefinitionResponse::Scalar( | ||
real_def_loc, | ||
) => real_def_loc == def_loc, | ||
_ => false, | ||
} | ||
} else { | ||
false | ||
} | ||
} | ||
Err(_) => { | ||
let _ = logger(format!("{cursor_path} compilation failed")); | ||
return false; | ||
} | ||
} | ||
}) | ||
.collect(), | ||
)); | ||
} else { | ||
return Ok(None); | ||
} | ||
}, | ||
_=> return Ok(None), | ||
} | ||
Err(_) => { | ||
logger("build word index failed".to_string())?; | ||
return Ok(None); | ||
} | ||
} | ||
} else { | ||
log_message("Definition item not found, result in no reference".to_string(), &sender)?; | ||
return Ok(None); | ||
} | ||
|
||
return Ok(None) | ||
} | ||
|
||
pub(crate) fn find_refs(def_loc:lsp_types::Location, name: String, word_index: HashMap<String, Vec<lsp_types::Location>>) | ||
-> anyhow::Result<Option<Vec<lsp_types::Location>>>{ | ||
if let Some(locs) = word_index.get(name.as_str()).cloned() { | ||
return anyhow::Ok(Some(locs.into_iter().filter(|ref_loc|{ | ||
// from location to real def | ||
// return if the real def location matches the def_loc | ||
let file_path = ref_loc.uri.path().to_string(); | ||
match parse_param_and_compile( | ||
Param { | ||
file: file_path.clone(), | ||
}, | ||
None, | ||
) { | ||
Ok((prog, scope, _)) => { | ||
let ref_pos = kcl_pos(&file_path, ref_loc.range.start); | ||
// find def from the ref_pos | ||
if let Some(real_def) = goto_definition(&prog, &ref_pos, &scope) { | ||
match real_def { | ||
lsp_types::GotoDefinitionResponse::Scalar(real_def_loc) => { | ||
real_def_loc == def_loc | ||
}, | ||
_ => false | ||
} | ||
} else { | ||
false | ||
} | ||
#[cfg(test)] | ||
mod tests { | ||
use super::find_refs; | ||
use lsp_types::{Location, Position, Range}; | ||
use proc_macro_crate::bench_test; | ||
use std::{path::PathBuf, vec}; | ||
|
||
fn logger(msg: String) -> Result<(), anyhow::Error> { | ||
println!("{}", msg); | ||
anyhow::Ok(()) | ||
} | ||
|
||
fn check_locations_match(expect: Vec<Location>, actual: anyhow::Result<Option<Vec<Location>>>) { | ||
match actual { | ||
Ok(act) => { | ||
if let Some(locations) = act { | ||
assert_eq!(expect, locations) | ||
} else { | ||
assert!(false, "got empty result. expect: {:?}", expect) | ||
} | ||
Err(_) => { | ||
// todo log compilation error | ||
return false; | ||
}, | ||
} | ||
}).collect())); | ||
} else { | ||
return Ok(None) | ||
Err(_) => assert!(false), | ||
} | ||
} | ||
|
||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
//todo | ||
// todo assert | ||
#[test] | ||
fn test_find_refs() { | ||
|
||
#[bench_test] | ||
fn find_refs_from_variable_test() { | ||
let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")); | ||
let mut path = root.clone(); | ||
path.push("src/test_data/find_refs_test/main.k"); | ||
let path = path.to_str().unwrap(); | ||
match lsp_types::Url::from_file_path(path) { | ||
Ok(url) => { | ||
let def_loc = Location { | ||
uri: url.clone(), | ||
range: Range { | ||
start: Position::new(0, 0), | ||
end: Position::new(0, 1), | ||
}, | ||
}; | ||
let expect = vec![ | ||
Location { | ||
uri: url.clone(), | ||
range: Range { | ||
start: Position::new(0, 0), | ||
end: Position::new(0, 1), | ||
}, | ||
}, | ||
Location { | ||
uri: url.clone(), | ||
range: Range { | ||
start: Position::new(1, 4), | ||
end: Position::new(1, 5), | ||
}, | ||
}, | ||
Location { | ||
uri: url.clone(), | ||
range: Range { | ||
start: Position::new(2, 4), | ||
end: Position::new(2, 5), | ||
}, | ||
}, | ||
Location { | ||
uri: url.clone(), | ||
range: Range { | ||
start: Position::new(12, 14), | ||
end: Position::new(12, 15), | ||
}, | ||
}, | ||
]; | ||
check_locations_match( | ||
expect, | ||
find_refs(def_loc, "a".to_string(), path.to_string(), logger), | ||
); | ||
} | ||
Err(_) => assert!(false, "file not found"), | ||
} | ||
} | ||
|
||
|
||
} | ||
#[test] | ||
#[bench_test] | ||
fn find_refs_from_schema_name_test() { | ||
let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")); | ||
let mut path = root.clone(); | ||
path.push("src/test_data/find_refs_test/main.k"); | ||
let path = path.to_str().unwrap(); | ||
match lsp_types::Url::from_file_path(path) { | ||
Ok(url) => { | ||
let def_loc = Location { | ||
uri: url.clone(), | ||
range: Range { | ||
start: Position::new(4, 0), | ||
end: Position::new(7, 0), | ||
}, | ||
}; | ||
let expect = vec![ | ||
Location { | ||
uri: url.clone(), | ||
range: Range { | ||
start: Position::new(4, 7), | ||
end: Position::new(4, 11), | ||
}, | ||
}, | ||
Location { | ||
uri: url.clone(), | ||
range: Range { | ||
start: Position::new(8, 4), | ||
end: Position::new(8, 8), | ||
}, | ||
}, | ||
Location { | ||
uri: url.clone(), | ||
range: Range { | ||
start: Position::new(11, 7), | ||
end: Position::new(11, 11), | ||
}, | ||
}, | ||
]; | ||
check_locations_match( | ||
expect, | ||
find_refs(def_loc, "Name".to_string(), path.to_string(), logger), | ||
); | ||
} | ||
Err(_) => assert!(false, "file not found"), | ||
} | ||
} | ||
#[test] | ||
#[bench_test] | ||
fn find_refs_from_schema_name_test() { | ||
let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")); | ||
let mut path = root.clone(); | ||
path.push("src/test_data/find_refs_test/main.k"); | ||
let path = path.to_str().unwrap(); | ||
match lsp_types::Url::from_file_path(path) { | ||
Ok(url) => { | ||
let def_loc = Location { | ||
uri: url.clone(), | ||
range: Range { | ||
start: Position::new(4, 0), | ||
end: Position::new(7, 0), | ||
}, | ||
}; | ||
let expect = vec![ | ||
Location { | ||
uri: url.clone(), | ||
range: Range { | ||
start: Position::new(4, 7), | ||
end: Position::new(4, 11), | ||
}, | ||
}, | ||
Location { | ||
uri: url.clone(), | ||
range: Range { | ||
start: Position::new(8, 4), | ||
end: Position::new(8, 8), | ||
}, | ||
}, | ||
Location { | ||
uri: url.clone(), | ||
range: Range { | ||
start: Position::new(11, 7), | ||
end: Position::new(11, 11), | ||
}, | ||
}, | ||
]; | ||
check_locations_match( | ||
expect, | ||
find_refs(def_loc, "Name".to_string(), path.to_string(), logger), | ||
); | ||
} | ||
Err(_) => assert!(false, "file not found"), | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.