1
1
//! See [`AssistContext`].
2
2
3
- use std:: mem;
4
-
5
3
use 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 } ;
15
6
use syntax:: {
16
7
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 ,
19
10
} ;
20
- use text_edit:: { TextEdit , TextEditBuilder } ;
21
11
22
12
use crate :: {
23
13
assist_config:: AssistConfig , Assist , AssistId , AssistKind , AssistResolveStrategy , GroupLabel ,
24
14
} ;
25
15
16
+ pub ( crate ) use ide_db:: source_change:: { SourceChangeBuilder , TreeMutator } ;
17
+
26
18
/// `AssistContext` allows to apply an assist or check if it could be applied.
27
19
///
28
20
/// Assists use a somewhat over-engineered approach, given the current needs.
@@ -163,7 +155,7 @@ impl Assists {
163
155
id : AssistId ,
164
156
label : impl Into < String > ,
165
157
target : TextRange ,
166
- f : impl FnOnce ( & mut AssistBuilder ) ,
158
+ f : impl FnOnce ( & mut SourceChangeBuilder ) ,
167
159
) -> Option < ( ) > {
168
160
let mut f = Some ( f) ;
169
161
self . add_impl ( None , id, label. into ( ) , target, & mut |it| f. take ( ) . unwrap ( ) ( it) )
@@ -175,7 +167,7 @@ impl Assists {
175
167
id : AssistId ,
176
168
label : impl Into < String > ,
177
169
target : TextRange ,
178
- f : impl FnOnce ( & mut AssistBuilder ) ,
170
+ f : impl FnOnce ( & mut SourceChangeBuilder ) ,
179
171
) -> Option < ( ) > {
180
172
let mut f = Some ( f) ;
181
173
self . add_impl ( Some ( group) , id, label. into ( ) , target, & mut |it| f. take ( ) . unwrap ( ) ( it) )
@@ -187,15 +179,15 @@ impl Assists {
187
179
id : AssistId ,
188
180
label : String ,
189
181
target : TextRange ,
190
- f : & mut dyn FnMut ( & mut AssistBuilder ) ,
182
+ f : & mut dyn FnMut ( & mut SourceChangeBuilder ) ,
191
183
) -> Option < ( ) > {
192
184
if !self . is_allowed ( & id) {
193
185
return None ;
194
186
}
195
187
196
188
let mut trigger_signature_help = false ;
197
189
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 ) ;
199
191
f ( & mut builder) ;
200
192
trigger_signature_help = builder. trigger_signature_help ;
201
193
Some ( builder. finish ( ) )
@@ -216,132 +208,3 @@ impl Assists {
216
208
}
217
209
}
218
210
}
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