Skip to content

Commit 2b17da6

Browse files
committed
Resolve macros in snippet require items
1 parent ca1fdd7 commit 2b17da6

File tree

3 files changed

+49
-54
lines changed

3 files changed

+49
-54
lines changed

crates/ide_completion/src/completions/postfix.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -232,8 +232,8 @@ fn add_custom_postfix_completions(
232232
ImportScope::find_insert_use_container_with_macros(&ctx.token.parent()?, &ctx.sema)?;
233233
ctx.config.postfix_snippets.iter().for_each(|snippet| {
234234
let imports = match snippet.imports(ctx, &import_scope) {
235-
Ok(imports) => imports,
236-
Err(_) => return,
235+
Some(imports) => imports,
236+
None => return,
237237
};
238238
let mut builder = postfix_snippet(
239239
&snippet.label,

crates/ide_completion/src/completions/snippet.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,8 @@ fn add_custom_completions(
105105
ImportScope::find_insert_use_container_with_macros(&ctx.token.parent()?, &ctx.sema)?;
106106
ctx.config.snippets.iter().filter(|snip| snip.scope == scope).for_each(|snip| {
107107
let imports = match snip.imports(ctx, &import_scope) {
108-
Ok(imports) => imports,
109-
Err(_) => return,
108+
Some(imports) => imports,
109+
None => return,
110110
};
111111
let mut builder = snippet(ctx, cap, &snip.label, &snip.snippet);
112112
for import in imports.into_iter() {

crates/ide_completion/src/snippet.rs

+45-50
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ pub struct Snippet {
3737
pub description: Option<String>,
3838
pub requires: Box<[String]>,
3939
}
40-
4140
impl Snippet {
4241
pub fn new(
4342
label: String,
@@ -46,19 +45,7 @@ impl Snippet {
4645
requires: &[String],
4746
scope: SnippetScope,
4847
) -> Option<Self> {
49-
// validate that these are indeed simple paths
50-
if requires.iter().any(|path| match ast::Path::parse(path) {
51-
Ok(path) => path.segments().any(|seg| {
52-
!matches!(seg.kind(), Some(ast::PathSegmentKind::Name(_)))
53-
|| seg.generic_arg_list().is_some()
54-
}),
55-
Err(_) => true,
56-
}) {
57-
return None;
58-
}
59-
let snippet = snippet.iter().join("\n");
60-
let description = description.iter().join("\n");
61-
let description = if description.is_empty() { None } else { Some(description) };
48+
let (snippet, description) = validate_snippet(snippet, description, requires)?;
6249
Some(Snippet {
6350
scope,
6451
label,
@@ -68,12 +55,12 @@ impl Snippet {
6855
})
6956
}
7057

71-
// FIXME: This shouldn't be fallible
58+
/// Returns None if the required items do not resolve.
7259
pub(crate) fn imports(
7360
&self,
7461
ctx: &CompletionContext,
7562
import_scope: &ImportScope,
76-
) -> Result<Vec<ImportEdit>, ()> {
63+
) -> Option<Vec<ImportEdit>> {
7764
import_edits(ctx, import_scope, &self.requires)
7865
}
7966

@@ -94,19 +81,7 @@ impl PostfixSnippet {
9481
requires: &[String],
9582
scope: PostfixSnippetScope,
9683
) -> Option<Self> {
97-
// validate that these are indeed simple paths
98-
if requires.iter().any(|path| match ast::Path::parse(path) {
99-
Ok(path) => path.segments().any(|seg| {
100-
!matches!(seg.kind(), Some(ast::PathSegmentKind::Name(_)))
101-
|| seg.generic_arg_list().is_some()
102-
}),
103-
Err(_) => true,
104-
}) {
105-
return None;
106-
}
107-
let snippet = snippet.iter().join("\n");
108-
let description = description.iter().join("\n");
109-
let description = if description.is_empty() { None } else { Some(description) };
84+
let (snippet, description) = validate_snippet(snippet, description, requires)?;
11085
Some(PostfixSnippet {
11186
scope,
11287
label,
@@ -116,12 +91,12 @@ impl PostfixSnippet {
11691
})
11792
}
11893

119-
// FIXME: This shouldn't be fallible
94+
/// Returns None if the required items do not resolve.
12095
pub(crate) fn imports(
12196
&self,
12297
ctx: &CompletionContext,
12398
import_scope: &ImportScope,
124-
) -> Result<Vec<ImportEdit>, ()> {
99+
) -> Option<Vec<ImportEdit>> {
125100
import_edits(ctx, import_scope, &self.requires)
126101
}
127102

@@ -142,32 +117,52 @@ fn import_edits(
142117
ctx: &CompletionContext,
143118
import_scope: &ImportScope,
144119
requires: &[String],
145-
) -> Result<Vec<ImportEdit>, ()> {
120+
) -> Option<Vec<ImportEdit>> {
146121
let resolve = |import| {
147122
let path = ast::Path::parse(import).ok()?;
148-
match ctx.scope.speculative_resolve(&path)? {
149-
hir::PathResolution::Macro(_) => None,
150-
hir::PathResolution::Def(def) => {
151-
let item = def.into();
152-
let path = ctx.scope.module()?.find_use_path_prefixed(
153-
ctx.db,
154-
item,
155-
ctx.config.insert_use.prefix_kind,
156-
)?;
157-
Some((path.len() > 1).then(|| ImportEdit {
158-
import: LocatedImport::new(path.clone(), item, item, None),
159-
scope: import_scope.clone(),
160-
}))
161-
}
162-
_ => None,
163-
}
123+
let item = match ctx.scope.speculative_resolve(&path)? {
124+
hir::PathResolution::Macro(mac) => mac.into(),
125+
hir::PathResolution::Def(def) => def.into(),
126+
_ => return None,
127+
};
128+
let path = ctx.scope.module()?.find_use_path_prefixed(
129+
ctx.db,
130+
item,
131+
ctx.config.insert_use.prefix_kind,
132+
)?;
133+
Some((path.len() > 1).then(|| ImportEdit {
134+
import: LocatedImport::new(path.clone(), item, item, None),
135+
scope: import_scope.clone(),
136+
}))
164137
};
165138
let mut res = Vec::with_capacity(requires.len());
166139
for import in requires {
167140
match resolve(import) {
168141
Some(first) => res.extend(first),
169-
None => return Err(()),
142+
None => return None,
170143
}
171144
}
172-
Ok(res)
145+
Some(res)
146+
}
147+
148+
fn validate_snippet(
149+
snippet: &[String],
150+
description: &[String],
151+
requires: &[String],
152+
) -> Option<(String, Option<String>)> {
153+
// validate that these are indeed simple paths
154+
// we can't save the paths unfortunately due to them not being Send+Sync
155+
if requires.iter().any(|path| match ast::Path::parse(path) {
156+
Ok(path) => path.segments().any(|seg| {
157+
!matches!(seg.kind(), Some(ast::PathSegmentKind::Name(_)))
158+
|| seg.generic_arg_list().is_some()
159+
}),
160+
Err(_) => true,
161+
}) {
162+
return None;
163+
}
164+
let snippet = snippet.iter().join("\n");
165+
let description = description.iter().join("\n");
166+
let description = if description.is_empty() { None } else { Some(description) };
167+
Some((snippet, description))
173168
}

0 commit comments

Comments
 (0)