Fix cfg completions not working

This commit is contained in:
Lukas Wirth 2023-08-29 10:57:24 +02:00
parent ca6ddd8ea3
commit 853f8a21f7
4 changed files with 98 additions and 33 deletions

View file

@ -179,8 +179,8 @@ impl ChangeFixture {
meta.edition, meta.edition,
Some(crate_name.clone().into()), Some(crate_name.clone().into()),
version, version,
meta.cfg, meta.cfg.clone(),
Default::default(), Some(meta.cfg),
meta.env, meta.env,
false, false,
origin, origin,
@ -200,7 +200,7 @@ impl ChangeFixture {
} else if meta.path == "/main.rs" || meta.path == "/lib.rs" { } else if meta.path == "/main.rs" || meta.path == "/lib.rs" {
assert!(default_crate_root.is_none()); assert!(default_crate_root.is_none());
default_crate_root = Some(file_id); default_crate_root = Some(file_id);
default_cfg = meta.cfg; default_cfg.extend(meta.cfg.into_iter());
default_env.extend(meta.env.iter().map(|(x, y)| (x.to_owned(), y.to_owned()))); default_env.extend(meta.env.iter().map(|(x, y)| (x.to_owned(), y.to_owned())));
default_target_data_layout = meta.target_data_layout; default_target_data_layout = meta.target_data_layout;
} }
@ -220,8 +220,8 @@ impl ChangeFixture {
Edition::CURRENT, Edition::CURRENT,
Some(CrateName::new("test").unwrap().into()), Some(CrateName::new("test").unwrap().into()),
None, None,
default_cfg, default_cfg.clone(),
Default::default(), Some(default_cfg),
default_env, default_env,
false, false,
CrateOrigin::Local { repo: None, name: None }, CrateOrigin::Local { repo: None, name: None },

View file

@ -86,6 +86,32 @@ impl CfgOptions {
} }
} }
impl Extend<CfgAtom> for CfgOptions {
fn extend<T: IntoIterator<Item = CfgAtom>>(&mut self, iter: T) {
iter.into_iter().for_each(|cfg_flag| _ = self.enabled.insert(cfg_flag));
}
}
impl IntoIterator for CfgOptions {
type Item = <FxHashSet<CfgAtom> as IntoIterator>::Item;
type IntoIter = <FxHashSet<CfgAtom> as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
<FxHashSet<CfgAtom> as IntoIterator>::into_iter(self.enabled)
}
}
impl<'a> IntoIterator for &'a CfgOptions {
type Item = <&'a FxHashSet<CfgAtom> as IntoIterator>::Item;
type IntoIter = <&'a FxHashSet<CfgAtom> as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
<&FxHashSet<CfgAtom> as IntoIterator>::into_iter(&self.enabled)
}
}
#[derive(Default, Clone, Debug, PartialEq, Eq)] #[derive(Default, Clone, Debug, PartialEq, Eq)]
pub struct CfgDiff { pub struct CfgDiff {
// Invariants: No duplicates, no atom that's both in `enable` and `disable`. // Invariants: No duplicates, no atom that's both in `enable` and `disable`.

View file

@ -1,10 +1,8 @@
//! Completion for cfg //! Completion for cfg
use std::iter;
use ide_db::SymbolKind; use ide_db::SymbolKind;
use itertools::Itertools; use itertools::Itertools;
use syntax::SyntaxKind; use syntax::{algo, ast::Ident, AstToken, Direction, NodeOrToken, SyntaxKind};
use crate::{completions::Completions, context::CompletionContext, CompletionItem}; use crate::{completions::Completions, context::CompletionContext, CompletionItem};
@ -15,31 +13,44 @@ pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext<'_>) {
acc.add(completion.build(ctx.db)); acc.add(completion.build(ctx.db));
}; };
let previous = iter::successors(ctx.original_token.prev_token(), |t| { // FIXME: Move this into context/analysis.rs
(matches!(t.kind(), SyntaxKind::EQ) || t.kind().is_trivia()) let previous = ctx
.then(|| t.prev_token()) .original_token
.flatten() .prev_token()
}) .and_then(|it| {
.find(|t| matches!(t.kind(), SyntaxKind::IDENT)); if matches!(it.kind(), SyntaxKind::EQ) {
Some(it.into())
} else {
algo::non_trivia_sibling(it.into(), Direction::Prev)
}
})
.filter(|t| matches!(t.kind(), SyntaxKind::EQ))
.and_then(|it| algo::non_trivia_sibling(it.prev_sibling_or_token()?, Direction::Prev))
.map(|it| match it {
NodeOrToken::Node(_) => None,
NodeOrToken::Token(t) => Ident::cast(t),
});
match previous {
Some(None) => (),
Some(Some(p)) => match p.text() {
"target_arch" => KNOWN_ARCH.iter().copied().for_each(add_completion),
"target_env" => KNOWN_ENV.iter().copied().for_each(add_completion),
"target_os" => KNOWN_OS.iter().copied().for_each(add_completion),
"target_vendor" => KNOWN_VENDOR.iter().copied().for_each(add_completion),
"target_endian" => ["little", "big"].into_iter().for_each(add_completion),
name => ctx.krate.potential_cfg(ctx.db).get_cfg_values(name).cloned().for_each(|s| {
let insert_text = format!(r#""{s}""#);
let mut item = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), s);
item.insert_text(insert_text);
match previous.as_ref().map(|p| p.text()) { acc.add(item.build(ctx.db));
Some("target_arch") => KNOWN_ARCH.iter().copied().for_each(add_completion), }),
Some("target_env") => KNOWN_ENV.iter().copied().for_each(add_completion), },
Some("target_os") => KNOWN_OS.iter().copied().for_each(add_completion),
Some("target_vendor") => KNOWN_VENDOR.iter().copied().for_each(add_completion),
Some("target_endian") => ["little", "big"].into_iter().for_each(add_completion),
Some(name) => ctx.krate.potential_cfg(ctx.db).get_cfg_values(name).cloned().for_each(|s| {
let insert_text = format!(r#""{s}""#);
let mut item = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), s);
item.insert_text(insert_text);
acc.add(item.build(ctx.db));
}),
None => ctx.krate.potential_cfg(ctx.db).get_cfg_keys().cloned().unique().for_each(|s| { None => ctx.krate.potential_cfg(ctx.db).get_cfg_keys().cloned().unique().for_each(|s| {
let item = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), s); let item = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), s);
acc.add(item.build(ctx.db)); acc.add(item.build(ctx.db));
}), }),
}; }
} }
const KNOWN_ARCH: [&str; 20] = [ const KNOWN_ARCH: [&str; 20] = [

View file

@ -66,11 +66,6 @@ struct Foo;
) )
} }
#[test]
fn inside_nested_attr() {
check(r#"#[cfg($0)]"#, expect![[]])
}
#[test] #[test]
fn with_existing_attr() { fn with_existing_attr() {
check( check(
@ -635,6 +630,32 @@ struct Foo;
mod cfg { mod cfg {
use super::*; use super::*;
#[test]
fn inside_cfg() {
check(
r#"
//- /main.rs cfg:test,dbg=false,opt_level=2
#[cfg($0)]
"#,
expect![[r#"
ba dbg
ba opt_level
ba test
"#]],
);
check(
r#"
//- /main.rs cfg:test,dbg=false,opt_level=2
#[cfg(b$0)]
"#,
expect![[r#"
ba dbg
ba opt_level
ba test
"#]],
);
}
#[test] #[test]
fn cfg_target_endian() { fn cfg_target_endian() {
check( check(
@ -644,6 +665,13 @@ mod cfg {
ba little ba little
"#]], "#]],
); );
check(
r#"#[cfg(target_endian = b$0"#,
expect![[r#"
ba big
ba little
"#]],
);
} }
} }