ide_completion/completions/attribute/
cfg.rs1use 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 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"), ];
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"];