From ccd1b0800a5de5e046e6e9a4b6f49030c1ce3639 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 28 Nov 2019 12:50:26 +0300 Subject: Rename Source -> InFile --- crates/ra_assists/src/assists/add_new.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'crates/ra_assists/src/assists') diff --git a/crates/ra_assists/src/assists/add_new.rs b/crates/ra_assists/src/assists/add_new.rs index 8f68bd5fb..f977547fb 100644 --- a/crates/ra_assists/src/assists/add_new.rs +++ b/crates/ra_assists/src/assists/add_new.rs @@ -1,5 +1,5 @@ use format_buf::format; -use hir::{db::HirDatabase, FromSource}; +use hir::{db::HirDatabase, FromSource, InFile}; use join_to_string::join; use ra_syntax::{ ast::{ @@ -141,7 +141,7 @@ fn find_struct_impl( })?; let struct_ty = { - let src = hir::Source { file_id: ctx.frange.file_id.into(), value: strukt.clone() }; + let src = InFile { file_id: ctx.frange.file_id.into(), value: strukt.clone() }; hir::Struct::from_source(db, src).unwrap().ty(db) }; @@ -152,7 +152,7 @@ fn find_struct_impl( return false; } - let src = hir::Source { file_id: ctx.frange.file_id.into(), value: impl_blk.clone() }; + let src = InFile { file_id: ctx.frange.file_id.into(), value: impl_blk.clone() }; let blk = hir::ImplBlock::from_source(db, src).unwrap(); let same_ty = blk.target_ty(db) == struct_ty; -- cgit v1.2.3 From 439080f0274cf4def3f393f466ceb05c8cb8bcd2 Mon Sep 17 00:00:00 2001 From: Paulo Lieuthier Date: Tue, 15 Oct 2019 15:29:20 -0300 Subject: assists: add assist for custom implementation for derived trait --- crates/ra_assists/src/assists/add_custom_impl.rs | 189 +++++++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 crates/ra_assists/src/assists/add_custom_impl.rs (limited to 'crates/ra_assists/src/assists') diff --git a/crates/ra_assists/src/assists/add_custom_impl.rs b/crates/ra_assists/src/assists/add_custom_impl.rs new file mode 100644 index 000000000..7e64cd902 --- /dev/null +++ b/crates/ra_assists/src/assists/add_custom_impl.rs @@ -0,0 +1,189 @@ +//! FIXME: write short doc here + +use crate::{Assist, AssistCtx, AssistId}; +use hir::db::HirDatabase; +use join_to_string::join; +use ra_syntax::{ + ast::{self, AstNode}, + Direction, SmolStr, + SyntaxKind::{IDENT, WHITESPACE}, + TextRange, TextUnit, +}; + +const DERIVE_TRAIT: &'static str = "derive"; + +pub(crate) fn add_custom_impl(ctx: AssistCtx) -> Option { + let input = ctx.find_node_at_offset::()?; + let attr = input.syntax().parent().and_then(ast::Attr::cast)?; + + let attr_name = attr + .syntax() + .descendants_with_tokens() + .filter(|t| t.kind() == IDENT) + .find_map(|i| i.into_token()) + .filter(|t| *t.text() == DERIVE_TRAIT)? + .text() + .clone(); + + let trait_token = + ctx.token_at_offset().filter(|t| t.kind() == IDENT && *t.text() != attr_name).next()?; + + let annotated = attr.syntax().siblings(Direction::Next).find_map(|s| ast::Name::cast(s))?; + let annotated_name = annotated.syntax().text().to_string(); + let start_offset = annotated.syntax().parent()?.text_range().end(); + + ctx.add_assist(AssistId("add_custom_impl"), "add custom impl", |edit| { + edit.target(attr.syntax().text_range()); + + let new_attr_input = input + .syntax() + .descendants_with_tokens() + .filter(|t| t.kind() == IDENT) + .filter_map(|t| t.into_token().map(|t| t.text().clone())) + .filter(|t| t != trait_token.text()) + .collect::>(); + let has_more_derives = new_attr_input.len() > 0; + let new_attr_input = + join(new_attr_input.iter()).separator(", ").surround_with("(", ")").to_string(); + let new_attr_input_len = new_attr_input.len(); + + let mut buf = String::new(); + buf.push_str("\n\nimpl "); + buf.push_str(trait_token.text().as_str()); + buf.push_str(" for "); + buf.push_str(annotated_name.as_str()); + buf.push_str(" {\n"); + + let cursor_delta = if has_more_derives { + edit.replace(input.syntax().text_range(), new_attr_input); + input.syntax().text_range().len() - TextUnit::from_usize(new_attr_input_len) + } else { + let attr_range = attr.syntax().text_range(); + edit.delete(attr_range); + + let line_break_range = attr + .syntax() + .next_sibling_or_token() + .filter(|t| t.kind() == WHITESPACE) + .map(|t| t.text_range()) + .unwrap_or(TextRange::from_to(TextUnit::from(0), TextUnit::from(0))); + edit.delete(line_break_range); + + attr_range.len() + line_break_range.len() + }; + + edit.set_cursor(start_offset + TextUnit::of_str(&buf) - cursor_delta); + buf.push_str("\n}"); + edit.insert(start_offset, buf); + }) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::helpers::{check_assist, check_assist_not_applicable}; + + #[test] + fn add_custom_impl_for_unique_input() { + check_assist( + add_custom_impl, + " +#[derive(Debu<|>g)] +struct Foo { + bar: String, +} + ", + " +struct Foo { + bar: String, +} + +impl Debug for Foo { +<|> +} + ", + ) + } + + #[test] + fn add_custom_impl_for_with_visibility_modifier() { + check_assist( + add_custom_impl, + " +#[derive(Debug<|>)] +pub struct Foo { + bar: String, +} + ", + " +pub struct Foo { + bar: String, +} + +impl Debug for Foo { +<|> +} + ", + ) + } + + #[test] + fn add_custom_impl_when_multiple_inputs() { + check_assist( + add_custom_impl, + " +#[derive(Display, Debug<|>, Serialize)] +struct Foo {} + ", + " +#[derive(Display, Serialize)] +struct Foo {} + +impl Debug for Foo { +<|> +} + ", + ) + } + + #[test] + fn test_ignore_derive_macro_without_input() { + check_assist_not_applicable( + add_custom_impl, + " +#[derive(<|>)] +struct Foo {} + ", + ) + } + + #[test] + fn test_ignore_if_cursor_on_param() { + check_assist_not_applicable( + add_custom_impl, + " +#[derive<|>(Debug)] +struct Foo {} + ", + ); + + check_assist_not_applicable( + add_custom_impl, + " +#[derive(Debug)<|>] +struct Foo {} + ", + ) + } + + #[test] + fn test_ignore_if_not_derive() { + check_assist_not_applicable( + add_custom_impl, + " +#[allow(non_camel_<|>case_types)] +struct Foo {} + ", + ) + } +} -- cgit v1.2.3 From 5b2d52c8df5235fce9d2ae78adc3182a9659b268 Mon Sep 17 00:00:00 2001 From: Paulo Lieuthier Date: Sat, 19 Oct 2019 08:19:06 -0300 Subject: docs: describe new feature 'add custom impl for derived trait' --- crates/ra_assists/src/assists/add_custom_impl.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'crates/ra_assists/src/assists') diff --git a/crates/ra_assists/src/assists/add_custom_impl.rs b/crates/ra_assists/src/assists/add_custom_impl.rs index 7e64cd902..037306fd6 100644 --- a/crates/ra_assists/src/assists/add_custom_impl.rs +++ b/crates/ra_assists/src/assists/add_custom_impl.rs @@ -12,6 +12,23 @@ use ra_syntax::{ const DERIVE_TRAIT: &'static str = "derive"; +// Assist: add_custom_impl +// +// Adds impl block for derived trait. +// +// ``` +// #[derive(Deb<|>ug, Display)] +// struct S; +// ``` +// -> +// ``` +// #[derive(Display)] +// struct S; +// +// impl Debug for S { +// +// } +// ``` pub(crate) fn add_custom_impl(ctx: AssistCtx) -> Option { let input = ctx.find_node_at_offset::()?; let attr = input.syntax().parent().and_then(ast::Attr::cast)?; -- cgit v1.2.3 From de08d30b80f80ee0eaecf4dddb72e6fb829a46b6 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 7 Dec 2019 11:52:20 +0100 Subject: Get rid of unwraps in add_new Probably fixes #2464. --- crates/ra_assists/src/assists/add_new.rs | 45 +++++++++++++++----------------- 1 file changed, 21 insertions(+), 24 deletions(-) (limited to 'crates/ra_assists/src/assists') diff --git a/crates/ra_assists/src/assists/add_new.rs b/crates/ra_assists/src/assists/add_new.rs index f977547fb..d340cac8f 100644 --- a/crates/ra_assists/src/assists/add_new.rs +++ b/crates/ra_assists/src/assists/add_new.rs @@ -56,42 +56,39 @@ pub(crate) fn add_new(ctx: AssistCtx) -> Option { let vis = vis.as_ref().map(String::as_str).unwrap_or(""); write!(&mut buf, " {}fn new(", vis).unwrap(); - join(field_list.fields().map(|f| { - format!( - "{}: {}", - f.name().unwrap().syntax().text(), - f.ascribed_type().unwrap().syntax().text() - ) + join(field_list.fields().filter_map(|f| { + Some(format!("{}: {}", f.name()?.syntax().text(), f.ascribed_type()?.syntax().text())) })) .separator(", ") .to_buf(&mut buf); buf.push_str(") -> Self { Self {"); - join(field_list.fields().map(|f| f.name().unwrap().syntax().text())) + join(field_list.fields().filter_map(|f| Some(f.name()?.syntax().text()))) .separator(", ") .surround_with(" ", " ") .to_buf(&mut buf); buf.push_str("} }"); - let (start_offset, end_offset) = if let Some(impl_block) = impl_block { - buf.push('\n'); - let start = impl_block - .syntax() - .descendants_with_tokens() - .find(|t| t.kind() == T!['{']) - .unwrap() - .text_range() - .end(); - - (start, TextUnit::from_usize(1)) - } else { - buf = generate_impl_text(&strukt, &buf); - let start = strukt.syntax().text_range().end(); - - (start, TextUnit::from_usize(3)) - }; + let (start_offset, end_offset) = impl_block + .and_then(|impl_block| { + buf.push('\n'); + let start = impl_block + .syntax() + .descendants_with_tokens() + .find(|t| t.kind() == T!['{'])? + .text_range() + .end(); + + Some((start, TextUnit::from_usize(1))) + }) + .unwrap_or_else(|| { + buf = generate_impl_text(&strukt, &buf); + let start = strukt.syntax().text_range().end(); + + (start, TextUnit::from_usize(3)) + }); edit.set_cursor(start_offset + TextUnit::of_str(&buf) - end_offset); edit.insert(start_offset, buf); -- cgit v1.2.3 From 03fe6b38b88b3d4f7f79912e7bf1b5fb14d7bbc2 Mon Sep 17 00:00:00 2001 From: kjeremy Date: Fri, 13 Dec 2019 17:35:15 -0500 Subject: Remove some unwraps in add_new --- crates/ra_assists/src/assists/add_new.rs | 35 +++++++++++++++----------------- 1 file changed, 16 insertions(+), 19 deletions(-) (limited to 'crates/ra_assists/src/assists') diff --git a/crates/ra_assists/src/assists/add_new.rs b/crates/ra_assists/src/assists/add_new.rs index d340cac8f..b2f946fac 100644 --- a/crates/ra_assists/src/assists/add_new.rs +++ b/crates/ra_assists/src/assists/add_new.rs @@ -139,43 +139,40 @@ fn find_struct_impl( let struct_ty = { let src = InFile { file_id: ctx.frange.file_id.into(), value: strukt.clone() }; - hir::Struct::from_source(db, src).unwrap().ty(db) + hir::Struct::from_source(db, src)?.ty(db) }; - let mut found_new_fn = false; - - let block = module.descendants().filter_map(ast::ImplBlock::cast).find(|impl_blk| { - if found_new_fn { - return false; - } - + let block = module.descendants().filter_map(ast::ImplBlock::cast).find_map(|impl_blk| { let src = InFile { file_id: ctx.frange.file_id.into(), value: impl_blk.clone() }; - let blk = hir::ImplBlock::from_source(db, src).unwrap(); + let blk = hir::ImplBlock::from_source(db, src)?; let same_ty = blk.target_ty(db) == struct_ty; let not_trait_impl = blk.target_trait(db).is_none(); if !(same_ty && not_trait_impl) { - return false; + None + } else { + Some(impl_blk) } - - found_new_fn = has_new_fn(impl_blk); - true }); - if found_new_fn { - None - } else { - Some(block) + if let Some(ref impl_blk) = block { + if has_new_fn(impl_blk) { + return None; + } } + + Some(block) } fn has_new_fn(imp: &ast::ImplBlock) -> bool { if let Some(il) = imp.item_list() { for item in il.impl_items() { if let ast::ImplItem::FnDef(f) = item { - if f.name().unwrap().text().eq_ignore_ascii_case("new") { - return true; + if let Some(name) = f.name() { + if name.text().eq_ignore_ascii_case("new") { + return true; + } } } } -- cgit v1.2.3 From 2619950b3b405324ab1c1745876165c834b3b4b9 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 13 Dec 2019 12:12:36 +0100 Subject: Use different types for path with and without generics --- crates/ra_assists/src/assists/add_import.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'crates/ra_assists/src/assists') diff --git a/crates/ra_assists/src/assists/add_import.rs b/crates/ra_assists/src/assists/add_import.rs index 363ade016..f81b4184a 100644 --- a/crates/ra_assists/src/assists/add_import.rs +++ b/crates/ra_assists/src/assists/add_import.rs @@ -578,7 +578,7 @@ fn apply_auto_import( fn collect_hir_path_segments(path: &hir::Path) -> Option> { let mut ps = Vec::::with_capacity(10); - match path.kind { + match path.kind() { hir::PathKind::Abs => ps.push("".into()), hir::PathKind::Crate => ps.push("crate".into()), hir::PathKind::Plain => {} @@ -586,9 +586,7 @@ fn collect_hir_path_segments(path: &hir::Path) -> Option> { hir::PathKind::Super => ps.push("super".into()), hir::PathKind::Type(_) | hir::PathKind::DollarCrate(_) => return None, } - for s in path.segments.iter() { - ps.push(s.name.to_string().into()); - } + ps.extend(path.segments().iter().map(|it| it.name.to_string().into())); Some(ps) } -- cgit v1.2.3 From aca022f1d49a6d945f3ef4f8c781d7337120b68d Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 17 Dec 2019 15:38:28 +0100 Subject: Refactor PathKind --- crates/ra_assists/src/assists/add_import.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'crates/ra_assists/src/assists') diff --git a/crates/ra_assists/src/assists/add_import.rs b/crates/ra_assists/src/assists/add_import.rs index f81b4184a..ceffee9b8 100644 --- a/crates/ra_assists/src/assists/add_import.rs +++ b/crates/ra_assists/src/assists/add_import.rs @@ -582,8 +582,14 @@ fn collect_hir_path_segments(path: &hir::Path) -> Option> { hir::PathKind::Abs => ps.push("".into()), hir::PathKind::Crate => ps.push("crate".into()), hir::PathKind::Plain => {} - hir::PathKind::Self_ => ps.push("self".into()), - hir::PathKind::Super => ps.push("super".into()), + hir::PathKind::Super(0) => ps.push("self".into()), + hir::PathKind::Super(lvl) => { + let mut chain = "super".to_string(); + for _ in 0..*lvl { + chain += "::super"; + } + ps.push(chain.into()); + } hir::PathKind::Type(_) | hir::PathKind::DollarCrate(_) => return None, } ps.extend(path.segments().iter().map(|it| it.name.to_string().into())); -- cgit v1.2.3 From 04715cbe1caf92e55d393a352a12454ba958845e Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 18 Dec 2019 17:41:33 +0100 Subject: Forbid ::foo syntax in mod paths --- crates/ra_assists/src/assists/add_import.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'crates/ra_assists/src/assists') diff --git a/crates/ra_assists/src/assists/add_import.rs b/crates/ra_assists/src/assists/add_import.rs index ceffee9b8..b8752cbad 100644 --- a/crates/ra_assists/src/assists/add_import.rs +++ b/crates/ra_assists/src/assists/add_import.rs @@ -590,7 +590,7 @@ fn collect_hir_path_segments(path: &hir::Path) -> Option> { } ps.push(chain.into()); } - hir::PathKind::Type(_) | hir::PathKind::DollarCrate(_) => return None, + hir::PathKind::DollarCrate(_) => return None, } ps.extend(path.segments().iter().map(|it| it.name.to_string().into())); Some(ps) -- cgit v1.2.3 From 0d5d63a80ea08f2af439bcc72fff9b24d144c70d Mon Sep 17 00:00:00 2001 From: kjeremy Date: Fri, 20 Dec 2019 15:14:30 -0500 Subject: Clippy lints --- crates/ra_assists/src/assists/early_return.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'crates/ra_assists/src/assists') diff --git a/crates/ra_assists/src/assists/early_return.rs b/crates/ra_assists/src/assists/early_return.rs index 264412526..023917aca 100644 --- a/crates/ra_assists/src/assists/early_return.rs +++ b/crates/ra_assists/src/assists/early_return.rs @@ -83,8 +83,8 @@ pub(crate) fn convert_to_guarded_return(ctx: AssistCtx) -> Opt let parent_container = parent_block.syntax().parent()?.parent()?; let early_expression: ast::Expr = match parent_container.kind() { - WHILE_EXPR | LOOP_EXPR => make::expr_continue().into(), - FN_DEF => make::expr_return().into(), + WHILE_EXPR | LOOP_EXPR => make::expr_continue(), + FN_DEF => make::expr_return(), _ => return None, }; @@ -116,13 +116,13 @@ pub(crate) fn convert_to_guarded_return(ctx: AssistCtx) -> Opt ) .into(), ), - make::expr_path(make::path_from_name_ref(make::name_ref("it"))).into(), + make::expr_path(make::path_from_name_ref(make::name_ref("it"))), ); let sad_arm = make::match_arm( // FIXME: would be cool to use `None` or `Err(_)` if appropriate once(make::placeholder_pat().into()), - early_expression.into(), + early_expression, ); make::expr_match(cond_expr, make::match_arm_list(vec![happy_arm, sad_arm])) @@ -130,7 +130,7 @@ pub(crate) fn convert_to_guarded_return(ctx: AssistCtx) -> Opt let let_stmt = make::let_stmt( make::bind_pat(make::name(&bound_ident.syntax().to_string())).into(), - Some(match_expr.into()), + Some(match_expr), ); let let_stmt = if_indent_level.increase_indent(let_stmt); replace(let_stmt.syntax(), &then_block, &parent_block, &if_expr) -- cgit v1.2.3