aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock36
-rw-r--r--crates/ra_hir_def/src/body/lower.rs68
-rw-r--r--crates/ra_hir_def/src/body/scope.rs4
-rw-r--r--crates/ra_hir_def/src/expr.rs19
-rw-r--r--crates/ra_hir_expand/src/name.rs5
-rw-r--r--crates/ra_hir_ty/src/infer.rs11
-rw-r--r--crates/ra_hir_ty/src/infer/expr.rs45
-rw-r--r--crates/ra_hir_ty/src/tests/simple.rs54
-rw-r--r--crates/ra_syntax/src/ast/generated/nodes.rs1
-rw-r--r--docs/user/generated_assists.adoc (renamed from docs/user/assists.md)466
-rw-r--r--docs/user/generated_features.adoc38
-rw-r--r--docs/user/manual.adoc (renamed from docs/user/readme.adoc)40
-rw-r--r--xtask/src/ast_src.rs2
-rw-r--r--xtask/src/codegen.rs66
-rw-r--r--xtask/src/codegen/gen_assists_docs.rs73
-rw-r--r--xtask/src/codegen/gen_feature_docs.rs23
-rw-r--r--xtask/src/lib.rs6
17 files changed, 663 insertions, 294 deletions
diff --git a/Cargo.lock b/Cargo.lock
index def4ed45e..9981a2e33 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -243,9 +243,9 @@ dependencies = [
243 243
244[[package]] 244[[package]]
245name = "crossbeam-queue" 245name = "crossbeam-queue"
246version = "0.2.1" 246version = "0.2.2"
247source = "registry+https://github.com/rust-lang/crates.io-index" 247source = "registry+https://github.com/rust-lang/crates.io-index"
248checksum = "c695eeca1e7173472a32221542ae469b3e9aac3a4fc81f7696bcad82029493db" 248checksum = "ab6bffe714b6bb07e42f201352c34f51fefd355ace793f9e638ebd52d23f98d2"
249dependencies = [ 249dependencies = [
250 "cfg-if", 250 "cfg-if",
251 "crossbeam-utils", 251 "crossbeam-utils",
@@ -640,9 +640,9 @@ dependencies = [
640 640
641[[package]] 641[[package]]
642name = "lsp-types" 642name = "lsp-types"
643version = "0.74.1" 643version = "0.74.2"
644source = "registry+https://github.com/rust-lang/crates.io-index" 644source = "registry+https://github.com/rust-lang/crates.io-index"
645checksum = "57c0e6a2b8837d27b29deb3f3e6dc1c6d2f57947677f9be1024e482ec5b59525" 645checksum = "b360754e89e0e13c114245131382ba921d4ff1efabb918e549422938aaa8d392"
646dependencies = [ 646dependencies = [
647 "base64", 647 "base64",
648 "bitflags", 648 "bitflags",
@@ -809,9 +809,9 @@ dependencies = [
809 809
810[[package]] 810[[package]]
811name = "paste" 811name = "paste"
812version = "0.1.14" 812version = "0.1.15"
813source = "registry+https://github.com/rust-lang/crates.io-index" 813source = "registry+https://github.com/rust-lang/crates.io-index"
814checksum = "3431e8f72b90f8a7af91dec890d9814000cb371258e0ec7370d93e085361f531" 814checksum = "d53181dcd37421c08d3b69f887784956674d09c3f9a47a04fece2b130a5b346b"
815dependencies = [ 815dependencies = [
816 "paste-impl", 816 "paste-impl",
817 "proc-macro-hack", 817 "proc-macro-hack",
@@ -819,9 +819,9 @@ dependencies = [
819 819
820[[package]] 820[[package]]
821name = "paste-impl" 821name = "paste-impl"
822version = "0.1.14" 822version = "0.1.15"
823source = "registry+https://github.com/rust-lang/crates.io-index" 823source = "registry+https://github.com/rust-lang/crates.io-index"
824checksum = "25af5fc872ba284d8d84608bf8a0fa9b5376c96c23f503b007dfd9e34dde5606" 824checksum = "05ca490fa1c034a71412b4d1edcb904ec5a0981a4426c9eb2128c0fda7a68d17"
825dependencies = [ 825dependencies = [
826 "proc-macro-hack", 826 "proc-macro-hack",
827 "proc-macro2", 827 "proc-macro2",
@@ -1291,9 +1291,9 @@ checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
1291 1291
1292[[package]] 1292[[package]]
1293name = "regex" 1293name = "regex"
1294version = "1.3.7" 1294version = "1.3.9"
1295source = "registry+https://github.com/rust-lang/crates.io-index" 1295source = "registry+https://github.com/rust-lang/crates.io-index"
1296checksum = "a6020f034922e3194c711b82a627453881bc4682166cabb07134a10c26ba7692" 1296checksum = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6"
1297dependencies = [ 1297dependencies = [
1298 "aho-corasick", 1298 "aho-corasick",
1299 "memchr", 1299 "memchr",
@@ -1303,9 +1303,9 @@ dependencies = [
1303 1303
1304[[package]] 1304[[package]]
1305name = "regex-syntax" 1305name = "regex-syntax"
1306version = "0.6.17" 1306version = "0.6.18"
1307source = "registry+https://github.com/rust-lang/crates.io-index" 1307source = "registry+https://github.com/rust-lang/crates.io-index"
1308checksum = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae" 1308checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8"
1309 1309
1310[[package]] 1310[[package]]
1311name = "relative-path" 1311name = "relative-path"
@@ -1491,18 +1491,18 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
1491 1491
1492[[package]] 1492[[package]]
1493name = "serde" 1493name = "serde"
1494version = "1.0.110" 1494version = "1.0.111"
1495source = "registry+https://github.com/rust-lang/crates.io-index" 1495source = "registry+https://github.com/rust-lang/crates.io-index"
1496checksum = "99e7b308464d16b56eba9964e4972a3eee817760ab60d88c3f86e1fecb08204c" 1496checksum = "c9124df5b40cbd380080b2cc6ab894c040a3070d995f5c9dc77e18c34a8ae37d"
1497dependencies = [ 1497dependencies = [
1498 "serde_derive", 1498 "serde_derive",
1499] 1499]
1500 1500
1501[[package]] 1501[[package]]
1502name = "serde_derive" 1502name = "serde_derive"
1503version = "1.0.110" 1503version = "1.0.111"
1504source = "registry+https://github.com/rust-lang/crates.io-index" 1504source = "registry+https://github.com/rust-lang/crates.io-index"
1505checksum = "818fbf6bfa9a42d3bfcaca148547aa00c7b915bec71d1757aa2d44ca68771984" 1505checksum = "3f2c3ac8e6ca1e9c80b8be1023940162bf81ae3cffbb1809474152f2ce1eb250"
1506dependencies = [ 1506dependencies = [
1507 "proc-macro2", 1507 "proc-macro2",
1508 "quote", 1508 "quote",
@@ -1576,9 +1576,9 @@ checksum = "ab16ced94dbd8a46c82fd81e3ed9a8727dac2977ea869d217bcc4ea1f122e81f"
1576 1576
1577[[package]] 1577[[package]]
1578name = "syn" 1578name = "syn"
1579version = "1.0.25" 1579version = "1.0.29"
1580source = "registry+https://github.com/rust-lang/crates.io-index" 1580source = "registry+https://github.com/rust-lang/crates.io-index"
1581checksum = "f14a640819f79b72a710c0be059dce779f9339ae046c8bef12c361d56702146f" 1581checksum = "bb37da98a55b1d08529362d9cbb863be17556873df2585904ab9d2bc951291d0"
1582dependencies = [ 1582dependencies = [
1583 "proc-macro2", 1583 "proc-macro2",
1584 "quote", 1584 "quote",
diff --git a/crates/ra_hir_def/src/body/lower.rs b/crates/ra_hir_def/src/body/lower.rs
index 905c0cf5d..f159f80af 100644
--- a/crates/ra_hir_def/src/body/lower.rs
+++ b/crates/ra_hir_def/src/body/lower.rs
@@ -134,7 +134,7 @@ impl ExprCollector<'_> {
134 self.make_expr(expr, Err(SyntheticSyntax)) 134 self.make_expr(expr, Err(SyntheticSyntax))
135 } 135 }
136 fn empty_block(&mut self) -> ExprId { 136 fn empty_block(&mut self) -> ExprId {
137 self.alloc_expr_desugared(Expr::Block { statements: Vec::new(), tail: None }) 137 self.alloc_expr_desugared(Expr::Block { statements: Vec::new(), tail: None, label: None })
138 } 138 }
139 fn missing_expr(&mut self) -> ExprId { 139 fn missing_expr(&mut self) -> ExprId {
140 self.alloc_expr_desugared(Expr::Missing) 140 self.alloc_expr_desugared(Expr::Missing)
@@ -215,7 +215,16 @@ impl ExprCollector<'_> {
215 ast::Expr::BlockExpr(e) => self.collect_block(e), 215 ast::Expr::BlockExpr(e) => self.collect_block(e),
216 ast::Expr::LoopExpr(e) => { 216 ast::Expr::LoopExpr(e) => {
217 let body = self.collect_block_opt(e.loop_body()); 217 let body = self.collect_block_opt(e.loop_body());
218 self.alloc_expr(Expr::Loop { body }, syntax_ptr) 218 self.alloc_expr(
219 Expr::Loop {
220 body,
221 label: e
222 .label()
223 .and_then(|l| l.lifetime_token())
224 .map(|l| Name::new_lifetime(&l)),
225 },
226 syntax_ptr,
227 )
219 } 228 }
220 ast::Expr::WhileExpr(e) => { 229 ast::Expr::WhileExpr(e) => {
221 let body = self.collect_block_opt(e.loop_body()); 230 let body = self.collect_block_opt(e.loop_body());
@@ -230,25 +239,56 @@ impl ExprCollector<'_> {
230 let pat = self.collect_pat(pat); 239 let pat = self.collect_pat(pat);
231 let match_expr = self.collect_expr_opt(condition.expr()); 240 let match_expr = self.collect_expr_opt(condition.expr());
232 let placeholder_pat = self.missing_pat(); 241 let placeholder_pat = self.missing_pat();
233 let break_ = self.alloc_expr_desugared(Expr::Break { expr: None }); 242 let break_ =
243 self.alloc_expr_desugared(Expr::Break { expr: None, label: None });
234 let arms = vec![ 244 let arms = vec![
235 MatchArm { pat, expr: body, guard: None }, 245 MatchArm { pat, expr: body, guard: None },
236 MatchArm { pat: placeholder_pat, expr: break_, guard: None }, 246 MatchArm { pat: placeholder_pat, expr: break_, guard: None },
237 ]; 247 ];
238 let match_expr = 248 let match_expr =
239 self.alloc_expr_desugared(Expr::Match { expr: match_expr, arms }); 249 self.alloc_expr_desugared(Expr::Match { expr: match_expr, arms });
240 return self.alloc_expr(Expr::Loop { body: match_expr }, syntax_ptr); 250 return self.alloc_expr(
251 Expr::Loop {
252 body: match_expr,
253 label: e
254 .label()
255 .and_then(|l| l.lifetime_token())
256 .map(|l| Name::new_lifetime(&l)),
257 },
258 syntax_ptr,
259 );
241 } 260 }
242 }, 261 },
243 }; 262 };
244 263
245 self.alloc_expr(Expr::While { condition, body }, syntax_ptr) 264 self.alloc_expr(
265 Expr::While {
266 condition,
267 body,
268 label: e
269 .label()
270 .and_then(|l| l.lifetime_token())
271 .map(|l| Name::new_lifetime(&l)),
272 },
273 syntax_ptr,
274 )
246 } 275 }
247 ast::Expr::ForExpr(e) => { 276 ast::Expr::ForExpr(e) => {
248 let iterable = self.collect_expr_opt(e.iterable()); 277 let iterable = self.collect_expr_opt(e.iterable());
249 let pat = self.collect_pat_opt(e.pat()); 278 let pat = self.collect_pat_opt(e.pat());
250 let body = self.collect_block_opt(e.loop_body()); 279 let body = self.collect_block_opt(e.loop_body());
251 self.alloc_expr(Expr::For { iterable, pat, body }, syntax_ptr) 280 self.alloc_expr(
281 Expr::For {
282 iterable,
283 pat,
284 body,
285 label: e
286 .label()
287 .and_then(|l| l.lifetime_token())
288 .map(|l| Name::new_lifetime(&l)),
289 },
290 syntax_ptr,
291 )
252 } 292 }
253 ast::Expr::CallExpr(e) => { 293 ast::Expr::CallExpr(e) => {
254 let callee = self.collect_expr_opt(e.expr()); 294 let callee = self.collect_expr_opt(e.expr());
@@ -301,13 +341,16 @@ impl ExprCollector<'_> {
301 .unwrap_or(Expr::Missing); 341 .unwrap_or(Expr::Missing);
302 self.alloc_expr(path, syntax_ptr) 342 self.alloc_expr(path, syntax_ptr)
303 } 343 }
304 ast::Expr::ContinueExpr(_e) => { 344 ast::Expr::ContinueExpr(e) => self.alloc_expr(
305 // FIXME: labels 345 Expr::Continue { label: e.lifetime_token().map(|l| Name::new_lifetime(&l)) },
306 self.alloc_expr(Expr::Continue, syntax_ptr) 346 syntax_ptr,
307 } 347 ),
308 ast::Expr::BreakExpr(e) => { 348 ast::Expr::BreakExpr(e) => {
309 let expr = e.expr().map(|e| self.collect_expr(e)); 349 let expr = e.expr().map(|e| self.collect_expr(e));
310 self.alloc_expr(Expr::Break { expr }, syntax_ptr) 350 self.alloc_expr(
351 Expr::Break { expr, label: e.lifetime_token().map(|l| Name::new_lifetime(&l)) },
352 syntax_ptr,
353 )
311 } 354 }
312 ast::Expr::ParenExpr(e) => { 355 ast::Expr::ParenExpr(e) => {
313 let inner = self.collect_expr_opt(e.expr()); 356 let inner = self.collect_expr_opt(e.expr());
@@ -529,7 +572,8 @@ impl ExprCollector<'_> {
529 }) 572 })
530 .collect(); 573 .collect();
531 let tail = block.expr().map(|e| self.collect_expr(e)); 574 let tail = block.expr().map(|e| self.collect_expr(e));
532 self.alloc_expr(Expr::Block { statements, tail }, syntax_node_ptr) 575 let label = block.label().and_then(|l| l.lifetime_token()).map(|t| Name::new_lifetime(&t));
576 self.alloc_expr(Expr::Block { statements, tail, label }, syntax_node_ptr)
533 } 577 }
534 578
535 fn collect_block_items(&mut self, block: &ast::BlockExpr) { 579 fn collect_block_items(&mut self, block: &ast::BlockExpr) {
diff --git a/crates/ra_hir_def/src/body/scope.rs b/crates/ra_hir_def/src/body/scope.rs
index 09e92b74e..e48ff38f9 100644
--- a/crates/ra_hir_def/src/body/scope.rs
+++ b/crates/ra_hir_def/src/body/scope.rs
@@ -138,10 +138,10 @@ fn compute_block_scopes(
138fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope: ScopeId) { 138fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope: ScopeId) {
139 scopes.set_scope(expr, scope); 139 scopes.set_scope(expr, scope);
140 match &body[expr] { 140 match &body[expr] {
141 Expr::Block { statements, tail } => { 141 Expr::Block { statements, tail, .. } => {
142 compute_block_scopes(&statements, *tail, body, scopes, scope); 142 compute_block_scopes(&statements, *tail, body, scopes, scope);
143 } 143 }
144 Expr::For { iterable, pat, body: body_expr } => { 144 Expr::For { iterable, pat, body: body_expr, .. } => {
145 compute_expr_scopes(*iterable, body, scopes, scope); 145 compute_expr_scopes(*iterable, body, scopes, scope);
146 let scope = scopes.new_scope(scope); 146 let scope = scopes.new_scope(scope);
147 scopes.add_bindings(body, scope, *pat); 147 scopes.add_bindings(body, scope, *pat);
diff --git a/crates/ra_hir_def/src/expr.rs b/crates/ra_hir_def/src/expr.rs
index f25c6f958..ca49b26d1 100644
--- a/crates/ra_hir_def/src/expr.rs
+++ b/crates/ra_hir_def/src/expr.rs
@@ -52,18 +52,22 @@ pub enum Expr {
52 Block { 52 Block {
53 statements: Vec<Statement>, 53 statements: Vec<Statement>,
54 tail: Option<ExprId>, 54 tail: Option<ExprId>,
55 label: Option<Name>,
55 }, 56 },
56 Loop { 57 Loop {
57 body: ExprId, 58 body: ExprId,
59 label: Option<Name>,
58 }, 60 },
59 While { 61 While {
60 condition: ExprId, 62 condition: ExprId,
61 body: ExprId, 63 body: ExprId,
64 label: Option<Name>,
62 }, 65 },
63 For { 66 For {
64 iterable: ExprId, 67 iterable: ExprId,
65 pat: PatId, 68 pat: PatId,
66 body: ExprId, 69 body: ExprId,
70 label: Option<Name>,
67 }, 71 },
68 Call { 72 Call {
69 callee: ExprId, 73 callee: ExprId,
@@ -79,9 +83,12 @@ pub enum Expr {
79 expr: ExprId, 83 expr: ExprId,
80 arms: Vec<MatchArm>, 84 arms: Vec<MatchArm>,
81 }, 85 },
82 Continue, 86 Continue {
87 label: Option<Name>,
88 },
83 Break { 89 Break {
84 expr: Option<ExprId>, 90 expr: Option<ExprId>,
91 label: Option<Name>,
85 }, 92 },
86 Return { 93 Return {
87 expr: Option<ExprId>, 94 expr: Option<ExprId>,
@@ -225,7 +232,7 @@ impl Expr {
225 f(*else_branch); 232 f(*else_branch);
226 } 233 }
227 } 234 }
228 Expr::Block { statements, tail } => { 235 Expr::Block { statements, tail, .. } => {
229 for stmt in statements { 236 for stmt in statements {
230 match stmt { 237 match stmt {
231 Statement::Let { initializer, .. } => { 238 Statement::Let { initializer, .. } => {
@@ -241,8 +248,8 @@ impl Expr {
241 } 248 }
242 } 249 }
243 Expr::TryBlock { body } => f(*body), 250 Expr::TryBlock { body } => f(*body),
244 Expr::Loop { body } => f(*body), 251 Expr::Loop { body, .. } => f(*body),
245 Expr::While { condition, body } => { 252 Expr::While { condition, body, .. } => {
246 f(*condition); 253 f(*condition);
247 f(*body); 254 f(*body);
248 } 255 }
@@ -268,8 +275,8 @@ impl Expr {
268 f(arm.expr); 275 f(arm.expr);
269 } 276 }
270 } 277 }
271 Expr::Continue => {} 278 Expr::Continue { .. } => {}
272 Expr::Break { expr } | Expr::Return { expr } => { 279 Expr::Break { expr, .. } | Expr::Return { expr } => {
273 if let Some(expr) = expr { 280 if let Some(expr) = expr {
274 f(*expr); 281 f(*expr);
275 } 282 }
diff --git a/crates/ra_hir_expand/src/name.rs b/crates/ra_hir_expand/src/name.rs
index fecce224e..ea495cb11 100644
--- a/crates/ra_hir_expand/src/name.rs
+++ b/crates/ra_hir_expand/src/name.rs
@@ -37,6 +37,11 @@ impl Name {
37 Name(Repr::TupleField(idx)) 37 Name(Repr::TupleField(idx))
38 } 38 }
39 39
40 pub fn new_lifetime(lt: &ra_syntax::SyntaxToken) -> Name {
41 assert!(lt.kind() == ra_syntax::SyntaxKind::LIFETIME);
42 Name(Repr::Text(lt.text().clone()))
43 }
44
40 /// Shortcut to create inline plain text name 45 /// Shortcut to create inline plain text name
41 const fn new_inline_ascii(text: &[u8]) -> Name { 46 const fn new_inline_ascii(text: &[u8]) -> Name {
42 Name::new_text(SmolStr::new_inline_from_ascii(text.len(), text)) 47 Name::new_text(SmolStr::new_inline_from_ascii(text.len(), text))
diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs
index 957d6e0b5..dc77e88e5 100644
--- a/crates/ra_hir_ty/src/infer.rs
+++ b/crates/ra_hir_ty/src/infer.rs
@@ -219,6 +219,17 @@ struct InferenceContext<'a> {
219struct BreakableContext { 219struct BreakableContext {
220 pub may_break: bool, 220 pub may_break: bool,
221 pub break_ty: Ty, 221 pub break_ty: Ty,
222 pub label: Option<name::Name>,
223}
224
225fn find_breakable<'c>(
226 ctxs: &'c mut [BreakableContext],
227 label: Option<&name::Name>,
228) -> Option<&'c mut BreakableContext> {
229 match label {
230 Some(_) => ctxs.iter_mut().rev().find(|ctx| ctx.label.as_ref() == label),
231 None => ctxs.last_mut(),
232 }
222} 233}
223 234
224impl<'a> InferenceContext<'a> { 235impl<'a> InferenceContext<'a> {
diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs
index 78084cb57..4a98e2deb 100644
--- a/crates/ra_hir_ty/src/infer/expr.rs
+++ b/crates/ra_hir_ty/src/infer/expr.rs
@@ -22,8 +22,8 @@ use crate::{
22}; 22};
23 23
24use super::{ 24use super::{
25 BindingMode, BreakableContext, Diverges, Expectation, InferenceContext, InferenceDiagnostic, 25 find_breakable, BindingMode, BreakableContext, Diverges, Expectation, InferenceContext,
26 TypeMismatch, 26 InferenceDiagnostic, TypeMismatch,
27}; 27};
28 28
29impl<'a> InferenceContext<'a> { 29impl<'a> InferenceContext<'a> {
@@ -86,16 +86,20 @@ impl<'a> InferenceContext<'a> {
86 86
87 self.coerce_merge_branch(&then_ty, &else_ty) 87 self.coerce_merge_branch(&then_ty, &else_ty)
88 } 88 }
89 Expr::Block { statements, tail } => self.infer_block(statements, *tail, expected), 89 Expr::Block { statements, tail, .. } => {
90 // FIXME: Breakable block inference
91 self.infer_block(statements, *tail, expected)
92 }
90 Expr::TryBlock { body } => { 93 Expr::TryBlock { body } => {
91 let _inner = self.infer_expr(*body, expected); 94 let _inner = self.infer_expr(*body, expected);
92 // FIXME should be std::result::Result<{inner}, _> 95 // FIXME should be std::result::Result<{inner}, _>
93 Ty::Unknown 96 Ty::Unknown
94 } 97 }
95 Expr::Loop { body } => { 98 Expr::Loop { body, label } => {
96 self.breakables.push(BreakableContext { 99 self.breakables.push(BreakableContext {
97 may_break: false, 100 may_break: false,
98 break_ty: self.table.new_type_var(), 101 break_ty: self.table.new_type_var(),
102 label: label.clone(),
99 }); 103 });
100 self.infer_expr(*body, &Expectation::has_type(Ty::unit())); 104 self.infer_expr(*body, &Expectation::has_type(Ty::unit()));
101 105
@@ -110,8 +114,12 @@ impl<'a> InferenceContext<'a> {
110 Ty::simple(TypeCtor::Never) 114 Ty::simple(TypeCtor::Never)
111 } 115 }
112 } 116 }
113 Expr::While { condition, body } => { 117 Expr::While { condition, body, label } => {
114 self.breakables.push(BreakableContext { may_break: false, break_ty: Ty::Unknown }); 118 self.breakables.push(BreakableContext {
119 may_break: false,
120 break_ty: Ty::Unknown,
121 label: label.clone(),
122 });
115 // while let is desugared to a match loop, so this is always simple while 123 // while let is desugared to a match loop, so this is always simple while
116 self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool))); 124 self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool)));
117 self.infer_expr(*body, &Expectation::has_type(Ty::unit())); 125 self.infer_expr(*body, &Expectation::has_type(Ty::unit()));
@@ -120,10 +128,14 @@ impl<'a> InferenceContext<'a> {
120 self.diverges = Diverges::Maybe; 128 self.diverges = Diverges::Maybe;
121 Ty::unit() 129 Ty::unit()
122 } 130 }
123 Expr::For { iterable, body, pat } => { 131 Expr::For { iterable, body, pat, label } => {
124 let iterable_ty = self.infer_expr(*iterable, &Expectation::none()); 132 let iterable_ty = self.infer_expr(*iterable, &Expectation::none());
125 133
126 self.breakables.push(BreakableContext { may_break: false, break_ty: Ty::Unknown }); 134 self.breakables.push(BreakableContext {
135 may_break: false,
136 break_ty: Ty::Unknown,
137 label: label.clone(),
138 });
127 let pat_ty = 139 let pat_ty =
128 self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item()); 140 self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item());
129 141
@@ -236,23 +248,24 @@ impl<'a> InferenceContext<'a> {
236 let resolver = resolver_for_expr(self.db.upcast(), self.owner, tgt_expr); 248 let resolver = resolver_for_expr(self.db.upcast(), self.owner, tgt_expr);
237 self.infer_path(&resolver, p, tgt_expr.into()).unwrap_or(Ty::Unknown) 249 self.infer_path(&resolver, p, tgt_expr.into()).unwrap_or(Ty::Unknown)
238 } 250 }
239 Expr::Continue => Ty::simple(TypeCtor::Never), 251 Expr::Continue { .. } => Ty::simple(TypeCtor::Never),
240 Expr::Break { expr } => { 252 Expr::Break { expr, label } => {
241 let val_ty = if let Some(expr) = expr { 253 let val_ty = if let Some(expr) = expr {
242 self.infer_expr(*expr, &Expectation::none()) 254 self.infer_expr(*expr, &Expectation::none())
243 } else { 255 } else {
244 Ty::unit() 256 Ty::unit()
245 }; 257 };
246 258
247 let last_ty = if let Some(ctxt) = self.breakables.last() { 259 let last_ty =
248 ctxt.break_ty.clone() 260 if let Some(ctxt) = find_breakable(&mut self.breakables, label.as_ref()) {
249 } else { 261 ctxt.break_ty.clone()
250 Ty::Unknown 262 } else {
251 }; 263 Ty::Unknown
264 };
252 265
253 let merged_type = self.coerce_merge_branch(&last_ty, &val_ty); 266 let merged_type = self.coerce_merge_branch(&last_ty, &val_ty);
254 267
255 if let Some(ctxt) = self.breakables.last_mut() { 268 if let Some(ctxt) = find_breakable(&mut self.breakables, label.as_ref()) {
256 ctxt.break_ty = merged_type; 269 ctxt.break_ty = merged_type;
257 ctxt.may_break = true; 270 ctxt.may_break = true;
258 } else { 271 } else {
diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs
index 839491b9e..88309157b 100644
--- a/crates/ra_hir_ty/src/tests/simple.rs
+++ b/crates/ra_hir_ty/src/tests/simple.rs
@@ -1943,3 +1943,57 @@ fn test() {
1943 "### 1943 "###
1944 ); 1944 );
1945} 1945}
1946
1947#[test]
1948fn infer_labelled_break_with_val() {
1949 assert_snapshot!(
1950 infer(r#"
1951fn foo() {
1952 let _x = || 'outer: loop {
1953 let inner = 'inner: loop {
1954 let i = Default::default();
1955 if (break 'outer i) {
1956 loop { break 'inner 5i8; };
1957 } else if true {
1958 break 'inner 6;
1959 }
1960 break 7;
1961 };
1962 break inner < 8;
1963 };
1964}
1965"#),
1966 @r###"
1967 10..336 '{ ... }; }': ()
1968 20..22 '_x': || -> bool
1969 25..333 '|| 'ou... }': || -> bool
1970 28..333 ''outer... }': bool
1971 41..333 '{ ... }': ()
1972 55..60 'inner': i8
1973 63..301 ''inner... }': i8
1974 76..301 '{ ... }': ()
1975 94..95 'i': bool
1976 98..114 'Defaul...efault': {unknown}
1977 98..116 'Defaul...ault()': bool
1978 130..270 'if (br... }': ()
1979 134..148 'break 'outer i': !
1980 147..148 'i': bool
1981 150..209 '{ ... }': ()
1982 168..194 'loop {...5i8; }': !
1983 173..194 '{ brea...5i8; }': ()
1984 175..191 'break ...er 5i8': !
1985 188..191 '5i8': i8
1986 215..270 'if tru... }': ()
1987 218..222 'true': bool
1988 223..270 '{ ... }': ()
1989 241..255 'break 'inner 6': !
1990 254..255 '6': i8
1991 283..290 'break 7': !
1992 289..290 '7': i8
1993 311..326 'break inner < 8': !
1994 317..322 'inner': i8
1995 317..326 'inner < 8': bool
1996 325..326 '8': i8
1997 "###
1998 );
1999}
diff --git a/crates/ra_syntax/src/ast/generated/nodes.rs b/crates/ra_syntax/src/ast/generated/nodes.rs
index 255402fbc..cb430ca01 100644
--- a/crates/ra_syntax/src/ast/generated/nodes.rs
+++ b/crates/ra_syntax/src/ast/generated/nodes.rs
@@ -1081,6 +1081,7 @@ pub struct BlockExpr {
1081impl ast::AttrsOwner for BlockExpr {} 1081impl ast::AttrsOwner for BlockExpr {}
1082impl ast::ModuleItemOwner for BlockExpr {} 1082impl ast::ModuleItemOwner for BlockExpr {}
1083impl BlockExpr { 1083impl BlockExpr {
1084 pub fn label(&self) -> Option<Label> { support::child(&self.syntax) }
1084 pub fn l_curly_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['{']) } 1085 pub fn l_curly_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['{']) }
1085 pub fn statements(&self) -> AstChildren<Stmt> { support::children(&self.syntax) } 1086 pub fn statements(&self) -> AstChildren<Stmt> { support::children(&self.syntax) }
1086 pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) } 1087 pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
diff --git a/docs/user/assists.md b/docs/user/generated_assists.adoc
index 04387e3b0..580ab4358 100644
--- a/docs/user/assists.md
+++ b/docs/user/generated_assists.adoc
@@ -1,18 +1,17 @@
1# Assists 1[discrete]
2 2=== `add_custom_impl`
3Cursor position or selection is signified by `┃` character. 3**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/add_custom_impl.rs#L14[add_custom_impl.rs]
4
5
6## `add_custom_impl`
7 4
8Adds impl block for derived trait. 5Adds impl block for derived trait.
9 6
7.Before
10```rust 8```rust
11// BEFORE
12#[derive(Deb┃ug, Display)] 9#[derive(Deb┃ug, Display)]
13struct S; 10struct S;
11```
14 12
15// AFTER 13.After
14```rust
16#[derive(Display)] 15#[derive(Display)]
17struct S; 16struct S;
18 17
@@ -21,18 +20,23 @@ impl Debug for S {
21} 20}
22``` 21```
23 22
24## `add_derive` 23
24[discrete]
25=== `add_derive`
26**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/add_derive.rs#L9[add_derive.rs]
25 27
26Adds a new `#[derive()]` clause to a struct or enum. 28Adds a new `#[derive()]` clause to a struct or enum.
27 29
30.Before
28```rust 31```rust
29// BEFORE
30struct Point { 32struct Point {
31 x: u32, 33 x: u32,
32 y: u32,┃ 34 y: u32,┃
33} 35}
36```
34 37
35// AFTER 38.After
39```rust
36#[derive($0)] 40#[derive($0)]
37struct Point { 41struct Point {
38 x: u32, 42 x: u32,
@@ -40,31 +44,41 @@ struct Point {
40} 44}
41``` 45```
42 46
43## `add_explicit_type` 47
48[discrete]
49=== `add_explicit_type`
50**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/add_explicit_type.rs#L9[add_explicit_type.rs]
44 51
45Specify type for a let binding. 52Specify type for a let binding.
46 53
54.Before
47```rust 55```rust
48// BEFORE
49fn main() { 56fn main() {
50 let x┃ = 92; 57 let x┃ = 92;
51} 58}
59```
52 60
53// AFTER 61.After
62```rust
54fn main() { 63fn main() {
55 let x: i32 = 92; 64 let x: i32 = 92;
56} 65}
57``` 66```
58 67
59## `add_from_impl_for_enum` 68
69[discrete]
70=== `add_from_impl_for_enum`
71**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs#L7[add_from_impl_for_enum.rs]
60 72
61Adds a From impl for an enum variant with one tuple field. 73Adds a From impl for an enum variant with one tuple field.
62 74
75.Before
63```rust 76```rust
64// BEFORE
65enum A { ┃One(u32) } 77enum A { ┃One(u32) }
78```
66 79
67// AFTER 80.After
81```rust
68enum A { One(u32) } 82enum A { One(u32) }
69 83
70impl From<u32> for A { 84impl From<u32> for A {
@@ -74,20 +88,25 @@ impl From<u32> for A {
74} 88}
75``` 89```
76 90
77## `add_function` 91
92[discrete]
93=== `add_function`
94**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/add_function.rs#L19[add_function.rs]
78 95
79Adds a stub function with a signature matching the function under the cursor. 96Adds a stub function with a signature matching the function under the cursor.
80 97
98.Before
81```rust 99```rust
82// BEFORE
83struct Baz; 100struct Baz;
84fn baz() -> Baz { Baz } 101fn baz() -> Baz { Baz }
85fn foo() { 102fn foo() {
86 bar┃("", baz()); 103 bar┃("", baz());
87} 104}
88 105
106```
89 107
90// AFTER 108.After
109```rust
91struct Baz; 110struct Baz;
92fn baz() -> Baz { Baz } 111fn baz() -> Baz { Baz }
93fn foo() { 112fn foo() {
@@ -100,33 +119,43 @@ fn bar(arg: &str, baz: Baz) {
100 119
101``` 120```
102 121
103## `add_hash` 122
123[discrete]
124=== `add_hash`
125**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/raw_string.rs#L65[raw_string.rs]
104 126
105Adds a hash to a raw string literal. 127Adds a hash to a raw string literal.
106 128
129.Before
107```rust 130```rust
108// BEFORE
109fn main() { 131fn main() {
110 r#"Hello,┃ World!"#; 132 r#"Hello,┃ World!"#;
111} 133}
134```
112 135
113// AFTER 136.After
137```rust
114fn main() { 138fn main() {
115 r##"Hello, World!"##; 139 r##"Hello, World!"##;
116} 140}
117``` 141```
118 142
119## `add_impl` 143
144[discrete]
145=== `add_impl`
146**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/add_impl.rs#L6[add_impl.rs]
120 147
121Adds a new inherent impl for a type. 148Adds a new inherent impl for a type.
122 149
150.Before
123```rust 151```rust
124// BEFORE
125struct Ctx<T: Clone> { 152struct Ctx<T: Clone> {
126 data: T,┃ 153 data: T,┃
127} 154}
155```
128 156
129// AFTER 157.After
158```rust
130struct Ctx<T: Clone> { 159struct Ctx<T: Clone> {
131 data: T, 160 data: T,
132} 161}
@@ -136,12 +165,15 @@ impl<T: Clone> Ctx<T> {
136} 165}
137``` 166```
138 167
139## `add_impl_default_members` 168
169[discrete]
170=== `add_impl_default_members`
171**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/add_missing_impl_members.rs#L64[add_missing_impl_members.rs]
140 172
141Adds scaffold for overriding default impl members. 173Adds scaffold for overriding default impl members.
142 174
175.Before
143```rust 176```rust
144// BEFORE
145trait Trait { 177trait Trait {
146 Type X; 178 Type X;
147 fn foo(&self); 179 fn foo(&self);
@@ -153,8 +185,10 @@ impl Trait for () {
153 fn foo(&self) {}┃ 185 fn foo(&self) {}┃
154 186
155} 187}
188```
156 189
157// AFTER 190.After
191```rust
158trait Trait { 192trait Trait {
159 Type X; 193 Type X;
160 fn foo(&self); 194 fn foo(&self);
@@ -169,12 +203,15 @@ impl Trait for () {
169} 203}
170``` 204```
171 205
172## `add_impl_missing_members` 206
207[discrete]
208=== `add_impl_missing_members`
209**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/add_missing_impl_members.rs#L24[add_missing_impl_members.rs]
173 210
174Adds scaffold for required impl members. 211Adds scaffold for required impl members.
175 212
213.Before
176```rust 214```rust
177// BEFORE
178trait Trait<T> { 215trait Trait<T> {
179 Type X; 216 Type X;
180 fn foo(&self) -> T; 217 fn foo(&self) -> T;
@@ -184,8 +221,10 @@ trait Trait<T> {
184impl Trait<u32> for () {┃ 221impl Trait<u32> for () {┃
185 222
186} 223}
224```
187 225
188// AFTER 226.After
227```rust
189trait Trait<T> { 228trait Trait<T> {
190 Type X; 229 Type X;
191 fn foo(&self) -> T; 230 fn foo(&self) -> T;
@@ -200,17 +239,22 @@ impl Trait<u32> for () {
200} 239}
201``` 240```
202 241
203## `add_new` 242
243[discrete]
244=== `add_new`
245**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/add_new.rs#L12[add_new.rs]
204 246
205Adds a new inherent impl for a type. 247Adds a new inherent impl for a type.
206 248
249.Before
207```rust 250```rust
208// BEFORE
209struct Ctx<T: Clone> { 251struct Ctx<T: Clone> {
210 data: T,┃ 252 data: T,┃
211} 253}
254```
212 255
213// AFTER 256.After
257```rust
214struct Ctx<T: Clone> { 258struct Ctx<T: Clone> {
215 data: T, 259 data: T,
216} 260}
@@ -221,25 +265,33 @@ impl<T: Clone> Ctx<T> {
221 265
222``` 266```
223 267
224## `add_turbo_fish` 268
269[discrete]
270=== `add_turbo_fish`
271**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/add_turbo_fish.rs#L10[add_turbo_fish.rs]
225 272
226Adds `::<_>` to a call of a generic method or function. 273Adds `::<_>` to a call of a generic method or function.
227 274
275.Before
228```rust 276```rust
229// BEFORE
230fn make<T>() -> T { todo!() } 277fn make<T>() -> T { todo!() }
231fn main() { 278fn main() {
232 let x = make┃(); 279 let x = make┃();
233} 280}
281```
234 282
235// AFTER 283.After
284```rust
236fn make<T>() -> T { todo!() } 285fn make<T>() -> T { todo!() }
237fn main() { 286fn main() {
238 let x = make::<${0:_}>(); 287 let x = make::<${0:_}>();
239} 288}
240``` 289```
241 290
242## `apply_demorgan` 291
292[discrete]
293=== `apply_demorgan`
294**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/apply_demorgan.rs#L5[apply_demorgan.rs]
243 295
244Apply [De Morgan's law](https://en.wikipedia.org/wiki/De_Morgan%27s_laws). 296Apply [De Morgan's law](https://en.wikipedia.org/wiki/De_Morgan%27s_laws).
245This transforms expressions of the form `!l || !r` into `!(l && r)`. 297This transforms expressions of the form `!l || !r` into `!(l && r)`.
@@ -247,29 +299,36 @@ This also works with `&&`. This assist can only be applied with the cursor
247on either `||` or `&&`, with both operands being a negation of some kind. 299on either `||` or `&&`, with both operands being a negation of some kind.
248This means something of the form `!x` or `x != y`. 300This means something of the form `!x` or `x != y`.
249 301
302.Before
250```rust 303```rust
251// BEFORE
252fn main() { 304fn main() {
253 if x != 4 ||┃ !y {} 305 if x != 4 ||┃ !y {}
254} 306}
307```
255 308
256// AFTER 309.After
310```rust
257fn main() { 311fn main() {
258 if !(x == 4 && y) {} 312 if !(x == 4 && y) {}
259} 313}
260``` 314```
261 315
262## `auto_import` 316
317[discrete]
318=== `auto_import`
319**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/auto_import.rs#L18[auto_import.rs]
263 320
264If the name is unresolved, provides all possible imports for it. 321If the name is unresolved, provides all possible imports for it.
265 322
323.Before
266```rust 324```rust
267// BEFORE
268fn main() { 325fn main() {
269 let map = HashMap┃::new(); 326 let map = HashMap┃::new();
270} 327}
328```
271 329
272// AFTER 330.After
331```rust
273use std::collections::HashMap; 332use std::collections::HashMap;
274 333
275fn main() { 334fn main() {
@@ -277,12 +336,15 @@ fn main() {
277} 336}
278``` 337```
279 338
280## `change_lifetime_anon_to_named` 339
340[discrete]
341=== `change_lifetime_anon_to_named`
342**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/change_lifetime_anon_to_named.rs#L9[change_lifetime_anon_to_named.rs]
281 343
282Change an anonymous lifetime to a named lifetime. 344Change an anonymous lifetime to a named lifetime.
283 345
346.Before
284```rust 347```rust
285// BEFORE
286impl Cursor<'_┃> { 348impl Cursor<'_┃> {
287 fn node(self) -> &SyntaxNode { 349 fn node(self) -> &SyntaxNode {
288 match self { 350 match self {
@@ -290,8 +352,10 @@ impl Cursor<'_┃> {
290 } 352 }
291 } 353 }
292} 354}
355```
293 356
294// AFTER 357.After
358```rust
295impl<'a> Cursor<'a> { 359impl<'a> Cursor<'a> {
296 fn node(self) -> &SyntaxNode { 360 fn node(self) -> &SyntaxNode {
297 match self { 361 match self {
@@ -301,44 +365,59 @@ impl<'a> Cursor<'a> {
301} 365}
302``` 366```
303 367
304## `change_return_type_to_result` 368
369[discrete]
370=== `change_return_type_to_result`
371**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/change_return_type_to_result.rs#L8[change_return_type_to_result.rs]
305 372
306Change the function's return type to Result. 373Change the function's return type to Result.
307 374
375.Before
308```rust 376```rust
309// BEFORE
310fn foo() -> i32┃ { 42i32 } 377fn foo() -> i32┃ { 42i32 }
378```
311 379
312// AFTER 380.After
381```rust
313fn foo() -> Result<i32, ${0:_}> { Ok(42i32) } 382fn foo() -> Result<i32, ${0:_}> { Ok(42i32) }
314``` 383```
315 384
316## `change_visibility` 385
386[discrete]
387=== `change_visibility`
388**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/change_visibility.rs#L14[change_visibility.rs]
317 389
318Adds or changes existing visibility specifier. 390Adds or changes existing visibility specifier.
319 391
392.Before
320```rust 393```rust
321// BEFORE
322┃fn frobnicate() {} 394┃fn frobnicate() {}
395```
323 396
324// AFTER 397.After
398```rust
325pub(crate) fn frobnicate() {} 399pub(crate) fn frobnicate() {}
326``` 400```
327 401
328## `convert_to_guarded_return` 402
403[discrete]
404=== `convert_to_guarded_return`
405**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/early_return.rs#L21[early_return.rs]
329 406
330Replace a large conditional with a guarded return. 407Replace a large conditional with a guarded return.
331 408
409.Before
332```rust 410```rust
333// BEFORE
334fn main() { 411fn main() {
335 ┃if cond { 412 ┃if cond {
336 foo(); 413 foo();
337 bar(); 414 bar();
338 } 415 }
339} 416}
417```
340 418
341// AFTER 419.After
420```rust
342fn main() { 421fn main() {
343 if !cond { 422 if !cond {
344 return; 423 return;
@@ -348,12 +427,15 @@ fn main() {
348} 427}
349``` 428```
350 429
351## `fill_match_arms` 430
431[discrete]
432=== `fill_match_arms`
433**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/fill_match_arms.rs#L14[fill_match_arms.rs]
352 434
353Adds missing clauses to a `match` expression. 435Adds missing clauses to a `match` expression.
354 436
437.Before
355```rust 438```rust
356// BEFORE
357enum Action { Move { distance: u32 }, Stop } 439enum Action { Move { distance: u32 }, Stop }
358 440
359fn handle(action: Action) { 441fn handle(action: Action) {
@@ -361,8 +443,10 @@ fn handle(action: Action) {
361 443
362 } 444 }
363} 445}
446```
364 447
365// AFTER 448.After
449```rust
366enum Action { Move { distance: u32 }, Stop } 450enum Action { Move { distance: u32 }, Stop }
367 451
368fn handle(action: Action) { 452fn handle(action: Action) {
@@ -373,20 +457,25 @@ fn handle(action: Action) {
373} 457}
374``` 458```
375 459
376## `fix_visibility` 460
461[discrete]
462=== `fix_visibility`
463**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/fix_visibility.rs#L13[fix_visibility.rs]
377 464
378Makes inaccessible item public. 465Makes inaccessible item public.
379 466
467.Before
380```rust 468```rust
381// BEFORE
382mod m { 469mod m {
383 fn frobnicate() {} 470 fn frobnicate() {}
384} 471}
385fn main() { 472fn main() {
386 m::frobnicate┃() {} 473 m::frobnicate┃() {}
387} 474}
475```
388 476
389// AFTER 477.After
478```rust
390mod m { 479mod m {
391 $0pub(crate) fn frobnicate() {} 480 $0pub(crate) fn frobnicate() {}
392} 481}
@@ -395,154 +484,202 @@ fn main() {
395} 484}
396``` 485```
397 486
398## `flip_binexpr` 487
488[discrete]
489=== `flip_binexpr`
490**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/flip_binexpr.rs#L5[flip_binexpr.rs]
399 491
400Flips operands of a binary expression. 492Flips operands of a binary expression.
401 493
494.Before
402```rust 495```rust
403// BEFORE
404fn main() { 496fn main() {
405 let _ = 90 +┃ 2; 497 let _ = 90 +┃ 2;
406} 498}
499```
407 500
408// AFTER 501.After
502```rust
409fn main() { 503fn main() {
410 let _ = 2 + 90; 504 let _ = 2 + 90;
411} 505}
412``` 506```
413 507
414## `flip_comma` 508
509[discrete]
510=== `flip_comma`
511**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/flip_comma.rs#L5[flip_comma.rs]
415 512
416Flips two comma-separated items. 513Flips two comma-separated items.
417 514
515.Before
418```rust 516```rust
419// BEFORE
420fn main() { 517fn main() {
421 ((1, 2),┃ (3, 4)); 518 ((1, 2),┃ (3, 4));
422} 519}
520```
423 521
424// AFTER 522.After
523```rust
425fn main() { 524fn main() {
426 ((3, 4), (1, 2)); 525 ((3, 4), (1, 2));
427} 526}
428``` 527```
429 528
430## `flip_trait_bound` 529
530[discrete]
531=== `flip_trait_bound`
532**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/flip_trait_bound.rs#L9[flip_trait_bound.rs]
431 533
432Flips two trait bounds. 534Flips two trait bounds.
433 535
536.Before
434```rust 537```rust
435// BEFORE
436fn foo<T: Clone +┃ Copy>() { } 538fn foo<T: Clone +┃ Copy>() { }
539```
437 540
438// AFTER 541.After
542```rust
439fn foo<T: Copy + Clone>() { } 543fn foo<T: Copy + Clone>() { }
440``` 544```
441 545
442## `inline_local_variable` 546
547[discrete]
548=== `inline_local_variable`
549**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/inline_local_variable.rs#L13[inline_local_variable.rs]
443 550
444Inlines local variable. 551Inlines local variable.
445 552
553.Before
446```rust 554```rust
447// BEFORE
448fn main() { 555fn main() {
449 let x┃ = 1 + 2; 556 let x┃ = 1 + 2;
450 x * 4; 557 x * 4;
451} 558}
559```
452 560
453// AFTER 561.After
562```rust
454fn main() { 563fn main() {
455 (1 + 2) * 4; 564 (1 + 2) * 4;
456} 565}
457``` 566```
458 567
459## `introduce_variable` 568
569[discrete]
570=== `introduce_variable`
571**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/introduce_variable.rs#L14[introduce_variable.rs]
460 572
461Extracts subexpression into a variable. 573Extracts subexpression into a variable.
462 574
575.Before
463```rust 576```rust
464// BEFORE
465fn main() { 577fn main() {
466 ┃(1 + 2)┃ * 4; 578 ┃(1 + 2)┃ * 4;
467} 579}
580```
468 581
469// AFTER 582.After
583```rust
470fn main() { 584fn main() {
471 let $0var_name = (1 + 2); 585 let $0var_name = (1 + 2);
472 var_name * 4; 586 var_name * 4;
473} 587}
474``` 588```
475 589
476## `invert_if` 590
591[discrete]
592=== `invert_if`
593**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/invert_if.rs#L12[invert_if.rs]
477 594
478Apply invert_if 595Apply invert_if
479This transforms if expressions of the form `if !x {A} else {B}` into `if x {B} else {A}` 596This transforms if expressions of the form `if !x {A} else {B}` into `if x {B} else {A}`
480This also works with `!=`. This assist can only be applied with the cursor 597This also works with `!=`. This assist can only be applied with the cursor
481on `if`. 598on `if`.
482 599
600.Before
483```rust 601```rust
484// BEFORE
485fn main() { 602fn main() {
486 if┃ !y { A } else { B } 603 if┃ !y { A } else { B }
487} 604}
605```
488 606
489// AFTER 607.After
608```rust
490fn main() { 609fn main() {
491 if y { B } else { A } 610 if y { B } else { A }
492} 611}
493``` 612```
494 613
495## `make_raw_string` 614
615[discrete]
616=== `make_raw_string`
617**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/raw_string.rs#L10[raw_string.rs]
496 618
497Adds `r#` to a plain string literal. 619Adds `r#` to a plain string literal.
498 620
621.Before
499```rust 622```rust
500// BEFORE
501fn main() { 623fn main() {
502 "Hello,┃ World!"; 624 "Hello,┃ World!";
503} 625}
626```
504 627
505// AFTER 628.After
629```rust
506fn main() { 630fn main() {
507 r#"Hello, World!"#; 631 r#"Hello, World!"#;
508} 632}
509``` 633```
510 634
511## `make_usual_string` 635
636[discrete]
637=== `make_usual_string`
638**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/raw_string.rs#L39[raw_string.rs]
512 639
513Turns a raw string into a plain string. 640Turns a raw string into a plain string.
514 641
642.Before
515```rust 643```rust
516// BEFORE
517fn main() { 644fn main() {
518 r#"Hello,┃ "World!""#; 645 r#"Hello,┃ "World!""#;
519} 646}
647```
520 648
521// AFTER 649.After
650```rust
522fn main() { 651fn main() {
523 "Hello, \"World!\""; 652 "Hello, \"World!\"";
524} 653}
525``` 654```
526 655
527## `merge_imports` 656
657[discrete]
658=== `merge_imports`
659**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/merge_imports.rs#L14[merge_imports.rs]
528 660
529Merges two imports with a common prefix. 661Merges two imports with a common prefix.
530 662
663.Before
531```rust 664```rust
532// BEFORE
533use std::┃fmt::Formatter; 665use std::┃fmt::Formatter;
534use std::io; 666use std::io;
667```
535 668
536// AFTER 669.After
670```rust
537use std::{fmt::Formatter, io}; 671use std::{fmt::Formatter, io};
538``` 672```
539 673
540## `merge_match_arms` 674
675[discrete]
676=== `merge_match_arms`
677**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/merge_match_arms.rs#L11[merge_match_arms.rs]
541 678
542Merges identical match arms. 679Merges identical match arms.
543 680
681.Before
544```rust 682```rust
545// BEFORE
546enum Action { Move { distance: u32 }, Stop } 683enum Action { Move { distance: u32 }, Stop }
547 684
548fn handle(action: Action) { 685fn handle(action: Action) {
@@ -551,8 +688,10 @@ fn handle(action: Action) {
551 Action::Stop => foo(), 688 Action::Stop => foo(),
552 } 689 }
553} 690}
691```
554 692
555// AFTER 693.After
694```rust
556enum Action { Move { distance: u32 }, Stop } 695enum Action { Move { distance: u32 }, Stop }
557 696
558fn handle(action: Action) { 697fn handle(action: Action) {
@@ -562,12 +701,15 @@ fn handle(action: Action) {
562} 701}
563``` 702```
564 703
565## `move_arm_cond_to_match_guard` 704
705[discrete]
706=== `move_arm_cond_to_match_guard`
707**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/move_guard.rs#L56[move_guard.rs]
566 708
567Moves if expression from match arm body into a guard. 709Moves if expression from match arm body into a guard.
568 710
711.Before
569```rust 712```rust
570// BEFORE
571enum Action { Move { distance: u32 }, Stop } 713enum Action { Move { distance: u32 }, Stop }
572 714
573fn handle(action: Action) { 715fn handle(action: Action) {
@@ -576,8 +718,10 @@ fn handle(action: Action) {
576 _ => (), 718 _ => (),
577 } 719 }
578} 720}
721```
579 722
580// AFTER 723.After
724```rust
581enum Action { Move { distance: u32 }, Stop } 725enum Action { Move { distance: u32 }, Stop }
582 726
583fn handle(action: Action) { 727fn handle(action: Action) {
@@ -588,28 +732,36 @@ fn handle(action: Action) {
588} 732}
589``` 733```
590 734
591## `move_bounds_to_where_clause` 735
736[discrete]
737=== `move_bounds_to_where_clause`
738**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/move_bounds.rs#L10[move_bounds.rs]
592 739
593Moves inline type bounds to a where clause. 740Moves inline type bounds to a where clause.
594 741
742.Before
595```rust 743```rust
596// BEFORE
597fn apply<T, U, ┃F: FnOnce(T) -> U>(f: F, x: T) -> U { 744fn apply<T, U, ┃F: FnOnce(T) -> U>(f: F, x: T) -> U {
598 f(x) 745 f(x)
599} 746}
747```
600 748
601// AFTER 749.After
750```rust
602fn apply<T, U, F>(f: F, x: T) -> U where F: FnOnce(T) -> U { 751fn apply<T, U, F>(f: F, x: T) -> U where F: FnOnce(T) -> U {
603 f(x) 752 f(x)
604} 753}
605``` 754```
606 755
607## `move_guard_to_arm_body` 756
757[discrete]
758=== `move_guard_to_arm_body`
759**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/move_guard.rs#L8[move_guard.rs]
608 760
609Moves match guard into match arm body. 761Moves match guard into match arm body.
610 762
763.Before
611```rust 764```rust
612// BEFORE
613enum Action { Move { distance: u32 }, Stop } 765enum Action { Move { distance: u32 }, Stop }
614 766
615fn handle(action: Action) { 767fn handle(action: Action) {
@@ -618,8 +770,10 @@ fn handle(action: Action) {
618 _ => (), 770 _ => (),
619 } 771 }
620} 772}
773```
621 774
622// AFTER 775.After
776```rust
623enum Action { Move { distance: u32 }, Stop } 777enum Action { Move { distance: u32 }, Stop }
624 778
625fn handle(action: Action) { 779fn handle(action: Action) {
@@ -630,75 +784,98 @@ fn handle(action: Action) {
630} 784}
631``` 785```
632 786
633## `remove_dbg` 787
788[discrete]
789=== `remove_dbg`
790**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/remove_dbg.rs#L8[remove_dbg.rs]
634 791
635Removes `dbg!()` macro call. 792Removes `dbg!()` macro call.
636 793
794.Before
637```rust 795```rust
638// BEFORE
639fn main() { 796fn main() {
640 ┃dbg!(92); 797 ┃dbg!(92);
641} 798}
799```
642 800
643// AFTER 801.After
802```rust
644fn main() { 803fn main() {
645 92; 804 92;
646} 805}
647``` 806```
648 807
649## `remove_hash` 808
809[discrete]
810=== `remove_hash`
811**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/raw_string.rs#L89[raw_string.rs]
650 812
651Removes a hash from a raw string literal. 813Removes a hash from a raw string literal.
652 814
815.Before
653```rust 816```rust
654// BEFORE
655fn main() { 817fn main() {
656 r#"Hello,┃ World!"#; 818 r#"Hello,┃ World!"#;
657} 819}
820```
658 821
659// AFTER 822.After
823```rust
660fn main() { 824fn main() {
661 r"Hello, World!"; 825 r"Hello, World!";
662} 826}
663``` 827```
664 828
665## `remove_mut` 829
830[discrete]
831=== `remove_mut`
832**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/remove_mut.rs#L5[remove_mut.rs]
666 833
667Removes the `mut` keyword. 834Removes the `mut` keyword.
668 835
836.Before
669```rust 837```rust
670// BEFORE
671impl Walrus { 838impl Walrus {
672 fn feed(&mut┃ self, amount: u32) {} 839 fn feed(&mut┃ self, amount: u32) {}
673} 840}
841```
674 842
675// AFTER 843.After
844```rust
676impl Walrus { 845impl Walrus {
677 fn feed(&self, amount: u32) {} 846 fn feed(&self, amount: u32) {}
678} 847}
679``` 848```
680 849
681## `reorder_fields` 850
851[discrete]
852=== `reorder_fields`
853**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/reorder_fields.rs#L10[reorder_fields.rs]
682 854
683Reorder the fields of record literals and record patterns in the same order as in 855Reorder the fields of record literals and record patterns in the same order as in
684the definition. 856the definition.
685 857
858.Before
686```rust 859```rust
687// BEFORE
688struct Foo {foo: i32, bar: i32}; 860struct Foo {foo: i32, bar: i32};
689const test: Foo = ┃Foo {bar: 0, foo: 1} 861const test: Foo = ┃Foo {bar: 0, foo: 1}
862```
690 863
691// AFTER 864.After
865```rust
692struct Foo {foo: i32, bar: i32}; 866struct Foo {foo: i32, bar: i32};
693const test: Foo = Foo {foo: 1, bar: 0} 867const test: Foo = Foo {foo: 1, bar: 0}
694``` 868```
695 869
696## `replace_if_let_with_match` 870
871[discrete]
872=== `replace_if_let_with_match`
873**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/replace_if_let_with_match.rs#L13[replace_if_let_with_match.rs]
697 874
698Replaces `if let` with an else branch with a `match` expression. 875Replaces `if let` with an else branch with a `match` expression.
699 876
877.Before
700```rust 878```rust
701// BEFORE
702enum Action { Move { distance: u32 }, Stop } 879enum Action { Move { distance: u32 }, Stop }
703 880
704fn handle(action: Action) { 881fn handle(action: Action) {
@@ -708,8 +885,10 @@ fn handle(action: Action) {
708 bar() 885 bar()
709 } 886 }
710} 887}
888```
711 889
712// AFTER 890.After
891```rust
713enum Action { Move { distance: u32 }, Stop } 892enum Action { Move { distance: u32 }, Stop }
714 893
715fn handle(action: Action) { 894fn handle(action: Action) {
@@ -720,20 +899,25 @@ fn handle(action: Action) {
720} 899}
721``` 900```
722 901
723## `replace_let_with_if_let` 902
903[discrete]
904=== `replace_let_with_if_let`
905**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/replace_let_with_if_let.rs#L14[replace_let_with_if_let.rs]
724 906
725Replaces `let` with an `if-let`. 907Replaces `let` with an `if-let`.
726 908
909.Before
727```rust 910```rust
728// BEFORE
729 911
730fn main(action: Action) { 912fn main(action: Action) {
731 ┃let x = compute(); 913 ┃let x = compute();
732} 914}
733 915
734fn compute() -> Option<i32> { None } 916fn compute() -> Option<i32> { None }
917```
735 918
736// AFTER 919.After
920```rust
737 921
738fn main(action: Action) { 922fn main(action: Action) {
739 if let Some(x) = compute() { 923 if let Some(x) = compute() {
@@ -743,33 +927,43 @@ fn main(action: Action) {
743fn compute() -> Option<i32> { None } 927fn compute() -> Option<i32> { None }
744``` 928```
745 929
746## `replace_qualified_name_with_use` 930
931[discrete]
932=== `replace_qualified_name_with_use`
933**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs#L6[replace_qualified_name_with_use.rs]
747 934
748Adds a use statement for a given fully-qualified name. 935Adds a use statement for a given fully-qualified name.
749 936
937.Before
750```rust 938```rust
751// BEFORE
752fn process(map: std::collections::┃HashMap<String, String>) {} 939fn process(map: std::collections::┃HashMap<String, String>) {}
940```
753 941
754// AFTER 942.After
943```rust
755use std::collections::HashMap; 944use std::collections::HashMap;
756 945
757fn process(map: HashMap<String, String>) {} 946fn process(map: HashMap<String, String>) {}
758``` 947```
759 948
760## `replace_unwrap_with_match` 949
950[discrete]
951=== `replace_unwrap_with_match`
952**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs#L17[replace_unwrap_with_match.rs]
761 953
762Replaces `unwrap` a `match` expression. Works for Result and Option. 954Replaces `unwrap` a `match` expression. Works for Result and Option.
763 955
956.Before
764```rust 957```rust
765// BEFORE
766enum Result<T, E> { Ok(T), Err(E) } 958enum Result<T, E> { Ok(T), Err(E) }
767fn main() { 959fn main() {
768 let x: Result<i32, i32> = Result::Ok(92); 960 let x: Result<i32, i32> = Result::Ok(92);
769 let y = x.┃unwrap(); 961 let y = x.┃unwrap();
770} 962}
963```
771 964
772// AFTER 965.After
966```rust
773enum Result<T, E> { Ok(T), Err(E) } 967enum Result<T, E> { Ok(T), Err(E) }
774fn main() { 968fn main() {
775 let x: Result<i32, i32> = Result::Ok(92); 969 let x: Result<i32, i32> = Result::Ok(92);
@@ -780,31 +974,41 @@ fn main() {
780} 974}
781``` 975```
782 976
783## `split_import` 977
978[discrete]
979=== `split_import`
980**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/split_import.rs#L7[split_import.rs]
784 981
785Wraps the tail of import into braces. 982Wraps the tail of import into braces.
786 983
984.Before
787```rust 985```rust
788// BEFORE
789use std::┃collections::HashMap; 986use std::┃collections::HashMap;
987```
790 988
791// AFTER 989.After
990```rust
792use std::{collections::HashMap}; 991use std::{collections::HashMap};
793``` 992```
794 993
795## `unwrap_block` 994
995[discrete]
996=== `unwrap_block`
997**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/unwrap_block.rs#L9[unwrap_block.rs]
796 998
797This assist removes if...else, for, while and loop control statements to just keep the body. 999This assist removes if...else, for, while and loop control statements to just keep the body.
798 1000
1001.Before
799```rust 1002```rust
800// BEFORE
801fn foo() { 1003fn foo() {
802 if true {┃ 1004 if true {┃
803 println!("foo"); 1005 println!("foo");
804 } 1006 }
805} 1007}
1008```
806 1009
807// AFTER 1010.After
1011```rust
808fn foo() { 1012fn foo() {
809 println!("foo"); 1013 println!("foo");
810} 1014}
diff --git a/docs/user/generated_features.adoc b/docs/user/generated_features.adoc
index 803073d55..12812fa0b 100644
--- a/docs/user/generated_features.adoc
+++ b/docs/user/generated_features.adoc
@@ -1,5 +1,5 @@
1=== Expand Macro Recursively 1=== Expand Macro Recursively
2**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/expand_macro.rs[expand_macro.rs] 2**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/expand_macro.rs#L15[expand_macro.rs]
3 3
4Shows the full macro expansion of the macro at current cursor. 4Shows the full macro expansion of the macro at current cursor.
5 5
@@ -11,7 +11,7 @@ Shows the full macro expansion of the macro at current cursor.
11 11
12 12
13=== Extend Selection 13=== Extend Selection
14**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/extend_selection.rs[extend_selection.rs] 14**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/extend_selection.rs#L15[extend_selection.rs]
15 15
16Extends the current selection to the encompassing syntactic construct 16Extends the current selection to the encompassing syntactic construct
17(expression, statement, item, module, etc). It works with multiple cursors. 17(expression, statement, item, module, etc). It works with multiple cursors.
@@ -24,7 +24,7 @@ Extends the current selection to the encompassing syntactic construct
24 24
25 25
26=== File Structure 26=== File Structure
27**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/display/structure.rs[structure.rs] 27**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/display/structure.rs#L17[structure.rs]
28 28
29Provides a tree of the symbols defined in the file. Can be used to 29Provides a tree of the symbols defined in the file. Can be used to
30 30
@@ -40,7 +40,7 @@ Provides a tree of the symbols defined in the file. Can be used to
40 40
41 41
42=== Go to Definition 42=== Go to Definition
43**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/goto_definition.rs[goto_definition.rs] 43**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/goto_definition.rs#L18[goto_definition.rs]
44 44
45Navigates to the definition of an identifier. 45Navigates to the definition of an identifier.
46 46
@@ -52,7 +52,7 @@ Navigates to the definition of an identifier.
52 52
53 53
54=== Go to Implementation 54=== Go to Implementation
55**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/goto_implementation.rs[goto_implementation.rs] 55**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/goto_implementation.rs#L7[goto_implementation.rs]
56 56
57Navigates to the impl block of structs, enums or traits. Also implemented as a code lens. 57Navigates to the impl block of structs, enums or traits. Also implemented as a code lens.
58 58
@@ -64,7 +64,7 @@ Navigates to the impl block of structs, enums or traits. Also implemented as a c
64 64
65 65
66=== Go to Type Definition 66=== Go to Type Definition
67**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/goto_type_definition.rs[goto_type_definition.rs] 67**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/goto_type_definition.rs#L6[goto_type_definition.rs]
68 68
69Navigates to the type of an identifier. 69Navigates to the type of an identifier.
70 70
@@ -76,14 +76,14 @@ Navigates to the type of an identifier.
76 76
77 77
78=== Hover 78=== Hover
79**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/hover.rs[hover.rs] 79**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/hover.rs#L63[hover.rs]
80 80
81Shows additional information, like type of an expression or documentation for definition when "focusing" code. 81Shows additional information, like type of an expression or documentation for definition when "focusing" code.
82Focusing is usually hovering with a mouse, but can also be triggered with a shortcut. 82Focusing is usually hovering with a mouse, but can also be triggered with a shortcut.
83 83
84 84
85=== Inlay Hints 85=== Inlay Hints
86**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/inlay_hints.rs[inlay_hints.rs] 86**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/inlay_hints.rs#L40[inlay_hints.rs]
87 87
88rust-analyzer shows additional information inline with the source code. 88rust-analyzer shows additional information inline with the source code.
89Editors usually render this using read-only virtual text snippets interspersed with code. 89Editors usually render this using read-only virtual text snippets interspersed with code.
@@ -106,7 +106,7 @@ https://github.com/rust-analyzer/rust-analyzer/issues/1623[1], https://github.co
106 106
107 107
108=== Join Lines 108=== Join Lines
109**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/join_lines.rs[join_lines.rs] 109**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/join_lines.rs#L12[join_lines.rs]
110 110
111Join selected lines into one, smartly fixing up whitespace, trailing commas, and braces. 111Join selected lines into one, smartly fixing up whitespace, trailing commas, and braces.
112 112
@@ -118,7 +118,7 @@ Join selected lines into one, smartly fixing up whitespace, trailing commas, and
118 118
119 119
120=== Magic Completions 120=== Magic Completions
121**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/completion.rs[completion.rs] 121**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/completion.rs#L38[completion.rs]
122 122
123In addition to usual reference completion, rust-analyzer provides some ✨magic✨ 123In addition to usual reference completion, rust-analyzer provides some ✨magic✨
124completions as well: 124completions as well:
@@ -163,7 +163,7 @@ mod tests {
163 163
164 164
165=== Matching Brace 165=== Matching Brace
166**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/matching_brace.rs[matching_brace.rs] 166**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/matching_brace.rs#L3[matching_brace.rs]
167 167
168If the cursor is on any brace (`<>(){}[]`) which is a part of a brace-pair, 168If the cursor is on any brace (`<>(){}[]`) which is a part of a brace-pair,
169moves cursor to the matching brace. It uses the actual parser to determine 169moves cursor to the matching brace. It uses the actual parser to determine
@@ -177,7 +177,7 @@ braces, so it won't confuse generics with comparisons.
177 177
178 178
179=== On Typing Assists 179=== On Typing Assists
180**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/typing.rs[typing.rs] 180**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/typing.rs#L35[typing.rs]
181 181
182Some features trigger on typing certain characters: 182Some features trigger on typing certain characters:
183 183
@@ -187,7 +187,7 @@ Some features trigger on typing certain characters:
187 187
188 188
189=== Parent Module 189=== Parent Module
190**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/parent_module.rs[parent_module.rs] 190**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/parent_module.rs#L12[parent_module.rs]
191 191
192Navigates to the parent module of the current module. 192Navigates to the parent module of the current module.
193 193
@@ -199,7 +199,7 @@ Navigates to the parent module of the current module.
199 199
200 200
201=== Run 201=== Run
202**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/runnables.rs[runnables.rs] 202**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/runnables.rs#L45[runnables.rs]
203 203
204Shows a popup suggesting to run a test/benchmark/binary **at the current cursor 204Shows a popup suggesting to run a test/benchmark/binary **at the current cursor
205location**. Super useful for repeatedly running just a single test. Do bind this 205location**. Super useful for repeatedly running just a single test. Do bind this
@@ -213,7 +213,7 @@ to a shortcut!
213 213
214 214
215=== Semantic Syntax Highlighting 215=== Semantic Syntax Highlighting
216**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/syntax_highlighting.rs[syntax_highlighting.rs] 216**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/syntax_highlighting.rs#L33[syntax_highlighting.rs]
217 217
218rust-analyzer highlights the code semantically. 218rust-analyzer highlights the code semantically.
219For example, `bar` in `foo::Bar` might be colored differently depending on whether `Bar` is an enum or a trait. 219For example, `bar` in `foo::Bar` might be colored differently depending on whether `Bar` is an enum or a trait.
@@ -225,7 +225,7 @@ We also give special modifier for `mut` and `&mut` local variables.
225 225
226 226
227=== Show Syntax Tree 227=== Show Syntax Tree
228**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/syntax_tree.rs[syntax_tree.rs] 228**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/syntax_tree.rs#L9[syntax_tree.rs]
229 229
230Shows the parse tree of the current file. It exists mostly for debugging 230Shows the parse tree of the current file. It exists mostly for debugging
231rust-analyzer itself. 231rust-analyzer itself.
@@ -238,7 +238,7 @@ rust-analyzer itself.
238 238
239 239
240=== Status 240=== Status
241**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/status.rs[status.rs] 241**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/status.rs#L27[status.rs]
242 242
243Shows internal statistic about memory usage of rust-analyzer. 243Shows internal statistic about memory usage of rust-analyzer.
244 244
@@ -250,7 +250,7 @@ Shows internal statistic about memory usage of rust-analyzer.
250 250
251 251
252=== Structural Seach and Replace 252=== Structural Seach and Replace
253**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/ssr.rs[ssr.rs] 253**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/ssr.rs#L26[ssr.rs]
254 254
255Search and replace with named wildcards that will match any expression. 255Search and replace with named wildcards that will match any expression.
256The syntax for a structural search replace command is `<search_pattern> ==>> <replace_pattern>`. 256The syntax for a structural search replace command is `<search_pattern> ==>> <replace_pattern>`.
@@ -275,7 +275,7 @@ String::from((y + 5).foo(z))
275 275
276 276
277=== Workspace Symbol 277=== Workspace Symbol
278**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide_db/src/symbol_index.rs[symbol_index.rs] 278**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide_db/src/symbol_index.rs#L113[symbol_index.rs]
279 279
280Uses fuzzy-search to find types, modules and functions by name across your 280Uses fuzzy-search to find types, modules and functions by name across your
281project and dependencies. This is **the** most useful feature, which improves code 281project and dependencies. This is **the** most useful feature, which improves code
diff --git a/docs/user/readme.adoc b/docs/user/manual.adoc
index 12def7327..202783fd9 100644
--- a/docs/user/readme.adoc
+++ b/docs/user/manual.adoc
@@ -2,12 +2,7 @@
2:toc: preamble 2:toc: preamble
3:sectanchors: 3:sectanchors:
4:page-layout: post 4:page-layout: post
5// https://gist.github.com/dcode/0cfbf2699a1fe9b46ff04c41721dda74#admonitions 5:icons: font
6:tip-caption: :bulb:
7:note-caption: :information_source:
8:important-caption: :heavy_exclamation_mark:
9:caution-caption: :fire:
10:warning-caption: :warning:
11:source-highlighter: rouge 6:source-highlighter: rouge
12:experimental: 7:experimental:
13 8
@@ -19,7 +14,7 @@ https://microsoft.github.io/language-server-protocol/[Language Server Protocol]
19The LSP allows various code editors, like VS Code, Emacs or Vim, to implement semantic features like completion or goto definition by talking to an external language server process. 14The LSP allows various code editors, like VS Code, Emacs or Vim, to implement semantic features like completion or goto definition by talking to an external language server process.
20 15
21To improve this document, send a pull request against 16To improve this document, send a pull request against
22https://github.com/rust-analyzer/rust-analyzer/blob/master/docs/user/readme.adoc[this file]. 17https://github.com/rust-analyzer/rust-analyzer/blob/master/docs/user/manual.adoc[this file].
23 18
24If you have questions about using rust-analyzer, please ask them in the https://users.rust-lang.org/c/ide/14["`IDEs and Editors`"] topic of Rust users forum. 19If you have questions about using rust-analyzer, please ask them in the https://users.rust-lang.org/c/ide/14["`IDEs and Editors`"] topic of Rust users forum.
25 20
@@ -67,16 +62,6 @@ The server binary is stored in:
67 62
68Note that we only support two most recent versions of VS Code. 63Note that we only support two most recent versions of VS Code.
69 64
70==== Special `when` clause context for keybindings.
71You may use `inRustProject` context to configure keybindings for rust projects only. For example:
72[source,json]
73----
74{ "key": "ctrl+shift+f5", "command": "workbench.action.debug.restart", "when": "inDebugMode && !inRustProject"},
75{ "key": "ctrl+shift+f5", "command": "rust-analyzer.debug", "when": "inRustProject"},
76{ "key": "ctrl+i", "command": "rust-analyzer.toggleInlayHints", "when": "inRustProject" }
77----
78More about `when` clause contexts https://code.visualstudio.com/docs/getstarted/keybindings#_when-clause-contexts[here].
79
80==== Updates 65==== Updates
81 66
82The extension will be updated automatically as new versions become available. It will ask your permission to download the matching language server version binary if needed. 67The extension will be updated automatically as new versions become available. It will ask your permission to download the matching language server version binary if needed.
@@ -124,10 +109,23 @@ Here are some useful self-diagnostic commands:
124* To log all LSP requests, add `"rust-analyzer.trace.server": "verbose"` to the settings and look for `Server Trace` in the panel. 109* To log all LSP requests, add `"rust-analyzer.trace.server": "verbose"` to the settings and look for `Server Trace` in the panel.
125* To enable client-side logging, add `"rust-analyzer.trace.extension": true` to the settings and open the `Console` tab of VS Code developer tools. 110* To enable client-side logging, add `"rust-analyzer.trace.extension": true` to the settings and open the `Console` tab of VS Code developer tools.
126 111
112==== Special `when` clause context for keybindings.
113You may use `inRustProject` context to configure keybindings for rust projects only. For example:
114[source,json]
115----
116{
117 "key": "ctrl+i",
118 "command": "rust-analyzer.toggleInlayHints",
119 "when": "inRustProject"
120}
121----
122More about `when` clause contexts https://code.visualstudio.com/docs/getstarted/keybindings#_when-clause-contexts[here].
123
127=== rust-analyzer Language Server Binary 124=== rust-analyzer Language Server Binary
128 125
129Other editors generally require the `rust-analyzer` binary to be in `$PATH`. 126Other editors generally require the `rust-analyzer` binary to be in `$PATH`.
130You can download the pre-built binary from the https://github.com/rust-analyzer/rust-analyzer/releases[releases] page. Typically, you then need to rename the binary for your platform, e.g. `rust-analyzer-mac` if you're on Mac OS, to `rust-analyzer` and make it executable in addition to moving it into a directory in your `$PATH`. 127You can download the pre-built binary from the https://github.com/rust-analyzer/rust-analyzer/releases[releases] page.
128Typically, you then need to rename the binary for your platform, e.g. `rust-analyzer-mac` if you're on Mac OS, to `rust-analyzer` and make it executable in addition to moving it into a directory in your `$PATH`.
131 129
132On Linux to install the `rust-analyzer` binary into `~/.local/bin`, this commands could be used 130On Linux to install the `rust-analyzer` binary into `~/.local/bin`, this commands could be used
133 131
@@ -147,7 +145,8 @@ $ git clone https://github.com/rust-analyzer/rust-analyzer.git && cd rust-analyz
147$ cargo xtask install --server 145$ cargo xtask install --server
148---- 146----
149 147
150If your editor can't find the binary even though the binary is on your `$PATH`, the likely explanation is that it doesn't see the same `$PATH` as the shell, see https://github.com/rust-analyzer/rust-analyzer/issues/1811[this issue]. On Unix, running the editor from a shell or changing the `.desktop` file to set the environment should help. 148If your editor can't find the binary even though the binary is on your `$PATH`, the likely explanation is that it doesn't see the same `$PATH` as the shell, see https://github.com/rust-analyzer/rust-analyzer/issues/1811[this issue].
149On Unix, running the editor from a shell or changing the `.desktop` file to set the environment should help.
151 150
152==== Arch Linux 151==== Arch Linux
153 152
@@ -278,5 +277,6 @@ include::./generated_features.adoc[]
278 277
279Assists, or code actions, are small local refactorings, available in a particular context. 278Assists, or code actions, are small local refactorings, available in a particular context.
280They are usually triggered by a shortcut or by clicking a light bulb icon in the editor. 279They are usually triggered by a shortcut or by clicking a light bulb icon in the editor.
280Cursor position or selection is signified by `┃` character.
281 281
282See [assists.md](./assists.md) for the list of available assists. 282include::./generated_assists.adoc[]
diff --git a/xtask/src/ast_src.rs b/xtask/src/ast_src.rs
index d4621930e..f60f0fb16 100644
--- a/xtask/src/ast_src.rs
+++ b/xtask/src/ast_src.rs
@@ -1058,7 +1058,7 @@ pub(crate) const AST_SRC: AstSrc = AstSrc {
1058 /// [Reference](https://doc.rust-lang.org/reference/expressions/block-expr.html) 1058 /// [Reference](https://doc.rust-lang.org/reference/expressions/block-expr.html)
1059 /// [Labels for blocks RFC](https://github.com/rust-lang/rfcs/blob/master/text/2046-label-break-value.md) 1059 /// [Labels for blocks RFC](https://github.com/rust-lang/rfcs/blob/master/text/2046-label-break-value.md)
1060 struct BlockExpr: AttrsOwner, ModuleItemOwner { 1060 struct BlockExpr: AttrsOwner, ModuleItemOwner {
1061 T!['{'], statements: [Stmt], Expr, T!['}'], 1061 Label, T!['{'], statements: [Stmt], Expr, T!['}'],
1062 } 1062 }
1063 1063
1064 /// Return expression. 1064 /// Return expression.
diff --git a/xtask/src/codegen.rs b/xtask/src/codegen.rs
index f47d54125..5511c01d5 100644
--- a/xtask/src/codegen.rs
+++ b/xtask/src/codegen.rs
@@ -10,9 +10,12 @@ mod gen_parser_tests;
10mod gen_assists_docs; 10mod gen_assists_docs;
11mod gen_feature_docs; 11mod gen_feature_docs;
12 12
13use std::{mem, path::Path}; 13use std::{
14 fmt, mem,
15 path::{Path, PathBuf},
16};
14 17
15use crate::{not_bash::fs2, Result}; 18use crate::{not_bash::fs2, project_root, Result};
16 19
17pub use self::{ 20pub use self::{
18 gen_assists_docs::generate_assists_docs, gen_feature_docs::generate_feature_docs, 21 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";
29 32
30const ASSISTS_DIR: &str = "crates/ra_assists/src/handlers"; 33const ASSISTS_DIR: &str = "crates/ra_assists/src/handlers";
31const ASSISTS_TESTS: &str = "crates/ra_assists/src/tests/generated.rs"; 34const ASSISTS_TESTS: &str = "crates/ra_assists/src/tests/generated.rs";
32const ASSISTS_DOCS: &str = "docs/user/assists.md";
33 35
34#[derive(Debug, PartialEq, Eq, Clone, Copy)] 36#[derive(Debug, PartialEq, Eq, Clone, Copy)]
35pub enum Mode { 37pub enum Mode {
@@ -59,18 +61,18 @@ fn update(path: &Path, contents: &str, mode: Mode) -> Result<()> {
59} 61}
60 62
61fn extract_comment_blocks(text: &str) -> Vec<Vec<String>> { 63fn extract_comment_blocks(text: &str) -> Vec<Vec<String>> {
62 do_extract_comment_blocks(text, false) 64 do_extract_comment_blocks(text, false).into_iter().map(|(_line, block)| block).collect()
63} 65}
64 66
65fn extract_comment_blocks_with_empty_lines(tag: &str, text: &str) -> Vec<CommentBlock> { 67fn extract_comment_blocks_with_empty_lines(tag: &str, text: &str) -> Vec<CommentBlock> {
66 assert!(tag.starts_with(char::is_uppercase)); 68 assert!(tag.starts_with(char::is_uppercase));
67 let tag = format!("{}:", tag); 69 let tag = format!("{}:", tag);
68 let mut res = Vec::new(); 70 let mut res = Vec::new();
69 for mut block in do_extract_comment_blocks(text, true) { 71 for (line, mut block) in do_extract_comment_blocks(text, true) {
70 let first = block.remove(0); 72 let first = block.remove(0);
71 if first.starts_with(&tag) { 73 if first.starts_with(&tag) {
72 let id = first[tag.len()..].trim().to_string(); 74 let id = first[tag.len()..].trim().to_string();
73 let block = CommentBlock { id, contents: block }; 75 let block = CommentBlock { id, line, contents: block };
74 res.push(block); 76 res.push(block);
75 } 77 }
76 } 78 }
@@ -79,31 +81,65 @@ fn extract_comment_blocks_with_empty_lines(tag: &str, text: &str) -> Vec<Comment
79 81
80struct CommentBlock { 82struct CommentBlock {
81 id: String, 83 id: String,
84 line: usize,
82 contents: Vec<String>, 85 contents: Vec<String>,
83} 86}
84 87
85fn do_extract_comment_blocks(text: &str, allow_blocks_with_empty_lines: bool) -> Vec<Vec<String>> { 88fn do_extract_comment_blocks(
89 text: &str,
90 allow_blocks_with_empty_lines: bool,
91) -> Vec<(usize, Vec<String>)> {
86 let mut res = Vec::new(); 92 let mut res = Vec::new();
87 93
88 let prefix = "// "; 94 let prefix = "// ";
89 let lines = text.lines().map(str::trim_start); 95 let lines = text.lines().map(str::trim_start);
90 96
91 let mut block = vec![]; 97 let mut block = (0, vec![]);
92 for line in lines { 98 for (line_num, line) in lines.enumerate() {
93 if line == "//" && allow_blocks_with_empty_lines { 99 if line == "//" && allow_blocks_with_empty_lines {
94 block.push(String::new()); 100 block.1.push(String::new());
95 continue; 101 continue;
96 } 102 }
97 103
98 let is_comment = line.starts_with(prefix); 104 let is_comment = line.starts_with(prefix);
99 if is_comment { 105 if is_comment {
100 block.push(line[prefix.len()..].to_string()); 106 block.1.push(line[prefix.len()..].to_string());
101 } else if !block.is_empty() { 107 } else {
102 res.push(mem::replace(&mut block, Vec::new())); 108 if !block.1.is_empty() {
109 res.push(mem::take(&mut block));
110 }
111 block.0 = line_num + 2;
103 } 112 }
104 } 113 }
105 if !block.is_empty() { 114 if !block.1.is_empty() {
106 res.push(mem::replace(&mut block, Vec::new())) 115 res.push(block)
107 } 116 }
108 res 117 res
109} 118}
119
120#[derive(Debug)]
121struct Location {
122 file: PathBuf,
123 line: usize,
124}
125
126impl Location {
127 fn new(file: PathBuf, line: usize) -> Self {
128 Self { file, line }
129 }
130}
131
132impl fmt::Display for Location {
133 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134 let path = self.file.strip_prefix(&project_root()).unwrap().display().to_string();
135 let path = path.replace('\\', "/");
136 let name = self.file.file_name().unwrap();
137 write!(
138 f,
139 "https://github.com/rust-analyzer/rust-analyzer/blob/master/{}#L{}[{}]",
140 path,
141 self.line,
142 name.to_str().unwrap()
143 )
144 }
145}
diff --git a/xtask/src/codegen/gen_assists_docs.rs b/xtask/src/codegen/gen_assists_docs.rs
index 6ebeb8aea..6c1be5350 100644
--- a/xtask/src/codegen/gen_assists_docs.rs
+++ b/xtask/src/codegen/gen_assists_docs.rs
@@ -1,22 +1,28 @@
1//! Generates `assists.md` documentation. 1//! Generates `assists.md` documentation.
2 2
3use std::{fs, path::Path}; 3use std::{fmt, fs, path::Path};
4 4
5use crate::{ 5use crate::{
6 codegen::{self, extract_comment_blocks_with_empty_lines, Mode}, 6 codegen::{self, extract_comment_blocks_with_empty_lines, Location, Mode},
7 project_root, rust_files, Result, 7 project_root, rust_files, Result,
8}; 8};
9 9
10pub fn generate_assists_docs(mode: Mode) -> Result<()> { 10pub fn generate_assists_docs(mode: Mode) -> Result<()> {
11 let assists = Assist::collect()?; 11 let assists = Assist::collect()?;
12 generate_tests(&assists, mode)?; 12 generate_tests(&assists, mode)?;
13 generate_docs(&assists, mode)?; 13
14 let contents = assists.into_iter().map(|it| it.to_string()).collect::<Vec<_>>().join("\n\n");
15 let contents = contents.trim().to_string() + "\n";
16 let dst = project_root().join("docs/user/generated_assists.adoc");
17 codegen::update(&dst, &contents, mode)?;
18
14 Ok(()) 19 Ok(())
15} 20}
16 21
17#[derive(Debug)] 22#[derive(Debug)]
18struct Assist { 23struct Assist {
19 id: String, 24 id: String,
25 location: Location,
20 doc: String, 26 doc: String,
21 before: String, 27 before: String,
22 after: String, 28 after: String,
@@ -58,7 +64,8 @@ impl Assist {
58 assert_eq!(lines.next().unwrap().as_str(), "->"); 64 assert_eq!(lines.next().unwrap().as_str(), "->");
59 assert_eq!(lines.next().unwrap().as_str(), "```"); 65 assert_eq!(lines.next().unwrap().as_str(), "```");
60 let after = take_until(lines.by_ref(), "```"); 66 let after = take_until(lines.by_ref(), "```");
61 acc.push(Assist { id, doc, before, after }) 67 let location = Location::new(path.to_path_buf(), block.line);
68 acc.push(Assist { id, location, doc, before, after })
62 } 69 }
63 70
64 fn take_until<'a>(lines: impl Iterator<Item = &'a String>, marker: &str) -> String { 71 fn take_until<'a>(lines: impl Iterator<Item = &'a String>, marker: &str) -> String {
@@ -76,6 +83,33 @@ impl Assist {
76 } 83 }
77} 84}
78 85
86impl fmt::Display for Assist {
87 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88 let before = self.before.replace("<|>", "┃"); // Unicode pseudo-graphics bar
89 let after = self.after.replace("<|>", "┃");
90 writeln!(
91 f,
92 "[discrete]\n=== `{}`
93**Source:** {}
94
95{}
96
97.Before
98```rust
99{}```
100
101.After
102```rust
103{}```",
104 self.id,
105 self.location,
106 self.doc,
107 hide_hash_comments(&before),
108 hide_hash_comments(&after)
109 )
110 }
111}
112
79fn generate_tests(assists: &[Assist], mode: Mode) -> Result<()> { 113fn generate_tests(assists: &[Assist], mode: Mode) -> Result<()> {
80 let mut buf = String::from("use super::check_doc_test;\n"); 114 let mut buf = String::from("use super::check_doc_test;\n");
81 115
@@ -103,37 +137,6 @@ r#####"
103 codegen::update(&project_root().join(codegen::ASSISTS_TESTS), &buf, mode) 137 codegen::update(&project_root().join(codegen::ASSISTS_TESTS), &buf, mode)
104} 138}
105 139
106fn generate_docs(assists: &[Assist], mode: Mode) -> Result<()> {
107 let mut buf = String::from(
108 "# Assists\n\nCursor position or selection is signified by `┃` character.\n\n",
109 );
110
111 for assist in assists {
112 let before = assist.before.replace("<|>", "┃"); // Unicode pseudo-graphics bar
113 let after = assist.after.replace("<|>", "┃");
114 let docs = format!(
115 "
116## `{}`
117
118{}
119
120```rust
121// BEFORE
122{}
123// AFTER
124{}```
125",
126 assist.id,
127 assist.doc,
128 hide_hash_comments(&before),
129 hide_hash_comments(&after)
130 );
131 buf.push_str(&docs);
132 }
133
134 codegen::update(&project_root().join(codegen::ASSISTS_DOCS), &buf, mode)
135}
136
137fn hide_hash_comments(text: &str) -> String { 140fn hide_hash_comments(text: &str) -> String {
138 text.split('\n') // want final newline 141 text.split('\n') // want final newline
139 .filter(|&it| !(it.starts_with("# ") || it == "#")) 142 .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 dbe583e8e..31bc3839d 100644
--- a/xtask/src/codegen/gen_feature_docs.rs
+++ b/xtask/src/codegen/gen_feature_docs.rs
@@ -3,7 +3,7 @@
3use std::{fmt, fs, path::PathBuf}; 3use std::{fmt, fs, path::PathBuf};
4 4
5use crate::{ 5use crate::{
6 codegen::{self, extract_comment_blocks_with_empty_lines, Mode}, 6 codegen::{self, extract_comment_blocks_with_empty_lines, Location, Mode},
7 project_root, rust_files, Result, 7 project_root, rust_files, Result,
8}; 8};
9 9
@@ -19,7 +19,7 @@ pub fn generate_feature_docs(mode: Mode) -> Result<()> {
19#[derive(Debug)] 19#[derive(Debug)]
20struct Feature { 20struct Feature {
21 id: String, 21 id: String,
22 path: PathBuf, 22 location: Location,
23 doc: String, 23 doc: String,
24} 24}
25 25
@@ -40,7 +40,8 @@ impl Feature {
40 let id = block.id; 40 let id = block.id;
41 assert!(is_valid_feature_name(&id), "invalid feature name: {:?}", id); 41 assert!(is_valid_feature_name(&id), "invalid feature name: {:?}", id);
42 let doc = block.contents.join("\n"); 42 let doc = block.contents.join("\n");
43 acc.push(Feature { id, path: path.clone(), doc }) 43 let location = Location::new(path.clone(), block.line);
44 acc.push(Feature { id, location, doc })
44 } 45 }
45 46
46 Ok(()) 47 Ok(())
@@ -69,20 +70,6 @@ fn is_valid_feature_name(feature: &str) -> bool {
69 70
70impl fmt::Display for Feature { 71impl fmt::Display for Feature {
71 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 72 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72 writeln!(f, "=== {}", self.id)?; 73 writeln!(f, "=== {}\n**Source:** {}\n{}", self.id, self.location, self.doc)
73 let path = self.path.strip_prefix(&project_root()).unwrap().display().to_string();
74 let path = path.replace('\\', "/");
75 let name = self.path.file_name().unwrap();
76
77 //FIXME: generate line number as well
78 writeln!(
79 f,
80 "**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/{}[{}]",
81 path,
82 name.to_str().unwrap(),
83 )?;
84
85 writeln!(f, "{}", self.doc)?;
86 Ok(())
87 } 74 }
88} 75}
diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs
index 2b7a461e5..874957885 100644
--- a/xtask/src/lib.rs
+++ b/xtask/src/lib.rs
@@ -191,7 +191,11 @@ Release: release:{}[]
191 let path = changelog_dir.join(format!("{}-changelog-{}.adoc", today, changelog_n)); 191 let path = changelog_dir.join(format!("{}-changelog-{}.adoc", today, changelog_n));
192 fs2::write(&path, &contents)?; 192 fs2::write(&path, &contents)?;
193 193
194 fs2::copy(project_root().join("./docs/user/readme.adoc"), website_root.join("manual.adoc"))?; 194 for &adoc in ["manual.adoc", "generated_features.adoc", "generated_assists.adoc"].iter() {
195 let src = project_root().join("./docs/user/").join(adoc);
196 let dst = website_root.join(adoc);
197 fs2::copy(src, dst)?;
198 }
195 199
196 let tags = run!("git tag --list"; echo = false)?; 200 let tags = run!("git tag --list"; echo = false)?;
197 let prev_tag = tags.lines().filter(|line| is_release_tag(line)).last().unwrap(); 201 let prev_tag = tags.lines().filter(|line| is_release_tag(line)).last().unwrap();