generate more assists docs

This commit is contained in:
Aleksey Kladov 2019-10-25 23:38:15 +03:00
parent e6cb06d285
commit d385438bcc
11 changed files with 419 additions and 114 deletions

View file

@ -1,5 +1,3 @@
//! FIXME: write short doc here
use hir::db::HirDatabase; use hir::db::HirDatabase;
use ra_syntax::{ use ra_syntax::{
ast::{self, AstNode, AttrsOwner}, ast::{self, AstNode, AttrsOwner},
@ -9,6 +7,22 @@ use ra_syntax::{
use crate::{Assist, AssistCtx, AssistId}; 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<impl HirDatabase>) -> Option<Assist> { pub(crate) fn add_derive(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
let nominal = ctx.node_at_offset::<ast::NominalDef>()?; let nominal = ctx.node_at_offset::<ast::NominalDef>()?;
let node_start = derive_insertion_offset(&nominal)?; let node_start = derive_insertion_offset(&nominal)?;

View file

@ -1,5 +1,3 @@
//! FIXME: write short doc here
use hir::{db::HirDatabase, HirDisplay, Ty}; use hir::{db::HirDatabase, HirDisplay, Ty};
use ra_syntax::{ use ra_syntax::{
ast::{self, AstNode, LetStmt, NameOwner}, ast::{self, AstNode, LetStmt, NameOwner},
@ -8,7 +6,19 @@ use ra_syntax::{
use crate::{Assist, AssistCtx, AssistId}; 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<impl HirDatabase>) -> Option<Assist> { pub(crate) fn add_explicit_type(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
let stmt = ctx.node_at_offset::<LetStmt>()?; let stmt = ctx.node_at_offset::<LetStmt>()?;
let expr = stmt.initializer()?; let expr = stmt.initializer()?;

View file

@ -1,5 +1,3 @@
//! FIXME: write short doc here
use format_buf::format; use format_buf::format;
use hir::db::HirDatabase; use hir::db::HirDatabase;
use join_to_string::join; use join_to_string::join;
@ -10,6 +8,23 @@ use ra_syntax::{
use crate::{Assist, AssistCtx, AssistId}; use crate::{Assist, AssistCtx, AssistId};
// Assist: add_impl
// Adds a new inherent impl for a type
// ```
// struct Ctx<T: Clone> {
// data: T,<|>
// }
// ```
// ->
// ```
// struct Ctx<T: Clone> {
// data: T,
// }
//
// impl<T: Clone> Ctx<T> {
//
// }
// ```
pub(crate) fn add_impl(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { pub(crate) fn add_impl(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
let nominal = ctx.node_at_offset::<ast::NominalDef>()?; let nominal = ctx.node_at_offset::<ast::NominalDef>()?;
let name = nominal.name()?; let name = nominal.name()?;

View file

@ -1,5 +1,3 @@
//! FIXME: write short doc here
use hir::{db::HirDatabase, HasSource}; use hir::{db::HirDatabase, HasSource};
use ra_syntax::{ use ra_syntax::{
ast::{self, edit, make, AstNode, NameOwner}, ast::{self, edit, make, AstNode, NameOwner},
@ -14,6 +12,32 @@ enum AddMissingImplMembersMode {
NoDefaultMethods, 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<impl HirDatabase>) -> Option<Assist> { pub(crate) fn add_missing_impl_members(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
add_missing_impl_members_inner( add_missing_impl_members_inner(
ctx, ctx,
@ -23,6 +47,36 @@ pub(crate) fn add_missing_impl_members(ctx: AssistCtx<impl HirDatabase>) -> 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<impl HirDatabase>) -> Option<Assist> { pub(crate) fn add_missing_default_members(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
add_missing_impl_members_inner( add_missing_impl_members_inner(
ctx, ctx,

View file

@ -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 hir::db::HirDatabase;
use ra_syntax::ast::{self, AstNode}; use ra_syntax::ast::{self, AstNode};
use ra_syntax::SyntaxNode; use ra_syntax::SyntaxNode;
use crate::{Assist, AssistCtx, AssistId}; use crate::{Assist, AssistCtx, AssistId};
/// Assist for applying demorgan's law // 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 transforms expressions of the form `!l || !r` into `!(l && r)`.
/// This also works with `&&`. This assist can only be applied with the cursor // 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. // on either `||` or `&&`, with both operands being a negation of some kind.
/// This means something of the form `!x` or `x != y`. // 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<impl HirDatabase>) -> Option<Assist> { pub(crate) fn apply_demorgan(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
let expr = ctx.node_at_offset::<ast::BinExpr>()?; let expr = ctx.node_at_offset::<ast::BinExpr>()?;
let op = expr.op_kind()?; let op = expr.op_kind()?;

View file

@ -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 (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 frange = FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
let (_assist_id, action) = let (_assist_id, action) = crate::assists(&db, frange)
crate::assists(&db, frange).into_iter().find(|(id, _)| id.id.0 == assist_id).unwrap(); .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); let actual = action.edit.apply(&before);
assert_eq_text!(after, &actual); assert_eq_text!(after, &actual);

View file

@ -2,6 +2,145 @@
use super::check; 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<T: Clone> {
data: T,<|>
}
"#####,
r#####"
struct Ctx<T: Clone> {
data: T,
}
impl<T: Clone> Ctx<T> {
}
"#####,
)
}
#[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] #[test]
fn doctest_convert_to_guarded_return() { fn doctest_convert_to_guarded_return() {
check( check(

View file

@ -1,5 +1,142 @@
# Assists # 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<T: Clone> {
data: T,<|>
}
// AFTER
struct Ctx<T: Clone> {
data: T,
}
impl<T: Clone> Ctx<T> {
}
```
## `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` ## `convert_to_guarded_return`
Replace a large conditional with a guarded return. Replace a large conditional with a guarded return.

View file

@ -104,84 +104,6 @@ the VS Code side to be able to position cursor. `<|>` signifies cursor
See [assists.md](./assists.md) 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 - Import path
```rust ```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<i32>) = (&2, Some(1));
}
```
- Move guard expression to match arm body - Move guard expression to match arm body
```rust ```rust
// before: // before:

View file

@ -74,6 +74,14 @@ fn reformat(text: impl std::fmt::Display) -> Result<String> {
} }
fn extract_comment_blocks(text: &str) -> Vec<Vec<String>> { fn extract_comment_blocks(text: &str) -> Vec<Vec<String>> {
do_extract_comment_blocks(text, false)
}
fn extract_comment_blocks_with_empty_lines(text: &str) -> Vec<Vec<String>> {
do_extract_comment_blocks(text, true)
}
fn do_extract_comment_blocks(text: &str, allow_blocks_with_empty_lins: bool) -> Vec<Vec<String>> {
let mut res = Vec::new(); let mut res = Vec::new();
let prefix = "// "; let prefix = "// ";
@ -81,6 +89,11 @@ fn extract_comment_blocks(text: &str) -> Vec<Vec<String>> {
let mut block = vec![]; let mut block = vec![];
for line in lines { for line in lines {
if line == "//" && allow_blocks_with_empty_lins {
block.push(String::new());
continue;
}
let is_comment = line.starts_with(prefix); let is_comment = line.starts_with(prefix);
if is_comment { if is_comment {
block.push(line[prefix.len()..].to_string()); block.push(line[prefix.len()..].to_string());

View file

@ -1,7 +1,7 @@
use std::{fs, path::Path}; use std::{fs, path::Path};
use crate::{ use crate::{
codegen::{self, extract_comment_blocks, Mode}, codegen::{self, extract_comment_blocks_with_empty_lines, Mode},
project_root, Result, project_root, Result,
}; };
@ -34,7 +34,7 @@ fn collect_assists() -> Result<Vec<Assist>> {
fn collect_file(acc: &mut Vec<Assist>, path: &Path) -> Result<()> { fn collect_file(acc: &mut Vec<Assist>, path: &Path) -> Result<()> {
let text = fs::read_to_string(path)?; 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 { for block in comment_blocks {
// FIXME: doesn't support blank lines yet, need to tweak // FIXME: doesn't support blank lines yet, need to tweak
@ -45,7 +45,11 @@ fn collect_assists() -> Result<Vec<Assist>> {
continue; continue;
} }
let id = first_line["Assist: ".len()..].to_string(); 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 doc = take_until(lines.by_ref(), "```");
let before = take_until(lines.by_ref(), "```"); let before = take_until(lines.by_ref(), "```");