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 completion.add_to(acc, 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 item.add_to(acc, ctx.db);
53 }),
54 },
55 None => ctx
56 .krate
57 .potential_cfg(ctx.db)
58 .into_iter()
59 .map(|x| match x {
60 hir::CfgAtom::Flag(key) => (key.as_str(), "".into()),
61 hir::CfgAtom::KeyValue { key, .. } => (
62 key.as_str(),
63 if ctx.config.snippet_cap.is_some() {
64 SmolStr::from_iter([key.as_str(), " = $0"])
65 } else {
66 SmolStr::default()
67 },
68 ),
69 })
70 .chain(CFG_CONDITION.iter().map(|&(k, snip)| (k, SmolStr::new_static(snip))))
71 .unique_by(|&(s, _)| s)
72 .for_each(|(s, snippet)| {
73 let mut item = CompletionItem::new(
74 SymbolKind::BuiltinAttr,
75 ctx.source_range(),
76 s,
77 ctx.edition,
78 );
79 if let Some(cap) = ctx.config.snippet_cap
80 && !snippet.is_empty()
81 {
82 item.insert_snippet(cap, snippet);
83 }
84 item.add_to(acc, ctx.db);
85 }),
86 }
87}
88
89const CFG_CONDITION: &[(&str, &str)] =
90 &[("all", "all($0)"), ("any", "any($0)"), ("not", "not($0)")];
91
92const KNOWN_ARCH: [&str; 20] = [
93 "aarch64",
94 "arm",
95 "avr",
96 "csky",
97 "hexagon",
98 "mips",
99 "mips64",
100 "msp430",
101 "nvptx64",
102 "powerpc",
103 "powerpc64",
104 "riscv32",
105 "riscv64",
106 "s390x",
107 "sparc",
108 "sparc64",
109 "wasm32",
110 "wasm64",
111 "x86",
112 "x86_64",
113];
114
115const KNOWN_ENV: [&str; 7] = ["eabihf", "gnu", "gnueabihf", "msvc", "relibc", "sgx", "uclibc"];
116
117const KNOWN_OS: [&str; 20] = [
118 "cuda",
119 "dragonfly",
120 "emscripten",
121 "freebsd",
122 "fuchsia",
123 "haiku",
124 "hermit",
125 "illumos",
126 "l4re",
127 "linux",
128 "netbsd",
129 "none",
130 "openbsd",
131 "psp",
132 "redox",
133 "solaris",
134 "uefi",
135 "unknown",
136 "vxworks",
137 "windows",
138];
139
140const KNOWN_VENDOR: [&str; 8] =
141 ["apple", "fortanix", "nvidia", "pc", "sony", "unknown", "wrs", "uwp"];