Skip to content

Commit

Permalink
find refs: add unit test; fix kcl_pos bug
Browse files Browse the repository at this point in the history
  • Loading branch information
amyXia1994 committed Sep 20, 2023
1 parent ff5e521 commit 4227404
Show file tree
Hide file tree
Showing 13 changed files with 407 additions and 149 deletions.
4 changes: 2 additions & 2 deletions kclvm/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion kclvm/parser/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ impl Loader {
fn _load_main(&mut self) -> Result<ast::Program, String> {
let compile_entries = get_compile_entries_from_paths(&self.paths, &self.opts)?;
let mut pkgs = HashMap::new();
let workdir = compile_entries.get_root_path().to_string();// get package root
let workdir = compile_entries.get_root_path().to_string();

debug_assert_eq!(compile_entries.len(), self.paths.len());

Expand Down
1 change: 1 addition & 0 deletions kclvm/tools/src/LSP/src/capabilities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti
),
document_formatting_provider: Some(OneOf::Left(true)),
document_range_formatting_provider: Some(OneOf::Left(true)),
references_provider: Some(OneOf::Left(true)),
..Default::default()
}
}
326 changes: 227 additions & 99 deletions kclvm/tools/src/LSP/src/find_refs.rs
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(&params.text_document_position.text_document.uri)?;
let path = from_lsp::abs_path(&params.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"),
}
}
}
6 changes: 1 addition & 5 deletions kclvm/tools/src/LSP/src/from_lsp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,7 @@ pub(crate) fn kcl_pos(file: &str, pos: Position) -> KCLPos {
KCLPos {
filename: file.to_string(),
line: (pos.line + 1) as u64,
column: if pos.character == 0 {
None
} else {
Some(pos.character as u64)
},
column: Some(pos.character as u64),
}
}

Expand Down
Loading

0 comments on commit 4227404

Please sign in to comment.