test: add rename testing (#448)

This commit is contained in:
Myriad-Dreamin 2024-07-22 14:29:53 +08:00 committed by GitHub
parent ede592796d
commit 1bbf800643
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 196 additions and 60 deletions

View file

@ -0,0 +1,5 @@
// path: user.typ
#let f() = 1;
-----
#import "user.typ": f
#(/* position after */ f);

View file

@ -1,6 +1,6 @@
--- ---
source: crates/tinymist-query/src/prepare_rename.rs source: crates/tinymist-query/src/prepare_rename.rs
expression: "JsonRepr::new_redacted(result, &REDACT_LOC)" expression: "JsonRepr::new_redacted(result, &REDACT_LOC)"
input_file: crates/tinymist-query/src/fixtures/prepare_rename/builtin.typ input_file: crates/tinymist-query/src/fixtures/rename/builtin.typ
--- ---
null null

View file

@ -0,0 +1,9 @@
---
source: crates/tinymist-query/src/prepare_rename.rs
expression: "JsonRepr::new_redacted(result, &REDACT_LOC)"
input_file: crates/tinymist-query/src/fixtures/rename/cross-module.typ
---
{
"placeholder": "f",
"range": "1:23:1:24"
}

View file

@ -1,7 +1,7 @@
--- ---
source: crates/tinymist-query/src/prepare_rename.rs source: crates/tinymist-query/src/prepare_rename.rs
expression: "JsonRepr::new_redacted(result, &REDACT_LOC)" expression: "JsonRepr::new_redacted(result, &REDACT_LOC)"
input_file: crates/tinymist-query/src/fixtures/prepare_rename/user.typ input_file: crates/tinymist-query/src/fixtures/rename/user.typ
--- ---
{ {
"placeholder": "f", "placeholder": "f",

View file

@ -0,0 +1,6 @@
---
source: crates/tinymist-query/src/rename.rs
expression: "JsonRepr::new_redacted(result, &REDACT_LOC)"
input_file: crates/tinymist-query/src/fixtures/rename/builtin.typ
---
null

View file

@ -0,0 +1,25 @@
---
source: crates/tinymist-query/src/rename.rs
expression: "JsonRepr::new_redacted(result, &REDACT_LOC)"
input_file: crates/tinymist-query/src/fixtures/rename/cross-module.typ
---
{
"changes": {
"s1.typ": [
{
"newText": "new_name",
"range": "0:20:0:21"
},
{
"newText": "new_name",
"range": "1:23:1:24"
}
],
"user.typ": [
{
"newText": "new_name",
"range": "0:5:0:6"
}
]
}
}

View file

@ -0,0 +1,19 @@
---
source: crates/tinymist-query/src/rename.rs
expression: "JsonRepr::new_redacted(result, &REDACT_LOC)"
input_file: crates/tinymist-query/src/fixtures/rename/user.typ
---
{
"changes": {
"s0.typ": [
{
"newText": "new_name",
"range": "1:5:1:6"
},
{
"newText": "new_name",
"range": "3:20:3:21"
}
]
}
}

View file

@ -50,7 +50,7 @@ impl StatefulRequest for GotoDefinitionRequest {
target_selection_range: range, target_selection_range: range,
}])); }]));
log::info!("goto_definition: {fid:?} {res:?}"); log::debug!("goto_definition: {fid:?} {res:?}");
res res
} }
} }

View file

@ -99,8 +99,8 @@ mod tests {
use crate::tests::*; use crate::tests::*;
#[test] #[test]
fn test() { fn prepare() {
snapshot_testing("prepare_rename", &|world, path| { snapshot_testing("rename", &|world, path| {
let source = world.source_by_path(&path).unwrap(); let source = world.source_by_path(&path).unwrap();
let request = PrepareRenameRequest { let request = PrepareRenameRequest {

View file

@ -1,6 +1,7 @@
use log::debug; use log::debug;
use crate::{ use crate::{
analysis::SearchCtx,
prelude::*, prelude::*,
syntax::{DerefTarget, IdentRef}, syntax::{DerefTarget, IdentRef},
SemanticRequest, SemanticRequest,
@ -27,7 +28,7 @@ impl SemanticRequest for ReferencesRequest {
let deref_target = ctx.deref_syntax_at(&source, self.position, 1)?; let deref_target = ctx.deref_syntax_at(&source, self.position, 1)?;
let def_use = ctx.def_use(source.clone())?; let def_use = ctx.def_use(source.clone())?;
let locations = find_references(ctx, def_use, deref_target, ctx.position_encoding())?; let locations = find_references(ctx, def_use, deref_target)?;
debug!("references: {locations:?}"); debug!("references: {locations:?}");
Some(locations) Some(locations)
@ -38,7 +39,6 @@ pub(crate) fn find_references(
ctx: &mut AnalysisContext<'_>, ctx: &mut AnalysisContext<'_>,
def_use: Arc<crate::analysis::DefUseInfo>, def_use: Arc<crate::analysis::DefUseInfo>,
deref_target: DerefTarget<'_>, deref_target: DerefTarget<'_>,
position_encoding: PositionEncoding,
) -> Option<Vec<LspLocation>> { ) -> Option<Vec<LspLocation>> {
let node = match deref_target { let node = match deref_target {
DerefTarget::VarAccess(node) => node, DerefTarget::VarAccess(node) => node,
@ -95,71 +95,83 @@ pub(crate) fn find_references(
let root_def_use = ctx.def_use(def_source)?; let root_def_use = ctx.def_use(def_source)?;
let root_def_id = root_def_use.get_def(def_fid, &def_ident)?.0; let root_def_id = root_def_use.get_def(def_fid, &def_ident)?.0;
find_references_root( let worker = ReferencesWorker {
ctx, ctx: ctx.fork_for_search(),
root_def_use, references: vec![],
def_fid, def_fid,
root_def_id,
def_ident, def_ident,
position_encoding, };
)
worker.root(root_def_use, root_def_id)
} }
pub(crate) fn find_references_root( struct ReferencesWorker<'a, 'w> {
ctx: &mut AnalysisContext<'_>, ctx: SearchCtx<'a, 'w>,
def_use: Arc<crate::analysis::DefUseInfo>, references: Vec<LspLocation>,
def_fid: TypstFileId, def_fid: TypstFileId,
def_id: DefId,
def_ident: IdentRef, def_ident: IdentRef,
position_encoding: PositionEncoding, }
) -> Option<Vec<LspLocation>> {
let def_source = ctx.source_by_id(def_fid).ok()?;
let uri = ctx.uri_for_id(def_fid).ok()?;
// todo: reuse uri, range to location impl<'a, 'w> ReferencesWorker<'a, 'w> {
let mut references = def_use fn file(&mut self, ref_fid: TypstFileId) -> Option<()> {
.get_refs(def_id) log::debug!("references: file: {ref_fid:?}");
.map(|r| { let ref_source = self.ctx.ctx.source_by_id(ref_fid).ok()?;
let range = typst_to_lsp::range(r.range.clone(), &def_source, position_encoding); let def_use = self.ctx.ctx.def_use(ref_source.clone())?;
LspLocation { let uri = self.ctx.ctx.uri_for_id(ref_fid).ok()?;
uri: uri.clone(),
range,
}
})
.collect::<Vec<_>>();
if def_use.is_exported(def_id) { let mut redefines = vec![];
// Find dependents if let Some((id, _def)) = def_use.get_def(self.def_fid, &self.def_ident) {
let mut ctx = ctx.fork_for_search(); self.references.extend(def_use.get_refs(id).map(|r| {
ctx.push_dependents(def_fid); log::debug!("references: at file: {ref_fid:?}, {r:?}");
while let Some(ref_fid) = ctx.worklist.pop() { let range = self.ctx.ctx.to_lsp_range(r.range.clone(), &ref_source);
let ref_source = ctx.ctx.source_by_id(ref_fid).ok()?;
let def_use = ctx.ctx.def_use(ref_source.clone())?;
let uri = ctx.ctx.uri_for_id(ref_fid).ok()?; LspLocation {
uri: uri.clone(),
let mut redefines = vec![]; range,
if let Some((id, _def)) = def_use.get_def(def_fid, &def_ident) {
references.extend(def_use.get_refs(id).map(|r| {
let range =
typst_to_lsp::range(r.range.clone(), &ref_source, position_encoding);
LspLocation {
uri: uri.clone(),
range,
}
}));
redefines.push(id);
if def_use.is_exported(id) {
ctx.push_dependents(ref_fid);
} }
}; }));
} redefines.push(id);
if def_use.is_exported(id) {
self.ctx.push_dependents(ref_fid);
}
};
Some(())
} }
Some(references) fn root(
mut self,
def_use: Arc<crate::analysis::DefUseInfo>,
def_id: DefId,
) -> Option<Vec<LspLocation>> {
let def_source = self.ctx.ctx.source_by_id(self.def_fid).ok()?;
let uri = self.ctx.ctx.uri_for_id(self.def_fid).ok()?;
// todo: reuse uri, range to location
self.references = def_use
.get_refs(def_id)
.map(|r| {
let range = self.ctx.ctx.to_lsp_range(r.range.clone(), &def_source);
LspLocation {
uri: uri.clone(),
range,
}
})
.collect::<Vec<_>>();
if def_use.is_exported(def_id) {
// Find dependents
self.ctx.push_dependents(self.def_fid);
while let Some(ref_fid) = self.ctx.worklist.pop() {
self.file(ref_fid);
}
}
Some(self.references)
}
} }
#[cfg(test)] #[cfg(test)]

View file

@ -31,7 +31,7 @@ impl StatefulRequest for RenameRequest {
validate_renaming_definition(&lnk)?; validate_renaming_definition(&lnk)?;
let def_use = ctx.def_use(source.clone())?; let def_use = ctx.def_use(source.clone())?;
let references = find_references(ctx, def_use, deref_target, ctx.position_encoding())?; let references = find_references(ctx, def_use, deref_target)?;
let mut editions = HashMap::new(); let mut editions = HashMap::new();
@ -63,6 +63,8 @@ impl StatefulRequest for RenameRequest {
}); });
} }
log::info!("rename editions: {editions:?}");
// todo: name conflict analysis // todo: name conflict analysis
Some(WorkspaceEdit { Some(WorkspaceEdit {
changes: Some(editions), changes: Some(editions),
@ -70,3 +72,37 @@ impl StatefulRequest for RenameRequest {
}) })
} }
} }
#[cfg(test)]
mod tests {
use super::*;
use crate::tests::*;
#[test]
fn test() {
snapshot_testing("rename", &|world, path| {
let source = world.source_by_path(&path).unwrap();
let request = RenameRequest {
path: path.clone(),
position: find_test_position(&source),
new_name: "new_name".to_string(),
};
let mut result = request.request(world, None);
// sort the edits to make the snapshot stable
if let Some(r) = result.as_mut().and_then(|r| r.changes.as_mut()) {
for edits in r.values_mut() {
edits.sort_by(|a, b| {
a.range
.start
.cmp(&b.range.start)
.then(a.range.end.cmp(&b.range.end))
});
}
};
assert_snapshot!(JsonRepr::new_redacted(result, &REDACT_LOC));
});
}
}

View file

@ -286,6 +286,7 @@ pub static REDACT_LOC: Lazy<RedactFields> = Lazy::new(|| {
"location", "location",
"uri", "uri",
"range", "range",
"changes",
"selectionRange", "selectionRange",
"targetRange", "targetRange",
"targetSelectionRange", "targetSelectionRange",
@ -356,6 +357,29 @@ impl Redact for RedactFields {
}; };
match k { match k {
"changes" => {
// object range => v
m.insert(
k.to_owned(),
Value::Object(
t.as_object()
.unwrap()
.iter()
.map(|(k, v)| {
(
Path::new(k)
.file_name()
.unwrap()
.to_str()
.unwrap()
.to_owned(),
v.clone(),
)
})
.collect(),
),
);
}
"range" "range"
| "selectionRange" | "selectionRange"
| "originSelectionRange" | "originSelectionRange"