diff --git a/crates/ra_assists/src/assists/add_derive.rs b/crates/ra_assists/src/assists/add_derive.rs index 77ecc33c9d..d3ba634c40 100644 --- a/crates/ra_assists/src/assists/add_derive.rs +++ b/crates/ra_assists/src/assists/add_derive.rs @@ -1,5 +1,3 @@ -//! FIXME: write short doc here - use hir::db::HirDatabase; use ra_syntax::{ ast::{self, AstNode, AttrsOwner}, @@ -9,6 +7,22 @@ use ra_syntax::{ use crate::{Assist, AssistCtx, AssistId}; +// Assist: add_derive +// Adds a new `#[derive()]` clause to a struct or enum. +// ``` +// struct Point { +// x: u32, +// y: u32,<|> +// } +// ``` +// -> +// ``` +// #[derive()] +// struct Point { +// x: u32, +// y: u32, +// } +// ``` pub(crate) fn add_derive(mut ctx: AssistCtx) -> Option { let nominal = ctx.node_at_offset::()?; let node_start = derive_insertion_offset(&nominal)?; diff --git a/crates/ra_assists/src/assists/add_explicit_type.rs b/crates/ra_assists/src/assists/add_explicit_type.rs index 8c83dc9878..33b7bea7f2 100644 --- a/crates/ra_assists/src/assists/add_explicit_type.rs +++ b/crates/ra_assists/src/assists/add_explicit_type.rs @@ -1,5 +1,3 @@ -//! FIXME: write short doc here - use hir::{db::HirDatabase, HirDisplay, Ty}; use ra_syntax::{ ast::{self, AstNode, LetStmt, NameOwner}, @@ -8,7 +6,19 @@ use ra_syntax::{ use crate::{Assist, AssistCtx, AssistId}; -/// Add explicit type assist. +// Assist: add_explicit_type +// Specify type for a let binding +// ``` +// fn main() { +// let x<|> = 92; +// } +// ``` +// -> +// ``` +// fn main() { +// let x: i32 = 92; +// } +// ``` pub(crate) fn add_explicit_type(mut ctx: AssistCtx) -> Option { let stmt = ctx.node_at_offset::()?; let expr = stmt.initializer()?; diff --git a/crates/ra_assists/src/assists/add_impl.rs b/crates/ra_assists/src/assists/add_impl.rs index 94801fbc9a..40bc5c4641 100644 --- a/crates/ra_assists/src/assists/add_impl.rs +++ b/crates/ra_assists/src/assists/add_impl.rs @@ -1,5 +1,3 @@ -//! FIXME: write short doc here - use format_buf::format; use hir::db::HirDatabase; use join_to_string::join; @@ -10,6 +8,23 @@ use ra_syntax::{ use crate::{Assist, AssistCtx, AssistId}; +// Assist: add_impl +// Adds a new inherent impl for a type +// ``` +// struct Ctx { +// data: T,<|> +// } +// ``` +// -> +// ``` +// struct Ctx { +// data: T, +// } +// +// impl Ctx { +// +// } +// ``` pub(crate) fn add_impl(mut ctx: AssistCtx) -> Option { let nominal = ctx.node_at_offset::()?; let name = nominal.name()?; diff --git a/crates/ra_assists/src/assists/add_missing_impl_members.rs b/crates/ra_assists/src/assists/add_missing_impl_members.rs index 565b96fb50..36fa6f9ea7 100644 --- a/crates/ra_assists/src/assists/add_missing_impl_members.rs +++ b/crates/ra_assists/src/assists/add_missing_impl_members.rs @@ -1,5 +1,3 @@ -//! FIXME: write short doc here - use hir::{db::HirDatabase, HasSource}; use ra_syntax::{ ast::{self, edit, make, AstNode, NameOwner}, @@ -14,6 +12,32 @@ enum AddMissingImplMembersMode { NoDefaultMethods, } +// Assist: add_impl_missing_members +// Adds scaffold for required impl members +// ``` +// trait T { +// Type X; +// fn foo(&self); +// fn bar(&self) {} +// } +// +// impl T for () {<|> +// +// } +// ``` +// -> +// ``` +// trait T { +// Type X; +// fn foo(&self); +// fn bar(&self) {} +// } +// +// impl T for () { +// fn foo(&self) { unimplemented!() } +// +// } +// ``` pub(crate) fn add_missing_impl_members(ctx: AssistCtx) -> Option { add_missing_impl_members_inner( ctx, @@ -23,6 +47,36 @@ pub(crate) fn add_missing_impl_members(ctx: AssistCtx) -> Opti ) } +// Assist: add_impl_default_members +// Adds scaffold for overriding default impl members +// ``` +// trait T { +// Type X; +// fn foo(&self); +// fn bar(&self) {} +// } +// +// impl T for () { +// Type X = (); +// fn foo(&self) {}<|> +// +// } +// ``` +// -> +// ``` +// trait T { +// Type X; +// fn foo(&self); +// fn bar(&self) {} +// } +// +// impl T for () { +// Type X = (); +// fn foo(&self) {} +// fn bar(&self) {} +// +// } +// ``` pub(crate) fn add_missing_default_members(ctx: AssistCtx) -> Option { add_missing_impl_members_inner( ctx, diff --git a/crates/ra_assists/src/assists/apply_demorgan.rs b/crates/ra_assists/src/assists/apply_demorgan.rs index 5f2b0dd189..a072f63e7a 100644 --- a/crates/ra_assists/src/assists/apply_demorgan.rs +++ b/crates/ra_assists/src/assists/apply_demorgan.rs @@ -1,18 +1,26 @@ -//! This contains the functions associated with the demorgan assist. -//! This assist transforms boolean expressions of the form `!a || !b` into -//! `!(a && b)`. use hir::db::HirDatabase; use ra_syntax::ast::{self, AstNode}; use ra_syntax::SyntaxNode; use crate::{Assist, AssistCtx, AssistId}; -/// Assist for applying demorgan's law -/// -/// This transforms expressions of the form `!l || !r` into `!(l && r)`. -/// This also works with `&&`. This assist can only be applied with the cursor -/// on either `||` or `&&`, with both operands being a negation of some kind. -/// This means something of the form `!x` or `x != y`. +// Assist: apply_demorgan +// Apply [De Morgan's law](https://en.wikipedia.org/wiki/De_Morgan%27s_laws). +// This transforms expressions of the form `!l || !r` into `!(l && r)`. +// This also works with `&&`. This assist can only be applied with the cursor +// on either `||` or `&&`, with both operands being a negation of some kind. +// This means something of the form `!x` or `x != y`. +// ``` +// fn main() { +// if x != 4 ||<|> !y {} +// } +// ``` +// -> +// ``` +// fn main() { +// if !(x == 4 && y) {} +// } +// ``` pub(crate) fn apply_demorgan(mut ctx: AssistCtx) -> Option { let expr = ctx.node_at_offset::()?; let op = expr.op_kind()?; diff --git a/crates/ra_assists/src/doc_tests.rs b/crates/ra_assists/src/doc_tests.rs index 88e901517c..872bbdf17a 100644 --- a/crates/ra_assists/src/doc_tests.rs +++ b/crates/ra_assists/src/doc_tests.rs @@ -15,8 +15,10 @@ fn check(assist_id: &str, before: &str, after: &str) { let (db, _source_root, file_id) = MockDatabase::with_single_file(&before); let frange = FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) }; - let (_assist_id, action) = - crate::assists(&db, frange).into_iter().find(|(id, _)| id.id.0 == assist_id).unwrap(); + let (_assist_id, action) = crate::assists(&db, frange) + .into_iter() + .find(|(id, _)| id.id.0 == assist_id) + .unwrap_or_else(|| panic!("Assist {:?} is not applicable", assist_id)); let actual = action.edit.apply(&before); assert_eq_text!(after, &actual); diff --git a/crates/ra_assists/src/doc_tests/generated.rs b/crates/ra_assists/src/doc_tests/generated.rs index e5f6910f11..76d86b93da 100644 --- a/crates/ra_assists/src/doc_tests/generated.rs +++ b/crates/ra_assists/src/doc_tests/generated.rs @@ -2,6 +2,145 @@ use super::check; +#[test] +fn doctest_add_derive() { + check( + "add_derive", + r#####" +struct Point { + x: u32, + y: u32,<|> +} +"#####, + r#####" +#[derive()] +struct Point { + x: u32, + y: u32, +} +"#####, + ) +} + +#[test] +fn doctest_add_explicit_type() { + check( + "add_explicit_type", + r#####" +fn main() { + let x<|> = 92; +} +"#####, + r#####" +fn main() { + let x: i32 = 92; +} +"#####, + ) +} + +#[test] +fn doctest_add_impl() { + check( + "add_impl", + r#####" +struct Ctx { + data: T,<|> +} +"#####, + r#####" +struct Ctx { + data: T, +} + +impl Ctx { + +} +"#####, + ) +} + +#[test] +fn doctest_add_impl_default_members() { + check( + "add_impl_default_members", + r#####" +trait T { + Type X; + fn foo(&self); + fn bar(&self) {} +} + +impl T for () { + Type X = (); + fn foo(&self) {}<|> + +} +"#####, + r#####" +trait T { + Type X; + fn foo(&self); + fn bar(&self) {} +} + +impl T for () { + Type X = (); + fn foo(&self) {} + fn bar(&self) {} + +} +"#####, + ) +} + +#[test] +fn doctest_add_impl_missing_members() { + check( + "add_impl_missing_members", + r#####" +trait T { + Type X; + fn foo(&self); + fn bar(&self) {} +} + +impl T for () {<|> + +} +"#####, + r#####" +trait T { + Type X; + fn foo(&self); + fn bar(&self) {} +} + +impl T for () { + fn foo(&self) { unimplemented!() } + +} +"#####, + ) +} + +#[test] +fn doctest_apply_demorgan() { + check( + "apply_demorgan", + r#####" +fn main() { + if x != 4 ||<|> !y {} +} +"#####, + r#####" +fn main() { + if !(x == 4 && y) {} +} +"#####, + ) +} + #[test] fn doctest_convert_to_guarded_return() { check( diff --git a/docs/user/assists.md b/docs/user/assists.md index cb4b0b9fb8..eeb4868321 100644 --- a/docs/user/assists.md +++ b/docs/user/assists.md @@ -1,5 +1,142 @@ # Assists +## `add_derive` + +Adds a new `#[derive()]` clause to a struct or enum. + +```rust +// BEFORE +struct Point { + x: u32, + y: u32,<|> +} + +// AFTER +#[derive()] +struct Point { + x: u32, + y: u32, +} +``` + +## `add_explicit_type` + +Specify type for a let binding + +```rust +// BEFORE +fn main() { + let x<|> = 92; +} + +// AFTER +fn main() { + let x: i32 = 92; +} +``` + +## `add_impl` + +Adds a new inherent impl for a type + +```rust +// BEFORE +struct Ctx { + data: T,<|> +} + +// AFTER +struct Ctx { + data: T, +} + +impl Ctx { + +} +``` + +## `add_impl_default_members` + +Adds scaffold for overriding default impl members + +```rust +// BEFORE +trait T { + Type X; + fn foo(&self); + fn bar(&self) {} +} + +impl T for () { + Type X = (); + fn foo(&self) {}<|> + +} + +// AFTER +trait T { + Type X; + fn foo(&self); + fn bar(&self) {} +} + +impl T for () { + Type X = (); + fn foo(&self) {} + fn bar(&self) {} + +} +``` + +## `add_impl_missing_members` + +Adds scaffold for required impl members + +```rust +// BEFORE +trait T { + Type X; + fn foo(&self); + fn bar(&self) {} +} + +impl T for () {<|> + +} + +// AFTER +trait T { + Type X; + fn foo(&self); + fn bar(&self) {} +} + +impl T for () { + fn foo(&self) { unimplemented!() } + +} +``` + +## `apply_demorgan` + +Apply [De Morgan's law](https://en.wikipedia.org/wiki/De_Morgan%27s_laws). +This transforms expressions of the form `!l || !r` into `!(l && r)`. +This also works with `&&`. This assist can only be applied with the cursor +on either `||` or `&&`, with both operands being a negation of some kind. +This means something of the form `!x` or `x != y`. + +```rust +// BEFORE +fn main() { + if x != 4 ||<|> !y {} +} + +// AFTER +fn main() { + if !(x == 4 && y) {} +} +``` + ## `convert_to_guarded_return` Replace a large conditional with a guarded return. diff --git a/docs/user/features.md b/docs/user/features.md index a94b65ad4d..acf092cec1 100644 --- a/docs/user/features.md +++ b/docs/user/features.md @@ -104,84 +104,6 @@ the VS Code side to be able to position cursor. `<|>` signifies cursor See [assists.md](./assists.md) -- Add `#[derive]` - -```rust -// before: -struct Foo { - <|>x: i32 -} -// after: -#[derive(<|>)] -struct Foo { - x: i32 -} -``` - -- Add `impl` - -```rust -// before: -struct Foo<'a, T: Debug> { - <|>t: T -} -// after: -struct Foo<'a, T: Debug> { - t: T -} - -impl<'a, T: Debug> Foo<'a, T> { - <|> -} -``` - -- Add missing `impl` members - -```rust -// before: -trait Foo { - fn foo(&self); - fn bar(&self); - fn baz(&self); -} - -struct S; - -impl Foo for S { - fn bar(&self) {} - <|> -} - -// after: -trait Foo { - fn foo(&self); - fn bar(&self); - fn baz(&self); -} - -struct S; - -impl Foo for S { - fn bar(&self) {} - fn foo(&self) { unimplemented!() } - fn baz(&self) { unimplemented!() }<|> -} -``` - -- Apply [De Morgan's law](https://en.wikipedia.org/wiki/De_Morgan%27s_laws) - -```rust -// before: -fn example(x: bool) -> bool { - !x || !x -} - -// after: -fn example(x: bool) -> bool { - !(x && x) -} -``` - - Import path ```rust @@ -391,19 +313,6 @@ fn foo() { } ``` -- Add explicit type - -```rust -// before: -fn foo() { - let t<|> = (&2, Some(1)); -} -// after: -fn foo() { - let t<|>: (&i32, Option) = (&2, Some(1)); -} -``` - - Move guard expression to match arm body ```rust // before: diff --git a/xtask/src/codegen.rs b/xtask/src/codegen.rs index 44729cd57a..4ec8ab75af 100644 --- a/xtask/src/codegen.rs +++ b/xtask/src/codegen.rs @@ -74,6 +74,14 @@ fn reformat(text: impl std::fmt::Display) -> Result { } fn extract_comment_blocks(text: &str) -> Vec> { + do_extract_comment_blocks(text, false) +} + +fn extract_comment_blocks_with_empty_lines(text: &str) -> Vec> { + do_extract_comment_blocks(text, true) +} + +fn do_extract_comment_blocks(text: &str, allow_blocks_with_empty_lins: bool) -> Vec> { let mut res = Vec::new(); let prefix = "// "; @@ -81,6 +89,11 @@ fn extract_comment_blocks(text: &str) -> Vec> { let mut block = vec![]; for line in lines { + if line == "//" && allow_blocks_with_empty_lins { + block.push(String::new()); + continue; + } + let is_comment = line.starts_with(prefix); if is_comment { block.push(line[prefix.len()..].to_string()); diff --git a/xtask/src/codegen/gen_assists_docs.rs b/xtask/src/codegen/gen_assists_docs.rs index 654ae09d66..e313820d18 100644 --- a/xtask/src/codegen/gen_assists_docs.rs +++ b/xtask/src/codegen/gen_assists_docs.rs @@ -1,7 +1,7 @@ use std::{fs, path::Path}; use crate::{ - codegen::{self, extract_comment_blocks, Mode}, + codegen::{self, extract_comment_blocks_with_empty_lines, Mode}, project_root, Result, }; @@ -34,7 +34,7 @@ fn collect_assists() -> Result> { fn collect_file(acc: &mut Vec, path: &Path) -> Result<()> { let text = fs::read_to_string(path)?; - let comment_blocks = extract_comment_blocks(&text); + let comment_blocks = extract_comment_blocks_with_empty_lines(&text); for block in comment_blocks { // FIXME: doesn't support blank lines yet, need to tweak @@ -45,7 +45,11 @@ fn collect_assists() -> Result> { continue; } let id = first_line["Assist: ".len()..].to_string(); - assert!(id.chars().all(|it| it.is_ascii_lowercase() || it == '_')); + assert!( + id.chars().all(|it| it.is_ascii_lowercase() || it == '_'), + "invalid assist id: {:?}", + id + ); let doc = take_until(lines.by_ref(), "```"); let before = take_until(lines.by_ref(), "```");