Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions crates/ide-completion/src/completions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ pub(crate) mod format_string;
pub(crate) mod item_list;
pub(crate) mod keyword;
pub(crate) mod lifetime;
pub(crate) mod macro_def;
pub(crate) mod mod_;
pub(crate) mod pattern;
pub(crate) mod postfix;
Expand Down
31 changes: 31 additions & 0 deletions crates/ide-completion/src/completions/macro_def.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//! Completion for macro meta-variable segments

use ide_db::SymbolKind;

use crate::{CompletionItem, Completions, context::CompletionContext};

pub(crate) fn complete_macro_segment(acc: &mut Completions, ctx: &CompletionContext<'_>) {
for &label in MACRO_SEGMENTS {
let item =
CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), label, ctx.edition);
item.add_to(acc, ctx.db);
}
}

const MACRO_SEGMENTS: &[&str] = &[
"ident",
"block",
"stmt",
"expr",
"pat",
"ty",
"lifetime",
"literal",
"path",
"meta",
"tt",
"item",
"vis",
"expr_2021",
"pat_param",
];
5 changes: 3 additions & 2 deletions crates/ide-completion/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use hir::{
};
use ide_db::{
FilePosition, FxHashMap, FxHashSet, RootDatabase, famous_defs::FamousDefs,
helpers::is_editable_crate,
helpers::is_editable_crate, syntax_helpers::node_ext::is_in_macro_matcher,
};
use syntax::{
AstNode, Edition, SmolStr,
Expand Down Expand Up @@ -385,6 +385,7 @@ pub(crate) enum CompletionAnalysis<'db> {
fake_attribute_under_caret: Option<ast::Attr>,
extern_crate: Option<ast::ExternCrate>,
},
MacroSegment,
}

/// Information about the field or method access we are completing.
Expand Down Expand Up @@ -729,7 +730,7 @@ impl<'db> CompletionContext<'db> {
let prev_token = original_token.prev_token()?;

// only has a single colon
if prev_token.kind() != T![:] {
if prev_token.kind() != T![:] && !is_in_macro_matcher(&original_token) {
return None;
}

Expand Down
17 changes: 16 additions & 1 deletion crates/ide-completion/src/context/analysis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::iter;
use base_db::salsa;
use hir::{ExpandResult, InFile, Semantics, Type, TypeInfo, Variant};
use ide_db::{RootDatabase, active_parameter::ActiveParameter};
use itertools::Either;
use itertools::{Either, Itertools};
use stdx::always;
use syntax::{
AstNode, AstToken, Direction, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken,
Expand Down Expand Up @@ -512,6 +512,21 @@ fn analyze<'db>(
colon_prefix,
extern_crate: p.ancestors().find_map(ast::ExternCrate::cast),
}
} else if p.kind() == SyntaxKind::TOKEN_TREE
&& p.ancestors().any(|it| ast::Macro::can_cast(it.kind()))
{
if let Some([_ident, colon, _name, dollar]) = fake_ident_token
.siblings_with_tokens(Direction::Prev)
.filter(|it| !it.kind().is_trivia())
.take(4)
.collect_array()
&& dollar.kind() == T![$]
&& colon.kind() == T![:]
{
CompletionAnalysis::MacroSegment
} else {
return None;
}
} else {
return None;
}
Expand Down
3 changes: 3 additions & 0 deletions crates/ide-completion/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,9 @@ pub fn completions(
extern_crate.as_ref(),
);
}
CompletionAnalysis::MacroSegment => {
completions::macro_def::complete_macro_segment(acc, ctx);
}
CompletionAnalysis::UnexpandedAttrTT { .. } | CompletionAnalysis::String { .. } => (),
}
}
Expand Down
229 changes: 229 additions & 0 deletions crates/ide-completion/src/tests/special.rs
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,226 @@ fn foo() {}
);
}

#[test]
fn completes_macro_segment() {
check(
r#"
macro_rules! foo {
($x:e$0) => ();
}
"#,
expect![[r#"
ba block
ba expr
ba expr_2021
ba ident
ba item
ba lifetime
ba literal
ba meta
ba pat
ba pat_param
ba path
ba stmt
ba tt
ba ty
ba vis
"#]],
);

check(
r#"
macro_rules! foo {
($x:$0) => ();
}
"#,
expect![[r#"
ba block
ba expr
ba expr_2021
ba ident
ba item
ba lifetime
ba literal
ba meta
ba pat
ba pat_param
ba path
ba stmt
ba tt
ba ty
ba vis
"#]],
);

check(
r#"
macro_rules! foo {
($($x:$0)*) => ();
}
"#,
expect![[r#"
ba block
ba expr
ba expr_2021
ba ident
ba item
ba lifetime
ba literal
ba meta
ba pat
ba pat_param
ba path
ba stmt
ba tt
ba ty
ba vis
"#]],
);

check(
r#"
macro foo {
($($x:$0)*) => ();
}
"#,
expect![[r#"
ba block
ba expr
ba expr_2021
ba ident
ba item
ba lifetime
ba literal
ba meta
ba pat
ba pat_param
ba path
ba stmt
ba tt
ba ty
ba vis
"#]],
);

check(
r#"
macro foo($($x:$0)*) {
xxx;
}
"#,
expect![[r#"
ba block
ba expr
ba expr_2021
ba ident
ba item
ba lifetime
ba literal
ba meta
ba pat
ba pat_param
ba path
ba stmt
ba tt
ba ty
ba vis
"#]],
);

check_edit(
"expr",
r#"
macro foo($($x:$0)*) {
xxx;
}
"#,
r#"
macro foo($($x:expr)*) {
xxx;
}
"#,
);

check(
r#"
macro_rules! foo {
($fn : e$0) => ();
}
"#,
expect![[r#"
ba block
ba expr
ba expr_2021
ba ident
ba item
ba lifetime
ba literal
ba meta
ba pat
ba pat_param
ba path
ba stmt
ba tt
ba ty
ba vis
"#]],
);

check_edit(
"expr",
r#"
macro foo($($x:ex$0)*) {
xxx;
}
"#,
r#"
macro foo($($x:expr)*) {
xxx;
}
"#,
);
}

#[test]
fn completes_in_macro_body() {
check(
r#"
macro_rules! foo {
($x:expr) => ($y:$0);
}
"#,
expect![[r#""#]],
);

check(
r#"
macro_rules! foo {
($x:expr) => ({$y:$0});
}
"#,
expect![[r#""#]],
);

check(
r#"
macro foo {
($x:expr) => ($y:$0);
}
"#,
expect![[r#""#]],
);

check(
r#"
macro foo($x:expr) {
$y:$0
}
"#,
expect![[r#""#]],
);
}

#[test]
fn function_mod_share_name() {
check_no_kw(
Expand Down Expand Up @@ -942,6 +1162,15 @@ fn foo { crate::$0 }
check_with_trigger_character(
r#"
fn foo { crate:$0 }
"#,
Some(':'),
expect![""],
);

check_with_trigger_character(
r#"
macro_rules! bar { ($($x:tt)*) => ($($x)*); }
fn foo { bar!(crate:$0) }
"#,
Some(':'),
expect![""],
Expand Down
34 changes: 33 additions & 1 deletion crates/ide-db/src/syntax_helpers/node_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ use itertools::Itertools;
use parser::T;
use span::Edition;
use syntax::{
AstNode, AstToken, Preorder, RustLanguage, WalkEvent,
AstNode, AstToken, Direction, Preorder, RustLanguage, SyntaxToken, WalkEvent,
algo::non_trivia_sibling,
ast::{self, HasLoopBody, MacroCall, PathSegmentKind, VisibilityKind},
syntax_editor::Element,
};

pub fn expr_as_name_ref(expr: &ast::Expr) -> Option<ast::NameRef> {
Expand Down Expand Up @@ -503,3 +505,33 @@ pub fn macro_call_for_string_token(string: &ast::String) -> Option<MacroCall> {
let macro_call = string.syntax().parent_ancestors().find_map(ast::MacroCall::cast)?;
Some(macro_call)
}

pub fn is_in_macro_matcher(token: &SyntaxToken) -> bool {
let Some(macro_def) = token.parent_ancestors().find_map(ast::Macro::cast) else {
return false;
};
let range = token.text_range();
let Some(body) = (match macro_def {
ast::Macro::MacroDef(macro_def) => {
if let Some(args) = macro_def.args() {
return args.syntax().text_range().contains_range(range);
}
macro_def.body()
}
ast::Macro::MacroRules(macro_rules) => macro_rules.token_tree(),
}) else {
return false;
};
if !body.syntax().text_range().contains_range(range) {
return false;
}
body.token_trees_and_tokens().filter_map(|tt| tt.into_node()).any(|tt| {
let Some(next) = non_trivia_sibling(tt.syntax().syntax_element(), Direction::Next) else {
return false;
};
let Some(next_next) = next.next_sibling_or_token() else { return false };
next.kind() == T![=]
&& next_next.kind() == T![>]
&& tt.syntax().text_range().contains_range(range)
})
}
Loading