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