11//! See [`AssistContext`].
22
3- use std:: mem;
4-
53use hir:: Semantics ;
6- use ide_db:: {
7- base_db:: { AnchoredPathBuf , FileId , FileRange } ,
8- SnippetCap ,
9- } ;
10- use ide_db:: {
11- label:: Label ,
12- source_change:: { FileSystemEdit , SourceChange } ,
13- RootDatabase ,
14- } ;
4+ use ide_db:: base_db:: { FileId , FileRange } ;
5+ use ide_db:: { label:: Label , RootDatabase } ;
156use syntax:: {
167 algo:: { self , find_node_at_offset, find_node_at_range} ,
17- AstNode , AstToken , Direction , SourceFile , SyntaxElement , SyntaxKind , SyntaxNode , SyntaxNodePtr ,
18- SyntaxToken , TextRange , TextSize , TokenAtOffset ,
8+ AstNode , AstToken , Direction , SourceFile , SyntaxElement , SyntaxKind , SyntaxToken , TextRange ,
9+ TextSize , TokenAtOffset ,
1910} ;
20- use text_edit:: { TextEdit , TextEditBuilder } ;
2111
2212use crate :: {
2313 assist_config:: AssistConfig , Assist , AssistId , AssistKind , AssistResolveStrategy , GroupLabel ,
2414} ;
2515
16+ pub ( crate ) use ide_db:: source_change:: { SourceChangeBuilder , TreeMutator } ;
17+
2618/// `AssistContext` allows to apply an assist or check if it could be applied.
2719///
2820/// Assists use a somewhat over-engineered approach, given the current needs.
@@ -163,7 +155,7 @@ impl Assists {
163155 id : AssistId ,
164156 label : impl Into < String > ,
165157 target : TextRange ,
166- f : impl FnOnce ( & mut AssistBuilder ) ,
158+ f : impl FnOnce ( & mut SourceChangeBuilder ) ,
167159 ) -> Option < ( ) > {
168160 let mut f = Some ( f) ;
169161 self . add_impl ( None , id, label. into ( ) , target, & mut |it| f. take ( ) . unwrap ( ) ( it) )
@@ -175,7 +167,7 @@ impl Assists {
175167 id : AssistId ,
176168 label : impl Into < String > ,
177169 target : TextRange ,
178- f : impl FnOnce ( & mut AssistBuilder ) ,
170+ f : impl FnOnce ( & mut SourceChangeBuilder ) ,
179171 ) -> Option < ( ) > {
180172 let mut f = Some ( f) ;
181173 self . add_impl ( Some ( group) , id, label. into ( ) , target, & mut |it| f. take ( ) . unwrap ( ) ( it) )
@@ -187,15 +179,15 @@ impl Assists {
187179 id : AssistId ,
188180 label : String ,
189181 target : TextRange ,
190- f : & mut dyn FnMut ( & mut AssistBuilder ) ,
182+ f : & mut dyn FnMut ( & mut SourceChangeBuilder ) ,
191183 ) -> Option < ( ) > {
192184 if !self . is_allowed ( & id) {
193185 return None ;
194186 }
195187
196188 let mut trigger_signature_help = false ;
197189 let source_change = if self . resolve . should_resolve ( & id) {
198- let mut builder = AssistBuilder :: new ( self . file ) ;
190+ let mut builder = SourceChangeBuilder :: new ( self . file ) ;
199191 f ( & mut builder) ;
200192 trigger_signature_help = builder. trigger_signature_help ;
201193 Some ( builder. finish ( ) )
@@ -216,132 +208,3 @@ impl Assists {
216208 }
217209 }
218210}
219-
220- pub ( crate ) struct AssistBuilder {
221- edit : TextEditBuilder ,
222- file_id : FileId ,
223- source_change : SourceChange ,
224- trigger_signature_help : bool ,
225-
226- /// Maps the original, immutable `SyntaxNode` to a `clone_for_update` twin.
227- mutated_tree : Option < TreeMutator > ,
228- }
229-
230- pub ( crate ) struct TreeMutator {
231- immutable : SyntaxNode ,
232- mutable_clone : SyntaxNode ,
233- }
234-
235- impl TreeMutator {
236- pub ( crate ) fn new ( immutable : & SyntaxNode ) -> TreeMutator {
237- let immutable = immutable. ancestors ( ) . last ( ) . unwrap ( ) ;
238- let mutable_clone = immutable. clone_for_update ( ) ;
239- TreeMutator { immutable, mutable_clone }
240- }
241-
242- pub ( crate ) fn make_mut < N : AstNode > ( & self , node : & N ) -> N {
243- N :: cast ( self . make_syntax_mut ( node. syntax ( ) ) ) . unwrap ( )
244- }
245-
246- pub ( crate ) fn make_syntax_mut ( & self , node : & SyntaxNode ) -> SyntaxNode {
247- let ptr = SyntaxNodePtr :: new ( node) ;
248- ptr. to_node ( & self . mutable_clone )
249- }
250- }
251-
252- impl AssistBuilder {
253- pub ( crate ) fn new ( file_id : FileId ) -> AssistBuilder {
254- AssistBuilder {
255- edit : TextEdit :: builder ( ) ,
256- file_id,
257- source_change : SourceChange :: default ( ) ,
258- trigger_signature_help : false ,
259- mutated_tree : None ,
260- }
261- }
262-
263- pub ( crate ) fn edit_file ( & mut self , file_id : FileId ) {
264- self . commit ( ) ;
265- self . file_id = file_id;
266- }
267-
268- fn commit ( & mut self ) {
269- if let Some ( tm) = self . mutated_tree . take ( ) {
270- algo:: diff ( & tm. immutable , & tm. mutable_clone ) . into_text_edit ( & mut self . edit )
271- }
272-
273- let edit = mem:: take ( & mut self . edit ) . finish ( ) ;
274- if !edit. is_empty ( ) {
275- self . source_change . insert_source_edit ( self . file_id , edit) ;
276- }
277- }
278-
279- pub ( crate ) fn make_mut < N : AstNode > ( & mut self , node : N ) -> N {
280- self . mutated_tree . get_or_insert_with ( || TreeMutator :: new ( node. syntax ( ) ) ) . make_mut ( & node)
281- }
282- /// Returns a copy of the `node`, suitable for mutation.
283- ///
284- /// Syntax trees in rust-analyzer are typically immutable, and mutating
285- /// operations panic at runtime. However, it is possible to make a copy of
286- /// the tree and mutate the copy freely. Mutation is based on interior
287- /// mutability, and different nodes in the same tree see the same mutations.
288- ///
289- /// The typical pattern for an assist is to find specific nodes in the read
290- /// phase, and then get their mutable couterparts using `make_mut` in the
291- /// mutable state.
292- pub ( crate ) fn make_syntax_mut ( & mut self , node : SyntaxNode ) -> SyntaxNode {
293- self . mutated_tree . get_or_insert_with ( || TreeMutator :: new ( & node) ) . make_syntax_mut ( & node)
294- }
295-
296- /// Remove specified `range` of text.
297- pub ( crate ) fn delete ( & mut self , range : TextRange ) {
298- self . edit . delete ( range)
299- }
300- /// Append specified `text` at the given `offset`
301- pub ( crate ) fn insert ( & mut self , offset : TextSize , text : impl Into < String > ) {
302- self . edit . insert ( offset, text. into ( ) )
303- }
304- /// Append specified `snippet` at the given `offset`
305- pub ( crate ) fn insert_snippet (
306- & mut self ,
307- _cap : SnippetCap ,
308- offset : TextSize ,
309- snippet : impl Into < String > ,
310- ) {
311- self . source_change . is_snippet = true ;
312- self . insert ( offset, snippet) ;
313- }
314- /// Replaces specified `range` of text with a given string.
315- pub ( crate ) fn replace ( & mut self , range : TextRange , replace_with : impl Into < String > ) {
316- self . edit . replace ( range, replace_with. into ( ) )
317- }
318- /// Replaces specified `range` of text with a given `snippet`.
319- pub ( crate ) fn replace_snippet (
320- & mut self ,
321- _cap : SnippetCap ,
322- range : TextRange ,
323- snippet : impl Into < String > ,
324- ) {
325- self . source_change . is_snippet = true ;
326- self . replace ( range, snippet) ;
327- }
328- pub ( crate ) fn replace_ast < N : AstNode > ( & mut self , old : N , new : N ) {
329- algo:: diff ( old. syntax ( ) , new. syntax ( ) ) . into_text_edit ( & mut self . edit )
330- }
331- pub ( crate ) fn create_file ( & mut self , dst : AnchoredPathBuf , content : impl Into < String > ) {
332- let file_system_edit = FileSystemEdit :: CreateFile { dst, initial_contents : content. into ( ) } ;
333- self . source_change . push_file_system_edit ( file_system_edit) ;
334- }
335- pub ( crate ) fn move_file ( & mut self , src : FileId , dst : AnchoredPathBuf ) {
336- let file_system_edit = FileSystemEdit :: MoveFile { src, dst } ;
337- self . source_change . push_file_system_edit ( file_system_edit) ;
338- }
339- pub ( crate ) fn trigger_signature_help ( & mut self ) {
340- self . trigger_signature_help = true ;
341- }
342-
343- fn finish ( mut self ) -> SourceChange {
344- self . commit ( ) ;
345- mem:: take ( & mut self . source_change )
346- }
347- }
0 commit comments