7650: Add `find_impl_block_end` assist helper r=Veykril a=yoshuawuyts

Fixes #7605. This makes it so assists can use helpers to either append a method to the start or the end of an `impl` block. Thanks!

@Veykril if this is merged, perhaps it could be good to update the gif in https://github.com/rust-analyzer/rust-analyzer/pull/7617#issuecomment-776622135 ? -- this should fix the ordering issue when generating multiple methods.

Co-authored-by: Yoshua Wuyts <yoshuawuyts@gmail.com>
This commit is contained in:
bors[bot] 2021-02-12 17:16:02 +00:00 committed by GitHub
commit 4d51b56444
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 171 additions and 18 deletions

View File

@ -4,7 +4,7 @@ use syntax::ast::{self, AstNode, NameOwner};
use test_utils::mark;
use crate::{
utils::{find_impl_block, find_struct_impl, generate_impl_text},
utils::{find_impl_block_end, find_struct_impl, generate_impl_text},
AssistContext, AssistId, AssistKind, Assists,
};
@ -80,7 +80,7 @@ pub(crate) fn generate_enum_match_method(acc: &mut Assists, ctx: &AssistContext)
);
let start_offset = impl_def
.and_then(|impl_def| find_impl_block(impl_def, &mut buf))
.and_then(|impl_def| find_impl_block_end(impl_def, &mut buf))
.unwrap_or_else(|| {
buf = generate_impl_text(&ast::Adt::Enum(parent_enum.clone()), &buf);
parent_enum.syntax().text_range().end()
@ -197,6 +197,43 @@ impl Variant {
pub(crate) fn is_minor(&self) -> bool {
matches!(self, Self::Minor)
}
}"#,
);
}
#[test]
fn test_multiple_generate_enum_match_from_variant() {
check_assist(
generate_enum_match_method,
r#"
enum Variant {
Undefined,
Minor,
Major$0,
}
impl Variant {
/// Returns `true` if the variant is [`Minor`].
fn is_minor(&self) -> bool {
matches!(self, Self::Minor)
}
}"#,
r#"enum Variant {
Undefined,
Minor,
Major,
}
impl Variant {
/// Returns `true` if the variant is [`Minor`].
fn is_minor(&self) -> bool {
matches!(self, Self::Minor)
}
/// Returns `true` if the variant is [`Major`].
fn is_major(&self) -> bool {
matches!(self, Self::Major)
}
}"#,
);
}

View File

@ -3,7 +3,7 @@ use syntax::ast::VisibilityOwner;
use syntax::ast::{self, AstNode, NameOwner};
use crate::{
utils::{find_impl_block, find_struct_impl, generate_impl_text},
utils::{find_impl_block_end, find_struct_impl, generate_impl_text},
AssistContext, AssistId, AssistKind, Assists,
};
@ -73,7 +73,7 @@ pub(crate) fn generate_getter(acc: &mut Assists, ctx: &AssistContext) -> Option<
);
let start_offset = impl_def
.and_then(|impl_def| find_impl_block(impl_def, &mut buf))
.and_then(|impl_def| find_impl_block_end(impl_def, &mut buf))
.unwrap_or_else(|| {
buf = generate_impl_text(&ast::Adt::Struct(strukt.clone()), &buf);
strukt.syntax().text_range().end()
@ -150,6 +150,42 @@ impl<T: Clone> Context<T> {
pub(crate) fn data(&self) -> &T {
&self.data
}
}"#,
);
}
#[test]
fn test_multiple_generate_getter() {
check_assist(
generate_getter,
r#"
struct Context<T: Clone> {
data: T,
cou$0nt: usize,
}
impl<T: Clone> Context<T> {
/// Get a reference to the context's data.
fn data(&self) -> &T {
&self.data
}
}"#,
r#"
struct Context<T: Clone> {
data: T,
count: usize,
}
impl<T: Clone> Context<T> {
/// Get a reference to the context's data.
fn data(&self) -> &T {
&self.data
}
/// Get a reference to the context's count.
fn count(&self) -> &usize {
&self.count
}
}"#,
);
}

View File

@ -3,7 +3,7 @@ use syntax::ast::VisibilityOwner;
use syntax::ast::{self, AstNode, NameOwner};
use crate::{
utils::{find_impl_block, find_struct_impl, generate_impl_text},
utils::{find_impl_block_end, find_struct_impl, generate_impl_text},
AssistContext, AssistId, AssistKind, Assists,
};
@ -76,7 +76,7 @@ pub(crate) fn generate_getter_mut(acc: &mut Assists, ctx: &AssistContext) -> Opt
);
let start_offset = impl_def
.and_then(|impl_def| find_impl_block(impl_def, &mut buf))
.and_then(|impl_def| find_impl_block_end(impl_def, &mut buf))
.unwrap_or_else(|| {
buf = generate_impl_text(&ast::Adt::Struct(strukt.clone()), &buf);
strukt.syntax().text_range().end()
@ -153,6 +153,42 @@ impl<T: Clone> Context<T> {
pub(crate) fn data_mut(&mut self) -> &mut T {
&mut self.data
}
}"#,
);
}
#[test]
fn test_multiple_generate_getter_mut() {
check_assist(
generate_getter_mut,
r#"
struct Context<T: Clone> {
data: T,
cou$0nt: usize,
}
impl<T: Clone> Context<T> {
/// Get a mutable reference to the context's data.
fn data_mut(&mut self) -> &mut T {
&mut self.data
}
}"#,
r#"
struct Context<T: Clone> {
data: T,
count: usize,
}
impl<T: Clone> Context<T> {
/// Get a mutable reference to the context's data.
fn data_mut(&mut self) -> &mut T {
&mut self.data
}
/// Get a mutable reference to the context's count.
fn count_mut(&mut self) -> &mut usize {
&mut self.count
}
}"#,
);
}

View File

@ -4,7 +4,7 @@ use stdx::format_to;
use syntax::ast::{self, AstNode, NameOwner, StructKind, VisibilityOwner};
use crate::{
utils::{find_impl_block, find_struct_impl, generate_impl_text},
utils::{find_impl_block_start, find_struct_impl, generate_impl_text},
AssistContext, AssistId, AssistKind, Assists,
};
@ -58,7 +58,7 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
format_to!(buf, " {}fn new({}) -> Self {{ Self {{ {} }} }}", vis, params, fields);
let start_offset = impl_def
.and_then(|impl_def| find_impl_block(impl_def, &mut buf))
.and_then(|impl_def| find_impl_block_start(impl_def, &mut buf))
.unwrap_or_else(|| {
buf = generate_impl_text(&Adt::Struct(strukt.clone()), &buf);
strukt.syntax().text_range().end()

View File

@ -3,7 +3,7 @@ use syntax::ast::VisibilityOwner;
use syntax::ast::{self, AstNode, NameOwner};
use crate::{
utils::{find_impl_block, find_struct_impl, generate_impl_text},
utils::{find_impl_block_end, find_struct_impl, generate_impl_text},
AssistContext, AssistId, AssistKind, Assists,
};
@ -79,7 +79,7 @@ pub(crate) fn generate_setter(acc: &mut Assists, ctx: &AssistContext) -> Option<
);
let start_offset = impl_def
.and_then(|impl_def| find_impl_block(impl_def, &mut buf))
.and_then(|impl_def| find_impl_block_end(impl_def, &mut buf))
.unwrap_or_else(|| {
buf = generate_impl_text(&ast::Adt::Struct(strukt.clone()), &buf);
strukt.syntax().text_range().end()
@ -156,6 +156,42 @@ impl<T: Clone> Person<T> {
pub(crate) fn set_data(&mut self, data: T) {
self.data = data;
}
}"#,
);
}
#[test]
fn test_multiple_generate_setter() {
check_assist(
generate_setter,
r#"
struct Context<T: Clone> {
data: T,
cou$0nt: usize,
}
impl<T: Clone> Context<T> {
/// Set the context's data.
fn set_data(&mut self, data: T) {
self.data = data;
}
}"#,
r#"
struct Context<T: Clone> {
data: T,
count: usize,
}
impl<T: Clone> Context<T> {
/// Set the context's data.
fn set_data(&mut self, data: T) {
self.data = data;
}
/// Set the context's count.
fn set_count(&mut self, count: usize) {
self.count = count;
}
}"#,
);
}

View File

@ -279,7 +279,7 @@ pub(crate) fn does_pat_match_variant(pat: &ast::Pat, var: &ast::Pat) -> bool {
//
// FIXME: change the new fn checking to a more semantic approach when that's more
// viable (e.g. we process proc macros, etc)
// FIXME: this partially overlaps with `find_impl_block`
// FIXME: this partially overlaps with `find_impl_block_*`
pub(crate) fn find_struct_impl(
ctx: &AssistContext,
strukt: &ast::Adt,
@ -343,17 +343,25 @@ fn has_fn(imp: &ast::Impl, rhs_name: &str) -> bool {
/// Find the start of the `impl` block for the given `ast::Impl`.
//
// FIXME: add a way to find the end of the `impl` block.
// FIXME: this partially overlaps with `find_struct_impl`
pub(crate) fn find_impl_block(impl_def: ast::Impl, buf: &mut String) -> Option<TextSize> {
pub(crate) fn find_impl_block_start(impl_def: ast::Impl, buf: &mut String) -> Option<TextSize> {
buf.push('\n');
let start = impl_def
.syntax()
.descendants_with_tokens()
.find(|t| t.kind() == T!['{'])?
let start = impl_def.assoc_item_list().and_then(|it| it.l_curly_token())?.text_range().end();
Some(start)
}
/// Find the end of the `impl` block for the given `ast::Impl`.
//
// FIXME: this partially overlaps with `find_struct_impl`
pub(crate) fn find_impl_block_end(impl_def: ast::Impl, buf: &mut String) -> Option<TextSize> {
buf.push('\n');
let end = impl_def
.assoc_item_list()
.and_then(|it| it.r_curly_token())?
.prev_sibling_or_token()?
.text_range()
.end();
Some(start)
Some(end)
}
// Generates the surrounding `impl Type { <code> }` including type and lifetime