aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJamie Cunliffe <[email protected]>2021-05-30 14:52:19 +0100
committerJamie Cunliffe <[email protected]>2021-06-21 17:47:00 +0100
commit284483b347d15bee3a7bf293d33e5f19a9740102 (patch)
treee582a7ecdef30511b8982529f11889f1aab3ca5e
parent1b05dbba39d5a4d46f321dc962df99038cddbf21 (diff)
Improve completion of cfg attributes
The completion of cfg will look at the enabled cfg keys when performing completion. It will also look crate features when completing a feature cfg option. A fixed list of known values for some cfg options are provided. For unknown keys it will look at the enabled values for that cfg key, which means that completion will only show enabled options for those.
-rw-r--r--crates/base_db/src/fixture.rs2
-rw-r--r--crates/base_db/src/input.rs13
-rw-r--r--crates/cfg/src/lib.rs20
-rw-r--r--crates/hir/src/lib.rs4
-rw-r--r--crates/ide/src/lib.rs1
-rw-r--r--crates/ide_completion/src/completions/attribute.rs15
-rw-r--r--crates/ide_completion/src/completions/attribute/cfg.rs126
-rw-r--r--crates/project_model/src/workspace.rs4
8 files changed, 185 insertions, 0 deletions
diff --git a/crates/base_db/src/fixture.rs b/crates/base_db/src/fixture.rs
index 6ce377710..6d3b1266e 100644
--- a/crates/base_db/src/fixture.rs
+++ b/crates/base_db/src/fixture.rs
@@ -131,6 +131,7 @@ impl ChangeFixture {
131 meta.cfg, 131 meta.cfg,
132 meta.env, 132 meta.env,
133 Default::default(), 133 Default::default(),
134 Default::default(),
134 ); 135 );
135 let prev = crates.insert(crate_name.clone(), crate_id); 136 let prev = crates.insert(crate_name.clone(), crate_id);
136 assert!(prev.is_none()); 137 assert!(prev.is_none());
@@ -160,6 +161,7 @@ impl ChangeFixture {
160 default_cfg, 161 default_cfg,
161 Env::default(), 162 Env::default(),
162 Default::default(), 163 Default::default(),
164 Default::default(),
163 ); 165 );
164 } else { 166 } else {
165 for (from, to) in crate_deps { 167 for (from, to) in crate_deps {
diff --git a/crates/base_db/src/input.rs b/crates/base_db/src/input.rs
index 23cb0c839..d99388f71 100644
--- a/crates/base_db/src/input.rs
+++ b/crates/base_db/src/input.rs
@@ -192,6 +192,7 @@ pub struct CrateData {
192 pub env: Env, 192 pub env: Env,
193 pub dependencies: Vec<Dependency>, 193 pub dependencies: Vec<Dependency>,
194 pub proc_macro: Vec<ProcMacro>, 194 pub proc_macro: Vec<ProcMacro>,
195 pub features: FxHashMap<String, Vec<String>>,
195} 196}
196 197
197#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 198#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
@@ -221,6 +222,7 @@ impl CrateGraph {
221 cfg_options: CfgOptions, 222 cfg_options: CfgOptions,
222 env: Env, 223 env: Env,
223 proc_macro: Vec<ProcMacro>, 224 proc_macro: Vec<ProcMacro>,
225 features: FxHashMap<String, Vec<String>>,
224 ) -> CrateId { 226 ) -> CrateId {
225 let data = CrateData { 227 let data = CrateData {
226 root_file_id: file_id, 228 root_file_id: file_id,
@@ -230,6 +232,7 @@ impl CrateGraph {
230 env, 232 env,
231 proc_macro, 233 proc_macro,
232 dependencies: Vec::new(), 234 dependencies: Vec::new(),
235 features,
233 }; 236 };
234 let crate_id = CrateId(self.arena.len() as u32); 237 let crate_id = CrateId(self.arena.len() as u32);
235 let prev = self.arena.insert(crate_id, data); 238 let prev = self.arena.insert(crate_id, data);
@@ -506,6 +509,7 @@ mod tests {
506 CfgOptions::default(), 509 CfgOptions::default(),
507 Env::default(), 510 Env::default(),
508 Default::default(), 511 Default::default(),
512 Default::default(),
509 ); 513 );
510 let crate2 = graph.add_crate_root( 514 let crate2 = graph.add_crate_root(
511 FileId(2u32), 515 FileId(2u32),
@@ -514,6 +518,7 @@ mod tests {
514 CfgOptions::default(), 518 CfgOptions::default(),
515 Env::default(), 519 Env::default(),
516 Default::default(), 520 Default::default(),
521 Default::default(),
517 ); 522 );
518 let crate3 = graph.add_crate_root( 523 let crate3 = graph.add_crate_root(
519 FileId(3u32), 524 FileId(3u32),
@@ -522,6 +527,7 @@ mod tests {
522 CfgOptions::default(), 527 CfgOptions::default(),
523 Env::default(), 528 Env::default(),
524 Default::default(), 529 Default::default(),
530 Default::default(),
525 ); 531 );
526 assert!(graph.add_dep(crate1, CrateName::new("crate2").unwrap(), crate2).is_ok()); 532 assert!(graph.add_dep(crate1, CrateName::new("crate2").unwrap(), crate2).is_ok());
527 assert!(graph.add_dep(crate2, CrateName::new("crate3").unwrap(), crate3).is_ok()); 533 assert!(graph.add_dep(crate2, CrateName::new("crate3").unwrap(), crate3).is_ok());
@@ -538,6 +544,7 @@ mod tests {
538 CfgOptions::default(), 544 CfgOptions::default(),
539 Env::default(), 545 Env::default(),
540 Default::default(), 546 Default::default(),
547 Default::default(),
541 ); 548 );
542 let crate2 = graph.add_crate_root( 549 let crate2 = graph.add_crate_root(
543 FileId(2u32), 550 FileId(2u32),
@@ -546,6 +553,7 @@ mod tests {
546 CfgOptions::default(), 553 CfgOptions::default(),
547 Env::default(), 554 Env::default(),
548 Default::default(), 555 Default::default(),
556 Default::default(),
549 ); 557 );
550 assert!(graph.add_dep(crate1, CrateName::new("crate2").unwrap(), crate2).is_ok()); 558 assert!(graph.add_dep(crate1, CrateName::new("crate2").unwrap(), crate2).is_ok());
551 assert!(graph.add_dep(crate2, CrateName::new("crate2").unwrap(), crate2).is_err()); 559 assert!(graph.add_dep(crate2, CrateName::new("crate2").unwrap(), crate2).is_err());
@@ -561,6 +569,7 @@ mod tests {
561 CfgOptions::default(), 569 CfgOptions::default(),
562 Env::default(), 570 Env::default(),
563 Default::default(), 571 Default::default(),
572 Default::default(),
564 ); 573 );
565 let crate2 = graph.add_crate_root( 574 let crate2 = graph.add_crate_root(
566 FileId(2u32), 575 FileId(2u32),
@@ -569,6 +578,7 @@ mod tests {
569 CfgOptions::default(), 578 CfgOptions::default(),
570 Env::default(), 579 Env::default(),
571 Default::default(), 580 Default::default(),
581 Default::default(),
572 ); 582 );
573 let crate3 = graph.add_crate_root( 583 let crate3 = graph.add_crate_root(
574 FileId(3u32), 584 FileId(3u32),
@@ -577,6 +587,7 @@ mod tests {
577 CfgOptions::default(), 587 CfgOptions::default(),
578 Env::default(), 588 Env::default(),
579 Default::default(), 589 Default::default(),
590 Default::default(),
580 ); 591 );
581 assert!(graph.add_dep(crate1, CrateName::new("crate2").unwrap(), crate2).is_ok()); 592 assert!(graph.add_dep(crate1, CrateName::new("crate2").unwrap(), crate2).is_ok());
582 assert!(graph.add_dep(crate2, CrateName::new("crate3").unwrap(), crate3).is_ok()); 593 assert!(graph.add_dep(crate2, CrateName::new("crate3").unwrap(), crate3).is_ok());
@@ -592,6 +603,7 @@ mod tests {
592 CfgOptions::default(), 603 CfgOptions::default(),
593 Env::default(), 604 Env::default(),
594 Default::default(), 605 Default::default(),
606 Default::default(),
595 ); 607 );
596 let crate2 = graph.add_crate_root( 608 let crate2 = graph.add_crate_root(
597 FileId(2u32), 609 FileId(2u32),
@@ -600,6 +612,7 @@ mod tests {
600 CfgOptions::default(), 612 CfgOptions::default(),
601 Env::default(), 613 Env::default(),
602 Default::default(), 614 Default::default(),
615 Default::default(),
603 ); 616 );
604 assert!(graph 617 assert!(graph
605 .add_dep(crate1, CrateName::normalize_dashes("crate-name-with-dashes"), crate2) 618 .add_dep(crate1, CrateName::normalize_dashes("crate-name-with-dashes"), crate2)
diff --git a/crates/cfg/src/lib.rs b/crates/cfg/src/lib.rs
index 916d39a0b..9a4baa636 100644
--- a/crates/cfg/src/lib.rs
+++ b/crates/cfg/src/lib.rs
@@ -50,6 +50,26 @@ impl CfgOptions {
50 self.enabled.remove(&atom); 50 self.enabled.remove(&atom);
51 } 51 }
52 } 52 }
53
54 pub fn get_cfg_keys(&self) -> Vec<&SmolStr> {
55 self.enabled
56 .iter()
57 .map(|x| match x {
58 CfgAtom::Flag(key) => key,
59 CfgAtom::KeyValue { key, .. } => key,
60 })
61 .collect()
62 }
63
64 pub fn get_cfg_values(&self, cfg_key: &str) -> Vec<&SmolStr> {
65 self.enabled
66 .iter()
67 .filter_map(|x| match x {
68 CfgAtom::KeyValue { key, value } if cfg_key == key => Some(value),
69 _ => None,
70 })
71 .collect()
72 }
53} 73}
54 74
55#[derive(Clone, Debug, PartialEq, Eq)] 75#[derive(Clone, Debug, PartialEq, Eq)]
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 88490fea9..2b2aaec94 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -233,6 +233,10 @@ impl Crate {
233 pub fn cfg(&self, db: &dyn HirDatabase) -> CfgOptions { 233 pub fn cfg(&self, db: &dyn HirDatabase) -> CfgOptions {
234 db.crate_graph()[self.id].cfg_options.clone() 234 db.crate_graph()[self.id].cfg_options.clone()
235 } 235 }
236
237 pub fn features(&self, db: &dyn HirDatabase) -> Vec<String> {
238 db.crate_graph()[self.id].features.iter().map(|(feat, _)| feat.clone()).collect()
239 }
236} 240}
237 241
238#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 242#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index 3798f32cc..0693869a2 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -220,6 +220,7 @@ impl Analysis {
220 cfg_options, 220 cfg_options,
221 Env::default(), 221 Env::default(),
222 Default::default(), 222 Default::default(),
223 Default::default(),
223 ); 224 );
224 change.change_file(file_id, Some(Arc::new(text))); 225 change.change_file(file_id, Some(Arc::new(text)));
225 change.set_crate_graph(crate_graph); 226 change.set_crate_graph(crate_graph);
diff --git a/crates/ide_completion/src/completions/attribute.rs b/crates/ide_completion/src/completions/attribute.rs
index 78fc30e16..cc4f4b2af 100644
--- a/crates/ide_completion/src/completions/attribute.rs
+++ b/crates/ide_completion/src/completions/attribute.rs
@@ -15,6 +15,7 @@ use crate::{
15 Completions, 15 Completions,
16}; 16};
17 17
18mod cfg;
18mod derive; 19mod derive;
19mod lint; 20mod lint;
20mod repr; 21mod repr;
@@ -30,6 +31,9 @@ pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext)
30 lint::complete_lint(acc, ctx, token_tree.clone(), DEFAULT_LINTS); 31 lint::complete_lint(acc, ctx, token_tree.clone(), DEFAULT_LINTS);
31 lint::complete_lint(acc, ctx, token_tree, CLIPPY_LINTS); 32 lint::complete_lint(acc, ctx, token_tree, CLIPPY_LINTS);
32 } 33 }
34 "cfg" => {
35 cfg::complete_cfg(acc, ctx);
36 }
33 _ => (), 37 _ => (),
34 }, 38 },
35 (None, Some(_)) => (), 39 (None, Some(_)) => (),
@@ -852,4 +856,15 @@ mod tests {
852 "#]], 856 "#]],
853 ); 857 );
854 } 858 }
859
860 #[test]
861 fn test_cfg() {
862 check(
863 r#"#[cfg(target_endian = $0"#,
864 expect![[r#"
865 at little
866 at big
867"#]],
868 );
869 }
855} 870}
diff --git a/crates/ide_completion/src/completions/attribute/cfg.rs b/crates/ide_completion/src/completions/attribute/cfg.rs
new file mode 100644
index 000000000..71e659563
--- /dev/null
+++ b/crates/ide_completion/src/completions/attribute/cfg.rs
@@ -0,0 +1,126 @@
1//! Completion for cfg
2
3use std::iter;
4
5use syntax::SyntaxKind;
6
7use crate::{
8 completions::Completions, context::CompletionContext, item::CompletionKind, CompletionItem,
9 CompletionItemKind,
10};
11
12pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext) {
13 let add_completion = |item: &&str| {
14 let mut completion =
15 CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), *item);
16 completion.insert_text(format!(r#""{}""#, item));
17 completion.kind(CompletionItemKind::Attribute);
18 acc.add(completion.build());
19 };
20
21 let previous = iter::successors(ctx.original_token.prev_token(), |t| {
22 (matches!(t.kind(), SyntaxKind::EQ) || t.kind().is_trivia())
23 .then(|| t.prev_token())
24 .flatten()
25 })
26 .find(|t| matches!(t.kind(), SyntaxKind::IDENT));
27
28 match previous.as_ref().map(|p| p.text()) {
29 Some("feature") => {
30 ctx.krate.map(|krate| {
31 krate.features(ctx.db).iter().for_each(|f| {
32 let mut item = CompletionItem::new(
33 CompletionKind::Attribute,
34 ctx.source_range(),
35 f.clone(),
36 );
37 item.insert_text(format!(r#""{}""#, f));
38
39 acc.add(item.build())
40 })
41 });
42 }
43 Some("target_arch") => KNOWN_ARCH.iter().for_each(add_completion),
44 Some("target_env") => KNOWN_ENV.iter().for_each(add_completion),
45 Some("target_os") => KNOWN_OS.iter().for_each(add_completion),
46 Some("target_vendor") => KNOWN_VENDOR.iter().for_each(add_completion),
47 Some("target_endian") => ["little", "big"].iter().for_each(add_completion),
48 Some(name) => {
49 ctx.krate.map(|krate| {
50 krate.cfg(ctx.db).get_cfg_values(&name).iter().for_each(|s| {
51 let mut item = CompletionItem::new(
52 CompletionKind::Attribute,
53 ctx.source_range(),
54 s.as_str(),
55 );
56 item.insert_text(format!(r#""{}""#, s));
57
58 acc.add(item.build());
59 })
60 });
61 }
62 None => {
63 ctx.krate.map(|krate| {
64 krate.cfg(ctx.db).get_cfg_keys().iter().for_each(|s| {
65 let item = CompletionItem::new(
66 CompletionKind::Attribute,
67 ctx.source_range(),
68 s.as_str(),
69 );
70 acc.add(item.build());
71 })
72 });
73 }
74 };
75}
76
77const KNOWN_ARCH: [&'static str; 19] = [
78 "aarch64",
79 "arm",
80 "avr",
81 "hexagon",
82 "mips",
83 "mips64",
84 "msp430",
85 "nvptx64",
86 "powerpc",
87 "powerpc64",
88 "riscv32",
89 "riscv64",
90 "s390x",
91 "sparc",
92 "sparc64",
93 "wasm32",
94 "wasm64",
95 "x86",
96 "x86_64",
97];
98
99const KNOWN_ENV: [&'static str; 7] =
100 ["eabihf", "gnu", "gnueabihf", "msvc", "relibc", "sgx", "uclibc"];
101
102const KNOWN_OS: [&'static str; 20] = [
103 "cuda",
104 "dragonfly",
105 "emscripten",
106 "freebsd",
107 "fuchsia",
108 "haiku",
109 "hermit",
110 "illumos",
111 "l4re",
112 "linux",
113 "netbsd",
114 "none",
115 "openbsd",
116 "psp",
117 "redox",
118 "solaris",
119 "uefi",
120 "unknown",
121 "vxworks",
122 "windows",
123];
124
125const KNOWN_VENDOR: [&'static str; 8] =
126 ["apple", "fortanix", "nvidia", "pc", "sony", "unknown", "wrs", "uwp"];
diff --git a/crates/project_model/src/workspace.rs b/crates/project_model/src/workspace.rs
index d8217f714..9ee3fc1a3 100644
--- a/crates/project_model/src/workspace.rs
+++ b/crates/project_model/src/workspace.rs
@@ -387,6 +387,7 @@ fn project_json_to_crate_graph(
387 cfg_options, 387 cfg_options,
388 env, 388 env,
389 proc_macro.unwrap_or_default(), 389 proc_macro.unwrap_or_default(),
390 Default::default(),
390 ), 391 ),
391 ) 392 )
392 }) 393 })
@@ -582,6 +583,7 @@ fn detached_files_to_crate_graph(
582 cfg_options.clone(), 583 cfg_options.clone(),
583 Env::default(), 584 Env::default(),
584 Vec::new(), 585 Vec::new(),
586 Default::default(),
585 ); 587 );
586 588
587 for (name, krate) in public_deps.iter() { 589 for (name, krate) in public_deps.iter() {
@@ -726,6 +728,7 @@ fn add_target_crate_root(
726 cfg_options, 728 cfg_options,
727 env, 729 env,
728 proc_macro, 730 proc_macro,
731 pkg.features.clone(),
729 ); 732 );
730 733
731 crate_id 734 crate_id
@@ -755,6 +758,7 @@ fn sysroot_to_crate_graph(
755 cfg_options.clone(), 758 cfg_options.clone(),
756 env, 759 env,
757 proc_macro, 760 proc_macro,
761 Default::default(),
758 ); 762 );
759 Some((krate, crate_id)) 763 Some((krate, crate_id))
760 }) 764 })