Implement a simple working assist

This commit is contained in:
Igor Matuszewski 2019-03-07 01:48:31 +01:00
parent 1c07c5ccf9
commit 2f616eea9c

View file

@ -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!() }<|>
}",
);
}