mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-27 05:23:24 +00:00
fix: Insert whitespace into trait-impl completions when coming from macros
This commit is contained in:
parent
6f006b7524
commit
86d1d9067e
6 changed files with 99 additions and 83 deletions
|
@ -939,7 +939,6 @@ struct Foo(usize);
|
||||||
|
|
||||||
impl FooB for Foo {
|
impl FooB for Foo {
|
||||||
$0fn foo< 'lt>(& 'lt self){}
|
$0fn foo< 'lt>(& 'lt self){}
|
||||||
|
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
|
|
|
@ -32,10 +32,12 @@
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
use hir::{self, HasAttrs};
|
use hir::{self, HasAttrs};
|
||||||
use ide_db::{path_transform::PathTransform, traits::get_missing_assoc_items, SymbolKind};
|
use ide_db::{
|
||||||
|
path_transform::PathTransform, syntax_helpers::insert_whitespace_into_node,
|
||||||
|
traits::get_missing_assoc_items, SymbolKind,
|
||||||
|
};
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{self, edit_in_place::AttrsOwnerEdit},
|
ast::{self, edit_in_place::AttrsOwnerEdit},
|
||||||
display::function_declaration,
|
|
||||||
AstNode, SyntaxElement, SyntaxKind, SyntaxNode, TextRange, T,
|
AstNode, SyntaxElement, SyntaxKind, SyntaxNode, TextRange, T,
|
||||||
};
|
};
|
||||||
use text_edit::TextEdit;
|
use text_edit::TextEdit;
|
||||||
|
@ -179,7 +181,7 @@ fn add_function_impl(
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let function_decl = function_declaration(&transformed_fn);
|
let function_decl = function_declaration(&transformed_fn, source.file_id.is_macro());
|
||||||
match ctx.config.snippet_cap {
|
match ctx.config.snippet_cap {
|
||||||
Some(cap) => {
|
Some(cap) => {
|
||||||
let snippet = format!("{} {{\n $0\n}}", function_decl);
|
let snippet = format!("{} {{\n $0\n}}", function_decl);
|
||||||
|
@ -260,7 +262,7 @@ fn add_const_impl(
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let label = make_const_compl_syntax(&transformed_const);
|
let label = make_const_compl_syntax(&transformed_const, source.file_id.is_macro());
|
||||||
let replacement = format!("{} ", label);
|
let replacement = format!("{} ", label);
|
||||||
|
|
||||||
let mut item = CompletionItem::new(SymbolKind::Const, replacement_range, label);
|
let mut item = CompletionItem::new(SymbolKind::Const, replacement_range, label);
|
||||||
|
@ -283,17 +285,18 @@ fn add_const_impl(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_const_compl_syntax(const_: &ast::Const) -> String {
|
fn make_const_compl_syntax(const_: &ast::Const, needs_whitespace: bool) -> String {
|
||||||
const_.remove_attrs_and_docs();
|
const_.remove_attrs_and_docs();
|
||||||
|
let const_ = if needs_whitespace {
|
||||||
|
insert_whitespace_into_node::insert_ws_into(const_.syntax().clone())
|
||||||
|
} else {
|
||||||
|
const_.syntax().clone()
|
||||||
|
};
|
||||||
|
|
||||||
let const_start = const_.syntax().text_range().start();
|
let start = const_.text_range().start();
|
||||||
let const_end = const_.syntax().text_range().end();
|
let const_end = const_.text_range().end();
|
||||||
|
|
||||||
let start =
|
|
||||||
const_.syntax().first_child_or_token().map_or(const_start, |f| f.text_range().start());
|
|
||||||
|
|
||||||
let end = const_
|
let end = const_
|
||||||
.syntax()
|
|
||||||
.children_with_tokens()
|
.children_with_tokens()
|
||||||
.find(|s| s.kind() == T![;] || s.kind() == T![=])
|
.find(|s| s.kind() == T![;] || s.kind() == T![=])
|
||||||
.map_or(const_end, |f| f.text_range().start());
|
.map_or(const_end, |f| f.text_range().start());
|
||||||
|
@ -301,11 +304,36 @@ fn make_const_compl_syntax(const_: &ast::Const) -> String {
|
||||||
let len = end - start;
|
let len = end - start;
|
||||||
let range = TextRange::new(0.into(), len);
|
let range = TextRange::new(0.into(), len);
|
||||||
|
|
||||||
let syntax = const_.syntax().text().slice(range).to_string();
|
let syntax = const_.text().slice(range).to_string();
|
||||||
|
|
||||||
format!("{} =", syntax.trim_end())
|
format!("{} =", syntax.trim_end())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn function_declaration(node: &ast::Fn, needs_whitespace: bool) -> String {
|
||||||
|
node.remove_attrs_and_docs();
|
||||||
|
|
||||||
|
let node = if needs_whitespace {
|
||||||
|
insert_whitespace_into_node::insert_ws_into(node.syntax().clone())
|
||||||
|
} else {
|
||||||
|
node.syntax().clone()
|
||||||
|
};
|
||||||
|
|
||||||
|
let start = node.text_range().start();
|
||||||
|
let end = node.text_range().end();
|
||||||
|
|
||||||
|
let end = node
|
||||||
|
.last_child_or_token()
|
||||||
|
.filter(|s| s.kind() == T![;] || s.kind() == SyntaxKind::BLOCK_EXPR)
|
||||||
|
.map_or(end, |f| f.text_range().start());
|
||||||
|
|
||||||
|
let len = end - start;
|
||||||
|
let range = TextRange::new(0.into(), len);
|
||||||
|
|
||||||
|
let syntax = node.text().slice(range).to_string();
|
||||||
|
|
||||||
|
syntax.trim_end().to_owned()
|
||||||
|
}
|
||||||
|
|
||||||
fn replacement_range(ctx: &CompletionContext, item: &SyntaxNode) -> TextRange {
|
fn replacement_range(ctx: &CompletionContext, item: &SyntaxNode) -> TextRange {
|
||||||
let first_child = item
|
let first_child = item
|
||||||
.children_with_tokens()
|
.children_with_tokens()
|
||||||
|
@ -655,8 +683,7 @@ trait Test {
|
||||||
struct T;
|
struct T;
|
||||||
|
|
||||||
impl Test for T {
|
impl Test for T {
|
||||||
fn foo<T>()
|
fn foo<T>() where T: Into<String> {
|
||||||
where T: Into<String> {
|
|
||||||
$0
|
$0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -992,7 +1019,7 @@ trait SomeTrait<T> {}
|
||||||
|
|
||||||
trait Foo<T> {
|
trait Foo<T> {
|
||||||
fn function()
|
fn function()
|
||||||
where Self: SomeTrait<T>;
|
where Self: SomeTrait<T>;
|
||||||
}
|
}
|
||||||
struct Bar;
|
struct Bar;
|
||||||
|
|
||||||
|
@ -1005,13 +1032,13 @@ trait SomeTrait<T> {}
|
||||||
|
|
||||||
trait Foo<T> {
|
trait Foo<T> {
|
||||||
fn function()
|
fn function()
|
||||||
where Self: SomeTrait<T>;
|
where Self: SomeTrait<T>;
|
||||||
}
|
}
|
||||||
struct Bar;
|
struct Bar;
|
||||||
|
|
||||||
impl Foo<u32> for Bar {
|
impl Foo<u32> for Bar {
|
||||||
fn function()
|
fn function()
|
||||||
where Self: SomeTrait<u32> {
|
where Self: SomeTrait<u32> {
|
||||||
$0
|
$0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1052,4 +1079,51 @@ impl Tr for () {
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fixes_up_macro_generated() {
|
||||||
|
check_edit(
|
||||||
|
"fn foo",
|
||||||
|
r#"
|
||||||
|
macro_rules! noop {
|
||||||
|
($($item: item)*) => {
|
||||||
|
$($item)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
noop! {
|
||||||
|
trait Foo {
|
||||||
|
fn foo(&mut self, bar: i64, baz: &mut u32) -> Result<(), u32>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Test;
|
||||||
|
|
||||||
|
impl Foo for Test {
|
||||||
|
$0
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
macro_rules! noop {
|
||||||
|
($($item: item)*) => {
|
||||||
|
$($item)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
noop! {
|
||||||
|
trait Foo {
|
||||||
|
fn foo(&mut self, bar: i64, baz: &mut u32) -> Result<(), u32>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Test;
|
||||||
|
|
||||||
|
impl Foo for Test {
|
||||||
|
fn foo(&mut self,bar:i64,baz: &mut u32) -> Result<(),u32> {
|
||||||
|
$0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -114,6 +114,10 @@ pub fn insert_ws_into(syn: SyntaxNode) -> SyntaxNode {
|
||||||
ted::insert(pos, insert);
|
ted::insert(pos, insert);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(it) = syn.last_token().filter(|it| it.kind() == SyntaxKind::WHITESPACE) {
|
||||||
|
ted::remove(it);
|
||||||
|
}
|
||||||
|
|
||||||
syn
|
syn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -250,8 +250,7 @@ fn main() {
|
||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
bar
|
bar
|
||||||
for _ in 0..42{}
|
for _ in 0..42{}"#]],
|
||||||
"#]],
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -273,7 +272,6 @@ f$0oo!();
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
foo
|
foo
|
||||||
fn b(){}
|
fn b(){}
|
||||||
|
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -297,8 +295,7 @@ f$0oo!();
|
||||||
fn some_thing() -> u32 {
|
fn some_thing() -> u32 {
|
||||||
let a = 0;
|
let a = 0;
|
||||||
a+10
|
a+10
|
||||||
}
|
}"#]],
|
||||||
"#]],
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -359,8 +356,7 @@ fn main() {
|
||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
match_ast
|
match_ast
|
||||||
{}
|
{}"#]],
|
||||||
"#]],
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -421,8 +417,7 @@ fn main() {
|
||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
foo
|
foo
|
||||||
fn f<T>(_: &dyn ::std::marker::Copy){}
|
fn f<T>(_: &dyn ::std::marker::Copy){}"#]],
|
||||||
"#]],
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -440,7 +435,6 @@ struct Foo {}
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
Clone
|
Clone
|
||||||
impl < >core::clone::Clone for Foo< >{}
|
impl < >core::clone::Clone for Foo< >{}
|
||||||
|
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -458,7 +452,6 @@ struct Foo {}
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
Copy
|
Copy
|
||||||
impl < >core::marker::Copy for Foo< >{}
|
impl < >core::marker::Copy for Foo< >{}
|
||||||
|
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -475,7 +468,6 @@ struct Foo {}
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
Copy
|
Copy
|
||||||
impl < >core::marker::Copy for Foo< >{}
|
impl < >core::marker::Copy for Foo< >{}
|
||||||
|
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
check(
|
check(
|
||||||
|
@ -488,7 +480,6 @@ struct Foo {}
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
Clone
|
Clone
|
||||||
impl < >core::clone::Clone for Foo< >{}
|
impl < >core::clone::Clone for Foo< >{}
|
||||||
|
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,51 +0,0 @@
|
||||||
//! This module contains utilities for rendering syntax nodes into a string representing their signature.
|
|
||||||
|
|
||||||
use crate::ast::{self, HasGenericParams, HasName};
|
|
||||||
|
|
||||||
use ast::HasVisibility;
|
|
||||||
use stdx::format_to;
|
|
||||||
|
|
||||||
pub fn function_declaration(node: &ast::Fn) -> String {
|
|
||||||
let mut buf = String::new();
|
|
||||||
if let Some(vis) = node.visibility() {
|
|
||||||
format_to!(buf, "{} ", vis);
|
|
||||||
}
|
|
||||||
if node.async_token().is_some() {
|
|
||||||
format_to!(buf, "async ");
|
|
||||||
}
|
|
||||||
if node.const_token().is_some() {
|
|
||||||
format_to!(buf, "const ");
|
|
||||||
}
|
|
||||||
if node.unsafe_token().is_some() {
|
|
||||||
format_to!(buf, "unsafe ");
|
|
||||||
}
|
|
||||||
if let Some(abi) = node.abi() {
|
|
||||||
// Keyword `extern` is included in the string.
|
|
||||||
format_to!(buf, "{} ", abi);
|
|
||||||
}
|
|
||||||
if let Some(name) = node.name() {
|
|
||||||
format_to!(buf, "fn {}", name);
|
|
||||||
}
|
|
||||||
if let Some(type_params) = node.generic_param_list() {
|
|
||||||
format_to!(buf, "{}", type_params);
|
|
||||||
}
|
|
||||||
if let Some(param_list) = node.param_list() {
|
|
||||||
let params: Vec<String> = param_list
|
|
||||||
.self_param()
|
|
||||||
.into_iter()
|
|
||||||
.map(|self_param| self_param.to_string())
|
|
||||||
.chain(param_list.params().map(|param| param.to_string()))
|
|
||||||
.collect();
|
|
||||||
// Useful to inline parameters
|
|
||||||
format_to!(buf, "({})", params.join(", "));
|
|
||||||
}
|
|
||||||
if let Some(ret_type) = node.ret_type() {
|
|
||||||
if ret_type.ty().is_some() {
|
|
||||||
format_to!(buf, " {}", ret_type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(where_clause) = node.where_clause() {
|
|
||||||
format_to!(buf, "\n{}", where_clause);
|
|
||||||
}
|
|
||||||
buf
|
|
||||||
}
|
|
|
@ -33,7 +33,6 @@ mod token_text;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
pub mod display;
|
|
||||||
pub mod algo;
|
pub mod algo;
|
||||||
pub mod ast;
|
pub mod ast;
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
|
Loading…
Reference in a new issue