Skip to main content

ide_completion/completions/attribute/
cfg.rs

1//! Completion for cfg
2
3use ide_db::SymbolKind;
4use itertools::Itertools;
5use syntax::{
6    AstToken, Direction, NodeOrToken, SmolStr, SyntaxKind, algo,
7    ast::{self, Ident, IsString},
8};
9
10use crate::{CompletionItem, completions::Completions, context::CompletionContext};
11
12pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext<'_, '_>) {
13    let (token_range, snip) = if let Some(string) = ast::String::cast(ctx.original_token.clone())
14        && let Some(range) = string.text_range_between_quotes()
15    {
16        (range, str::to_owned as for<'a> fn(&'a str) -> _)
17    } else {
18        (ctx.source_range(), (|s| format!(r#""{s}""#)) as for<'a> fn(&'a str) -> _)
19    };
20    let add_completion = |item: &str| {
21        let mut completion =
22            CompletionItem::new(SymbolKind::BuiltinAttr, token_range, item, ctx.edition);
23        completion.insert_text(snip(item));
24        completion.add_to(acc, ctx.db);
25    };
26
27    // FIXME: Move this into context/analysis.rs
28    let previous = ctx
29        .original_token
30        .prev_token()
31        .and_then(|it| {
32            if matches!(it.kind(), SyntaxKind::EQ) {
33                Some(it.into())
34            } else {
35                algo::non_trivia_sibling(it.into(), Direction::Prev)
36            }
37        })
38        .filter(|t| matches!(t.kind(), SyntaxKind::EQ))
39        .and_then(|it| algo::non_trivia_sibling(it.prev_sibling_or_token()?, Direction::Prev))
40        .map(|it| match it {
41            NodeOrToken::Node(_) => None,
42            NodeOrToken::Token(t) => Ident::cast(t),
43        });
44    match previous {
45        Some(None) => (),
46        Some(Some(p)) => match p.text() {
47            "target_arch" => KNOWN_ARCH.iter().copied().for_each(add_completion),
48            "target_env" => KNOWN_ENV.iter().copied().for_each(add_completion),
49            "target_os" => KNOWN_OS.iter().copied().for_each(add_completion),
50            "target_vendor" => KNOWN_VENDOR.iter().copied().for_each(add_completion),
51            "target_endian" => ["little", "big"].into_iter().for_each(add_completion),
52            name => ctx.krate.potential_cfg(ctx.db).get_cfg_values(name).for_each(|s| {
53                let s = s.as_str();
54                let mut item =
55                    CompletionItem::new(SymbolKind::BuiltinAttr, token_range, s, ctx.edition);
56                item.insert_text(snip(s));
57                item.add_to(acc, ctx.db);
58            }),
59        },
60        None => ctx
61            .krate
62            .potential_cfg(ctx.db)
63            .into_iter()
64            .map(|x| match x {
65                hir::CfgAtom::Flag(key) => (key.as_str(), "".into()),
66                hir::CfgAtom::KeyValue { key, .. } => (
67                    key.as_str(),
68                    if ctx.config.snippet_cap.is_some() {
69                        SmolStr::from_iter([key.as_str(), " = \"$1\""])
70                    } else {
71                        SmolStr::default()
72                    },
73                ),
74            })
75            .chain(CFG_CONDITION.iter().map(|&(k, snip)| (k, SmolStr::new_static(snip))))
76            .unique_by(|&(s, _)| s)
77            .for_each(|(s, snippet)| {
78                let mut item =
79                    CompletionItem::new(SymbolKind::BuiltinAttr, token_range, s, ctx.edition);
80                if let Some(cap) = ctx.config.snippet_cap
81                    && !snippet.is_empty()
82                {
83                    item.insert_snippet(cap, snippet);
84                }
85                item.add_to(acc, ctx.db);
86            }),
87    }
88}
89
90const CFG_CONDITION: &[(&str, &str)] = &[
91    ("all", "all($0)"),
92    ("any", "any($0)"),
93    ("not", "not($0)"),
94    ("false", "false"), // 'true' in 'potential_cfg'
95];
96
97const KNOWN_ARCH: [&str; 20] = [
98    "aarch64",
99    "arm",
100    "avr",
101    "csky",
102    "hexagon",
103    "mips",
104    "mips64",
105    "msp430",
106    "nvptx64",
107    "powerpc",
108    "powerpc64",
109    "riscv32",
110    "riscv64",
111    "s390x",
112    "sparc",
113    "sparc64",
114    "wasm32",
115    "wasm64",
116    "x86",
117    "x86_64",
118];
119
120const KNOWN_ENV: [&str; 7] = ["eabihf", "gnu", "gnueabihf", "msvc", "relibc", "sgx", "uclibc"];
121
122const KNOWN_OS: [&str; 22] = [
123    "cuda",
124    "dragonfly",
125    "emscripten",
126    "freebsd",
127    "fuchsia",
128    "haiku",
129    "hermit",
130    "hyperlight",
131    "illumos",
132    "l4re",
133    "linux",
134    "netbsd",
135    "none",
136    "openbsd",
137    "ps3",
138    "psp",
139    "redox",
140    "solaris",
141    "uefi",
142    "unknown",
143    "vxworks",
144    "windows",
145];
146
147const KNOWN_VENDOR: [&str; 8] =
148    ["apple", "fortanix", "nvidia", "pc", "sony", "unknown", "wrs", "uwp"];