@@ -9,10 +9,13 @@ use crate::{assists::Command, SnippetCap};
99use base_db:: AnchoredPathBuf ;
1010use itertools:: Itertools ;
1111use nohash_hasher:: IntMap ;
12+ use rustc_hash:: FxHashMap ;
1213use span:: FileId ;
1314use stdx:: never;
1415use 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 ,
1619} ;
1720use text_edit:: { TextEdit , TextEditBuilder } ;
1821
@@ -197,6 +200,11 @@ pub struct SourceChangeBuilder {
197200 pub source_change : SourceChange ,
198201 pub command : Option < Command > ,
199202
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+
200208 /// Maps the original, immutable `SyntaxNode` to a `clone_for_update` twin.
201209 pub mutated_tree : Option < TreeMutator > ,
202210 /// Keeps track of where to place snippets
@@ -238,6 +246,8 @@ impl SourceChangeBuilder {
238246 file_id : file_id. into ( ) ,
239247 source_change : SourceChange :: default ( ) ,
240248 command : None ,
249+ file_editors : FxHashMap :: default ( ) ,
250+ snippet_annotations : vec ! [ ] ,
241251 mutated_tree : None ,
242252 snippet_builder : None ,
243253 }
@@ -248,7 +258,75 @@ impl SourceChangeBuilder {
248258 self . file_id = file_id. into ( ) ;
249259 }
250260
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+
251286 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
252330 let snippet_edit = self . snippet_builder . take ( ) . map ( |builder| {
253331 SnippetEdit :: new (
254332 builder. places . into_iter ( ) . flat_map ( PlaceSnippet :: finalize_position) . collect ( ) ,
@@ -369,6 +447,13 @@ impl SourceChangeBuilder {
369447 self . source_change . is_snippet = true ;
370448 }
371449
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+
372457 pub fn finish ( mut self ) -> SourceChange {
373458 self . commit ( ) ;
374459
@@ -416,6 +501,15 @@ pub enum Snippet {
416501 PlaceholderGroup ( Vec < TextRange > ) ,
417502}
418503
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+
419513enum PlaceSnippet {
420514 /// Place a tabstop before an element
421515 Before ( SyntaxElement ) ,
0 commit comments