diff --git a/docs/user/assists.md b/docs/user/generated_assists.adoc similarity index 77% rename from docs/user/assists.md rename to docs/user/generated_assists.adoc index 04387e3b0e..b8cdfb1cdf 100644 --- a/docs/user/assists.md +++ b/docs/user/generated_assists.adoc @@ -1,18 +1,16 @@ -# Assists - -Cursor position or selection is signified by `┃` character. - - -## `add_custom_impl` +[discrete] +=== `add_custom_impl` Adds impl block for derived trait. +.Before ```rust -// BEFORE #[derive(Deb┃ug, Display)] struct S; +``` -// AFTER +.After +```rust #[derive(Display)] struct S; @@ -21,18 +19,22 @@ impl Debug for S { } ``` -## `add_derive` + +[discrete] +=== `add_derive` Adds a new `#[derive()]` clause to a struct or enum. +.Before ```rust -// BEFORE struct Point { x: u32, y: u32,┃ } +``` -// AFTER +.After +```rust #[derive($0)] struct Point { x: u32, @@ -40,31 +42,39 @@ struct Point { } ``` -## `add_explicit_type` + +[discrete] +=== `add_explicit_type` Specify type for a let binding. +.Before ```rust -// BEFORE fn main() { let x┃ = 92; } +``` -// AFTER +.After +```rust fn main() { let x: i32 = 92; } ``` -## `add_from_impl_for_enum` + +[discrete] +=== `add_from_impl_for_enum` Adds a From impl for an enum variant with one tuple field. +.Before ```rust -// BEFORE enum A { ┃One(u32) } +``` -// AFTER +.After +```rust enum A { One(u32) } impl From for A { @@ -74,20 +84,24 @@ impl From for A { } ``` -## `add_function` + +[discrete] +=== `add_function` Adds a stub function with a signature matching the function under the cursor. +.Before ```rust -// BEFORE struct Baz; fn baz() -> Baz { Baz } fn foo() { bar┃("", baz()); } +``` -// AFTER +.After +```rust struct Baz; fn baz() -> Baz { Baz } fn foo() { @@ -100,33 +114,41 @@ fn bar(arg: &str, baz: Baz) { ``` -## `add_hash` + +[discrete] +=== `add_hash` Adds a hash to a raw string literal. +.Before ```rust -// BEFORE fn main() { r#"Hello,┃ World!"#; } +``` -// AFTER +.After +```rust fn main() { r##"Hello, World!"##; } ``` -## `add_impl` + +[discrete] +=== `add_impl` Adds a new inherent impl for a type. +.Before ```rust -// BEFORE struct Ctx { data: T,┃ } +``` -// AFTER +.After +```rust struct Ctx { data: T, } @@ -136,12 +158,14 @@ impl Ctx { } ``` -## `add_impl_default_members` + +[discrete] +=== `add_impl_default_members` Adds scaffold for overriding default impl members. +.Before ```rust -// BEFORE trait Trait { Type X; fn foo(&self); @@ -153,8 +177,10 @@ impl Trait for () { fn foo(&self) {}┃ } +``` -// AFTER +.After +```rust trait Trait { Type X; fn foo(&self); @@ -169,12 +195,14 @@ impl Trait for () { } ``` -## `add_impl_missing_members` + +[discrete] +=== `add_impl_missing_members` Adds scaffold for required impl members. +.Before ```rust -// BEFORE trait Trait { Type X; fn foo(&self) -> T; @@ -184,8 +212,10 @@ trait Trait { impl Trait for () {┃ } +``` -// AFTER +.After +```rust trait Trait { Type X; fn foo(&self) -> T; @@ -200,17 +230,21 @@ impl Trait for () { } ``` -## `add_new` + +[discrete] +=== `add_new` Adds a new inherent impl for a type. +.Before ```rust -// BEFORE struct Ctx { data: T,┃ } +``` -// AFTER +.After +```rust struct Ctx { data: T, } @@ -221,25 +255,31 @@ impl Ctx { ``` -## `add_turbo_fish` + +[discrete] +=== `add_turbo_fish` Adds `::<_>` to a call of a generic method or function. +.Before ```rust -// BEFORE fn make() -> T { todo!() } fn main() { let x = make┃(); } +``` -// AFTER +.After +```rust fn make() -> T { todo!() } fn main() { let x = make::<${0:_}>(); } ``` -## `apply_demorgan` + +[discrete] +=== `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)`. @@ -247,29 +287,35 @@ 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`. +.Before ```rust -// BEFORE fn main() { if x != 4 ||┃ !y {} } +``` -// AFTER +.After +```rust fn main() { if !(x == 4 && y) {} } ``` -## `auto_import` + +[discrete] +=== `auto_import` If the name is unresolved, provides all possible imports for it. +.Before ```rust -// BEFORE fn main() { let map = HashMap┃::new(); } +``` -// AFTER +.After +```rust use std::collections::HashMap; fn main() { @@ -277,12 +323,14 @@ fn main() { } ``` -## `change_lifetime_anon_to_named` + +[discrete] +=== `change_lifetime_anon_to_named` Change an anonymous lifetime to a named lifetime. +.Before ```rust -// BEFORE impl Cursor<'_┃> { fn node(self) -> &SyntaxNode { match self { @@ -290,8 +338,10 @@ impl Cursor<'_┃> { } } } +``` -// AFTER +.After +```rust impl<'a> Cursor<'a> { fn node(self) -> &SyntaxNode { match self { @@ -301,44 +351,56 @@ impl<'a> Cursor<'a> { } ``` -## `change_return_type_to_result` + +[discrete] +=== `change_return_type_to_result` Change the function's return type to Result. +.Before ```rust -// BEFORE fn foo() -> i32┃ { 42i32 } +``` -// AFTER +.After +```rust fn foo() -> Result { Ok(42i32) } ``` -## `change_visibility` + +[discrete] +=== `change_visibility` Adds or changes existing visibility specifier. +.Before ```rust -// BEFORE ┃fn frobnicate() {} +``` -// AFTER +.After +```rust pub(crate) fn frobnicate() {} ``` -## `convert_to_guarded_return` + +[discrete] +=== `convert_to_guarded_return` Replace a large conditional with a guarded return. +.Before ```rust -// BEFORE fn main() { ┃if cond { foo(); bar(); } } +``` -// AFTER +.After +```rust fn main() { if !cond { return; @@ -348,12 +410,14 @@ fn main() { } ``` -## `fill_match_arms` + +[discrete] +=== `fill_match_arms` Adds missing clauses to a `match` expression. +.Before ```rust -// BEFORE enum Action { Move { distance: u32 }, Stop } fn handle(action: Action) { @@ -361,8 +425,10 @@ fn handle(action: Action) { ┃ } } +``` -// AFTER +.After +```rust enum Action { Move { distance: u32 }, Stop } fn handle(action: Action) { @@ -373,20 +439,24 @@ fn handle(action: Action) { } ``` -## `fix_visibility` + +[discrete] +=== `fix_visibility` Makes inaccessible item public. +.Before ```rust -// BEFORE mod m { fn frobnicate() {} } fn main() { m::frobnicate┃() {} } +``` -// AFTER +.After +```rust mod m { $0pub(crate) fn frobnicate() {} } @@ -395,154 +465,192 @@ fn main() { } ``` -## `flip_binexpr` + +[discrete] +=== `flip_binexpr` Flips operands of a binary expression. +.Before ```rust -// BEFORE fn main() { let _ = 90 +┃ 2; } +``` -// AFTER +.After +```rust fn main() { let _ = 2 + 90; } ``` -## `flip_comma` + +[discrete] +=== `flip_comma` Flips two comma-separated items. +.Before ```rust -// BEFORE fn main() { ((1, 2),┃ (3, 4)); } +``` -// AFTER +.After +```rust fn main() { ((3, 4), (1, 2)); } ``` -## `flip_trait_bound` + +[discrete] +=== `flip_trait_bound` Flips two trait bounds. +.Before ```rust -// BEFORE fn foo() { } +``` -// AFTER +.After +```rust fn foo() { } ``` -## `inline_local_variable` + +[discrete] +=== `inline_local_variable` Inlines local variable. +.Before ```rust -// BEFORE fn main() { let x┃ = 1 + 2; x * 4; } +``` -// AFTER +.After +```rust fn main() { (1 + 2) * 4; } ``` -## `introduce_variable` + +[discrete] +=== `introduce_variable` Extracts subexpression into a variable. +.Before ```rust -// BEFORE fn main() { ┃(1 + 2)┃ * 4; } +``` -// AFTER +.After +```rust fn main() { let $0var_name = (1 + 2); var_name * 4; } ``` -## `invert_if` + +[discrete] +=== `invert_if` Apply invert_if This transforms if expressions of the form `if !x {A} else {B}` into `if x {B} else {A}` This also works with `!=`. This assist can only be applied with the cursor on `if`. +.Before ```rust -// BEFORE fn main() { if┃ !y { A } else { B } } +``` -// AFTER +.After +```rust fn main() { if y { B } else { A } } ``` -## `make_raw_string` + +[discrete] +=== `make_raw_string` Adds `r#` to a plain string literal. +.Before ```rust -// BEFORE fn main() { "Hello,┃ World!"; } +``` -// AFTER +.After +```rust fn main() { r#"Hello, World!"#; } ``` -## `make_usual_string` + +[discrete] +=== `make_usual_string` Turns a raw string into a plain string. +.Before ```rust -// BEFORE fn main() { r#"Hello,┃ "World!""#; } +``` -// AFTER +.After +```rust fn main() { "Hello, \"World!\""; } ``` -## `merge_imports` + +[discrete] +=== `merge_imports` Merges two imports with a common prefix. +.Before ```rust -// BEFORE use std::┃fmt::Formatter; use std::io; +``` -// AFTER +.After +```rust use std::{fmt::Formatter, io}; ``` -## `merge_match_arms` + +[discrete] +=== `merge_match_arms` Merges identical match arms. +.Before ```rust -// BEFORE enum Action { Move { distance: u32 }, Stop } fn handle(action: Action) { @@ -551,8 +659,10 @@ fn handle(action: Action) { Action::Stop => foo(), } } +``` -// AFTER +.After +```rust enum Action { Move { distance: u32 }, Stop } fn handle(action: Action) { @@ -562,12 +672,14 @@ fn handle(action: Action) { } ``` -## `move_arm_cond_to_match_guard` + +[discrete] +=== `move_arm_cond_to_match_guard` Moves if expression from match arm body into a guard. +.Before ```rust -// BEFORE enum Action { Move { distance: u32 }, Stop } fn handle(action: Action) { @@ -576,8 +688,10 @@ fn handle(action: Action) { _ => (), } } +``` -// AFTER +.After +```rust enum Action { Move { distance: u32 }, Stop } fn handle(action: Action) { @@ -588,28 +702,34 @@ fn handle(action: Action) { } ``` -## `move_bounds_to_where_clause` + +[discrete] +=== `move_bounds_to_where_clause` Moves inline type bounds to a where clause. +.Before ```rust -// BEFORE fn apply U>(f: F, x: T) -> U { f(x) } +``` -// AFTER +.After +```rust fn apply(f: F, x: T) -> U where F: FnOnce(T) -> U { f(x) } ``` -## `move_guard_to_arm_body` + +[discrete] +=== `move_guard_to_arm_body` Moves match guard into match arm body. +.Before ```rust -// BEFORE enum Action { Move { distance: u32 }, Stop } fn handle(action: Action) { @@ -618,8 +738,10 @@ fn handle(action: Action) { _ => (), } } +``` -// AFTER +.After +```rust enum Action { Move { distance: u32 }, Stop } fn handle(action: Action) { @@ -630,75 +752,93 @@ fn handle(action: Action) { } ``` -## `remove_dbg` + +[discrete] +=== `remove_dbg` Removes `dbg!()` macro call. +.Before ```rust -// BEFORE fn main() { ┃dbg!(92); } +``` -// AFTER +.After +```rust fn main() { 92; } ``` -## `remove_hash` + +[discrete] +=== `remove_hash` Removes a hash from a raw string literal. +.Before ```rust -// BEFORE fn main() { r#"Hello,┃ World!"#; } +``` -// AFTER +.After +```rust fn main() { r"Hello, World!"; } ``` -## `remove_mut` + +[discrete] +=== `remove_mut` Removes the `mut` keyword. +.Before ```rust -// BEFORE impl Walrus { fn feed(&mut┃ self, amount: u32) {} } +``` -// AFTER +.After +```rust impl Walrus { fn feed(&self, amount: u32) {} } ``` -## `reorder_fields` + +[discrete] +=== `reorder_fields` Reorder the fields of record literals and record patterns in the same order as in the definition. +.Before ```rust -// BEFORE struct Foo {foo: i32, bar: i32}; const test: Foo = ┃Foo {bar: 0, foo: 1} +``` -// AFTER +.After +```rust struct Foo {foo: i32, bar: i32}; const test: Foo = Foo {foo: 1, bar: 0} ``` -## `replace_if_let_with_match` + +[discrete] +=== `replace_if_let_with_match` Replaces `if let` with an else branch with a `match` expression. +.Before ```rust -// BEFORE enum Action { Move { distance: u32 }, Stop } fn handle(action: Action) { @@ -708,8 +848,10 @@ fn handle(action: Action) { bar() } } +``` -// AFTER +.After +```rust enum Action { Move { distance: u32 }, Stop } fn handle(action: Action) { @@ -720,20 +862,24 @@ fn handle(action: Action) { } ``` -## `replace_let_with_if_let` + +[discrete] +=== `replace_let_with_if_let` Replaces `let` with an `if-let`. +.Before ```rust -// BEFORE fn main(action: Action) { ┃let x = compute(); } fn compute() -> Option { None } +``` -// AFTER +.After +```rust fn main(action: Action) { if let Some(x) = compute() { @@ -743,33 +889,41 @@ fn main(action: Action) { fn compute() -> Option { None } ``` -## `replace_qualified_name_with_use` + +[discrete] +=== `replace_qualified_name_with_use` Adds a use statement for a given fully-qualified name. +.Before ```rust -// BEFORE fn process(map: std::collections::┃HashMap) {} +``` -// AFTER +.After +```rust use std::collections::HashMap; fn process(map: HashMap) {} ``` -## `replace_unwrap_with_match` + +[discrete] +=== `replace_unwrap_with_match` Replaces `unwrap` a `match` expression. Works for Result and Option. +.Before ```rust -// BEFORE enum Result { Ok(T), Err(E) } fn main() { let x: Result = Result::Ok(92); let y = x.┃unwrap(); } +``` -// AFTER +.After +```rust enum Result { Ok(T), Err(E) } fn main() { let x: Result = Result::Ok(92); @@ -780,31 +934,39 @@ fn main() { } ``` -## `split_import` + +[discrete] +=== `split_import` Wraps the tail of import into braces. +.Before ```rust -// BEFORE use std::┃collections::HashMap; +``` -// AFTER +.After +```rust use std::{collections::HashMap}; ``` -## `unwrap_block` + +[discrete] +=== `unwrap_block` This assist removes if...else, for, while and loop control statements to just keep the body. +.Before ```rust -// BEFORE fn foo() { if true {┃ println!("foo"); } } +``` -// AFTER +.After +```rust fn foo() { println!("foo"); } diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc index f40139804d..27b3792135 100644 --- a/docs/user/manual.adoc +++ b/docs/user/manual.adoc @@ -278,5 +278,6 @@ include::./generated_features.adoc[] Assists, or code actions, are small local refactorings, available in a particular context. They are usually triggered by a shortcut or by clicking a light bulb icon in the editor. +Cursor position or selection is signified by `┃` character. -See [assists.md](./assists.md) for the list of available assists. +include::./generated_assists.adoc[] diff --git a/xtask/src/codegen.rs b/xtask/src/codegen.rs index f47d54125f..f3917a244a 100644 --- a/xtask/src/codegen.rs +++ b/xtask/src/codegen.rs @@ -10,9 +10,12 @@ mod gen_parser_tests; mod gen_assists_docs; mod gen_feature_docs; -use std::{mem, path::Path}; +use std::{ + fmt, mem, + path::{Path, PathBuf}, +}; -use crate::{not_bash::fs2, Result}; +use crate::{not_bash::fs2, project_root, Result}; pub use self::{ gen_assists_docs::generate_assists_docs, gen_feature_docs::generate_feature_docs, @@ -29,7 +32,6 @@ const AST_TOKENS: &str = "crates/ra_syntax/src/ast/generated/tokens.rs"; const ASSISTS_DIR: &str = "crates/ra_assists/src/handlers"; const ASSISTS_TESTS: &str = "crates/ra_assists/src/tests/generated.rs"; -const ASSISTS_DOCS: &str = "docs/user/assists.md"; #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum Mode { @@ -107,3 +109,28 @@ fn do_extract_comment_blocks(text: &str, allow_blocks_with_empty_lines: bool) -> } res } + +#[derive(Debug)] +struct Location { + file: PathBuf, +} + +impl Location { + fn new(file: PathBuf) -> Self { + Self { file } + } +} + +impl fmt::Display for Location { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let path = self.file.strip_prefix(&project_root()).unwrap().display().to_string(); + let path = path.replace('\\', "/"); + let name = self.file.file_name().unwrap(); + write!( + f, + "https://github.com/rust-analyzer/rust-analyzer/blob/master/{}[{}]", + path, + name.to_str().unwrap() + ) + } +} diff --git a/xtask/src/codegen/gen_assists_docs.rs b/xtask/src/codegen/gen_assists_docs.rs index 6ebeb8aea4..15a02d317b 100644 --- a/xtask/src/codegen/gen_assists_docs.rs +++ b/xtask/src/codegen/gen_assists_docs.rs @@ -1,22 +1,28 @@ //! Generates `assists.md` documentation. -use std::{fs, path::Path}; +use std::{fmt, fs, path::Path}; use crate::{ - codegen::{self, extract_comment_blocks_with_empty_lines, Mode}, + codegen::{self, extract_comment_blocks_with_empty_lines, Location, Mode}, project_root, rust_files, Result, }; pub fn generate_assists_docs(mode: Mode) -> Result<()> { let assists = Assist::collect()?; generate_tests(&assists, mode)?; - generate_docs(&assists, mode)?; + + let contents = assists.into_iter().map(|it| it.to_string()).collect::>().join("\n\n"); + let contents = contents.trim().to_string() + "\n"; + let dst = project_root().join("docs/user/generated_assists.adoc"); + codegen::update(&dst, &contents, mode)?; + Ok(()) } #[derive(Debug)] struct Assist { id: String, + location: Location, doc: String, before: String, after: String, @@ -58,7 +64,8 @@ impl Assist { assert_eq!(lines.next().unwrap().as_str(), "->"); assert_eq!(lines.next().unwrap().as_str(), "```"); let after = take_until(lines.by_ref(), "```"); - acc.push(Assist { id, doc, before, after }) + let location = Location::new(path.to_path_buf()); + acc.push(Assist { id, location, doc, before, after }) } fn take_until<'a>(lines: impl Iterator, marker: &str) -> String { @@ -76,6 +83,31 @@ impl Assist { } } +impl fmt::Display for Assist { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let before = self.before.replace("<|>", "┃"); // Unicode pseudo-graphics bar + let after = self.after.replace("<|>", "┃"); + writeln!( + f, + "[discrete]\n=== `{}` + +{} + +.Before +```rust +{}``` + +.After +```rust +{}```", + self.id, + self.doc, + hide_hash_comments(&before), + hide_hash_comments(&after) + ) + } +} + fn generate_tests(assists: &[Assist], mode: Mode) -> Result<()> { let mut buf = String::from("use super::check_doc_test;\n"); @@ -103,37 +135,6 @@ r#####" codegen::update(&project_root().join(codegen::ASSISTS_TESTS), &buf, mode) } -fn generate_docs(assists: &[Assist], mode: Mode) -> Result<()> { - let mut buf = String::from( - "# Assists\n\nCursor position or selection is signified by `┃` character.\n\n", - ); - - for assist in assists { - let before = assist.before.replace("<|>", "┃"); // Unicode pseudo-graphics bar - let after = assist.after.replace("<|>", "┃"); - let docs = format!( - " -## `{}` - -{} - -```rust -// BEFORE -{} -// AFTER -{}``` -", - assist.id, - assist.doc, - hide_hash_comments(&before), - hide_hash_comments(&after) - ); - buf.push_str(&docs); - } - - codegen::update(&project_root().join(codegen::ASSISTS_DOCS), &buf, mode) -} - fn hide_hash_comments(text: &str) -> String { text.split('\n') // want final newline .filter(|&it| !(it.starts_with("# ") || it == "#")) diff --git a/xtask/src/codegen/gen_feature_docs.rs b/xtask/src/codegen/gen_feature_docs.rs index dbe583e8e4..731e7ecf25 100644 --- a/xtask/src/codegen/gen_feature_docs.rs +++ b/xtask/src/codegen/gen_feature_docs.rs @@ -3,7 +3,7 @@ use std::{fmt, fs, path::PathBuf}; use crate::{ - codegen::{self, extract_comment_blocks_with_empty_lines, Mode}, + codegen::{self, extract_comment_blocks_with_empty_lines, Location, Mode}, project_root, rust_files, Result, }; @@ -19,7 +19,7 @@ pub fn generate_feature_docs(mode: Mode) -> Result<()> { #[derive(Debug)] struct Feature { id: String, - path: PathBuf, + location: Location, doc: String, } @@ -40,7 +40,7 @@ impl Feature { let id = block.id; assert!(is_valid_feature_name(&id), "invalid feature name: {:?}", id); let doc = block.contents.join("\n"); - acc.push(Feature { id, path: path.clone(), doc }) + acc.push(Feature { id, location: Location::new(path.clone()), doc }) } Ok(()) @@ -69,20 +69,6 @@ fn is_valid_feature_name(feature: &str) -> bool { impl fmt::Display for Feature { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - writeln!(f, "=== {}", self.id)?; - let path = self.path.strip_prefix(&project_root()).unwrap().display().to_string(); - let path = path.replace('\\', "/"); - let name = self.path.file_name().unwrap(); - - //FIXME: generate line number as well - writeln!( - f, - "**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/{}[{}]", - path, - name.to_str().unwrap(), - )?; - - writeln!(f, "{}", self.doc)?; - Ok(()) + writeln!(f, "=== {}\n**Source:** {}\n{}", self.id, self.location, self.doc) } } diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs index 06043d19f8..874957885e 100644 --- a/xtask/src/lib.rs +++ b/xtask/src/lib.rs @@ -191,7 +191,7 @@ Release: release:{}[] let path = changelog_dir.join(format!("{}-changelog-{}.adoc", today, changelog_n)); fs2::write(&path, &contents)?; - for &adoc in ["manual.adoc", "generated_features.adoc"].iter() { + for &adoc in ["manual.adoc", "generated_features.adoc", "generated_assists.adoc"].iter() { let src = project_root().join("./docs/user/").join(adoc); let dst = website_root.join(adoc); fs2::copy(src, dst)?;