1515 cast ,
1616)
1717
18- # from ....utils.async_itertools import async_next
18+ from ....utils .async_itertools import async_next
1919from ....utils .async_tools import threaded
2020from ....utils .logging import LoggingDescriptor
21-
22- # from ....utils.uri import Uri
2321from ...common .decorators import language_id
2422from ...common .lsp_types import (
2523 AnnotatedTextEdit ,
3634)
3735from ...common .parts .rename import CantRenameException
3836from ...common .text_document import TextDocument
39-
40- # from ..diagnostics.entities import VariableDefinition
37+ from ..diagnostics .entities import VariableDefinition , VariableDefinitionType
4138from ..diagnostics .library_doc import KeywordDoc
42- from ..utils .ast_utils import ( # HasTokens,; Statement,; get_tokens_at_position,
39+ from ..utils .ast_utils import (
40+ HasTokens ,
41+ Statement ,
4342 Token ,
4443 get_nodes_at_position ,
44+ get_tokens_at_position ,
4545 range_from_token ,
4646)
4747
@@ -148,67 +148,119 @@ async def collect_prepare(
148148 async def _prepare_rename_default (
149149 self , nodes : List [ast .AST ], document : TextDocument , position : Position
150150 ) -> Optional [PrepareRenameResult ]:
151- pass
151+ result = await self ._find_default (nodes , document , position )
152+ if result is not None :
153+ var , token = result
154+
155+ if var .type == VariableDefinitionType .BUILTIN_VARIABLE :
156+ self .parent .window .show_message ("You cannot rename a builtin variable, only references are renamed." )
157+
158+ elif var .type == VariableDefinitionType .IMPORTED_VARIABLE :
159+ self .parent .window .show_message (
160+ "You are about to rename an imported variable. "
161+ "Only references are renamed and you have to rename the variable definition yourself."
162+ )
163+ elif var .type == VariableDefinitionType .COMMAND_LINE_VARIABLE :
164+ self .parent .window .show_message (
165+ "You are about to rename a variable defined at commandline. "
166+ "Only references are renamed and you have to rename the variable definition yourself."
167+ )
168+ elif var .type == VariableDefinitionType .ENVIRONMENT_VARIABLE :
169+ self .parent .window .show_message (
170+ "You are about to rename an environment variable. "
171+ "Only references are renamed and you have to rename the variable definition yourself."
172+ )
173+
174+ return PrepareRenameResultWithPlaceHolder (range_from_token (token ), token .value )
175+
176+ return None
152177
153178 async def _rename_default (
154179 self , nodes : List [ast .AST ], document : TextDocument , position : Position , new_name : str
155180 ) -> Optional [WorkspaceEdit ]:
156- # from robot.parsing.lexer.tokens import Token as RobotToken
157-
158- # namespace = await self.parent.documents_cache.get_namespace(document)
159- # if namespace is None:
160- # return None
161-
162- # if not nodes:
163- # return None
164-
165- # node = nodes[-1]
166-
167- # if not isinstance(node, HasTokens):
168- # return None
169-
170- # tokens = get_tokens_at_position(node, position)
171-
172- # token_and_var: Optional[Tuple[Token, VariableDefinition]] = None
173-
174- # for token in tokens:
175- # token_and_var = await async_next(
176- # (
177- # (var_token, var)
178- # async for var_token, var in self.iter_variables_from_token(token, namespace, nodes, position)
179- # if position in range_from_token(var_token)
180- # ),
181- # None,
182- # )
183-
184- # if (
185- # token_and_var is None
186- # and isinstance(node, Statement)
187- # and isinstance(node, self.get_expression_statement_types())
188- # and (token := node.get_token(RobotToken.ARGUMENT)) is not None
189- # and position in range_from_token(token)
190- # ):
191- # token_and_var = await async_next(
192- # (
193- # (var_token, var)
194- # async for var_token, var in self.iter_expression_variables_from_token(
195- # token, namespace, nodes, position
196- # )
197- # if position in range_from_token(var_token)
198- # ),
199- # None,
200- # )
201-
202- # if token_and_var is not None:
203- # _, variable = token_and_var
204-
205- # return [
206- # DocumentHighlight(e.range, DocumentHighlightKind.TEXT)
207- # for e in await self.parent.robot_references.find_variable_references_in_file(document, variable)
208- # ]
181+ result = await self ._find_default (nodes , document , position )
182+
183+ if result is not None :
184+ var , _ = result
185+
186+ references = await self .parent .robot_references .find_variable_references (
187+ document ,
188+ var ,
189+ include_declaration = var .type
190+ in [
191+ VariableDefinitionType .VARIABLE ,
192+ VariableDefinitionType .ARGUMENT ,
193+ VariableDefinitionType .LOCAL_VARIABLE ,
194+ ],
195+ )
196+ changes : List [Union [TextDocumentEdit , CreateFile , RenameFile , DeleteFile ]] = []
197+
198+ for reference in references :
199+ changes .append (
200+ TextDocumentEdit (
201+ OptionalVersionedTextDocumentIdentifier (reference .uri , None ),
202+ [AnnotatedTextEdit (reference .range , new_name , annotation_id = "rename_variable" )],
203+ )
204+ )
205+
206+ return WorkspaceEdit (
207+ document_changes = changes ,
208+ change_annotations = {"rename_variable" : ChangeAnnotation ("Rename Variable" , False )},
209+ )
209210
210211 return None
211212
213+ async def _find_default (
214+ self , nodes : List [ast .AST ], document : TextDocument , position : Position
215+ ) -> Optional [Tuple [VariableDefinition , Token ]]:
216+ from robot .parsing .lexer .tokens import Token as RobotToken
217+
218+ namespace = await self .parent .documents_cache .get_namespace (document )
219+ if namespace is None :
220+ return None
221+
222+ if not nodes :
223+ return None
224+
225+ node = nodes [- 1 ]
226+
227+ if not isinstance (node , HasTokens ):
228+ return None
229+
230+ tokens = get_tokens_at_position (node , position )
231+
232+ token_and_var : Optional [Tuple [VariableDefinition , Token ]] = None
233+
234+ for token in tokens :
235+ token_and_var = await async_next (
236+ (
237+ (var , var_token )
238+ async for var_token , var in self .iter_variables_from_token (token , namespace , nodes , position )
239+ if position in range_from_token (var_token )
240+ ),
241+ None ,
242+ )
243+
244+ if (
245+ token_and_var is None
246+ and isinstance (node , Statement )
247+ and isinstance (node , self .get_expression_statement_types ())
248+ and (token := node .get_token (RobotToken .ARGUMENT )) is not None
249+ and position in range_from_token (token )
250+ ):
251+ token_and_var = await async_next (
252+ (
253+ (var , var_token )
254+ async for var_token , var in self .iter_expression_variables_from_token (
255+ token , namespace , nodes , position
256+ )
257+ if position in range_from_token (var_token )
258+ ),
259+ None ,
260+ )
261+
262+ return token_and_var
263+
212264 def _prepare_rename_keyword (self , result : Optional [Tuple [KeywordDoc , Token ]]) -> Optional [PrepareRenameResult ]:
213265 if result is not None :
214266 kw_doc , token = result
@@ -230,7 +282,7 @@ async def _rename_keyword(
230282 self , document : TextDocument , new_name : str , result : Optional [Tuple [KeywordDoc , Token ]]
231283 ) -> Optional [WorkspaceEdit ]:
232284 if result is not None :
233- kw_doc , token = result
285+ kw_doc , _ = result
234286
235287 references = await self .parent .robot_references .find_keyword_references (
236288 document , kw_doc , include_declaration = kw_doc .is_resource_keyword
@@ -241,12 +293,13 @@ async def _rename_keyword(
241293 changes .append (
242294 TextDocumentEdit (
243295 OptionalVersionedTextDocumentIdentifier (reference .uri , None ),
244- [AnnotatedTextEdit (reference .range , new_name , annotation_id = "a " )],
296+ [AnnotatedTextEdit (reference .range , new_name , annotation_id = "rename_keyword " )],
245297 )
246298 )
247299
248300 return WorkspaceEdit (
249- document_changes = changes , change_annotations = {"a" : ChangeAnnotation ("refactor" , False , "replace call" )}
301+ document_changes = changes ,
302+ change_annotations = {"rename_keyword" : ChangeAnnotation ("Rename Keyword" , False )},
250303 )
251304
252305 return None
@@ -404,7 +457,7 @@ async def _find_Fixture( # noqa: N802
404457 r .end .character = r .start .character + len (kw_namespace )
405458 kw_range .start .character = r .end .character + 1
406459 if position in r :
407- # TODO highlight namespaces
460+ # TODO namespaces
408461 return None
409462
410463 if position in kw_range and keyword_doc is not None and not keyword_doc .is_error_handler :
@@ -456,7 +509,7 @@ async def _find_Template_or_TestTemplate( # noqa: N802
456509 r .end .character = r .start .character + len (kw_namespace )
457510 kw_range .start .character = r .end .character + 1
458511 if position in r :
459- # TODO highlight namespaces
512+ # TODO namespaces
460513 return None
461514
462515 if not keyword_doc .is_error_handler :
0 commit comments