tinymist_query/
prepare_rename.rsuse tinymist_world::vfs::WorkspaceResolver;
use crate::{
analysis::Definition,
prelude::*,
syntax::{Decl, SyntaxClass},
};
#[derive(Debug, Clone)]
pub struct PrepareRenameRequest {
pub path: PathBuf,
pub position: LspPosition,
}
impl StatefulRequest for PrepareRenameRequest {
type Response = PrepareRenameResponse;
fn request(self, ctx: &mut LocalContext, graph: LspComputeGraph) -> Option<Self::Response> {
let doc = graph.snap.success_doc.as_ref();
let source = ctx.source_by_path(&self.path).ok()?;
let syntax = ctx.classify_for_decl(&source, self.position)?;
if matches!(syntax.node().kind(), SyntaxKind::FieldAccess) {
log::info!("prepare_rename: field access is not a definition site");
return None;
}
let origin_selection_range = ctx.to_lsp_range(syntax.node().range(), &source);
let def = ctx.def_of_syntax(&source, doc, syntax.clone())?;
let (name, range) = prepare_renaming(&syntax, &def)?;
Some(PrepareRenameResponse::RangeWithPlaceholder {
range: range.unwrap_or(origin_selection_range),
placeholder: name,
})
}
}
pub(crate) fn prepare_renaming(
deref_target: &SyntaxClass,
def: &Definition,
) -> Option<(String, Option<LspRange>)> {
let name = def.name().clone();
let def_fid = def.file_id()?;
if WorkspaceResolver::is_package_file(def_fid) {
crate::log_debug_ct!(
"prepare_rename: {name} is in a package {pkg:?}",
pkg = def_fid.package(),
);
return None;
}
let var_rename = || Some((name.to_string(), None));
crate::log_debug_ct!("prepare_rename: {name}");
use Decl::*;
match def.decl.as_ref() {
Var(..) => var_rename(),
Func(..) | Closure(..) => validate_fn_renaming(def).map(|_| (name.to_string(), None)),
Module(..) | ModuleAlias(..) | PathStem(..) | ImportPath(..) | IncludePath(..)
| ModuleImport(..) => {
let node = deref_target.node().get().clone();
let path = node.cast::<ast::Str>()?;
let name = path.get().to_string();
Some((name, None))
}
BibEntry(..) | Label(..) | ContentRef(..) => None,
ImportAlias(..) | Constant(..) | IdentRef(..) | Import(..) | StrName(..) | Spread(..) => {
None
}
Pattern(..) | Content(..) | Generated(..) | Docs(..) => None,
}
}
fn validate_fn_renaming(def: &Definition) -> Option<()> {
use typst::foundations::func::Repr;
let value = def.value();
let mut func = match &value {
None => return Some(()),
Some(Value::Func(func)) => func,
Some(..) => {
log::info!(
"prepare_rename: not a function on function definition site: {:?}",
def.term
);
return None;
}
};
loop {
match func.inner() {
Repr::With(w) => func = &w.0,
Repr::Closure(..) | Repr::Plugin(..) => return Some(()),
Repr::Native(..) | Repr::Element(..) => return None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tests::*;
#[test]
fn prepare() {
snapshot_testing("rename", &|ctx, path| {
let source = ctx.source_by_path(&path).unwrap();
let request = PrepareRenameRequest {
path: path.clone(),
position: find_test_position(&source),
};
let snap = WorldComputeGraph::from_world(ctx.world.clone());
let result = request.request(ctx, snap);
assert_snapshot!(JsonRepr::new_redacted(result, &REDACT_LOC));
});
}
}