7572: Add `find_or_create_impl_block` to assist utils r=matklad a=yoshuawuyts

This is another continuation of https://github.com/rust-analyzer/rust-analyzer/pull/7562, introducing a small util to either find an `impl` block, or create a new one if none exists. I copied this code from the `generate_new` assist into https://github.com/rust-analyzer/rust-analyzer/pull/7562, and this unifies both into a helper.

It doesn't feel super polished in its current state, but my hope is that this is enough of a starting point that it can be expanded on later. For example something that would be useful would be a flag which either returns the index of the start of the block, or the end of the block.

Anyway, I hope this is useful. Thanks!

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

View File

@ -1,9 +1,12 @@
use stdx::{format_to, to_lower_snake_case};
use syntax::ast::VisibilityOwner;
use syntax::ast::{self, AstNode, NameOwner};
use syntax::{ast::VisibilityOwner, T};
use test_utils::mark;
use crate::{utils::find_struct_impl, AssistContext, AssistId, AssistKind, Assists};
use crate::{
utils::{find_impl_block, find_struct_impl},
AssistContext, AssistId, AssistKind, Assists,
};
// Assist: generate_enum_match_method
//
@ -63,7 +66,6 @@ pub(crate) fn generate_enum_match_method(acc: &mut Assists, ctx: &AssistContext)
}
let vis = parent_enum.visibility().map_or(String::new(), |v| format!("{} ", v));
format_to!(
buf,
" /// Returns `true` if the {} is [`{}`].
@ -78,17 +80,7 @@ pub(crate) fn generate_enum_match_method(acc: &mut Assists, ctx: &AssistContext)
);
let start_offset = impl_def
.and_then(|impl_def| {
buf.push('\n');
let start = impl_def
.syntax()
.descendants_with_tokens()
.find(|t| t.kind() == T!['{'])?
.text_range()
.end();
Some(start)
})
.and_then(|impl_def| find_impl_block(impl_def, &mut buf))
.unwrap_or_else(|| {
buf = generate_impl_text(&parent_enum, &buf);
parent_enum.syntax().text_range().end()

View File

@ -2,10 +2,13 @@ use itertools::Itertools;
use stdx::format_to;
use syntax::{
ast::{self, AstNode, GenericParamsOwner, NameOwner, StructKind, VisibilityOwner},
SmolStr, T,
SmolStr,
};
use crate::{utils::find_struct_impl, AssistContext, AssistId, AssistKind, Assists};
use crate::{
utils::{find_impl_block, find_struct_impl},
AssistContext, AssistId, AssistKind, Assists,
};
// Assist: generate_new
//
@ -58,17 +61,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| {
buf.push('\n');
let start = impl_def
.syntax()
.descendants_with_tokens()
.find(|t| t.kind() == T!['{'])?
.text_range()
.end();
Some(start)
})
.and_then(|impl_def| find_impl_block(impl_def, &mut buf))
.unwrap_or_else(|| {
buf = generate_impl_text(&strukt, &buf);
strukt.syntax().text_range().end()
@ -93,7 +86,7 @@ fn generate_impl_text(strukt: &ast::Struct, code: &str) -> String {
if let Some(type_params) = &type_params {
format_to!(buf, "{}", type_params.syntax());
}
buf.push_str(" ");
buf.push(' ');
buf.push_str(strukt.name().unwrap().text());
if let Some(type_params) = type_params {
let lifetime_params = type_params

View File

@ -274,10 +274,11 @@ pub(crate) fn does_pat_match_variant(pat: &ast::Pat, var: &ast::Pat) -> bool {
// Uses a syntax-driven approach to find any impl blocks for the struct that
// exist within the module/file
//
// Returns `None` if we've found an existing `new` fn
// Returns `None` if we've found an existing fn
//
// 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`
pub(crate) fn find_struct_impl(
ctx: &AssistContext,
strukt: &ast::AdtDef,
@ -338,3 +339,18 @@ fn has_fn(imp: &ast::Impl, rhs_name: &str) -> bool {
false
}
/// 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> {
buf.push('\n');
let start = impl_def
.syntax()
.descendants_with_tokens()
.find(|t| t.kind() == T!['{'])?
.text_range()
.end();
Some(start)
}