ra_ap_ide_completion/completions/
item_list.rs

1//! Completion of paths and keywords at item list position.
2
3use crate::{
4    CompletionContext, Completions,
5    context::{ItemListKind, PathCompletionCtx, PathExprCtx, Qualified},
6};
7
8pub(crate) mod trait_impl;
9
10pub(crate) fn complete_item_list_in_expr(
11    acc: &mut Completions,
12    ctx: &CompletionContext<'_>,
13    path_ctx: &PathCompletionCtx<'_>,
14    expr_ctx: &PathExprCtx<'_>,
15) {
16    if !expr_ctx.in_block_expr {
17        return;
18    }
19    if !path_ctx.is_trivial_path() {
20        return;
21    }
22    add_keywords(acc, ctx, None);
23}
24
25pub(crate) fn complete_item_list(
26    acc: &mut Completions,
27    ctx: &CompletionContext<'_>,
28    path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx<'_>,
29    kind: &ItemListKind,
30) {
31    let _p = tracing::info_span!("complete_item_list").entered();
32
33    // We handle completions for trait-impls in [`item_list::trait_impl`]
34    if path_ctx.is_trivial_path() && !matches!(kind, ItemListKind::TraitImpl(_)) {
35        add_keywords(acc, ctx, Some(kind));
36    }
37
38    match qualified {
39        Qualified::With {
40            resolution: Some(hir::PathResolution::Def(hir::ModuleDef::Module(module))),
41            super_chain_len,
42            ..
43        } => {
44            for (name, def) in module.scope(ctx.db, Some(ctx.module)) {
45                match def {
46                    hir::ScopeDef::ModuleDef(hir::ModuleDef::Macro(m)) if m.is_fn_like(ctx.db) => {
47                        acc.add_macro(ctx, path_ctx, m, name)
48                    }
49                    hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => {
50                        acc.add_module(ctx, path_ctx, m, name, vec![])
51                    }
52                    _ => (),
53                }
54            }
55
56            acc.add_super_keyword(ctx, *super_chain_len);
57        }
58        Qualified::Absolute => acc.add_crate_roots(ctx, path_ctx),
59        Qualified::No if ctx.qualifier_ctx.none() => {
60            ctx.process_all_names(&mut |name, def, doc_aliases| match def {
61                hir::ScopeDef::ModuleDef(hir::ModuleDef::Macro(m)) if m.is_fn_like(ctx.db) => {
62                    acc.add_macro(ctx, path_ctx, m, name)
63                }
64                hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => {
65                    acc.add_module(ctx, path_ctx, m, name, doc_aliases)
66                }
67                _ => (),
68            });
69            acc.add_nameref_keywords_with_colon(ctx);
70        }
71        Qualified::TypeAnchor { .. } | Qualified::No | Qualified::With { .. } => {}
72    }
73}
74
75fn add_keywords(acc: &mut Completions, ctx: &CompletionContext<'_>, kind: Option<&ItemListKind>) {
76    let mut add_keyword = |kw, snippet| acc.add_keyword_snippet(ctx, kw, snippet);
77
78    let in_item_list = matches!(kind, Some(ItemListKind::SourceFile | ItemListKind::Module) | None);
79    let in_assoc_non_trait_impl = matches!(kind, Some(ItemListKind::Impl | ItemListKind::Trait));
80
81    let in_extern_block = matches!(kind, Some(ItemListKind::ExternBlock { .. }));
82    let in_unsafe_extern_block =
83        matches!(kind, Some(ItemListKind::ExternBlock { is_unsafe: true }));
84
85    let in_trait = matches!(kind, Some(ItemListKind::Trait));
86    let in_inherent_impl = matches!(kind, Some(ItemListKind::Impl));
87    let in_block = kind.is_none();
88
89    let no_vis_qualifiers = ctx.qualifier_ctx.vis_node.is_none();
90    let has_unsafe_kw = ctx.qualifier_ctx.unsafe_tok.is_some();
91    let has_async_kw = ctx.qualifier_ctx.async_tok.is_some();
92    let has_safe_kw = ctx.qualifier_ctx.safe_tok.is_some();
93
94    // Some keywords are invalid after non-vis qualifiers, so we handle them first.
95    if (has_unsafe_kw || has_safe_kw) && in_extern_block {
96        add_keyword("fn", "fn $1($2);");
97        add_keyword("static", "static $1: $2;");
98        return;
99    }
100
101    if has_unsafe_kw || has_async_kw {
102        if !has_unsafe_kw {
103            add_keyword("unsafe", "unsafe $0");
104        }
105        if !has_async_kw {
106            add_keyword("async", "async $0");
107        }
108
109        if in_item_list || in_assoc_non_trait_impl {
110            add_keyword("fn", "fn $1($2) {\n    $0\n}");
111        }
112
113        if has_unsafe_kw && in_item_list {
114            add_keyword("trait", "trait $1 {\n    $0\n}");
115            if no_vis_qualifiers {
116                add_keyword("impl", "impl $1 {\n    $0\n}");
117                add_keyword("impl for", "impl $1 for $2 {\n    $0\n}");
118            }
119        }
120
121        if !has_async_kw && no_vis_qualifiers && in_item_list {
122            add_keyword("extern", "extern $0");
123        }
124
125        return;
126    }
127
128    // ...and the rest deals with cases without any non-vis qualifiers.
129
130    // Visibility qualifiers
131    if !in_trait && !in_block && no_vis_qualifiers {
132        add_keyword("pub(crate)", "pub(crate) $0");
133        add_keyword("pub(super)", "pub(super) $0");
134        add_keyword("pub", "pub $0");
135    }
136
137    // Keywords that are valid in `item_list`
138    if in_item_list {
139        add_keyword("enum", "enum $1 {\n    $0\n}");
140        add_keyword("mod", "mod $0");
141        add_keyword("static", "static $0");
142        add_keyword("struct", "struct $0");
143        add_keyword("trait", "trait $1 {\n    $0\n}");
144        add_keyword("union", "union $1 {\n    $0\n}");
145        add_keyword("use", "use $0;");
146        if no_vis_qualifiers {
147            add_keyword("impl", "impl $1 {\n    $0\n}");
148            add_keyword("impl for", "impl $1 for $2 {\n    $0\n}");
149        }
150    }
151
152    if in_extern_block {
153        add_keyword("unsafe", "unsafe $0");
154        if in_unsafe_extern_block {
155            add_keyword("safe", "safe $0");
156        }
157
158        add_keyword("fn", "fn $1($2);");
159        add_keyword("static", "static $1: $2;");
160    } else {
161        if !in_inherent_impl {
162            if !in_trait {
163                add_keyword("extern", "extern $0");
164            }
165            add_keyword("type", "type $0");
166        }
167
168        add_keyword("fn", "fn $1($2) {\n    $0\n}");
169        add_keyword("unsafe", "unsafe $0");
170        add_keyword("const", "const $0");
171        add_keyword("async", "async $0");
172    }
173}