mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-02 22:54:58 +00:00
Speculatively expand attributes in completions
This commit is contained in:
parent
c3eb646487
commit
2b907652ee
7 changed files with 238 additions and 66 deletions
|
@ -175,48 +175,90 @@ impl<'a> CompletionContext<'a> {
|
|||
incomplete_let: false,
|
||||
no_completion_required: false,
|
||||
};
|
||||
|
||||
let mut original_file = original_file.syntax().clone();
|
||||
let mut speculative_file = file_with_fake_ident.syntax().clone();
|
||||
let mut offset = position.offset;
|
||||
let mut fake_ident_token = fake_ident_token;
|
||||
|
||||
// Are we inside a macro call?
|
||||
while let (Some(actual_macro_call), Some(macro_call_with_fake_ident)) = (
|
||||
find_node_at_offset::<ast::MacroCall>(&original_file, offset),
|
||||
find_node_at_offset::<ast::MacroCall>(&speculative_file, offset),
|
||||
) {
|
||||
if actual_macro_call.path().as_ref().map(|s| s.syntax().text())
|
||||
!= macro_call_with_fake_ident.path().as_ref().map(|s| s.syntax().text())
|
||||
{
|
||||
break;
|
||||
}
|
||||
let speculative_args = match macro_call_with_fake_ident.token_tree() {
|
||||
Some(tt) => tt,
|
||||
None => break,
|
||||
};
|
||||
if let (Some(actual_expansion), Some(speculative_expansion)) = (
|
||||
ctx.sema.expand(&actual_macro_call),
|
||||
ctx.sema.speculative_expand(
|
||||
&actual_macro_call,
|
||||
&speculative_args,
|
||||
fake_ident_token,
|
||||
),
|
||||
ctx.expand_and_fill(
|
||||
original_file.syntax().clone(),
|
||||
file_with_fake_ident.syntax().clone(),
|
||||
position.offset,
|
||||
fake_ident_token,
|
||||
);
|
||||
Some(ctx)
|
||||
}
|
||||
fn expand_and_fill(
|
||||
&mut self,
|
||||
mut original_file: SyntaxNode,
|
||||
mut speculative_file: SyntaxNode,
|
||||
mut offset: TextSize,
|
||||
mut fake_ident_token: SyntaxToken,
|
||||
) {
|
||||
loop {
|
||||
if let (Some(actual_item), Some(item_with_fake_ident)) = (
|
||||
find_node_at_offset::<ast::Item>(&original_file, offset),
|
||||
find_node_at_offset::<ast::Item>(&speculative_file, offset),
|
||||
) {
|
||||
let new_offset = speculative_expansion.1.text_range().start();
|
||||
if new_offset > actual_expansion.text_range().end() {
|
||||
match (
|
||||
self.sema.expand_attr_macro(&actual_item),
|
||||
self.sema.speculative_expand_attr_macro(
|
||||
&actual_item,
|
||||
&item_with_fake_ident,
|
||||
fake_ident_token.clone(),
|
||||
),
|
||||
) {
|
||||
(Some(actual_expansion), Some(speculative_expansion)) => {
|
||||
let new_offset = speculative_expansion.1.text_range().start();
|
||||
if new_offset > actual_expansion.text_range().end() {
|
||||
break;
|
||||
}
|
||||
original_file = actual_expansion;
|
||||
speculative_file = speculative_expansion.0;
|
||||
fake_ident_token = speculative_expansion.1;
|
||||
offset = new_offset;
|
||||
continue;
|
||||
}
|
||||
(None, None) => (),
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
|
||||
if let (Some(actual_macro_call), Some(macro_call_with_fake_ident)) = (
|
||||
find_node_at_offset::<ast::MacroCall>(&original_file, offset),
|
||||
find_node_at_offset::<ast::MacroCall>(&speculative_file, offset),
|
||||
) {
|
||||
let mac_call_path0 = actual_macro_call.path().as_ref().map(|s| s.syntax().text());
|
||||
let mac_call_path1 =
|
||||
macro_call_with_fake_ident.path().as_ref().map(|s| s.syntax().text());
|
||||
if mac_call_path0 != mac_call_path1 {
|
||||
break;
|
||||
}
|
||||
let speculative_args = match macro_call_with_fake_ident.token_tree() {
|
||||
Some(tt) => tt,
|
||||
None => break,
|
||||
};
|
||||
|
||||
if let (Some(actual_expansion), Some(speculative_expansion)) = (
|
||||
self.sema.expand(&actual_macro_call),
|
||||
self.sema.speculative_expand(
|
||||
&actual_macro_call,
|
||||
&speculative_args,
|
||||
fake_ident_token,
|
||||
),
|
||||
) {
|
||||
let new_offset = speculative_expansion.1.text_range().start();
|
||||
if new_offset > actual_expansion.text_range().end() {
|
||||
break;
|
||||
}
|
||||
original_file = actual_expansion;
|
||||
speculative_file = speculative_expansion.0;
|
||||
fake_ident_token = speculative_expansion.1;
|
||||
offset = new_offset;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
original_file = actual_expansion;
|
||||
speculative_file = speculative_expansion.0;
|
||||
fake_ident_token = speculative_expansion.1;
|
||||
offset = new_offset;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ctx.fill(&original_file, speculative_file, offset);
|
||||
Some(ctx)
|
||||
|
||||
self.fill(&original_file, speculative_file, offset);
|
||||
}
|
||||
|
||||
/// Checks whether completions in that particular case don't make much sense.
|
||||
|
|
|
@ -15,6 +15,7 @@ mod item_list;
|
|||
mod item;
|
||||
mod pattern;
|
||||
mod predicate;
|
||||
mod proc_macros;
|
||||
mod record;
|
||||
mod sourcegen;
|
||||
mod type_pos;
|
||||
|
@ -23,7 +24,7 @@ mod visibility;
|
|||
|
||||
use std::mem;
|
||||
|
||||
use hir::{PrefixKind, Semantics};
|
||||
use hir::{db::DefDatabase, PrefixKind, Semantics};
|
||||
use ide_db::{
|
||||
base_db::{fixture::ChangeFixture, FileLoader, FilePosition},
|
||||
helpers::{
|
||||
|
@ -96,6 +97,7 @@ fn completion_list_with_config(config: CompletionConfig, ra_fixture: &str) -> St
|
|||
pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
|
||||
let change_fixture = ChangeFixture::parse(ra_fixture);
|
||||
let mut database = RootDatabase::default();
|
||||
database.set_enable_proc_attr_macros(true);
|
||||
database.apply_change(change_fixture.change);
|
||||
let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
|
||||
let offset = range_or_offset.expect_offset();
|
||||
|
|
75
crates/ide_completion/src/tests/proc_macros.rs
Normal file
75
crates/ide_completion/src/tests/proc_macros.rs
Normal file
|
@ -0,0 +1,75 @@
|
|||
//! Completion tests for expressions.
|
||||
use expect_test::{expect, Expect};
|
||||
|
||||
use crate::tests::completion_list;
|
||||
|
||||
fn check(ra_fixture: &str, expect: Expect) {
|
||||
let actual = completion_list(ra_fixture);
|
||||
expect.assert_eq(&actual)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn complete_dot_in_attr() {
|
||||
check(
|
||||
r#"
|
||||
//- proc_macros: identity
|
||||
pub struct Foo;
|
||||
impl Foo {
|
||||
fn foo(&self) {}
|
||||
}
|
||||
|
||||
#[proc_macros::identity]
|
||||
fn main() {
|
||||
Foo.$0
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
me foo() fn(&self)
|
||||
sn ref &expr
|
||||
sn refm &mut expr
|
||||
sn match match expr {}
|
||||
sn box Box::new(expr)
|
||||
sn ok Ok(expr)
|
||||
sn err Err(expr)
|
||||
sn some Some(expr)
|
||||
sn dbg dbg!(expr)
|
||||
sn dbgr dbg!(&expr)
|
||||
sn call function(expr)
|
||||
sn let let
|
||||
sn letm let mut
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn complete_dot_in_attr2() {
|
||||
check(
|
||||
r#"
|
||||
//- proc_macros: identity
|
||||
pub struct Foo;
|
||||
impl Foo {
|
||||
fn foo(&self) {}
|
||||
}
|
||||
|
||||
#[proc_macros::identity]
|
||||
fn main() {
|
||||
Foo.f$0
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
me foo() fn(&self)
|
||||
sn ref &expr
|
||||
sn refm &mut expr
|
||||
sn match match expr {}
|
||||
sn box Box::new(expr)
|
||||
sn ok Ok(expr)
|
||||
sn err Err(expr)
|
||||
sn some Some(expr)
|
||||
sn dbg dbg!(expr)
|
||||
sn dbgr dbg!(&expr)
|
||||
sn call function(expr)
|
||||
sn let let
|
||||
sn letm let mut
|
||||
"#]],
|
||||
)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue