mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-24 12:03:31 +00:00
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.
This commit is contained in:
parent
1b05dbba39
commit
284483b347
8 changed files with 185 additions and 0 deletions
|
@ -131,6 +131,7 @@ impl ChangeFixture {
|
|||
meta.cfg,
|
||||
meta.env,
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
);
|
||||
let prev = crates.insert(crate_name.clone(), crate_id);
|
||||
assert!(prev.is_none());
|
||||
|
@ -160,6 +161,7 @@ impl ChangeFixture {
|
|||
default_cfg,
|
||||
Env::default(),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
);
|
||||
} else {
|
||||
for (from, to) in crate_deps {
|
||||
|
|
|
@ -192,6 +192,7 @@ pub struct CrateData {
|
|||
pub env: Env,
|
||||
pub dependencies: Vec<Dependency>,
|
||||
pub proc_macro: Vec<ProcMacro>,
|
||||
pub features: FxHashMap<String, Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
|
@ -221,6 +222,7 @@ impl CrateGraph {
|
|||
cfg_options: CfgOptions,
|
||||
env: Env,
|
||||
proc_macro: Vec<ProcMacro>,
|
||||
features: FxHashMap<String, Vec<String>>,
|
||||
) -> CrateId {
|
||||
let data = CrateData {
|
||||
root_file_id: file_id,
|
||||
|
@ -230,6 +232,7 @@ impl CrateGraph {
|
|||
env,
|
||||
proc_macro,
|
||||
dependencies: Vec::new(),
|
||||
features,
|
||||
};
|
||||
let crate_id = CrateId(self.arena.len() as u32);
|
||||
let prev = self.arena.insert(crate_id, data);
|
||||
|
@ -506,6 +509,7 @@ mod tests {
|
|||
CfgOptions::default(),
|
||||
Env::default(),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
);
|
||||
let crate2 = graph.add_crate_root(
|
||||
FileId(2u32),
|
||||
|
@ -514,6 +518,7 @@ mod tests {
|
|||
CfgOptions::default(),
|
||||
Env::default(),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
);
|
||||
let crate3 = graph.add_crate_root(
|
||||
FileId(3u32),
|
||||
|
@ -522,6 +527,7 @@ mod tests {
|
|||
CfgOptions::default(),
|
||||
Env::default(),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
);
|
||||
assert!(graph.add_dep(crate1, CrateName::new("crate2").unwrap(), crate2).is_ok());
|
||||
assert!(graph.add_dep(crate2, CrateName::new("crate3").unwrap(), crate3).is_ok());
|
||||
|
@ -538,6 +544,7 @@ mod tests {
|
|||
CfgOptions::default(),
|
||||
Env::default(),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
);
|
||||
let crate2 = graph.add_crate_root(
|
||||
FileId(2u32),
|
||||
|
@ -546,6 +553,7 @@ mod tests {
|
|||
CfgOptions::default(),
|
||||
Env::default(),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
);
|
||||
assert!(graph.add_dep(crate1, CrateName::new("crate2").unwrap(), crate2).is_ok());
|
||||
assert!(graph.add_dep(crate2, CrateName::new("crate2").unwrap(), crate2).is_err());
|
||||
|
@ -561,6 +569,7 @@ mod tests {
|
|||
CfgOptions::default(),
|
||||
Env::default(),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
);
|
||||
let crate2 = graph.add_crate_root(
|
||||
FileId(2u32),
|
||||
|
@ -569,6 +578,7 @@ mod tests {
|
|||
CfgOptions::default(),
|
||||
Env::default(),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
);
|
||||
let crate3 = graph.add_crate_root(
|
||||
FileId(3u32),
|
||||
|
@ -577,6 +587,7 @@ mod tests {
|
|||
CfgOptions::default(),
|
||||
Env::default(),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
);
|
||||
assert!(graph.add_dep(crate1, CrateName::new("crate2").unwrap(), crate2).is_ok());
|
||||
assert!(graph.add_dep(crate2, CrateName::new("crate3").unwrap(), crate3).is_ok());
|
||||
|
@ -592,6 +603,7 @@ mod tests {
|
|||
CfgOptions::default(),
|
||||
Env::default(),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
);
|
||||
let crate2 = graph.add_crate_root(
|
||||
FileId(2u32),
|
||||
|
@ -600,6 +612,7 @@ mod tests {
|
|||
CfgOptions::default(),
|
||||
Env::default(),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
);
|
||||
assert!(graph
|
||||
.add_dep(crate1, CrateName::normalize_dashes("crate-name-with-dashes"), crate2)
|
||||
|
|
|
@ -50,6 +50,26 @@ impl CfgOptions {
|
|||
self.enabled.remove(&atom);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_cfg_keys(&self) -> Vec<&SmolStr> {
|
||||
self.enabled
|
||||
.iter()
|
||||
.map(|x| match x {
|
||||
CfgAtom::Flag(key) => key,
|
||||
CfgAtom::KeyValue { key, .. } => key,
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn get_cfg_values(&self, cfg_key: &str) -> Vec<&SmolStr> {
|
||||
self.enabled
|
||||
.iter()
|
||||
.filter_map(|x| match x {
|
||||
CfgAtom::KeyValue { key, value } if cfg_key == key => Some(value),
|
||||
_ => None,
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
|
|
|
@ -233,6 +233,10 @@ impl Crate {
|
|||
pub fn cfg(&self, db: &dyn HirDatabase) -> CfgOptions {
|
||||
db.crate_graph()[self.id].cfg_options.clone()
|
||||
}
|
||||
|
||||
pub fn features(&self, db: &dyn HirDatabase) -> Vec<String> {
|
||||
db.crate_graph()[self.id].features.iter().map(|(feat, _)| feat.clone()).collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
|
|
|
@ -220,6 +220,7 @@ impl Analysis {
|
|||
cfg_options,
|
||||
Env::default(),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
);
|
||||
change.change_file(file_id, Some(Arc::new(text)));
|
||||
change.set_crate_graph(crate_graph);
|
||||
|
|
|
@ -15,6 +15,7 @@ use crate::{
|
|||
Completions,
|
||||
};
|
||||
|
||||
mod cfg;
|
||||
mod derive;
|
||||
mod lint;
|
||||
mod repr;
|
||||
|
@ -30,6 +31,9 @@ pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext)
|
|||
lint::complete_lint(acc, ctx, token_tree.clone(), DEFAULT_LINTS);
|
||||
lint::complete_lint(acc, ctx, token_tree, CLIPPY_LINTS);
|
||||
}
|
||||
"cfg" => {
|
||||
cfg::complete_cfg(acc, ctx);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
(None, Some(_)) => (),
|
||||
|
@ -852,4 +856,15 @@ mod tests {
|
|||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cfg() {
|
||||
check(
|
||||
r#"#[cfg(target_endian = $0"#,
|
||||
expect![[r#"
|
||||
at little
|
||||
at big
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
126
crates/ide_completion/src/completions/attribute/cfg.rs
Normal file
126
crates/ide_completion/src/completions/attribute/cfg.rs
Normal file
|
@ -0,0 +1,126 @@
|
|||
//! Completion for cfg
|
||||
|
||||
use std::iter;
|
||||
|
||||
use syntax::SyntaxKind;
|
||||
|
||||
use crate::{
|
||||
completions::Completions, context::CompletionContext, item::CompletionKind, CompletionItem,
|
||||
CompletionItemKind,
|
||||
};
|
||||
|
||||
pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext) {
|
||||
let add_completion = |item: &&str| {
|
||||
let mut completion =
|
||||
CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), *item);
|
||||
completion.insert_text(format!(r#""{}""#, item));
|
||||
completion.kind(CompletionItemKind::Attribute);
|
||||
acc.add(completion.build());
|
||||
};
|
||||
|
||||
let previous = iter::successors(ctx.original_token.prev_token(), |t| {
|
||||
(matches!(t.kind(), SyntaxKind::EQ) || t.kind().is_trivia())
|
||||
.then(|| t.prev_token())
|
||||
.flatten()
|
||||
})
|
||||
.find(|t| matches!(t.kind(), SyntaxKind::IDENT));
|
||||
|
||||
match previous.as_ref().map(|p| p.text()) {
|
||||
Some("feature") => {
|
||||
ctx.krate.map(|krate| {
|
||||
krate.features(ctx.db).iter().for_each(|f| {
|
||||
let mut item = CompletionItem::new(
|
||||
CompletionKind::Attribute,
|
||||
ctx.source_range(),
|
||||
f.clone(),
|
||||
);
|
||||
item.insert_text(format!(r#""{}""#, f));
|
||||
|
||||
acc.add(item.build())
|
||||
})
|
||||
});
|
||||
}
|
||||
Some("target_arch") => KNOWN_ARCH.iter().for_each(add_completion),
|
||||
Some("target_env") => KNOWN_ENV.iter().for_each(add_completion),
|
||||
Some("target_os") => KNOWN_OS.iter().for_each(add_completion),
|
||||
Some("target_vendor") => KNOWN_VENDOR.iter().for_each(add_completion),
|
||||
Some("target_endian") => ["little", "big"].iter().for_each(add_completion),
|
||||
Some(name) => {
|
||||
ctx.krate.map(|krate| {
|
||||
krate.cfg(ctx.db).get_cfg_values(&name).iter().for_each(|s| {
|
||||
let mut item = CompletionItem::new(
|
||||
CompletionKind::Attribute,
|
||||
ctx.source_range(),
|
||||
s.as_str(),
|
||||
);
|
||||
item.insert_text(format!(r#""{}""#, s));
|
||||
|
||||
acc.add(item.build());
|
||||
})
|
||||
});
|
||||
}
|
||||
None => {
|
||||
ctx.krate.map(|krate| {
|
||||
krate.cfg(ctx.db).get_cfg_keys().iter().for_each(|s| {
|
||||
let item = CompletionItem::new(
|
||||
CompletionKind::Attribute,
|
||||
ctx.source_range(),
|
||||
s.as_str(),
|
||||
);
|
||||
acc.add(item.build());
|
||||
})
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const KNOWN_ARCH: [&'static str; 19] = [
|
||||
"aarch64",
|
||||
"arm",
|
||||
"avr",
|
||||
"hexagon",
|
||||
"mips",
|
||||
"mips64",
|
||||
"msp430",
|
||||
"nvptx64",
|
||||
"powerpc",
|
||||
"powerpc64",
|
||||
"riscv32",
|
||||
"riscv64",
|
||||
"s390x",
|
||||
"sparc",
|
||||
"sparc64",
|
||||
"wasm32",
|
||||
"wasm64",
|
||||
"x86",
|
||||
"x86_64",
|
||||
];
|
||||
|
||||
const KNOWN_ENV: [&'static str; 7] =
|
||||
["eabihf", "gnu", "gnueabihf", "msvc", "relibc", "sgx", "uclibc"];
|
||||
|
||||
const KNOWN_OS: [&'static str; 20] = [
|
||||
"cuda",
|
||||
"dragonfly",
|
||||
"emscripten",
|
||||
"freebsd",
|
||||
"fuchsia",
|
||||
"haiku",
|
||||
"hermit",
|
||||
"illumos",
|
||||
"l4re",
|
||||
"linux",
|
||||
"netbsd",
|
||||
"none",
|
||||
"openbsd",
|
||||
"psp",
|
||||
"redox",
|
||||
"solaris",
|
||||
"uefi",
|
||||
"unknown",
|
||||
"vxworks",
|
||||
"windows",
|
||||
];
|
||||
|
||||
const KNOWN_VENDOR: [&'static str; 8] =
|
||||
["apple", "fortanix", "nvidia", "pc", "sony", "unknown", "wrs", "uwp"];
|
|
@ -387,6 +387,7 @@ fn project_json_to_crate_graph(
|
|||
cfg_options,
|
||||
env,
|
||||
proc_macro.unwrap_or_default(),
|
||||
Default::default(),
|
||||
),
|
||||
)
|
||||
})
|
||||
|
@ -582,6 +583,7 @@ fn detached_files_to_crate_graph(
|
|||
cfg_options.clone(),
|
||||
Env::default(),
|
||||
Vec::new(),
|
||||
Default::default(),
|
||||
);
|
||||
|
||||
for (name, krate) in public_deps.iter() {
|
||||
|
@ -726,6 +728,7 @@ fn add_target_crate_root(
|
|||
cfg_options,
|
||||
env,
|
||||
proc_macro,
|
||||
pkg.features.clone(),
|
||||
);
|
||||
|
||||
crate_id
|
||||
|
@ -755,6 +758,7 @@ fn sysroot_to_crate_graph(
|
|||
cfg_options.clone(),
|
||||
env,
|
||||
proc_macro,
|
||||
Default::default(),
|
||||
);
|
||||
Some((krate, crate_id))
|
||||
})
|
||||
|
|
Loading…
Reference in a new issue