mirror of
https://github.com/Myriad-Dreamin/tinymist.git
synced 2025-07-24 13:13:43 +00:00
test: add rename testing (#448)
This commit is contained in:
parent
ede592796d
commit
1bbf800643
14 changed files with 196 additions and 60 deletions
|
@ -0,0 +1,5 @@
|
|||
// path: user.typ
|
||||
#let f() = 1;
|
||||
-----
|
||||
#import "user.typ": f
|
||||
#(/* position after */ f);
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
source: crates/tinymist-query/src/prepare_rename.rs
|
||||
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
|
|
@ -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"
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
source: crates/tinymist-query/src/prepare_rename.rs
|
||||
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",
|
|
@ -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
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -50,7 +50,7 @@ impl StatefulRequest for GotoDefinitionRequest {
|
|||
target_selection_range: range,
|
||||
}]));
|
||||
|
||||
log::info!("goto_definition: {fid:?} {res:?}");
|
||||
log::debug!("goto_definition: {fid:?} {res:?}");
|
||||
res
|
||||
}
|
||||
}
|
||||
|
|
|
@ -99,8 +99,8 @@ mod tests {
|
|||
use crate::tests::*;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
snapshot_testing("prepare_rename", &|world, path| {
|
||||
fn prepare() {
|
||||
snapshot_testing("rename", &|world, path| {
|
||||
let source = world.source_by_path(&path).unwrap();
|
||||
|
||||
let request = PrepareRenameRequest {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use log::debug;
|
||||
|
||||
use crate::{
|
||||
analysis::SearchCtx,
|
||||
prelude::*,
|
||||
syntax::{DerefTarget, IdentRef},
|
||||
SemanticRequest,
|
||||
|
@ -27,7 +28,7 @@ impl SemanticRequest for ReferencesRequest {
|
|||
let deref_target = ctx.deref_syntax_at(&source, self.position, 1)?;
|
||||
|
||||
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:?}");
|
||||
Some(locations)
|
||||
|
@ -38,7 +39,6 @@ pub(crate) fn find_references(
|
|||
ctx: &mut AnalysisContext<'_>,
|
||||
def_use: Arc<crate::analysis::DefUseInfo>,
|
||||
deref_target: DerefTarget<'_>,
|
||||
position_encoding: PositionEncoding,
|
||||
) -> Option<Vec<LspLocation>> {
|
||||
let node = match deref_target {
|
||||
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_id = root_def_use.get_def(def_fid, &def_ident)?.0;
|
||||
|
||||
find_references_root(
|
||||
ctx,
|
||||
root_def_use,
|
||||
let worker = ReferencesWorker {
|
||||
ctx: ctx.fork_for_search(),
|
||||
references: vec![],
|
||||
def_fid,
|
||||
root_def_id,
|
||||
def_ident,
|
||||
position_encoding,
|
||||
)
|
||||
};
|
||||
|
||||
worker.root(root_def_use, root_def_id)
|
||||
}
|
||||
|
||||
pub(crate) fn find_references_root(
|
||||
ctx: &mut AnalysisContext<'_>,
|
||||
def_use: Arc<crate::analysis::DefUseInfo>,
|
||||
struct ReferencesWorker<'a, 'w> {
|
||||
ctx: SearchCtx<'a, 'w>,
|
||||
references: Vec<LspLocation>,
|
||||
def_fid: TypstFileId,
|
||||
def_id: DefId,
|
||||
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
|
||||
let mut references = def_use
|
||||
.get_refs(def_id)
|
||||
.map(|r| {
|
||||
let range = typst_to_lsp::range(r.range.clone(), &def_source, position_encoding);
|
||||
impl<'a, 'w> ReferencesWorker<'a, 'w> {
|
||||
fn file(&mut self, ref_fid: TypstFileId) -> Option<()> {
|
||||
log::debug!("references: file: {ref_fid:?}");
|
||||
let ref_source = self.ctx.ctx.source_by_id(ref_fid).ok()?;
|
||||
let def_use = self.ctx.ctx.def_use(ref_source.clone())?;
|
||||
|
||||
LspLocation {
|
||||
uri: uri.clone(),
|
||||
range,
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let uri = self.ctx.ctx.uri_for_id(ref_fid).ok()?;
|
||||
|
||||
if def_use.is_exported(def_id) {
|
||||
// Find dependents
|
||||
let mut ctx = ctx.fork_for_search();
|
||||
ctx.push_dependents(def_fid);
|
||||
while let Some(ref_fid) = ctx.worklist.pop() {
|
||||
let ref_source = ctx.ctx.source_by_id(ref_fid).ok()?;
|
||||
let def_use = ctx.ctx.def_use(ref_source.clone())?;
|
||||
let mut redefines = vec![];
|
||||
if let Some((id, _def)) = def_use.get_def(self.def_fid, &self.def_ident) {
|
||||
self.references.extend(def_use.get_refs(id).map(|r| {
|
||||
log::debug!("references: at file: {ref_fid:?}, {r:?}");
|
||||
let range = self.ctx.ctx.to_lsp_range(r.range.clone(), &ref_source);
|
||||
|
||||
let uri = ctx.ctx.uri_for_id(ref_fid).ok()?;
|
||||
|
||||
let mut redefines = vec![];
|
||||
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);
|
||||
LspLocation {
|
||||
uri: uri.clone(),
|
||||
range,
|
||||
}
|
||||
};
|
||||
}
|
||||
}));
|
||||
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)]
|
||||
|
|
|
@ -31,7 +31,7 @@ impl StatefulRequest for RenameRequest {
|
|||
validate_renaming_definition(&lnk)?;
|
||||
|
||||
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();
|
||||
|
||||
|
@ -63,6 +63,8 @@ impl StatefulRequest for RenameRequest {
|
|||
});
|
||||
}
|
||||
|
||||
log::info!("rename editions: {editions:?}");
|
||||
|
||||
// todo: name conflict analysis
|
||||
Some(WorkspaceEdit {
|
||||
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));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -286,6 +286,7 @@ pub static REDACT_LOC: Lazy<RedactFields> = Lazy::new(|| {
|
|||
"location",
|
||||
"uri",
|
||||
"range",
|
||||
"changes",
|
||||
"selectionRange",
|
||||
"targetRange",
|
||||
"targetSelectionRange",
|
||||
|
@ -356,6 +357,29 @@ impl Redact for RedactFields {
|
|||
};
|
||||
|
||||
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"
|
||||
| "selectionRange"
|
||||
| "originSelectionRange"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue