@@ -56,7 +56,7 @@ use std::ops::Deref;
56
56
// It does not act as a tabstop.
57
57
use ide_db:: helpers:: { import_assets:: LocatedImport , insert_use:: ImportScope } ;
58
58
use itertools:: Itertools ;
59
- use syntax:: ast;
59
+ use syntax:: { ast, AstNode , GreenNode , SyntaxNode } ;
60
60
61
61
use crate :: { context:: CompletionContext , ImportEdit } ;
62
62
@@ -75,9 +75,12 @@ pub struct Snippet {
75
75
pub postfix_triggers : Box < [ Box < str > ] > ,
76
76
pub prefix_triggers : Box < [ Box < str > ] > ,
77
77
pub scope : SnippetScope ,
78
- snippet : String ,
79
78
pub description : Option < Box < str > > ,
80
- pub requires : Box < [ Box < str > ] > ,
79
+ snippet : String ,
80
+ // These are `ast::Path`'s but due to SyntaxNodes not being Send we store these
81
+ // and reconstruct them on demand instead. This is cheaper than reparsing them
82
+ // from strings
83
+ requires : Box < [ GreenNode ] > ,
81
84
}
82
85
83
86
impl Snippet {
@@ -92,15 +95,15 @@ impl Snippet {
92
95
if prefix_triggers. is_empty ( ) && postfix_triggers. is_empty ( ) {
93
96
return None ;
94
97
}
95
- let ( snippet, description) = validate_snippet ( snippet, description, requires) ?;
98
+ let ( requires , snippet, description) = validate_snippet ( snippet, description, requires) ?;
96
99
Some ( Snippet {
97
100
// Box::into doesn't work as that has a Copy bound 😒
98
101
postfix_triggers : postfix_triggers. iter ( ) . map ( Deref :: deref) . map ( Into :: into) . collect ( ) ,
99
102
prefix_triggers : prefix_triggers. iter ( ) . map ( Deref :: deref) . map ( Into :: into) . collect ( ) ,
100
103
scope,
101
104
snippet,
102
105
description,
103
- requires : requires . iter ( ) . map ( Deref :: deref ) . map ( Into :: into ) . collect ( ) ,
106
+ requires,
104
107
} )
105
108
}
106
109
@@ -125,10 +128,10 @@ impl Snippet {
125
128
fn import_edits (
126
129
ctx : & CompletionContext ,
127
130
import_scope : & ImportScope ,
128
- requires : & [ Box < str > ] ,
131
+ requires : & [ GreenNode ] ,
129
132
) -> Option < Vec < ImportEdit > > {
130
- let resolve = |import| {
131
- let path = ast:: Path :: parse ( import) . ok ( ) ?;
133
+ let resolve = |import : & GreenNode | {
134
+ let path = ast:: Path :: cast ( SyntaxNode :: new_root ( import. clone ( ) ) ) ?;
132
135
let item = match ctx. scope . speculative_resolve ( & path) ? {
133
136
hir:: PathResolution :: Macro ( mac) => mac. into ( ) ,
134
137
hir:: PathResolution :: Def ( def) => def. into ( ) ,
@@ -158,19 +161,21 @@ fn validate_snippet(
158
161
snippet : & [ String ] ,
159
162
description : & str ,
160
163
requires : & [ String ] ,
161
- ) -> Option < ( String , Option < Box < str > > ) > {
162
- // validate that these are indeed simple paths
163
- // we can't save the paths unfortunately due to them not being Send+Sync
164
- if requires. iter ( ) . any ( |path| match ast:: Path :: parse ( path) {
165
- Ok ( path) => path. segments ( ) . any ( |seg| {
166
- !matches ! ( seg. kind( ) , Some ( ast:: PathSegmentKind :: Name ( _) ) )
167
- || seg. generic_arg_list ( ) . is_some ( )
168
- } ) ,
169
- Err ( _) => true ,
170
- } ) {
171
- return None ;
164
+ ) -> Option < ( Box < [ GreenNode ] > , String , Option < Box < str > > ) > {
165
+ let mut imports = Vec :: with_capacity ( requires. len ( ) ) ;
166
+ for path in requires. iter ( ) {
167
+ let path = ast:: Path :: parse ( path) . ok ( ) ?;
168
+ let valid_use_path = path. segments ( ) . all ( |seg| {
169
+ matches ! ( seg. kind( ) , Some ( ast:: PathSegmentKind :: Name ( _) ) )
170
+ || seg. generic_arg_list ( ) . is_none ( )
171
+ } ) ;
172
+ if !valid_use_path {
173
+ return None ;
174
+ }
175
+ let green = path. syntax ( ) . green ( ) . into_owned ( ) ;
176
+ imports. push ( green) ;
172
177
}
173
178
let snippet = snippet. iter ( ) . join ( "\n " ) ;
174
179
let description = if description. is_empty ( ) { None } else { Some ( description. into ( ) ) } ;
175
- Some ( ( snippet, description) )
180
+ Some ( ( imports . into_boxed_slice ( ) , snippet, description) )
176
181
}
0 commit comments