mirror of
https://github.com/Myriad-Dreamin/tinymist.git
synced 2025-08-03 01:42:14 +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
|
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
|
|
@ -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
|
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",
|
|
@ -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,
|
target_selection_range: range,
|
||||||
}]));
|
}]));
|
||||||
|
|
||||||
log::info!("goto_definition: {fid:?} {res:?}");
|
log::debug!("goto_definition: {fid:?} {res:?}");
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
|
@ -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));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue