fix: Insert whitespace into trait-impl completions when coming from macros

This commit is contained in:
Lukas Wirth 2022-05-24 22:33:42 +02:00
parent 6f006b7524
commit 86d1d9067e
6 changed files with 99 additions and 83 deletions

View file

@ -939,7 +939,6 @@ struct Foo(usize);
impl FooB for Foo {
$0fn foo< 'lt>(& 'lt self){}
}
"#,
)

View file

@ -32,10 +32,12 @@
//! ```
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::{
ast::{self, edit_in_place::AttrsOwnerEdit},
display::function_declaration,
AstNode, SyntaxElement, SyntaxKind, SyntaxNode, TextRange, T,
};
use text_edit::TextEdit;
@ -179,7 +181,7 @@ fn add_function_impl(
_ => 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 {
Some(cap) => {
let snippet = format!("{} {{\n $0\n}}", function_decl);
@ -260,7 +262,7 @@ fn add_const_impl(
_ => 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 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();
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 const_end = const_.syntax().text_range().end();
let start =
const_.syntax().first_child_or_token().map_or(const_start, |f| f.text_range().start());
let start = const_.text_range().start();
let const_end = const_.text_range().end();
let end = const_
.syntax()
.children_with_tokens()
.find(|s| s.kind() == T![;] || s.kind() == T![=])
.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 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())
}
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 {
let first_child = item
.children_with_tokens()
@ -655,8 +683,7 @@ trait Test {
struct T;
impl Test for T {
fn foo<T>()
where T: Into<String> {
fn foo<T>() where T: Into<String> {
$0
}
}
@ -1011,7 +1038,7 @@ struct Bar;
impl Foo<u32> for Bar {
fn function()
where Self: SomeTrait<u32> {
where Self: SomeTrait<u32> {
$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
}
}
"#,
);
}
}

View file

@ -114,6 +114,10 @@ pub fn insert_ws_into(syn: SyntaxNode) -> SyntaxNode {
ted::insert(pos, insert);
}
if let Some(it) = syn.last_token().filter(|it| it.kind() == SyntaxKind::WHITESPACE) {
ted::remove(it);
}
syn
}

View file

@ -250,8 +250,7 @@ fn main() {
"#,
expect![[r#"
bar
for _ in 0..42{}
"#]],
for _ in 0..42{}"#]],
);
}
@ -273,7 +272,6 @@ f$0oo!();
expect![[r#"
foo
fn b(){}
"#]],
);
}
@ -297,8 +295,7 @@ f$0oo!();
fn some_thing() -> u32 {
let a = 0;
a+10
}
"#]],
}"#]],
);
}
@ -359,8 +356,7 @@ fn main() {
"#,
expect![[r#"
match_ast
{}
"#]],
{}"#]],
);
}
@ -421,8 +417,7 @@ fn main() {
"#,
expect![[r#"
foo
fn f<T>(_: &dyn ::std::marker::Copy){}
"#]],
fn f<T>(_: &dyn ::std::marker::Copy){}"#]],
);
}
@ -440,7 +435,6 @@ struct Foo {}
expect![[r#"
Clone
impl < >core::clone::Clone for Foo< >{}
"#]],
);
}
@ -458,7 +452,6 @@ struct Foo {}
expect![[r#"
Copy
impl < >core::marker::Copy for Foo< >{}
"#]],
);
}
@ -475,7 +468,6 @@ struct Foo {}
expect![[r#"
Copy
impl < >core::marker::Copy for Foo< >{}
"#]],
);
check(
@ -488,7 +480,6 @@ struct Foo {}
expect![[r#"
Clone
impl < >core::clone::Clone for Foo< >{}
"#]],
);
}

View file

@ -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
}

View file

@ -33,7 +33,6 @@ mod token_text;
#[cfg(test)]
mod tests;
pub mod display;
pub mod algo;
pub mod ast;
#[doc(hidden)]