@@ -6,6 +6,7 @@ use oak_db::Db;
66use oak_db:: Definition ;
77use oak_db:: File ;
88use oak_db:: Identifier ;
9+ use oak_db:: Name ;
910use oak_db:: RootKind ;
1011
1112use crate :: find_references;
@@ -22,22 +23,28 @@ pub struct RenameTargets {
2223}
2324
2425/// Identify the renamable identifier at `offset`, returning its range and
25- /// current (unquoted) name. Returns `None` when the cursor is on a
26- /// non-identifier, a `pkg::sym` namespace access, or a `$`/`@` member name.
27- pub fn prepare_rename ( db : & dyn Db , file : File , offset : TextSize ) -> Option < ( TextRange , String ) > {
28- match Identifier :: classify ( db, file, offset) ? {
29- Identifier :: Variable { name, range } => Some ( ( range, name. text ( db) . to_string ( ) ) ) ,
30- Identifier :: Member { .. } => None ,
31- }
26+ /// current (unquoted) name.
27+ ///
28+ /// Returns `Ok(None)` when the cursor isn't on something we can rename (a
29+ /// non-identifier, a `pkg::sym` namespace access, or a `$`/`@` member name),
30+ /// so the client simply offers no rename. Returns `Err` when the cursor is on
31+ /// a renamable identifier that we still refuse, today only a symbol defined in
32+ /// an installed package, so the client can surface why at prepare time.
33+ pub fn prepare_rename (
34+ db : & dyn Db ,
35+ file : File ,
36+ offset : TextSize ,
37+ ) -> anyhow:: Result < Option < ( TextRange , String ) > > {
38+ Ok ( renamable_at ( db, file, offset) ?. map ( |( range, name) | ( range, name. text ( db) . to_string ( ) ) ) )
3239}
3340
3441/// Compute all rename edits across the database.
3542///
3643/// Returns `Err` when:
3744/// - `new_name` is empty, is an R reserved word, or contains a literal
3845/// backtick (which can't appear in a backtick-quoted identifier).
39- /// - The cursor isn't on a renamable identifier (no `prepare_rename()` target).
40- /// - Any target definition lives in an installed package .
46+ /// - The cursor isn't on a renamable identifier, or it resolves to an
47+ /// installed package (both via `renamable_at`) .
4148/// - Nothing in the database binds the cursor's symbol. Rename would
4249/// produce no edits, so we refuse rather than silently succeed.
4350pub fn rename (
@@ -48,19 +55,10 @@ pub fn rename(
4855) -> anyhow:: Result < RenameTargets > {
4956 let new_text = to_identifier_text ( new_name) ?;
5057
51- let ident = Identifier :: classify ( db, file, offset) ;
52- let Some ( Identifier :: Variable { range, .. } ) = & ident else {
58+ let Some ( _) = renamable_at ( db, file, offset) ? else {
5359 return Err ( anyhow ! ( "Can't rename identifier at cursor." ) ) ;
5460 } ;
5561
56- for def in file. resolve_at ( db, range. start ( ) ) {
57- if is_library_def ( db, def) {
58- return Err ( anyhow ! (
59- "Can't rename: symbol is defined in an installed package."
60- ) ) ;
61- }
62- }
63-
6462 let ranges = find_references ( db, file, offset, true ) ;
6563 if ranges. is_empty ( ) {
6664 return Err ( anyhow ! (
@@ -71,6 +69,26 @@ pub fn rename(
7169 Ok ( RenameTargets { ranges, new_text } )
7270}
7371
72+ fn renamable_at < ' db > (
73+ db : & ' db dyn Db ,
74+ file : File ,
75+ offset : TextSize ,
76+ ) -> anyhow:: Result < Option < ( TextRange , Name < ' db > ) > > {
77+ let Some ( Identifier :: Variable { name, range } ) = Identifier :: classify ( db, file, offset) else {
78+ return Ok ( None ) ;
79+ } ;
80+
81+ for def in file. resolve_at ( db, range. start ( ) ) {
82+ if is_library_def ( db, def) {
83+ return Err ( anyhow ! (
84+ "Can't rename: symbol is defined in an installed package."
85+ ) ) ;
86+ }
87+ }
88+
89+ Ok ( Some ( ( range, name) ) )
90+ }
91+
7492fn is_library_def ( db : & dyn Db , def : Definition ) -> bool {
7593 def. file ( db)
7694 . root ( db)
0 commit comments