diff options
author | Jamie Cunliffe <[email protected]> | 2021-05-30 14:52:19 +0100 |
---|---|---|
committer | Jamie Cunliffe <[email protected]> | 2021-06-21 17:47:00 +0100 |
commit | 284483b347d15bee3a7bf293d33e5f19a9740102 (patch) | |
tree | e582a7ecdef30511b8982529f11889f1aab3ca5e | |
parent | 1b05dbba39d5a4d46f321dc962df99038cddbf21 (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.rs | 2 | ||||
-rw-r--r-- | crates/base_db/src/input.rs | 13 | ||||
-rw-r--r-- | crates/cfg/src/lib.rs | 20 | ||||
-rw-r--r-- | crates/hir/src/lib.rs | 4 | ||||
-rw-r--r-- | crates/ide/src/lib.rs | 1 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/attribute.rs | 15 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/attribute/cfg.rs | 126 | ||||
-rw-r--r-- | crates/project_model/src/workspace.rs | 4 |
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 | ||
18 | mod cfg; | ||
18 | mod derive; | 19 | mod derive; |
19 | mod lint; | 20 | mod lint; |
20 | mod repr; | 21 | mod 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 | |||
3 | use std::iter; | ||
4 | |||
5 | use syntax::SyntaxKind; | ||
6 | |||
7 | use crate::{ | ||
8 | completions::Completions, context::CompletionContext, item::CompletionKind, CompletionItem, | ||
9 | CompletionItemKind, | ||
10 | }; | ||
11 | |||
12 | pub(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 | |||
77 | const 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 | |||
99 | const KNOWN_ENV: [&'static str; 7] = | ||
100 | ["eabihf", "gnu", "gnueabihf", "msvc", "relibc", "sgx", "uclibc"]; | ||
101 | |||
102 | const 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 | |||
125 | const 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 | }) |