11use std:: iter:: { self , successors} ;
22
33use algo:: skip_trivia_token;
4- use ast:: { edit:: AstNodeEdit , PathSegmentKind , VisibilityOwner } ;
5- use either:: Either ;
4+ use ast:: {
5+ edit:: { AstNodeEdit , IndentLevel } ,
6+ PathSegmentKind , VisibilityOwner ,
7+ } ;
68use syntax:: {
79 algo,
810 ast:: { self , make, AstNode } ,
@@ -11,64 +13,129 @@ use syntax::{
1113
1214use test_utils:: mark;
1315
14- /// Determines the containing syntax node in which to insert a `use` statement affecting `position`.
15- pub ( crate ) fn find_insert_use_container (
16- position : & SyntaxNode ,
17- ctx : & crate :: assist_context:: AssistContext ,
18- ) -> Option < Either < ast:: ItemList , ast:: SourceFile > > {
19- ctx. sema . ancestors_with_macros ( position. clone ( ) ) . find_map ( |n| {
20- if let Some ( module) = ast:: Module :: cast ( n. clone ( ) ) {
21- module. item_list ( ) . map ( Either :: Left )
16+ #[ derive( Debug ) ]
17+ pub enum ImportScope {
18+ File ( ast:: SourceFile ) ,
19+ Module ( ast:: ItemList ) ,
20+ }
21+
22+ impl ImportScope {
23+ pub fn from ( syntax : SyntaxNode ) -> Option < Self > {
24+ if let Some ( module) = ast:: Module :: cast ( syntax. clone ( ) ) {
25+ module. item_list ( ) . map ( ImportScope :: Module )
26+ } else if let this @ Some ( _) = ast:: SourceFile :: cast ( syntax. clone ( ) ) {
27+ this. map ( ImportScope :: File )
2228 } else {
23- Some ( Either :: Right ( ast:: SourceFile :: cast ( n ) ? ) )
29+ ast:: ItemList :: cast ( syntax ) . map ( ImportScope :: Module )
2430 }
25- } )
31+ }
32+
33+ /// Determines the containing syntax node in which to insert a `use` statement affecting `position`.
34+ pub ( crate ) fn find_insert_use_container (
35+ position : & SyntaxNode ,
36+ ctx : & crate :: assist_context:: AssistContext ,
37+ ) -> Option < Self > {
38+ ctx. sema . ancestors_with_macros ( position. clone ( ) ) . find_map ( Self :: from)
39+ }
40+
41+ pub ( crate ) fn as_syntax_node ( & self ) -> & SyntaxNode {
42+ match self {
43+ ImportScope :: File ( file) => file. syntax ( ) ,
44+ ImportScope :: Module ( item_list) => item_list. syntax ( ) ,
45+ }
46+ }
47+
48+ fn indent_level ( & self ) -> IndentLevel {
49+ match self {
50+ ImportScope :: File ( file) => file. indent_level ( ) ,
51+ ImportScope :: Module ( item_list) => item_list. indent_level ( ) + 1 ,
52+ }
53+ }
54+
55+ fn first_insert_pos ( & self ) -> ( InsertPosition < SyntaxElement > , AddBlankLine ) {
56+ match self {
57+ ImportScope :: File ( _) => ( InsertPosition :: First , AddBlankLine :: AfterTwice ) ,
58+ // don't insert the impotrs before the item lists curly brace
59+ ImportScope :: Module ( item_list) => item_list
60+ . l_curly_token ( )
61+ . map ( |b| ( InsertPosition :: After ( b. into ( ) ) , AddBlankLine :: Around ) )
62+ . unwrap_or ( ( InsertPosition :: First , AddBlankLine :: AfterTwice ) ) ,
63+ }
64+ }
65+
66+ fn insert_pos_after_inner_attribute ( & self ) -> ( InsertPosition < SyntaxElement > , AddBlankLine ) {
67+ // check if the scope has a inner attributes, we dont want to insert in front of it
68+ match self
69+ . as_syntax_node ( )
70+ . children ( )
71+ // no flat_map here cause we want to short circuit the iterator
72+ . map ( ast:: Attr :: cast)
73+ . take_while ( |attr| {
74+ attr. as_ref ( ) . map ( |attr| attr. kind ( ) == ast:: AttrKind :: Inner ) . unwrap_or ( false )
75+ } )
76+ . last ( )
77+ . flatten ( )
78+ {
79+ Some ( attr) => {
80+ ( InsertPosition :: After ( attr. syntax ( ) . clone ( ) . into ( ) ) , AddBlankLine :: BeforeTwice )
81+ }
82+ None => self . first_insert_pos ( ) ,
83+ }
84+ }
2685}
2786
2887/// Insert an import path into the given file/node. A `merge` value of none indicates that no import merging is allowed to occur.
29- pub fn insert_use (
30- where_ : & SyntaxNode ,
88+ pub ( crate ) fn insert_use (
89+ scope : & ImportScope ,
3190 path : ast:: Path ,
3291 merge : Option < MergeBehaviour > ,
3392) -> SyntaxNode {
3493 let use_item = make:: use_ ( make:: use_tree ( path. clone ( ) , None , None , false ) ) ;
3594 // merge into existing imports if possible
3695 if let Some ( mb) = merge {
37- for existing_use in where_ . children ( ) . filter_map ( ast:: Use :: cast) {
96+ for existing_use in scope . as_syntax_node ( ) . children ( ) . filter_map ( ast:: Use :: cast) {
3897 if let Some ( merged) = try_merge_imports ( & existing_use, & use_item, mb) {
3998 let to_delete: SyntaxElement = existing_use. syntax ( ) . clone ( ) . into ( ) ;
4099 let to_delete = to_delete. clone ( ) ..=to_delete;
41100 let to_insert = iter:: once ( merged. syntax ( ) . clone ( ) . into ( ) ) ;
42- return algo:: replace_children ( where_ , to_delete, to_insert) ;
101+ return algo:: replace_children ( scope . as_syntax_node ( ) , to_delete, to_insert) ;
43102 }
44103 }
45104 }
46105
47106 // either we weren't allowed to merge or there is no import that fits the merge conditions
48107 // so look for the place we have to insert to
49- let ( insert_position, add_blank) = find_insert_position ( where_ , path) ;
108+ let ( insert_position, add_blank) = find_insert_position ( scope , path) ;
50109
51110 let to_insert: Vec < SyntaxElement > = {
52111 let mut buf = Vec :: new ( ) ;
53112
54113 match add_blank {
55- AddBlankLine :: Before => buf. push ( make:: tokens:: single_newline ( ) . into ( ) ) ,
114+ AddBlankLine :: Before | AddBlankLine :: Around => {
115+ buf. push ( make:: tokens:: single_newline ( ) . into ( ) )
116+ }
56117 AddBlankLine :: BeforeTwice => buf. push ( make:: tokens:: blank_line ( ) . into ( ) ) ,
57118 _ => ( ) ,
58119 }
59120
121+ if let ident_level @ 1 ..=usize:: MAX = scope. indent_level ( ) . 0 as usize {
122+ // TODO: this alone doesnt properly re-align all cases
123+ buf. push ( make:: tokens:: whitespace ( & " " . repeat ( 4 * ident_level) ) . into ( ) ) ;
124+ }
60125 buf. push ( use_item. syntax ( ) . clone ( ) . into ( ) ) ;
61126
62127 match add_blank {
63- AddBlankLine :: After => buf. push ( make:: tokens:: single_newline ( ) . into ( ) ) ,
128+ AddBlankLine :: After | AddBlankLine :: Around => {
129+ buf. push ( make:: tokens:: single_newline ( ) . into ( ) )
130+ }
64131 AddBlankLine :: AfterTwice => buf. push ( make:: tokens:: blank_line ( ) . into ( ) ) ,
65132 _ => ( ) ,
66133 }
67134
68135 buf
69136 } ;
70137
71- algo:: insert_children ( where_ , insert_position, to_insert)
138+ algo:: insert_children ( scope . as_syntax_node ( ) , insert_position, to_insert)
72139}
73140
74141fn try_merge_imports (
@@ -218,16 +285,18 @@ fn segment_iter(path: &ast::Path) -> impl Iterator<Item = ast::PathSegment> + Cl
218285enum AddBlankLine {
219286 Before ,
220287 BeforeTwice ,
288+ Around ,
221289 After ,
222290 AfterTwice ,
223291}
224292
225293fn find_insert_position (
226- scope : & SyntaxNode ,
294+ scope : & ImportScope ,
227295 insert_path : ast:: Path ,
228296) -> ( InsertPosition < SyntaxElement > , AddBlankLine ) {
229297 let group = ImportGroup :: new ( & insert_path) ;
230298 let path_node_iter = scope
299+ . as_syntax_node ( )
231300 . children ( )
232301 . filter_map ( |node| ast:: Use :: cast ( node. clone ( ) ) . zip ( Some ( node) ) )
233302 . flat_map ( |( use_, node) | use_. use_tree ( ) . and_then ( |tree| tree. path ( ) ) . zip ( Some ( node) ) ) ;
@@ -275,27 +344,7 @@ fn find_insert_position(
275344 ( InsertPosition :: After ( node. into ( ) ) , AddBlankLine :: BeforeTwice )
276345 }
277346 // there are no imports in this file at all
278- None => {
279- // check if the scope has a inner attributes, we dont want to insert in front of it
280- match scope
281- . children ( )
282- // no flat_map here cause we want to short circuit the iterator
283- . map ( ast:: Attr :: cast)
284- . take_while ( |attr| {
285- attr. as_ref ( )
286- . map ( |attr| attr. kind ( ) == ast:: AttrKind :: Inner )
287- . unwrap_or ( false )
288- } )
289- . last ( )
290- . flatten ( )
291- {
292- Some ( attr) => (
293- InsertPosition :: After ( attr. syntax ( ) . clone ( ) . into ( ) ) ,
294- AddBlankLine :: BeforeTwice ,
295- ) ,
296- None => ( InsertPosition :: First , AddBlankLine :: AfterTwice ) ,
297- }
298- }
347+ None => scope. insert_pos_after_inner_attribute ( ) ,
299348 } ,
300349 }
301350 }
@@ -640,7 +689,10 @@ use foo::bar;",
640689 ra_fixture_after : & str ,
641690 mb : Option < MergeBehaviour > ,
642691 ) {
643- let file = ast:: SourceFile :: parse ( ra_fixture_before) . tree ( ) . syntax ( ) . clone ( ) ;
692+ let file = super :: ImportScope :: from (
693+ ast:: SourceFile :: parse ( ra_fixture_before) . tree ( ) . syntax ( ) . clone ( ) ,
694+ )
695+ . unwrap ( ) ;
644696 let path = ast:: SourceFile :: parse ( & format ! ( "use {};" , path) )
645697 . tree ( )
646698 . syntax ( )
0 commit comments