@@ -9,10 +9,13 @@ use crate::{assists::Command, SnippetCap};
9
9
use base_db:: AnchoredPathBuf ;
10
10
use itertools:: Itertools ;
11
11
use nohash_hasher:: IntMap ;
12
+ use rustc_hash:: FxHashMap ;
12
13
use span:: FileId ;
13
14
use stdx:: never;
14
15
use syntax:: {
15
- algo, AstNode , SyntaxElement , SyntaxNode , SyntaxNodePtr , SyntaxToken , TextRange , TextSize ,
16
+ algo,
17
+ syntax_editor:: { SyntaxAnnotation , SyntaxEditor } ,
18
+ AstNode , SyntaxElement , SyntaxNode , SyntaxNodePtr , SyntaxToken , TextRange , TextSize ,
16
19
} ;
17
20
use text_edit:: { TextEdit , TextEditBuilder } ;
18
21
@@ -197,6 +200,11 @@ pub struct SourceChangeBuilder {
197
200
pub source_change : SourceChange ,
198
201
pub command : Option < Command > ,
199
202
203
+ /// Keeps track of all edits performed on each file
204
+ pub file_editors : FxHashMap < FileId , SyntaxEditor > ,
205
+ /// Keeps track of which annotations correspond to which snippets
206
+ pub snippet_annotations : Vec < ( AnnotationSnippet , SyntaxAnnotation ) > ,
207
+
200
208
/// Maps the original, immutable `SyntaxNode` to a `clone_for_update` twin.
201
209
pub mutated_tree : Option < TreeMutator > ,
202
210
/// Keeps track of where to place snippets
@@ -238,6 +246,8 @@ impl SourceChangeBuilder {
238
246
file_id : file_id. into ( ) ,
239
247
source_change : SourceChange :: default ( ) ,
240
248
command : None ,
249
+ file_editors : FxHashMap :: default ( ) ,
250
+ snippet_annotations : vec ! [ ] ,
241
251
mutated_tree : None ,
242
252
snippet_builder : None ,
243
253
}
@@ -248,7 +258,75 @@ impl SourceChangeBuilder {
248
258
self . file_id = file_id. into ( ) ;
249
259
}
250
260
261
+ pub fn make_editor ( & self , node : & SyntaxNode ) -> SyntaxEditor {
262
+ SyntaxEditor :: new ( node. ancestors ( ) . last ( ) . unwrap_or_else ( || node. clone ( ) ) )
263
+ }
264
+
265
+ pub fn add_file_edits ( & mut self , file_id : impl Into < FileId > , edit : SyntaxEditor ) {
266
+ match self . file_editors . entry ( file_id. into ( ) ) {
267
+ Entry :: Occupied ( mut entry) => entry. get_mut ( ) . merge ( edit) ,
268
+ Entry :: Vacant ( entry) => {
269
+ entry. insert ( edit) ;
270
+ }
271
+ }
272
+ }
273
+
274
+ pub fn make_placeholder_snippet ( & mut self , _cap : SnippetCap ) -> SyntaxAnnotation {
275
+ self . add_snippet_annotation ( AnnotationSnippet :: Over )
276
+ }
277
+
278
+ pub fn make_tabstop_before ( & mut self , _cap : SnippetCap ) -> SyntaxAnnotation {
279
+ self . add_snippet_annotation ( AnnotationSnippet :: Before )
280
+ }
281
+
282
+ pub fn make_tabstop_after ( & mut self , _cap : SnippetCap ) -> SyntaxAnnotation {
283
+ self . add_snippet_annotation ( AnnotationSnippet :: After )
284
+ }
285
+
251
286
fn commit ( & mut self ) {
287
+ // Apply syntax editor edits
288
+ for ( file_id, editor) in mem:: take ( & mut self . file_editors ) {
289
+ let edit_result = editor. finish ( ) ;
290
+ let mut snippet_edit = vec ! [ ] ;
291
+
292
+ // Find snippet edits
293
+ for ( kind, annotation) in & self . snippet_annotations {
294
+ let elements = edit_result. find_annotation ( * annotation) ;
295
+
296
+ let snippet = match ( kind, elements) {
297
+ ( AnnotationSnippet :: Before , [ element] ) => {
298
+ Snippet :: Tabstop ( element. text_range ( ) . start ( ) )
299
+ }
300
+ ( AnnotationSnippet :: After , [ element] ) => {
301
+ Snippet :: Tabstop ( element. text_range ( ) . end ( ) )
302
+ }
303
+ ( AnnotationSnippet :: Over , [ element] ) => {
304
+ Snippet :: Placeholder ( element. text_range ( ) )
305
+ }
306
+ ( AnnotationSnippet :: Over , elements) if !elements. is_empty ( ) => {
307
+ Snippet :: PlaceholderGroup (
308
+ elements. iter ( ) . map ( |it| it. text_range ( ) ) . collect ( ) ,
309
+ )
310
+ }
311
+ _ => continue ,
312
+ } ;
313
+
314
+ snippet_edit. push ( snippet) ;
315
+ }
316
+
317
+ let mut edit = TextEdit :: builder ( ) ;
318
+ algo:: diff ( edit_result. old_root ( ) , edit_result. new_root ( ) ) . into_text_edit ( & mut edit) ;
319
+ let edit = edit. finish ( ) ;
320
+
321
+ let snippet_edit =
322
+ if !snippet_edit. is_empty ( ) { Some ( SnippetEdit :: new ( snippet_edit) ) } else { None } ;
323
+
324
+ if !edit. is_empty ( ) || snippet_edit. is_some ( ) {
325
+ self . source_change . insert_source_and_snippet_edit ( file_id, edit, snippet_edit) ;
326
+ }
327
+ }
328
+
329
+ // Apply mutable edits
252
330
let snippet_edit = self . snippet_builder . take ( ) . map ( |builder| {
253
331
SnippetEdit :: new (
254
332
builder. places . into_iter ( ) . flat_map ( PlaceSnippet :: finalize_position) . collect ( ) ,
@@ -369,6 +447,13 @@ impl SourceChangeBuilder {
369
447
self . source_change . is_snippet = true ;
370
448
}
371
449
450
+ fn add_snippet_annotation ( & mut self , kind : AnnotationSnippet ) -> SyntaxAnnotation {
451
+ let annotation = SyntaxAnnotation :: new ( ) ;
452
+ self . snippet_annotations . push ( ( kind, annotation) ) ;
453
+ self . source_change . is_snippet = true ;
454
+ annotation
455
+ }
456
+
372
457
pub fn finish ( mut self ) -> SourceChange {
373
458
self . commit ( ) ;
374
459
@@ -416,6 +501,15 @@ pub enum Snippet {
416
501
PlaceholderGroup ( Vec < TextRange > ) ,
417
502
}
418
503
504
+ pub enum AnnotationSnippet {
505
+ /// Place a tabstop before an element
506
+ Before ,
507
+ /// Place a tabstop before an element
508
+ After ,
509
+ /// Place a placeholder snippet in place of the element(s)
510
+ Over ,
511
+ }
512
+
419
513
enum PlaceSnippet {
420
514
/// Place a tabstop before an element
421
515
Before ( SyntaxElement ) ,
0 commit comments