aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock80
-rw-r--r--crates/ra_assists/src/assist_ctx.rs44
-rw-r--r--crates/ra_assists/src/assists/add_derive.rs8
-rw-r--r--crates/ra_assists/src/assists/add_explicit_type.rs7
-rw-r--r--crates/ra_assists/src/assists/add_impl.rs8
-rw-r--r--crates/ra_assists/src/assists/add_import.rs66
-rw-r--r--crates/ra_assists/src/assists/add_missing_impl_members.rs8
-rw-r--r--crates/ra_assists/src/assists/apply_demorgan.rs7
-rw-r--r--crates/ra_assists/src/assists/change_visibility.rs18
-rw-r--r--crates/ra_assists/src/assists/early_return.rs7
-rw-r--r--crates/ra_assists/src/assists/fill_match_arms.rs8
-rw-r--r--crates/ra_assists/src/assists/flip_binexpr.rs8
-rw-r--r--crates/ra_assists/src/assists/flip_comma.rs8
-rw-r--r--crates/ra_assists/src/assists/flip_trait_bound.rs8
-rw-r--r--crates/ra_assists/src/assists/inline_local_variable.rs8
-rw-r--r--crates/ra_assists/src/assists/introduce_variable.rs8
-rw-r--r--crates/ra_assists/src/assists/merge_match_arms.rs8
-rw-r--r--crates/ra_assists/src/assists/move_bounds.rs58
-rw-r--r--crates/ra_assists/src/assists/move_guard.rs14
-rw-r--r--crates/ra_assists/src/assists/raw_string.rs67
-rw-r--r--crates/ra_assists/src/assists/remove_dbg.rs8
-rw-r--r--crates/ra_assists/src/assists/replace_if_let_with_match.rs8
-rw-r--r--crates/ra_assists/src/assists/split_import.rs8
-rw-r--r--crates/ra_assists/src/doc_tests/generated.rs15
-rw-r--r--crates/ra_assists/src/lib.rs83
-rw-r--r--crates/ra_syntax/src/ast.rs24
-rw-r--r--crates/ra_syntax/src/ast/traits.rs7
-rw-r--r--docs/user/assists.md14
-rw-r--r--docs/user/features.md20
29 files changed, 299 insertions, 336 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 6f8a4431d..0206f16bc 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -108,8 +108,8 @@ version = "0.9.0"
108source = "registry+https://github.com/rust-lang/crates.io-index" 108source = "registry+https://github.com/rust-lang/crates.io-index"
109dependencies = [ 109dependencies = [
110 "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", 110 "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
111 "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", 111 "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
112 "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", 112 "serde_derive 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
113 "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", 113 "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)",
114] 114]
115 115
@@ -171,7 +171,7 @@ dependencies = [
171 "chalk-macros 0.1.1 (git+https://github.com/rust-lang/chalk.git?rev=1e5c1929364dfbb7c0c7ac0956b8250abe7c2cae)", 171 "chalk-macros 0.1.1 (git+https://github.com/rust-lang/chalk.git?rev=1e5c1929364dfbb7c0c7ac0956b8250abe7c2cae)",
172 "chalk-rust-ir 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=1e5c1929364dfbb7c0c7ac0956b8250abe7c2cae)", 172 "chalk-rust-ir 0.1.0 (git+https://github.com/rust-lang/chalk.git?rev=1e5c1929364dfbb7c0c7ac0956b8250abe7c2cae)",
173 "ena 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", 173 "ena 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)",
174 "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 174 "itertools 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
175 "petgraph 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)", 175 "petgraph 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)",
176 "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 176 "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
177] 177]
@@ -288,7 +288,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
288dependencies = [ 288dependencies = [
289 "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 289 "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
290 "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 290 "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
291 "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", 291 "syn 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
292] 292]
293 293
294[[package]] 294[[package]]
@@ -413,7 +413,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
413 413
414[[package]] 414[[package]]
415name = "getrandom" 415name = "getrandom"
416version = "0.1.12" 416version = "0.1.13"
417source = "registry+https://github.com/rust-lang/crates.io-index" 417source = "registry+https://github.com/rust-lang/crates.io-index"
418dependencies = [ 418dependencies = [
419 "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 419 "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -501,7 +501,7 @@ dependencies = [
501 "console 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", 501 "console 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
502 "difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 502 "difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
503 "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 503 "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
504 "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", 504 "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
505 "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", 505 "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)",
506 "serde_yaml 0.8.11 (registry+https://github.com/rust-lang/crates.io-index)", 506 "serde_yaml 0.8.11 (registry+https://github.com/rust-lang/crates.io-index)",
507 "uuid 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", 507 "uuid 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -517,7 +517,7 @@ dependencies = [
517 517
518[[package]] 518[[package]]
519name = "itertools" 519name = "itertools"
520version = "0.8.0" 520version = "0.8.1"
521source = "registry+https://github.com/rust-lang/crates.io-index" 521source = "registry+https://github.com/rust-lang/crates.io-index"
522dependencies = [ 522dependencies = [
523 "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", 523 "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -624,7 +624,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
624dependencies = [ 624dependencies = [
625 "crossbeam-channel 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", 625 "crossbeam-channel 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
626 "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", 626 "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
627 "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", 627 "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
628 "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", 628 "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)",
629] 629]
630 630
@@ -634,7 +634,7 @@ version = "0.61.0"
634source = "registry+https://github.com/rust-lang/crates.io-index" 634source = "registry+https://github.com/rust-lang/crates.io-index"
635dependencies = [ 635dependencies = [
636 "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 636 "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
637 "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", 637 "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
638 "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", 638 "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)",
639 "serde_repr 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 639 "serde_repr 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
640 "url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 640 "url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -811,7 +811,7 @@ dependencies = [
811 "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", 811 "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)",
812 "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 812 "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
813 "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 813 "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
814 "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", 814 "syn 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
815] 815]
816 816
817[[package]] 817[[package]]
@@ -845,7 +845,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
845dependencies = [ 845dependencies = [
846 "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 846 "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
847 "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 847 "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
848 "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", 848 "syn 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
849] 849]
850 850
851[[package]] 851[[package]]
@@ -903,7 +903,7 @@ name = "ra_assists"
903version = "0.1.0" 903version = "0.1.0"
904dependencies = [ 904dependencies = [
905 "format-buf 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 905 "format-buf 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
906 "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 906 "itertools 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
907 "join_to_string 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", 907 "join_to_string 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
908 "ra_db 0.1.0", 908 "ra_db 0.1.0",
909 "ra_fmt 0.1.0", 909 "ra_fmt 0.1.0",
@@ -970,7 +970,7 @@ dependencies = [
970name = "ra_fmt" 970name = "ra_fmt"
971version = "0.1.0" 971version = "0.1.0"
972dependencies = [ 972dependencies = [
973 "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 973 "itertools 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
974 "ra_syntax 0.1.0", 974 "ra_syntax 0.1.0",
975] 975]
976 976
@@ -1007,7 +1007,7 @@ dependencies = [
1007 "format-buf 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 1007 "format-buf 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
1008 "fst 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", 1008 "fst 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
1009 "insta 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", 1009 "insta 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
1010 "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 1010 "itertools 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
1011 "join_to_string 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", 1011 "join_to_string 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
1012 "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", 1012 "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
1013 "once_cell 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 1013 "once_cell 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1049,7 +1049,7 @@ dependencies = [
1049 "ra_vfs_glob 0.1.0", 1049 "ra_vfs_glob 0.1.0",
1050 "relative-path 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 1050 "relative-path 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
1051 "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 1051 "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
1052 "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", 1052 "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
1053 "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", 1053 "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)",
1054 "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 1054 "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
1055 "test_utils 0.1.0", 1055 "test_utils 0.1.0",
@@ -1081,7 +1081,7 @@ name = "ra_prof"
1081version = "0.1.0" 1081version = "0.1.0"
1082dependencies = [ 1082dependencies = [
1083 "backtrace 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)", 1083 "backtrace 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)",
1084 "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 1084 "itertools 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
1085 "jemalloc-ctl 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 1085 "jemalloc-ctl 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
1086 "jemallocator 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", 1086 "jemallocator 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
1087 "once_cell 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 1087 "once_cell 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1097,7 +1097,7 @@ dependencies = [
1097 "ra_cfg 0.1.0", 1097 "ra_cfg 0.1.0",
1098 "ra_db 0.1.0", 1098 "ra_db 0.1.0",
1099 "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 1099 "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
1100 "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", 1100 "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
1101 "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", 1101 "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)",
1102] 1102]
1103 1103
@@ -1106,7 +1106,7 @@ name = "ra_syntax"
1106version = "0.1.0" 1106version = "0.1.0"
1107dependencies = [ 1107dependencies = [
1108 "arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", 1108 "arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
1109 "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 1109 "itertools 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
1110 "once_cell 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 1110 "once_cell 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
1111 "ra_parser 0.1.0", 1111 "ra_parser 0.1.0",
1112 "ra_text_edit 0.1.0", 1112 "ra_text_edit 0.1.0",
@@ -1180,7 +1180,7 @@ name = "rand"
1180version = "0.7.2" 1180version = "0.7.2"
1181source = "registry+https://github.com/rust-lang/crates.io-index" 1181source = "registry+https://github.com/rust-lang/crates.io-index"
1182dependencies = [ 1182dependencies = [
1183 "getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", 1183 "getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
1184 "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", 1184 "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
1185 "rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 1185 "rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
1186 "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", 1186 "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1224,7 +1224,7 @@ name = "rand_core"
1224version = "0.5.1" 1224version = "0.5.1"
1225source = "registry+https://github.com/rust-lang/crates.io-index" 1225source = "registry+https://github.com/rust-lang/crates.io-index"
1226dependencies = [ 1226dependencies = [
1227 "getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", 1227 "getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
1228] 1228]
1229 1229
1230[[package]] 1230[[package]]
@@ -1370,7 +1370,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1370dependencies = [ 1370dependencies = [
1371 "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", 1371 "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
1372 "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 1372 "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
1373 "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", 1373 "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
1374] 1374]
1375 1375
1376[[package]] 1376[[package]]
@@ -1441,7 +1441,7 @@ dependencies = [
1441 "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 1441 "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
1442 "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 1442 "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
1443 "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 1443 "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
1444 "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", 1444 "syn 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
1445] 1445]
1446 1446
1447[[package]] 1447[[package]]
@@ -1463,7 +1463,7 @@ version = "0.9.0"
1463source = "registry+https://github.com/rust-lang/crates.io-index" 1463source = "registry+https://github.com/rust-lang/crates.io-index"
1464dependencies = [ 1464dependencies = [
1465 "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 1465 "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
1466 "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", 1466 "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
1467] 1467]
1468 1468
1469[[package]] 1469[[package]]
@@ -1473,20 +1473,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1473 1473
1474[[package]] 1474[[package]]
1475name = "serde" 1475name = "serde"
1476version = "1.0.101" 1476version = "1.0.102"
1477source = "registry+https://github.com/rust-lang/crates.io-index" 1477source = "registry+https://github.com/rust-lang/crates.io-index"
1478dependencies = [ 1478dependencies = [
1479 "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", 1479 "serde_derive 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
1480] 1480]
1481 1481
1482[[package]] 1482[[package]]
1483name = "serde_derive" 1483name = "serde_derive"
1484version = "1.0.101" 1484version = "1.0.102"
1485source = "registry+https://github.com/rust-lang/crates.io-index" 1485source = "registry+https://github.com/rust-lang/crates.io-index"
1486dependencies = [ 1486dependencies = [
1487 "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 1487 "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
1488 "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 1488 "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
1489 "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", 1489 "syn 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
1490] 1490]
1491 1491
1492[[package]] 1492[[package]]
@@ -1496,7 +1496,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1496dependencies = [ 1496dependencies = [
1497 "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", 1497 "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
1498 "ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 1498 "ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
1499 "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", 1499 "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
1500] 1500]
1501 1501
1502[[package]] 1502[[package]]
@@ -1506,7 +1506,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1506dependencies = [ 1506dependencies = [
1507 "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 1507 "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
1508 "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 1508 "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
1509 "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", 1509 "syn 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
1510] 1510]
1511 1511
1512[[package]] 1512[[package]]
@@ -1516,7 +1516,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1516dependencies = [ 1516dependencies = [
1517 "dtoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", 1517 "dtoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
1518 "linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", 1518 "linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
1519 "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", 1519 "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
1520 "yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", 1520 "yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
1521] 1521]
1522 1522
@@ -1535,7 +1535,7 @@ name = "smol_str"
1535version = "0.1.15" 1535version = "0.1.15"
1536source = "registry+https://github.com/rust-lang/crates.io-index" 1536source = "registry+https://github.com/rust-lang/crates.io-index"
1537dependencies = [ 1537dependencies = [
1538 "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", 1538 "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
1539] 1539]
1540 1540
1541[[package]] 1541[[package]]
@@ -1557,7 +1557,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1557 1557
1558[[package]] 1558[[package]]
1559name = "syn" 1559name = "syn"
1560version = "1.0.5" 1560version = "1.0.7"
1561source = "registry+https://github.com/rust-lang/crates.io-index" 1561source = "registry+https://github.com/rust-lang/crates.io-index"
1562dependencies = [ 1562dependencies = [
1563 "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 1563 "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1673,7 +1673,7 @@ dependencies = [
1673 "idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 1673 "idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
1674 "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", 1674 "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
1675 "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 1675 "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
1676 "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", 1676 "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
1677] 1677]
1678 1678
1679[[package]] 1679[[package]]
@@ -1682,7 +1682,7 @@ version = "0.8.1"
1682source = "registry+https://github.com/rust-lang/crates.io-index" 1682source = "registry+https://github.com/rust-lang/crates.io-index"
1683dependencies = [ 1683dependencies = [
1684 "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", 1684 "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
1685 "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", 1685 "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
1686] 1686]
1687 1687
1688[[package]] 1688[[package]]
@@ -1759,7 +1759,7 @@ dependencies = [
1759 "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 1759 "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
1760 "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 1760 "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
1761 "ron 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", 1761 "ron 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
1762 "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", 1762 "serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)",
1763 "walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)", 1763 "walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
1764] 1764]
1765 1765
@@ -1828,7 +1828,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1828"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" 1828"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
1829"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" 1829"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
1830"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" 1830"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
1831"checksum getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "473a1265acc8ff1e808cd0a1af8cee3c2ee5200916058a2ca113c29f2d903571" 1831"checksum getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "e7db7ca94ed4cd01190ceee0d8a8052f08a247aa1b469a7f68c6a3b71afcf407"
1832"checksum glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" 1832"checksum glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
1833"checksum globset 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "925aa2cac82d8834e2b2a4415b6f6879757fb5c0928fc445ae76461a12eed8f2" 1833"checksum globset 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "925aa2cac82d8834e2b2a4415b6f6879757fb5c0928fc445ae76461a12eed8f2"
1834"checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" 1834"checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
@@ -1839,7 +1839,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1839"checksum inotify-sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e74a1aa87c59aeff6ef2cc2fa62d41bc43f54952f55652656b18a02fd5e356c0" 1839"checksum inotify-sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e74a1aa87c59aeff6ef2cc2fa62d41bc43f54952f55652656b18a02fd5e356c0"
1840"checksum insta 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0d499dc062e841590a67230d853bce62d0abeb91304927871670b7c55c461349" 1840"checksum insta 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0d499dc062e841590a67230d853bce62d0abeb91304927871670b7c55c461349"
1841"checksum iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" 1841"checksum iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e"
1842"checksum itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358" 1842"checksum itertools 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "87fa75c9dea7b07be3138c49abbb83fd4bea199b5cdc76f9804458edc5da0d6e"
1843"checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" 1843"checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f"
1844"checksum jemalloc-ctl 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c502a5ff9dd2924f1ed32ba96e3b65735d837b4bfd978d3161b1702e66aca4b7" 1844"checksum jemalloc-ctl 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c502a5ff9dd2924f1ed32ba96e3b65735d837b4bfd978d3161b1702e66aca4b7"
1845"checksum jemalloc-sys 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0d3b9f3f5c9b31aa0f5ed3260385ac205db665baa41d49bb8338008ae94ede45" 1845"checksum jemalloc-sys 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0d3b9f3f5c9b31aa0f5ed3260385ac205db665baa41d49bb8338008ae94ede45"
@@ -1922,8 +1922,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1922"checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" 1922"checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d"
1923"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 1923"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
1924"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 1924"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
1925"checksum serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)" = "9796c9b7ba2ffe7a9ce53c2287dfc48080f4b2b362fcc245a259b3a7201119dd" 1925"checksum serde 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)" = "0c4b39bd9b0b087684013a792c59e3e07a46a01d2322518d8a1104641a0b1be0"
1926"checksum serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)" = "4b133a43a1ecd55d4086bd5b4dc6c1751c68b1bfbeba7a5040442022c7e7c02e" 1926"checksum serde_derive 1.0.102 (registry+https://github.com/rust-lang/crates.io-index)" = "ca13fc1a832f793322228923fbb3aba9f3f44444898f835d31ad1b74fa0a2bf8"
1927"checksum serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)" = "2f72eb2a68a7dc3f9a691bfda9305a1c017a6215e5a4545c258500d2099a37c2" 1927"checksum serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)" = "2f72eb2a68a7dc3f9a691bfda9305a1c017a6215e5a4545c258500d2099a37c2"
1928"checksum serde_repr 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "cd02c7587ec314570041b2754829f84d873ced14a96d1fd1823531e11db40573" 1928"checksum serde_repr 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "cd02c7587ec314570041b2754829f84d873ced14a96d1fd1823531e11db40573"
1929"checksum serde_yaml 0.8.11 (registry+https://github.com/rust-lang/crates.io-index)" = "691b17f19fc1ec9d94ec0b5864859290dff279dbd7b03f017afda54eb36c3c35" 1929"checksum serde_yaml 0.8.11 (registry+https://github.com/rust-lang/crates.io-index)" = "691b17f19fc1ec9d94ec0b5864859290dff279dbd7b03f017afda54eb36c3c35"
@@ -1932,7 +1932,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1932"checksum smol_str 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "34836c9a295c62c2ce3514471117c5cb269891e8421b2aafdd910050576c4d8b" 1932"checksum smol_str 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "34836c9a295c62c2ce3514471117c5cb269891e8421b2aafdd910050576c4d8b"
1933"checksum stacker 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "d96fc4f13a0ac088e9a3cd9af1cc8c5cc1ab5deb2145cef661267dfc9c542f8a" 1933"checksum stacker 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "d96fc4f13a0ac088e9a3cd9af1cc8c5cc1ab5deb2145cef661267dfc9c542f8a"
1934"checksum superslice 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ab16ced94dbd8a46c82fd81e3ed9a8727dac2977ea869d217bcc4ea1f122e81f" 1934"checksum superslice 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ab16ced94dbd8a46c82fd81e3ed9a8727dac2977ea869d217bcc4ea1f122e81f"
1935"checksum syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf" 1935"checksum syn 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "0e7bedb3320d0f3035594b0b723c8a28d7d336a3eda3881db79e61d676fb644c"
1936"checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" 1936"checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
1937"checksum termios 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "72b620c5ea021d75a735c943269bb07d30c9b77d6ac6b236bc8b5c496ef05625" 1937"checksum termios 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "72b620c5ea021d75a735c943269bb07d30c9b77d6ac6b236bc8b5c496ef05625"
1938"checksum text_unit 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e08bbcb7a3adbda0eb23431206b653bdad3d8dea311e72d36bf2215e27a42579" 1938"checksum text_unit 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e08bbcb7a3adbda0eb23431206b653bdad3d8dea311e72d36bf2215e27a42579"
diff --git a/crates/ra_assists/src/assist_ctx.rs b/crates/ra_assists/src/assist_ctx.rs
index c52736679..1908bdec9 100644
--- a/crates/ra_assists/src/assist_ctx.rs
+++ b/crates/ra_assists/src/assist_ctx.rs
@@ -14,8 +14,8 @@ use crate::{AssistAction, AssistId, AssistLabel};
14 14
15#[derive(Clone, Debug)] 15#[derive(Clone, Debug)]
16pub(crate) enum Assist { 16pub(crate) enum Assist {
17 Unresolved(Vec<AssistLabel>), 17 Unresolved { label: AssistLabel },
18 Resolved(Vec<(AssistLabel, AssistAction)>), 18 Resolved { label: AssistLabel, action: AssistAction },
19} 19}
20 20
21/// `AssistCtx` allows to apply an assist or check if it could be applied. 21/// `AssistCtx` allows to apply an assist or check if it could be applied.
@@ -54,7 +54,6 @@ pub(crate) struct AssistCtx<'a, DB> {
54 pub(crate) frange: FileRange, 54 pub(crate) frange: FileRange,
55 source_file: SourceFile, 55 source_file: SourceFile,
56 should_compute_edit: bool, 56 should_compute_edit: bool,
57 assist: Assist,
58} 57}
59 58
60impl<'a, DB> Clone for AssistCtx<'a, DB> { 59impl<'a, DB> Clone for AssistCtx<'a, DB> {
@@ -64,7 +63,6 @@ impl<'a, DB> Clone for AssistCtx<'a, DB> {
64 frange: self.frange, 63 frange: self.frange,
65 source_file: self.source_file.clone(), 64 source_file: self.source_file.clone(),
66 should_compute_edit: self.should_compute_edit, 65 should_compute_edit: self.should_compute_edit,
67 assist: self.assist.clone(),
68 } 66 }
69 } 67 }
70} 68}
@@ -75,36 +73,30 @@ impl<'a, DB: HirDatabase> AssistCtx<'a, DB> {
75 F: FnOnce(AssistCtx<DB>) -> T, 73 F: FnOnce(AssistCtx<DB>) -> T,
76 { 74 {
77 let parse = db.parse(frange.file_id); 75 let parse = db.parse(frange.file_id);
78 let assist =
79 if should_compute_edit { Assist::Resolved(vec![]) } else { Assist::Unresolved(vec![]) };
80 76
81 let ctx = AssistCtx { db, frange, source_file: parse.tree(), should_compute_edit, assist }; 77 let ctx = AssistCtx { db, frange, source_file: parse.tree(), should_compute_edit };
82 f(ctx) 78 f(ctx)
83 } 79 }
84 80
85 pub(crate) fn add_action( 81 pub(crate) fn add_assist(
86 &mut self, 82 self,
87 id: AssistId, 83 id: AssistId,
88 label: impl Into<String>, 84 label: impl Into<String>,
89 f: impl FnOnce(&mut AssistBuilder), 85 f: impl FnOnce(&mut AssistBuilder),
90 ) -> &mut Self { 86 ) -> Option<Assist> {
91 let label = AssistLabel { label: label.into(), id }; 87 let label = AssistLabel { label: label.into(), id };
92 match &mut self.assist { 88 let assist = if self.should_compute_edit {
93 Assist::Unresolved(labels) => labels.push(label), 89 let action = {
94 Assist::Resolved(labels_actions) => { 90 let mut edit = AssistBuilder::default();
95 let action = { 91 f(&mut edit);
96 let mut edit = AssistBuilder::default(); 92 edit.build()
97 f(&mut edit); 93 };
98 edit.build() 94 Assist::Resolved { label, action }
99 }; 95 } else {
100 labels_actions.push((label, action)); 96 Assist::Unresolved { label }
101 } 97 };
102 } 98
103 self 99 Some(assist)
104 }
105
106 pub(crate) fn build(self) -> Option<Assist> {
107 Some(self.assist)
108 } 100 }
109 101
110 pub(crate) fn token_at_offset(&self) -> TokenAtOffset<SyntaxToken> { 102 pub(crate) fn token_at_offset(&self) -> TokenAtOffset<SyntaxToken> {
diff --git a/crates/ra_assists/src/assists/add_derive.rs b/crates/ra_assists/src/assists/add_derive.rs
index d1e925b71..764b17bd8 100644
--- a/crates/ra_assists/src/assists/add_derive.rs
+++ b/crates/ra_assists/src/assists/add_derive.rs
@@ -25,10 +25,10 @@ use crate::{Assist, AssistCtx, AssistId};
25// y: u32, 25// y: u32,
26// } 26// }
27// ``` 27// ```
28pub(crate) fn add_derive(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 28pub(crate) fn add_derive(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
29 let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?; 29 let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?;
30 let node_start = derive_insertion_offset(&nominal)?; 30 let node_start = derive_insertion_offset(&nominal)?;
31 ctx.add_action(AssistId("add_derive"), "add `#[derive]`", |edit| { 31 ctx.add_assist(AssistId("add_derive"), "add `#[derive]`", |edit| {
32 let derive_attr = nominal 32 let derive_attr = nominal
33 .attrs() 33 .attrs()
34 .filter_map(|x| x.as_simple_call()) 34 .filter_map(|x| x.as_simple_call())
@@ -44,9 +44,7 @@ pub(crate) fn add_derive(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist>
44 }; 44 };
45 edit.target(nominal.syntax().text_range()); 45 edit.target(nominal.syntax().text_range());
46 edit.set_cursor(offset) 46 edit.set_cursor(offset)
47 }); 47 })
48
49 ctx.build()
50} 48}
51 49
52// Insert `derive` after doc comments. 50// Insert `derive` after doc comments.
diff --git a/crates/ra_assists/src/assists/add_explicit_type.rs b/crates/ra_assists/src/assists/add_explicit_type.rs
index ffbdc0b62..ddda1a0f2 100644
--- a/crates/ra_assists/src/assists/add_explicit_type.rs
+++ b/crates/ra_assists/src/assists/add_explicit_type.rs
@@ -21,7 +21,7 @@ use crate::{Assist, AssistCtx, AssistId};
21// let x: i32 = 92; 21// let x: i32 = 92;
22// } 22// }
23// ``` 23// ```
24pub(crate) fn add_explicit_type(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 24pub(crate) fn add_explicit_type(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
25 let stmt = ctx.find_node_at_offset::<LetStmt>()?; 25 let stmt = ctx.find_node_at_offset::<LetStmt>()?;
26 let expr = stmt.initializer()?; 26 let expr = stmt.initializer()?;
27 let pat = stmt.pat()?; 27 let pat = stmt.pat()?;
@@ -47,11 +47,10 @@ pub(crate) fn add_explicit_type(mut ctx: AssistCtx<impl HirDatabase>) -> Option<
47 return None; 47 return None;
48 } 48 }
49 49
50 ctx.add_action(AssistId("add_explicit_type"), "add explicit type", |edit| { 50 ctx.add_assist(AssistId("add_explicit_type"), "add explicit type", |edit| {
51 edit.target(pat_range); 51 edit.target(pat_range);
52 edit.insert(name_range.end(), format!(": {}", ty.display(db))); 52 edit.insert(name_range.end(), format!(": {}", ty.display(db)));
53 }); 53 })
54 ctx.build()
55} 54}
56 55
57/// Returns true if any type parameter is unknown 56/// Returns true if any type parameter is unknown
diff --git a/crates/ra_assists/src/assists/add_impl.rs b/crates/ra_assists/src/assists/add_impl.rs
index fd3588d24..7da0cfd0d 100644
--- a/crates/ra_assists/src/assists/add_impl.rs
+++ b/crates/ra_assists/src/assists/add_impl.rs
@@ -27,10 +27,10 @@ use crate::{Assist, AssistCtx, AssistId};
27// 27//
28// } 28// }
29// ``` 29// ```
30pub(crate) fn add_impl(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 30pub(crate) fn add_impl(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
31 let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?; 31 let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?;
32 let name = nominal.name()?; 32 let name = nominal.name()?;
33 ctx.add_action(AssistId("add_impl"), "add impl", |edit| { 33 ctx.add_assist(AssistId("add_impl"), "add impl", |edit| {
34 edit.target(nominal.syntax().text_range()); 34 edit.target(nominal.syntax().text_range());
35 let type_params = nominal.type_param_list(); 35 let type_params = nominal.type_param_list();
36 let start_offset = nominal.syntax().text_range().end(); 36 let start_offset = nominal.syntax().text_range().end();
@@ -54,9 +54,7 @@ pub(crate) fn add_impl(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
54 edit.set_cursor(start_offset + TextUnit::of_str(&buf)); 54 edit.set_cursor(start_offset + TextUnit::of_str(&buf));
55 buf.push_str("\n}"); 55 buf.push_str("\n}");
56 edit.insert(start_offset, buf); 56 edit.insert(start_offset, buf);
57 }); 57 })
58
59 ctx.build()
60} 58}
61 59
62#[cfg(test)] 60#[cfg(test)]
diff --git a/crates/ra_assists/src/assists/add_import.rs b/crates/ra_assists/src/assists/add_import.rs
index 149d1403f..e87fae1af 100644
--- a/crates/ra_assists/src/assists/add_import.rs
+++ b/crates/ra_assists/src/assists/add_import.rs
@@ -1,5 +1,3 @@
1//! FIXME: write short doc here
2
3use hir::{self, db::HirDatabase}; 1use hir::{self, db::HirDatabase};
4use ra_syntax::{ 2use ra_syntax::{
5 ast::{self, NameOwner}, 3 ast::{self, NameOwner},
@@ -14,9 +12,9 @@ use crate::{
14 AssistId, 12 AssistId,
15}; 13};
16 14
17// This function produces sequence of text edits into edit 15/// This function produces sequence of text edits into edit
18// to import the target path in the most appropriate scope given 16/// to import the target path in the most appropriate scope given
19// the cursor position 17/// the cursor position
20pub fn auto_import_text_edit( 18pub fn auto_import_text_edit(
21 // Ideally the position of the cursor, used to 19 // Ideally the position of the cursor, used to
22 position: &SyntaxNode, 20 position: &SyntaxNode,
@@ -39,7 +37,20 @@ pub fn auto_import_text_edit(
39 } 37 }
40} 38}
41 39
42pub(crate) fn add_import(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 40// Assist: add_import
41//
42// Adds a use statement for a given fully-qualified path.
43//
44// ```
45// fn process(map: std::collections::<|>HashMap<String, String>) {}
46// ```
47// ->
48// ```
49// use std::collections::HashMap;
50//
51// fn process(map: HashMap<String, String>) {}
52// ```
53pub(crate) fn add_import(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
43 let path: ast::Path = ctx.find_node_at_offset()?; 54 let path: ast::Path = ctx.find_node_at_offset()?;
44 // We don't want to mess with use statements 55 // We don't want to mess with use statements
45 if path.syntax().ancestors().find_map(ast::UseItem::cast).is_some() { 56 if path.syntax().ancestors().find_map(ast::UseItem::cast).is_some() {
@@ -52,38 +63,18 @@ pub(crate) fn add_import(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist>
52 return None; 63 return None;
53 } 64 }
54 65
55 if let Some(module) = path.syntax().ancestors().find_map(ast::Module::cast) { 66 let module = path.syntax().ancestors().find_map(ast::Module::cast);
56 if let (Some(item_list), Some(name)) = (module.item_list(), module.name()) { 67 let position = match module.and_then(|it| it.item_list()) {
57 ctx.add_action( 68 Some(item_list) => item_list.syntax().clone(),
58 AssistId("add_import"), 69 None => {
59 format!("import {} in mod {}", fmt_segments(&segments), name.text()), 70 let current_file = path.syntax().ancestors().find_map(ast::SourceFile::cast)?;
60 |edit| { 71 current_file.syntax().clone()
61 apply_auto_import(
62 item_list.syntax(),
63 &path,
64 &segments,
65 edit.text_edit_builder(),
66 );
67 },
68 );
69 } 72 }
70 } else { 73 };
71 let current_file = path.syntax().ancestors().find_map(ast::SourceFile::cast)?;
72 ctx.add_action(
73 AssistId("add_import"),
74 format!("import {} in the current file", fmt_segments(&segments)),
75 |edit| {
76 apply_auto_import(
77 current_file.syntax(),
78 &path,
79 &segments,
80 edit.text_edit_builder(),
81 );
82 },
83 );
84 }
85 74
86 ctx.build() 75 ctx.add_assist(AssistId("add_import"), format!("import {}", fmt_segments(&segments)), |edit| {
76 apply_auto_import(&position, &path, &segments, edit.text_edit_builder());
77 })
87} 78}
88 79
89fn collect_path_segments_raw( 80fn collect_path_segments_raw(
@@ -595,9 +586,10 @@ fn collect_hir_path_segments(path: &hir::Path) -> Option<Vec<SmolStr>> {
595 586
596#[cfg(test)] 587#[cfg(test)]
597mod tests { 588mod tests {
598 use super::*;
599 use crate::helpers::{check_assist, check_assist_not_applicable}; 589 use crate::helpers::{check_assist, check_assist_not_applicable};
600 590
591 use super::*;
592
601 #[test] 593 #[test]
602 fn test_auto_import_add_use_no_anchor() { 594 fn test_auto_import_add_use_no_anchor() {
603 check_assist( 595 check_assist(
diff --git a/crates/ra_assists/src/assists/add_missing_impl_members.rs b/crates/ra_assists/src/assists/add_missing_impl_members.rs
index 2585f3045..41de23921 100644
--- a/crates/ra_assists/src/assists/add_missing_impl_members.rs
+++ b/crates/ra_assists/src/assists/add_missing_impl_members.rs
@@ -91,7 +91,7 @@ pub(crate) fn add_missing_default_members(ctx: AssistCtx<impl HirDatabase>) -> O
91} 91}
92 92
93fn add_missing_impl_members_inner( 93fn add_missing_impl_members_inner(
94 mut ctx: AssistCtx<impl HirDatabase>, 94 ctx: AssistCtx<impl HirDatabase>,
95 mode: AddMissingImplMembersMode, 95 mode: AddMissingImplMembersMode,
96 assist_id: &'static str, 96 assist_id: &'static str,
97 label: &'static str, 97 label: &'static str,
@@ -133,7 +133,7 @@ fn add_missing_impl_members_inner(
133 return None; 133 return None;
134 } 134 }
135 135
136 ctx.add_action(AssistId(assist_id), label, |edit| { 136 ctx.add_assist(AssistId(assist_id), label, |edit| {
137 let n_existing_items = impl_item_list.impl_items().count(); 137 let n_existing_items = impl_item_list.impl_items().count();
138 let items = missing_items 138 let items = missing_items
139 .into_iter() 139 .into_iter()
@@ -150,9 +150,7 @@ fn add_missing_impl_members_inner(
150 150
151 edit.replace_ast(impl_item_list, new_impl_item_list); 151 edit.replace_ast(impl_item_list, new_impl_item_list);
152 edit.set_cursor(cursor_position); 152 edit.set_cursor(cursor_position);
153 }); 153 })
154
155 ctx.build()
156} 154}
157 155
158fn add_body(fn_def: ast::FnDef) -> ast::FnDef { 156fn add_body(fn_def: ast::FnDef) -> ast::FnDef {
diff --git a/crates/ra_assists/src/assists/apply_demorgan.rs b/crates/ra_assists/src/assists/apply_demorgan.rs
index 8d5984a58..068da1774 100644
--- a/crates/ra_assists/src/assists/apply_demorgan.rs
+++ b/crates/ra_assists/src/assists/apply_demorgan.rs
@@ -23,7 +23,7 @@ use crate::{Assist, AssistCtx, AssistId};
23// if !(x == 4 && y) {} 23// if !(x == 4 && y) {}
24// } 24// }
25// ``` 25// ```
26pub(crate) fn apply_demorgan(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 26pub(crate) fn apply_demorgan(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
27 let expr = ctx.find_node_at_offset::<ast::BinExpr>()?; 27 let expr = ctx.find_node_at_offset::<ast::BinExpr>()?;
28 let op = expr.op_kind()?; 28 let op = expr.op_kind()?;
29 let op_range = expr.op_token()?.text_range(); 29 let op_range = expr.op_token()?.text_range();
@@ -39,13 +39,12 @@ pub(crate) fn apply_demorgan(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Ass
39 let not_lhs = undo_negation(lhs)?; 39 let not_lhs = undo_negation(lhs)?;
40 let not_rhs = undo_negation(rhs)?; 40 let not_rhs = undo_negation(rhs)?;
41 41
42 ctx.add_action(AssistId("apply_demorgan"), "apply demorgan's law", |edit| { 42 ctx.add_assist(AssistId("apply_demorgan"), "apply demorgan's law", |edit| {
43 edit.target(op_range); 43 edit.target(op_range);
44 edit.replace(op_range, opposite_op); 44 edit.replace(op_range, opposite_op);
45 edit.replace(lhs_range, format!("!({}", not_lhs)); 45 edit.replace(lhs_range, format!("!({}", not_lhs));
46 edit.replace(rhs_range, format!("{})", not_rhs)); 46 edit.replace(rhs_range, format!("{})", not_rhs));
47 }); 47 })
48 ctx.build()
49} 48}
50 49
51// Return the opposite text for a given logical operator, if it makes sense 50// Return the opposite text for a given logical operator, if it makes sense
diff --git a/crates/ra_assists/src/assists/change_visibility.rs b/crates/ra_assists/src/assists/change_visibility.rs
index 770ea04fa..132c9dc1d 100644
--- a/crates/ra_assists/src/assists/change_visibility.rs
+++ b/crates/ra_assists/src/assists/change_visibility.rs
@@ -29,7 +29,7 @@ pub(crate) fn change_visibility(ctx: AssistCtx<impl HirDatabase>) -> Option<Assi
29 add_vis(ctx) 29 add_vis(ctx)
30} 30}
31 31
32fn add_vis(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 32fn add_vis(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
33 let item_keyword = ctx.token_at_offset().find(|leaf| match leaf.kind() { 33 let item_keyword = ctx.token_at_offset().find(|leaf| match leaf.kind() {
34 T![fn] | T![mod] | T![struct] | T![enum] | T![trait] => true, 34 T![fn] | T![mod] | T![struct] | T![enum] | T![trait] => true,
35 _ => false, 35 _ => false,
@@ -57,13 +57,11 @@ fn add_vis(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
57 (vis_offset(field.syntax()), ident.text_range()) 57 (vis_offset(field.syntax()), ident.text_range())
58 }; 58 };
59 59
60 ctx.add_action(AssistId("change_visibility"), "make pub(crate)", |edit| { 60 ctx.add_assist(AssistId("change_visibility"), "make pub(crate)", |edit| {
61 edit.target(target); 61 edit.target(target);
62 edit.insert(offset, "pub(crate) "); 62 edit.insert(offset, "pub(crate) ");
63 edit.set_cursor(offset); 63 edit.set_cursor(offset);
64 }); 64 })
65
66 ctx.build()
67} 65}
68 66
69fn vis_offset(node: &SyntaxNode) -> TextUnit { 67fn vis_offset(node: &SyntaxNode) -> TextUnit {
@@ -77,24 +75,20 @@ fn vis_offset(node: &SyntaxNode) -> TextUnit {
77 .unwrap_or_else(|| node.text_range().start()) 75 .unwrap_or_else(|| node.text_range().start())
78} 76}
79 77
80fn change_vis(mut ctx: AssistCtx<impl HirDatabase>, vis: ast::Visibility) -> Option<Assist> { 78fn change_vis(ctx: AssistCtx<impl HirDatabase>, vis: ast::Visibility) -> Option<Assist> {
81 if vis.syntax().text() == "pub" { 79 if vis.syntax().text() == "pub" {
82 ctx.add_action(AssistId("change_visibility"), "change to pub(crate)", |edit| { 80 return ctx.add_assist(AssistId("change_visibility"), "change to pub(crate)", |edit| {
83 edit.target(vis.syntax().text_range()); 81 edit.target(vis.syntax().text_range());
84 edit.replace(vis.syntax().text_range(), "pub(crate)"); 82 edit.replace(vis.syntax().text_range(), "pub(crate)");
85 edit.set_cursor(vis.syntax().text_range().start()) 83 edit.set_cursor(vis.syntax().text_range().start())
86 }); 84 });
87
88 return ctx.build();
89 } 85 }
90 if vis.syntax().text() == "pub(crate)" { 86 if vis.syntax().text() == "pub(crate)" {
91 ctx.add_action(AssistId("change_visibility"), "change to pub", |edit| { 87 return ctx.add_assist(AssistId("change_visibility"), "change to pub", |edit| {
92 edit.target(vis.syntax().text_range()); 88 edit.target(vis.syntax().text_range());
93 edit.replace(vis.syntax().text_range(), "pub"); 89 edit.replace(vis.syntax().text_range(), "pub");
94 edit.set_cursor(vis.syntax().text_range().start()); 90 edit.set_cursor(vis.syntax().text_range().start());
95 }); 91 });
96
97 return ctx.build();
98 } 92 }
99 None 93 None
100} 94}
diff --git a/crates/ra_assists/src/assists/early_return.rs b/crates/ra_assists/src/assists/early_return.rs
index 75822dc65..e839d831e 100644
--- a/crates/ra_assists/src/assists/early_return.rs
+++ b/crates/ra_assists/src/assists/early_return.rs
@@ -35,7 +35,7 @@ use crate::{
35// bar(); 35// bar();
36// } 36// }
37// ``` 37// ```
38pub(crate) fn convert_to_guarded_return(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 38pub(crate) fn convert_to_guarded_return(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
39 let if_expr: ast::IfExpr = ctx.find_node_at_offset()?; 39 let if_expr: ast::IfExpr = ctx.find_node_at_offset()?;
40 let expr = if_expr.condition()?.expr()?; 40 let expr = if_expr.condition()?.expr()?;
41 let then_block = if_expr.then_branch()?.block()?; 41 let then_block = if_expr.then_branch()?.block()?;
@@ -75,7 +75,7 @@ pub(crate) fn convert_to_guarded_return(mut ctx: AssistCtx<impl HirDatabase>) ->
75 then_block.syntax().last_child_or_token().filter(|t| t.kind() == R_CURLY)?; 75 then_block.syntax().last_child_or_token().filter(|t| t.kind() == R_CURLY)?;
76 let cursor_position = ctx.frange.range.start(); 76 let cursor_position = ctx.frange.range.start();
77 77
78 ctx.add_action(AssistId("convert_to_guarded_return"), "convert to guarded return", |edit| { 78 ctx.add_assist(AssistId("convert_to_guarded_return"), "convert to guarded return", |edit| {
79 let if_indent_level = IndentLevel::from_node(&if_expr.syntax()); 79 let if_indent_level = IndentLevel::from_node(&if_expr.syntax());
80 let new_if_expr = 80 let new_if_expr =
81 if_indent_level.increase_indent(make::if_expression(&expr, early_expression)); 81 if_indent_level.increase_indent(make::if_expression(&expr, early_expression));
@@ -105,8 +105,7 @@ pub(crate) fn convert_to_guarded_return(mut ctx: AssistCtx<impl HirDatabase>) ->
105 edit.target(if_expr.syntax().text_range()); 105 edit.target(if_expr.syntax().text_range());
106 edit.replace_ast(parent_block, ast::Block::cast(new_block).unwrap()); 106 edit.replace_ast(parent_block, ast::Block::cast(new_block).unwrap());
107 edit.set_cursor(cursor_position); 107 edit.set_cursor(cursor_position);
108 }); 108 })
109 ctx.build()
110} 109}
111 110
112#[cfg(test)] 111#[cfg(test)]
diff --git a/crates/ra_assists/src/assists/fill_match_arms.rs b/crates/ra_assists/src/assists/fill_match_arms.rs
index c62c0efbe..2b74f355c 100644
--- a/crates/ra_assists/src/assists/fill_match_arms.rs
+++ b/crates/ra_assists/src/assists/fill_match_arms.rs
@@ -31,7 +31,7 @@ use crate::{Assist, AssistCtx, AssistId};
31// } 31// }
32// } 32// }
33// ``` 33// ```
34pub(crate) fn fill_match_arms(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 34pub(crate) fn fill_match_arms(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
35 let match_expr = ctx.find_node_at_offset::<ast::MatchExpr>()?; 35 let match_expr = ctx.find_node_at_offset::<ast::MatchExpr>()?;
36 let match_arm_list = match_expr.match_arm_list()?; 36 let match_arm_list = match_expr.match_arm_list()?;
37 37
@@ -53,7 +53,7 @@ pub(crate) fn fill_match_arms(mut ctx: AssistCtx<impl HirDatabase>) -> Option<As
53 }; 53 };
54 let variant_list = enum_def.variant_list()?; 54 let variant_list = enum_def.variant_list()?;
55 55
56 ctx.add_action(AssistId("fill_match_arms"), "fill match arms", |edit| { 56 ctx.add_assist(AssistId("fill_match_arms"), "fill match arms", |edit| {
57 let indent_level = IndentLevel::from_node(match_arm_list.syntax()); 57 let indent_level = IndentLevel::from_node(match_arm_list.syntax());
58 58
59 let new_arm_list = { 59 let new_arm_list = {
@@ -67,9 +67,7 @@ pub(crate) fn fill_match_arms(mut ctx: AssistCtx<impl HirDatabase>) -> Option<As
67 edit.target(match_expr.syntax().text_range()); 67 edit.target(match_expr.syntax().text_range());
68 edit.set_cursor(expr.syntax().text_range().start()); 68 edit.set_cursor(expr.syntax().text_range().start());
69 edit.replace_ast(match_arm_list, new_arm_list); 69 edit.replace_ast(match_arm_list, new_arm_list);
70 }); 70 })
71
72 ctx.build()
73} 71}
74 72
75fn is_trivial(arm: &ast::MatchArm) -> bool { 73fn is_trivial(arm: &ast::MatchArm) -> bool {
diff --git a/crates/ra_assists/src/assists/flip_binexpr.rs b/crates/ra_assists/src/assists/flip_binexpr.rs
index 9765d5ddd..386045eb0 100644
--- a/crates/ra_assists/src/assists/flip_binexpr.rs
+++ b/crates/ra_assists/src/assists/flip_binexpr.rs
@@ -18,7 +18,7 @@ use crate::{Assist, AssistCtx, AssistId};
18// let _ = 2 + 90; 18// let _ = 2 + 90;
19// } 19// }
20// ``` 20// ```
21pub(crate) fn flip_binexpr(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 21pub(crate) fn flip_binexpr(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
22 let expr = ctx.find_node_at_offset::<BinExpr>()?; 22 let expr = ctx.find_node_at_offset::<BinExpr>()?;
23 let lhs = expr.lhs()?.syntax().clone(); 23 let lhs = expr.lhs()?.syntax().clone();
24 let rhs = expr.rhs()?.syntax().clone(); 24 let rhs = expr.rhs()?.syntax().clone();
@@ -34,16 +34,14 @@ pub(crate) fn flip_binexpr(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assis
34 return None; 34 return None;
35 } 35 }
36 36
37 ctx.add_action(AssistId("flip_binexpr"), "flip binary expression", |edit| { 37 ctx.add_assist(AssistId("flip_binexpr"), "flip binary expression", |edit| {
38 edit.target(op_range); 38 edit.target(op_range);
39 if let FlipAction::FlipAndReplaceOp(new_op) = action { 39 if let FlipAction::FlipAndReplaceOp(new_op) = action {
40 edit.replace(op_range, new_op); 40 edit.replace(op_range, new_op);
41 } 41 }
42 edit.replace(lhs.text_range(), rhs.text()); 42 edit.replace(lhs.text_range(), rhs.text());
43 edit.replace(rhs.text_range(), lhs.text()); 43 edit.replace(rhs.text_range(), lhs.text());
44 }); 44 })
45
46 ctx.build()
47} 45}
48 46
49enum FlipAction { 47enum FlipAction {
diff --git a/crates/ra_assists/src/assists/flip_comma.rs b/crates/ra_assists/src/assists/flip_comma.rs
index 53ba8011d..9be1c1dc6 100644
--- a/crates/ra_assists/src/assists/flip_comma.rs
+++ b/crates/ra_assists/src/assists/flip_comma.rs
@@ -18,7 +18,7 @@ use crate::{Assist, AssistCtx, AssistId};
18// ((3, 4), (1, 2)); 18// ((3, 4), (1, 2));
19// } 19// }
20// ``` 20// ```
21pub(crate) fn flip_comma(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 21pub(crate) fn flip_comma(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
22 let comma = ctx.find_token_at_offset(T![,])?; 22 let comma = ctx.find_token_at_offset(T![,])?;
23 let prev = non_trivia_sibling(comma.clone().into(), Direction::Prev)?; 23 let prev = non_trivia_sibling(comma.clone().into(), Direction::Prev)?;
24 let next = non_trivia_sibling(comma.clone().into(), Direction::Next)?; 24 let next = non_trivia_sibling(comma.clone().into(), Direction::Next)?;
@@ -29,13 +29,11 @@ pub(crate) fn flip_comma(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist>
29 return None; 29 return None;
30 } 30 }
31 31
32 ctx.add_action(AssistId("flip_comma"), "flip comma", |edit| { 32 ctx.add_assist(AssistId("flip_comma"), "flip comma", |edit| {
33 edit.target(comma.text_range()); 33 edit.target(comma.text_range());
34 edit.replace(prev.text_range(), next.to_string()); 34 edit.replace(prev.text_range(), next.to_string());
35 edit.replace(next.text_range(), prev.to_string()); 35 edit.replace(next.text_range(), prev.to_string());
36 }); 36 })
37
38 ctx.build()
39} 37}
40 38
41#[cfg(test)] 39#[cfg(test)]
diff --git a/crates/ra_assists/src/assists/flip_trait_bound.rs b/crates/ra_assists/src/assists/flip_trait_bound.rs
index 1625b241f..6017b39dd 100644
--- a/crates/ra_assists/src/assists/flip_trait_bound.rs
+++ b/crates/ra_assists/src/assists/flip_trait_bound.rs
@@ -18,7 +18,7 @@ use crate::{Assist, AssistCtx, AssistId};
18// ``` 18// ```
19// fn foo<T: Copy + Clone>() { } 19// fn foo<T: Copy + Clone>() { }
20// ``` 20// ```
21pub(crate) fn flip_trait_bound(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 21pub(crate) fn flip_trait_bound(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
22 // We want to replicate the behavior of `flip_binexpr` by only suggesting 22 // We want to replicate the behavior of `flip_binexpr` by only suggesting
23 // the assist when the cursor is on a `+` 23 // the assist when the cursor is on a `+`
24 let plus = ctx.find_token_at_offset(T![+])?; 24 let plus = ctx.find_token_at_offset(T![+])?;
@@ -33,13 +33,11 @@ pub(crate) fn flip_trait_bound(mut ctx: AssistCtx<impl HirDatabase>) -> Option<A
33 non_trivia_sibling(plus.clone().into(), Direction::Next)?, 33 non_trivia_sibling(plus.clone().into(), Direction::Next)?,
34 ); 34 );
35 35
36 ctx.add_action(AssistId("flip_trait_bound"), "flip trait bound", |edit| { 36 ctx.add_assist(AssistId("flip_trait_bound"), "flip trait bound", |edit| {
37 edit.target(plus.text_range()); 37 edit.target(plus.text_range());
38 edit.replace(before.text_range(), after.to_string()); 38 edit.replace(before.text_range(), after.to_string());
39 edit.replace(after.text_range(), before.to_string()); 39 edit.replace(after.text_range(), before.to_string());
40 }); 40 })
41
42 ctx.build()
43} 41}
44 42
45#[cfg(test)] 43#[cfg(test)]
diff --git a/crates/ra_assists/src/assists/inline_local_variable.rs b/crates/ra_assists/src/assists/inline_local_variable.rs
index fe8fa2a86..f43910574 100644
--- a/crates/ra_assists/src/assists/inline_local_variable.rs
+++ b/crates/ra_assists/src/assists/inline_local_variable.rs
@@ -23,7 +23,7 @@ use crate::{Assist, AssistCtx, AssistId};
23// (1 + 2) * 4; 23// (1 + 2) * 4;
24// } 24// }
25// ``` 25// ```
26pub(crate) fn inline_local_varialbe(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 26pub(crate) fn inline_local_varialbe(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
27 let let_stmt = ctx.find_node_at_offset::<ast::LetStmt>()?; 27 let let_stmt = ctx.find_node_at_offset::<ast::LetStmt>()?;
28 let bind_pat = match let_stmt.pat()? { 28 let bind_pat = match let_stmt.pat()? {
29 ast::Pat::BindPat(pat) => pat, 29 ast::Pat::BindPat(pat) => pat,
@@ -93,7 +93,7 @@ pub(crate) fn inline_local_varialbe(mut ctx: AssistCtx<impl HirDatabase>) -> Opt
93 let init_str = initializer_expr.syntax().text().to_string(); 93 let init_str = initializer_expr.syntax().text().to_string();
94 let init_in_paren = format!("({})", &init_str); 94 let init_in_paren = format!("({})", &init_str);
95 95
96 ctx.add_action( 96 ctx.add_assist(
97 AssistId("inline_local_variable"), 97 AssistId("inline_local_variable"),
98 "inline local variable", 98 "inline local variable",
99 move |edit: &mut AssistBuilder| { 99 move |edit: &mut AssistBuilder| {
@@ -107,9 +107,7 @@ pub(crate) fn inline_local_varialbe(mut ctx: AssistCtx<impl HirDatabase>) -> Opt
107 } 107 }
108 edit.set_cursor(delete_range.start()) 108 edit.set_cursor(delete_range.start())
109 }, 109 },
110 ); 110 )
111
112 ctx.build()
113} 111}
114 112
115#[cfg(test)] 113#[cfg(test)]
diff --git a/crates/ra_assists/src/assists/introduce_variable.rs b/crates/ra_assists/src/assists/introduce_variable.rs
index 8245dc99f..0623d4475 100644
--- a/crates/ra_assists/src/assists/introduce_variable.rs
+++ b/crates/ra_assists/src/assists/introduce_variable.rs
@@ -28,7 +28,7 @@ use crate::{Assist, AssistCtx, AssistId};
28// var_name * 4; 28// var_name * 4;
29// } 29// }
30// ``` 30// ```
31pub(crate) fn introduce_variable(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 31pub(crate) fn introduce_variable(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
32 if ctx.frange.range.is_empty() { 32 if ctx.frange.range.is_empty() {
33 return None; 33 return None;
34 } 34 }
@@ -43,7 +43,7 @@ pub(crate) fn introduce_variable(mut ctx: AssistCtx<impl HirDatabase>) -> Option
43 if indent.kind() != WHITESPACE { 43 if indent.kind() != WHITESPACE {
44 return None; 44 return None;
45 } 45 }
46 ctx.add_action(AssistId("introduce_variable"), "introduce variable", move |edit| { 46 ctx.add_assist(AssistId("introduce_variable"), "introduce variable", move |edit| {
47 let mut buf = String::new(); 47 let mut buf = String::new();
48 48
49 let cursor_offset = if wrap_in_block { 49 let cursor_offset = if wrap_in_block {
@@ -88,9 +88,7 @@ pub(crate) fn introduce_variable(mut ctx: AssistCtx<impl HirDatabase>) -> Option
88 } 88 }
89 } 89 }
90 edit.set_cursor(anchor_stmt.text_range().start() + cursor_offset); 90 edit.set_cursor(anchor_stmt.text_range().start() + cursor_offset);
91 }); 91 })
92
93 ctx.build()
94} 92}
95 93
96/// Check whether the node is a valid expression which can be extracted to a variable. 94/// Check whether the node is a valid expression which can be extracted to a variable.
diff --git a/crates/ra_assists/src/assists/merge_match_arms.rs b/crates/ra_assists/src/assists/merge_match_arms.rs
index b0c4ee78b..e9f2cae91 100644
--- a/crates/ra_assists/src/assists/merge_match_arms.rs
+++ b/crates/ra_assists/src/assists/merge_match_arms.rs
@@ -26,7 +26,7 @@ use ra_syntax::ast::{AstNode, MatchArm};
26// } 26// }
27// } 27// }
28// ``` 28// ```
29pub(crate) fn merge_match_arms(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 29pub(crate) fn merge_match_arms(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
30 let current_arm = ctx.find_node_at_offset::<MatchArm>()?; 30 let current_arm = ctx.find_node_at_offset::<MatchArm>()?;
31 31
32 // We check if the following match arm matches this one. We could, but don't, 32 // We check if the following match arm matches this one. We could, but don't,
@@ -52,7 +52,7 @@ pub(crate) fn merge_match_arms(mut ctx: AssistCtx<impl HirDatabase>) -> Option<A
52 52
53 let cursor_to_end = current_arm.syntax().text_range().end() - ctx.frange.range.start(); 53 let cursor_to_end = current_arm.syntax().text_range().end() - ctx.frange.range.start();
54 54
55 ctx.add_action(AssistId("merge_match_arms"), "merge match arms", |edit| { 55 ctx.add_assist(AssistId("merge_match_arms"), "merge match arms", |edit| {
56 fn contains_placeholder(a: &MatchArm) -> bool { 56 fn contains_placeholder(a: &MatchArm) -> bool {
57 a.pats().any(|x| match x { 57 a.pats().any(|x| match x {
58 ra_syntax::ast::Pat::PlaceholderPat(..) => true, 58 ra_syntax::ast::Pat::PlaceholderPat(..) => true,
@@ -80,9 +80,7 @@ pub(crate) fn merge_match_arms(mut ctx: AssistCtx<impl HirDatabase>) -> Option<A
80 edit.target(current_arm.syntax().text_range()); 80 edit.target(current_arm.syntax().text_range());
81 edit.replace(TextRange::from_to(start, end), arm); 81 edit.replace(TextRange::from_to(start, end), arm);
82 edit.set_cursor(start + offset); 82 edit.set_cursor(start + offset);
83 }); 83 })
84
85 ctx.build()
86} 84}
87 85
88#[cfg(test)] 86#[cfg(test)]
diff --git a/crates/ra_assists/src/assists/move_bounds.rs b/crates/ra_assists/src/assists/move_bounds.rs
index edf2897c6..3145d7625 100644
--- a/crates/ra_assists/src/assists/move_bounds.rs
+++ b/crates/ra_assists/src/assists/move_bounds.rs
@@ -22,7 +22,7 @@ use crate::{Assist, AssistCtx, AssistId};
22// f(x) 22// f(x)
23// } 23// }
24// ``` 24// ```
25pub(crate) fn move_bounds_to_where_clause(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 25pub(crate) fn move_bounds_to_where_clause(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
26 let type_param_list = ctx.find_node_at_offset::<ast::TypeParamList>()?; 26 let type_param_list = ctx.find_node_at_offset::<ast::TypeParamList>()?;
27 27
28 let mut type_params = type_param_list.type_params(); 28 let mut type_params = type_param_list.type_params();
@@ -46,38 +46,30 @@ pub(crate) fn move_bounds_to_where_clause(mut ctx: AssistCtx<impl HirDatabase>)
46 _ => return None, 46 _ => return None,
47 }; 47 };
48 48
49 ctx.add_action( 49 ctx.add_assist(AssistId("move_bounds_to_where_clause"), "move_bounds_to_where_clause", |edit| {
50 AssistId("move_bounds_to_where_clause"), 50 let new_params = type_param_list
51 "move_bounds_to_where_clause", 51 .type_params()
52 |edit| { 52 .filter(|it| it.type_bound_list().is_some())
53 let new_params = type_param_list 53 .map(|type_param| {
54 .type_params() 54 let without_bounds = type_param.remove_bounds();
55 .filter(|it| it.type_bound_list().is_some()) 55 (type_param, without_bounds)
56 .map(|type_param| { 56 });
57 let without_bounds = type_param.remove_bounds(); 57
58 (type_param, without_bounds) 58 let new_type_param_list = edit::replace_descendants(&type_param_list, new_params);
59 }); 59 edit.replace_ast(type_param_list.clone(), new_type_param_list);
60 60
61 let new_type_param_list = edit::replace_descendants(&type_param_list, new_params); 61 let where_clause = {
62 edit.replace_ast(type_param_list.clone(), new_type_param_list); 62 let predicates = type_param_list.type_params().filter_map(build_predicate);
63 63 make::where_clause(predicates)
64 let where_clause = { 64 };
65 let predicates = type_param_list.type_params().filter_map(build_predicate); 65
66 make::where_clause(predicates) 66 let to_insert = match anchor.prev_sibling_or_token() {
67 }; 67 Some(ref elem) if elem.kind() == WHITESPACE => format!("{} ", where_clause.syntax()),
68 68 _ => format!(" {}", where_clause.syntax()),
69 let to_insert = match anchor.prev_sibling_or_token() { 69 };
70 Some(ref elem) if elem.kind() == WHITESPACE => { 70 edit.insert(anchor.text_range().start(), to_insert);
71 format!("{} ", where_clause.syntax()) 71 edit.target(type_param_list.syntax().text_range());
72 } 72 })
73 _ => format!(" {}", where_clause.syntax()),
74 };
75 edit.insert(anchor.text_range().start(), to_insert);
76 edit.target(type_param_list.syntax().text_range());
77 },
78 );
79
80 ctx.build()
81} 73}
82 74
83fn build_predicate(param: ast::TypeParam) -> Option<ast::WherePred> { 75fn build_predicate(param: ast::TypeParam) -> Option<ast::WherePred> {
diff --git a/crates/ra_assists/src/assists/move_guard.rs b/crates/ra_assists/src/assists/move_guard.rs
index e820a73c8..b49ec6172 100644
--- a/crates/ra_assists/src/assists/move_guard.rs
+++ b/crates/ra_assists/src/assists/move_guard.rs
@@ -32,7 +32,7 @@ use crate::{Assist, AssistCtx, AssistId};
32// } 32// }
33// } 33// }
34// ``` 34// ```
35pub(crate) fn move_guard_to_arm_body(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 35pub(crate) fn move_guard_to_arm_body(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
36 let match_arm = ctx.find_node_at_offset::<MatchArm>()?; 36 let match_arm = ctx.find_node_at_offset::<MatchArm>()?;
37 let guard = match_arm.guard()?; 37 let guard = match_arm.guard()?;
38 let space_before_guard = guard.syntax().prev_sibling_or_token(); 38 let space_before_guard = guard.syntax().prev_sibling_or_token();
@@ -41,7 +41,7 @@ pub(crate) fn move_guard_to_arm_body(mut ctx: AssistCtx<impl HirDatabase>) -> Op
41 let arm_expr = match_arm.expr()?; 41 let arm_expr = match_arm.expr()?;
42 let buf = format!("if {} {{ {} }}", guard_conditions.syntax().text(), arm_expr.syntax().text()); 42 let buf = format!("if {} {{ {} }}", guard_conditions.syntax().text(), arm_expr.syntax().text());
43 43
44 ctx.add_action(AssistId("move_guard_to_arm_body"), "move guard to arm body", |edit| { 44 ctx.add_assist(AssistId("move_guard_to_arm_body"), "move guard to arm body", |edit| {
45 edit.target(guard.syntax().text_range()); 45 edit.target(guard.syntax().text_range());
46 let offseting_amount = match space_before_guard.and_then(|it| it.into_token()) { 46 let offseting_amount = match space_before_guard.and_then(|it| it.into_token()) {
47 Some(tok) => { 47 Some(tok) => {
@@ -61,8 +61,7 @@ pub(crate) fn move_guard_to_arm_body(mut ctx: AssistCtx<impl HirDatabase>) -> Op
61 edit.set_cursor( 61 edit.set_cursor(
62 arm_expr.syntax().text_range().start() + TextUnit::from(3) - offseting_amount, 62 arm_expr.syntax().text_range().start() + TextUnit::from(3) - offseting_amount,
63 ); 63 );
64 }); 64 })
65 ctx.build()
66} 65}
67 66
68// Assist: move_arm_cond_to_match_guard 67// Assist: move_arm_cond_to_match_guard
@@ -90,7 +89,7 @@ pub(crate) fn move_guard_to_arm_body(mut ctx: AssistCtx<impl HirDatabase>) -> Op
90// } 89// }
91// } 90// }
92// ``` 91// ```
93pub(crate) fn move_arm_cond_to_match_guard(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 92pub(crate) fn move_arm_cond_to_match_guard(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
94 let match_arm: MatchArm = ctx.find_node_at_offset::<MatchArm>()?; 93 let match_arm: MatchArm = ctx.find_node_at_offset::<MatchArm>()?;
95 let last_match_pat = match_arm.pats().last()?; 94 let last_match_pat = match_arm.pats().last()?;
96 95
@@ -110,7 +109,7 @@ pub(crate) fn move_arm_cond_to_match_guard(mut ctx: AssistCtx<impl HirDatabase>)
110 109
111 let buf = format!(" if {}", cond.syntax().text()); 110 let buf = format!(" if {}", cond.syntax().text());
112 111
113 ctx.add_action( 112 ctx.add_assist(
114 AssistId("move_arm_cond_to_match_guard"), 113 AssistId("move_arm_cond_to_match_guard"),
115 "move condition to match guard", 114 "move condition to match guard",
116 |edit| { 115 |edit| {
@@ -127,8 +126,7 @@ pub(crate) fn move_arm_cond_to_match_guard(mut ctx: AssistCtx<impl HirDatabase>)
127 edit.insert(last_match_pat.syntax().text_range().end(), buf); 126 edit.insert(last_match_pat.syntax().text_range().end(), buf);
128 edit.set_cursor(last_match_pat.syntax().text_range().end() + TextUnit::from(1)); 127 edit.set_cursor(last_match_pat.syntax().text_range().end() + TextUnit::from(1));
129 }, 128 },
130 ); 129 )
131 ctx.build()
132} 130}
133 131
134#[cfg(test)] 132#[cfg(test)]
diff --git a/crates/ra_assists/src/assists/raw_string.rs b/crates/ra_assists/src/assists/raw_string.rs
index 2df48a838..6f4b66c31 100644
--- a/crates/ra_assists/src/assists/raw_string.rs
+++ b/crates/ra_assists/src/assists/raw_string.rs
@@ -22,7 +22,7 @@ use crate::{Assist, AssistCtx, AssistId};
22// r#"Hello, World!"#; 22// r#"Hello, World!"#;
23// } 23// }
24// ``` 24// ```
25pub(crate) fn make_raw_string(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 25pub(crate) fn make_raw_string(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
26 let token = ctx.find_token_at_offset(STRING)?; 26 let token = ctx.find_token_at_offset(STRING)?;
27 let text = token.text().as_str(); 27 let text = token.text().as_str();
28 let usual_string_range = find_usual_string_range(text)?; 28 let usual_string_range = find_usual_string_range(text)?;
@@ -41,7 +41,7 @@ pub(crate) fn make_raw_string(mut ctx: AssistCtx<impl HirDatabase>) -> Option<As
41 if error.is_err() { 41 if error.is_err() {
42 return None; 42 return None;
43 } 43 }
44 ctx.add_action(AssistId("make_raw_string"), "make raw string", |edit| { 44 ctx.add_assist(AssistId("make_raw_string"), "make raw string", |edit| {
45 edit.target(token.text_range()); 45 edit.target(token.text_range());
46 let max_hash_streak = count_hashes(&unescaped); 46 let max_hash_streak = count_hashes(&unescaped);
47 let mut hashes = String::with_capacity(max_hash_streak + 1); 47 let mut hashes = String::with_capacity(max_hash_streak + 1);
@@ -49,8 +49,7 @@ pub(crate) fn make_raw_string(mut ctx: AssistCtx<impl HirDatabase>) -> Option<As
49 hashes.push('#'); 49 hashes.push('#');
50 } 50 }
51 edit.replace(token.text_range(), format!("r{}\"{}\"{}", hashes, unescaped, hashes)); 51 edit.replace(token.text_range(), format!("r{}\"{}\"{}", hashes, unescaped, hashes));
52 }); 52 })
53 ctx.build()
54} 53}
55 54
56// Assist: make_usual_string 55// Assist: make_usual_string
@@ -68,11 +67,11 @@ pub(crate) fn make_raw_string(mut ctx: AssistCtx<impl HirDatabase>) -> Option<As
68// "Hello, \"World!\""; 67// "Hello, \"World!\"";
69// } 68// }
70// ``` 69// ```
71pub(crate) fn make_usual_string(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 70pub(crate) fn make_usual_string(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
72 let token = ctx.find_token_at_offset(RAW_STRING)?; 71 let token = ctx.find_token_at_offset(RAW_STRING)?;
73 let text = token.text().as_str(); 72 let text = token.text().as_str();
74 let usual_string_range = find_usual_string_range(text)?; 73 let usual_string_range = find_usual_string_range(text)?;
75 ctx.add_action(AssistId("make_usual_string"), "make usual string", |edit| { 74 ctx.add_assist(AssistId("make_usual_string"), "make usual string", |edit| {
76 edit.target(token.text_range()); 75 edit.target(token.text_range());
77 // parse inside string to escape `"` 76 // parse inside string to escape `"`
78 let start_of_inside = usual_string_range.start().to_usize() + 1; 77 let start_of_inside = usual_string_range.start().to_usize() + 1;
@@ -80,8 +79,7 @@ pub(crate) fn make_usual_string(mut ctx: AssistCtx<impl HirDatabase>) -> Option<
80 let inside_str = &text[start_of_inside..end_of_inside]; 79 let inside_str = &text[start_of_inside..end_of_inside];
81 let escaped = inside_str.escape_default().to_string(); 80 let escaped = inside_str.escape_default().to_string();
82 edit.replace(token.text_range(), format!("\"{}\"", escaped)); 81 edit.replace(token.text_range(), format!("\"{}\"", escaped));
83 }); 82 })
84 ctx.build()
85} 83}
86 84
87// Assist: add_hash 85// Assist: add_hash
@@ -99,14 +97,13 @@ pub(crate) fn make_usual_string(mut ctx: AssistCtx<impl HirDatabase>) -> Option<
99// r##"Hello, World!"##; 97// r##"Hello, World!"##;
100// } 98// }
101// ``` 99// ```
102pub(crate) fn add_hash(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 100pub(crate) fn add_hash(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
103 let token = ctx.find_token_at_offset(RAW_STRING)?; 101 let token = ctx.find_token_at_offset(RAW_STRING)?;
104 ctx.add_action(AssistId("add_hash"), "add hash to raw string", |edit| { 102 ctx.add_assist(AssistId("add_hash"), "add hash to raw string", |edit| {
105 edit.target(token.text_range()); 103 edit.target(token.text_range());
106 edit.insert(token.text_range().start() + TextUnit::of_char('r'), "#"); 104 edit.insert(token.text_range().start() + TextUnit::of_char('r'), "#");
107 edit.insert(token.text_range().end(), "#"); 105 edit.insert(token.text_range().end(), "#");
108 }); 106 })
109 ctx.build()
110} 107}
111 108
112// Assist: remove_hash 109// Assist: remove_hash
@@ -124,14 +121,14 @@ pub(crate) fn add_hash(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
124// r"Hello, World!"; 121// r"Hello, World!";
125// } 122// }
126// ``` 123// ```
127pub(crate) fn remove_hash(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 124pub(crate) fn remove_hash(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
128 let token = ctx.find_token_at_offset(RAW_STRING)?; 125 let token = ctx.find_token_at_offset(RAW_STRING)?;
129 let text = token.text().as_str(); 126 let text = token.text().as_str();
130 if text.starts_with("r\"") { 127 if text.starts_with("r\"") {
131 // no hash to remove 128 // no hash to remove
132 return None; 129 return None;
133 } 130 }
134 ctx.add_action(AssistId("remove_hash"), "remove hash from raw string", |edit| { 131 ctx.add_assist(AssistId("remove_hash"), "remove hash from raw string", |edit| {
135 edit.target(token.text_range()); 132 edit.target(token.text_range());
136 let result = &text[2..text.len() - 1]; 133 let result = &text[2..text.len() - 1];
137 let result = if result.starts_with("\"") { 134 let result = if result.starts_with("\"") {
@@ -142,8 +139,7 @@ pub(crate) fn remove_hash(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist
142 result.to_owned() 139 result.to_owned()
143 }; 140 };
144 edit.replace(token.text_range(), format!("r{}", result)); 141 edit.replace(token.text_range(), format!("r{}", result));
145 }); 142 })
146 ctx.build()
147} 143}
148 144
149fn count_hashes(s: &str) -> usize { 145fn count_hashes(s: &str) -> usize {
@@ -159,10 +155,17 @@ fn count_hashes(s: &str) -> usize {
159} 155}
160 156
161fn find_usual_string_range(s: &str) -> Option<TextRange> { 157fn find_usual_string_range(s: &str) -> Option<TextRange> {
162 Some(TextRange::from_to( 158 let left_quote = s.find('"')?;
163 TextUnit::from(s.find('"')? as u32), 159 let right_quote = s.rfind('"')?;
164 TextUnit::from(s.rfind('"')? as u32), 160 if left_quote == right_quote {
165 )) 161 // `s` only contains one quote
162 None
163 } else {
164 Some(TextRange::from_to(
165 TextUnit::from(left_quote as u32),
166 TextUnit::from(right_quote as u32),
167 ))
168 }
166} 169}
167 170
168#[cfg(test)] 171#[cfg(test)]
@@ -272,6 +275,30 @@ string"###;
272 } 275 }
273 276
274 #[test] 277 #[test]
278 fn make_raw_string_not_works_on_partial_string() {
279 check_assist_not_applicable(
280 make_raw_string,
281 r#"
282 fn f() {
283 let s = "foo<|>
284 }
285 "#,
286 )
287 }
288
289 #[test]
290 fn make_usual_string_not_works_on_partial_string() {
291 check_assist_not_applicable(
292 make_usual_string,
293 r#"
294 fn main() {
295 let s = r#"bar<|>
296 }
297 "#,
298 )
299 }
300
301 #[test]
275 fn add_hash_target() { 302 fn add_hash_target() {
276 check_assist_target( 303 check_assist_target(
277 add_hash, 304 add_hash,
diff --git a/crates/ra_assists/src/assists/remove_dbg.rs b/crates/ra_assists/src/assists/remove_dbg.rs
index 44b8de814..aedf8747f 100644
--- a/crates/ra_assists/src/assists/remove_dbg.rs
+++ b/crates/ra_assists/src/assists/remove_dbg.rs
@@ -21,7 +21,7 @@ use crate::{Assist, AssistCtx, AssistId};
21// 92; 21// 92;
22// } 22// }
23// ``` 23// ```
24pub(crate) fn remove_dbg(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 24pub(crate) fn remove_dbg(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
25 let macro_call = ctx.find_node_at_offset::<ast::MacroCall>()?; 25 let macro_call = ctx.find_node_at_offset::<ast::MacroCall>()?;
26 26
27 if !is_valid_macrocall(&macro_call, "dbg")? { 27 if !is_valid_macrocall(&macro_call, "dbg")? {
@@ -58,13 +58,11 @@ pub(crate) fn remove_dbg(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist>
58 text.slice(without_parens).to_string() 58 text.slice(without_parens).to_string()
59 }; 59 };
60 60
61 ctx.add_action(AssistId("remove_dbg"), "remove dbg!()", |edit| { 61 ctx.add_assist(AssistId("remove_dbg"), "remove dbg!()", |edit| {
62 edit.target(macro_call.syntax().text_range()); 62 edit.target(macro_call.syntax().text_range());
63 edit.replace(macro_range, macro_content); 63 edit.replace(macro_range, macro_content);
64 edit.set_cursor(cursor_pos); 64 edit.set_cursor(cursor_pos);
65 }); 65 })
66
67 ctx.build()
68} 66}
69 67
70/// Verifies that the given macro_call actually matches the given name 68/// Verifies that the given macro_call actually matches the given name
diff --git a/crates/ra_assists/src/assists/replace_if_let_with_match.rs b/crates/ra_assists/src/assists/replace_if_let_with_match.rs
index 58ef2ff20..dff84d865 100644
--- a/crates/ra_assists/src/assists/replace_if_let_with_match.rs
+++ b/crates/ra_assists/src/assists/replace_if_let_with_match.rs
@@ -31,7 +31,7 @@ use crate::{Assist, AssistCtx, AssistId};
31// } 31// }
32// } 32// }
33// ``` 33// ```
34pub(crate) fn replace_if_let_with_match(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 34pub(crate) fn replace_if_let_with_match(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
35 let if_expr: ast::IfExpr = ctx.find_node_at_offset()?; 35 let if_expr: ast::IfExpr = ctx.find_node_at_offset()?;
36 let cond = if_expr.condition()?; 36 let cond = if_expr.condition()?;
37 let pat = cond.pat()?; 37 let pat = cond.pat()?;
@@ -42,14 +42,12 @@ pub(crate) fn replace_if_let_with_match(mut ctx: AssistCtx<impl HirDatabase>) ->
42 ast::ElseBranch::IfExpr(_) => return None, 42 ast::ElseBranch::IfExpr(_) => return None,
43 }; 43 };
44 44
45 ctx.add_action(AssistId("replace_if_let_with_match"), "replace with match", |edit| { 45 ctx.add_assist(AssistId("replace_if_let_with_match"), "replace with match", |edit| {
46 let match_expr = build_match_expr(expr, pat, then_block, else_block); 46 let match_expr = build_match_expr(expr, pat, then_block, else_block);
47 edit.target(if_expr.syntax().text_range()); 47 edit.target(if_expr.syntax().text_range());
48 edit.replace_node_and_indent(if_expr.syntax(), match_expr); 48 edit.replace_node_and_indent(if_expr.syntax(), match_expr);
49 edit.set_cursor(if_expr.syntax().text_range().start()) 49 edit.set_cursor(if_expr.syntax().text_range().start())
50 }); 50 })
51
52 ctx.build()
53} 51}
54 52
55fn build_match_expr( 53fn build_match_expr(
diff --git a/crates/ra_assists/src/assists/split_import.rs b/crates/ra_assists/src/assists/split_import.rs
index 8d8a28987..5f8d6b0be 100644
--- a/crates/ra_assists/src/assists/split_import.rs
+++ b/crates/ra_assists/src/assists/split_import.rs
@@ -16,7 +16,7 @@ use crate::{Assist, AssistCtx, AssistId};
16// ``` 16// ```
17// use std::{collections::HashMap}; 17// use std::{collections::HashMap};
18// ``` 18// ```
19pub(crate) fn split_import(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 19pub(crate) fn split_import(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
20 let colon_colon = ctx.find_token_at_offset(T![::])?; 20 let colon_colon = ctx.find_token_at_offset(T![::])?;
21 let path = ast::Path::cast(colon_colon.parent())?; 21 let path = ast::Path::cast(colon_colon.parent())?;
22 let top_path = successors(Some(path), |it| it.parent_path()).last()?; 22 let top_path = successors(Some(path), |it| it.parent_path()).last()?;
@@ -32,14 +32,12 @@ pub(crate) fn split_import(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assis
32 None => top_path.syntax().text_range().end(), 32 None => top_path.syntax().text_range().end(),
33 }; 33 };
34 34
35 ctx.add_action(AssistId("split_import"), "split import", |edit| { 35 ctx.add_assist(AssistId("split_import"), "split import", |edit| {
36 edit.target(colon_colon.text_range()); 36 edit.target(colon_colon.text_range());
37 edit.insert(l_curly, "{"); 37 edit.insert(l_curly, "{");
38 edit.insert(r_curly, "}"); 38 edit.insert(r_curly, "}");
39 edit.set_cursor(l_curly + TextUnit::of_str("{")); 39 edit.set_cursor(l_curly + TextUnit::of_str("{"));
40 }); 40 })
41
42 ctx.build()
43} 41}
44 42
45#[cfg(test)] 43#[cfg(test)]
diff --git a/crates/ra_assists/src/doc_tests/generated.rs b/crates/ra_assists/src/doc_tests/generated.rs
index ebe49aecf..1bee76f59 100644
--- a/crates/ra_assists/src/doc_tests/generated.rs
+++ b/crates/ra_assists/src/doc_tests/generated.rs
@@ -142,6 +142,21 @@ impl T for () {
142} 142}
143 143
144#[test] 144#[test]
145fn doctest_add_import() {
146 check(
147 "add_import",
148 r#####"
149fn process(map: std::collections::<|>HashMap<String, String>) {}
150"#####,
151 r#####"
152use std::collections::HashMap;
153
154fn process(map: HashMap<String, String>) {}
155"#####,
156 )
157}
158
159#[test]
145fn doctest_apply_demorgan() { 160fn doctest_apply_demorgan() {
146 check( 161 check(
147 "apply_demorgan", 162 "apply_demorgan",
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index 7a1657d87..38599d4f1 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -11,7 +11,6 @@ mod marks;
11mod doc_tests; 11mod doc_tests;
12 12
13use hir::db::HirDatabase; 13use hir::db::HirDatabase;
14use itertools::Itertools;
15use ra_db::FileRange; 14use ra_db::FileRange;
16use ra_syntax::{TextRange, TextUnit}; 15use ra_syntax::{TextRange, TextUnit};
17use ra_text_edit::TextEdit; 16use ra_text_edit::TextEdit;
@@ -51,10 +50,10 @@ where
51 .iter() 50 .iter()
52 .filter_map(|f| f(ctx.clone())) 51 .filter_map(|f| f(ctx.clone()))
53 .map(|a| match a { 52 .map(|a| match a {
54 Assist::Unresolved(labels) => labels, 53 Assist::Unresolved { label } => label,
55 Assist::Resolved(..) => unreachable!(), 54 Assist::Resolved { .. } => unreachable!(),
56 }) 55 })
57 .concat() 56 .collect()
58 }) 57 })
59} 58}
60 59
@@ -73,10 +72,10 @@ where
73 .iter() 72 .iter()
74 .filter_map(|f| f(ctx.clone())) 73 .filter_map(|f| f(ctx.clone()))
75 .map(|a| match a { 74 .map(|a| match a {
76 Assist::Resolved(labels_actions) => labels_actions, 75 Assist::Resolved { label, action } => (label, action),
77 Assist::Unresolved(..) => unreachable!(), 76 Assist::Unresolved { .. } => unreachable!(),
78 }) 77 })
79 .concat(); 78 .collect::<Vec<_>>();
80 a.sort_by(|a, b| match (a.1.target, b.1.target) { 79 a.sort_by(|a, b| match (a.1.target, b.1.target) {
81 (Some(a), Some(b)) => a.len().cmp(&b.len()), 80 (Some(a), Some(b)) => a.len().cmp(&b.len()),
82 (Some(_), None) => Ordering::Less, 81 (Some(_), None) => Ordering::Less,
@@ -159,51 +158,17 @@ mod helpers {
159 before: &str, 158 before: &str,
160 after: &str, 159 after: &str,
161 ) { 160 ) {
162 check_assist_nth_action(assist, before, after, 0)
163 }
164
165 pub(crate) fn check_assist_range(
166 assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
167 before: &str,
168 after: &str,
169 ) {
170 check_assist_range_nth_action(assist, before, after, 0)
171 }
172
173 pub(crate) fn check_assist_target(
174 assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
175 before: &str,
176 target: &str,
177 ) {
178 check_assist_target_nth_action(assist, before, target, 0)
179 }
180
181 pub(crate) fn check_assist_range_target(
182 assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
183 before: &str,
184 target: &str,
185 ) {
186 check_assist_range_target_nth_action(assist, before, target, 0)
187 }
188
189 pub(crate) fn check_assist_nth_action(
190 assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
191 before: &str,
192 after: &str,
193 index: usize,
194 ) {
195 let (before_cursor_pos, before) = extract_offset(before); 161 let (before_cursor_pos, before) = extract_offset(before);
196 let (db, _source_root, file_id) = MockDatabase::with_single_file(&before); 162 let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
197 let frange = 163 let frange =
198 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) }; 164 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
199 let assist = 165 let assist =
200 AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); 166 AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable");
201 let labels_actions = match assist { 167 let action = match assist {
202 Assist::Unresolved(_) => unreachable!(), 168 Assist::Unresolved { .. } => unreachable!(),
203 Assist::Resolved(labels_actions) => labels_actions, 169 Assist::Resolved { action, .. } => action,
204 }; 170 };
205 171
206 let (_, action) = labels_actions.get(index).expect("expect assist action at index");
207 let actual = action.edit.apply(&before); 172 let actual = action.edit.apply(&before);
208 let actual_cursor_pos = match action.cursor_position { 173 let actual_cursor_pos = match action.cursor_position {
209 None => action 174 None => action
@@ -216,23 +181,21 @@ mod helpers {
216 assert_eq_text!(after, &actual); 181 assert_eq_text!(after, &actual);
217 } 182 }
218 183
219 pub(crate) fn check_assist_range_nth_action( 184 pub(crate) fn check_assist_range(
220 assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>, 185 assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
221 before: &str, 186 before: &str,
222 after: &str, 187 after: &str,
223 index: usize,
224 ) { 188 ) {
225 let (range, before) = extract_range(before); 189 let (range, before) = extract_range(before);
226 let (db, _source_root, file_id) = MockDatabase::with_single_file(&before); 190 let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
227 let frange = FileRange { file_id, range }; 191 let frange = FileRange { file_id, range };
228 let assist = 192 let assist =
229 AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); 193 AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable");
230 let labels_actions = match assist { 194 let action = match assist {
231 Assist::Unresolved(_) => unreachable!(), 195 Assist::Unresolved { .. } => unreachable!(),
232 Assist::Resolved(labels_actions) => labels_actions, 196 Assist::Resolved { action, .. } => action,
233 }; 197 };
234 198
235 let (_, action) = labels_actions.get(index).expect("expect assist action at index");
236 let mut actual = action.edit.apply(&before); 199 let mut actual = action.edit.apply(&before);
237 if let Some(pos) = action.cursor_position { 200 if let Some(pos) = action.cursor_position {
238 actual = add_cursor(&actual, pos); 201 actual = add_cursor(&actual, pos);
@@ -240,11 +203,10 @@ mod helpers {
240 assert_eq_text!(after, &actual); 203 assert_eq_text!(after, &actual);
241 } 204 }
242 205
243 pub(crate) fn check_assist_target_nth_action( 206 pub(crate) fn check_assist_target(
244 assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>, 207 assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
245 before: &str, 208 before: &str,
246 target: &str, 209 target: &str,
247 index: usize,
248 ) { 210 ) {
249 let (before_cursor_pos, before) = extract_offset(before); 211 let (before_cursor_pos, before) = extract_offset(before);
250 let (db, _source_root, file_id) = MockDatabase::with_single_file(&before); 212 let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
@@ -252,33 +214,30 @@ mod helpers {
252 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) }; 214 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
253 let assist = 215 let assist =
254 AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); 216 AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable");
255 let labels_actions = match assist { 217 let action = match assist {
256 Assist::Unresolved(_) => unreachable!(), 218 Assist::Unresolved { .. } => unreachable!(),
257 Assist::Resolved(labels_actions) => labels_actions, 219 Assist::Resolved { action, .. } => action,
258 }; 220 };
259 221
260 let (_, action) = labels_actions.get(index).expect("expect assist action at index");
261 let range = action.target.expect("expected target on action"); 222 let range = action.target.expect("expected target on action");
262 assert_eq_text!(&before[range.start().to_usize()..range.end().to_usize()], target); 223 assert_eq_text!(&before[range.start().to_usize()..range.end().to_usize()], target);
263 } 224 }
264 225
265 pub(crate) fn check_assist_range_target_nth_action( 226 pub(crate) fn check_assist_range_target(
266 assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>, 227 assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
267 before: &str, 228 before: &str,
268 target: &str, 229 target: &str,
269 index: usize,
270 ) { 230 ) {
271 let (range, before) = extract_range(before); 231 let (range, before) = extract_range(before);
272 let (db, _source_root, file_id) = MockDatabase::with_single_file(&before); 232 let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
273 let frange = FileRange { file_id, range }; 233 let frange = FileRange { file_id, range };
274 let assist = 234 let assist =
275 AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); 235 AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable");
276 let labels_actions = match assist { 236 let action = match assist {
277 Assist::Unresolved(_) => unreachable!(), 237 Assist::Unresolved { .. } => unreachable!(),
278 Assist::Resolved(labels_actions) => labels_actions, 238 Assist::Resolved { action, .. } => action,
279 }; 239 };
280 240
281 let (_, action) = labels_actions.get(index).expect("expect assist action at index");
282 let range = action.target.expect("expected target on action"); 241 let range = action.target.expect("expected target on action");
283 assert_eq_text!(&before[range.start().to_usize()..range.end().to_usize()], target); 242 assert_eq_text!(&before[range.start().to_usize()..range.end().to_usize()], target);
284 } 243 }
diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs
index 1b2ce921a..a12da5be2 100644
--- a/crates/ra_syntax/src/ast.rs
+++ b/crates/ra_syntax/src/ast.rs
@@ -173,7 +173,7 @@ fn test_doc_comment_single_line_block_strips_suffix_whitespace() {
173 .ok() 173 .ok()
174 .unwrap(); 174 .unwrap();
175 let module = file.syntax().descendants().find_map(Module::cast).unwrap(); 175 let module = file.syntax().descendants().find_map(Module::cast).unwrap();
176 assert_eq!("this is mod foo", module.doc_comment_text().unwrap()); 176 assert_eq!("this is mod foo ", module.doc_comment_text().unwrap());
177} 177}
178 178
179#[test] 179#[test]
@@ -191,7 +191,27 @@ fn test_doc_comment_multi_line_block_strips_suffix() {
191 .ok() 191 .ok()
192 .unwrap(); 192 .unwrap();
193 let module = file.syntax().descendants().find_map(Module::cast).unwrap(); 193 let module = file.syntax().descendants().find_map(Module::cast).unwrap();
194 assert_eq!(" this\n is\n mod foo", module.doc_comment_text().unwrap()); 194 assert_eq!(
195 " this\n is\n mod foo\n ",
196 module.doc_comment_text().unwrap()
197 );
198}
199
200#[test]
201fn test_comments_preserve_trailing_whitespace() {
202 let file = SourceFile::parse(
203 r#"
204/// Representation of a Realm.
205/// In the specification these are called Realm Records.
206struct Realm {}"#,
207 )
208 .ok()
209 .unwrap();
210 let def = file.syntax().descendants().find_map(StructDef::cast).unwrap();
211 assert_eq!(
212 "Representation of a Realm. \nIn the specification these are called Realm Records.",
213 def.doc_comment_text().unwrap()
214 );
195} 215}
196 216
197#[test] 217#[test]
diff --git a/crates/ra_syntax/src/ast/traits.rs b/crates/ra_syntax/src/ast/traits.rs
index f275a4955..76313684e 100644
--- a/crates/ra_syntax/src/ast/traits.rs
+++ b/crates/ra_syntax/src/ast/traits.rs
@@ -120,7 +120,7 @@ pub trait DocCommentsOwner: AstNode {
120 has_comments = true; 120 has_comments = true;
121 let prefix_len = comment.prefix().len(); 121 let prefix_len = comment.prefix().len();
122 122
123 let line = comment.text().as_str(); 123 let line: &str = comment.text().as_str();
124 124
125 // Determine if the prefix or prefix + 1 char is stripped 125 // Determine if the prefix or prefix + 1 char is stripped
126 let pos = 126 let pos =
@@ -136,7 +136,10 @@ pub trait DocCommentsOwner: AstNode {
136 line.len() 136 line.len()
137 }; 137 };
138 138
139 line[pos..end].trim_end().to_owned() 139 // Note that we do not trim the end of the line here
140 // since whitespace can have special meaning at the end
141 // of a line in markdown.
142 line[pos..end].to_owned()
140 }) 143 })
141 .join("\n"); 144 .join("\n");
142 145
diff --git a/docs/user/assists.md b/docs/user/assists.md
index b1fe44d84..303353e74 100644
--- a/docs/user/assists.md
+++ b/docs/user/assists.md
@@ -136,6 +136,20 @@ impl T for () {
136} 136}
137``` 137```
138 138
139## `add_import`
140
141Adds a use statement for a given fully-qualified path.
142
143```rust
144// BEFORE
145fn process(map: std::collections::┃HashMap<String, String>) {}
146
147// AFTER
148use std::collections::HashMap;
149
150fn process(map: HashMap<String, String>) {}
151```
152
139## `apply_demorgan` 153## `apply_demorgan`
140 154
141Apply [De Morgan's law](https://en.wikipedia.org/wiki/De_Morgan%27s_laws). 155Apply [De Morgan's law](https://en.wikipedia.org/wiki/De_Morgan%27s_laws).
diff --git a/docs/user/features.md b/docs/user/features.md
index 7ae2ca7b6..c160dd70b 100644
--- a/docs/user/features.md
+++ b/docs/user/features.md
@@ -99,24 +99,10 @@ Stop `cargo watch`
99 99
100### Assists (Code Actions) 100### Assists (Code Actions)
101 101
102These are triggered in a particular context via light bulb. We use custom code on 102Assists, or code actions, are small local refactorings, available in a particular context.
103the VS Code side to be able to position cursor. `<|>` signifies cursor 103They are usually triggered by a shortcut or by clicking a light bulb icon in the editor.
104 104
105See [assists.md](./assists.md) 105See [assists.md](./assists.md) for the list of available assists.
106
107- Import path
108
109```rust
110// before:
111impl std::fmt::Debug<|> for Foo {
112}
113
114// after:
115use std::fmt::Debug;
116
117impl Debug<|> for Foo {
118}
119```
120 106
121### Magic Completions 107### Magic Completions
122 108