aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--Cargo.lock36
-rw-r--r--crates/ra_assists/src/handlers/add_from_impl_for_enum.rs4
-rw-r--r--crates/ra_assists/src/handlers/introduce_named_lifetime.rs (renamed from crates/ra_assists/src/handlers/change_lifetime_anon_to_named.rs)62
-rw-r--r--crates/ra_assists/src/lib.rs4
-rw-r--r--crates/ra_assists/src/tests/generated.rs69
-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_ide/src/completion.rs47
-rw-r--r--crates/ra_ide/src/completion/complete_postfix.rs6
-rw-r--r--crates/ra_ide/src/display/structure.rs19
-rw-r--r--crates/ra_ide/src/expand_macro.rs11
-rw-r--r--crates/ra_ide/src/extend_selection.rs12
-rw-r--r--crates/ra_ide/src/goto_definition.rs11
-rw-r--r--crates/ra_ide/src/goto_implementation.rs (renamed from crates/ra_ide/src/impls.rs)11
-rw-r--r--crates/ra_ide/src/goto_type_definition.rs11
-rw-r--r--crates/ra_ide/src/hover.rs116
-rw-r--r--crates/ra_ide/src/inlay_hints.rs22
-rw-r--r--crates/ra_ide/src/join_lines.rs11
-rw-r--r--crates/ra_ide/src/lib.rs4
-rw-r--r--crates/ra_ide/src/matching_brace.rs13
-rw-r--r--crates/ra_ide/src/parent_module.rs12
-rw-r--r--crates/ra_ide/src/references.rs27
-rw-r--r--crates/ra_ide/src/runnables.rs13
-rw-r--r--crates/ra_ide/src/ssr.rs24
-rw-r--r--crates/ra_ide/src/status.rs11
-rw-r--r--crates/ra_ide/src/syntax_highlighting.rs161
-rw-r--r--crates/ra_ide/src/syntax_tree.rs16
-rw-r--r--crates/ra_ide/src/typing.rs7
-rw-r--r--crates/ra_ide_db/src/search.rs40
-rw-r--r--crates/ra_ide_db/src/symbol_index.rs21
-rw-r--r--crates/ra_syntax/src/ast.rs2
-rw-r--r--crates/ra_syntax/src/ast/generated/nodes.rs1
-rw-r--r--crates/ra_syntax/src/syntax_node.rs4
-rw-r--r--docs/dev/lsp-extensions.md10
-rw-r--r--docs/dev/lsp-features.md72
-rw-r--r--docs/user/features.md218
-rw-r--r--docs/user/generated_assists.adoc (renamed from docs/user/assists.md)518
-rw-r--r--docs/user/generated_features.adoc298
-rw-r--r--docs/user/manual.adoc (renamed from docs/user/readme.adoc)51
-rw-r--r--editors/code/src/run.ts2
-rw-r--r--xtask/src/ast_src.rs2
-rw-r--r--xtask/src/codegen.rs89
-rw-r--r--xtask/src/codegen/gen_assists_docs.rs83
-rw-r--r--xtask/src/codegen/gen_feature_docs.rs75
-rw-r--r--xtask/src/lib.rs6
-rw-r--r--xtask/src/main.rs1
-rw-r--r--xtask/tests/tidy.rs14
53 files changed, 1620 insertions, 835 deletions
diff --git a/.gitignore b/.gitignore
index f835edef0..dab51647d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,4 +7,4 @@ crates/*/target
7*.log 7*.log
8*.iml 8*.iml
9.vscode/settings.json 9.vscode/settings.json
10cargo-timing*.html 10*.html
diff --git a/Cargo.lock b/Cargo.lock
index 07f18c760..af27bfc85 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",
@@ -1292,9 +1292,9 @@ checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
1292 1292
1293[[package]] 1293[[package]]
1294name = "regex" 1294name = "regex"
1295version = "1.3.7" 1295version = "1.3.9"
1296source = "registry+https://github.com/rust-lang/crates.io-index" 1296source = "registry+https://github.com/rust-lang/crates.io-index"
1297checksum = "a6020f034922e3194c711b82a627453881bc4682166cabb07134a10c26ba7692" 1297checksum = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6"
1298dependencies = [ 1298dependencies = [
1299 "aho-corasick", 1299 "aho-corasick",
1300 "memchr", 1300 "memchr",
@@ -1304,9 +1304,9 @@ dependencies = [
1304 1304
1305[[package]] 1305[[package]]
1306name = "regex-syntax" 1306name = "regex-syntax"
1307version = "0.6.17" 1307version = "0.6.18"
1308source = "registry+https://github.com/rust-lang/crates.io-index" 1308source = "registry+https://github.com/rust-lang/crates.io-index"
1309checksum = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae" 1309checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8"
1310 1310
1311[[package]] 1311[[package]]
1312name = "relative-path" 1312name = "relative-path"
@@ -1492,18 +1492,18 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
1492 1492
1493[[package]] 1493[[package]]
1494name = "serde" 1494name = "serde"
1495version = "1.0.110" 1495version = "1.0.111"
1496source = "registry+https://github.com/rust-lang/crates.io-index" 1496source = "registry+https://github.com/rust-lang/crates.io-index"
1497checksum = "99e7b308464d16b56eba9964e4972a3eee817760ab60d88c3f86e1fecb08204c" 1497checksum = "c9124df5b40cbd380080b2cc6ab894c040a3070d995f5c9dc77e18c34a8ae37d"
1498dependencies = [ 1498dependencies = [
1499 "serde_derive", 1499 "serde_derive",
1500] 1500]
1501 1501
1502[[package]] 1502[[package]]
1503name = "serde_derive" 1503name = "serde_derive"
1504version = "1.0.110" 1504version = "1.0.111"
1505source = "registry+https://github.com/rust-lang/crates.io-index" 1505source = "registry+https://github.com/rust-lang/crates.io-index"
1506checksum = "818fbf6bfa9a42d3bfcaca148547aa00c7b915bec71d1757aa2d44ca68771984" 1506checksum = "3f2c3ac8e6ca1e9c80b8be1023940162bf81ae3cffbb1809474152f2ce1eb250"
1507dependencies = [ 1507dependencies = [
1508 "proc-macro2", 1508 "proc-macro2",
1509 "quote", 1509 "quote",
@@ -1577,9 +1577,9 @@ checksum = "ab16ced94dbd8a46c82fd81e3ed9a8727dac2977ea869d217bcc4ea1f122e81f"
1577 1577
1578[[package]] 1578[[package]]
1579name = "syn" 1579name = "syn"
1580version = "1.0.25" 1580version = "1.0.29"
1581source = "registry+https://github.com/rust-lang/crates.io-index" 1581source = "registry+https://github.com/rust-lang/crates.io-index"
1582checksum = "f14a640819f79b72a710c0be059dce779f9339ae046c8bef12c361d56702146f" 1582checksum = "bb37da98a55b1d08529362d9cbb863be17556873df2585904ab9d2bc951291d0"
1583dependencies = [ 1583dependencies = [
1584 "proc-macro2", 1584 "proc-macro2",
1585 "quote", 1585 "quote",
diff --git a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs b/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs
index 6a675e812..776bddf91 100644
--- a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs
+++ b/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs
@@ -4,9 +4,9 @@ use test_utils::mark;
4 4
5use crate::{utils::FamousDefs, AssistContext, AssistId, Assists}; 5use crate::{utils::FamousDefs, AssistContext, AssistId, Assists};
6 6
7// Assist add_from_impl_for_enum 7// Assist: add_from_impl_for_enum
8// 8//
9// Adds a From impl for an enum variant with one tuple field 9// Adds a From impl for an enum variant with one tuple field.
10// 10//
11// ``` 11// ```
12// enum A { <|>One(u32) } 12// enum A { <|>One(u32) }
diff --git a/crates/ra_assists/src/handlers/change_lifetime_anon_to_named.rs b/crates/ra_assists/src/handlers/introduce_named_lifetime.rs
index 999aec421..beb5b7366 100644
--- a/crates/ra_assists/src/handlers/change_lifetime_anon_to_named.rs
+++ b/crates/ra_assists/src/handlers/introduce_named_lifetime.rs
@@ -1,12 +1,15 @@
1use crate::{assist_context::AssistBuilder, AssistContext, AssistId, Assists}; 1use ra_syntax::{
2use ast::{NameOwner, ParamList, TypeAscriptionOwner, TypeParamList, TypeRef}; 2 ast::{self, NameOwner, TypeAscriptionOwner, TypeParamsOwner},
3use ra_syntax::{ast, ast::TypeParamsOwner, AstNode, SyntaxKind, TextRange, TextSize}; 3 AstNode, SyntaxKind, TextRange, TextSize,
4};
4use rustc_hash::FxHashSet; 5use rustc_hash::FxHashSet;
5 6
6static ASSIST_NAME: &str = "change_lifetime_anon_to_named"; 7use crate::{assist_context::AssistBuilder, AssistContext, AssistId, Assists};
7static ASSIST_LABEL: &str = "Give anonymous lifetime a name"; 8
9static ASSIST_NAME: &str = "introduce_named_lifetime";
10static ASSIST_LABEL: &str = "Introduce named lifetime";
8 11
9// Assist: change_lifetime_anon_to_named 12// Assist: introduce_named_lifetime
10// 13//
11// Change an anonymous lifetime to a named lifetime. 14// Change an anonymous lifetime to a named lifetime.
12// 15//
@@ -31,7 +34,7 @@ static ASSIST_LABEL: &str = "Give anonymous lifetime a name";
31// ``` 34// ```
32// FIXME: How can we handle renaming any one of multiple anonymous lifetimes? 35// FIXME: How can we handle renaming any one of multiple anonymous lifetimes?
33// FIXME: should also add support for the case fun(f: &Foo) -> &<|>Foo 36// FIXME: should also add support for the case fun(f: &Foo) -> &<|>Foo
34pub(crate) fn change_lifetime_anon_to_named(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 37pub(crate) fn introduce_named_lifetime(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
35 let lifetime_token = ctx 38 let lifetime_token = ctx
36 .find_token_at_offset(SyntaxKind::LIFETIME) 39 .find_token_at_offset(SyntaxKind::LIFETIME)
37 .filter(|lifetime| lifetime.text() == "'_")?; 40 .filter(|lifetime| lifetime.text() == "'_")?;
@@ -52,7 +55,7 @@ fn generate_fn_def_assist(
52 fn_def: &ast::FnDef, 55 fn_def: &ast::FnDef,
53 lifetime_loc: TextRange, 56 lifetime_loc: TextRange,
54) -> Option<()> { 57) -> Option<()> {
55 let param_list: ParamList = fn_def.param_list()?; 58 let param_list: ast::ParamList = fn_def.param_list()?;
56 let new_lifetime_param = generate_unique_lifetime_param_name(&fn_def.type_param_list())?; 59 let new_lifetime_param = generate_unique_lifetime_param_name(&fn_def.type_param_list())?;
57 let end_of_fn_ident = fn_def.name()?.ident_token()?.text_range().end(); 60 let end_of_fn_ident = fn_def.name()?.ident_token()?.text_range().end();
58 let self_param = 61 let self_param =
@@ -67,7 +70,7 @@ fn generate_fn_def_assist(
67 let fn_params_without_lifetime: Vec<_> = param_list 70 let fn_params_without_lifetime: Vec<_> = param_list
68 .params() 71 .params()
69 .filter_map(|param| match param.ascribed_type() { 72 .filter_map(|param| match param.ascribed_type() {
70 Some(TypeRef::ReferenceType(ascribed_type)) 73 Some(ast::TypeRef::ReferenceType(ascribed_type))
71 if ascribed_type.lifetime_token() == None => 74 if ascribed_type.lifetime_token() == None =>
72 { 75 {
73 Some(ascribed_type.amp_token()?.text_range().end()) 76 Some(ascribed_type.amp_token()?.text_range().end())
@@ -106,7 +109,7 @@ fn generate_impl_def_assist(
106/// Given a type parameter list, generate a unique lifetime parameter name 109/// Given a type parameter list, generate a unique lifetime parameter name
107/// which is not in the list 110/// which is not in the list
108fn generate_unique_lifetime_param_name( 111fn generate_unique_lifetime_param_name(
109 existing_type_param_list: &Option<TypeParamList>, 112 existing_type_param_list: &Option<ast::TypeParamList>,
110) -> Option<char> { 113) -> Option<char> {
111 match existing_type_param_list { 114 match existing_type_param_list {
112 Some(type_params) => { 115 Some(type_params) => {
@@ -151,7 +154,7 @@ mod tests {
151 #[test] 154 #[test]
152 fn test_example_case() { 155 fn test_example_case() {
153 check_assist( 156 check_assist(
154 change_lifetime_anon_to_named, 157 introduce_named_lifetime,
155 r#"impl Cursor<'_<|>> { 158 r#"impl Cursor<'_<|>> {
156 fn node(self) -> &SyntaxNode { 159 fn node(self) -> &SyntaxNode {
157 match self { 160 match self {
@@ -172,7 +175,7 @@ mod tests {
172 #[test] 175 #[test]
173 fn test_example_case_simplified() { 176 fn test_example_case_simplified() {
174 check_assist( 177 check_assist(
175 change_lifetime_anon_to_named, 178 introduce_named_lifetime,
176 r#"impl Cursor<'_<|>> {"#, 179 r#"impl Cursor<'_<|>> {"#,
177 r#"impl<'a> Cursor<'a> {"#, 180 r#"impl<'a> Cursor<'a> {"#,
178 ); 181 );
@@ -181,7 +184,7 @@ mod tests {
181 #[test] 184 #[test]
182 fn test_example_case_cursor_after_tick() { 185 fn test_example_case_cursor_after_tick() {
183 check_assist( 186 check_assist(
184 change_lifetime_anon_to_named, 187 introduce_named_lifetime,
185 r#"impl Cursor<'<|>_> {"#, 188 r#"impl Cursor<'<|>_> {"#,
186 r#"impl<'a> Cursor<'a> {"#, 189 r#"impl<'a> Cursor<'a> {"#,
187 ); 190 );
@@ -190,7 +193,7 @@ mod tests {
190 #[test] 193 #[test]
191 fn test_example_case_cursor_before_tick() { 194 fn test_example_case_cursor_before_tick() {
192 check_assist( 195 check_assist(
193 change_lifetime_anon_to_named, 196 introduce_named_lifetime,
194 r#"impl Cursor<<|>'_> {"#, 197 r#"impl Cursor<<|>'_> {"#,
195 r#"impl<'a> Cursor<'a> {"#, 198 r#"impl<'a> Cursor<'a> {"#,
196 ); 199 );
@@ -198,23 +201,20 @@ mod tests {
198 201
199 #[test] 202 #[test]
200 fn test_not_applicable_cursor_position() { 203 fn test_not_applicable_cursor_position() {
201 check_assist_not_applicable(change_lifetime_anon_to_named, r#"impl Cursor<'_><|> {"#); 204 check_assist_not_applicable(introduce_named_lifetime, r#"impl Cursor<'_><|> {"#);
202 check_assist_not_applicable(change_lifetime_anon_to_named, r#"impl Cursor<|><'_> {"#); 205 check_assist_not_applicable(introduce_named_lifetime, r#"impl Cursor<|><'_> {"#);
203 } 206 }
204 207
205 #[test] 208 #[test]
206 fn test_not_applicable_lifetime_already_name() { 209 fn test_not_applicable_lifetime_already_name() {
207 check_assist_not_applicable(change_lifetime_anon_to_named, r#"impl Cursor<'a<|>> {"#); 210 check_assist_not_applicable(introduce_named_lifetime, r#"impl Cursor<'a<|>> {"#);
208 check_assist_not_applicable( 211 check_assist_not_applicable(introduce_named_lifetime, r#"fn my_fun<'a>() -> X<'a<|>>"#);
209 change_lifetime_anon_to_named,
210 r#"fn my_fun<'a>() -> X<'a<|>>"#,
211 );
212 } 212 }
213 213
214 #[test] 214 #[test]
215 fn test_with_type_parameter() { 215 fn test_with_type_parameter() {
216 check_assist( 216 check_assist(
217 change_lifetime_anon_to_named, 217 introduce_named_lifetime,
218 r#"impl<T> Cursor<T, '_<|>>"#, 218 r#"impl<T> Cursor<T, '_<|>>"#,
219 r#"impl<T, 'a> Cursor<T, 'a>"#, 219 r#"impl<T, 'a> Cursor<T, 'a>"#,
220 ); 220 );
@@ -223,7 +223,7 @@ mod tests {
223 #[test] 223 #[test]
224 fn test_with_existing_lifetime_name_conflict() { 224 fn test_with_existing_lifetime_name_conflict() {
225 check_assist( 225 check_assist(
226 change_lifetime_anon_to_named, 226 introduce_named_lifetime,
227 r#"impl<'a, 'b> Cursor<'a, 'b, '_<|>>"#, 227 r#"impl<'a, 'b> Cursor<'a, 'b, '_<|>>"#,
228 r#"impl<'a, 'b, 'c> Cursor<'a, 'b, 'c>"#, 228 r#"impl<'a, 'b, 'c> Cursor<'a, 'b, 'c>"#,
229 ); 229 );
@@ -232,7 +232,7 @@ mod tests {
232 #[test] 232 #[test]
233 fn test_function_return_value_anon_lifetime_param() { 233 fn test_function_return_value_anon_lifetime_param() {
234 check_assist( 234 check_assist(
235 change_lifetime_anon_to_named, 235 introduce_named_lifetime,
236 r#"fn my_fun() -> X<'_<|>>"#, 236 r#"fn my_fun() -> X<'_<|>>"#,
237 r#"fn my_fun<'a>() -> X<'a>"#, 237 r#"fn my_fun<'a>() -> X<'a>"#,
238 ); 238 );
@@ -241,7 +241,7 @@ mod tests {
241 #[test] 241 #[test]
242 fn test_function_return_value_anon_reference_lifetime() { 242 fn test_function_return_value_anon_reference_lifetime() {
243 check_assist( 243 check_assist(
244 change_lifetime_anon_to_named, 244 introduce_named_lifetime,
245 r#"fn my_fun() -> &'_<|> X"#, 245 r#"fn my_fun() -> &'_<|> X"#,
246 r#"fn my_fun<'a>() -> &'a X"#, 246 r#"fn my_fun<'a>() -> &'a X"#,
247 ); 247 );
@@ -250,7 +250,7 @@ mod tests {
250 #[test] 250 #[test]
251 fn test_function_param_anon_lifetime() { 251 fn test_function_param_anon_lifetime() {
252 check_assist( 252 check_assist(
253 change_lifetime_anon_to_named, 253 introduce_named_lifetime,
254 r#"fn my_fun(x: X<'_<|>>)"#, 254 r#"fn my_fun(x: X<'_<|>>)"#,
255 r#"fn my_fun<'a>(x: X<'a>)"#, 255 r#"fn my_fun<'a>(x: X<'a>)"#,
256 ); 256 );
@@ -259,7 +259,7 @@ mod tests {
259 #[test] 259 #[test]
260 fn test_function_add_lifetime_to_params() { 260 fn test_function_add_lifetime_to_params() {
261 check_assist( 261 check_assist(
262 change_lifetime_anon_to_named, 262 introduce_named_lifetime,
263 r#"fn my_fun(f: &Foo) -> X<'_<|>>"#, 263 r#"fn my_fun(f: &Foo) -> X<'_<|>>"#,
264 r#"fn my_fun<'a>(f: &'a Foo) -> X<'a>"#, 264 r#"fn my_fun<'a>(f: &'a Foo) -> X<'a>"#,
265 ); 265 );
@@ -268,7 +268,7 @@ mod tests {
268 #[test] 268 #[test]
269 fn test_function_add_lifetime_to_params_in_presence_of_other_lifetime() { 269 fn test_function_add_lifetime_to_params_in_presence_of_other_lifetime() {
270 check_assist( 270 check_assist(
271 change_lifetime_anon_to_named, 271 introduce_named_lifetime,
272 r#"fn my_fun<'other>(f: &Foo, b: &'other Bar) -> X<'_<|>>"#, 272 r#"fn my_fun<'other>(f: &Foo, b: &'other Bar) -> X<'_<|>>"#,
273 r#"fn my_fun<'other, 'a>(f: &'a Foo, b: &'other Bar) -> X<'a>"#, 273 r#"fn my_fun<'other, 'a>(f: &'a Foo, b: &'other Bar) -> X<'a>"#,
274 ); 274 );
@@ -278,7 +278,7 @@ mod tests {
278 fn test_function_not_applicable_without_self_and_multiple_unnamed_param_lifetimes() { 278 fn test_function_not_applicable_without_self_and_multiple_unnamed_param_lifetimes() {
279 // this is not permitted under lifetime elision rules 279 // this is not permitted under lifetime elision rules
280 check_assist_not_applicable( 280 check_assist_not_applicable(
281 change_lifetime_anon_to_named, 281 introduce_named_lifetime,
282 r#"fn my_fun(f: &Foo, b: &Bar) -> X<'_<|>>"#, 282 r#"fn my_fun(f: &Foo, b: &Bar) -> X<'_<|>>"#,
283 ); 283 );
284 } 284 }
@@ -286,7 +286,7 @@ mod tests {
286 #[test] 286 #[test]
287 fn test_function_add_lifetime_to_self_ref_param() { 287 fn test_function_add_lifetime_to_self_ref_param() {
288 check_assist( 288 check_assist(
289 change_lifetime_anon_to_named, 289 introduce_named_lifetime,
290 r#"fn my_fun<'other>(&self, f: &Foo, b: &'other Bar) -> X<'_<|>>"#, 290 r#"fn my_fun<'other>(&self, f: &Foo, b: &'other Bar) -> X<'_<|>>"#,
291 r#"fn my_fun<'other, 'a>(&'a self, f: &Foo, b: &'other Bar) -> X<'a>"#, 291 r#"fn my_fun<'other, 'a>(&'a self, f: &Foo, b: &'other Bar) -> X<'a>"#,
292 ); 292 );
@@ -295,7 +295,7 @@ mod tests {
295 #[test] 295 #[test]
296 fn test_function_add_lifetime_to_param_with_non_ref_self() { 296 fn test_function_add_lifetime_to_param_with_non_ref_self() {
297 check_assist( 297 check_assist(
298 change_lifetime_anon_to_named, 298 introduce_named_lifetime,
299 r#"fn my_fun<'other>(self, f: &Foo, b: &'other Bar) -> X<'_<|>>"#, 299 r#"fn my_fun<'other>(self, f: &Foo, b: &'other Bar) -> X<'_<|>>"#,
300 r#"fn my_fun<'other, 'a>(self, f: &'a Foo, b: &'other Bar) -> X<'a>"#, 300 r#"fn my_fun<'other, 'a>(self, f: &'a Foo, b: &'other Bar) -> X<'a>"#,
301 ); 301 );
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index 3f8f7ffbf..fb5d59a87 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -112,7 +112,6 @@ mod handlers {
112 mod add_turbo_fish; 112 mod add_turbo_fish;
113 mod apply_demorgan; 113 mod apply_demorgan;
114 mod auto_import; 114 mod auto_import;
115 mod change_lifetime_anon_to_named;
116 mod change_return_type_to_result; 115 mod change_return_type_to_result;
117 mod change_visibility; 116 mod change_visibility;
118 mod early_return; 117 mod early_return;
@@ -122,6 +121,7 @@ mod handlers {
122 mod flip_comma; 121 mod flip_comma;
123 mod flip_trait_bound; 122 mod flip_trait_bound;
124 mod inline_local_variable; 123 mod inline_local_variable;
124 mod introduce_named_lifetime;
125 mod introduce_variable; 125 mod introduce_variable;
126 mod invert_if; 126 mod invert_if;
127 mod merge_imports; 127 mod merge_imports;
@@ -152,7 +152,6 @@ mod handlers {
152 add_turbo_fish::add_turbo_fish, 152 add_turbo_fish::add_turbo_fish,
153 apply_demorgan::apply_demorgan, 153 apply_demorgan::apply_demorgan,
154 auto_import::auto_import, 154 auto_import::auto_import,
155 change_lifetime_anon_to_named::change_lifetime_anon_to_named,
156 change_return_type_to_result::change_return_type_to_result, 155 change_return_type_to_result::change_return_type_to_result,
157 change_visibility::change_visibility, 156 change_visibility::change_visibility,
158 early_return::convert_to_guarded_return, 157 early_return::convert_to_guarded_return,
@@ -162,6 +161,7 @@ mod handlers {
162 flip_comma::flip_comma, 161 flip_comma::flip_comma,
163 flip_trait_bound::flip_trait_bound, 162 flip_trait_bound::flip_trait_bound,
164 inline_local_variable::inline_local_variable, 163 inline_local_variable::inline_local_variable,
164 introduce_named_lifetime::introduce_named_lifetime,
165 introduce_variable::introduce_variable, 165 introduce_variable::introduce_variable,
166 invert_if::invert_if, 166 invert_if::invert_if,
167 merge_imports::merge_imports, 167 merge_imports::merge_imports,
diff --git a/crates/ra_assists/src/tests/generated.rs b/crates/ra_assists/src/tests/generated.rs
index abffbf97c..d17504529 100644
--- a/crates/ra_assists/src/tests/generated.rs
+++ b/crates/ra_assists/src/tests/generated.rs
@@ -59,6 +59,25 @@ fn main() {
59} 59}
60 60
61#[test] 61#[test]
62fn doctest_add_from_impl_for_enum() {
63 check_doc_test(
64 "add_from_impl_for_enum",
65 r#####"
66enum A { <|>One(u32) }
67"#####,
68 r#####"
69enum A { One(u32) }
70
71impl From<u32> for A {
72 fn from(v: u32) -> Self {
73 A::One(v)
74 }
75}
76"#####,
77 )
78}
79
80#[test]
62fn doctest_add_function() { 81fn doctest_add_function() {
63 check_doc_test( 82 check_doc_test(
64 "add_function", 83 "add_function",
@@ -269,31 +288,6 @@ pub mod std { pub mod collections { pub struct HashMap { } } }
269} 288}
270 289
271#[test] 290#[test]
272fn doctest_change_lifetime_anon_to_named() {
273 check_doc_test(
274 "change_lifetime_anon_to_named",
275 r#####"
276impl Cursor<'_<|>> {
277 fn node(self) -> &SyntaxNode {
278 match self {
279 Cursor::Replace(node) | Cursor::Before(node) => node,
280 }
281 }
282}
283"#####,
284 r#####"
285impl<'a> Cursor<'a> {
286 fn node(self) -> &SyntaxNode {
287 match self {
288 Cursor::Replace(node) | Cursor::Before(node) => node,
289 }
290 }
291}
292"#####,
293 )
294}
295
296#[test]
297fn doctest_change_return_type_to_result() { 291fn doctest_change_return_type_to_result() {
298 check_doc_test( 292 check_doc_test(
299 "change_return_type_to_result", 293 "change_return_type_to_result",
@@ -458,6 +452,31 @@ fn main() {
458} 452}
459 453
460#[test] 454#[test]
455fn doctest_introduce_named_lifetime() {
456 check_doc_test(
457 "introduce_named_lifetime",
458 r#####"
459impl Cursor<'_<|>> {
460 fn node(self) -> &SyntaxNode {
461 match self {
462 Cursor::Replace(node) | Cursor::Before(node) => node,
463 }
464 }
465}
466"#####,
467 r#####"
468impl<'a> Cursor<'a> {
469 fn node(self) -> &SyntaxNode {
470 match self {
471 Cursor::Replace(node) | Cursor::Before(node) => node,
472 }
473 }
474}
475"#####,
476 )
477}
478
479#[test]
461fn doctest_introduce_variable() { 480fn doctest_introduce_variable() {
462 check_doc_test( 481 check_doc_test(
463 "introduce_variable", 482 "introduce_variable",
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_ide/src/completion.rs b/crates/ra_ide/src/completion.rs
index 191300704..d890b69d2 100644
--- a/crates/ra_ide/src/completion.rs
+++ b/crates/ra_ide/src/completion.rs
@@ -1,5 +1,3 @@
1//! FIXME: write short doc here
2
3mod completion_config; 1mod completion_config;
4mod completion_item; 2mod completion_item;
5mod completion_context; 3mod completion_context;
@@ -35,6 +33,51 @@ pub use crate::completion::{
35 completion_item::{CompletionItem, CompletionItemKind, CompletionScore, InsertTextFormat}, 33 completion_item::{CompletionItem, CompletionItemKind, CompletionScore, InsertTextFormat},
36}; 34};
37 35
36//FIXME: split the following feature into fine-grained features.
37
38// Feature: Magic Completions
39//
40// In addition to usual reference completion, rust-analyzer provides some ✨magic✨
41// completions as well:
42//
43// Keywords like `if`, `else` `while`, `loop` are completed with braces, and cursor
44// is placed at the appropriate position. Even though `if` is easy to type, you
45// still want to complete it, to get ` { }` for free! `return` is inserted with a
46// space or `;` depending on the return type of the function.
47//
48// When completing a function call, `()` are automatically inserted. If a function
49// takes arguments, the cursor is positioned inside the parenthesis.
50//
51// There are postfix completions, which can be triggered by typing something like
52// `foo().if`. The word after `.` determines postfix completion. Possible variants are:
53//
54// - `expr.if` -> `if expr {}` or `if let ... {}` for `Option` or `Result`
55// - `expr.match` -> `match expr {}`
56// - `expr.while` -> `while expr {}` or `while let ... {}` for `Option` or `Result`
57// - `expr.ref` -> `&expr`
58// - `expr.refm` -> `&mut expr`
59// - `expr.not` -> `!expr`
60// - `expr.dbg` -> `dbg!(expr)`
61//
62// There also snippet completions:
63//
64// .Expressions
65// - `pd` -> `println!("{:?}")`
66// - `ppd` -> `println!("{:#?}")`
67//
68// .Items
69// - `tfn` -> `#[test] fn f(){}`
70// - `tmod` ->
71// ```rust
72// #[cfg(test)]
73// mod tests {
74// use super::*;
75//
76// #[test]
77// fn test_fn() {}
78// }
79// ```
80
38/// Main entry point for completion. We run completion as a two-phase process. 81/// Main entry point for completion. We run completion as a two-phase process.
39/// 82///
40/// First, we look at the position and collect a so-called `CompletionContext. 83/// First, we look at the position and collect a so-called `CompletionContext.
diff --git a/crates/ra_ide/src/completion/complete_postfix.rs b/crates/ra_ide/src/completion/complete_postfix.rs
index 02e660ca8..59b58bf98 100644
--- a/crates/ra_ide/src/completion/complete_postfix.rs
+++ b/crates/ra_ide/src/completion/complete_postfix.rs
@@ -1,12 +1,11 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2use ra_assists::utils::TryEnum;
3use ra_syntax::{ 3use ra_syntax::{
4 ast::{self, AstNode}, 4 ast::{self, AstNode},
5 TextRange, TextSize, 5 TextRange, TextSize,
6}; 6};
7use ra_text_edit::TextEdit; 7use ra_text_edit::TextEdit;
8 8
9use super::completion_config::SnippetCap;
10use crate::{ 9use crate::{
11 completion::{ 10 completion::{
12 completion_context::CompletionContext, 11 completion_context::CompletionContext,
@@ -14,7 +13,8 @@ use crate::{
14 }, 13 },
15 CompletionItem, 14 CompletionItem,
16}; 15};
17use ra_assists::utils::TryEnum; 16
17use super::completion_config::SnippetCap;
18 18
19pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { 19pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
20 if !ctx.config.enable_postfix_completions { 20 if !ctx.config.enable_postfix_completions {
diff --git a/crates/ra_ide/src/display/structure.rs b/crates/ra_ide/src/display/structure.rs
index 967eee5d2..aad5a8e4d 100644
--- a/crates/ra_ide/src/display/structure.rs
+++ b/crates/ra_ide/src/display/structure.rs
@@ -1,10 +1,6 @@
1//! FIXME: write short doc here
2
3use crate::TextRange;
4
5use ra_syntax::{ 1use ra_syntax::{
6 ast::{self, AttrsOwner, NameOwner, TypeAscriptionOwner, TypeParamsOwner}, 2 ast::{self, AttrsOwner, NameOwner, TypeAscriptionOwner, TypeParamsOwner},
7 match_ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, WalkEvent, 3 match_ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, TextRange, WalkEvent,
8}; 4};
9 5
10#[derive(Debug, Clone)] 6#[derive(Debug, Clone)]
@@ -18,6 +14,19 @@ pub struct StructureNode {
18 pub deprecated: bool, 14 pub deprecated: bool,
19} 15}
20 16
17// Feature: File Structure
18//
19// Provides a tree of the symbols defined in the file. Can be used to
20//
21// * fuzzy search symbol in a file (super useful)
22// * draw breadcrumbs to describe the context around the cursor
23// * draw outline of the file
24//
25// |===
26// | Editor | Shortcut
27//
28// | VS Code | kbd:[Ctrl+Shift+O]
29// |===
21pub fn file_structure(file: &SourceFile) -> Vec<StructureNode> { 30pub fn file_structure(file: &SourceFile) -> Vec<StructureNode> {
22 let mut res = Vec::new(); 31 let mut res = Vec::new();
23 let mut stack = Vec::new(); 32 let mut stack = Vec::new();
diff --git a/crates/ra_ide/src/expand_macro.rs b/crates/ra_ide/src/expand_macro.rs
index f536ba3e7..54a47aac0 100644
--- a/crates/ra_ide/src/expand_macro.rs
+++ b/crates/ra_ide/src/expand_macro.rs
@@ -1,5 +1,3 @@
1//! This modules implements "expand macro" functionality in the IDE
2
3use hir::Semantics; 1use hir::Semantics;
4use ra_ide_db::RootDatabase; 2use ra_ide_db::RootDatabase;
5use ra_syntax::{ 3use ra_syntax::{
@@ -14,6 +12,15 @@ pub struct ExpandedMacro {
14 pub expansion: String, 12 pub expansion: String,
15} 13}
16 14
15// Feature: Expand Macro Recursively
16//
17// Shows the full macro expansion of the macro at current cursor.
18//
19// |===
20// | Editor | Action Name
21//
22// | VS Code | **Rust Analyzer: Expand macro recursively**
23// |===
17pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<ExpandedMacro> { 24pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<ExpandedMacro> {
18 let sema = Semantics::new(db); 25 let sema = Semantics::new(db);
19 let file = sema.parse(position.file_id); 26 let file = sema.parse(position.file_id);
diff --git a/crates/ra_ide/src/extend_selection.rs b/crates/ra_ide/src/extend_selection.rs
index 554594a43..a4bc93cdb 100644
--- a/crates/ra_ide/src/extend_selection.rs
+++ b/crates/ra_ide/src/extend_selection.rs
@@ -1,5 +1,3 @@
1//! FIXME: write short doc here
2
3use std::iter::successors; 1use std::iter::successors;
4 2
5use hir::Semantics; 3use hir::Semantics;
@@ -14,6 +12,16 @@ use ra_syntax::{
14 12
15use crate::FileRange; 13use crate::FileRange;
16 14
15// Feature: Extend Selection
16//
17// Extends the current selection to the encompassing syntactic construct
18// (expression, statement, item, module, etc). It works with multiple cursors.
19//
20// |===
21// | Editor | Shortcut
22//
23// | VS Code | kbd:[Ctrl+Shift+→]
24// |===
17pub(crate) fn extend_selection(db: &RootDatabase, frange: FileRange) -> TextRange { 25pub(crate) fn extend_selection(db: &RootDatabase, frange: FileRange) -> TextRange {
18 let sema = Semantics::new(db); 26 let sema = Semantics::new(db);
19 let src = sema.parse(frange.file_id); 27 let src = sema.parse(frange.file_id);
diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs
index 90e85d419..a6c86e99c 100644
--- a/crates/ra_ide/src/goto_definition.rs
+++ b/crates/ra_ide/src/goto_definition.rs
@@ -1,5 +1,3 @@
1//! FIXME: write short doc here
2
3use hir::Semantics; 1use hir::Semantics;
4use ra_ide_db::{ 2use ra_ide_db::{
5 defs::{classify_name, classify_name_ref}, 3 defs::{classify_name, classify_name_ref},
@@ -17,6 +15,15 @@ use crate::{
17 FilePosition, NavigationTarget, RangeInfo, 15 FilePosition, NavigationTarget, RangeInfo,
18}; 16};
19 17
18// Feature: Go to Definition
19//
20// Navigates to the definition of an identifier.
21//
22// |===
23// | Editor | Shortcut
24//
25// | VS Code | kbd:[F12]
26// |===
20pub(crate) fn goto_definition( 27pub(crate) fn goto_definition(
21 db: &RootDatabase, 28 db: &RootDatabase,
22 position: FilePosition, 29 position: FilePosition,
diff --git a/crates/ra_ide/src/impls.rs b/crates/ra_ide/src/goto_implementation.rs
index ea2225f70..0cec0657e 100644
--- a/crates/ra_ide/src/impls.rs
+++ b/crates/ra_ide/src/goto_implementation.rs
@@ -1,11 +1,18 @@
1//! FIXME: write short doc here
2
3use hir::{Crate, ImplDef, Semantics}; 1use hir::{Crate, ImplDef, Semantics};
4use ra_ide_db::RootDatabase; 2use ra_ide_db::RootDatabase;
5use ra_syntax::{algo::find_node_at_offset, ast, AstNode}; 3use ra_syntax::{algo::find_node_at_offset, ast, AstNode};
6 4
7use crate::{display::ToNav, FilePosition, NavigationTarget, RangeInfo}; 5use crate::{display::ToNav, FilePosition, NavigationTarget, RangeInfo};
8 6
7// Feature: Go to Implementation
8//
9// Navigates to the impl block of structs, enums or traits. Also implemented as a code lens.
10//
11// |===
12// | Editor | Shortcut
13//
14// | VS Code | kbd:[Ctrl+F12]
15// |===
9pub(crate) fn goto_implementation( 16pub(crate) fn goto_implementation(
10 db: &RootDatabase, 17 db: &RootDatabase,
11 position: FilePosition, 18 position: FilePosition,
diff --git a/crates/ra_ide/src/goto_type_definition.rs b/crates/ra_ide/src/goto_type_definition.rs
index a84637489..91a3097fb 100644
--- a/crates/ra_ide/src/goto_type_definition.rs
+++ b/crates/ra_ide/src/goto_type_definition.rs
@@ -1,10 +1,17 @@
1//! FIXME: write short doc here
2
3use ra_ide_db::RootDatabase; 1use ra_ide_db::RootDatabase;
4use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset}; 2use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset};
5 3
6use crate::{display::ToNav, FilePosition, NavigationTarget, RangeInfo}; 4use crate::{display::ToNav, FilePosition, NavigationTarget, RangeInfo};
7 5
6// Feature: Go to Type Definition
7//
8// Navigates to the type of an identifier.
9//
10// |===
11// | Editor | Action Name
12//
13// | VS Code | **Go to Type Definition*
14// |===
8pub(crate) fn goto_type_definition( 15pub(crate) fn goto_type_definition(
9 db: &RootDatabase, 16 db: &RootDatabase,
10 position: FilePosition, 17 position: FilePosition,
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs
index 3e721dcca..d96cb5596 100644
--- a/crates/ra_ide/src/hover.rs
+++ b/crates/ra_ide/src/hover.rs
@@ -1,10 +1,10 @@
1//! Logic for computing info that is displayed when the user hovers over any 1use std::iter::once;
2//! source code items (e.g. function call, struct field, variable symbol...)
3 2
4use hir::{ 3use hir::{
5 Adt, AsAssocItem, AssocItemContainer, FieldSource, HasSource, HirDisplay, ModuleDef, 4 Adt, AsAssocItem, AssocItemContainer, FieldSource, HasSource, HirDisplay, ModuleDef,
6 ModuleSource, Semantics, 5 ModuleSource, Semantics,
7}; 6};
7use itertools::Itertools;
8use ra_db::SourceDatabase; 8use ra_db::SourceDatabase;
9use ra_ide_db::{ 9use ra_ide_db::{
10 defs::{classify_name, classify_name_ref, Definition}, 10 defs::{classify_name, classify_name_ref, Definition},
@@ -21,8 +21,6 @@ use crate::{
21 display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel}, 21 display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel},
22 FilePosition, RangeInfo, 22 FilePosition, RangeInfo,
23}; 23};
24use itertools::Itertools;
25use std::iter::once;
26 24
27/// Contains the results when hovering over an item 25/// Contains the results when hovering over an item
28#[derive(Debug, Default)] 26#[derive(Debug, Default)]
@@ -62,6 +60,63 @@ impl HoverResult {
62 } 60 }
63} 61}
64 62
63// Feature: Hover
64//
65// Shows additional information, like type of an expression or documentation for definition when "focusing" code.
66// Focusing is usually hovering with a mouse, but can also be triggered with a shortcut.
67pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<HoverResult>> {
68 let sema = Semantics::new(db);
69 let file = sema.parse(position.file_id).syntax().clone();
70 let token = pick_best(file.token_at_offset(position.offset))?;
71 let token = sema.descend_into_macros(token);
72
73 let mut res = HoverResult::new();
74
75 if let Some((node, name_kind)) = match_ast! {
76 match (token.parent()) {
77 ast::NameRef(name_ref) => {
78 classify_name_ref(&sema, &name_ref).map(|d| (name_ref.syntax().clone(), d.definition()))
79 },
80 ast::Name(name) => {
81 classify_name(&sema, &name).map(|d| (name.syntax().clone(), d.definition()))
82 },
83 _ => None,
84 }
85 } {
86 let range = sema.original_range(&node).range;
87 res.extend(hover_text_from_name_kind(db, name_kind));
88
89 if !res.is_empty() {
90 return Some(RangeInfo::new(range, res));
91 }
92 }
93
94 let node = token
95 .ancestors()
96 .find(|n| ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some())?;
97
98 let ty = match_ast! {
99 match node {
100 ast::MacroCall(_it) => {
101 // If this node is a MACRO_CALL, it means that `descend_into_macros` failed to resolve.
102 // (e.g expanding a builtin macro). So we give up here.
103 return None;
104 },
105 ast::Expr(it) => {
106 sema.type_of_expr(&it)
107 },
108 ast::Pat(it) => {
109 sema.type_of_pat(&it)
110 },
111 _ => None,
112 }
113 }?;
114
115 res.extend(Some(rust_code_markup(&ty.display(db))));
116 let range = sema.original_range(&node).range;
117 Some(RangeInfo::new(range, res))
118}
119
65fn hover_text( 120fn hover_text(
66 docs: Option<String>, 121 docs: Option<String>,
67 desc: Option<String>, 122 desc: Option<String>,
@@ -160,59 +215,6 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<Strin
160 } 215 }
161} 216}
162 217
163pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<HoverResult>> {
164 let sema = Semantics::new(db);
165 let file = sema.parse(position.file_id).syntax().clone();
166 let token = pick_best(file.token_at_offset(position.offset))?;
167 let token = sema.descend_into_macros(token);
168
169 let mut res = HoverResult::new();
170
171 if let Some((node, name_kind)) = match_ast! {
172 match (token.parent()) {
173 ast::NameRef(name_ref) => {
174 classify_name_ref(&sema, &name_ref).map(|d| (name_ref.syntax().clone(), d.definition()))
175 },
176 ast::Name(name) => {
177 classify_name(&sema, &name).map(|d| (name.syntax().clone(), d.definition()))
178 },
179 _ => None,
180 }
181 } {
182 let range = sema.original_range(&node).range;
183 res.extend(hover_text_from_name_kind(db, name_kind));
184
185 if !res.is_empty() {
186 return Some(RangeInfo::new(range, res));
187 }
188 }
189
190 let node = token
191 .ancestors()
192 .find(|n| ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some())?;
193
194 let ty = match_ast! {
195 match node {
196 ast::MacroCall(_it) => {
197 // If this node is a MACRO_CALL, it means that `descend_into_macros` failed to resolve.
198 // (e.g expanding a builtin macro). So we give up here.
199 return None;
200 },
201 ast::Expr(it) => {
202 sema.type_of_expr(&it)
203 },
204 ast::Pat(it) => {
205 sema.type_of_pat(&it)
206 },
207 _ => None,
208 }
209 }?;
210
211 res.extend(Some(rust_code_markup(&ty.display(db))));
212 let range = sema.original_range(&node).range;
213 Some(RangeInfo::new(range, res))
214}
215
216fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { 218fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
217 return tokens.max_by_key(priority); 219 return tokens.max_by_key(priority);
218 fn priority(n: &SyntaxToken) -> usize { 220 fn priority(n: &SyntaxToken) -> usize {
diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs
index b391f903a..75bd3c96b 100644
--- a/crates/ra_ide/src/inlay_hints.rs
+++ b/crates/ra_ide/src/inlay_hints.rs
@@ -1,5 +1,3 @@
1//! This module defines multiple types of inlay hints and their visibility
2
3use hir::{Adt, HirDisplay, Semantics, Type}; 1use hir::{Adt, HirDisplay, Semantics, Type};
4use ra_ide_db::RootDatabase; 2use ra_ide_db::RootDatabase;
5use ra_prof::profile; 3use ra_prof::profile;
@@ -39,6 +37,26 @@ pub struct InlayHint {
39 pub label: SmolStr, 37 pub label: SmolStr,
40} 38}
41 39
40// Feature: Inlay Hints
41//
42// rust-analyzer shows additional information inline with the source code.
43// Editors usually render this using read-only virtual text snippets interspersed with code.
44//
45// rust-analyzer shows hits for
46//
47// * types of local variables
48// * names of function arguments
49// * types of chained expressions
50//
51// **Note:** VS Code does not have native support for inlay hints https://github.com/microsoft/vscode/issues/16221[yet] and the hints are implemented using decorations.
52// This approach has limitations, the caret movement and bracket highlighting near the edges of the hint may be weird:
53// https://github.com/rust-analyzer/rust-analyzer/issues/1623[1], https://github.com/rust-analyzer/rust-analyzer/issues/3453[2].
54//
55// |===
56// | Editor | Action Name
57//
58// | VS Code | **Rust Analyzer: Toggle inlay hints*
59// |===
42pub(crate) fn inlay_hints( 60pub(crate) fn inlay_hints(
43 db: &RootDatabase, 61 db: &RootDatabase,
44 file_id: FileId, 62 file_id: FileId,
diff --git a/crates/ra_ide/src/join_lines.rs b/crates/ra_ide/src/join_lines.rs
index af1ade8a1..5036c1fb0 100644
--- a/crates/ra_ide/src/join_lines.rs
+++ b/crates/ra_ide/src/join_lines.rs
@@ -1,5 +1,3 @@
1//! FIXME: write short doc here
2
3use itertools::Itertools; 1use itertools::Itertools;
4use ra_fmt::{compute_ws, extract_trivial_expression}; 2use ra_fmt::{compute_ws, extract_trivial_expression};
5use ra_syntax::{ 3use ra_syntax::{
@@ -11,6 +9,15 @@ use ra_syntax::{
11}; 9};
12use ra_text_edit::{TextEdit, TextEditBuilder}; 10use ra_text_edit::{TextEdit, TextEditBuilder};
13 11
12// Feature: Join Lines
13//
14// Join selected lines into one, smartly fixing up whitespace, trailing commas, and braces.
15//
16// |===
17// | Editor | Action Name
18//
19// | VS Code | **Rust Analyzer: Join lines**
20// |===
14pub fn join_lines(file: &SourceFile, range: TextRange) -> TextEdit { 21pub fn join_lines(file: &SourceFile, range: TextRange) -> TextEdit {
15 let range = if range.is_empty() { 22 let range = if range.is_empty() {
16 let syntax = file.syntax(); 23 let syntax = file.syntax();
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs
index d983cd910..12d5716e8 100644
--- a/crates/ra_ide/src/lib.rs
+++ b/crates/ra_ide/src/lib.rs
@@ -23,6 +23,7 @@ mod completion;
23mod runnables; 23mod runnables;
24mod goto_definition; 24mod goto_definition;
25mod goto_type_definition; 25mod goto_type_definition;
26mod goto_implementation;
26mod extend_selection; 27mod extend_selection;
27mod hover; 28mod hover;
28mod call_hierarchy; 29mod call_hierarchy;
@@ -30,7 +31,6 @@ mod call_info;
30mod syntax_highlighting; 31mod syntax_highlighting;
31mod parent_module; 32mod parent_module;
32mod references; 33mod references;
33mod impls;
34mod diagnostics; 34mod diagnostics;
35mod syntax_tree; 35mod syntax_tree;
36mod folding_ranges; 36mod folding_ranges;
@@ -373,7 +373,7 @@ impl Analysis {
373 &self, 373 &self,
374 position: FilePosition, 374 position: FilePosition,
375 ) -> Cancelable<Option<RangeInfo<Vec<NavigationTarget>>>> { 375 ) -> Cancelable<Option<RangeInfo<Vec<NavigationTarget>>>> {
376 self.with_db(|db| impls::goto_implementation(db, position)) 376 self.with_db(|db| goto_implementation::goto_implementation(db, position))
377 } 377 }
378 378
379 /// Returns the type definitions for the symbol at `position`. 379 /// Returns the type definitions for the symbol at `position`.
diff --git a/crates/ra_ide/src/matching_brace.rs b/crates/ra_ide/src/matching_brace.rs
index b85348706..407a9636d 100644
--- a/crates/ra_ide/src/matching_brace.rs
+++ b/crates/ra_ide/src/matching_brace.rs
@@ -1,7 +1,16 @@
1//! FIXME: write short doc here
2
3use ra_syntax::{ast::AstNode, SourceFile, SyntaxKind, TextSize, T}; 1use ra_syntax::{ast::AstNode, SourceFile, SyntaxKind, TextSize, T};
4 2
3// Feature: Matching Brace
4//
5// If the cursor is on any brace (`<>(){}[]`) which is a part of a brace-pair,
6// moves cursor to the matching brace. It uses the actual parser to determine
7// braces, so it won't confuse generics with comparisons.
8//
9// |===
10// | Editor | Action Name
11//
12// | VS Code | **Rust Analyzer: Find matching brace**
13// |===
5pub fn matching_brace(file: &SourceFile, offset: TextSize) -> Option<TextSize> { 14pub fn matching_brace(file: &SourceFile, offset: TextSize) -> Option<TextSize> {
6 const BRACES: &[SyntaxKind] = 15 const BRACES: &[SyntaxKind] =
7 &[T!['{'], T!['}'], T!['['], T![']'], T!['('], T![')'], T![<], T![>]]; 16 &[T!['{'], T!['}'], T!['['], T![']'], T!['('], T![')'], T![<], T![>]];
diff --git a/crates/ra_ide/src/parent_module.rs b/crates/ra_ide/src/parent_module.rs
index a083fb1eb..fa1535da5 100644
--- a/crates/ra_ide/src/parent_module.rs
+++ b/crates/ra_ide/src/parent_module.rs
@@ -1,5 +1,3 @@
1//! FIXME: write short doc here
2
3use hir::Semantics; 1use hir::Semantics;
4use ra_db::{CrateId, FileId, FilePosition}; 2use ra_db::{CrateId, FileId, FilePosition};
5use ra_ide_db::RootDatabase; 3use ra_ide_db::RootDatabase;
@@ -11,6 +9,16 @@ use test_utils::mark;
11 9
12use crate::NavigationTarget; 10use crate::NavigationTarget;
13 11
12// Feature: Parent Module
13//
14// Navigates to the parent module of the current module.
15//
16// |===
17// | Editor | Action Name
18//
19// | VS Code | **Rust Analyzer: Locate parent module**
20// |===
21
14/// This returns `Vec` because a module may be included from several places. We 22/// This returns `Vec` because a module may be included from several places. We
15/// don't handle this case yet though, so the Vec has length at most one. 23/// don't handle this case yet though, so the Vec has length at most one.
16pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<NavigationTarget> { 24pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<NavigationTarget> {
diff --git a/crates/ra_ide/src/references.rs b/crates/ra_ide/src/references.rs
index 96444bf6a..bb40d2043 100644
--- a/crates/ra_ide/src/references.rs
+++ b/crates/ra_ide/src/references.rs
@@ -615,6 +615,33 @@ mod tests {
615 ); 615 );
616 } 616 }
617 617
618 #[test]
619 fn test_find_all_refs_nested_module() {
620 let code = r#"
621 //- /lib.rs
622 mod foo {
623 mod bar;
624 }
625
626 fn f<|>() {}
627
628 //- /foo/bar.rs
629 use crate::f;
630
631 fn g() {
632 f();
633 }
634 "#;
635
636 let (analysis, pos) = analysis_and_position(code);
637 let refs = analysis.find_all_refs(pos, None).unwrap().unwrap();
638 check_result(
639 refs,
640 "f FN_DEF FileId(1) 25..34 28..29 Other",
641 &["FileId(2) 11..12 Other", "FileId(2) 27..28 StructLiteral"],
642 );
643 }
644
618 fn get_all_refs(text: &str) -> ReferenceSearchResult { 645 fn get_all_refs(text: &str) -> ReferenceSearchResult {
619 let (analysis, position) = single_file_with_position(text); 646 let (analysis, position) = single_file_with_position(text);
620 analysis.find_all_refs(position, None).unwrap().unwrap() 647 analysis.find_all_refs(position, None).unwrap().unwrap()
diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs
index 6e7e47199..286d45eee 100644
--- a/crates/ra_ide/src/runnables.rs
+++ b/crates/ra_ide/src/runnables.rs
@@ -1,5 +1,3 @@
1//! FIXME: write short doc here
2
3use hir::{AsAssocItem, Attrs, HirFileId, InFile, Semantics}; 1use hir::{AsAssocItem, Attrs, HirFileId, InFile, Semantics};
4use itertools::Itertools; 2use itertools::Itertools;
5use ra_ide_db::RootDatabase; 3use ra_ide_db::RootDatabase;
@@ -44,6 +42,17 @@ pub enum RunnableKind {
44 Bin, 42 Bin,
45} 43}
46 44
45// Feature: Run
46//
47// Shows a popup suggesting to run a test/benchmark/binary **at the current cursor
48// location**. Super useful for repeatedly running just a single test. Do bind this
49// to a shortcut!
50//
51// |===
52// | Editor | Action Name
53//
54// | VS Code | **Rust Analyzer: Run**
55// |===
47pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> { 56pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
48 let sema = Semantics::new(db); 57 let sema = Semantics::new(db);
49 let source_file = sema.parse(file_id); 58 let source_file = sema.parse(file_id);
diff --git a/crates/ra_ide/src/ssr.rs b/crates/ra_ide/src/ssr.rs
index 130d3b4c3..93e9aee1d 100644
--- a/crates/ra_ide/src/ssr.rs
+++ b/crates/ra_ide/src/ssr.rs
@@ -1,5 +1,3 @@
1//! structural search replace
2
3use std::{collections::HashMap, iter::once, str::FromStr}; 1use std::{collections::HashMap, iter::once, str::FromStr};
4 2
5use ra_db::{SourceDatabase, SourceDatabaseExt}; 3use ra_db::{SourceDatabase, SourceDatabaseExt};
@@ -25,6 +23,28 @@ impl std::fmt::Display for SsrError {
25 23
26impl std::error::Error for SsrError {} 24impl std::error::Error for SsrError {}
27 25
26// Feature: Structural Seach and Replace
27//
28// Search and replace with named wildcards that will match any expression.
29// The syntax for a structural search replace command is `<search_pattern> ==>> <replace_pattern>`.
30// A `$<name>:expr` placeholder in the search pattern will match any expression and `$<name>` will reference it in the replacement.
31// Available via the command `rust-analyzer.ssr`.
32//
33// ```rust
34// // Using structural search replace command [foo($a:expr, $b:expr) ==>> ($a).foo($b)]
35//
36// // BEFORE
37// String::from(foo(y + 5, z))
38//
39// // AFTER
40// String::from((y + 5).foo(z))
41// ```
42//
43// |===
44// | Editor | Action Name
45//
46// | VS Code | **Rust Analyzer: Structural Search Replace**
47// |===
28pub fn parse_search_replace( 48pub fn parse_search_replace(
29 query: &str, 49 query: &str,
30 parse_only: bool, 50 parse_only: bool,
diff --git a/crates/ra_ide/src/status.rs b/crates/ra_ide/src/status.rs
index 30eb5c995..5b7992920 100644
--- a/crates/ra_ide/src/status.rs
+++ b/crates/ra_ide/src/status.rs
@@ -1,5 +1,3 @@
1//! FIXME: write short doc here
2
3use std::{fmt, iter::FromIterator, sync::Arc}; 1use std::{fmt, iter::FromIterator, sync::Arc};
4 2
5use hir::MacroFile; 3use hir::MacroFile;
@@ -26,6 +24,15 @@ fn macro_syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats {
26 db.query(hir::db::ParseMacroQuery).entries::<SyntaxTreeStats>() 24 db.query(hir::db::ParseMacroQuery).entries::<SyntaxTreeStats>()
27} 25}
28 26
27// Feature: Status
28//
29// Shows internal statistic about memory usage of rust-analyzer.
30//
31// |===
32// | Editor | Action Name
33//
34// | VS Code | **Rust Analyzer: Status**
35// |===
29pub(crate) fn status(db: &RootDatabase) -> String { 36pub(crate) fn status(db: &RootDatabase) -> String {
30 let files_stats = db.query(FileTextQuery).entries::<FilesStats>(); 37 let files_stats = db.query(FileTextQuery).entries::<FilesStats>();
31 let syntax_tree_stats = syntax_tree_stats(db); 38 let syntax_tree_stats = syntax_tree_stats(db);
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index cd6464b40..0b53ebe69 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -1,5 +1,3 @@
1//! Implements syntax highlighting.
2
3mod tags; 1mod tags;
4mod html; 2mod html;
5#[cfg(test)] 3#[cfg(test)]
@@ -32,81 +30,15 @@ pub struct HighlightedRange {
32 pub binding_hash: Option<u64>, 30 pub binding_hash: Option<u64>,
33} 31}
34 32
35#[derive(Debug)] 33// Feature: Semantic Syntax Highlighting
36struct HighlightedRangeStack { 34//
37 stack: Vec<Vec<HighlightedRange>>, 35// rust-analyzer highlights the code semantically.
38} 36// For example, `bar` in `foo::Bar` might be colored differently depending on whether `Bar` is an enum or a trait.
39 37// rust-analyzer does not specify colors directly, instead it assigns tag (like `struct`) and a set of modifiers (like `declaration`) to each token.
40/// We use a stack to implement the flattening logic for the highlighted 38// It's up to the client to map those to specific colors.
41/// syntax ranges. 39//
42impl HighlightedRangeStack { 40// The general rule is that a reference to an entity gets colored the same way as the entity itself.
43 fn new() -> Self { 41// We also give special modifier for `mut` and `&mut` local variables.
44 Self { stack: vec![Vec::new()] }
45 }
46
47 fn push(&mut self) {
48 self.stack.push(Vec::new());
49 }
50
51 /// Flattens the highlighted ranges.
52 ///
53 /// For example `#[cfg(feature = "foo")]` contains the nested ranges:
54 /// 1) parent-range: Attribute [0, 23)
55 /// 2) child-range: String [16, 21)
56 ///
57 /// The following code implements the flattening, for our example this results to:
58 /// `[Attribute [0, 16), String [16, 21), Attribute [21, 23)]`
59 fn pop(&mut self) {
60 let children = self.stack.pop().unwrap();
61 let prev = self.stack.last_mut().unwrap();
62 let needs_flattening = !children.is_empty()
63 && !prev.is_empty()
64 && prev.last().unwrap().range.contains_range(children.first().unwrap().range);
65 if !needs_flattening {
66 prev.extend(children);
67 } else {
68 let mut parent = prev.pop().unwrap();
69 for ele in children {
70 assert!(parent.range.contains_range(ele.range));
71 let mut cloned = parent.clone();
72 parent.range = TextRange::new(parent.range.start(), ele.range.start());
73 cloned.range = TextRange::new(ele.range.end(), cloned.range.end());
74 if !parent.range.is_empty() {
75 prev.push(parent);
76 }
77 prev.push(ele);
78 parent = cloned;
79 }
80 if !parent.range.is_empty() {
81 prev.push(parent);
82 }
83 }
84 }
85
86 fn add(&mut self, range: HighlightedRange) {
87 self.stack
88 .last_mut()
89 .expect("during DFS traversal, the stack must not be empty")
90 .push(range)
91 }
92
93 fn flattened(mut self) -> Vec<HighlightedRange> {
94 assert_eq!(
95 self.stack.len(),
96 1,
97 "after DFS traversal, the stack should only contain a single element"
98 );
99 let mut res = self.stack.pop().unwrap();
100 res.sort_by_key(|range| range.range.start());
101 // Check that ranges are sorted and disjoint
102 assert!(res
103 .iter()
104 .zip(res.iter().skip(1))
105 .all(|(left, right)| left.range.end() <= right.range.start()));
106 res
107 }
108}
109
110pub(crate) fn highlight( 42pub(crate) fn highlight(
111 db: &RootDatabase, 43 db: &RootDatabase,
112 file_id: FileId, 44 file_id: FileId,
@@ -291,6 +223,81 @@ pub(crate) fn highlight(
291 stack.flattened() 223 stack.flattened()
292} 224}
293 225
226#[derive(Debug)]
227struct HighlightedRangeStack {
228 stack: Vec<Vec<HighlightedRange>>,
229}
230
231/// We use a stack to implement the flattening logic for the highlighted
232/// syntax ranges.
233impl HighlightedRangeStack {
234 fn new() -> Self {
235 Self { stack: vec![Vec::new()] }
236 }
237
238 fn push(&mut self) {
239 self.stack.push(Vec::new());
240 }
241
242 /// Flattens the highlighted ranges.
243 ///
244 /// For example `#[cfg(feature = "foo")]` contains the nested ranges:
245 /// 1) parent-range: Attribute [0, 23)
246 /// 2) child-range: String [16, 21)
247 ///
248 /// The following code implements the flattening, for our example this results to:
249 /// `[Attribute [0, 16), String [16, 21), Attribute [21, 23)]`
250 fn pop(&mut self) {
251 let children = self.stack.pop().unwrap();
252 let prev = self.stack.last_mut().unwrap();
253 let needs_flattening = !children.is_empty()
254 && !prev.is_empty()
255 && prev.last().unwrap().range.contains_range(children.first().unwrap().range);
256 if !needs_flattening {
257 prev.extend(children);
258 } else {
259 let mut parent = prev.pop().unwrap();
260 for ele in children {
261 assert!(parent.range.contains_range(ele.range));
262 let mut cloned = parent.clone();
263 parent.range = TextRange::new(parent.range.start(), ele.range.start());
264 cloned.range = TextRange::new(ele.range.end(), cloned.range.end());
265 if !parent.range.is_empty() {
266 prev.push(parent);
267 }
268 prev.push(ele);
269 parent = cloned;
270 }
271 if !parent.range.is_empty() {
272 prev.push(parent);
273 }
274 }
275 }
276
277 fn add(&mut self, range: HighlightedRange) {
278 self.stack
279 .last_mut()
280 .expect("during DFS traversal, the stack must not be empty")
281 .push(range)
282 }
283
284 fn flattened(mut self) -> Vec<HighlightedRange> {
285 assert_eq!(
286 self.stack.len(),
287 1,
288 "after DFS traversal, the stack should only contain a single element"
289 );
290 let mut res = self.stack.pop().unwrap();
291 res.sort_by_key(|range| range.range.start());
292 // Check that ranges are sorted and disjoint
293 assert!(res
294 .iter()
295 .zip(res.iter().skip(1))
296 .all(|(left, right)| left.range.end() <= right.range.start()));
297 res
298 }
299}
300
294fn highlight_format_specifier(kind: FormatSpecifier) -> Option<HighlightTag> { 301fn highlight_format_specifier(kind: FormatSpecifier) -> Option<HighlightTag> {
295 Some(match kind { 302 Some(match kind {
296 FormatSpecifier::Open 303 FormatSpecifier::Open
diff --git a/crates/ra_ide/src/syntax_tree.rs b/crates/ra_ide/src/syntax_tree.rs
index 86c70ff83..a341684fd 100644
--- a/crates/ra_ide/src/syntax_tree.rs
+++ b/crates/ra_ide/src/syntax_tree.rs
@@ -1,6 +1,4 @@
1//! FIXME: write short doc here 1use ra_db::{FileId, SourceDatabase};
2
3use ra_db::SourceDatabase;
4use ra_ide_db::RootDatabase; 2use ra_ide_db::RootDatabase;
5use ra_syntax::{ 3use ra_syntax::{
6 algo, AstNode, NodeOrToken, SourceFile, 4 algo, AstNode, NodeOrToken, SourceFile,
@@ -8,8 +6,16 @@ use ra_syntax::{
8 SyntaxToken, TextRange, TextSize, 6 SyntaxToken, TextRange, TextSize,
9}; 7};
10 8
11pub use ra_db::FileId; 9// Feature: Show Syntax Tree
12 10//
11// Shows the parse tree of the current file. It exists mostly for debugging
12// rust-analyzer itself.
13//
14// |===
15// | Editor | Action Name
16//
17// | VS Code | **Rust Analyzer: Show Syntax Tree**
18// |===
13pub(crate) fn syntax_tree( 19pub(crate) fn syntax_tree(
14 db: &RootDatabase, 20 db: &RootDatabase,
15 file_id: FileId, 21 file_id: FileId,
diff --git a/crates/ra_ide/src/typing.rs b/crates/ra_ide/src/typing.rs
index 39bb3b357..67e2c33a0 100644
--- a/crates/ra_ide/src/typing.rs
+++ b/crates/ra_ide/src/typing.rs
@@ -32,6 +32,13 @@ pub(crate) use on_enter::on_enter;
32 32
33pub(crate) const TRIGGER_CHARS: &str = ".=>"; 33pub(crate) const TRIGGER_CHARS: &str = ".=>";
34 34
35// Feature: On Typing Assists
36//
37// Some features trigger on typing certain characters:
38//
39// - typing `let =` tries to smartly add `;` if `=` is followed by an existing expression
40// - Enter inside comments automatically inserts `///`
41// - typing `.` in a chain method call auto-indents
35pub(crate) fn on_char_typed( 42pub(crate) fn on_char_typed(
36 db: &RootDatabase, 43 db: &RootDatabase,
37 position: FilePosition, 44 position: FilePosition,
diff --git a/crates/ra_ide_db/src/search.rs b/crates/ra_ide_db/src/search.rs
index 589f44771..335a1ad03 100644
--- a/crates/ra_ide_db/src/search.rs
+++ b/crates/ra_ide_db/src/search.rs
@@ -124,29 +124,33 @@ impl Definition {
124 124
125 let vis = self.visibility(db); 125 let vis = self.visibility(db);
126 126
127 // FIXME:
128 // The following logic are wrong that it does not search
129 // for submodules within other files recursively.
130
131 if let Some(Visibility::Module(module)) = vis.and_then(|it| it.into()) { 127 if let Some(Visibility::Module(module)) = vis.and_then(|it| it.into()) {
132 let module: Module = module.into(); 128 let module: Module = module.into();
133 let mut res = FxHashMap::default(); 129 let mut res = FxHashMap::default();
134 let src = module.definition_source(db);
135 let file_id = src.file_id.original_file(db);
136 130
137 match src.value { 131 let mut to_visit = vec![module];
138 ModuleSource::Module(m) => { 132 let mut is_first = true;
139 let range = Some(m.syntax().text_range()); 133 while let Some(module) = to_visit.pop() {
140 res.insert(file_id, range); 134 let src = module.definition_source(db);
141 } 135 let file_id = src.file_id.original_file(db);
142 ModuleSource::SourceFile(_) => { 136 match src.value {
143 res.insert(file_id, None); 137 ModuleSource::Module(m) => {
144 res.extend(module.children(db).map(|m| { 138 if is_first {
145 let src = m.definition_source(db); 139 let range = Some(m.syntax().text_range());
146 (src.file_id.original_file(db), None) 140 res.insert(file_id, range);
147 })); 141 } else {
148 } 142 // We have already added the enclosing file to the search scope,
143 // so do nothing.
144 }
145 }
146 ModuleSource::SourceFile(_) => {
147 res.insert(file_id, None);
148 }
149 };
150 is_first = false;
151 to_visit.extend(module.children(db));
149 } 152 }
153
150 return SearchScope::new(res); 154 return SearchScope::new(res);
151 } 155 }
152 156
diff --git a/crates/ra_ide_db/src/symbol_index.rs b/crates/ra_ide_db/src/symbol_index.rs
index 95be11134..acc31fe3b 100644
--- a/crates/ra_ide_db/src/symbol_index.rs
+++ b/crates/ra_ide_db/src/symbol_index.rs
@@ -110,6 +110,27 @@ fn file_symbols(db: &impl SymbolsDatabase, file_id: FileId) -> Arc<SymbolIndex>
110 Arc::new(SymbolIndex::new(symbols)) 110 Arc::new(SymbolIndex::new(symbols))
111} 111}
112 112
113// Feature: Workspace Symbol
114//
115// Uses fuzzy-search to find types, modules and functions by name across your
116// project and dependencies. This is **the** most useful feature, which improves code
117// navigation tremendously. It mostly works on top of the built-in LSP
118// functionality, however `#` and `*` symbols can be used to narrow down the
119// search. Specifically,
120//
121// - `Foo` searches for `Foo` type in the current workspace
122// - `foo#` searches for `foo` function in the current workspace
123// - `Foo*` searches for `Foo` type among dependencies, including `stdlib`
124// - `foo#*` searches for `foo` function among dependencies
125//
126// That is, `#` switches from "types" to all symbols, `*` switches from the current
127// workspace to dependencies.
128//
129// |===
130// | Editor | Shortcut
131//
132// | VS Code | kbd:[Ctrl+T]
133// |===
113pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec<FileSymbol> { 134pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec<FileSymbol> {
114 /// Need to wrap Snapshot to provide `Clone` impl for `map_with` 135 /// Need to wrap Snapshot to provide `Clone` impl for `map_with`
115 struct Snap(salsa::Snapshot<RootDatabase>); 136 struct Snap(salsa::Snapshot<RootDatabase>);
diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs
index 1876afe95..eddc807d5 100644
--- a/crates/ra_syntax/src/ast.rs
+++ b/crates/ra_syntax/src/ast.rs
@@ -75,7 +75,7 @@ impl<N> AstChildren<N> {
75impl<N: AstNode> Iterator for AstChildren<N> { 75impl<N: AstNode> Iterator for AstChildren<N> {
76 type Item = N; 76 type Item = N;
77 fn next(&mut self) -> Option<N> { 77 fn next(&mut self) -> Option<N> {
78 self.inner.by_ref().find_map(N::cast) 78 self.inner.find_map(N::cast)
79 } 79 }
80} 80}
81 81
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/crates/ra_syntax/src/syntax_node.rs b/crates/ra_syntax/src/syntax_node.rs
index e566af7e8..9650b8781 100644
--- a/crates/ra_syntax/src/syntax_node.rs
+++ b/crates/ra_syntax/src/syntax_node.rs
@@ -48,11 +48,11 @@ impl SyntaxTreeBuilder {
48 48
49 pub fn finish(self) -> Parse<SyntaxNode> { 49 pub fn finish(self) -> Parse<SyntaxNode> {
50 let (green, errors) = self.finish_raw(); 50 let (green, errors) = self.finish_raw();
51 let node = SyntaxNode::new_root(green);
52 if cfg!(debug_assertions) { 51 if cfg!(debug_assertions) {
52 let node = SyntaxNode::new_root(green.clone());
53 crate::validation::validate_block_structure(&node); 53 crate::validation::validate_block_structure(&node);
54 } 54 }
55 Parse::new(node.green().clone(), errors) 55 Parse::new(green, errors)
56 } 56 }
57 57
58 pub fn token(&mut self, kind: SyntaxKind, text: SmolStr) { 58 pub fn token(&mut self, kind: SyntaxKind, text: SmolStr) {
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md
index d06da355d..b7237ee90 100644
--- a/docs/dev/lsp-extensions.md
+++ b/docs/dev/lsp-extensions.md
@@ -7,6 +7,16 @@ All capabilities are enabled via `experimental` field of `ClientCapabilities` or
7Requests which we hope to upstream live under `experimental/` namespace. 7Requests which we hope to upstream live under `experimental/` namespace.
8Requests, which are likely to always remain specific to `rust-analyzer` are under `rust-analyzer/` namespace. 8Requests, which are likely to always remain specific to `rust-analyzer` are under `rust-analyzer/` namespace.
9 9
10If you want to be notified about the changes to this document, subscribe to [#4604](https://github.com/rust-analyzer/rust-analyzer/issues/4604).
11
12## `initializationOptions`
13
14As `initializationOptions`, `rust-analyzer` expects `"rust-analyzer"` section of the configuration.
15That is, `rust-analyzer` usually sends `"workspace/configuration"` request with `{ "items": ["rust-analyzer"] }` payload.
16`initializationOptions` should contain the same data that would be in the first item of the result.
17It's OK to not send anything, then all the settings would take their default values.
18However, some settings can not be changed after startup at the moment.
19
10## Snippet `TextEdit` 20## Snippet `TextEdit`
11 21
12**Issue:** https://github.com/microsoft/language-server-protocol/issues/724 22**Issue:** https://github.com/microsoft/language-server-protocol/issues/724
diff --git a/docs/dev/lsp-features.md b/docs/dev/lsp-features.md
deleted file mode 100644
index 00b0867d7..000000000
--- a/docs/dev/lsp-features.md
+++ /dev/null
@@ -1,72 +0,0 @@
1# Supported LSP features
2
3This list documents LSP features, supported by rust-analyzer.
4
5## General
6- [x] [initialize](https://microsoft.github.io/language-server-protocol/specification#initialize)
7- [x] [initialized](https://microsoft.github.io/language-server-protocol/specification#initialized)
8- [x] [shutdown](https://microsoft.github.io/language-server-protocol/specification#shutdown)
9- [ ] [exit](https://microsoft.github.io/language-server-protocol/specification#exit)
10- [x] [$/cancelRequest](https://microsoft.github.io/language-server-protocol/specification#cancelRequest)
11
12## Workspace
13- [ ] [workspace/workspaceFolders](https://microsoft.github.io/language-server-protocol/specification#workspace_workspaceFolders)
14- [ ] [workspace/didChangeWorkspaceFolders](https://microsoft.github.io/language-server-protocol/specification#workspace_didChangeWorkspaceFolders)
15- [x] [workspace/didChangeConfiguration](https://microsoft.github.io/language-server-protocol/specification#workspace_didChangeConfiguration)
16- [ ] [workspace/configuration](https://microsoft.github.io/language-server-protocol/specification#workspace_configuration)
17- [x] [workspace/didChangeWatchedFiles](https://microsoft.github.io/language-server-protocol/specification#workspace_didChangeWatchedFiles)
18- [x] [workspace/symbol](https://microsoft.github.io/language-server-protocol/specification#workspace_symbol)
19- [ ] [workspace/applyEdit](https://microsoft.github.io/language-server-protocol/specification#workspace_applyEdit)
20
21## Text Synchronization
22- [x] [textDocument/didOpen](https://microsoft.github.io/language-server-protocol/specification#textDocument_didOpen)
23- [x] [textDocument/didChange](https://microsoft.github.io/language-server-protocol/specification#textDocument_didChange)
24- [ ] [textDocument/willSave](https://microsoft.github.io/language-server-protocol/specification#textDocument_willSave)
25- [ ] [textDocument/willSaveWaitUntil](https://microsoft.github.io/language-server-protocol/specification#textDocument_willSaveWaitUntil)
26- [x] [textDocument/didSave](https://microsoft.github.io/language-server-protocol/specification#textDocument_didSave)
27- [x] [textDocument/didClose](https://microsoft.github.io/language-server-protocol/specification#textDocument_didClose)
28
29## Diagnostics
30- [x] [textDocument/publishDiagnostics](https://microsoft.github.io/language-server-protocol/specification#textDocument_publishDiagnostics)
31
32## Lanuguage Features
33- [x] [textDocument/completion](https://microsoft.github.io/language-server-protocol/specification#textDocument_completion)
34 - open close: false
35 - change: Full
36 - will save: false
37 - will save wait until: false
38 - save: false
39- [x] [completionItem/resolve](https://microsoft.github.io/language-server-protocol/specification#completionItem_resolve)
40 - resolve provider: none
41 - trigger characters: `:`, `.`
42- [x] [textDocument/hover](https://microsoft.github.io/language-server-protocol/specification#textDocument_hover)
43- [x] [textDocument/signatureHelp](https://microsoft.github.io/language-server-protocol/specification#textDocument_signatureHelp)
44 - trigger characters: `(`, `,`
45- [ ] [textDocument/declaration](https://microsoft.github.io/language-server-protocol/specification#textDocument_declaration)
46- [x] [textDocument/definition](https://microsoft.github.io/language-server-protocol/specification#textDocument_definition)
47- [x] [textDocument/typeDefinition](https://microsoft.github.io/language-server-protocol/specification#textDocument_typeDefinition)
48- [x] [textDocument/implementation](https://microsoft.github.io/language-server-protocol/specification#textDocument_implementation)
49- [x] [textDocument/references](https://microsoft.github.io/language-server-protocol/specification#textDocument_references)
50- [x] [textDocument/documentHighlight](https://microsoft.github.io/language-server-protocol/specification#textDocument_documentHighlight)
51- [x] [textDocument/documentSymbol](https://microsoft.github.io/language-server-protocol/specification#textDocument_documentSymbol)
52- [x] [textDocument/codeAction](https://microsoft.github.io/language-server-protocol/specification#textDocument_codeAction)
53- [x] [textDocument/selectionRange](https://github.com/Microsoft/language-server-protocol/issues/613)
54 - rust-analyzer.syntaxTree
55 - rust-analyzer.matchingBrace
56 - rust-analyzer.parentModule
57 - rust-analyzer.joinLines
58 - rust-analyzer.run
59 - rust-analyzer.analyzerStatus
60- [x] [textDocument/codeLens](https://microsoft.github.io/language-server-protocol/specification#textDocument_codeLens)
61- [x] [codeLens/resolve](https://microsoft.github.io/language-server-protocol/specification#codeLens_resolve)
62- [ ] [documentLink/resolve](https://microsoft.github.io/language-server-protocol/specification#documentLink_resolve)
63- [ ] [textDocument/documentColor](https://microsoft.github.io/language-server-protocol/specification#textDocument_documentColor)
64- [ ] [textDocument/colorPresentation](https://microsoft.github.io/language-server-protocol/specification#textDocument_colorPresentation)
65- [x] [textDocument/formatting](https://microsoft.github.io/language-server-protocol/specification#textDocument_formatting)
66- [ ] [textDocument/rangeFormatting](https://microsoft.github.io/language-server-protocol/specification#textDocument_rangeFormatting)
67- [x] [textDocument/onTypeFormatting](https://microsoft.github.io/language-server-protocol/specification#textDocument_onTypeFormatting)
68 - first trigger character: `=`
69 - more trigger character `.`
70- [x] [textDocument/rename](https://microsoft.github.io/language-server-protocol/specification#textDocument_rename)
71- [x] [textDocument/prepareRename](https://microsoft.github.io/language-server-protocol/specification#textDocument_prepareRename)
72- [x] [textDocument/foldingRange](https://microsoft.github.io/language-server-protocol/specification#textDocument_foldingRange)
diff --git a/docs/user/features.md b/docs/user/features.md
deleted file mode 100644
index 12ecdec13..000000000
--- a/docs/user/features.md
+++ /dev/null
@@ -1,218 +0,0 @@
1This document is an index of features that the rust-analyzer language server
2provides. Shortcuts are for the default VS Code layout. If there's no shortcut,
3you can use <kbd>Ctrl+Shift+P</kbd> to search for the corresponding action.
4
5### Workspace Symbol <kbd>ctrl+t</kbd>
6
7Uses fuzzy-search to find types, modules and functions by name across your
8project and dependencies. This is **the** most useful feature, which improves code
9navigation tremendously. It mostly works on top of the built-in LSP
10functionality, however `#` and `*` symbols can be used to narrow down the
11search. Specifically,
12
13- `Foo` searches for `Foo` type in the current workspace
14- `foo#` searches for `foo` function in the current workspace
15- `Foo*` searches for `Foo` type among dependencies, including `stdlib`
16- `foo#*` searches for `foo` function among dependencies
17
18That is, `#` switches from "types" to all symbols, `*` switches from the current
19workspace to dependencies.
20
21### Document Symbol <kbd>ctrl+shift+o</kbd>
22
23Provides a tree of the symbols defined in the file. Can be used to
24
25* fuzzy search symbol in a file (super useful)
26* draw breadcrumbs to describe the context around the cursor
27* draw outline of the file
28
29### On Typing Assists
30
31Some features trigger on typing certain characters:
32
33- typing `let =` tries to smartly add `;` if `=` is followed by an existing expression
34- Enter inside comments automatically inserts `///`
35- typing `.` in a chain method call auto-indents
36
37### Extend Selection
38
39Extends the current selection to the encompassing syntactic construct
40(expression, statement, item, module, etc). It works with multiple cursors. This
41is a relatively new feature of LSP:
42https://github.com/Microsoft/language-server-protocol/issues/613, check your
43editor's LSP library to see if this feature is supported.
44
45### Go to Definition
46
47Navigates to the definition of an identifier.
48
49### Go to Implementation
50
51Navigates to the impl block of structs, enums or traits. Also implemented as a code lens.
52
53### Go to Type Defintion
54
55Navigates to the type of an identifier.
56
57### Commands <kbd>ctrl+shift+p</kbd>
58
59#### Run
60
61Shows a popup suggesting to run a test/benchmark/binary **at the current cursor
62location**. Super useful for repeatedly running just a single test. Do bind this
63to a shortcut!
64
65#### Parent Module
66
67Navigates to the parent module of the current module.
68
69#### Matching Brace
70
71If the cursor is on any brace (`<>(){}[]`) which is a part of a brace-pair,
72moves cursor to the matching brace. It uses the actual parser to determine
73braces, so it won't confuse generics with comparisons.
74
75#### Join Lines
76
77Join selected lines into one, smartly fixing up whitespace and trailing commas.
78
79#### Show Syntax Tree
80
81Shows the parse tree of the current file. It exists mostly for debugging
82rust-analyzer itself.
83
84#### Expand Macro Recursively
85
86Shows the full macro expansion of the macro at current cursor.
87
88#### Status
89
90Shows internal statistic about memory usage of rust-analyzer.
91
92#### Show RA Version
93
94Show current rust-analyzer version.
95
96#### Toggle inlay hints
97
98Toggle inlay hints view for the current workspace.
99It is recommended to assign a shortcut for this command to quickly turn off
100inlay hints when they prevent you from reading/writing the code.
101
102#### Run Garbage Collection
103
104Manually triggers GC.
105
106#### Start Cargo Watch
107
108Start `cargo watch` for live error highlighting. Will prompt to install if it's not already installed.
109
110#### Stop Cargo Watch
111
112Stop `cargo watch`.
113
114#### Structural Seach and Replace
115
116Search and replace with named wildcards that will match any expression.
117The syntax for a structural search replace command is `<search_pattern> ==>> <replace_pattern>`. A `$<name>:expr` placeholder in the search pattern will match any expression and `$<name>` will reference it in the replacement. Available via the command `rust-analyzer.ssr`.
118
119```rust
120// Using structural search replace command [foo($a:expr, $b:expr) ==>> ($a).foo($b)]
121
122// BEFORE
123String::from(foo(y + 5, z))
124
125// AFTER
126String::from((y + 5).foo(z))
127```
128
129### Assists (Code Actions)
130
131Assists, or code actions, are small local refactorings, available in a particular context.
132They are usually triggered by a shortcut or by clicking a light bulb icon in the editor.
133
134See [assists.md](./assists.md) for the list of available assists.
135
136### Magic Completions
137
138In addition to usual reference completion, rust-analyzer provides some ✨magic✨
139completions as well:
140
141Keywords like `if`, `else` `while`, `loop` are completed with braces, and cursor
142is placed at the appropriate position. Even though `if` is easy to type, you
143still want to complete it, to get ` { }` for free! `return` is inserted with a
144space or `;` depending on the return type of the function.
145
146When completing a function call, `()` are automatically inserted. If a function
147takes arguments, the cursor is positioned inside the parenthesis.
148
149There are postfix completions, which can be triggered by typing something like
150`foo().if`. The word after `.` determines postfix completion. Possible variants are:
151
152- `expr.if` -> `if expr {}` or `if let ... {}` for `Option` or `Result`
153- `expr.match` -> `match expr {}`
154- `expr.while` -> `while expr {}` or `while let ... {}` for `Option` or `Result`
155- `expr.ref` -> `&expr`
156- `expr.refm` -> `&mut expr`
157- `expr.not` -> `!expr`
158- `expr.dbg` -> `dbg!(expr)`
159
160There also snippet completions:
161
162#### Inside Expressions
163
164- `pd` -> `println!("{:?}")`
165- `ppd` -> `println!("{:#?}")`
166
167#### Inside Modules
168
169- `tfn` -> `#[test] fn f(){}`
170- `tmod` ->
171```rust
172#[cfg(test)]
173mod tests {
174 use super::*;
175
176 #[test]
177 fn test_fn() {}
178}
179```
180
181### Code Highlighting
182
183Experimental feature to let rust-analyzer highlight Rust code instead of using the
184default highlighter.
185
186#### Rainbow Highlighting
187
188Experimental feature that, given code highlighting using rust-analyzer is
189active, will pick unique colors for identifiers.
190
191### Code hints
192
193Rust-analyzer has two types of hints to show the information about the code:
194
195* hover hints, appearing on hover on any element.
196
197These contain extended information on the hovered language item.
198
199* inlay hints, shown near the element hinted directly in the editor.
200
201Two types of inlay hints are displayed currently:
202
203* type hints, displaying the minimal information on the type of the expression (if the information is available)
204* method chaining hints, type information for multi-line method chains
205* parameter name hints, displaying the names of the parameters in the corresponding methods
206
207#### VS Code
208
209In VS Code, the following settings can be used to configure the inlay hints:
210
211* `rust-analyzer.inlayHints.typeHints` - enable hints for inferred types.
212* `rust-analyzer.inlayHints.chainingHints` - enable hints for inferred types on method chains.
213* `rust-analyzer.inlayHints.parameterHints` - enable hints for function parameters.
214* `rust-analyzer.inlayHints.maxLength` — shortens the hints if their length exceeds the value specified. If no value is specified (`null`), no shortening is applied.
215
216**Note:** VS Code does not have native support for inlay hints [yet](https://github.com/microsoft/vscode/issues/16221) and the hints are implemented using decorations.
217This approach has limitations, the caret movement and bracket highlighting near the edges of the hint may be weird:
218[1](https://github.com/rust-analyzer/rust-analyzer/issues/1623), [2](https://github.com/rust-analyzer/rust-analyzer/issues/3453).
diff --git a/docs/user/assists.md b/docs/user/generated_assists.adoc
index a1058ecde..4d2fb31d4 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,36 +44,69 @@ 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_function` 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]
72
73Adds a From impl for an enum variant with one tuple field.
74
75.Before
76```rust
77enum A { ┃One(u32) }
78```
79
80.After
81```rust
82enum A { One(u32) }
83
84impl From<u32> for A {
85 fn from(v: u32) -> Self {
86 A::One(v)
87 }
88}
89```
90
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]
60 95
61Adds 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.
62 97
98.Before
63```rust 99```rust
64// BEFORE
65struct Baz; 100struct Baz;
66fn baz() -> Baz { Baz } 101fn baz() -> Baz { Baz }
67fn foo() { 102fn foo() {
68 bar┃("", baz()); 103 bar┃("", baz());
69} 104}
70 105
106```
71 107
72// AFTER 108.After
109```rust
73struct Baz; 110struct Baz;
74fn baz() -> Baz { Baz } 111fn baz() -> Baz { Baz }
75fn foo() { 112fn foo() {
@@ -82,33 +119,43 @@ fn bar(arg: &str, baz: Baz) {
82 119
83``` 120```
84 121
85## `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]
86 126
87Adds a hash to a raw string literal. 127Adds a hash to a raw string literal.
88 128
129.Before
89```rust 130```rust
90// BEFORE
91fn main() { 131fn main() {
92 r#"Hello,┃ World!"#; 132 r#"Hello,┃ World!"#;
93} 133}
134```
94 135
95// AFTER 136.After
137```rust
96fn main() { 138fn main() {
97 r##"Hello, World!"##; 139 r##"Hello, World!"##;
98} 140}
99``` 141```
100 142
101## `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]
102 147
103Adds a new inherent impl for a type. 148Adds a new inherent impl for a type.
104 149
150.Before
105```rust 151```rust
106// BEFORE
107struct Ctx<T: Clone> { 152struct Ctx<T: Clone> {
108 data: T,┃ 153 data: T,┃
109} 154}
155```
110 156
111// AFTER 157.After
158```rust
112struct Ctx<T: Clone> { 159struct Ctx<T: Clone> {
113 data: T, 160 data: T,
114} 161}
@@ -118,12 +165,15 @@ impl<T: Clone> Ctx<T> {
118} 165}
119``` 166```
120 167
121## `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]
122 172
123Adds scaffold for overriding default impl members. 173Adds scaffold for overriding default impl members.
124 174
175.Before
125```rust 176```rust
126// BEFORE
127trait Trait { 177trait Trait {
128 Type X; 178 Type X;
129 fn foo(&self); 179 fn foo(&self);
@@ -135,8 +185,10 @@ impl Trait for () {
135 fn foo(&self) {}┃ 185 fn foo(&self) {}┃
136 186
137} 187}
188```
138 189
139// AFTER 190.After
191```rust
140trait Trait { 192trait Trait {
141 Type X; 193 Type X;
142 fn foo(&self); 194 fn foo(&self);
@@ -151,12 +203,15 @@ impl Trait for () {
151} 203}
152``` 204```
153 205
154## `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]
155 210
156Adds scaffold for required impl members. 211Adds scaffold for required impl members.
157 212
213.Before
158```rust 214```rust
159// BEFORE
160trait Trait<T> { 215trait Trait<T> {
161 Type X; 216 Type X;
162 fn foo(&self) -> T; 217 fn foo(&self) -> T;
@@ -166,8 +221,10 @@ trait Trait<T> {
166impl Trait<u32> for () {┃ 221impl Trait<u32> for () {┃
167 222
168} 223}
224```
169 225
170// AFTER 226.After
227```rust
171trait Trait<T> { 228trait Trait<T> {
172 Type X; 229 Type X;
173 fn foo(&self) -> T; 230 fn foo(&self) -> T;
@@ -182,17 +239,22 @@ impl Trait<u32> for () {
182} 239}
183``` 240```
184 241
185## `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]
186 246
187Adds a new inherent impl for a type. 247Adds a new inherent impl for a type.
188 248
249.Before
189```rust 250```rust
190// BEFORE
191struct Ctx<T: Clone> { 251struct Ctx<T: Clone> {
192 data: T,┃ 252 data: T,┃
193} 253}
254```
194 255
195// AFTER 256.After
257```rust
196struct Ctx<T: Clone> { 258struct Ctx<T: Clone> {
197 data: T, 259 data: T,
198} 260}
@@ -203,25 +265,33 @@ impl<T: Clone> Ctx<T> {
203 265
204``` 266```
205 267
206## `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]
207 272
208Adds `::<_>` to a call of a generic method or function. 273Adds `::<_>` to a call of a generic method or function.
209 274
275.Before
210```rust 276```rust
211// BEFORE
212fn make<T>() -> T { todo!() } 277fn make<T>() -> T { todo!() }
213fn main() { 278fn main() {
214 let x = make┃(); 279 let x = make┃();
215} 280}
281```
216 282
217// AFTER 283.After
284```rust
218fn make<T>() -> T { todo!() } 285fn make<T>() -> T { todo!() }
219fn main() { 286fn main() {
220 let x = make::<${0:_}>(); 287 let x = make::<${0:_}>();
221} 288}
222``` 289```
223 290
224## `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]
225 295
226Apply [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).
227This transforms expressions of the form `!l || !r` into `!(l && r)`. 297This transforms expressions of the form `!l || !r` into `!(l && r)`.
@@ -229,29 +299,36 @@ This also works with `&&`. This assist can only be applied with the cursor
229on either `||` or `&&`, with both operands being a negation of some kind. 299on either `||` or `&&`, with both operands being a negation of some kind.
230This means something of the form `!x` or `x != y`. 300This means something of the form `!x` or `x != y`.
231 301
302.Before
232```rust 303```rust
233// BEFORE
234fn main() { 304fn main() {
235 if x != 4 ||┃ !y {} 305 if x != 4 ||┃ !y {}
236} 306}
307```
237 308
238// AFTER 309.After
310```rust
239fn main() { 311fn main() {
240 if !(x == 4 && y) {} 312 if !(x == 4 && y) {}
241} 313}
242``` 314```
243 315
244## `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]
245 320
246If the name is unresolved, provides all possible imports for it. 321If the name is unresolved, provides all possible imports for it.
247 322
323.Before
248```rust 324```rust
249// BEFORE
250fn main() { 325fn main() {
251 let map = HashMap┃::new(); 326 let map = HashMap┃::new();
252} 327}
328```
253 329
254// AFTER 330.After
331```rust
255use std::collections::HashMap; 332use std::collections::HashMap;
256 333
257fn main() { 334fn main() {
@@ -259,68 +336,59 @@ fn main() {
259} 336}
260``` 337```
261 338
262## `change_lifetime_anon_to_named`
263
264Change an anonymous lifetime to a named lifetime.
265
266```rust
267// BEFORE
268impl Cursor<'_┃> {
269 fn node(self) -> &SyntaxNode {
270 match self {
271 Cursor::Replace(node) | Cursor::Before(node) => node,
272 }
273 }
274}
275
276// AFTER
277impl<'a> Cursor<'a> {
278 fn node(self) -> &SyntaxNode {
279 match self {
280 Cursor::Replace(node) | Cursor::Before(node) => node,
281 }
282 }
283}
284```
285 339
286## `change_return_type_to_result` 340[discrete]
341=== `change_return_type_to_result`
342**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]
287 343
288Change the function's return type to Result. 344Change the function's return type to Result.
289 345
346.Before
290```rust 347```rust
291// BEFORE
292fn foo() -> i32┃ { 42i32 } 348fn foo() -> i32┃ { 42i32 }
349```
293 350
294// AFTER 351.After
352```rust
295fn foo() -> Result<i32, ${0:_}> { Ok(42i32) } 353fn foo() -> Result<i32, ${0:_}> { Ok(42i32) }
296``` 354```
297 355
298## `change_visibility` 356
357[discrete]
358=== `change_visibility`
359**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/change_visibility.rs#L14[change_visibility.rs]
299 360
300Adds or changes existing visibility specifier. 361Adds or changes existing visibility specifier.
301 362
363.Before
302```rust 364```rust
303// BEFORE
304┃fn frobnicate() {} 365┃fn frobnicate() {}
366```
305 367
306// AFTER 368.After
369```rust
307pub(crate) fn frobnicate() {} 370pub(crate) fn frobnicate() {}
308``` 371```
309 372
310## `convert_to_guarded_return` 373
374[discrete]
375=== `convert_to_guarded_return`
376**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/early_return.rs#L21[early_return.rs]
311 377
312Replace a large conditional with a guarded return. 378Replace a large conditional with a guarded return.
313 379
380.Before
314```rust 381```rust
315// BEFORE
316fn main() { 382fn main() {
317 ┃if cond { 383 ┃if cond {
318 foo(); 384 foo();
319 bar(); 385 bar();
320 } 386 }
321} 387}
388```
322 389
323// AFTER 390.After
391```rust
324fn main() { 392fn main() {
325 if !cond { 393 if !cond {
326 return; 394 return;
@@ -330,12 +398,15 @@ fn main() {
330} 398}
331``` 399```
332 400
333## `fill_match_arms` 401
402[discrete]
403=== `fill_match_arms`
404**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/fill_match_arms.rs#L14[fill_match_arms.rs]
334 405
335Adds missing clauses to a `match` expression. 406Adds missing clauses to a `match` expression.
336 407
408.Before
337```rust 409```rust
338// BEFORE
339enum Action { Move { distance: u32 }, Stop } 410enum Action { Move { distance: u32 }, Stop }
340 411
341fn handle(action: Action) { 412fn handle(action: Action) {
@@ -343,8 +414,10 @@ fn handle(action: Action) {
343 414
344 } 415 }
345} 416}
417```
346 418
347// AFTER 419.After
420```rust
348enum Action { Move { distance: u32 }, Stop } 421enum Action { Move { distance: u32 }, Stop }
349 422
350fn handle(action: Action) { 423fn handle(action: Action) {
@@ -355,20 +428,25 @@ fn handle(action: Action) {
355} 428}
356``` 429```
357 430
358## `fix_visibility` 431
432[discrete]
433=== `fix_visibility`
434**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/fix_visibility.rs#L13[fix_visibility.rs]
359 435
360Makes inaccessible item public. 436Makes inaccessible item public.
361 437
438.Before
362```rust 439```rust
363// BEFORE
364mod m { 440mod m {
365 fn frobnicate() {} 441 fn frobnicate() {}
366} 442}
367fn main() { 443fn main() {
368 m::frobnicate┃() {} 444 m::frobnicate┃() {}
369} 445}
446```
370 447
371// AFTER 448.After
449```rust
372mod m { 450mod m {
373 $0pub(crate) fn frobnicate() {} 451 $0pub(crate) fn frobnicate() {}
374} 452}
@@ -377,154 +455,231 @@ fn main() {
377} 455}
378``` 456```
379 457
380## `flip_binexpr` 458
459[discrete]
460=== `flip_binexpr`
461**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/flip_binexpr.rs#L5[flip_binexpr.rs]
381 462
382Flips operands of a binary expression. 463Flips operands of a binary expression.
383 464
465.Before
384```rust 466```rust
385// BEFORE
386fn main() { 467fn main() {
387 let _ = 90 +┃ 2; 468 let _ = 90 +┃ 2;
388} 469}
470```
389 471
390// AFTER 472.After
473```rust
391fn main() { 474fn main() {
392 let _ = 2 + 90; 475 let _ = 2 + 90;
393} 476}
394``` 477```
395 478
396## `flip_comma` 479
480[discrete]
481=== `flip_comma`
482**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/flip_comma.rs#L5[flip_comma.rs]
397 483
398Flips two comma-separated items. 484Flips two comma-separated items.
399 485
486.Before
400```rust 487```rust
401// BEFORE
402fn main() { 488fn main() {
403 ((1, 2),┃ (3, 4)); 489 ((1, 2),┃ (3, 4));
404} 490}
491```
405 492
406// AFTER 493.After
494```rust
407fn main() { 495fn main() {
408 ((3, 4), (1, 2)); 496 ((3, 4), (1, 2));
409} 497}
410``` 498```
411 499
412## `flip_trait_bound` 500
501[discrete]
502=== `flip_trait_bound`
503**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/flip_trait_bound.rs#L9[flip_trait_bound.rs]
413 504
414Flips two trait bounds. 505Flips two trait bounds.
415 506
507.Before
416```rust 508```rust
417// BEFORE
418fn foo<T: Clone +┃ Copy>() { } 509fn foo<T: Clone +┃ Copy>() { }
510```
419 511
420// AFTER 512.After
513```rust
421fn foo<T: Copy + Clone>() { } 514fn foo<T: Copy + Clone>() { }
422``` 515```
423 516
424## `inline_local_variable` 517
518[discrete]
519=== `inline_local_variable`
520**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/inline_local_variable.rs#L13[inline_local_variable.rs]
425 521
426Inlines local variable. 522Inlines local variable.
427 523
524.Before
428```rust 525```rust
429// BEFORE
430fn main() { 526fn main() {
431 let x┃ = 1 + 2; 527 let x┃ = 1 + 2;
432 x * 4; 528 x * 4;
433} 529}
530```
434 531
435// AFTER 532.After
533```rust
436fn main() { 534fn main() {
437 (1 + 2) * 4; 535 (1 + 2) * 4;
438} 536}
439``` 537```
440 538
441## `introduce_variable` 539
540[discrete]
541=== `introduce_named_lifetime`
542**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/introduce_named_lifetime.rs#L12[introduce_named_lifetime.rs]
543
544Change an anonymous lifetime to a named lifetime.
545
546.Before
547```rust
548impl Cursor<'_┃> {
549 fn node(self) -> &SyntaxNode {
550 match self {
551 Cursor::Replace(node) | Cursor::Before(node) => node,
552 }
553 }
554}
555```
556
557.After
558```rust
559impl<'a> Cursor<'a> {
560 fn node(self) -> &SyntaxNode {
561 match self {
562 Cursor::Replace(node) | Cursor::Before(node) => node,
563 }
564 }
565}
566```
567
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]
442 572
443Extracts subexpression into a variable. 573Extracts subexpression into a variable.
444 574
575.Before
445```rust 576```rust
446// BEFORE
447fn main() { 577fn main() {
448 ┃(1 + 2)┃ * 4; 578 ┃(1 + 2)┃ * 4;
449} 579}
580```
450 581
451// AFTER 582.After
583```rust
452fn main() { 584fn main() {
453 let $0var_name = (1 + 2); 585 let $0var_name = (1 + 2);
454 var_name * 4; 586 var_name * 4;
455} 587}
456``` 588```
457 589
458## `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]
459 594
460Apply invert_if 595Apply invert_if
461This 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}`
462This 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
463on `if`. 598on `if`.
464 599
600.Before
465```rust 601```rust
466// BEFORE
467fn main() { 602fn main() {
468 if┃ !y { A } else { B } 603 if┃ !y { A } else { B }
469} 604}
605```
470 606
471// AFTER 607.After
608```rust
472fn main() { 609fn main() {
473 if y { B } else { A } 610 if y { B } else { A }
474} 611}
475``` 612```
476 613
477## `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]
478 618
479Adds `r#` to a plain string literal. 619Adds `r#` to a plain string literal.
480 620
621.Before
481```rust 622```rust
482// BEFORE
483fn main() { 623fn main() {
484 "Hello,┃ World!"; 624 "Hello,┃ World!";
485} 625}
626```
486 627
487// AFTER 628.After
629```rust
488fn main() { 630fn main() {
489 r#"Hello, World!"#; 631 r#"Hello, World!"#;
490} 632}
491``` 633```
492 634
493## `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]
494 639
495Turns a raw string into a plain string. 640Turns a raw string into a plain string.
496 641
642.Before
497```rust 643```rust
498// BEFORE
499fn main() { 644fn main() {
500 r#"Hello,┃ "World!""#; 645 r#"Hello,┃ "World!""#;
501} 646}
647```
502 648
503// AFTER 649.After
650```rust
504fn main() { 651fn main() {
505 "Hello, \"World!\""; 652 "Hello, \"World!\"";
506} 653}
507``` 654```
508 655
509## `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]
510 660
511Merges two imports with a common prefix. 661Merges two imports with a common prefix.
512 662
663.Before
513```rust 664```rust
514// BEFORE
515use std::┃fmt::Formatter; 665use std::┃fmt::Formatter;
516use std::io; 666use std::io;
667```
517 668
518// AFTER 669.After
670```rust
519use std::{fmt::Formatter, io}; 671use std::{fmt::Formatter, io};
520``` 672```
521 673
522## `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]
523 678
524Merges identical match arms. 679Merges identical match arms.
525 680
681.Before
526```rust 682```rust
527// BEFORE
528enum Action { Move { distance: u32 }, Stop } 683enum Action { Move { distance: u32 }, Stop }
529 684
530fn handle(action: Action) { 685fn handle(action: Action) {
@@ -533,8 +688,10 @@ fn handle(action: Action) {
533 Action::Stop => foo(), 688 Action::Stop => foo(),
534 } 689 }
535} 690}
691```
536 692
537// AFTER 693.After
694```rust
538enum Action { Move { distance: u32 }, Stop } 695enum Action { Move { distance: u32 }, Stop }
539 696
540fn handle(action: Action) { 697fn handle(action: Action) {
@@ -544,12 +701,15 @@ fn handle(action: Action) {
544} 701}
545``` 702```
546 703
547## `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]
548 708
549Moves if expression from match arm body into a guard. 709Moves if expression from match arm body into a guard.
550 710
711.Before
551```rust 712```rust
552// BEFORE
553enum Action { Move { distance: u32 }, Stop } 713enum Action { Move { distance: u32 }, Stop }
554 714
555fn handle(action: Action) { 715fn handle(action: Action) {
@@ -558,8 +718,10 @@ fn handle(action: Action) {
558 _ => (), 718 _ => (),
559 } 719 }
560} 720}
721```
561 722
562// AFTER 723.After
724```rust
563enum Action { Move { distance: u32 }, Stop } 725enum Action { Move { distance: u32 }, Stop }
564 726
565fn handle(action: Action) { 727fn handle(action: Action) {
@@ -570,28 +732,36 @@ fn handle(action: Action) {
570} 732}
571``` 733```
572 734
573## `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]
574 739
575Moves inline type bounds to a where clause. 740Moves inline type bounds to a where clause.
576 741
742.Before
577```rust 743```rust
578// BEFORE
579fn 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 {
580 f(x) 745 f(x)
581} 746}
747```
582 748
583// AFTER 749.After
750```rust
584fn 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 {
585 f(x) 752 f(x)
586} 753}
587``` 754```
588 755
589## `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]
590 760
591Moves match guard into match arm body. 761Moves match guard into match arm body.
592 762
763.Before
593```rust 764```rust
594// BEFORE
595enum Action { Move { distance: u32 }, Stop } 765enum Action { Move { distance: u32 }, Stop }
596 766
597fn handle(action: Action) { 767fn handle(action: Action) {
@@ -600,8 +770,10 @@ fn handle(action: Action) {
600 _ => (), 770 _ => (),
601 } 771 }
602} 772}
773```
603 774
604// AFTER 775.After
776```rust
605enum Action { Move { distance: u32 }, Stop } 777enum Action { Move { distance: u32 }, Stop }
606 778
607fn handle(action: Action) { 779fn handle(action: Action) {
@@ -612,75 +784,98 @@ fn handle(action: Action) {
612} 784}
613``` 785```
614 786
615## `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]
616 791
617Removes `dbg!()` macro call. 792Removes `dbg!()` macro call.
618 793
794.Before
619```rust 795```rust
620// BEFORE
621fn main() { 796fn main() {
622 ┃dbg!(92); 797 ┃dbg!(92);
623} 798}
799```
624 800
625// AFTER 801.After
802```rust
626fn main() { 803fn main() {
627 92; 804 92;
628} 805}
629``` 806```
630 807
631## `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]
632 812
633Removes a hash from a raw string literal. 813Removes a hash from a raw string literal.
634 814
815.Before
635```rust 816```rust
636// BEFORE
637fn main() { 817fn main() {
638 r#"Hello,┃ World!"#; 818 r#"Hello,┃ World!"#;
639} 819}
820```
640 821
641// AFTER 822.After
823```rust
642fn main() { 824fn main() {
643 r"Hello, World!"; 825 r"Hello, World!";
644} 826}
645``` 827```
646 828
647## `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]
648 833
649Removes the `mut` keyword. 834Removes the `mut` keyword.
650 835
836.Before
651```rust 837```rust
652// BEFORE
653impl Walrus { 838impl Walrus {
654 fn feed(&mut┃ self, amount: u32) {} 839 fn feed(&mut┃ self, amount: u32) {}
655} 840}
841```
656 842
657// AFTER 843.After
844```rust
658impl Walrus { 845impl Walrus {
659 fn feed(&self, amount: u32) {} 846 fn feed(&self, amount: u32) {}
660} 847}
661``` 848```
662 849
663## `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]
664 854
665Reorder 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
666the definition. 856the definition.
667 857
858.Before
668```rust 859```rust
669// BEFORE
670struct Foo {foo: i32, bar: i32}; 860struct Foo {foo: i32, bar: i32};
671const test: Foo = ┃Foo {bar: 0, foo: 1} 861const test: Foo = ┃Foo {bar: 0, foo: 1}
862```
672 863
673// AFTER 864.After
865```rust
674struct Foo {foo: i32, bar: i32}; 866struct Foo {foo: i32, bar: i32};
675const test: Foo = Foo {foo: 1, bar: 0} 867const test: Foo = Foo {foo: 1, bar: 0}
676``` 868```
677 869
678## `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]
679 874
680Replaces `if let` with an else branch with a `match` expression. 875Replaces `if let` with an else branch with a `match` expression.
681 876
877.Before
682```rust 878```rust
683// BEFORE
684enum Action { Move { distance: u32 }, Stop } 879enum Action { Move { distance: u32 }, Stop }
685 880
686fn handle(action: Action) { 881fn handle(action: Action) {
@@ -690,8 +885,10 @@ fn handle(action: Action) {
690 bar() 885 bar()
691 } 886 }
692} 887}
888```
693 889
694// AFTER 890.After
891```rust
695enum Action { Move { distance: u32 }, Stop } 892enum Action { Move { distance: u32 }, Stop }
696 893
697fn handle(action: Action) { 894fn handle(action: Action) {
@@ -702,20 +899,25 @@ fn handle(action: Action) {
702} 899}
703``` 900```
704 901
705## `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]
706 906
707Replaces `let` with an `if-let`. 907Replaces `let` with an `if-let`.
708 908
909.Before
709```rust 910```rust
710// BEFORE
711 911
712fn main(action: Action) { 912fn main(action: Action) {
713 ┃let x = compute(); 913 ┃let x = compute();
714} 914}
715 915
716fn compute() -> Option<i32> { None } 916fn compute() -> Option<i32> { None }
917```
717 918
718// AFTER 919.After
920```rust
719 921
720fn main(action: Action) { 922fn main(action: Action) {
721 if let Some(x) = compute() { 923 if let Some(x) = compute() {
@@ -725,33 +927,43 @@ fn main(action: Action) {
725fn compute() -> Option<i32> { None } 927fn compute() -> Option<i32> { None }
726``` 928```
727 929
728## `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]
729 934
730Adds a use statement for a given fully-qualified name. 935Adds a use statement for a given fully-qualified name.
731 936
937.Before
732```rust 938```rust
733// BEFORE
734fn process(map: std::collections::┃HashMap<String, String>) {} 939fn process(map: std::collections::┃HashMap<String, String>) {}
940```
735 941
736// AFTER 942.After
943```rust
737use std::collections::HashMap; 944use std::collections::HashMap;
738 945
739fn process(map: HashMap<String, String>) {} 946fn process(map: HashMap<String, String>) {}
740``` 947```
741 948
742## `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]
743 953
744Replaces `unwrap` a `match` expression. Works for Result and Option. 954Replaces `unwrap` a `match` expression. Works for Result and Option.
745 955
956.Before
746```rust 957```rust
747// BEFORE
748enum Result<T, E> { Ok(T), Err(E) } 958enum Result<T, E> { Ok(T), Err(E) }
749fn main() { 959fn main() {
750 let x: Result<i32, i32> = Result::Ok(92); 960 let x: Result<i32, i32> = Result::Ok(92);
751 let y = x.┃unwrap(); 961 let y = x.┃unwrap();
752} 962}
963```
753 964
754// AFTER 965.After
966```rust
755enum Result<T, E> { Ok(T), Err(E) } 967enum Result<T, E> { Ok(T), Err(E) }
756fn main() { 968fn main() {
757 let x: Result<i32, i32> = Result::Ok(92); 969 let x: Result<i32, i32> = Result::Ok(92);
@@ -762,31 +974,41 @@ fn main() {
762} 974}
763``` 975```
764 976
765## `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]
766 981
767Wraps the tail of import into braces. 982Wraps the tail of import into braces.
768 983
984.Before
769```rust 985```rust
770// BEFORE
771use std::┃collections::HashMap; 986use std::┃collections::HashMap;
987```
772 988
773// AFTER 989.After
990```rust
774use std::{collections::HashMap}; 991use std::{collections::HashMap};
775``` 992```
776 993
777## `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]
778 998
779This 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.
780 1000
1001.Before
781```rust 1002```rust
782// BEFORE
783fn foo() { 1003fn foo() {
784 if true {┃ 1004 if true {┃
785 println!("foo"); 1005 println!("foo");
786 } 1006 }
787} 1007}
1008```
788 1009
789// AFTER 1010.After
1011```rust
790fn foo() { 1012fn foo() {
791 println!("foo"); 1013 println!("foo");
792} 1014}
diff --git a/docs/user/generated_features.adoc b/docs/user/generated_features.adoc
new file mode 100644
index 000000000..12812fa0b
--- /dev/null
+++ b/docs/user/generated_features.adoc
@@ -0,0 +1,298 @@
1=== Expand Macro Recursively
2**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/expand_macro.rs#L15[expand_macro.rs]
3
4Shows the full macro expansion of the macro at current cursor.
5
6|===
7| Editor | Action Name
8
9| VS Code | **Rust Analyzer: Expand macro recursively**
10|===
11
12
13=== Extend Selection
14**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/extend_selection.rs#L15[extend_selection.rs]
15
16Extends the current selection to the encompassing syntactic construct
17(expression, statement, item, module, etc). It works with multiple cursors.
18
19|===
20| Editor | Shortcut
21
22| VS Code | kbd:[Ctrl+Shift+→]
23|===
24
25
26=== File Structure
27**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/display/structure.rs#L17[structure.rs]
28
29Provides a tree of the symbols defined in the file. Can be used to
30
31* fuzzy search symbol in a file (super useful)
32* draw breadcrumbs to describe the context around the cursor
33* draw outline of the file
34
35|===
36| Editor | Shortcut
37
38| VS Code | kbd:[Ctrl+Shift+O]
39|===
40
41
42=== Go to Definition
43**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/goto_definition.rs#L18[goto_definition.rs]
44
45Navigates to the definition of an identifier.
46
47|===
48| Editor | Shortcut
49
50| VS Code | kbd:[F12]
51|===
52
53
54=== Go to Implementation
55**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/goto_implementation.rs#L7[goto_implementation.rs]
56
57Navigates to the impl block of structs, enums or traits. Also implemented as a code lens.
58
59|===
60| Editor | Shortcut
61
62| VS Code | kbd:[Ctrl+F12]
63|===
64
65
66=== Go to Type Definition
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
69Navigates to the type of an identifier.
70
71|===
72| Editor | Action Name
73
74| VS Code | **Go to Type Definition*
75|===
76
77
78=== Hover
79**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/hover.rs#L63[hover.rs]
80
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.
83
84
85=== Inlay Hints
86**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/inlay_hints.rs#L40[inlay_hints.rs]
87
88rust-analyzer shows additional information inline with the source code.
89Editors usually render this using read-only virtual text snippets interspersed with code.
90
91rust-analyzer shows hits for
92
93* types of local variables
94* names of function arguments
95* types of chained expressions
96
97**Note:** VS Code does not have native support for inlay hints https://github.com/microsoft/vscode/issues/16221[yet] and the hints are implemented using decorations.
98This approach has limitations, the caret movement and bracket highlighting near the edges of the hint may be weird:
99https://github.com/rust-analyzer/rust-analyzer/issues/1623[1], https://github.com/rust-analyzer/rust-analyzer/issues/3453[2].
100
101|===
102| Editor | Action Name
103
104| VS Code | **Rust Analyzer: Toggle inlay hints*
105|===
106
107
108=== Join Lines
109**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/join_lines.rs#L12[join_lines.rs]
110
111Join selected lines into one, smartly fixing up whitespace, trailing commas, and braces.
112
113|===
114| Editor | Action Name
115
116| VS Code | **Rust Analyzer: Join lines**
117|===
118
119
120=== Magic Completions
121**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/completion.rs#L38[completion.rs]
122
123In addition to usual reference completion, rust-analyzer provides some ✨magic✨
124completions as well:
125
126Keywords like `if`, `else` `while`, `loop` are completed with braces, and cursor
127is placed at the appropriate position. Even though `if` is easy to type, you
128still want to complete it, to get ` { }` for free! `return` is inserted with a
129space or `;` depending on the return type of the function.
130
131When completing a function call, `()` are automatically inserted. If a function
132takes arguments, the cursor is positioned inside the parenthesis.
133
134There are postfix completions, which can be triggered by typing something like
135`foo().if`. The word after `.` determines postfix completion. Possible variants are:
136
137- `expr.if` -> `if expr {}` or `if let ... {}` for `Option` or `Result`
138- `expr.match` -> `match expr {}`
139- `expr.while` -> `while expr {}` or `while let ... {}` for `Option` or `Result`
140- `expr.ref` -> `&expr`
141- `expr.refm` -> `&mut expr`
142- `expr.not` -> `!expr`
143- `expr.dbg` -> `dbg!(expr)`
144
145There also snippet completions:
146
147.Expressions
148- `pd` -> `println!("{:?}")`
149- `ppd` -> `println!("{:#?}")`
150
151.Items
152- `tfn` -> `#[test] fn f(){}`
153- `tmod` ->
154```rust
155#[cfg(test)]
156mod tests {
157 use super::*;
158
159 #[test]
160 fn test_fn() {}
161}
162```
163
164
165=== Matching Brace
166**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/matching_brace.rs#L3[matching_brace.rs]
167
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
170braces, so it won't confuse generics with comparisons.
171
172|===
173| Editor | Action Name
174
175| VS Code | **Rust Analyzer: Find matching brace**
176|===
177
178
179=== On Typing Assists
180**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/typing.rs#L35[typing.rs]
181
182Some features trigger on typing certain characters:
183
184- typing `let =` tries to smartly add `;` if `=` is followed by an existing expression
185- Enter inside comments automatically inserts `///`
186- typing `.` in a chain method call auto-indents
187
188
189=== Parent Module
190**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/parent_module.rs#L12[parent_module.rs]
191
192Navigates to the parent module of the current module.
193
194|===
195| Editor | Action Name
196
197| VS Code | **Rust Analyzer: Locate parent module**
198|===
199
200
201=== Run
202**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/runnables.rs#L45[runnables.rs]
203
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
206to a shortcut!
207
208|===
209| Editor | Action Name
210
211| VS Code | **Rust Analyzer: Run**
212|===
213
214
215=== Semantic Syntax Highlighting
216**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/syntax_highlighting.rs#L33[syntax_highlighting.rs]
217
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.
220rust-analyzer does not specify colors directly, instead it assigns tag (like `struct`) and a set of modifiers (like `declaration`) to each token.
221It's up to the client to map those to specific colors.
222
223The general rule is that a reference to an entity gets colored the same way as the entity itself.
224We also give special modifier for `mut` and `&mut` local variables.
225
226
227=== Show Syntax Tree
228**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/syntax_tree.rs#L9[syntax_tree.rs]
229
230Shows the parse tree of the current file. It exists mostly for debugging
231rust-analyzer itself.
232
233|===
234| Editor | Action Name
235
236| VS Code | **Rust Analyzer: Show Syntax Tree**
237|===
238
239
240=== Status
241**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/status.rs#L27[status.rs]
242
243Shows internal statistic about memory usage of rust-analyzer.
244
245|===
246| Editor | Action Name
247
248| VS Code | **Rust Analyzer: Status**
249|===
250
251
252=== Structural Seach and Replace
253**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/ssr.rs#L26[ssr.rs]
254
255Search and replace with named wildcards that will match any expression.
256The syntax for a structural search replace command is `<search_pattern> ==>> <replace_pattern>`.
257A `$<name>:expr` placeholder in the search pattern will match any expression and `$<name>` will reference it in the replacement.
258Available via the command `rust-analyzer.ssr`.
259
260```rust
261// Using structural search replace command [foo($a:expr, $b:expr) ==>> ($a).foo($b)]
262
263// BEFORE
264String::from(foo(y + 5, z))
265
266// AFTER
267String::from((y + 5).foo(z))
268```
269
270|===
271| Editor | Action Name
272
273| VS Code | **Rust Analyzer: Structural Search Replace**
274|===
275
276
277=== Workspace Symbol
278**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide_db/src/symbol_index.rs#L113[symbol_index.rs]
279
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
282navigation tremendously. It mostly works on top of the built-in LSP
283functionality, however `#` and `*` symbols can be used to narrow down the
284search. Specifically,
285
286- `Foo` searches for `Foo` type in the current workspace
287- `foo#` searches for `foo` function in the current workspace
288- `Foo*` searches for `Foo` type among dependencies, including `stdlib`
289- `foo#*` searches for `foo` function among dependencies
290
291That is, `#` switches from "types" to all symbols, `*` switches from the current
292workspace to dependencies.
293
294|===
295| Editor | Shortcut
296
297| VS Code | kbd:[Ctrl+T]
298|===
diff --git a/docs/user/readme.adoc b/docs/user/manual.adoc
index 64bd0feb1..202783fd9 100644
--- a/docs/user/readme.adoc
+++ b/docs/user/manual.adoc
@@ -2,12 +2,9 @@
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: 6:source-highlighter: rouge
7:note-caption: :information_source: 7:experimental:
8:important-caption: :heavy_exclamation_mark:
9:caution-caption: :fire:
10:warning-caption: :warning:
11 8
12// Master copy of this document lives in the https://github.com/rust-analyzer/rust-analyzer repository 9// Master copy of this document lives in the https://github.com/rust-analyzer/rust-analyzer repository
13 10
@@ -17,7 +14,7 @@ https://microsoft.github.io/language-server-protocol/[Language Server Protocol]
17The 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.
18 15
19To improve this document, send a pull request against 16To improve this document, send a pull request against
20https://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].
21 18
22If 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.
23 20
@@ -65,16 +62,6 @@ The server binary is stored in:
65 62
66Note that we only support two most recent versions of VS Code. 63Note that we only support two most recent versions of VS Code.
67 64
68==== Special `when` clause context for keybindings.
69You may use `inRustProject` context to configure keybindings for rust projects only. For example:
70[source,json]
71----
72{ "key": "ctrl+shift+f5", "command": "workbench.action.debug.restart", "when": "inDebugMode && !inRustProject"},
73{ "key": "ctrl+shift+f5", "command": "rust-analyzer.debug", "when": "inRustProject"},
74{ "key": "ctrl+i", "command": "rust-analyzer.toggleInlayHints", "when": "inRustProject" }
75----
76More about `when` clause contexts https://code.visualstudio.com/docs/getstarted/keybindings#_when-clause-contexts[here].
77
78==== Updates 65==== Updates
79 66
80The 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.
@@ -122,10 +109,23 @@ Here are some useful self-diagnostic commands:
122* 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.
123* 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.
124 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
125=== rust-analyzer Language Server Binary 124=== rust-analyzer Language Server Binary
126 125
127Other editors generally require the `rust-analyzer` binary to be in `$PATH`. 126Other editors generally require the `rust-analyzer` binary to be in `$PATH`.
128You 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`.
129 129
130On 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
131 131
@@ -145,7 +145,8 @@ $ git clone https://github.com/rust-analyzer/rust-analyzer.git && cd rust-analyz
145$ cargo xtask install --server 145$ cargo xtask install --server
146---- 146----
147 147
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]. 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.
149 150
150==== Arch Linux 151==== Arch Linux
151 152
@@ -268,6 +269,14 @@ Gnome Builder currently has support for RLS, and there's no way to configure the
2681. Rename, symlink or copy the `rust-analyzer` binary to `rls` and place it somewhere Builder can find (in `PATH`, or under `~/.cargo/bin`). 2691. Rename, symlink or copy the `rust-analyzer` binary to `rls` and place it somewhere Builder can find (in `PATH`, or under `~/.cargo/bin`).
2692. Enable the Rust Builder plugin. 2702. Enable the Rust Builder plugin.
270 271
271== Usage 272== Features
273
274include::./generated_features.adoc[]
275
276== Assists (Code Actions)
277
278Assists, or code actions, are small local refactorings, available in a particular context.
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.
272 281
273See https://github.com/rust-analyzer/rust-analyzer/blob/master/docs/user/features.md[features.md]. 282include::./generated_assists.adoc[]
diff --git a/editors/code/src/run.ts b/editors/code/src/run.ts
index 401cb76af..113354bab 100644
--- a/editors/code/src/run.ts
+++ b/editors/code/src/run.ts
@@ -178,7 +178,7 @@ export function createTask(spec: ra.Runnable): vscode.Task {
178 label: spec.label, 178 label: spec.label,
179 command: toolchain.getPathForExecutable(spec.kind), 179 command: toolchain.getPathForExecutable(spec.kind),
180 args: spec.extraArgs ? [...spec.args, '--', ...spec.extraArgs] : spec.args, 180 args: spec.extraArgs ? [...spec.args, '--', ...spec.extraArgs] : spec.args,
181 env: spec.env, 181 env: Object.assign({}, process.env, spec.env),
182 }; 182 };
183 183
184 const execOption: vscode.ShellExecutionOptions = { 184 const execOption: vscode.ShellExecutionOptions = {
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 b4907f4b2..5511c01d5 100644
--- a/xtask/src/codegen.rs
+++ b/xtask/src/codegen.rs
@@ -8,14 +8,18 @@
8mod gen_syntax; 8mod gen_syntax;
9mod gen_parser_tests; 9mod gen_parser_tests;
10mod gen_assists_docs; 10mod gen_assists_docs;
11mod gen_feature_docs;
11 12
12use std::{mem, path::Path}; 13use std::{
14 fmt, mem,
15 path::{Path, PathBuf},
16};
13 17
14use crate::{not_bash::fs2, Result}; 18use crate::{not_bash::fs2, project_root, Result};
15 19
16pub use self::{ 20pub use self::{
17 gen_assists_docs::generate_assists_docs, gen_parser_tests::generate_parser_tests, 21 gen_assists_docs::generate_assists_docs, gen_feature_docs::generate_feature_docs,
18 gen_syntax::generate_syntax, 22 gen_parser_tests::generate_parser_tests, gen_syntax::generate_syntax,
19}; 23};
20 24
21const GRAMMAR_DIR: &str = "crates/ra_parser/src/grammar"; 25const GRAMMAR_DIR: &str = "crates/ra_parser/src/grammar";
@@ -28,7 +32,6 @@ const AST_TOKENS: &str = "crates/ra_syntax/src/ast/generated/tokens.rs";
28 32
29const ASSISTS_DIR: &str = "crates/ra_assists/src/handlers"; 33const ASSISTS_DIR: &str = "crates/ra_assists/src/handlers";
30const ASSISTS_TESTS: &str = "crates/ra_assists/src/tests/generated.rs"; 34const ASSISTS_TESTS: &str = "crates/ra_assists/src/tests/generated.rs";
31const ASSISTS_DOCS: &str = "docs/user/assists.md";
32 35
33#[derive(Debug, PartialEq, Eq, Clone, Copy)] 36#[derive(Debug, PartialEq, Eq, Clone, Copy)]
34pub enum Mode { 37pub enum Mode {
@@ -40,7 +43,7 @@ pub enum Mode {
40/// With verify = false, 43/// With verify = false,
41fn update(path: &Path, contents: &str, mode: Mode) -> Result<()> { 44fn update(path: &Path, contents: &str, mode: Mode) -> Result<()> {
42 match fs2::read_to_string(path) { 45 match fs2::read_to_string(path) {
43 Ok(ref old_contents) if normalize(old_contents) == normalize(contents) => { 46 Ok(old_contents) if normalize(&old_contents) == normalize(contents) => {
44 return Ok(()); 47 return Ok(());
45 } 48 }
46 _ => (), 49 _ => (),
@@ -58,35 +61,85 @@ fn update(path: &Path, contents: &str, mode: Mode) -> Result<()> {
58} 61}
59 62
60fn extract_comment_blocks(text: &str) -> Vec<Vec<String>> { 63fn extract_comment_blocks(text: &str) -> Vec<Vec<String>> {
61 do_extract_comment_blocks(text, false) 64 do_extract_comment_blocks(text, false).into_iter().map(|(_line, block)| block).collect()
65}
66
67fn extract_comment_blocks_with_empty_lines(tag: &str, text: &str) -> Vec<CommentBlock> {
68 assert!(tag.starts_with(char::is_uppercase));
69 let tag = format!("{}:", tag);
70 let mut res = Vec::new();
71 for (line, mut block) in do_extract_comment_blocks(text, true) {
72 let first = block.remove(0);
73 if first.starts_with(&tag) {
74 let id = first[tag.len()..].trim().to_string();
75 let block = CommentBlock { id, line, contents: block };
76 res.push(block);
77 }
78 }
79 res
62} 80}
63 81
64fn extract_comment_blocks_with_empty_lines(text: &str) -> Vec<Vec<String>> { 82struct CommentBlock {
65 do_extract_comment_blocks(text, true) 83 id: String,
84 line: usize,
85 contents: Vec<String>,
66} 86}
67 87
68fn 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>)> {
69 let mut res = Vec::new(); 92 let mut res = Vec::new();
70 93
71 let prefix = "// "; 94 let prefix = "// ";
72 let lines = text.lines().map(str::trim_start); 95 let lines = text.lines().map(str::trim_start);
73 96
74 let mut block = vec![]; 97 let mut block = (0, vec![]);
75 for line in lines { 98 for (line_num, line) in lines.enumerate() {
76 if line == "//" && allow_blocks_with_empty_lines { 99 if line == "//" && allow_blocks_with_empty_lines {
77 block.push(String::new()); 100 block.1.push(String::new());
78 continue; 101 continue;
79 } 102 }
80 103
81 let is_comment = line.starts_with(prefix); 104 let is_comment = line.starts_with(prefix);
82 if is_comment { 105 if is_comment {
83 block.push(line[prefix.len()..].to_string()); 106 block.1.push(line[prefix.len()..].to_string());
84 } else if !block.is_empty() { 107 } else {
85 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;
86 } 112 }
87 } 113 }
88 if !block.is_empty() { 114 if !block.1.is_empty() {
89 res.push(mem::replace(&mut block, Vec::new())) 115 res.push(block)
90 } 116 }
91 res 117 res
92} 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 20dcde820..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,
@@ -33,22 +39,18 @@ impl Assist {
33 39
34 fn collect_file(acc: &mut Vec<Assist>, path: &Path) -> Result<()> { 40 fn collect_file(acc: &mut Vec<Assist>, path: &Path) -> Result<()> {
35 let text = fs::read_to_string(path)?; 41 let text = fs::read_to_string(path)?;
36 let comment_blocks = extract_comment_blocks_with_empty_lines(&text); 42 let comment_blocks = extract_comment_blocks_with_empty_lines("Assist", &text);
37 43
38 for block in comment_blocks { 44 for block in comment_blocks {
39 // FIXME: doesn't support blank lines yet, need to tweak 45 // FIXME: doesn't support blank lines yet, need to tweak
40 // `extract_comment_blocks` for that. 46 // `extract_comment_blocks` for that.
41 let mut lines = block.iter(); 47 let id = block.id;
42 let first_line = lines.next().unwrap();
43 if !first_line.starts_with("Assist: ") {
44 continue;
45 }
46 let id = first_line["Assist: ".len()..].to_string();
47 assert!( 48 assert!(
48 id.chars().all(|it| it.is_ascii_lowercase() || it == '_'), 49 id.chars().all(|it| it.is_ascii_lowercase() || it == '_'),
49 "invalid assist id: {:?}", 50 "invalid assist id: {:?}",
50 id 51 id
51 ); 52 );
53 let mut lines = block.contents.iter();
52 54
53 let doc = take_until(lines.by_ref(), "```").trim().to_string(); 55 let doc = take_until(lines.by_ref(), "```").trim().to_string();
54 assert!( 56 assert!(
@@ -62,7 +64,8 @@ impl Assist {
62 assert_eq!(lines.next().unwrap().as_str(), "->"); 64 assert_eq!(lines.next().unwrap().as_str(), "->");
63 assert_eq!(lines.next().unwrap().as_str(), "```"); 65 assert_eq!(lines.next().unwrap().as_str(), "```");
64 let after = take_until(lines.by_ref(), "```"); 66 let after = take_until(lines.by_ref(), "```");
65 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 })
66 } 69 }
67 70
68 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 {
@@ -80,6 +83,33 @@ impl Assist {
80 } 83 }
81} 84}
82 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
83fn generate_tests(assists: &[Assist], mode: Mode) -> Result<()> { 113fn generate_tests(assists: &[Assist], mode: Mode) -> Result<()> {
84 let mut buf = String::from("use super::check_doc_test;\n"); 114 let mut buf = String::from("use super::check_doc_test;\n");
85 115
@@ -107,37 +137,6 @@ r#####"
107 codegen::update(&project_root().join(codegen::ASSISTS_TESTS), &buf, mode) 137 codegen::update(&project_root().join(codegen::ASSISTS_TESTS), &buf, mode)
108} 138}
109 139
110fn generate_docs(assists: &[Assist], mode: Mode) -> Result<()> {
111 let mut buf = String::from(
112 "# Assists\n\nCursor position or selection is signified by `┃` character.\n\n",
113 );
114
115 for assist in assists {
116 let before = assist.before.replace("<|>", "┃"); // Unicode pseudo-graphics bar
117 let after = assist.after.replace("<|>", "┃");
118 let docs = format!(
119 "
120## `{}`
121
122{}
123
124```rust
125// BEFORE
126{}
127// AFTER
128{}```
129",
130 assist.id,
131 assist.doc,
132 hide_hash_comments(&before),
133 hide_hash_comments(&after)
134 );
135 buf.push_str(&docs);
136 }
137
138 codegen::update(&project_root().join(codegen::ASSISTS_DOCS), &buf, mode)
139}
140
141fn hide_hash_comments(text: &str) -> String { 140fn hide_hash_comments(text: &str) -> String {
142 text.split('\n') // want final newline 141 text.split('\n') // want final newline
143 .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
new file mode 100644
index 000000000..31bc3839d
--- /dev/null
+++ b/xtask/src/codegen/gen_feature_docs.rs
@@ -0,0 +1,75 @@
1//! Generates `assists.md` documentation.
2
3use std::{fmt, fs, path::PathBuf};
4
5use crate::{
6 codegen::{self, extract_comment_blocks_with_empty_lines, Location, Mode},
7 project_root, rust_files, Result,
8};
9
10pub fn generate_feature_docs(mode: Mode) -> Result<()> {
11 let features = Feature::collect()?;
12 let contents = features.into_iter().map(|it| it.to_string()).collect::<Vec<_>>().join("\n\n");
13 let contents = contents.trim().to_string() + "\n";
14 let dst = project_root().join("docs/user/generated_features.adoc");
15 codegen::update(&dst, &contents, mode)?;
16 Ok(())
17}
18
19#[derive(Debug)]
20struct Feature {
21 id: String,
22 location: Location,
23 doc: String,
24}
25
26impl Feature {
27 fn collect() -> Result<Vec<Feature>> {
28 let mut res = Vec::new();
29 for path in rust_files(&project_root()) {
30 collect_file(&mut res, path)?;
31 }
32 res.sort_by(|lhs, rhs| lhs.id.cmp(&rhs.id));
33 return Ok(res);
34
35 fn collect_file(acc: &mut Vec<Feature>, path: PathBuf) -> Result<()> {
36 let text = fs::read_to_string(&path)?;
37 let comment_blocks = extract_comment_blocks_with_empty_lines("Feature", &text);
38
39 for block in comment_blocks {
40 let id = block.id;
41 assert!(is_valid_feature_name(&id), "invalid feature name: {:?}", id);
42 let doc = block.contents.join("\n");
43 let location = Location::new(path.clone(), block.line);
44 acc.push(Feature { id, location, doc })
45 }
46
47 Ok(())
48 }
49 }
50}
51
52fn is_valid_feature_name(feature: &str) -> bool {
53 'word: for word in feature.split_whitespace() {
54 for &short in ["to", "and"].iter() {
55 if word == short {
56 continue 'word;
57 }
58 }
59 for &short in ["To", "And"].iter() {
60 if word == short {
61 return false;
62 }
63 }
64 if !word.starts_with(char::is_uppercase) {
65 return false;
66 }
67 }
68 true
69}
70
71impl fmt::Display for Feature {
72 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73 writeln!(f, "=== {}\n**Source:** {}\n{}", self.id, self.location, self.doc)
74 }
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();
diff --git a/xtask/src/main.rs b/xtask/src/main.rs
index dff3ce4a1..9d7cdd114 100644
--- a/xtask/src/main.rs
+++ b/xtask/src/main.rs
@@ -75,6 +75,7 @@ FLAGS:
75 codegen::generate_syntax(Mode::Overwrite)?; 75 codegen::generate_syntax(Mode::Overwrite)?;
76 codegen::generate_parser_tests(Mode::Overwrite)?; 76 codegen::generate_parser_tests(Mode::Overwrite)?;
77 codegen::generate_assists_docs(Mode::Overwrite)?; 77 codegen::generate_assists_docs(Mode::Overwrite)?;
78 codegen::generate_feature_docs(Mode::Overwrite)?;
78 Ok(()) 79 Ok(())
79 } 80 }
80 "format" => { 81 "format" => {
diff --git a/xtask/tests/tidy.rs b/xtask/tests/tidy.rs
index 2e9fcf07c..4ac5d929f 100644
--- a/xtask/tests/tidy.rs
+++ b/xtask/tests/tidy.rs
@@ -31,6 +31,13 @@ fn generated_assists_are_fresh() {
31} 31}
32 32
33#[test] 33#[test]
34fn generated_features_are_fresh() {
35 if let Err(error) = codegen::generate_feature_docs(Mode::Verify) {
36 panic!("{}. Please update features by running `cargo xtask codegen`", error);
37 }
38}
39
40#[test]
34fn check_code_formatting() { 41fn check_code_formatting() {
35 if let Err(error) = run_rustfmt(Mode::Verify) { 42 if let Err(error) = run_rustfmt(Mode::Verify) {
36 panic!("{}. Please format the code by running `cargo format`", error); 43 panic!("{}. Please format the code by running `cargo format`", error);
@@ -95,7 +102,7 @@ impl TidyDocs {
95 fn visit(&mut self, path: &Path, text: &str) { 102 fn visit(&mut self, path: &Path, text: &str) {
96 // Test hopefully don't really need comments, and for assists we already 103 // Test hopefully don't really need comments, and for assists we already
97 // have special comments which are source of doc tests and user docs. 104 // have special comments which are source of doc tests and user docs.
98 if is_exclude_dir(path, &["tests", "test_data", "handlers"]) { 105 if is_exclude_dir(path, &["tests", "test_data"]) {
99 return; 106 return;
100 } 107 }
101 108
@@ -110,9 +117,12 @@ impl TidyDocs {
110 117
111 if first_line.starts_with("//!") { 118 if first_line.starts_with("//!") {
112 if first_line.contains("FIXME") { 119 if first_line.contains("FIXME") {
113 self.contains_fixme.push(path.to_path_buf()) 120 self.contains_fixme.push(path.to_path_buf());
114 } 121 }
115 } else { 122 } else {
123 if text.contains("// Feature:") || text.contains("// Assist:") {
124 return;
125 }
116 self.missing_docs.push(path.display().to_string()); 126 self.missing_docs.push(path.display().to_string());
117 } 127 }
118 128