mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-12-27 16:07:46 +00:00
Add macro segment completion
Example
---
```rust
macro_rules! foo {
($($x:$0)*) => ();
}
```
**Completion items**:
```text
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
```
This commit is contained in:
parent
075b3beb5d
commit
d2cc89ffc7
@ -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;
|
||||
|
||||
31
crates/ide-completion/src/completions/macro_def.rs
Normal file
31
crates/ide-completion/src/completions/macro_def.rs
Normal file
@ -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",
|
||||
];
|
||||
@ -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 itertools::Either;
|
||||
use syntax::{
|
||||
@ -389,6 +389,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.
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@ use hir::{ExpandResult, InFile, Semantics, Type, TypeInfo, Variant};
|
||||
use ide_db::{
|
||||
RootDatabase, active_parameter::ActiveParameter, syntax_helpers::node_ext::find_loops,
|
||||
};
|
||||
use itertools::Either;
|
||||
use itertools::{Either, Itertools};
|
||||
use stdx::always;
|
||||
use syntax::{
|
||||
AstNode, AstToken, Direction, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken,
|
||||
@ -510,6 +510,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;
|
||||
}
|
||||
|
||||
@ -263,6 +263,9 @@ pub fn completions(
|
||||
extern_crate.as_ref(),
|
||||
);
|
||||
}
|
||||
CompletionAnalysis::MacroSegment => {
|
||||
completions::macro_def::complete_macro_segment(acc, ctx);
|
||||
}
|
||||
CompletionAnalysis::UnexpandedAttrTT { .. } | CompletionAnalysis::String { .. } => (),
|
||||
}
|
||||
}
|
||||
|
||||
@ -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(
|
||||
@ -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![""],
|
||||
|
||||
@ -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> {
|
||||
@ -542,3 +544,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…
x
Reference in New Issue
Block a user