11use {
22 crate :: {
3+ definition,
34 lsp_types:: { TextEdit , Url as Uri , WorkspaceEdit } ,
45 tree:: { self , field, kind} ,
56 utils,
67 } ,
78 ropey:: Rope ,
8- std:: collections:: HashMap ,
9+ std:: collections:: { HashMap , HashSet } ,
910 tree_sitter:: { Node , Point , Tree } ,
1011} ;
1112
13+ // 1. figure out if expression at position can be renamed (is identifier and not rhs of @ or $)
14+ // 2. find previous definition for identifier
15+ // 3. if identifier is global scope -> stop (for now, we would to rename in all files)
16+ // 4. find all references for identifier
17+
1218pub fn rename (
1319 uri : & Uri ,
1420 line : usize ,
@@ -19,60 +25,58 @@ pub fn rename(
1925) -> Option < WorkspaceEdit > {
2026 let start = try_get_identifier ( tree, line, col) ?;
2127 let parent = start. parent ( ) ?;
22- let current_name = rope. byte_slice ( start. byte_range ( ) ) . to_string ( ) ;
28+ let name = rope. byte_slice ( start. byte_range ( ) ) . to_string ( ) ;
2329
2430 tracing:: debug!(
25- ?current_name ,
31+ ?name ,
2632 ?new_name,
2733 start = start. kind( ) ,
2834 parent = parent. kind( ) ,
2935 "rename request"
3036 ) ;
3137
32- // Check if rename is allowed - don't allow renaming RHS of certain operators
3338 if !is_rename_allowed ( start, parent) {
3439 tracing:: debug!( "Rename not allowed for this symbol" ) ;
3540 return None ;
3641 }
3742
38- // Find all references to this symbol in the appropriate scope
39- let references = find_all_references ( start, rope, & current_name) ?;
40-
41- // Convert references to text edits
42- let mut edits = Vec :: new ( ) ;
43- for reference in references {
44- let range = utils:: node_range ( reference) ;
45- edits. push ( TextEdit {
46- range,
47- new_text : new_name. to_string ( ) ,
48- } ) ;
49- }
43+ let definition = definition:: find_previous_definition ( start, rope, & name) ?;
44+ let scope = find_scope_containing ( definition) ?; // for now we return if global scope
45+
46+ let mut references = Vec :: new ( ) ;
47+ find_references_in_scope ( scope, rope, & name, definition, & mut references) ;
48+
49+ let edits: Vec < _ > = references
50+ . into_iter ( )
51+ . filter_map ( {
52+ let mut seen = HashSet :: new ( ) ; // make sure we don't edit same node multiple times
53+ move |reference| {
54+ seen. insert ( reference. id ( ) ) . then ( || TextEdit {
55+ range : utils:: node_range ( reference) ,
56+ new_text : new_name. to_string ( ) ,
57+ } )
58+ }
59+ } )
60+ . collect ( ) ;
5061
5162 if edits. is_empty ( ) {
5263 return None ;
5364 }
5465
55- // Create workspace edit
56- let mut changes = HashMap :: new ( ) ;
57- changes. insert ( uri. clone ( ) , edits) ;
66+ tracing:: debug!( ?edits) ;
5867
5968 Some ( WorkspaceEdit {
60- changes : Some ( changes ) ,
69+ changes : Some ( HashMap :: from_iter ( [ ( uri . clone ( ) , edits ) ] ) ) ,
6170 ..Default :: default ( )
6271 } )
6372}
6473
6574fn is_rename_allowed ( symbol : Node , parent : Node ) -> bool {
6675 // Don't allow renaming RHS of extract @, extract $, or namespace :: operators
67- if [ kind:: EXTRACT_OPERATOR , kind:: NAMESPACE_OPERATOR ] . contains ( & parent. kind_id ( ) )
76+ ! ( [ kind:: EXTRACT_OPERATOR , kind:: NAMESPACE_OPERATOR ] . contains ( & parent. kind_id ( ) )
6877 && parent
6978 . child_by_field_id ( field:: RHS )
70- . is_some_and ( |rhs| rhs. id ( ) == symbol. id ( ) )
71- {
72- return false ;
73- }
74-
75- true
79+ . is_some_and ( |rhs| rhs. id ( ) == symbol. id ( ) ) )
7680}
7781
7882fn try_get_identifier < ' tree > ( tree : & ' tree Tree , line : usize , col : usize ) -> Option < Node < ' tree > > {
@@ -94,82 +98,9 @@ fn try_get_identifier<'tree>(tree: &'tree Tree, line: usize, col: usize) -> Opti
9498 ( start. kind_id ( ) == kind:: IDENTIFIER ) . then_some ( start)
9599}
96100
97- /// Find all references to a symbol within its scope
98- fn find_all_references < ' a > ( start : Node < ' a > , rope : & Rope , name : & str ) -> Option < Vec < Node < ' a > > > {
99- let mut references = Vec :: new ( ) ;
100-
101- // Find the definition of the symbol
102- let definition = find_definition ( start, rope, name) ?;
103-
104- // Find the scope containing the definition
105- let scope = find_scope_containing ( definition) ?;
106-
107- // Find all references within this scope
108- find_references_in_scope ( scope, rope, name, definition, & mut references) ;
109-
110- // Deduplicate references by node ID
111- let mut unique_refs = Vec :: new ( ) ;
112- let mut seen_ids = std:: collections:: HashSet :: new ( ) ;
113-
114- for reference in references {
115- if seen_ids. insert ( reference. id ( ) ) {
116- unique_refs. push ( reference) ;
117- }
118- }
119-
120- Some ( unique_refs)
121- }
122-
123- fn find_definition < ' a > ( start : Node < ' a > , rope : & Rope , name : & str ) -> Option < Node < ' a > > {
124- let mut node = start;
125- loop {
126- let is_descendent;
127- if let Some ( sibling) = node. prev_sibling ( ) {
128- node = sibling;
129- is_descendent = false ;
130- } else if let Some ( parent) = node. parent ( ) {
131- node = parent;
132- is_descendent = true ;
133- } else {
134- break None ;
135- }
136-
137- let maybe_definition = match node. kind_id ( ) {
138- kind:: PARAMETERS => node
139- . children_by_field_name ( "parameter" , & mut node. walk ( ) )
140- . filter_map ( |parameter| parameter. child_by_field_id ( field:: NAME ) )
141- . find ( |child| rope. byte_slice ( child. byte_range ( ) ) == name) ,
142- kind:: BINARY_OPERATOR => {
143- let maybe_lhs = node. child_by_field_id ( field:: LHS ) ;
144- let maybe_op = node. child_by_field_id ( field:: OPERATOR ) ;
145- maybe_lhs. filter ( |lhs| {
146- lhs. kind_id ( ) == kind:: IDENTIFIER
147- && maybe_op. is_some_and ( |op| {
148- [ kind:: EQUAL , kind:: LEFT_ASSIGN ] . contains ( & op. kind_id ( ) )
149- } )
150- && rope. byte_slice ( lhs. byte_range ( ) ) == name
151- && ( !is_descendent || lhs. id ( ) == start. id ( ) )
152- } )
153- }
154- _ => None ,
155- } ;
156-
157- if maybe_definition. is_some ( ) {
158- return maybe_definition;
159- }
160- }
161- }
162-
163- fn find_scope_containing < ' a > ( node : Node < ' a > ) -> Option < Node < ' a > > {
164- let mut current = Some ( node) ;
165- while let Some ( node) = current {
166- match node. kind_id ( ) {
167- kind:: FUNCTION_DEFINITION => return Some ( node) ,
168- kind:: PROGRAM => return Some ( node) ,
169- _ => current = node. parent ( ) ,
170- }
171- }
172- None
101+ fn find_scope_containing ( node : Node ) -> Option < Node > {
102+ std:: iter:: successors ( Some ( node) , |node| node. parent ( ) )
103+ . find ( |node| node. kind_id ( ) == kind:: FUNCTION_DEFINITION )
173104}
174105
175106fn find_references_in_scope < ' a > (
@@ -186,47 +117,41 @@ fn find_references_in_scope<'a>(
186117 // If this is an identifier with matching name, check if it refers to our definition
187118 if node. kind_id ( ) == kind:: IDENTIFIER
188119 && rope. byte_slice ( node. byte_range ( ) ) == name
189- && is_reference_to_symbol ( node, rope, name)
120+ // todo consider moving this check up
121+ && ( node
122+ . parent ( )
123+ . is_none_or ( |parent| is_rename_allowed ( node, parent) ) )
190124 && refers_to_definition ( node, definition, rope, name)
191125 {
192126 references. push ( node) ;
193127 }
194128
195- // Add children to queue for processing
196- let mut child_cursor = node. walk ( ) ;
197- if child_cursor. goto_first_child ( ) {
129+ let mut cursor = node. walk ( ) ;
130+ if cursor. goto_first_child ( ) {
198131 loop {
199- let child = child_cursor . node ( ) ;
132+ let child = cursor . node ( ) ;
200133
201134 // We need to traverse nested functions too for free variables
202135 // But we need to be careful about scope - we'll let the definition
203136 // finding logic handle whether a reference is valid
204137 queue. push ( child) ;
205138
206- if !child_cursor . goto_next_sibling ( ) {
139+ if !cursor . goto_next_sibling ( ) {
207140 break ;
208141 }
209142 }
210143 }
211144 }
212145}
213146
214- fn is_reference_to_symbol ( node : Node , rope : & Rope , name : & str ) -> bool {
215- node. kind_id ( ) == kind:: IDENTIFIER
216- && rope. byte_slice ( node. byte_range ( ) ) == name
217- && ( node
218- . parent ( )
219- . is_none_or ( |parent| is_rename_allowed ( node, parent) ) )
220- }
221-
222147fn refers_to_definition ( reference : Node , definition : Node , rope : & Rope , name : & str ) -> bool {
223148 // If this is the definition itself, include it
224149 if reference. id ( ) == definition. id ( ) {
225150 return true ;
226151 }
227152
228153 // Use the same logic as find_definition to see if this reference would resolve to our definition
229- let found_definition = find_definition ( reference, rope, name) ;
154+ let found_definition = definition :: find_previous_definition ( reference, rope, name) ;
230155 match found_definition {
231156 Some ( found_def) => found_def. id ( ) == definition. id ( ) ,
232157 None => false ,
@@ -239,21 +164,41 @@ mod tests {
239164
240165 fn setup ( src : & str , line : usize , col : usize , new_name : & str ) -> Option < WorkspaceEdit > {
241166 let rope = Rope :: from_str ( src) ;
242- let mut parser = tree:: new_parser ( ) ;
243- let tree = tree:: parse ( & mut parser, src, None ) ;
167+ let tree = tree:: parse ( & mut tree:: new_parser ( ) , src, None ) ;
244168 let uri = Uri :: parse ( "file:///test.R" ) . unwrap ( ) ;
245169 rename ( & uri, line, col, new_name, & rope, & tree)
246170 }
247171
172+ // #[test]
173+ // fn global_variable_rename() {
174+ // let src = indoc! {r#"
175+ // x <- 1
176+ // y <- x + 2
177+ // x
178+ // "#};
179+
180+ // let result = setup(src, 0, 0, "new_var").unwrap();
181+ // let changes = result.changes.unwrap();
182+ // let uri = Uri::parse("file:///test.R").unwrap();
183+ // let edits = &changes[&uri];
184+
185+ // assert_eq!(edits.len(), 3); // Should find 3 references
186+ // assert_eq!(edits[0].new_text, "new_var");
187+ // assert_eq!(edits[1].new_text, "new_var");
188+ // assert_eq!(edits[2].new_text, "new_var");
189+ // }
190+
248191 #[ test]
249- fn basic_variable_rename ( ) {
192+ fn local_variable_rename ( ) {
250193 let src = indoc ! { r#"
251- x <- 1
252- y <- x + 2
253- x
194+ function() {
195+ x <- 1
196+ y <- x + 2
197+ x
198+ }
254199 "# } ;
255200
256- let result = setup ( src, 0 , 0 , "new_var" ) . unwrap ( ) ;
201+ let result = setup ( src, 2 , 9 , "new_var" ) . unwrap ( ) ;
257202 let changes = result. changes . unwrap ( ) ;
258203 let uri = Uri :: parse ( "file:///test.R" ) . unwrap ( ) ;
259204 let edits = & changes[ & uri] ;
@@ -263,7 +208,6 @@ mod tests {
263208 assert_eq ! ( edits[ 1 ] . new_text, "new_var" ) ;
264209 assert_eq ! ( edits[ 2 ] . new_text, "new_var" ) ;
265210 }
266-
267211 #[ test]
268212 fn parameter_rename ( ) {
269213 let src = indoc ! { r#"
@@ -367,17 +311,19 @@ mod tests {
367311 }
368312
369313 #[ test]
370- fn shadowing_variables ( ) {
314+ fn shadowing_local_variables ( ) {
371315 let src = indoc ! { r#"
372- x <- 1
373316 function() {
374- x <- 2
375- x
317+ x <- 1
318+ function() {
319+ x <- 2
320+ x
321+ }
376322 }
377323 "# } ;
378324
379325 // Rename outer x - should only affect outer scope
380- let result = setup ( src, 0 , 0 , "outer_x" ) . unwrap ( ) ;
326+ let result = setup ( src, 1 , 4 , "outer_x" ) . unwrap ( ) ;
381327 let changes = result. changes . unwrap ( ) ;
382328 let uri = Uri :: parse ( "file:///test.R" ) . unwrap ( ) ;
383329 let edits = & changes[ & uri] ;
@@ -408,7 +354,7 @@ mod tests {
408354 fn variable_in_control_structures ( ) {
409355 let src = indoc ! { r#"
410356 x <- 1
411- if (TRUE ) {
357+ if (condition ) {
412358 x <- 2
413359 x
414360 }
0 commit comments