1
1
use std:: iter:: { self , successors} ;
2
2
3
3
use 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
+ } ;
6
8
use syntax:: {
7
9
algo,
8
10
ast:: { self , make, AstNode } ,
@@ -11,64 +13,129 @@ use syntax::{
11
13
12
14
use test_utils:: mark;
13
15
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 )
22
28
} else {
23
- Some ( Either :: Right ( ast:: SourceFile :: cast ( n ) ? ) )
29
+ ast:: ItemList :: cast ( syntax ) . map ( ImportScope :: Module )
24
30
}
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
+ }
26
85
}
27
86
28
87
/// 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 ,
31
90
path : ast:: Path ,
32
91
merge : Option < MergeBehaviour > ,
33
92
) -> SyntaxNode {
34
93
let use_item = make:: use_ ( make:: use_tree ( path. clone ( ) , None , None , false ) ) ;
35
94
// merge into existing imports if possible
36
95
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) {
38
97
if let Some ( merged) = try_merge_imports ( & existing_use, & use_item, mb) {
39
98
let to_delete: SyntaxElement = existing_use. syntax ( ) . clone ( ) . into ( ) ;
40
99
let to_delete = to_delete. clone ( ) ..=to_delete;
41
100
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) ;
43
102
}
44
103
}
45
104
}
46
105
47
106
// either we weren't allowed to merge or there is no import that fits the merge conditions
48
107
// 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) ;
50
109
51
110
let to_insert: Vec < SyntaxElement > = {
52
111
let mut buf = Vec :: new ( ) ;
53
112
54
113
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
+ }
56
117
AddBlankLine :: BeforeTwice => buf. push ( make:: tokens:: blank_line ( ) . into ( ) ) ,
57
118
_ => ( ) ,
58
119
}
59
120
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
+ }
60
125
buf. push ( use_item. syntax ( ) . clone ( ) . into ( ) ) ;
61
126
62
127
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
+ }
64
131
AddBlankLine :: AfterTwice => buf. push ( make:: tokens:: blank_line ( ) . into ( ) ) ,
65
132
_ => ( ) ,
66
133
}
67
134
68
135
buf
69
136
} ;
70
137
71
- algo:: insert_children ( where_ , insert_position, to_insert)
138
+ algo:: insert_children ( scope . as_syntax_node ( ) , insert_position, to_insert)
72
139
}
73
140
74
141
fn try_merge_imports (
@@ -218,16 +285,18 @@ fn segment_iter(path: &ast::Path) -> impl Iterator<Item = ast::PathSegment> + Cl
218
285
enum AddBlankLine {
219
286
Before ,
220
287
BeforeTwice ,
288
+ Around ,
221
289
After ,
222
290
AfterTwice ,
223
291
}
224
292
225
293
fn find_insert_position (
226
- scope : & SyntaxNode ,
294
+ scope : & ImportScope ,
227
295
insert_path : ast:: Path ,
228
296
) -> ( InsertPosition < SyntaxElement > , AddBlankLine ) {
229
297
let group = ImportGroup :: new ( & insert_path) ;
230
298
let path_node_iter = scope
299
+ . as_syntax_node ( )
231
300
. children ( )
232
301
. filter_map ( |node| ast:: Use :: cast ( node. clone ( ) ) . zip ( Some ( node) ) )
233
302
. flat_map ( |( use_, node) | use_. use_tree ( ) . and_then ( |tree| tree. path ( ) ) . zip ( Some ( node) ) ) ;
@@ -275,27 +344,7 @@ fn find_insert_position(
275
344
( InsertPosition :: After ( node. into ( ) ) , AddBlankLine :: BeforeTwice )
276
345
}
277
346
// 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 ( ) ,
299
348
} ,
300
349
}
301
350
}
@@ -640,7 +689,10 @@ use foo::bar;",
640
689
ra_fixture_after : & str ,
641
690
mb : Option < MergeBehaviour > ,
642
691
) {
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 ( ) ;
644
696
let path = ast:: SourceFile :: parse ( & format ! ( "use {};" , path) )
645
697
. tree ( )
646
698
. syntax ( )
0 commit comments