Implement a simple working assist
This commit is contained in:
parent
1c07c5ccf9
commit
2f616eea9c
@ -1,12 +1,15 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use crate::assist_ctx::{Assist, AssistCtx};
|
||||
use crate::{Assist, AssistId, AssistCtx};
|
||||
|
||||
use hir::Resolver;
|
||||
use hir::db::HirDatabase;
|
||||
use ra_syntax::{SmolStr, SyntaxKind, SyntaxNode, TreeArc};
|
||||
use ra_syntax::{SmolStr, SyntaxKind, SyntaxNode, TextUnit, TreeArc};
|
||||
use ra_syntax::ast::{self, AstNode, FnDef, ImplItem, ImplItemKind, NameOwner};
|
||||
use ra_db::FilePosition;
|
||||
use ra_fmt::{leading_indent, reindent};
|
||||
|
||||
use itertools::Itertools;
|
||||
|
||||
/// Given an `ast::ImplBlock`, resolves the target trait (the one being
|
||||
/// implemented) to a `ast::TraitDef`.
|
||||
@ -24,7 +27,21 @@ pub(crate) fn resolve_target_trait_def(
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn add_missing_impl_members(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
pub(crate) fn build_func_body(def: &ast::FnDef) -> String {
|
||||
let mut buf = String::new();
|
||||
|
||||
for child in def.syntax().children() {
|
||||
if child.kind() == SyntaxKind::SEMI {
|
||||
buf.push_str(" { unimplemented!() }")
|
||||
} else {
|
||||
child.text().push_to(&mut buf);
|
||||
}
|
||||
}
|
||||
|
||||
buf.trim_end().to_string()
|
||||
}
|
||||
|
||||
pub(crate) fn add_missing_impl_members(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||
use SyntaxKind::{IMPL_BLOCK, ITEM_LIST, WHITESPACE};
|
||||
|
||||
let node = ctx.covering_node();
|
||||
@ -35,6 +52,7 @@ pub(crate) fn add_missing_impl_members(ctx: AssistCtx<impl HirDatabase>) -> Opti
|
||||
}
|
||||
|
||||
let impl_node = node.ancestors().find_map(ast::ImplBlock::cast)?;
|
||||
let impl_item_list = impl_node.item_list()?;
|
||||
|
||||
let trait_def = {
|
||||
let db = ctx.db;
|
||||
@ -47,23 +65,49 @@ pub(crate) fn add_missing_impl_members(ctx: AssistCtx<impl HirDatabase>) -> Opti
|
||||
};
|
||||
|
||||
let fn_def_opt = |kind| if let ImplItemKind::FnDef(def) = kind { Some(def) } else { None };
|
||||
let def_name = |&def| -> Option<&SmolStr> { FnDef::name(def).map(ast::Name::text) };
|
||||
let def_name = |def| -> Option<&SmolStr> { FnDef::name(def).map(ast::Name::text) };
|
||||
|
||||
let trait_items = trait_def.syntax().descendants().find_map(ast::ItemList::cast)?.impl_items();
|
||||
let impl_items = impl_node.item_list()?.impl_items();
|
||||
let impl_items = impl_item_list.impl_items();
|
||||
|
||||
let trait_fns = trait_items.map(ImplItem::kind).filter_map(fn_def_opt).collect::<Vec<_>>();
|
||||
let impl_fns = impl_items.map(ImplItem::kind).filter_map(fn_def_opt).collect::<Vec<_>>();
|
||||
|
||||
let trait_fn_names = trait_fns.iter().filter_map(def_name).collect::<HashSet<_>>();
|
||||
let impl_fn_names = impl_fns.iter().filter_map(def_name).collect::<HashSet<_>>();
|
||||
let trait_fn_names = trait_fns.iter().cloned().filter_map(def_name).collect::<HashSet<_>>();
|
||||
let impl_fn_names = impl_fns.iter().cloned().filter_map(def_name).collect::<HashSet<_>>();
|
||||
|
||||
let missing_fn_names = trait_fn_names.difference(&impl_fn_names).collect::<HashSet<_>>();
|
||||
let missing_fns = trait_fns
|
||||
let missing_fns: Vec<_> = trait_fns
|
||||
.iter()
|
||||
.filter(|&t| def_name(t).map(|n| missing_fn_names.contains(&n)).unwrap_or(false));
|
||||
.cloned()
|
||||
.filter(|t| def_name(t).map(|n| missing_fn_names.contains(&n)).unwrap_or(false))
|
||||
.collect();
|
||||
|
||||
unimplemented!()
|
||||
if missing_fns.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let last_whitespace_node =
|
||||
impl_item_list.syntax().children().filter_map(ast::Whitespace::cast).last()?.syntax();
|
||||
|
||||
ctx.add_action(AssistId("add_impl_missing_members"), "add impl missing members", |edit| {
|
||||
let func_bodies = missing_fns.into_iter().map(build_func_body).join("\n");
|
||||
let func_bodies = String::from("\n") + &func_bodies;
|
||||
|
||||
let first_impl_item = impl_item_list.impl_items().next();
|
||||
// FIXME: We should respect the indent of the first item from the item list or the indent of leading block + some default indent (4?)
|
||||
// Another approach is to not indent at all if there are no items here
|
||||
let indent = first_impl_item.and_then(|i| leading_indent(i.syntax())).unwrap_or_default();
|
||||
let func_bodies = reindent(&func_bodies, indent) + "\n";
|
||||
|
||||
let changed_range = last_whitespace_node.range();
|
||||
let replaced_text_range = TextUnit::of_str(&func_bodies);
|
||||
|
||||
edit.replace(changed_range, func_bodies);
|
||||
edit.set_cursor(changed_range.start() + replaced_text_range - TextUnit::of_str("\n"));
|
||||
});
|
||||
|
||||
ctx.build()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -78,24 +122,26 @@ mod tests {
|
||||
"
|
||||
trait Foo {
|
||||
fn foo(&self);
|
||||
fn bar(&self);
|
||||
}
|
||||
|
||||
struct S;
|
||||
|
||||
impl Foo for S {
|
||||
fn bar(&self) {}
|
||||
<|>
|
||||
}",
|
||||
"
|
||||
trait Foo {
|
||||
fn foo(&self);
|
||||
fn bar(&self);
|
||||
}
|
||||
|
||||
struct S;
|
||||
|
||||
impl Foo for S {
|
||||
fn foo(&self) {
|
||||
<|>
|
||||
}
|
||||
fn bar(&self) {}
|
||||
fn foo(&self) { unimplemented!() }<|>
|
||||
}",
|
||||
);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user