diff options
130 files changed, 2975 insertions, 1971 deletions
diff --git a/Cargo.lock b/Cargo.lock index 4412b0708..c06236692 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -2,9 +2,9 @@ | |||
2 | # It is not intended for manual editing. | 2 | # It is not intended for manual editing. |
3 | [[package]] | 3 | [[package]] |
4 | name = "addr2line" | 4 | name = "addr2line" |
5 | version = "0.12.0" | 5 | version = "0.12.1" |
6 | source = "registry+https://github.com/rust-lang/crates.io-index" | 6 | source = "registry+https://github.com/rust-lang/crates.io-index" |
7 | checksum = "456d75cbb82da1ad150c8a9d97285ffcd21c9931dcb11e995903e7d75141b38b" | 7 | checksum = "a49806b9dadc843c61e7c97e72490ad7f7220ae249012fbda9ad0609457c0543" |
8 | dependencies = [ | 8 | dependencies = [ |
9 | "gimli", | 9 | "gimli", |
10 | ] | 10 | ] |
@@ -20,9 +20,9 @@ dependencies = [ | |||
20 | 20 | ||
21 | [[package]] | 21 | [[package]] |
22 | name = "anyhow" | 22 | name = "anyhow" |
23 | version = "1.0.29" | 23 | version = "1.0.31" |
24 | source = "registry+https://github.com/rust-lang/crates.io-index" | 24 | source = "registry+https://github.com/rust-lang/crates.io-index" |
25 | checksum = "dc98824304f5513bb8f862f9e5985219003de4d730689e59d8f28818283a6fe4" | 25 | checksum = "85bb70cc08ec97ca5450e6eba421deeea5f172c0fc61f78b5357b2a8e8be195f" |
26 | 26 | ||
27 | [[package]] | 27 | [[package]] |
28 | name = "anymap" | 28 | name = "anymap" |
@@ -55,9 +55,9 @@ checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" | |||
55 | 55 | ||
56 | [[package]] | 56 | [[package]] |
57 | name = "backtrace" | 57 | name = "backtrace" |
58 | version = "0.3.47" | 58 | version = "0.3.48" |
59 | source = "registry+https://github.com/rust-lang/crates.io-index" | 59 | source = "registry+https://github.com/rust-lang/crates.io-index" |
60 | checksum = "a5393cb2f40a6fae0014c9af00018e95846f3b241b331a6b7733c326d3e58108" | 60 | checksum = "0df2f85c8a2abbe3b7d7e748052fdd9b76a0458fdeb16ad4223f5eca78c7c130" |
61 | dependencies = [ | 61 | dependencies = [ |
62 | "addr2line", | 62 | "addr2line", |
63 | "cfg-if", | 63 | "cfg-if", |
@@ -101,9 +101,9 @@ dependencies = [ | |||
101 | 101 | ||
102 | [[package]] | 102 | [[package]] |
103 | name = "cc" | 103 | name = "cc" |
104 | version = "1.0.52" | 104 | version = "1.0.53" |
105 | source = "registry+https://github.com/rust-lang/crates.io-index" | 105 | source = "registry+https://github.com/rust-lang/crates.io-index" |
106 | checksum = "c3d87b23d6a92cd03af510a5ade527033f6aa6fa92161e2d5863a907d4c5e31d" | 106 | checksum = "404b1fe4f65288577753b17e3b36a04596ee784493ec249bf81c7f2d2acd751c" |
107 | 107 | ||
108 | [[package]] | 108 | [[package]] |
109 | name = "cfg-if" | 109 | name = "cfg-if" |
@@ -360,9 +360,9 @@ checksum = "86d4de0081402f5e88cdac65c8dcdcc73118c1a7a465e2a05f0da05843a8ea33" | |||
360 | 360 | ||
361 | [[package]] | 361 | [[package]] |
362 | name = "fnv" | 362 | name = "fnv" |
363 | version = "1.0.6" | 363 | version = "1.0.7" |
364 | source = "registry+https://github.com/rust-lang/crates.io-index" | 364 | source = "registry+https://github.com/rust-lang/crates.io-index" |
365 | checksum = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" | 365 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" |
366 | 366 | ||
367 | [[package]] | 367 | [[package]] |
368 | name = "fs_extra" | 368 | name = "fs_extra" |
@@ -463,9 +463,9 @@ dependencies = [ | |||
463 | 463 | ||
464 | [[package]] | 464 | [[package]] |
465 | name = "hermit-abi" | 465 | name = "hermit-abi" |
466 | version = "0.1.12" | 466 | version = "0.1.13" |
467 | source = "registry+https://github.com/rust-lang/crates.io-index" | 467 | source = "registry+https://github.com/rust-lang/crates.io-index" |
468 | checksum = "61565ff7aaace3525556587bd2dc31d4a07071957be715e63ce7b1eccf51a8f4" | 468 | checksum = "91780f809e750b0a89f5544be56617ff6b1227ee485bcb06ebe10cdf89bd3b71" |
469 | dependencies = [ | 469 | dependencies = [ |
470 | "libc", | 470 | "libc", |
471 | ] | 471 | ] |
@@ -809,9 +809,9 @@ checksum = "9cbca9424c482ee628fa549d9c812e2cd22f1180b9222c9200fdfa6eb31aecb2" | |||
809 | 809 | ||
810 | [[package]] | 810 | [[package]] |
811 | name = "once_cell" | 811 | name = "once_cell" |
812 | version = "1.3.1" | 812 | version = "1.4.0" |
813 | source = "registry+https://github.com/rust-lang/crates.io-index" | 813 | source = "registry+https://github.com/rust-lang/crates.io-index" |
814 | checksum = "b1c601810575c99596d4afc46f78a678c80105117c379eb3650cf99b8a21ce5b" | 814 | checksum = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d" |
815 | 815 | ||
816 | [[package]] | 816 | [[package]] |
817 | name = "ordermap" | 817 | name = "ordermap" |
@@ -895,9 +895,9 @@ checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" | |||
895 | 895 | ||
896 | [[package]] | 896 | [[package]] |
897 | name = "ppv-lite86" | 897 | name = "ppv-lite86" |
898 | version = "0.2.6" | 898 | version = "0.2.8" |
899 | source = "registry+https://github.com/rust-lang/crates.io-index" | 899 | source = "registry+https://github.com/rust-lang/crates.io-index" |
900 | checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" | 900 | checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" |
901 | 901 | ||
902 | [[package]] | 902 | [[package]] |
903 | name = "proc-macro-hack" | 903 | name = "proc-macro-hack" |
@@ -907,18 +907,18 @@ checksum = "0d659fe7c6d27f25e9d80a1a094c223f5246f6a6596453e09d7229bf42750b63" | |||
907 | 907 | ||
908 | [[package]] | 908 | [[package]] |
909 | name = "proc-macro2" | 909 | name = "proc-macro2" |
910 | version = "1.0.12" | 910 | version = "1.0.13" |
911 | source = "registry+https://github.com/rust-lang/crates.io-index" | 911 | source = "registry+https://github.com/rust-lang/crates.io-index" |
912 | checksum = "8872cf6f48eee44265156c111456a700ab3483686b3f96df4cf5481c89157319" | 912 | checksum = "53f5ffe53a6b28e37c9c1ce74893477864d64f74778a93a4beb43c8fa167f639" |
913 | dependencies = [ | 913 | dependencies = [ |
914 | "unicode-xid", | 914 | "unicode-xid", |
915 | ] | 915 | ] |
916 | 916 | ||
917 | [[package]] | 917 | [[package]] |
918 | name = "quote" | 918 | name = "quote" |
919 | version = "1.0.5" | 919 | version = "1.0.6" |
920 | source = "registry+https://github.com/rust-lang/crates.io-index" | 920 | source = "registry+https://github.com/rust-lang/crates.io-index" |
921 | checksum = "42934bc9c8ab0d3b273a16d8551c8f0fcff46be73276ca083ec2414c15c4ba5e" | 921 | checksum = "54a21852a652ad6f610c9510194f398ff6f8692e334fd1145fed931f7fbe44ea" |
922 | dependencies = [ | 922 | dependencies = [ |
923 | "proc-macro2", | 923 | "proc-macro2", |
924 | ] | 924 | ] |
@@ -1610,9 +1610,9 @@ checksum = "ab16ced94dbd8a46c82fd81e3ed9a8727dac2977ea869d217bcc4ea1f122e81f" | |||
1610 | 1610 | ||
1611 | [[package]] | 1611 | [[package]] |
1612 | name = "syn" | 1612 | name = "syn" |
1613 | version = "1.0.21" | 1613 | version = "1.0.22" |
1614 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1614 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1615 | checksum = "4696caa4048ac7ce2bcd2e484b3cef88c1004e41b8e945a277e2c25dc0b72060" | 1615 | checksum = "1425de3c33b0941002740a420b1a906a350b88d08b82b2c8a01035a3f9447bac" |
1616 | dependencies = [ | 1616 | dependencies = [ |
1617 | "proc-macro2", | 1617 | "proc-macro2", |
1618 | "quote", | 1618 | "quote", |
diff --git a/crates/ra_assists/src/assist_config.rs b/crates/ra_assists/src/assist_config.rs new file mode 100644 index 000000000..c0a0226fb --- /dev/null +++ b/crates/ra_assists/src/assist_config.rs | |||
@@ -0,0 +1,27 @@ | |||
1 | //! Settings for tweaking assists. | ||
2 | //! | ||
3 | //! The fun thing here is `SnippetCap` -- this type can only be created in this | ||
4 | //! module, and we use to statically check that we only produce snippet | ||
5 | //! assists if we are allowed to. | ||
6 | |||
7 | #[derive(Clone, Debug, PartialEq, Eq)] | ||
8 | pub struct AssistConfig { | ||
9 | pub snippet_cap: Option<SnippetCap>, | ||
10 | } | ||
11 | |||
12 | impl AssistConfig { | ||
13 | pub fn allow_snippets(&mut self, yes: bool) { | ||
14 | self.snippet_cap = if yes { Some(SnippetCap { _private: () }) } else { None } | ||
15 | } | ||
16 | } | ||
17 | |||
18 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] | ||
19 | pub struct SnippetCap { | ||
20 | _private: (), | ||
21 | } | ||
22 | |||
23 | impl Default for AssistConfig { | ||
24 | fn default() -> Self { | ||
25 | AssistConfig { snippet_cap: Some(SnippetCap { _private: () }) } | ||
26 | } | ||
27 | } | ||
diff --git a/crates/ra_assists/src/assist_context.rs b/crates/ra_assists/src/assist_context.rs index a680f752b..f3af70a3e 100644 --- a/crates/ra_assists/src/assist_context.rs +++ b/crates/ra_assists/src/assist_context.rs | |||
@@ -15,7 +15,10 @@ use ra_syntax::{ | |||
15 | }; | 15 | }; |
16 | use ra_text_edit::TextEditBuilder; | 16 | use ra_text_edit::TextEditBuilder; |
17 | 17 | ||
18 | use crate::{Assist, AssistId, GroupLabel, ResolvedAssist}; | 18 | use crate::{ |
19 | assist_config::{AssistConfig, SnippetCap}, | ||
20 | Assist, AssistId, GroupLabel, ResolvedAssist, | ||
21 | }; | ||
19 | 22 | ||
20 | /// `AssistContext` allows to apply an assist or check if it could be applied. | 23 | /// `AssistContext` allows to apply an assist or check if it could be applied. |
21 | /// | 24 | /// |
@@ -48,6 +51,7 @@ use crate::{Assist, AssistId, GroupLabel, ResolvedAssist}; | |||
48 | /// moment, because the LSP API is pretty awkward in this place, and it's much | 51 | /// moment, because the LSP API is pretty awkward in this place, and it's much |
49 | /// easier to just compute the edit eagerly :-) | 52 | /// easier to just compute the edit eagerly :-) |
50 | pub(crate) struct AssistContext<'a> { | 53 | pub(crate) struct AssistContext<'a> { |
54 | pub(crate) config: &'a AssistConfig, | ||
51 | pub(crate) sema: Semantics<'a, RootDatabase>, | 55 | pub(crate) sema: Semantics<'a, RootDatabase>, |
52 | pub(crate) db: &'a RootDatabase, | 56 | pub(crate) db: &'a RootDatabase, |
53 | pub(crate) frange: FileRange, | 57 | pub(crate) frange: FileRange, |
@@ -55,10 +59,14 @@ pub(crate) struct AssistContext<'a> { | |||
55 | } | 59 | } |
56 | 60 | ||
57 | impl<'a> AssistContext<'a> { | 61 | impl<'a> AssistContext<'a> { |
58 | pub fn new(sema: Semantics<'a, RootDatabase>, frange: FileRange) -> AssistContext<'a> { | 62 | pub(crate) fn new( |
63 | sema: Semantics<'a, RootDatabase>, | ||
64 | config: &'a AssistConfig, | ||
65 | frange: FileRange, | ||
66 | ) -> AssistContext<'a> { | ||
59 | let source_file = sema.parse(frange.file_id); | 67 | let source_file = sema.parse(frange.file_id); |
60 | let db = sema.db; | 68 | let db = sema.db; |
61 | AssistContext { sema, db, frange, source_file } | 69 | AssistContext { config, sema, db, frange, source_file } |
62 | } | 70 | } |
63 | 71 | ||
64 | // NB, this ignores active selection. | 72 | // NB, this ignores active selection. |
@@ -163,13 +171,13 @@ impl Assists { | |||
163 | 171 | ||
164 | pub(crate) struct AssistBuilder { | 172 | pub(crate) struct AssistBuilder { |
165 | edit: TextEditBuilder, | 173 | edit: TextEditBuilder, |
166 | cursor_position: Option<TextSize>, | ||
167 | file: FileId, | 174 | file: FileId, |
175 | is_snippet: bool, | ||
168 | } | 176 | } |
169 | 177 | ||
170 | impl AssistBuilder { | 178 | impl AssistBuilder { |
171 | pub(crate) fn new(file: FileId) -> AssistBuilder { | 179 | pub(crate) fn new(file: FileId) -> AssistBuilder { |
172 | AssistBuilder { edit: TextEditBuilder::default(), cursor_position: None, file } | 180 | AssistBuilder { edit: TextEditBuilder::default(), file, is_snippet: false } |
173 | } | 181 | } |
174 | 182 | ||
175 | /// Remove specified `range` of text. | 183 | /// Remove specified `range` of text. |
@@ -180,10 +188,30 @@ impl AssistBuilder { | |||
180 | pub(crate) fn insert(&mut self, offset: TextSize, text: impl Into<String>) { | 188 | pub(crate) fn insert(&mut self, offset: TextSize, text: impl Into<String>) { |
181 | self.edit.insert(offset, text.into()) | 189 | self.edit.insert(offset, text.into()) |
182 | } | 190 | } |
191 | /// Append specified `snippet` at the given `offset` | ||
192 | pub(crate) fn insert_snippet( | ||
193 | &mut self, | ||
194 | _cap: SnippetCap, | ||
195 | offset: TextSize, | ||
196 | snippet: impl Into<String>, | ||
197 | ) { | ||
198 | self.is_snippet = true; | ||
199 | self.insert(offset, snippet); | ||
200 | } | ||
183 | /// Replaces specified `range` of text with a given string. | 201 | /// Replaces specified `range` of text with a given string. |
184 | pub(crate) fn replace(&mut self, range: TextRange, replace_with: impl Into<String>) { | 202 | pub(crate) fn replace(&mut self, range: TextRange, replace_with: impl Into<String>) { |
185 | self.edit.replace(range, replace_with.into()) | 203 | self.edit.replace(range, replace_with.into()) |
186 | } | 204 | } |
205 | /// Replaces specified `range` of text with a given `snippet`. | ||
206 | pub(crate) fn replace_snippet( | ||
207 | &mut self, | ||
208 | _cap: SnippetCap, | ||
209 | range: TextRange, | ||
210 | snippet: impl Into<String>, | ||
211 | ) { | ||
212 | self.is_snippet = true; | ||
213 | self.replace(range, snippet); | ||
214 | } | ||
187 | pub(crate) fn replace_ast<N: AstNode>(&mut self, old: N, new: N) { | 215 | pub(crate) fn replace_ast<N: AstNode>(&mut self, old: N, new: N) { |
188 | algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit) | 216 | algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit) |
189 | } | 217 | } |
@@ -207,10 +235,6 @@ impl AssistBuilder { | |||
207 | algo::diff(&node, &new).into_text_edit(&mut self.edit) | 235 | algo::diff(&node, &new).into_text_edit(&mut self.edit) |
208 | } | 236 | } |
209 | 237 | ||
210 | /// Specify desired position of the cursor after the assist is applied. | ||
211 | pub(crate) fn set_cursor(&mut self, offset: TextSize) { | ||
212 | self.cursor_position = Some(offset) | ||
213 | } | ||
214 | // FIXME: better API | 238 | // FIXME: better API |
215 | pub(crate) fn set_file(&mut self, assist_file: FileId) { | 239 | pub(crate) fn set_file(&mut self, assist_file: FileId) { |
216 | self.file = assist_file; | 240 | self.file = assist_file; |
@@ -224,10 +248,10 @@ impl AssistBuilder { | |||
224 | 248 | ||
225 | fn finish(self, change_label: String) -> SourceChange { | 249 | fn finish(self, change_label: String) -> SourceChange { |
226 | let edit = self.edit.finish(); | 250 | let edit = self.edit.finish(); |
227 | if edit.is_empty() && self.cursor_position.is_none() { | 251 | let mut res = SingleFileChange { label: change_label, edit }.into_source_change(self.file); |
228 | panic!("Only call `add_assist` if the assist can be applied") | 252 | if self.is_snippet { |
253 | res.is_snippet = true; | ||
229 | } | 254 | } |
230 | SingleFileChange { label: change_label, edit, cursor_position: self.cursor_position } | 255 | res |
231 | .into_source_change(self.file) | ||
232 | } | 256 | } |
233 | } | 257 | } |
diff --git a/crates/ra_assists/src/handlers/add_custom_impl.rs b/crates/ra_assists/src/handlers/add_custom_impl.rs index 2baeb8607..fa70c8496 100644 --- a/crates/ra_assists/src/handlers/add_custom_impl.rs +++ b/crates/ra_assists/src/handlers/add_custom_impl.rs | |||
@@ -25,7 +25,7 @@ use crate::{ | |||
25 | // struct S; | 25 | // struct S; |
26 | // | 26 | // |
27 | // impl Debug for S { | 27 | // impl Debug for S { |
28 | // | 28 | // $0 |
29 | // } | 29 | // } |
30 | // ``` | 30 | // ``` |
31 | pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 31 | pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
@@ -52,7 +52,7 @@ pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
52 | format!("Add custom impl `{}` for `{}`", trait_token.text().as_str(), annotated_name); | 52 | format!("Add custom impl `{}` for `{}`", trait_token.text().as_str(), annotated_name); |
53 | 53 | ||
54 | let target = attr.syntax().text_range(); | 54 | let target = attr.syntax().text_range(); |
55 | acc.add(AssistId("add_custom_impl"), label, target, |edit| { | 55 | acc.add(AssistId("add_custom_impl"), label, target, |builder| { |
56 | let new_attr_input = input | 56 | let new_attr_input = input |
57 | .syntax() | 57 | .syntax() |
58 | .descendants_with_tokens() | 58 | .descendants_with_tokens() |
@@ -63,20 +63,11 @@ pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
63 | let has_more_derives = !new_attr_input.is_empty(); | 63 | let has_more_derives = !new_attr_input.is_empty(); |
64 | let new_attr_input = new_attr_input.iter().sep_by(", ").surround_with("(", ")").to_string(); | 64 | let new_attr_input = new_attr_input.iter().sep_by(", ").surround_with("(", ")").to_string(); |
65 | 65 | ||
66 | let mut buf = String::new(); | 66 | if has_more_derives { |
67 | buf.push_str("\n\nimpl "); | 67 | builder.replace(input.syntax().text_range(), new_attr_input); |
68 | buf.push_str(trait_token.text().as_str()); | ||
69 | buf.push_str(" for "); | ||
70 | buf.push_str(annotated_name.as_str()); | ||
71 | buf.push_str(" {\n"); | ||
72 | |||
73 | let cursor_delta = if has_more_derives { | ||
74 | let delta = input.syntax().text_range().len() - TextSize::of(&new_attr_input); | ||
75 | edit.replace(input.syntax().text_range(), new_attr_input); | ||
76 | delta | ||
77 | } else { | 68 | } else { |
78 | let attr_range = attr.syntax().text_range(); | 69 | let attr_range = attr.syntax().text_range(); |
79 | edit.delete(attr_range); | 70 | builder.delete(attr_range); |
80 | 71 | ||
81 | let line_break_range = attr | 72 | let line_break_range = attr |
82 | .syntax() | 73 | .syntax() |
@@ -84,14 +75,24 @@ pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
84 | .filter(|t| t.kind() == WHITESPACE) | 75 | .filter(|t| t.kind() == WHITESPACE) |
85 | .map(|t| t.text_range()) | 76 | .map(|t| t.text_range()) |
86 | .unwrap_or_else(|| TextRange::new(TextSize::from(0), TextSize::from(0))); | 77 | .unwrap_or_else(|| TextRange::new(TextSize::from(0), TextSize::from(0))); |
87 | edit.delete(line_break_range); | 78 | builder.delete(line_break_range); |
88 | 79 | } | |
89 | attr_range.len() + line_break_range.len() | 80 | |
90 | }; | 81 | match ctx.config.snippet_cap { |
91 | 82 | Some(cap) => { | |
92 | edit.set_cursor(start_offset + TextSize::of(&buf) - cursor_delta); | 83 | builder.insert_snippet( |
93 | buf.push_str("\n}"); | 84 | cap, |
94 | edit.insert(start_offset, buf); | 85 | start_offset, |
86 | format!("\n\nimpl {} for {} {{\n $0\n}}", trait_token, annotated_name), | ||
87 | ); | ||
88 | } | ||
89 | None => { | ||
90 | builder.insert( | ||
91 | start_offset, | ||
92 | format!("\n\nimpl {} for {} {{\n\n}}", trait_token, annotated_name), | ||
93 | ); | ||
94 | } | ||
95 | } | ||
95 | }) | 96 | }) |
96 | } | 97 | } |
97 | 98 | ||
@@ -117,7 +118,7 @@ struct Foo { | |||
117 | } | 118 | } |
118 | 119 | ||
119 | impl Debug for Foo { | 120 | impl Debug for Foo { |
120 | <|> | 121 | $0 |
121 | } | 122 | } |
122 | ", | 123 | ", |
123 | ) | 124 | ) |
@@ -139,7 +140,7 @@ pub struct Foo { | |||
139 | } | 140 | } |
140 | 141 | ||
141 | impl Debug for Foo { | 142 | impl Debug for Foo { |
142 | <|> | 143 | $0 |
143 | } | 144 | } |
144 | ", | 145 | ", |
145 | ) | 146 | ) |
@@ -158,7 +159,7 @@ struct Foo {} | |||
158 | struct Foo {} | 159 | struct Foo {} |
159 | 160 | ||
160 | impl Debug for Foo { | 161 | impl Debug for Foo { |
161 | <|> | 162 | $0 |
162 | } | 163 | } |
163 | ", | 164 | ", |
164 | ) | 165 | ) |
diff --git a/crates/ra_assists/src/handlers/add_derive.rs b/crates/ra_assists/src/handlers/add_derive.rs index fb08c19e9..b123b8498 100644 --- a/crates/ra_assists/src/handlers/add_derive.rs +++ b/crates/ra_assists/src/handlers/add_derive.rs | |||
@@ -18,31 +18,37 @@ use crate::{AssistContext, AssistId, Assists}; | |||
18 | // ``` | 18 | // ``` |
19 | // -> | 19 | // -> |
20 | // ``` | 20 | // ``` |
21 | // #[derive()] | 21 | // #[derive($0)] |
22 | // struct Point { | 22 | // struct Point { |
23 | // x: u32, | 23 | // x: u32, |
24 | // y: u32, | 24 | // y: u32, |
25 | // } | 25 | // } |
26 | // ``` | 26 | // ``` |
27 | pub(crate) fn add_derive(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 27 | pub(crate) fn add_derive(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
28 | let cap = ctx.config.snippet_cap?; | ||
28 | let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?; | 29 | let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?; |
29 | let node_start = derive_insertion_offset(&nominal)?; | 30 | let node_start = derive_insertion_offset(&nominal)?; |
30 | let target = nominal.syntax().text_range(); | 31 | let target = nominal.syntax().text_range(); |
31 | acc.add(AssistId("add_derive"), "Add `#[derive]`", target, |edit| { | 32 | acc.add(AssistId("add_derive"), "Add `#[derive]`", target, |builder| { |
32 | let derive_attr = nominal | 33 | let derive_attr = nominal |
33 | .attrs() | 34 | .attrs() |
34 | .filter_map(|x| x.as_simple_call()) | 35 | .filter_map(|x| x.as_simple_call()) |
35 | .filter(|(name, _arg)| name == "derive") | 36 | .filter(|(name, _arg)| name == "derive") |
36 | .map(|(_name, arg)| arg) | 37 | .map(|(_name, arg)| arg) |
37 | .next(); | 38 | .next(); |
38 | let offset = match derive_attr { | 39 | match derive_attr { |
39 | None => { | 40 | None => { |
40 | edit.insert(node_start, "#[derive()]\n"); | 41 | builder.insert_snippet(cap, node_start, "#[derive($0)]\n"); |
41 | node_start + TextSize::of("#[derive(") | 42 | } |
43 | Some(tt) => { | ||
44 | // Just move the cursor. | ||
45 | builder.insert_snippet( | ||
46 | cap, | ||
47 | tt.syntax().text_range().end() - TextSize::of(')'), | ||
48 | "$0", | ||
49 | ) | ||
42 | } | 50 | } |
43 | Some(tt) => tt.syntax().text_range().end() - TextSize::of(')'), | ||
44 | }; | 51 | }; |
45 | edit.set_cursor(offset) | ||
46 | }) | 52 | }) |
47 | } | 53 | } |
48 | 54 | ||
@@ -66,12 +72,12 @@ mod tests { | |||
66 | check_assist( | 72 | check_assist( |
67 | add_derive, | 73 | add_derive, |
68 | "struct Foo { a: i32, <|>}", | 74 | "struct Foo { a: i32, <|>}", |
69 | "#[derive(<|>)]\nstruct Foo { a: i32, }", | 75 | "#[derive($0)]\nstruct Foo { a: i32, }", |
70 | ); | 76 | ); |
71 | check_assist( | 77 | check_assist( |
72 | add_derive, | 78 | add_derive, |
73 | "struct Foo { <|> a: i32, }", | 79 | "struct Foo { <|> a: i32, }", |
74 | "#[derive(<|>)]\nstruct Foo { a: i32, }", | 80 | "#[derive($0)]\nstruct Foo { a: i32, }", |
75 | ); | 81 | ); |
76 | } | 82 | } |
77 | 83 | ||
@@ -80,7 +86,7 @@ mod tests { | |||
80 | check_assist( | 86 | check_assist( |
81 | add_derive, | 87 | add_derive, |
82 | "#[derive(Clone)]\nstruct Foo { a: i32<|>, }", | 88 | "#[derive(Clone)]\nstruct Foo { a: i32<|>, }", |
83 | "#[derive(Clone<|>)]\nstruct Foo { a: i32, }", | 89 | "#[derive(Clone$0)]\nstruct Foo { a: i32, }", |
84 | ); | 90 | ); |
85 | } | 91 | } |
86 | 92 | ||
@@ -96,7 +102,7 @@ struct Foo { a: i32<|>, } | |||
96 | " | 102 | " |
97 | /// `Foo` is a pretty important struct. | 103 | /// `Foo` is a pretty important struct. |
98 | /// It does stuff. | 104 | /// It does stuff. |
99 | #[derive(<|>)] | 105 | #[derive($0)] |
100 | struct Foo { a: i32, } | 106 | struct Foo { a: i32, } |
101 | ", | 107 | ", |
102 | ); | 108 | ); |
diff --git a/crates/ra_assists/src/handlers/add_explicit_type.rs b/crates/ra_assists/src/handlers/add_explicit_type.rs index 0c7d5e355..ab20c6649 100644 --- a/crates/ra_assists/src/handlers/add_explicit_type.rs +++ b/crates/ra_assists/src/handlers/add_explicit_type.rs | |||
@@ -25,9 +25,8 @@ pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Optio | |||
25 | let stmt = ctx.find_node_at_offset::<LetStmt>()?; | 25 | let stmt = ctx.find_node_at_offset::<LetStmt>()?; |
26 | let module = ctx.sema.scope(stmt.syntax()).module()?; | 26 | let module = ctx.sema.scope(stmt.syntax()).module()?; |
27 | let expr = stmt.initializer()?; | 27 | let expr = stmt.initializer()?; |
28 | let pat = stmt.pat()?; | ||
29 | // Must be a binding | 28 | // Must be a binding |
30 | let pat = match pat { | 29 | let pat = match stmt.pat()? { |
31 | ast::Pat::BindPat(bind_pat) => bind_pat, | 30 | ast::Pat::BindPat(bind_pat) => bind_pat, |
32 | _ => return None, | 31 | _ => return None, |
33 | }; | 32 | }; |
@@ -46,7 +45,7 @@ pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Optio | |||
46 | // Assist not applicable if the type has already been specified | 45 | // Assist not applicable if the type has already been specified |
47 | // and it has no placeholders | 46 | // and it has no placeholders |
48 | let ascribed_ty = stmt.ascribed_type(); | 47 | let ascribed_ty = stmt.ascribed_type(); |
49 | if let Some(ref ty) = ascribed_ty { | 48 | if let Some(ty) = &ascribed_ty { |
50 | if ty.syntax().descendants().find_map(ast::PlaceholderType::cast).is_none() { | 49 | if ty.syntax().descendants().find_map(ast::PlaceholderType::cast).is_none() { |
51 | return None; | 50 | return None; |
52 | } | 51 | } |
@@ -87,11 +86,7 @@ mod tests { | |||
87 | 86 | ||
88 | #[test] | 87 | #[test] |
89 | fn add_explicit_type_works_for_simple_expr() { | 88 | fn add_explicit_type_works_for_simple_expr() { |
90 | check_assist( | 89 | check_assist(add_explicit_type, "fn f() { let a<|> = 1; }", "fn f() { let a: i32 = 1; }"); |
91 | add_explicit_type, | ||
92 | "fn f() { let a<|> = 1; }", | ||
93 | "fn f() { let a<|>: i32 = 1; }", | ||
94 | ); | ||
95 | } | 90 | } |
96 | 91 | ||
97 | #[test] | 92 | #[test] |
@@ -99,7 +94,7 @@ mod tests { | |||
99 | check_assist( | 94 | check_assist( |
100 | add_explicit_type, | 95 | add_explicit_type, |
101 | "fn f() { let a<|>: _ = 1; }", | 96 | "fn f() { let a<|>: _ = 1; }", |
102 | "fn f() { let a<|>: i32 = 1; }", | 97 | "fn f() { let a: i32 = 1; }", |
103 | ); | 98 | ); |
104 | } | 99 | } |
105 | 100 | ||
@@ -123,7 +118,7 @@ mod tests { | |||
123 | } | 118 | } |
124 | 119 | ||
125 | fn f() { | 120 | fn f() { |
126 | let a<|>: Option<i32> = Option::Some(1); | 121 | let a: Option<i32> = Option::Some(1); |
127 | }"#, | 122 | }"#, |
128 | ); | 123 | ); |
129 | } | 124 | } |
@@ -133,7 +128,7 @@ mod tests { | |||
133 | check_assist( | 128 | check_assist( |
134 | add_explicit_type, | 129 | add_explicit_type, |
135 | r"macro_rules! v { () => {0u64} } fn f() { let a<|> = v!(); }", | 130 | r"macro_rules! v { () => {0u64} } fn f() { let a<|> = v!(); }", |
136 | r"macro_rules! v { () => {0u64} } fn f() { let a<|>: u64 = v!(); }", | 131 | r"macro_rules! v { () => {0u64} } fn f() { let a: u64 = v!(); }", |
137 | ); | 132 | ); |
138 | } | 133 | } |
139 | 134 | ||
@@ -141,8 +136,8 @@ mod tests { | |||
141 | fn add_explicit_type_works_for_macro_call_recursive() { | 136 | fn add_explicit_type_works_for_macro_call_recursive() { |
142 | check_assist( | 137 | check_assist( |
143 | add_explicit_type, | 138 | add_explicit_type, |
144 | "macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a<|> = v!(); }", | 139 | r#"macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a<|> = v!(); }"#, |
145 | "macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a<|>: u64 = v!(); }", | 140 | r#"macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a: u64 = v!(); }"#, |
146 | ); | 141 | ); |
147 | } | 142 | } |
148 | 143 | ||
@@ -209,7 +204,7 @@ struct Test<K, T = u8> { | |||
209 | } | 204 | } |
210 | 205 | ||
211 | fn main() { | 206 | fn main() { |
212 | let test<|>: Test<i32> = Test { t: 23, k: 33 }; | 207 | let test: Test<i32> = Test { t: 23, k: 33 }; |
213 | }"#, | 208 | }"#, |
214 | ); | 209 | ); |
215 | } | 210 | } |
diff --git a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs b/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs index 6a49b7dbd..6a675e812 100644 --- a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs +++ b/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs | |||
@@ -1,7 +1,6 @@ | |||
1 | use ra_ide_db::RootDatabase; | 1 | use ra_ide_db::RootDatabase; |
2 | use ra_syntax::ast::{self, AstNode, NameOwner}; | 2 | use ra_syntax::ast::{self, AstNode, NameOwner}; |
3 | use stdx::format_to; | 3 | use test_utils::mark; |
4 | use test_utils::tested_by; | ||
5 | 4 | ||
6 | use crate::{utils::FamousDefs, AssistContext, AssistId, Assists}; | 5 | use crate::{utils::FamousDefs, AssistContext, AssistId, Assists}; |
7 | 6 | ||
@@ -35,12 +34,12 @@ pub(crate) fn add_from_impl_for_enum(acc: &mut Assists, ctx: &AssistContext) -> | |||
35 | } | 34 | } |
36 | let field_type = field_list.fields().next()?.type_ref()?; | 35 | let field_type = field_list.fields().next()?.type_ref()?; |
37 | let path = match field_type { | 36 | let path = match field_type { |
38 | ast::TypeRef::PathType(p) => p, | 37 | ast::TypeRef::PathType(it) => it, |
39 | _ => return None, | 38 | _ => return None, |
40 | }; | 39 | }; |
41 | 40 | ||
42 | if existing_from_impl(&ctx.sema, &variant).is_some() { | 41 | if existing_from_impl(&ctx.sema, &variant).is_some() { |
43 | tested_by!(test_add_from_impl_already_exists); | 42 | mark::hit!(test_add_from_impl_already_exists); |
44 | return None; | 43 | return None; |
45 | } | 44 | } |
46 | 45 | ||
@@ -51,9 +50,7 @@ pub(crate) fn add_from_impl_for_enum(acc: &mut Assists, ctx: &AssistContext) -> | |||
51 | target, | 50 | target, |
52 | |edit| { | 51 | |edit| { |
53 | let start_offset = variant.parent_enum().syntax().text_range().end(); | 52 | let start_offset = variant.parent_enum().syntax().text_range().end(); |
54 | let mut buf = String::new(); | 53 | let buf = format!( |
55 | format_to!( | ||
56 | buf, | ||
57 | r#" | 54 | r#" |
58 | 55 | ||
59 | impl From<{0}> for {1} {{ | 56 | impl From<{0}> for {1} {{ |
@@ -93,7 +90,7 @@ fn existing_from_impl( | |||
93 | 90 | ||
94 | #[cfg(test)] | 91 | #[cfg(test)] |
95 | mod tests { | 92 | mod tests { |
96 | use test_utils::covers; | 93 | use test_utils::mark; |
97 | 94 | ||
98 | use crate::tests::{check_assist, check_assist_not_applicable}; | 95 | use crate::tests::{check_assist, check_assist_not_applicable}; |
99 | 96 | ||
@@ -104,7 +101,7 @@ mod tests { | |||
104 | check_assist( | 101 | check_assist( |
105 | add_from_impl_for_enum, | 102 | add_from_impl_for_enum, |
106 | "enum A { <|>One(u32) }", | 103 | "enum A { <|>One(u32) }", |
107 | r#"enum A { <|>One(u32) } | 104 | r#"enum A { One(u32) } |
108 | 105 | ||
109 | impl From<u32> for A { | 106 | impl From<u32> for A { |
110 | fn from(v: u32) -> Self { | 107 | fn from(v: u32) -> Self { |
@@ -119,7 +116,7 @@ impl From<u32> for A { | |||
119 | check_assist( | 116 | check_assist( |
120 | add_from_impl_for_enum, | 117 | add_from_impl_for_enum, |
121 | r#"enum A { <|>One(foo::bar::baz::Boo) }"#, | 118 | r#"enum A { <|>One(foo::bar::baz::Boo) }"#, |
122 | r#"enum A { <|>One(foo::bar::baz::Boo) } | 119 | r#"enum A { One(foo::bar::baz::Boo) } |
123 | 120 | ||
124 | impl From<foo::bar::baz::Boo> for A { | 121 | impl From<foo::bar::baz::Boo> for A { |
125 | fn from(v: foo::bar::baz::Boo) -> Self { | 122 | fn from(v: foo::bar::baz::Boo) -> Self { |
@@ -152,7 +149,7 @@ impl From<foo::bar::baz::Boo> for A { | |||
152 | 149 | ||
153 | #[test] | 150 | #[test] |
154 | fn test_add_from_impl_already_exists() { | 151 | fn test_add_from_impl_already_exists() { |
155 | covers!(test_add_from_impl_already_exists); | 152 | mark::check!(test_add_from_impl_already_exists); |
156 | check_not_applicable( | 153 | check_not_applicable( |
157 | r#" | 154 | r#" |
158 | enum A { <|>One(u32), } | 155 | enum A { <|>One(u32), } |
@@ -181,7 +178,7 @@ impl From<String> for A { | |||
181 | pub trait From<T> { | 178 | pub trait From<T> { |
182 | fn from(T) -> Self; | 179 | fn from(T) -> Self; |
183 | }"#, | 180 | }"#, |
184 | r#"enum A { <|>One(u32), Two(String), } | 181 | r#"enum A { One(u32), Two(String), } |
185 | 182 | ||
186 | impl From<u32> for A { | 183 | impl From<u32> for A { |
187 | fn from(v: u32) -> Self { | 184 | fn from(v: u32) -> Self { |
diff --git a/crates/ra_assists/src/handlers/add_function.rs b/crates/ra_assists/src/handlers/add_function.rs index de016ae4e..24f931a85 100644 --- a/crates/ra_assists/src/handlers/add_function.rs +++ b/crates/ra_assists/src/handlers/add_function.rs | |||
@@ -4,13 +4,17 @@ use ra_syntax::{ | |||
4 | ast::{ | 4 | ast::{ |
5 | self, | 5 | self, |
6 | edit::{AstNodeEdit, IndentLevel}, | 6 | edit::{AstNodeEdit, IndentLevel}, |
7 | ArgListOwner, AstNode, ModuleItemOwner, | 7 | make, ArgListOwner, AstNode, ModuleItemOwner, |
8 | }, | 8 | }, |
9 | SyntaxKind, SyntaxNode, TextSize, | 9 | SyntaxKind, SyntaxNode, TextSize, |
10 | }; | 10 | }; |
11 | use rustc_hash::{FxHashMap, FxHashSet}; | 11 | use rustc_hash::{FxHashMap, FxHashSet}; |
12 | 12 | ||
13 | use crate::{AssistContext, AssistId, Assists}; | 13 | use crate::{ |
14 | assist_config::SnippetCap, | ||
15 | utils::{render_snippet, Cursor}, | ||
16 | AssistContext, AssistId, Assists, | ||
17 | }; | ||
14 | 18 | ||
15 | // Assist: add_function | 19 | // Assist: add_function |
16 | // | 20 | // |
@@ -33,7 +37,7 @@ use crate::{AssistContext, AssistId, Assists}; | |||
33 | // } | 37 | // } |
34 | // | 38 | // |
35 | // fn bar(arg: &str, baz: Baz) { | 39 | // fn bar(arg: &str, baz: Baz) { |
36 | // todo!() | 40 | // ${0:todo!()} |
37 | // } | 41 | // } |
38 | // | 42 | // |
39 | // ``` | 43 | // ``` |
@@ -58,21 +62,40 @@ pub(crate) fn add_function(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
58 | let function_builder = FunctionBuilder::from_call(&ctx, &call, &path, target_module)?; | 62 | let function_builder = FunctionBuilder::from_call(&ctx, &call, &path, target_module)?; |
59 | 63 | ||
60 | let target = call.syntax().text_range(); | 64 | let target = call.syntax().text_range(); |
61 | acc.add(AssistId("add_function"), "Add function", target, |edit| { | 65 | acc.add(AssistId("add_function"), "Add function", target, |builder| { |
62 | let function_template = function_builder.render(); | 66 | let function_template = function_builder.render(); |
63 | edit.set_file(function_template.file); | 67 | builder.set_file(function_template.file); |
64 | edit.set_cursor(function_template.cursor_offset); | 68 | let new_fn = function_template.to_string(ctx.config.snippet_cap); |
65 | edit.insert(function_template.insert_offset, function_template.fn_def.to_string()); | 69 | match ctx.config.snippet_cap { |
70 | Some(cap) => builder.insert_snippet(cap, function_template.insert_offset, new_fn), | ||
71 | None => builder.insert(function_template.insert_offset, new_fn), | ||
72 | } | ||
66 | }) | 73 | }) |
67 | } | 74 | } |
68 | 75 | ||
69 | struct FunctionTemplate { | 76 | struct FunctionTemplate { |
70 | insert_offset: TextSize, | 77 | insert_offset: TextSize, |
71 | cursor_offset: TextSize, | 78 | placeholder_expr: ast::MacroCall, |
72 | fn_def: ast::SourceFile, | 79 | leading_ws: String, |
80 | fn_def: ast::FnDef, | ||
81 | trailing_ws: String, | ||
73 | file: FileId, | 82 | file: FileId, |
74 | } | 83 | } |
75 | 84 | ||
85 | impl FunctionTemplate { | ||
86 | fn to_string(&self, cap: Option<SnippetCap>) -> String { | ||
87 | let f = match cap { | ||
88 | Some(cap) => render_snippet( | ||
89 | cap, | ||
90 | self.fn_def.syntax(), | ||
91 | Cursor::Replace(self.placeholder_expr.syntax()), | ||
92 | ), | ||
93 | None => self.fn_def.to_string(), | ||
94 | }; | ||
95 | format!("{}{}{}", self.leading_ws, f, self.trailing_ws) | ||
96 | } | ||
97 | } | ||
98 | |||
76 | struct FunctionBuilder { | 99 | struct FunctionBuilder { |
77 | target: GeneratedFunctionTarget, | 100 | target: GeneratedFunctionTarget, |
78 | fn_name: ast::Name, | 101 | fn_name: ast::Name, |
@@ -110,35 +133,41 @@ impl FunctionBuilder { | |||
110 | } | 133 | } |
111 | 134 | ||
112 | fn render(self) -> FunctionTemplate { | 135 | fn render(self) -> FunctionTemplate { |
113 | let placeholder_expr = ast::make::expr_todo(); | 136 | let placeholder_expr = make::expr_todo(); |
114 | let fn_body = ast::make::block_expr(vec![], Some(placeholder_expr)); | 137 | let fn_body = make::block_expr(vec![], Some(placeholder_expr)); |
115 | let mut fn_def = ast::make::fn_def(self.fn_name, self.type_params, self.params, fn_body); | 138 | let visibility = if self.needs_pub { Some(make::visibility_pub_crate()) } else { None }; |
116 | if self.needs_pub { | 139 | let mut fn_def = |
117 | fn_def = ast::make::add_pub_crate_modifier(fn_def); | 140 | make::fn_def(visibility, self.fn_name, self.type_params, self.params, fn_body); |
118 | } | 141 | let leading_ws; |
119 | 142 | let trailing_ws; | |
120 | let (fn_def, insert_offset) = match self.target { | 143 | |
144 | let insert_offset = match self.target { | ||
121 | GeneratedFunctionTarget::BehindItem(it) => { | 145 | GeneratedFunctionTarget::BehindItem(it) => { |
122 | let with_leading_blank_line = ast::make::add_leading_newlines(2, fn_def); | 146 | let indent = IndentLevel::from_node(&it); |
123 | let indented = with_leading_blank_line.indent(IndentLevel::from_node(&it)); | 147 | leading_ws = format!("\n\n{}", indent); |
124 | (indented, it.text_range().end()) | 148 | fn_def = fn_def.indent(indent); |
149 | trailing_ws = String::new(); | ||
150 | it.text_range().end() | ||
125 | } | 151 | } |
126 | GeneratedFunctionTarget::InEmptyItemList(it) => { | 152 | GeneratedFunctionTarget::InEmptyItemList(it) => { |
127 | let indent_once = IndentLevel(1); | ||
128 | let indent = IndentLevel::from_node(it.syntax()); | 153 | let indent = IndentLevel::from_node(it.syntax()); |
129 | let fn_def = ast::make::add_leading_newlines(1, fn_def); | 154 | leading_ws = format!("\n{}", indent + 1); |
130 | let fn_def = fn_def.indent(indent_once); | 155 | fn_def = fn_def.indent(indent + 1); |
131 | let fn_def = ast::make::add_trailing_newlines(1, fn_def); | 156 | trailing_ws = format!("\n{}", indent); |
132 | let fn_def = fn_def.indent(indent); | 157 | it.syntax().text_range().start() + TextSize::of('{') |
133 | (fn_def, it.syntax().text_range().start() + TextSize::of('{')) | ||
134 | } | 158 | } |
135 | }; | 159 | }; |
136 | 160 | ||
137 | let placeholder_expr = | 161 | let placeholder_expr = |
138 | fn_def.syntax().descendants().find_map(ast::MacroCall::cast).unwrap(); | 162 | fn_def.syntax().descendants().find_map(ast::MacroCall::cast).unwrap(); |
139 | let cursor_offset_from_fn_start = placeholder_expr.syntax().text_range().start(); | 163 | FunctionTemplate { |
140 | let cursor_offset = insert_offset + cursor_offset_from_fn_start; | 164 | insert_offset, |
141 | FunctionTemplate { insert_offset, cursor_offset, fn_def, file: self.file } | 165 | placeholder_expr, |
166 | leading_ws, | ||
167 | fn_def, | ||
168 | trailing_ws, | ||
169 | file: self.file, | ||
170 | } | ||
142 | } | 171 | } |
143 | } | 172 | } |
144 | 173 | ||
@@ -158,7 +187,7 @@ impl GeneratedFunctionTarget { | |||
158 | 187 | ||
159 | fn fn_name(call: &ast::Path) -> Option<ast::Name> { | 188 | fn fn_name(call: &ast::Path) -> Option<ast::Name> { |
160 | let name = call.segment()?.syntax().to_string(); | 189 | let name = call.segment()?.syntax().to_string(); |
161 | Some(ast::make::name(&name)) | 190 | Some(make::name(&name)) |
162 | } | 191 | } |
163 | 192 | ||
164 | /// Computes the type variables and arguments required for the generated function | 193 | /// Computes the type variables and arguments required for the generated function |
@@ -180,8 +209,8 @@ fn fn_args( | |||
180 | }); | 209 | }); |
181 | } | 210 | } |
182 | deduplicate_arg_names(&mut arg_names); | 211 | deduplicate_arg_names(&mut arg_names); |
183 | let params = arg_names.into_iter().zip(arg_types).map(|(name, ty)| ast::make::param(name, ty)); | 212 | let params = arg_names.into_iter().zip(arg_types).map(|(name, ty)| make::param(name, ty)); |
184 | Some((None, ast::make::param_list(params))) | 213 | Some((None, make::param_list(params))) |
185 | } | 214 | } |
186 | 215 | ||
187 | /// Makes duplicate argument names unique by appending incrementing numbers. | 216 | /// Makes duplicate argument names unique by appending incrementing numbers. |
@@ -316,7 +345,7 @@ fn foo() { | |||
316 | } | 345 | } |
317 | 346 | ||
318 | fn bar() { | 347 | fn bar() { |
319 | <|>todo!() | 348 | ${0:todo!()} |
320 | } | 349 | } |
321 | ", | 350 | ", |
322 | ) | 351 | ) |
@@ -343,7 +372,7 @@ impl Foo { | |||
343 | } | 372 | } |
344 | 373 | ||
345 | fn bar() { | 374 | fn bar() { |
346 | <|>todo!() | 375 | ${0:todo!()} |
347 | } | 376 | } |
348 | ", | 377 | ", |
349 | ) | 378 | ) |
@@ -367,7 +396,7 @@ fn foo1() { | |||
367 | } | 396 | } |
368 | 397 | ||
369 | fn bar() { | 398 | fn bar() { |
370 | <|>todo!() | 399 | ${0:todo!()} |
371 | } | 400 | } |
372 | 401 | ||
373 | fn foo2() {} | 402 | fn foo2() {} |
@@ -393,7 +422,7 @@ mod baz { | |||
393 | } | 422 | } |
394 | 423 | ||
395 | fn bar() { | 424 | fn bar() { |
396 | <|>todo!() | 425 | ${0:todo!()} |
397 | } | 426 | } |
398 | } | 427 | } |
399 | ", | 428 | ", |
@@ -419,7 +448,7 @@ fn foo() { | |||
419 | } | 448 | } |
420 | 449 | ||
421 | fn bar(baz: Baz) { | 450 | fn bar(baz: Baz) { |
422 | <|>todo!() | 451 | ${0:todo!()} |
423 | } | 452 | } |
424 | ", | 453 | ", |
425 | ); | 454 | ); |
@@ -452,7 +481,7 @@ impl Baz { | |||
452 | } | 481 | } |
453 | 482 | ||
454 | fn bar(baz: Baz) { | 483 | fn bar(baz: Baz) { |
455 | <|>todo!() | 484 | ${0:todo!()} |
456 | } | 485 | } |
457 | ", | 486 | ", |
458 | ) | 487 | ) |
@@ -473,7 +502,7 @@ fn foo() { | |||
473 | } | 502 | } |
474 | 503 | ||
475 | fn bar(arg: &str) { | 504 | fn bar(arg: &str) { |
476 | <|>todo!() | 505 | ${0:todo!()} |
477 | } | 506 | } |
478 | "#, | 507 | "#, |
479 | ) | 508 | ) |
@@ -494,7 +523,7 @@ fn foo() { | |||
494 | } | 523 | } |
495 | 524 | ||
496 | fn bar(arg: char) { | 525 | fn bar(arg: char) { |
497 | <|>todo!() | 526 | ${0:todo!()} |
498 | } | 527 | } |
499 | "#, | 528 | "#, |
500 | ) | 529 | ) |
@@ -515,7 +544,7 @@ fn foo() { | |||
515 | } | 544 | } |
516 | 545 | ||
517 | fn bar(arg: i32) { | 546 | fn bar(arg: i32) { |
518 | <|>todo!() | 547 | ${0:todo!()} |
519 | } | 548 | } |
520 | ", | 549 | ", |
521 | ) | 550 | ) |
@@ -536,7 +565,7 @@ fn foo() { | |||
536 | } | 565 | } |
537 | 566 | ||
538 | fn bar(arg: u8) { | 567 | fn bar(arg: u8) { |
539 | <|>todo!() | 568 | ${0:todo!()} |
540 | } | 569 | } |
541 | ", | 570 | ", |
542 | ) | 571 | ) |
@@ -561,7 +590,7 @@ fn foo() { | |||
561 | } | 590 | } |
562 | 591 | ||
563 | fn bar(x: u8) { | 592 | fn bar(x: u8) { |
564 | <|>todo!() | 593 | ${0:todo!()} |
565 | } | 594 | } |
566 | ", | 595 | ", |
567 | ) | 596 | ) |
@@ -584,7 +613,7 @@ fn foo() { | |||
584 | } | 613 | } |
585 | 614 | ||
586 | fn bar(worble: ()) { | 615 | fn bar(worble: ()) { |
587 | <|>todo!() | 616 | ${0:todo!()} |
588 | } | 617 | } |
589 | ", | 618 | ", |
590 | ) | 619 | ) |
@@ -613,7 +642,7 @@ fn baz() { | |||
613 | } | 642 | } |
614 | 643 | ||
615 | fn bar(foo: impl Foo) { | 644 | fn bar(foo: impl Foo) { |
616 | <|>todo!() | 645 | ${0:todo!()} |
617 | } | 646 | } |
618 | ", | 647 | ", |
619 | ) | 648 | ) |
@@ -640,7 +669,7 @@ fn foo() { | |||
640 | } | 669 | } |
641 | 670 | ||
642 | fn bar(baz: &Baz) { | 671 | fn bar(baz: &Baz) { |
643 | <|>todo!() | 672 | ${0:todo!()} |
644 | } | 673 | } |
645 | ", | 674 | ", |
646 | ) | 675 | ) |
@@ -669,7 +698,7 @@ fn foo() { | |||
669 | } | 698 | } |
670 | 699 | ||
671 | fn bar(baz: Baz::Bof) { | 700 | fn bar(baz: Baz::Bof) { |
672 | <|>todo!() | 701 | ${0:todo!()} |
673 | } | 702 | } |
674 | ", | 703 | ", |
675 | ) | 704 | ) |
@@ -692,7 +721,7 @@ fn foo<T>(t: T) { | |||
692 | } | 721 | } |
693 | 722 | ||
694 | fn bar<T>(t: T) { | 723 | fn bar<T>(t: T) { |
695 | <|>todo!() | 724 | ${0:todo!()} |
696 | } | 725 | } |
697 | ", | 726 | ", |
698 | ) | 727 | ) |
@@ -723,7 +752,7 @@ fn foo() { | |||
723 | } | 752 | } |
724 | 753 | ||
725 | fn bar(arg: fn() -> Baz) { | 754 | fn bar(arg: fn() -> Baz) { |
726 | <|>todo!() | 755 | ${0:todo!()} |
727 | } | 756 | } |
728 | ", | 757 | ", |
729 | ) | 758 | ) |
@@ -748,7 +777,7 @@ fn foo() { | |||
748 | } | 777 | } |
749 | 778 | ||
750 | fn bar(closure: impl Fn(i64) -> i64) { | 779 | fn bar(closure: impl Fn(i64) -> i64) { |
751 | <|>todo!() | 780 | ${0:todo!()} |
752 | } | 781 | } |
753 | ", | 782 | ", |
754 | ) | 783 | ) |
@@ -769,7 +798,7 @@ fn foo() { | |||
769 | } | 798 | } |
770 | 799 | ||
771 | fn bar(baz: ()) { | 800 | fn bar(baz: ()) { |
772 | <|>todo!() | 801 | ${0:todo!()} |
773 | } | 802 | } |
774 | ", | 803 | ", |
775 | ) | 804 | ) |
@@ -794,7 +823,7 @@ fn foo() { | |||
794 | } | 823 | } |
795 | 824 | ||
796 | fn bar(baz_1: Baz, baz_2: Baz) { | 825 | fn bar(baz_1: Baz, baz_2: Baz) { |
797 | <|>todo!() | 826 | ${0:todo!()} |
798 | } | 827 | } |
799 | ", | 828 | ", |
800 | ) | 829 | ) |
@@ -819,7 +848,7 @@ fn foo() { | |||
819 | } | 848 | } |
820 | 849 | ||
821 | fn bar(baz_1: Baz, baz_2: Baz, arg_1: &str, arg_2: &str) { | 850 | fn bar(baz_1: Baz, baz_2: Baz, arg_1: &str, arg_2: &str) { |
822 | <|>todo!() | 851 | ${0:todo!()} |
823 | } | 852 | } |
824 | "#, | 853 | "#, |
825 | ) | 854 | ) |
@@ -839,7 +868,7 @@ fn foo() { | |||
839 | r" | 868 | r" |
840 | mod bar { | 869 | mod bar { |
841 | pub(crate) fn my_fn() { | 870 | pub(crate) fn my_fn() { |
842 | <|>todo!() | 871 | ${0:todo!()} |
843 | } | 872 | } |
844 | } | 873 | } |
845 | 874 | ||
@@ -878,7 +907,7 @@ fn bar() { | |||
878 | } | 907 | } |
879 | 908 | ||
880 | fn baz(foo: foo::Foo) { | 909 | fn baz(foo: foo::Foo) { |
881 | <|>todo!() | 910 | ${0:todo!()} |
882 | } | 911 | } |
883 | ", | 912 | ", |
884 | ) | 913 | ) |
@@ -902,7 +931,7 @@ mod bar { | |||
902 | fn something_else() {} | 931 | fn something_else() {} |
903 | 932 | ||
904 | pub(crate) fn my_fn() { | 933 | pub(crate) fn my_fn() { |
905 | <|>todo!() | 934 | ${0:todo!()} |
906 | } | 935 | } |
907 | } | 936 | } |
908 | 937 | ||
@@ -930,7 +959,7 @@ fn foo() { | |||
930 | mod bar { | 959 | mod bar { |
931 | mod baz { | 960 | mod baz { |
932 | pub(crate) fn my_fn() { | 961 | pub(crate) fn my_fn() { |
933 | <|>todo!() | 962 | ${0:todo!()} |
934 | } | 963 | } |
935 | } | 964 | } |
936 | } | 965 | } |
@@ -959,7 +988,7 @@ fn main() { | |||
959 | 988 | ||
960 | 989 | ||
961 | pub(crate) fn bar() { | 990 | pub(crate) fn bar() { |
962 | <|>todo!() | 991 | ${0:todo!()} |
963 | }", | 992 | }", |
964 | ) | 993 | ) |
965 | } | 994 | } |
diff --git a/crates/ra_assists/src/handlers/add_impl.rs b/crates/ra_assists/src/handlers/add_impl.rs index df114a0d8..eceba7d0a 100644 --- a/crates/ra_assists/src/handlers/add_impl.rs +++ b/crates/ra_assists/src/handlers/add_impl.rs | |||
@@ -1,7 +1,4 @@ | |||
1 | use ra_syntax::{ | 1 | use ra_syntax::ast::{self, AstNode, NameOwner, TypeParamsOwner}; |
2 | ast::{self, AstNode, NameOwner, TypeParamsOwner}, | ||
3 | TextSize, | ||
4 | }; | ||
5 | use stdx::{format_to, SepBy}; | 2 | use stdx::{format_to, SepBy}; |
6 | 3 | ||
7 | use crate::{AssistContext, AssistId, Assists}; | 4 | use crate::{AssistContext, AssistId, Assists}; |
@@ -12,17 +9,17 @@ use crate::{AssistContext, AssistId, Assists}; | |||
12 | // | 9 | // |
13 | // ``` | 10 | // ``` |
14 | // struct Ctx<T: Clone> { | 11 | // struct Ctx<T: Clone> { |
15 | // data: T,<|> | 12 | // data: T,<|> |
16 | // } | 13 | // } |
17 | // ``` | 14 | // ``` |
18 | // -> | 15 | // -> |
19 | // ``` | 16 | // ``` |
20 | // struct Ctx<T: Clone> { | 17 | // struct Ctx<T: Clone> { |
21 | // data: T, | 18 | // data: T, |
22 | // } | 19 | // } |
23 | // | 20 | // |
24 | // impl<T: Clone> Ctx<T> { | 21 | // impl<T: Clone> Ctx<T> { |
25 | // | 22 | // $0 |
26 | // } | 23 | // } |
27 | // ``` | 24 | // ``` |
28 | pub(crate) fn add_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 25 | pub(crate) fn add_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
@@ -50,30 +47,37 @@ pub(crate) fn add_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | |||
50 | let generic_params = lifetime_params.chain(type_params).sep_by(", "); | 47 | let generic_params = lifetime_params.chain(type_params).sep_by(", "); |
51 | format_to!(buf, "<{}>", generic_params) | 48 | format_to!(buf, "<{}>", generic_params) |
52 | } | 49 | } |
53 | buf.push_str(" {\n"); | 50 | match ctx.config.snippet_cap { |
54 | edit.set_cursor(start_offset + TextSize::of(&buf)); | 51 | Some(cap) => { |
55 | buf.push_str("\n}"); | 52 | buf.push_str(" {\n $0\n}"); |
56 | edit.insert(start_offset, buf); | 53 | edit.insert_snippet(cap, start_offset, buf); |
54 | } | ||
55 | None => { | ||
56 | buf.push_str(" {\n}"); | ||
57 | edit.insert(start_offset, buf); | ||
58 | } | ||
59 | } | ||
57 | }) | 60 | }) |
58 | } | 61 | } |
59 | 62 | ||
60 | #[cfg(test)] | 63 | #[cfg(test)] |
61 | mod tests { | 64 | mod tests { |
62 | use super::*; | ||
63 | use crate::tests::{check_assist, check_assist_target}; | 65 | use crate::tests::{check_assist, check_assist_target}; |
64 | 66 | ||
67 | use super::*; | ||
68 | |||
65 | #[test] | 69 | #[test] |
66 | fn test_add_impl() { | 70 | fn test_add_impl() { |
67 | check_assist(add_impl, "struct Foo {<|>}\n", "struct Foo {}\n\nimpl Foo {\n<|>\n}\n"); | 71 | check_assist(add_impl, "struct Foo {<|>}\n", "struct Foo {}\n\nimpl Foo {\n $0\n}\n"); |
68 | check_assist( | 72 | check_assist( |
69 | add_impl, | 73 | add_impl, |
70 | "struct Foo<T: Clone> {<|>}", | 74 | "struct Foo<T: Clone> {<|>}", |
71 | "struct Foo<T: Clone> {}\n\nimpl<T: Clone> Foo<T> {\n<|>\n}", | 75 | "struct Foo<T: Clone> {}\n\nimpl<T: Clone> Foo<T> {\n $0\n}", |
72 | ); | 76 | ); |
73 | check_assist( | 77 | check_assist( |
74 | add_impl, | 78 | add_impl, |
75 | "struct Foo<'a, T: Foo<'a>> {<|>}", | 79 | "struct Foo<'a, T: Foo<'a>> {<|>}", |
76 | "struct Foo<'a, T: Foo<'a>> {}\n\nimpl<'a, T: Foo<'a>> Foo<'a, T> {\n<|>\n}", | 80 | "struct Foo<'a, T: Foo<'a>> {}\n\nimpl<'a, T: Foo<'a>> Foo<'a, T> {\n $0\n}", |
77 | ); | 81 | ); |
78 | } | 82 | } |
79 | 83 | ||
diff --git a/crates/ra_assists/src/handlers/add_missing_impl_members.rs b/crates/ra_assists/src/handlers/add_missing_impl_members.rs index 22e1156d2..abacd4065 100644 --- a/crates/ra_assists/src/handlers/add_missing_impl_members.rs +++ b/crates/ra_assists/src/handlers/add_missing_impl_members.rs | |||
@@ -11,7 +11,7 @@ use ra_syntax::{ | |||
11 | use crate::{ | 11 | use crate::{ |
12 | assist_context::{AssistContext, Assists}, | 12 | assist_context::{AssistContext, Assists}, |
13 | ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams}, | 13 | ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams}, |
14 | utils::{get_missing_assoc_items, resolve_target_trait}, | 14 | utils::{get_missing_assoc_items, render_snippet, resolve_target_trait, Cursor}, |
15 | AssistId, | 15 | AssistId, |
16 | }; | 16 | }; |
17 | 17 | ||
@@ -46,7 +46,7 @@ enum AddMissingImplMembersMode { | |||
46 | // | 46 | // |
47 | // impl Trait<u32> for () { | 47 | // impl Trait<u32> for () { |
48 | // fn foo(&self) -> u32 { | 48 | // fn foo(&self) -> u32 { |
49 | // todo!() | 49 | // ${0:todo!()} |
50 | // } | 50 | // } |
51 | // | 51 | // |
52 | // } | 52 | // } |
@@ -89,7 +89,7 @@ pub(crate) fn add_missing_impl_members(acc: &mut Assists, ctx: &AssistContext) - | |||
89 | // impl Trait for () { | 89 | // impl Trait for () { |
90 | // Type X = (); | 90 | // Type X = (); |
91 | // fn foo(&self) {} | 91 | // fn foo(&self) {} |
92 | // fn bar(&self) {} | 92 | // $0fn bar(&self) {} |
93 | // | 93 | // |
94 | // } | 94 | // } |
95 | // ``` | 95 | // ``` |
@@ -147,7 +147,7 @@ fn add_missing_impl_members_inner( | |||
147 | } | 147 | } |
148 | 148 | ||
149 | let target = impl_def.syntax().text_range(); | 149 | let target = impl_def.syntax().text_range(); |
150 | acc.add(AssistId(assist_id), label, target, |edit| { | 150 | acc.add(AssistId(assist_id), label, target, |builder| { |
151 | let n_existing_items = impl_item_list.assoc_items().count(); | 151 | let n_existing_items = impl_item_list.assoc_items().count(); |
152 | let source_scope = ctx.sema.scope_for_def(trait_); | 152 | let source_scope = ctx.sema.scope_for_def(trait_); |
153 | let target_scope = ctx.sema.scope(impl_item_list.syntax()); | 153 | let target_scope = ctx.sema.scope(impl_item_list.syntax()); |
@@ -162,13 +162,29 @@ fn add_missing_impl_members_inner( | |||
162 | }) | 162 | }) |
163 | .map(|it| edit::remove_attrs_and_docs(&it)); | 163 | .map(|it| edit::remove_attrs_and_docs(&it)); |
164 | let new_impl_item_list = impl_item_list.append_items(items); | 164 | let new_impl_item_list = impl_item_list.append_items(items); |
165 | let cursor_position = { | 165 | let first_new_item = new_impl_item_list.assoc_items().nth(n_existing_items).unwrap(); |
166 | let first_new_item = new_impl_item_list.assoc_items().nth(n_existing_items).unwrap(); | 166 | |
167 | first_new_item.syntax().text_range().start() | 167 | let original_range = impl_item_list.syntax().text_range(); |
168 | match ctx.config.snippet_cap { | ||
169 | None => builder.replace(original_range, new_impl_item_list.to_string()), | ||
170 | Some(cap) => { | ||
171 | let mut cursor = Cursor::Before(first_new_item.syntax()); | ||
172 | let placeholder; | ||
173 | if let ast::AssocItem::FnDef(func) = &first_new_item { | ||
174 | if let Some(m) = func.syntax().descendants().find_map(ast::MacroCall::cast) { | ||
175 | if m.syntax().text() == "todo!()" { | ||
176 | placeholder = m; | ||
177 | cursor = Cursor::Replace(placeholder.syntax()); | ||
178 | } | ||
179 | } | ||
180 | } | ||
181 | builder.replace_snippet( | ||
182 | cap, | ||
183 | original_range, | ||
184 | render_snippet(cap, new_impl_item_list.syntax(), cursor), | ||
185 | ) | ||
186 | } | ||
168 | }; | 187 | }; |
169 | |||
170 | edit.replace_ast(impl_item_list, new_impl_item_list); | ||
171 | edit.set_cursor(cursor_position); | ||
172 | }) | 188 | }) |
173 | } | 189 | } |
174 | 190 | ||
@@ -222,7 +238,7 @@ struct S; | |||
222 | 238 | ||
223 | impl Foo for S { | 239 | impl Foo for S { |
224 | fn bar(&self) {} | 240 | fn bar(&self) {} |
225 | <|>type Output; | 241 | $0type Output; |
226 | const CONST: usize = 42; | 242 | const CONST: usize = 42; |
227 | fn foo(&self) { | 243 | fn foo(&self) { |
228 | todo!() | 244 | todo!() |
@@ -263,8 +279,8 @@ struct S; | |||
263 | 279 | ||
264 | impl Foo for S { | 280 | impl Foo for S { |
265 | fn bar(&self) {} | 281 | fn bar(&self) {} |
266 | <|>fn foo(&self) { | 282 | fn foo(&self) { |
267 | todo!() | 283 | ${0:todo!()} |
268 | } | 284 | } |
269 | 285 | ||
270 | }"#, | 286 | }"#, |
@@ -283,8 +299,8 @@ impl Foo for S { <|> }"#, | |||
283 | trait Foo { fn foo(&self); } | 299 | trait Foo { fn foo(&self); } |
284 | struct S; | 300 | struct S; |
285 | impl Foo for S { | 301 | impl Foo for S { |
286 | <|>fn foo(&self) { | 302 | fn foo(&self) { |
287 | todo!() | 303 | ${0:todo!()} |
288 | } | 304 | } |
289 | }"#, | 305 | }"#, |
290 | ); | 306 | ); |
@@ -302,8 +318,8 @@ impl Foo<u32> for S { <|> }"#, | |||
302 | trait Foo<T> { fn foo(&self, t: T) -> &T; } | 318 | trait Foo<T> { fn foo(&self, t: T) -> &T; } |
303 | struct S; | 319 | struct S; |
304 | impl Foo<u32> for S { | 320 | impl Foo<u32> for S { |
305 | <|>fn foo(&self, t: u32) -> &u32 { | 321 | fn foo(&self, t: u32) -> &u32 { |
306 | todo!() | 322 | ${0:todo!()} |
307 | } | 323 | } |
308 | }"#, | 324 | }"#, |
309 | ); | 325 | ); |
@@ -321,8 +337,8 @@ impl<U> Foo<U> for S { <|> }"#, | |||
321 | trait Foo<T> { fn foo(&self, t: T) -> &T; } | 337 | trait Foo<T> { fn foo(&self, t: T) -> &T; } |
322 | struct S; | 338 | struct S; |
323 | impl<U> Foo<U> for S { | 339 | impl<U> Foo<U> for S { |
324 | <|>fn foo(&self, t: U) -> &U { | 340 | fn foo(&self, t: U) -> &U { |
325 | todo!() | 341 | ${0:todo!()} |
326 | } | 342 | } |
327 | }"#, | 343 | }"#, |
328 | ); | 344 | ); |
@@ -340,8 +356,8 @@ impl Foo for S {}<|>"#, | |||
340 | trait Foo { fn foo(&self); } | 356 | trait Foo { fn foo(&self); } |
341 | struct S; | 357 | struct S; |
342 | impl Foo for S { | 358 | impl Foo for S { |
343 | <|>fn foo(&self) { | 359 | fn foo(&self) { |
344 | todo!() | 360 | ${0:todo!()} |
345 | } | 361 | } |
346 | }"#, | 362 | }"#, |
347 | ) | 363 | ) |
@@ -365,8 +381,8 @@ mod foo { | |||
365 | } | 381 | } |
366 | struct S; | 382 | struct S; |
367 | impl foo::Foo for S { | 383 | impl foo::Foo for S { |
368 | <|>fn foo(&self, bar: foo::Bar) { | 384 | fn foo(&self, bar: foo::Bar) { |
369 | todo!() | 385 | ${0:todo!()} |
370 | } | 386 | } |
371 | }"#, | 387 | }"#, |
372 | ); | 388 | ); |
@@ -390,8 +406,8 @@ mod foo { | |||
390 | } | 406 | } |
391 | struct S; | 407 | struct S; |
392 | impl foo::Foo for S { | 408 | impl foo::Foo for S { |
393 | <|>fn foo(&self, bar: foo::Bar<u32>) { | 409 | fn foo(&self, bar: foo::Bar<u32>) { |
394 | todo!() | 410 | ${0:todo!()} |
395 | } | 411 | } |
396 | }"#, | 412 | }"#, |
397 | ); | 413 | ); |
@@ -415,8 +431,8 @@ mod foo { | |||
415 | } | 431 | } |
416 | struct S; | 432 | struct S; |
417 | impl foo::Foo<u32> for S { | 433 | impl foo::Foo<u32> for S { |
418 | <|>fn foo(&self, bar: foo::Bar<u32>) { | 434 | fn foo(&self, bar: foo::Bar<u32>) { |
419 | todo!() | 435 | ${0:todo!()} |
420 | } | 436 | } |
421 | }"#, | 437 | }"#, |
422 | ); | 438 | ); |
@@ -443,8 +459,8 @@ mod foo { | |||
443 | struct Param; | 459 | struct Param; |
444 | struct S; | 460 | struct S; |
445 | impl foo::Foo<Param> for S { | 461 | impl foo::Foo<Param> for S { |
446 | <|>fn foo(&self, bar: Param) { | 462 | fn foo(&self, bar: Param) { |
447 | todo!() | 463 | ${0:todo!()} |
448 | } | 464 | } |
449 | }"#, | 465 | }"#, |
450 | ); | 466 | ); |
@@ -470,8 +486,8 @@ mod foo { | |||
470 | } | 486 | } |
471 | struct S; | 487 | struct S; |
472 | impl foo::Foo for S { | 488 | impl foo::Foo for S { |
473 | <|>fn foo(&self, bar: foo::Bar<u32>::Assoc) { | 489 | fn foo(&self, bar: foo::Bar<u32>::Assoc) { |
474 | todo!() | 490 | ${0:todo!()} |
475 | } | 491 | } |
476 | }"#, | 492 | }"#, |
477 | ); | 493 | ); |
@@ -497,8 +513,8 @@ mod foo { | |||
497 | } | 513 | } |
498 | struct S; | 514 | struct S; |
499 | impl foo::Foo for S { | 515 | impl foo::Foo for S { |
500 | <|>fn foo(&self, bar: foo::Bar<foo::Baz>) { | 516 | fn foo(&self, bar: foo::Bar<foo::Baz>) { |
501 | todo!() | 517 | ${0:todo!()} |
502 | } | 518 | } |
503 | }"#, | 519 | }"#, |
504 | ); | 520 | ); |
@@ -522,8 +538,8 @@ mod foo { | |||
522 | } | 538 | } |
523 | struct S; | 539 | struct S; |
524 | impl foo::Foo for S { | 540 | impl foo::Foo for S { |
525 | <|>fn foo(&self, bar: dyn Fn(u32) -> i32) { | 541 | fn foo(&self, bar: dyn Fn(u32) -> i32) { |
526 | todo!() | 542 | ${0:todo!()} |
527 | } | 543 | } |
528 | }"#, | 544 | }"#, |
529 | ); | 545 | ); |
@@ -580,7 +596,7 @@ trait Foo { | |||
580 | } | 596 | } |
581 | struct S; | 597 | struct S; |
582 | impl Foo for S { | 598 | impl Foo for S { |
583 | <|>type Output; | 599 | $0type Output; |
584 | fn foo(&self) { | 600 | fn foo(&self) { |
585 | todo!() | 601 | todo!() |
586 | } | 602 | } |
@@ -614,7 +630,7 @@ trait Foo { | |||
614 | } | 630 | } |
615 | struct S; | 631 | struct S; |
616 | impl Foo for S { | 632 | impl Foo for S { |
617 | <|>fn valid(some: u32) -> bool { false } | 633 | $0fn valid(some: u32) -> bool { false } |
618 | }"#, | 634 | }"#, |
619 | ) | 635 | ) |
620 | } | 636 | } |
@@ -637,8 +653,8 @@ trait Foo<T = Self> { | |||
637 | 653 | ||
638 | struct S; | 654 | struct S; |
639 | impl Foo for S { | 655 | impl Foo for S { |
640 | <|>fn bar(&self, other: &Self) { | 656 | fn bar(&self, other: &Self) { |
641 | todo!() | 657 | ${0:todo!()} |
642 | } | 658 | } |
643 | }"#, | 659 | }"#, |
644 | ) | 660 | ) |
@@ -662,8 +678,8 @@ trait Foo<T1, T2 = Self> { | |||
662 | 678 | ||
663 | struct S<T>; | 679 | struct S<T>; |
664 | impl Foo<T> for S<T> { | 680 | impl Foo<T> for S<T> { |
665 | <|>fn bar(&self, this: &T, that: &Self) { | 681 | fn bar(&self, this: &T, that: &Self) { |
666 | todo!() | 682 | ${0:todo!()} |
667 | } | 683 | } |
668 | }"#, | 684 | }"#, |
669 | ) | 685 | ) |
diff --git a/crates/ra_assists/src/handlers/add_new.rs b/crates/ra_assists/src/handlers/add_new.rs index fe7451dcf..837aa8377 100644 --- a/crates/ra_assists/src/handlers/add_new.rs +++ b/crates/ra_assists/src/handlers/add_new.rs | |||
@@ -3,7 +3,7 @@ use ra_syntax::{ | |||
3 | ast::{ | 3 | ast::{ |
4 | self, AstNode, NameOwner, StructKind, TypeAscriptionOwner, TypeParamsOwner, VisibilityOwner, | 4 | self, AstNode, NameOwner, StructKind, TypeAscriptionOwner, TypeParamsOwner, VisibilityOwner, |
5 | }, | 5 | }, |
6 | TextSize, T, | 6 | T, |
7 | }; | 7 | }; |
8 | use stdx::{format_to, SepBy}; | 8 | use stdx::{format_to, SepBy}; |
9 | 9 | ||
@@ -25,7 +25,7 @@ use crate::{AssistContext, AssistId, Assists}; | |||
25 | // } | 25 | // } |
26 | // | 26 | // |
27 | // impl<T: Clone> Ctx<T> { | 27 | // impl<T: Clone> Ctx<T> { |
28 | // fn new(data: T) -> Self { Self { data } } | 28 | // fn $0new(data: T) -> Self { Self { data } } |
29 | // } | 29 | // } |
30 | // | 30 | // |
31 | // ``` | 31 | // ``` |
@@ -42,31 +42,26 @@ pub(crate) fn add_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | |||
42 | let impl_def = find_struct_impl(&ctx, &strukt)?; | 42 | let impl_def = find_struct_impl(&ctx, &strukt)?; |
43 | 43 | ||
44 | let target = strukt.syntax().text_range(); | 44 | let target = strukt.syntax().text_range(); |
45 | acc.add(AssistId("add_new"), "Add default constructor", target, |edit| { | 45 | acc.add(AssistId("add_new"), "Add default constructor", target, |builder| { |
46 | let mut buf = String::with_capacity(512); | 46 | let mut buf = String::with_capacity(512); |
47 | 47 | ||
48 | if impl_def.is_some() { | 48 | if impl_def.is_some() { |
49 | buf.push('\n'); | 49 | buf.push('\n'); |
50 | } | 50 | } |
51 | 51 | ||
52 | let vis = strukt.visibility().map(|v| format!("{} ", v)); | 52 | let vis = strukt.visibility().map_or(String::new(), |v| format!("{} ", v)); |
53 | let vis = vis.as_deref().unwrap_or(""); | ||
54 | 53 | ||
55 | let params = field_list | 54 | let params = field_list |
56 | .fields() | 55 | .fields() |
57 | .filter_map(|f| { | 56 | .filter_map(|f| { |
58 | Some(format!( | 57 | Some(format!("{}: {}", f.name()?.syntax(), f.ascribed_type()?.syntax())) |
59 | "{}: {}", | ||
60 | f.name()?.syntax().text(), | ||
61 | f.ascribed_type()?.syntax().text() | ||
62 | )) | ||
63 | }) | 58 | }) |
64 | .sep_by(", "); | 59 | .sep_by(", "); |
65 | let fields = field_list.fields().filter_map(|f| f.name()).sep_by(", "); | 60 | let fields = field_list.fields().filter_map(|f| f.name()).sep_by(", "); |
66 | 61 | ||
67 | format_to!(buf, " {}fn new({}) -> Self {{ Self {{ {} }} }}", vis, params, fields); | 62 | format_to!(buf, " {}fn new({}) -> Self {{ Self {{ {} }} }}", vis, params, fields); |
68 | 63 | ||
69 | let (start_offset, end_offset) = impl_def | 64 | let start_offset = impl_def |
70 | .and_then(|impl_def| { | 65 | .and_then(|impl_def| { |
71 | buf.push('\n'); | 66 | buf.push('\n'); |
72 | let start = impl_def | 67 | let start = impl_def |
@@ -76,17 +71,20 @@ pub(crate) fn add_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | |||
76 | .text_range() | 71 | .text_range() |
77 | .end(); | 72 | .end(); |
78 | 73 | ||
79 | Some((start, TextSize::of("\n"))) | 74 | Some(start) |
80 | }) | 75 | }) |
81 | .unwrap_or_else(|| { | 76 | .unwrap_or_else(|| { |
82 | buf = generate_impl_text(&strukt, &buf); | 77 | buf = generate_impl_text(&strukt, &buf); |
83 | let start = strukt.syntax().text_range().end(); | 78 | strukt.syntax().text_range().end() |
84 | |||
85 | (start, TextSize::of("\n}\n")) | ||
86 | }); | 79 | }); |
87 | 80 | ||
88 | edit.set_cursor(start_offset + TextSize::of(&buf) - end_offset); | 81 | match ctx.config.snippet_cap { |
89 | edit.insert(start_offset, buf); | 82 | None => builder.insert(start_offset, buf), |
83 | Some(cap) => { | ||
84 | buf = buf.replace("fn new", "fn $0new"); | ||
85 | builder.insert_snippet(cap, start_offset, buf); | ||
86 | } | ||
87 | } | ||
90 | }) | 88 | }) |
91 | } | 89 | } |
92 | 90 | ||
@@ -191,7 +189,7 @@ mod tests { | |||
191 | "struct Foo {} | 189 | "struct Foo {} |
192 | 190 | ||
193 | impl Foo { | 191 | impl Foo { |
194 | fn new() -> Self { Self { } }<|> | 192 | fn $0new() -> Self { Self { } } |
195 | } | 193 | } |
196 | ", | 194 | ", |
197 | ); | 195 | ); |
@@ -201,7 +199,7 @@ impl Foo { | |||
201 | "struct Foo<T: Clone> {} | 199 | "struct Foo<T: Clone> {} |
202 | 200 | ||
203 | impl<T: Clone> Foo<T> { | 201 | impl<T: Clone> Foo<T> { |
204 | fn new() -> Self { Self { } }<|> | 202 | fn $0new() -> Self { Self { } } |
205 | } | 203 | } |
206 | ", | 204 | ", |
207 | ); | 205 | ); |
@@ -211,7 +209,7 @@ impl<T: Clone> Foo<T> { | |||
211 | "struct Foo<'a, T: Foo<'a>> {} | 209 | "struct Foo<'a, T: Foo<'a>> {} |
212 | 210 | ||
213 | impl<'a, T: Foo<'a>> Foo<'a, T> { | 211 | impl<'a, T: Foo<'a>> Foo<'a, T> { |
214 | fn new() -> Self { Self { } }<|> | 212 | fn $0new() -> Self { Self { } } |
215 | } | 213 | } |
216 | ", | 214 | ", |
217 | ); | 215 | ); |
@@ -221,7 +219,7 @@ impl<'a, T: Foo<'a>> Foo<'a, T> { | |||
221 | "struct Foo { baz: String } | 219 | "struct Foo { baz: String } |
222 | 220 | ||
223 | impl Foo { | 221 | impl Foo { |
224 | fn new(baz: String) -> Self { Self { baz } }<|> | 222 | fn $0new(baz: String) -> Self { Self { baz } } |
225 | } | 223 | } |
226 | ", | 224 | ", |
227 | ); | 225 | ); |
@@ -231,7 +229,7 @@ impl Foo { | |||
231 | "struct Foo { baz: String, qux: Vec<i32> } | 229 | "struct Foo { baz: String, qux: Vec<i32> } |
232 | 230 | ||
233 | impl Foo { | 231 | impl Foo { |
234 | fn new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } }<|> | 232 | fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } } |
235 | } | 233 | } |
236 | ", | 234 | ", |
237 | ); | 235 | ); |
@@ -243,7 +241,7 @@ impl Foo { | |||
243 | "struct Foo { pub baz: String, pub qux: Vec<i32> } | 241 | "struct Foo { pub baz: String, pub qux: Vec<i32> } |
244 | 242 | ||
245 | impl Foo { | 243 | impl Foo { |
246 | fn new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } }<|> | 244 | fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } } |
247 | } | 245 | } |
248 | ", | 246 | ", |
249 | ); | 247 | ); |
@@ -258,7 +256,7 @@ impl Foo {} | |||
258 | "struct Foo {} | 256 | "struct Foo {} |
259 | 257 | ||
260 | impl Foo { | 258 | impl Foo { |
261 | fn new() -> Self { Self { } }<|> | 259 | fn $0new() -> Self { Self { } } |
262 | } | 260 | } |
263 | ", | 261 | ", |
264 | ); | 262 | ); |
@@ -273,7 +271,7 @@ impl Foo { | |||
273 | "struct Foo {} | 271 | "struct Foo {} |
274 | 272 | ||
275 | impl Foo { | 273 | impl Foo { |
276 | fn new() -> Self { Self { } }<|> | 274 | fn $0new() -> Self { Self { } } |
277 | 275 | ||
278 | fn qux(&self) {} | 276 | fn qux(&self) {} |
279 | } | 277 | } |
@@ -294,7 +292,7 @@ impl Foo { | |||
294 | "struct Foo {} | 292 | "struct Foo {} |
295 | 293 | ||
296 | impl Foo { | 294 | impl Foo { |
297 | fn new() -> Self { Self { } }<|> | 295 | fn $0new() -> Self { Self { } } |
298 | 296 | ||
299 | fn qux(&self) {} | 297 | fn qux(&self) {} |
300 | fn baz() -> i32 { | 298 | fn baz() -> i32 { |
@@ -311,7 +309,7 @@ impl Foo { | |||
311 | "pub struct Foo {} | 309 | "pub struct Foo {} |
312 | 310 | ||
313 | impl Foo { | 311 | impl Foo { |
314 | pub fn new() -> Self { Self { } }<|> | 312 | pub fn $0new() -> Self { Self { } } |
315 | } | 313 | } |
316 | ", | 314 | ", |
317 | ); | 315 | ); |
@@ -321,7 +319,7 @@ impl Foo { | |||
321 | "pub(crate) struct Foo {} | 319 | "pub(crate) struct Foo {} |
322 | 320 | ||
323 | impl Foo { | 321 | impl Foo { |
324 | pub(crate) fn new() -> Self { Self { } }<|> | 322 | pub(crate) fn $0new() -> Self { Self { } } |
325 | } | 323 | } |
326 | ", | 324 | ", |
327 | ); | 325 | ); |
@@ -414,7 +412,7 @@ pub struct Source<T> { | |||
414 | } | 412 | } |
415 | 413 | ||
416 | impl<T> Source<T> { | 414 | impl<T> Source<T> { |
417 | pub fn new(file_id: HirFileId, ast: T) -> Self { Self { file_id, ast } }<|> | 415 | pub fn $0new(file_id: HirFileId, ast: T) -> Self { Self { file_id, ast } } |
418 | 416 | ||
419 | pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> { | 417 | pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> { |
420 | Source { file_id: self.file_id, ast: f(self.ast) } | 418 | Source { file_id: self.file_id, ast: f(self.ast) } |
diff --git a/crates/ra_assists/src/handlers/add_turbo_fish.rs b/crates/ra_assists/src/handlers/add_turbo_fish.rs new file mode 100644 index 000000000..26acf81f2 --- /dev/null +++ b/crates/ra_assists/src/handlers/add_turbo_fish.rs | |||
@@ -0,0 +1,134 @@ | |||
1 | use ra_ide_db::defs::{classify_name_ref, Definition, NameRefClass}; | ||
2 | use ra_syntax::{ast, AstNode, SyntaxKind, T}; | ||
3 | use test_utils::mark; | ||
4 | |||
5 | use crate::{ | ||
6 | assist_context::{AssistContext, Assists}, | ||
7 | AssistId, | ||
8 | }; | ||
9 | |||
10 | // Assist: add_turbo_fish | ||
11 | // | ||
12 | // Adds `::<_>` to a call of a generic method or function. | ||
13 | // | ||
14 | // ``` | ||
15 | // fn make<T>() -> T { todo!() } | ||
16 | // fn main() { | ||
17 | // let x = make<|>(); | ||
18 | // } | ||
19 | // ``` | ||
20 | // -> | ||
21 | // ``` | ||
22 | // fn make<T>() -> T { todo!() } | ||
23 | // fn main() { | ||
24 | // let x = make::<${0:_}>(); | ||
25 | // } | ||
26 | // ``` | ||
27 | pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
28 | let ident = ctx.find_token_at_offset(SyntaxKind::IDENT)?; | ||
29 | let next_token = ident.next_token()?; | ||
30 | if next_token.kind() == T![::] { | ||
31 | mark::hit!(add_turbo_fish_one_fish_is_enough); | ||
32 | return None; | ||
33 | } | ||
34 | let name_ref = ast::NameRef::cast(ident.parent())?; | ||
35 | let def = match classify_name_ref(&ctx.sema, &name_ref)? { | ||
36 | NameRefClass::Definition(def) => def, | ||
37 | NameRefClass::FieldShorthand { .. } => return None, | ||
38 | }; | ||
39 | let fun = match def { | ||
40 | Definition::ModuleDef(hir::ModuleDef::Function(it)) => it, | ||
41 | _ => return None, | ||
42 | }; | ||
43 | let generics = hir::GenericDef::Function(fun).params(ctx.sema.db); | ||
44 | if generics.is_empty() { | ||
45 | mark::hit!(add_turbo_fish_non_generic); | ||
46 | return None; | ||
47 | } | ||
48 | acc.add(AssistId("add_turbo_fish"), "Add `::<>`", ident.text_range(), |builder| { | ||
49 | match ctx.config.snippet_cap { | ||
50 | Some(cap) => builder.insert_snippet(cap, ident.text_range().end(), "::<${0:_}>"), | ||
51 | None => builder.insert(ident.text_range().end(), "::<_>"), | ||
52 | } | ||
53 | }) | ||
54 | } | ||
55 | |||
56 | #[cfg(test)] | ||
57 | mod tests { | ||
58 | use crate::tests::{check_assist, check_assist_not_applicable}; | ||
59 | |||
60 | use super::*; | ||
61 | use test_utils::mark; | ||
62 | |||
63 | #[test] | ||
64 | fn add_turbo_fish_function() { | ||
65 | check_assist( | ||
66 | add_turbo_fish, | ||
67 | r#" | ||
68 | fn make<T>() -> T {} | ||
69 | fn main() { | ||
70 | make<|>(); | ||
71 | } | ||
72 | "#, | ||
73 | r#" | ||
74 | fn make<T>() -> T {} | ||
75 | fn main() { | ||
76 | make::<${0:_}>(); | ||
77 | } | ||
78 | "#, | ||
79 | ); | ||
80 | } | ||
81 | |||
82 | #[test] | ||
83 | fn add_turbo_fish_method() { | ||
84 | check_assist( | ||
85 | add_turbo_fish, | ||
86 | r#" | ||
87 | struct S; | ||
88 | impl S { | ||
89 | fn make<T>(&self) -> T {} | ||
90 | } | ||
91 | fn main() { | ||
92 | S.make<|>(); | ||
93 | } | ||
94 | "#, | ||
95 | r#" | ||
96 | struct S; | ||
97 | impl S { | ||
98 | fn make<T>(&self) -> T {} | ||
99 | } | ||
100 | fn main() { | ||
101 | S.make::<${0:_}>(); | ||
102 | } | ||
103 | "#, | ||
104 | ); | ||
105 | } | ||
106 | |||
107 | #[test] | ||
108 | fn add_turbo_fish_one_fish_is_enough() { | ||
109 | mark::check!(add_turbo_fish_one_fish_is_enough); | ||
110 | check_assist_not_applicable( | ||
111 | add_turbo_fish, | ||
112 | r#" | ||
113 | fn make<T>() -> T {} | ||
114 | fn main() { | ||
115 | make<|>::<()>(); | ||
116 | } | ||
117 | "#, | ||
118 | ); | ||
119 | } | ||
120 | |||
121 | #[test] | ||
122 | fn add_turbo_fish_non_generic() { | ||
123 | mark::check!(add_turbo_fish_non_generic); | ||
124 | check_assist_not_applicable( | ||
125 | add_turbo_fish, | ||
126 | r#" | ||
127 | fn make() -> () {} | ||
128 | fn main() { | ||
129 | make<|>(); | ||
130 | } | ||
131 | "#, | ||
132 | ); | ||
133 | } | ||
134 | } | ||
diff --git a/crates/ra_assists/src/handlers/apply_demorgan.rs b/crates/ra_assists/src/handlers/apply_demorgan.rs index 0feba5e11..233e8fb8e 100644 --- a/crates/ra_assists/src/handlers/apply_demorgan.rs +++ b/crates/ra_assists/src/handlers/apply_demorgan.rs | |||
@@ -63,22 +63,22 @@ mod tests { | |||
63 | 63 | ||
64 | #[test] | 64 | #[test] |
65 | fn demorgan_turns_and_into_or() { | 65 | fn demorgan_turns_and_into_or() { |
66 | check_assist(apply_demorgan, "fn f() { !x &&<|> !x }", "fn f() { !(x ||<|> x) }") | 66 | check_assist(apply_demorgan, "fn f() { !x &&<|> !x }", "fn f() { !(x || x) }") |
67 | } | 67 | } |
68 | 68 | ||
69 | #[test] | 69 | #[test] |
70 | fn demorgan_turns_or_into_and() { | 70 | fn demorgan_turns_or_into_and() { |
71 | check_assist(apply_demorgan, "fn f() { !x ||<|> !x }", "fn f() { !(x &&<|> x) }") | 71 | check_assist(apply_demorgan, "fn f() { !x ||<|> !x }", "fn f() { !(x && x) }") |
72 | } | 72 | } |
73 | 73 | ||
74 | #[test] | 74 | #[test] |
75 | fn demorgan_removes_inequality() { | 75 | fn demorgan_removes_inequality() { |
76 | check_assist(apply_demorgan, "fn f() { x != x ||<|> !x }", "fn f() { !(x == x &&<|> x) }") | 76 | check_assist(apply_demorgan, "fn f() { x != x ||<|> !x }", "fn f() { !(x == x && x) }") |
77 | } | 77 | } |
78 | 78 | ||
79 | #[test] | 79 | #[test] |
80 | fn demorgan_general_case() { | 80 | fn demorgan_general_case() { |
81 | check_assist(apply_demorgan, "fn f() { x ||<|> x }", "fn f() { !(!x &&<|> !x) }") | 81 | check_assist(apply_demorgan, "fn f() { x ||<|> x }", "fn f() { !(!x && !x) }") |
82 | } | 82 | } |
83 | 83 | ||
84 | #[test] | 84 | #[test] |
diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs index 78d23150d..edf96d50e 100644 --- a/crates/ra_assists/src/handlers/auto_import.rs +++ b/crates/ra_assists/src/handlers/auto_import.rs | |||
@@ -50,7 +50,12 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
50 | format!("Import `{}`", &import), | 50 | format!("Import `{}`", &import), |
51 | range, | 51 | range, |
52 | |builder| { | 52 | |builder| { |
53 | insert_use_statement(&auto_import_assets.syntax_under_caret, &import, ctx, builder); | 53 | insert_use_statement( |
54 | &auto_import_assets.syntax_under_caret, | ||
55 | &import, | ||
56 | ctx, | ||
57 | builder.text_edit_builder(), | ||
58 | ); | ||
54 | }, | 59 | }, |
55 | ); | 60 | ); |
56 | } | 61 | } |
@@ -293,7 +298,7 @@ mod tests { | |||
293 | } | 298 | } |
294 | ", | 299 | ", |
295 | r" | 300 | r" |
296 | <|>use PubMod::PubStruct; | 301 | use PubMod::PubStruct; |
297 | 302 | ||
298 | PubStruct | 303 | PubStruct |
299 | 304 | ||
@@ -324,7 +329,7 @@ mod tests { | |||
324 | macro_rules! foo { | 329 | macro_rules! foo { |
325 | ($i:ident) => { fn foo(a: $i) {} } | 330 | ($i:ident) => { fn foo(a: $i) {} } |
326 | } | 331 | } |
327 | foo!(Pub<|>Struct); | 332 | foo!(PubStruct); |
328 | 333 | ||
329 | pub mod PubMod { | 334 | pub mod PubMod { |
330 | pub struct PubStruct; | 335 | pub struct PubStruct; |
@@ -355,7 +360,7 @@ mod tests { | |||
355 | use PubMod::{PubStruct2, PubStruct1}; | 360 | use PubMod::{PubStruct2, PubStruct1}; |
356 | 361 | ||
357 | struct Test { | 362 | struct Test { |
358 | test: Pub<|>Struct2<u8>, | 363 | test: PubStruct2<u8>, |
359 | } | 364 | } |
360 | 365 | ||
361 | pub mod PubMod { | 366 | pub mod PubMod { |
@@ -388,7 +393,7 @@ mod tests { | |||
388 | r" | 393 | r" |
389 | use PubMod3::PubStruct; | 394 | use PubMod3::PubStruct; |
390 | 395 | ||
391 | PubSt<|>ruct | 396 | PubStruct |
392 | 397 | ||
393 | pub mod PubMod1 { | 398 | pub mod PubMod1 { |
394 | pub struct PubStruct; | 399 | pub struct PubStruct; |
@@ -469,7 +474,7 @@ mod tests { | |||
469 | r" | 474 | r" |
470 | use PubMod::test_function; | 475 | use PubMod::test_function; |
471 | 476 | ||
472 | test_function<|> | 477 | test_function |
473 | 478 | ||
474 | pub mod PubMod { | 479 | pub mod PubMod { |
475 | pub fn test_function() {}; | 480 | pub fn test_function() {}; |
@@ -496,7 +501,7 @@ mod tests { | |||
496 | r"use crate_with_macro::foo; | 501 | r"use crate_with_macro::foo; |
497 | 502 | ||
498 | fn main() { | 503 | fn main() { |
499 | foo<|> | 504 | foo |
500 | } | 505 | } |
501 | ", | 506 | ", |
502 | ); | 507 | ); |
@@ -582,7 +587,7 @@ fn main() { | |||
582 | } | 587 | } |
583 | 588 | ||
584 | fn main() { | 589 | fn main() { |
585 | TestStruct::test_function<|> | 590 | TestStruct::test_function |
586 | } | 591 | } |
587 | ", | 592 | ", |
588 | ); | 593 | ); |
@@ -615,7 +620,7 @@ fn main() { | |||
615 | } | 620 | } |
616 | 621 | ||
617 | fn main() { | 622 | fn main() { |
618 | TestStruct::TEST_CONST<|> | 623 | TestStruct::TEST_CONST |
619 | } | 624 | } |
620 | ", | 625 | ", |
621 | ); | 626 | ); |
@@ -654,7 +659,7 @@ fn main() { | |||
654 | } | 659 | } |
655 | 660 | ||
656 | fn main() { | 661 | fn main() { |
657 | test_mod::TestStruct::test_function<|> | 662 | test_mod::TestStruct::test_function |
658 | } | 663 | } |
659 | ", | 664 | ", |
660 | ); | 665 | ); |
@@ -725,7 +730,7 @@ fn main() { | |||
725 | } | 730 | } |
726 | 731 | ||
727 | fn main() { | 732 | fn main() { |
728 | test_mod::TestStruct::TEST_CONST<|> | 733 | test_mod::TestStruct::TEST_CONST |
729 | } | 734 | } |
730 | ", | 735 | ", |
731 | ); | 736 | ); |
@@ -798,7 +803,7 @@ fn main() { | |||
798 | 803 | ||
799 | fn main() { | 804 | fn main() { |
800 | let test_struct = test_mod::TestStruct {}; | 805 | let test_struct = test_mod::TestStruct {}; |
801 | test_struct.test_meth<|>od() | 806 | test_struct.test_method() |
802 | } | 807 | } |
803 | ", | 808 | ", |
804 | ); | 809 | ); |
diff --git a/crates/ra_assists/src/handlers/change_return_type_to_result.rs b/crates/ra_assists/src/handlers/change_return_type_to_result.rs index 5c907097e..c6baa0a57 100644 --- a/crates/ra_assists/src/handlers/change_return_type_to_result.rs +++ b/crates/ra_assists/src/handlers/change_return_type_to_result.rs | |||
@@ -1,8 +1,6 @@ | |||
1 | use ra_syntax::{ | 1 | use ra_syntax::{ |
2 | ast::{self, BlockExpr, Expr, LoopBodyOwner}, | 2 | ast::{self, BlockExpr, Expr, LoopBodyOwner}, |
3 | AstNode, | 3 | AstNode, SyntaxNode, |
4 | SyntaxKind::{COMMENT, WHITESPACE}, | ||
5 | SyntaxNode, TextSize, | ||
6 | }; | 4 | }; |
7 | 5 | ||
8 | use crate::{AssistContext, AssistId, Assists}; | 6 | use crate::{AssistContext, AssistId, Assists}; |
@@ -16,39 +14,40 @@ use crate::{AssistContext, AssistId, Assists}; | |||
16 | // ``` | 14 | // ``` |
17 | // -> | 15 | // -> |
18 | // ``` | 16 | // ``` |
19 | // fn foo() -> Result<i32, > { Ok(42i32) } | 17 | // fn foo() -> Result<i32, ${0:_}> { Ok(42i32) } |
20 | // ``` | 18 | // ``` |
21 | pub(crate) fn change_return_type_to_result(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 19 | pub(crate) fn change_return_type_to_result(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
22 | let fn_def = ctx.find_node_at_offset::<ast::FnDef>(); | 20 | let ret_type = ctx.find_node_at_offset::<ast::RetType>()?; |
23 | let fn_def = &mut fn_def?; | 21 | // FIXME: extend to lambdas as well |
24 | let ret_type = &fn_def.ret_type()?.type_ref()?; | 22 | let fn_def = ret_type.syntax().parent().and_then(ast::FnDef::cast)?; |
25 | if ret_type.syntax().text().to_string().starts_with("Result<") { | 23 | |
24 | let type_ref = &ret_type.type_ref()?; | ||
25 | if type_ref.syntax().text().to_string().starts_with("Result<") { | ||
26 | return None; | 26 | return None; |
27 | } | 27 | } |
28 | 28 | ||
29 | let block_expr = &fn_def.body()?; | 29 | let block_expr = &fn_def.body()?; |
30 | let cursor_in_ret_type = | ||
31 | fn_def.ret_type()?.syntax().text_range().contains_range(ctx.frange.range); | ||
32 | if !cursor_in_ret_type { | ||
33 | return None; | ||
34 | } | ||
35 | 30 | ||
36 | acc.add( | 31 | acc.add( |
37 | AssistId("change_return_type_to_result"), | 32 | AssistId("change_return_type_to_result"), |
38 | "Change return type to Result", | 33 | "Change return type to Result", |
39 | ret_type.syntax().text_range(), | 34 | type_ref.syntax().text_range(), |
40 | |edit| { | 35 | |builder| { |
41 | let mut tail_return_expr_collector = TailReturnCollector::new(); | 36 | let mut tail_return_expr_collector = TailReturnCollector::new(); |
42 | tail_return_expr_collector.collect_jump_exprs(block_expr, false); | 37 | tail_return_expr_collector.collect_jump_exprs(block_expr, false); |
43 | tail_return_expr_collector.collect_tail_exprs(block_expr); | 38 | tail_return_expr_collector.collect_tail_exprs(block_expr); |
44 | 39 | ||
45 | for ret_expr_arg in tail_return_expr_collector.exprs_to_wrap { | 40 | for ret_expr_arg in tail_return_expr_collector.exprs_to_wrap { |
46 | edit.replace_node_and_indent(&ret_expr_arg, format!("Ok({})", ret_expr_arg)); | 41 | builder.replace_node_and_indent(&ret_expr_arg, format!("Ok({})", ret_expr_arg)); |
47 | } | 42 | } |
48 | edit.replace_node_and_indent(ret_type.syntax(), format!("Result<{}, >", ret_type)); | ||
49 | 43 | ||
50 | if let Some(node_start) = result_insertion_offset(&ret_type) { | 44 | match ctx.config.snippet_cap { |
51 | edit.set_cursor(node_start + TextSize::of(&format!("Result<{}, ", ret_type))); | 45 | Some(cap) => { |
46 | let snippet = format!("Result<{}, ${{0:_}}>", type_ref); | ||
47 | builder.replace_snippet(cap, type_ref.syntax().text_range(), snippet) | ||
48 | } | ||
49 | None => builder | ||
50 | .replace(type_ref.syntax().text_range(), format!("Result<{}, _>", type_ref)), | ||
52 | } | 51 | } |
53 | }, | 52 | }, |
54 | ) | 53 | ) |
@@ -250,17 +249,8 @@ fn get_tail_expr_from_block(expr: &Expr) -> Option<Vec<NodeType>> { | |||
250 | } | 249 | } |
251 | } | 250 | } |
252 | 251 | ||
253 | fn result_insertion_offset(ret_type: &ast::TypeRef) -> Option<TextSize> { | ||
254 | let non_ws_child = ret_type | ||
255 | .syntax() | ||
256 | .children_with_tokens() | ||
257 | .find(|it| it.kind() != COMMENT && it.kind() != WHITESPACE)?; | ||
258 | Some(non_ws_child.text_range().start()) | ||
259 | } | ||
260 | |||
261 | #[cfg(test)] | 252 | #[cfg(test)] |
262 | mod tests { | 253 | mod tests { |
263 | |||
264 | use crate::tests::{check_assist, check_assist_not_applicable}; | 254 | use crate::tests::{check_assist, check_assist_not_applicable}; |
265 | 255 | ||
266 | use super::*; | 256 | use super::*; |
@@ -273,7 +263,7 @@ mod tests { | |||
273 | let test = "test"; | 263 | let test = "test"; |
274 | return 42i32; | 264 | return 42i32; |
275 | }"#, | 265 | }"#, |
276 | r#"fn foo() -> Result<i32, <|>> { | 266 | r#"fn foo() -> Result<i32, ${0:_}> { |
277 | let test = "test"; | 267 | let test = "test"; |
278 | return Ok(42i32); | 268 | return Ok(42i32); |
279 | }"#, | 269 | }"#, |
@@ -288,7 +278,7 @@ mod tests { | |||
288 | let test = "test"; | 278 | let test = "test"; |
289 | return 42i32; | 279 | return 42i32; |
290 | }"#, | 280 | }"#, |
291 | r#"fn foo() -> Result<i32, <|>> { | 281 | r#"fn foo() -> Result<i32, ${0:_}> { |
292 | let test = "test"; | 282 | let test = "test"; |
293 | return Ok(42i32); | 283 | return Ok(42i32); |
294 | }"#, | 284 | }"#, |
@@ -314,7 +304,7 @@ mod tests { | |||
314 | let test = "test"; | 304 | let test = "test"; |
315 | return 42i32; | 305 | return 42i32; |
316 | }"#, | 306 | }"#, |
317 | r#"fn foo() -> Result<i32, <|>> { | 307 | r#"fn foo() -> Result<i32, ${0:_}> { |
318 | let test = "test"; | 308 | let test = "test"; |
319 | return Ok(42i32); | 309 | return Ok(42i32); |
320 | }"#, | 310 | }"#, |
@@ -329,7 +319,7 @@ mod tests { | |||
329 | let test = "test"; | 319 | let test = "test"; |
330 | 42i32 | 320 | 42i32 |
331 | }"#, | 321 | }"#, |
332 | r#"fn foo() -> Result<i32, <|>> { | 322 | r#"fn foo() -> Result<i32, ${0:_}> { |
333 | let test = "test"; | 323 | let test = "test"; |
334 | Ok(42i32) | 324 | Ok(42i32) |
335 | }"#, | 325 | }"#, |
@@ -343,7 +333,7 @@ mod tests { | |||
343 | r#"fn foo() -> i32<|> { | 333 | r#"fn foo() -> i32<|> { |
344 | 42i32 | 334 | 42i32 |
345 | }"#, | 335 | }"#, |
346 | r#"fn foo() -> Result<i32, <|>> { | 336 | r#"fn foo() -> Result<i32, ${0:_}> { |
347 | Ok(42i32) | 337 | Ok(42i32) |
348 | }"#, | 338 | }"#, |
349 | ); | 339 | ); |
@@ -359,7 +349,7 @@ mod tests { | |||
359 | 24i32 | 349 | 24i32 |
360 | } | 350 | } |
361 | }"#, | 351 | }"#, |
362 | r#"fn foo() -> Result<i32, <|>> { | 352 | r#"fn foo() -> Result<i32, ${0:_}> { |
363 | if true { | 353 | if true { |
364 | Ok(42i32) | 354 | Ok(42i32) |
365 | } else { | 355 | } else { |
@@ -384,7 +374,7 @@ mod tests { | |||
384 | 24i32 | 374 | 24i32 |
385 | } | 375 | } |
386 | }"#, | 376 | }"#, |
387 | r#"fn foo() -> Result<i32, <|>> { | 377 | r#"fn foo() -> Result<i32, ${0:_}> { |
388 | if true { | 378 | if true { |
389 | if false { | 379 | if false { |
390 | Ok(1) | 380 | Ok(1) |
@@ -413,7 +403,7 @@ mod tests { | |||
413 | 24i32.await | 403 | 24i32.await |
414 | } | 404 | } |
415 | }"#, | 405 | }"#, |
416 | r#"async fn foo() -> Result<i32, <|>> { | 406 | r#"async fn foo() -> Result<i32, ${0:_}> { |
417 | if true { | 407 | if true { |
418 | if false { | 408 | if false { |
419 | Ok(1.await) | 409 | Ok(1.await) |
@@ -434,7 +424,7 @@ mod tests { | |||
434 | r#"fn foo() -> [i32;<|> 3] { | 424 | r#"fn foo() -> [i32;<|> 3] { |
435 | [1, 2, 3] | 425 | [1, 2, 3] |
436 | }"#, | 426 | }"#, |
437 | r#"fn foo() -> Result<[i32; 3], <|>> { | 427 | r#"fn foo() -> Result<[i32; 3], ${0:_}> { |
438 | Ok([1, 2, 3]) | 428 | Ok([1, 2, 3]) |
439 | }"#, | 429 | }"#, |
440 | ); | 430 | ); |
@@ -455,7 +445,7 @@ mod tests { | |||
455 | 24 as i32 | 445 | 24 as i32 |
456 | } | 446 | } |
457 | }"#, | 447 | }"#, |
458 | r#"fn foo() -> Result<i32, <|>> { | 448 | r#"fn foo() -> Result<i32, ${0:_}> { |
459 | if true { | 449 | if true { |
460 | if false { | 450 | if false { |
461 | Ok(1 as i32) | 451 | Ok(1 as i32) |
@@ -480,7 +470,7 @@ mod tests { | |||
480 | _ => 24i32, | 470 | _ => 24i32, |
481 | } | 471 | } |
482 | }"#, | 472 | }"#, |
483 | r#"fn foo() -> Result<i32, <|>> { | 473 | r#"fn foo() -> Result<i32, ${0:_}> { |
484 | let my_var = 5; | 474 | let my_var = 5; |
485 | match my_var { | 475 | match my_var { |
486 | 5 => Ok(42i32), | 476 | 5 => Ok(42i32), |
@@ -503,7 +493,7 @@ mod tests { | |||
503 | 493 | ||
504 | my_var | 494 | my_var |
505 | }"#, | 495 | }"#, |
506 | r#"fn foo() -> Result<i32, <|>> { | 496 | r#"fn foo() -> Result<i32, ${0:_}> { |
507 | let my_var = 5; | 497 | let my_var = 5; |
508 | loop { | 498 | loop { |
509 | println!("test"); | 499 | println!("test"); |
@@ -526,7 +516,7 @@ mod tests { | |||
526 | 516 | ||
527 | my_var | 517 | my_var |
528 | }"#, | 518 | }"#, |
529 | r#"fn foo() -> Result<i32, <|>> { | 519 | r#"fn foo() -> Result<i32, ${0:_}> { |
530 | let my_var = let x = loop { | 520 | let my_var = let x = loop { |
531 | break 1; | 521 | break 1; |
532 | }; | 522 | }; |
@@ -549,7 +539,7 @@ mod tests { | |||
549 | 539 | ||
550 | res | 540 | res |
551 | }"#, | 541 | }"#, |
552 | r#"fn foo() -> Result<i32, <|>> { | 542 | r#"fn foo() -> Result<i32, ${0:_}> { |
553 | let my_var = 5; | 543 | let my_var = 5; |
554 | let res = match my_var { | 544 | let res = match my_var { |
555 | 5 => 42i32, | 545 | 5 => 42i32, |
@@ -572,7 +562,7 @@ mod tests { | |||
572 | 562 | ||
573 | res | 563 | res |
574 | }"#, | 564 | }"#, |
575 | r#"fn foo() -> Result<i32, <|>> { | 565 | r#"fn foo() -> Result<i32, ${0:_}> { |
576 | let my_var = 5; | 566 | let my_var = 5; |
577 | let res = if my_var == 5 { | 567 | let res = if my_var == 5 { |
578 | 42i32 | 568 | 42i32 |
@@ -608,7 +598,7 @@ mod tests { | |||
608 | }, | 598 | }, |
609 | } | 599 | } |
610 | }"#, | 600 | }"#, |
611 | r#"fn foo() -> Result<i32, <|>> { | 601 | r#"fn foo() -> Result<i32, ${0:_}> { |
612 | let my_var = 5; | 602 | let my_var = 5; |
613 | match my_var { | 603 | match my_var { |
614 | 5 => { | 604 | 5 => { |
@@ -641,7 +631,7 @@ mod tests { | |||
641 | } | 631 | } |
642 | 53i32 | 632 | 53i32 |
643 | }"#, | 633 | }"#, |
644 | r#"fn foo() -> Result<i32, <|>> { | 634 | r#"fn foo() -> Result<i32, ${0:_}> { |
645 | let test = "test"; | 635 | let test = "test"; |
646 | if test == "test" { | 636 | if test == "test" { |
647 | return Ok(24i32); | 637 | return Ok(24i32); |
@@ -672,7 +662,7 @@ mod tests { | |||
672 | 662 | ||
673 | the_field | 663 | the_field |
674 | }"#, | 664 | }"#, |
675 | r#"fn foo(the_field: u32) -> Result<u32, <|>> { | 665 | r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> { |
676 | let true_closure = || { | 666 | let true_closure = || { |
677 | return true; | 667 | return true; |
678 | }; | 668 | }; |
@@ -711,7 +701,7 @@ mod tests { | |||
711 | 701 | ||
712 | t.unwrap_or_else(|| the_field) | 702 | t.unwrap_or_else(|| the_field) |
713 | }"#, | 703 | }"#, |
714 | r#"fn foo(the_field: u32) -> Result<u32, <|>> { | 704 | r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> { |
715 | let true_closure = || { | 705 | let true_closure = || { |
716 | return true; | 706 | return true; |
717 | }; | 707 | }; |
@@ -749,7 +739,7 @@ mod tests { | |||
749 | i += 1; | 739 | i += 1; |
750 | } | 740 | } |
751 | }"#, | 741 | }"#, |
752 | r#"fn foo() -> Result<i32, <|>> { | 742 | r#"fn foo() -> Result<i32, ${0:_}> { |
753 | let test = "test"; | 743 | let test = "test"; |
754 | if test == "test" { | 744 | if test == "test" { |
755 | return Ok(24i32); | 745 | return Ok(24i32); |
@@ -781,7 +771,7 @@ mod tests { | |||
781 | } | 771 | } |
782 | } | 772 | } |
783 | }"#, | 773 | }"#, |
784 | r#"fn foo() -> Result<i32, <|>> { | 774 | r#"fn foo() -> Result<i32, ${0:_}> { |
785 | let test = "test"; | 775 | let test = "test"; |
786 | if test == "test" { | 776 | if test == "test" { |
787 | return Ok(24i32); | 777 | return Ok(24i32); |
@@ -819,7 +809,7 @@ mod tests { | |||
819 | } | 809 | } |
820 | } | 810 | } |
821 | }"#, | 811 | }"#, |
822 | r#"fn foo() -> Result<i32, <|>> { | 812 | r#"fn foo() -> Result<i32, ${0:_}> { |
823 | let test = "test"; | 813 | let test = "test"; |
824 | let other = 5; | 814 | let other = 5; |
825 | if test == "test" { | 815 | if test == "test" { |
@@ -860,7 +850,7 @@ mod tests { | |||
860 | 850 | ||
861 | the_field | 851 | the_field |
862 | }"#, | 852 | }"#, |
863 | r#"fn foo(the_field: u32) -> Result<u32, <|>> { | 853 | r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> { |
864 | if the_field < 5 { | 854 | if the_field < 5 { |
865 | let mut i = 0; | 855 | let mut i = 0; |
866 | loop { | 856 | loop { |
@@ -894,7 +884,7 @@ mod tests { | |||
894 | 884 | ||
895 | the_field | 885 | the_field |
896 | }"#, | 886 | }"#, |
897 | r#"fn foo(the_field: u32) -> Result<u32, <|>> { | 887 | r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> { |
898 | if the_field < 5 { | 888 | if the_field < 5 { |
899 | let mut i = 0; | 889 | let mut i = 0; |
900 | 890 | ||
@@ -923,7 +913,7 @@ mod tests { | |||
923 | 913 | ||
924 | the_field | 914 | the_field |
925 | }"#, | 915 | }"#, |
926 | r#"fn foo(the_field: u32) -> Result<u32, <|>> { | 916 | r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> { |
927 | if the_field < 5 { | 917 | if the_field < 5 { |
928 | let mut i = 0; | 918 | let mut i = 0; |
929 | 919 | ||
@@ -953,7 +943,7 @@ mod tests { | |||
953 | 943 | ||
954 | the_field | 944 | the_field |
955 | }"#, | 945 | }"#, |
956 | r#"fn foo(the_field: u32) -> Result<u32, <|>> { | 946 | r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> { |
957 | if the_field < 5 { | 947 | if the_field < 5 { |
958 | let mut i = 0; | 948 | let mut i = 0; |
959 | 949 | ||
diff --git a/crates/ra_assists/src/handlers/change_visibility.rs b/crates/ra_assists/src/handlers/change_visibility.rs index 40cf4b422..c21d75be0 100644 --- a/crates/ra_assists/src/handlers/change_visibility.rs +++ b/crates/ra_assists/src/handlers/change_visibility.rs | |||
@@ -5,14 +5,11 @@ use ra_syntax::{ | |||
5 | ATTR, COMMENT, CONST_DEF, ENUM_DEF, FN_DEF, MODULE, STRUCT_DEF, TRAIT_DEF, VISIBILITY, | 5 | ATTR, COMMENT, CONST_DEF, ENUM_DEF, FN_DEF, MODULE, STRUCT_DEF, TRAIT_DEF, VISIBILITY, |
6 | WHITESPACE, | 6 | WHITESPACE, |
7 | }, | 7 | }, |
8 | SyntaxNode, TextRange, TextSize, T, | 8 | SyntaxNode, TextSize, T, |
9 | }; | 9 | }; |
10 | 10 | use test_utils::mark; | |
11 | use hir::{db::HirDatabase, HasSource, HasVisibility, PathResolution}; | ||
12 | use test_utils::tested_by; | ||
13 | 11 | ||
14 | use crate::{AssistContext, AssistId, Assists}; | 12 | use crate::{AssistContext, AssistId, Assists}; |
15 | use ra_db::FileId; | ||
16 | 13 | ||
17 | // Assist: change_visibility | 14 | // Assist: change_visibility |
18 | // | 15 | // |
@@ -30,8 +27,6 @@ pub(crate) fn change_visibility(acc: &mut Assists, ctx: &AssistContext) -> Optio | |||
30 | return change_vis(acc, vis); | 27 | return change_vis(acc, vis); |
31 | } | 28 | } |
32 | add_vis(acc, ctx) | 29 | add_vis(acc, ctx) |
33 | .or_else(|| add_vis_to_referenced_module_def(acc, ctx)) | ||
34 | .or_else(|| add_vis_to_referenced_record_field(acc, ctx)) | ||
35 | } | 30 | } |
36 | 31 | ||
37 | fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 32 | fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
@@ -55,7 +50,7 @@ fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | |||
55 | } else if let Some(field_name) = ctx.find_node_at_offset::<ast::Name>() { | 50 | } else if let Some(field_name) = ctx.find_node_at_offset::<ast::Name>() { |
56 | let field = field_name.syntax().ancestors().find_map(ast::RecordFieldDef::cast)?; | 51 | let field = field_name.syntax().ancestors().find_map(ast::RecordFieldDef::cast)?; |
57 | if field.name()? != field_name { | 52 | if field.name()? != field_name { |
58 | tested_by!(change_visibility_field_false_positive); | 53 | mark::hit!(change_visibility_field_false_positive); |
59 | return None; | 54 | return None; |
60 | } | 55 | } |
61 | if field.visibility().is_some() { | 56 | if field.visibility().is_some() { |
@@ -73,147 +68,9 @@ fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | |||
73 | 68 | ||
74 | acc.add(AssistId("change_visibility"), "Change visibility to pub(crate)", target, |edit| { | 69 | acc.add(AssistId("change_visibility"), "Change visibility to pub(crate)", target, |edit| { |
75 | edit.insert(offset, "pub(crate) "); | 70 | edit.insert(offset, "pub(crate) "); |
76 | edit.set_cursor(offset); | ||
77 | }) | ||
78 | } | ||
79 | |||
80 | fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
81 | let path: ast::Path = ctx.find_node_at_offset()?; | ||
82 | let path_res = ctx.sema.resolve_path(&path)?; | ||
83 | let def = match path_res { | ||
84 | PathResolution::Def(def) => def, | ||
85 | _ => return None, | ||
86 | }; | ||
87 | |||
88 | let current_module = ctx.sema.scope(&path.syntax()).module()?; | ||
89 | let target_module = def.module(ctx.db)?; | ||
90 | |||
91 | let vis = target_module.visibility_of(ctx.db, &def)?; | ||
92 | if vis.is_visible_from(ctx.db, current_module.into()) { | ||
93 | return None; | ||
94 | }; | ||
95 | |||
96 | let (offset, target, target_file, target_name) = target_data_for_def(ctx.db, def)?; | ||
97 | |||
98 | let missing_visibility = | ||
99 | if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" }; | ||
100 | |||
101 | let assist_label = match target_name { | ||
102 | None => format!("Change visibility to {}", missing_visibility), | ||
103 | Some(name) => format!("Change visibility of {} to {}", name, missing_visibility), | ||
104 | }; | ||
105 | |||
106 | acc.add(AssistId("change_visibility"), assist_label, target, |edit| { | ||
107 | edit.set_file(target_file); | ||
108 | edit.insert(offset, format!("{} ", missing_visibility)); | ||
109 | edit.set_cursor(offset); | ||
110 | }) | ||
111 | } | ||
112 | |||
113 | fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
114 | let record_field: ast::RecordField = ctx.find_node_at_offset()?; | ||
115 | let (record_field_def, _) = ctx.sema.resolve_record_field(&record_field)?; | ||
116 | |||
117 | let current_module = ctx.sema.scope(record_field.syntax()).module()?; | ||
118 | let visibility = record_field_def.visibility(ctx.db); | ||
119 | if visibility.is_visible_from(ctx.db, current_module.into()) { | ||
120 | return None; | ||
121 | } | ||
122 | |||
123 | let parent = record_field_def.parent_def(ctx.db); | ||
124 | let parent_name = parent.name(ctx.db); | ||
125 | let target_module = parent.module(ctx.db); | ||
126 | |||
127 | let in_file_source = record_field_def.source(ctx.db); | ||
128 | let (offset, target) = match in_file_source.value { | ||
129 | hir::FieldSource::Named(it) => { | ||
130 | let s = it.syntax(); | ||
131 | (vis_offset(s), s.text_range()) | ||
132 | } | ||
133 | hir::FieldSource::Pos(it) => { | ||
134 | let s = it.syntax(); | ||
135 | (vis_offset(s), s.text_range()) | ||
136 | } | ||
137 | }; | ||
138 | |||
139 | let missing_visibility = | ||
140 | if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" }; | ||
141 | let target_file = in_file_source.file_id.original_file(ctx.db); | ||
142 | |||
143 | let target_name = record_field_def.name(ctx.db); | ||
144 | let assist_label = | ||
145 | format!("Change visibility of {}.{} to {}", parent_name, target_name, missing_visibility); | ||
146 | |||
147 | acc.add(AssistId("change_visibility"), assist_label, target, |edit| { | ||
148 | edit.set_file(target_file); | ||
149 | edit.insert(offset, format!("{} ", missing_visibility)); | ||
150 | edit.set_cursor(offset) | ||
151 | }) | 71 | }) |
152 | } | 72 | } |
153 | 73 | ||
154 | fn target_data_for_def( | ||
155 | db: &dyn HirDatabase, | ||
156 | def: hir::ModuleDef, | ||
157 | ) -> Option<(TextSize, TextRange, FileId, Option<hir::Name>)> { | ||
158 | fn offset_target_and_file_id<S, Ast>( | ||
159 | db: &dyn HirDatabase, | ||
160 | x: S, | ||
161 | ) -> (TextSize, TextRange, FileId) | ||
162 | where | ||
163 | S: HasSource<Ast = Ast>, | ||
164 | Ast: AstNode, | ||
165 | { | ||
166 | let source = x.source(db); | ||
167 | let in_file_syntax = source.syntax(); | ||
168 | let file_id = in_file_syntax.file_id; | ||
169 | let syntax = in_file_syntax.value; | ||
170 | (vis_offset(syntax), syntax.text_range(), file_id.original_file(db.upcast())) | ||
171 | } | ||
172 | |||
173 | let target_name; | ||
174 | let (offset, target, target_file) = match def { | ||
175 | hir::ModuleDef::Function(f) => { | ||
176 | target_name = Some(f.name(db)); | ||
177 | offset_target_and_file_id(db, f) | ||
178 | } | ||
179 | hir::ModuleDef::Adt(adt) => { | ||
180 | target_name = Some(adt.name(db)); | ||
181 | match adt { | ||
182 | hir::Adt::Struct(s) => offset_target_and_file_id(db, s), | ||
183 | hir::Adt::Union(u) => offset_target_and_file_id(db, u), | ||
184 | hir::Adt::Enum(e) => offset_target_and_file_id(db, e), | ||
185 | } | ||
186 | } | ||
187 | hir::ModuleDef::Const(c) => { | ||
188 | target_name = c.name(db); | ||
189 | offset_target_and_file_id(db, c) | ||
190 | } | ||
191 | hir::ModuleDef::Static(s) => { | ||
192 | target_name = s.name(db); | ||
193 | offset_target_and_file_id(db, s) | ||
194 | } | ||
195 | hir::ModuleDef::Trait(t) => { | ||
196 | target_name = Some(t.name(db)); | ||
197 | offset_target_and_file_id(db, t) | ||
198 | } | ||
199 | hir::ModuleDef::TypeAlias(t) => { | ||
200 | target_name = Some(t.name(db)); | ||
201 | offset_target_and_file_id(db, t) | ||
202 | } | ||
203 | hir::ModuleDef::Module(m) => { | ||
204 | target_name = m.name(db); | ||
205 | let in_file_source = m.declaration_source(db)?; | ||
206 | let file_id = in_file_source.file_id.original_file(db.upcast()); | ||
207 | let syntax = in_file_source.value.syntax(); | ||
208 | (vis_offset(syntax), syntax.text_range(), file_id) | ||
209 | } | ||
210 | // Enum variants can't be private, we can't modify builtin types | ||
211 | hir::ModuleDef::EnumVariant(_) | hir::ModuleDef::BuiltinType(_) => return None, | ||
212 | }; | ||
213 | |||
214 | Some((offset, target, target_file, target_name)) | ||
215 | } | ||
216 | |||
217 | fn vis_offset(node: &SyntaxNode) -> TextSize { | 74 | fn vis_offset(node: &SyntaxNode) -> TextSize { |
218 | node.children_with_tokens() | 75 | node.children_with_tokens() |
219 | .skip_while(|it| match it.kind() { | 76 | .skip_while(|it| match it.kind() { |
@@ -234,7 +91,6 @@ fn change_vis(acc: &mut Assists, vis: ast::Visibility) -> Option<()> { | |||
234 | target, | 91 | target, |
235 | |edit| { | 92 | |edit| { |
236 | edit.replace(vis.syntax().text_range(), "pub(crate)"); | 93 | edit.replace(vis.syntax().text_range(), "pub(crate)"); |
237 | edit.set_cursor(vis.syntax().text_range().start()) | ||
238 | }, | 94 | }, |
239 | ); | 95 | ); |
240 | } | 96 | } |
@@ -246,7 +102,6 @@ fn change_vis(acc: &mut Assists, vis: ast::Visibility) -> Option<()> { | |||
246 | target, | 102 | target, |
247 | |edit| { | 103 | |edit| { |
248 | edit.replace(vis.syntax().text_range(), "pub"); | 104 | edit.replace(vis.syntax().text_range(), "pub"); |
249 | edit.set_cursor(vis.syntax().text_range().start()); | ||
250 | }, | 105 | }, |
251 | ); | 106 | ); |
252 | } | 107 | } |
@@ -255,7 +110,7 @@ fn change_vis(acc: &mut Assists, vis: ast::Visibility) -> Option<()> { | |||
255 | 110 | ||
256 | #[cfg(test)] | 111 | #[cfg(test)] |
257 | mod tests { | 112 | mod tests { |
258 | use test_utils::covers; | 113 | use test_utils::mark; |
259 | 114 | ||
260 | use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; | 115 | use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; |
261 | 116 | ||
@@ -263,17 +118,13 @@ mod tests { | |||
263 | 118 | ||
264 | #[test] | 119 | #[test] |
265 | fn change_visibility_adds_pub_crate_to_items() { | 120 | fn change_visibility_adds_pub_crate_to_items() { |
266 | check_assist(change_visibility, "<|>fn foo() {}", "<|>pub(crate) fn foo() {}"); | 121 | check_assist(change_visibility, "<|>fn foo() {}", "pub(crate) fn foo() {}"); |
267 | check_assist(change_visibility, "f<|>n foo() {}", "<|>pub(crate) fn foo() {}"); | 122 | check_assist(change_visibility, "f<|>n foo() {}", "pub(crate) fn foo() {}"); |
268 | check_assist(change_visibility, "<|>struct Foo {}", "<|>pub(crate) struct Foo {}"); | 123 | check_assist(change_visibility, "<|>struct Foo {}", "pub(crate) struct Foo {}"); |
269 | check_assist(change_visibility, "<|>mod foo {}", "<|>pub(crate) mod foo {}"); | 124 | check_assist(change_visibility, "<|>mod foo {}", "pub(crate) mod foo {}"); |
270 | check_assist(change_visibility, "<|>trait Foo {}", "<|>pub(crate) trait Foo {}"); | 125 | check_assist(change_visibility, "<|>trait Foo {}", "pub(crate) trait Foo {}"); |
271 | check_assist(change_visibility, "m<|>od {}", "<|>pub(crate) mod {}"); | 126 | check_assist(change_visibility, "m<|>od {}", "pub(crate) mod {}"); |
272 | check_assist( | 127 | check_assist(change_visibility, "unsafe f<|>n foo() {}", "pub(crate) unsafe fn foo() {}"); |
273 | change_visibility, | ||
274 | "unsafe f<|>n foo() {}", | ||
275 | "<|>pub(crate) unsafe fn foo() {}", | ||
276 | ); | ||
277 | } | 128 | } |
278 | 129 | ||
279 | #[test] | 130 | #[test] |
@@ -281,14 +132,14 @@ mod tests { | |||
281 | check_assist( | 132 | check_assist( |
282 | change_visibility, | 133 | change_visibility, |
283 | r"struct S { <|>field: u32 }", | 134 | r"struct S { <|>field: u32 }", |
284 | r"struct S { <|>pub(crate) field: u32 }", | 135 | r"struct S { pub(crate) field: u32 }", |
285 | ); | 136 | ); |
286 | check_assist(change_visibility, r"struct S ( <|>u32 )", r"struct S ( <|>pub(crate) u32 )"); | 137 | check_assist(change_visibility, r"struct S ( <|>u32 )", r"struct S ( pub(crate) u32 )"); |
287 | } | 138 | } |
288 | 139 | ||
289 | #[test] | 140 | #[test] |
290 | fn change_visibility_field_false_positive() { | 141 | fn change_visibility_field_false_positive() { |
291 | covers!(change_visibility_field_false_positive); | 142 | mark::check!(change_visibility_field_false_positive); |
292 | check_assist_not_applicable( | 143 | check_assist_not_applicable( |
293 | change_visibility, | 144 | change_visibility, |
294 | r"struct S { field: [(); { let <|>x = ();}] }", | 145 | r"struct S { field: [(); { let <|>x = ();}] }", |
@@ -297,17 +148,17 @@ mod tests { | |||
297 | 148 | ||
298 | #[test] | 149 | #[test] |
299 | fn change_visibility_pub_to_pub_crate() { | 150 | fn change_visibility_pub_to_pub_crate() { |
300 | check_assist(change_visibility, "<|>pub fn foo() {}", "<|>pub(crate) fn foo() {}") | 151 | check_assist(change_visibility, "<|>pub fn foo() {}", "pub(crate) fn foo() {}") |
301 | } | 152 | } |
302 | 153 | ||
303 | #[test] | 154 | #[test] |
304 | fn change_visibility_pub_crate_to_pub() { | 155 | fn change_visibility_pub_crate_to_pub() { |
305 | check_assist(change_visibility, "<|>pub(crate) fn foo() {}", "<|>pub fn foo() {}") | 156 | check_assist(change_visibility, "<|>pub(crate) fn foo() {}", "pub fn foo() {}") |
306 | } | 157 | } |
307 | 158 | ||
308 | #[test] | 159 | #[test] |
309 | fn change_visibility_const() { | 160 | fn change_visibility_const() { |
310 | check_assist(change_visibility, "<|>const FOO = 3u8;", "<|>pub(crate) const FOO = 3u8;"); | 161 | check_assist(change_visibility, "<|>const FOO = 3u8;", "pub(crate) const FOO = 3u8;"); |
311 | } | 162 | } |
312 | 163 | ||
313 | #[test] | 164 | #[test] |
@@ -328,199 +179,12 @@ mod tests { | |||
328 | // comments | 179 | // comments |
329 | 180 | ||
330 | #[derive(Debug)] | 181 | #[derive(Debug)] |
331 | <|>pub(crate) struct Foo; | 182 | pub(crate) struct Foo; |
332 | ", | 183 | ", |
333 | ) | 184 | ) |
334 | } | 185 | } |
335 | 186 | ||
336 | #[test] | 187 | #[test] |
337 | fn change_visibility_of_fn_via_path() { | ||
338 | check_assist( | ||
339 | change_visibility, | ||
340 | r"mod foo { fn foo() {} } | ||
341 | fn main() { foo::foo<|>() } ", | ||
342 | r"mod foo { <|>pub(crate) fn foo() {} } | ||
343 | fn main() { foo::foo() } ", | ||
344 | ); | ||
345 | check_assist_not_applicable( | ||
346 | change_visibility, | ||
347 | r"mod foo { pub fn foo() {} } | ||
348 | fn main() { foo::foo<|>() } ", | ||
349 | ) | ||
350 | } | ||
351 | |||
352 | #[test] | ||
353 | fn change_visibility_of_adt_in_submodule_via_path() { | ||
354 | check_assist( | ||
355 | change_visibility, | ||
356 | r"mod foo { struct Foo; } | ||
357 | fn main() { foo::Foo<|> } ", | ||
358 | r"mod foo { <|>pub(crate) struct Foo; } | ||
359 | fn main() { foo::Foo } ", | ||
360 | ); | ||
361 | check_assist_not_applicable( | ||
362 | change_visibility, | ||
363 | r"mod foo { pub struct Foo; } | ||
364 | fn main() { foo::Foo<|> } ", | ||
365 | ); | ||
366 | check_assist( | ||
367 | change_visibility, | ||
368 | r"mod foo { enum Foo; } | ||
369 | fn main() { foo::Foo<|> } ", | ||
370 | r"mod foo { <|>pub(crate) enum Foo; } | ||
371 | fn main() { foo::Foo } ", | ||
372 | ); | ||
373 | check_assist_not_applicable( | ||
374 | change_visibility, | ||
375 | r"mod foo { pub enum Foo; } | ||
376 | fn main() { foo::Foo<|> } ", | ||
377 | ); | ||
378 | check_assist( | ||
379 | change_visibility, | ||
380 | r"mod foo { union Foo; } | ||
381 | fn main() { foo::Foo<|> } ", | ||
382 | r"mod foo { <|>pub(crate) union Foo; } | ||
383 | fn main() { foo::Foo } ", | ||
384 | ); | ||
385 | check_assist_not_applicable( | ||
386 | change_visibility, | ||
387 | r"mod foo { pub union Foo; } | ||
388 | fn main() { foo::Foo<|> } ", | ||
389 | ); | ||
390 | } | ||
391 | |||
392 | #[test] | ||
393 | fn change_visibility_of_adt_in_other_file_via_path() { | ||
394 | check_assist( | ||
395 | change_visibility, | ||
396 | r" | ||
397 | //- /main.rs | ||
398 | mod foo; | ||
399 | fn main() { foo::Foo<|> } | ||
400 | |||
401 | //- /foo.rs | ||
402 | struct Foo; | ||
403 | ", | ||
404 | r"<|>pub(crate) struct Foo; | ||
405 | |||
406 | ", | ||
407 | ); | ||
408 | } | ||
409 | |||
410 | #[test] | ||
411 | fn change_visibility_of_struct_field_via_path() { | ||
412 | check_assist( | ||
413 | change_visibility, | ||
414 | r"mod foo { pub struct Foo { bar: (), } } | ||
415 | fn main() { foo::Foo { <|>bar: () }; } ", | ||
416 | r"mod foo { pub struct Foo { <|>pub(crate) bar: (), } } | ||
417 | fn main() { foo::Foo { bar: () }; } ", | ||
418 | ); | ||
419 | check_assist( | ||
420 | change_visibility, | ||
421 | r"//- /lib.rs | ||
422 | mod foo; | ||
423 | fn main() { foo::Foo { <|>bar: () }; } | ||
424 | //- /foo.rs | ||
425 | pub struct Foo { bar: () } | ||
426 | ", | ||
427 | r"pub struct Foo { <|>pub(crate) bar: () } | ||
428 | |||
429 | ", | ||
430 | ); | ||
431 | check_assist_not_applicable( | ||
432 | change_visibility, | ||
433 | r"mod foo { pub struct Foo { pub bar: (), } } | ||
434 | fn main() { foo::Foo { <|>bar: () }; } ", | ||
435 | ); | ||
436 | check_assist_not_applicable( | ||
437 | change_visibility, | ||
438 | r"//- /lib.rs | ||
439 | mod foo; | ||
440 | fn main() { foo::Foo { <|>bar: () }; } | ||
441 | //- /foo.rs | ||
442 | pub struct Foo { pub bar: () } | ||
443 | ", | ||
444 | ); | ||
445 | } | ||
446 | |||
447 | #[test] | ||
448 | fn change_visibility_of_enum_variant_field_via_path() { | ||
449 | check_assist( | ||
450 | change_visibility, | ||
451 | r"mod foo { pub enum Foo { Bar { bar: () } } } | ||
452 | fn main() { foo::Foo::Bar { <|>bar: () }; } ", | ||
453 | r"mod foo { pub enum Foo { Bar { <|>pub(crate) bar: () } } } | ||
454 | fn main() { foo::Foo::Bar { bar: () }; } ", | ||
455 | ); | ||
456 | check_assist( | ||
457 | change_visibility, | ||
458 | r"//- /lib.rs | ||
459 | mod foo; | ||
460 | fn main() { foo::Foo::Bar { <|>bar: () }; } | ||
461 | //- /foo.rs | ||
462 | pub enum Foo { Bar { bar: () } } | ||
463 | ", | ||
464 | r"pub enum Foo { Bar { <|>pub(crate) bar: () } } | ||
465 | |||
466 | ", | ||
467 | ); | ||
468 | check_assist_not_applicable( | ||
469 | change_visibility, | ||
470 | r"mod foo { pub struct Foo { pub bar: (), } } | ||
471 | fn main() { foo::Foo { <|>bar: () }; } ", | ||
472 | ); | ||
473 | check_assist_not_applicable( | ||
474 | change_visibility, | ||
475 | r"//- /lib.rs | ||
476 | mod foo; | ||
477 | fn main() { foo::Foo { <|>bar: () }; } | ||
478 | //- /foo.rs | ||
479 | pub struct Foo { pub bar: () } | ||
480 | ", | ||
481 | ); | ||
482 | } | ||
483 | |||
484 | #[test] | ||
485 | #[ignore] | ||
486 | // FIXME reenable this test when `Semantics::resolve_record_field` works with union fields | ||
487 | fn change_visibility_of_union_field_via_path() { | ||
488 | check_assist( | ||
489 | change_visibility, | ||
490 | r"mod foo { pub union Foo { bar: (), } } | ||
491 | fn main() { foo::Foo { <|>bar: () }; } ", | ||
492 | r"mod foo { pub union Foo { <|>pub(crate) bar: (), } } | ||
493 | fn main() { foo::Foo { bar: () }; } ", | ||
494 | ); | ||
495 | check_assist( | ||
496 | change_visibility, | ||
497 | r"//- /lib.rs | ||
498 | mod foo; | ||
499 | fn main() { foo::Foo { <|>bar: () }; } | ||
500 | //- /foo.rs | ||
501 | pub union Foo { bar: () } | ||
502 | ", | ||
503 | r"pub union Foo { <|>pub(crate) bar: () } | ||
504 | |||
505 | ", | ||
506 | ); | ||
507 | check_assist_not_applicable( | ||
508 | change_visibility, | ||
509 | r"mod foo { pub union Foo { pub bar: (), } } | ||
510 | fn main() { foo::Foo { <|>bar: () }; } ", | ||
511 | ); | ||
512 | check_assist_not_applicable( | ||
513 | change_visibility, | ||
514 | r"//- /lib.rs | ||
515 | mod foo; | ||
516 | fn main() { foo::Foo { <|>bar: () }; } | ||
517 | //- /foo.rs | ||
518 | pub union Foo { pub bar: () } | ||
519 | ", | ||
520 | ); | ||
521 | } | ||
522 | |||
523 | #[test] | ||
524 | fn not_applicable_for_enum_variants() { | 188 | fn not_applicable_for_enum_variants() { |
525 | check_assist_not_applicable( | 189 | check_assist_not_applicable( |
526 | change_visibility, | 190 | change_visibility, |
@@ -530,182 +194,6 @@ mod tests { | |||
530 | } | 194 | } |
531 | 195 | ||
532 | #[test] | 196 | #[test] |
533 | fn change_visibility_of_const_via_path() { | ||
534 | check_assist( | ||
535 | change_visibility, | ||
536 | r"mod foo { const FOO: () = (); } | ||
537 | fn main() { foo::FOO<|> } ", | ||
538 | r"mod foo { <|>pub(crate) const FOO: () = (); } | ||
539 | fn main() { foo::FOO } ", | ||
540 | ); | ||
541 | check_assist_not_applicable( | ||
542 | change_visibility, | ||
543 | r"mod foo { pub const FOO: () = (); } | ||
544 | fn main() { foo::FOO<|> } ", | ||
545 | ); | ||
546 | } | ||
547 | |||
548 | #[test] | ||
549 | fn change_visibility_of_static_via_path() { | ||
550 | check_assist( | ||
551 | change_visibility, | ||
552 | r"mod foo { static FOO: () = (); } | ||
553 | fn main() { foo::FOO<|> } ", | ||
554 | r"mod foo { <|>pub(crate) static FOO: () = (); } | ||
555 | fn main() { foo::FOO } ", | ||
556 | ); | ||
557 | check_assist_not_applicable( | ||
558 | change_visibility, | ||
559 | r"mod foo { pub static FOO: () = (); } | ||
560 | fn main() { foo::FOO<|> } ", | ||
561 | ); | ||
562 | } | ||
563 | |||
564 | #[test] | ||
565 | fn change_visibility_of_trait_via_path() { | ||
566 | check_assist( | ||
567 | change_visibility, | ||
568 | r"mod foo { trait Foo { fn foo(&self) {} } } | ||
569 | fn main() { let x: &dyn foo::<|>Foo; } ", | ||
570 | r"mod foo { <|>pub(crate) trait Foo { fn foo(&self) {} } } | ||
571 | fn main() { let x: &dyn foo::Foo; } ", | ||
572 | ); | ||
573 | check_assist_not_applicable( | ||
574 | change_visibility, | ||
575 | r"mod foo { pub trait Foo { fn foo(&self) {} } } | ||
576 | fn main() { let x: &dyn foo::Foo<|>; } ", | ||
577 | ); | ||
578 | } | ||
579 | |||
580 | #[test] | ||
581 | fn change_visibility_of_type_alias_via_path() { | ||
582 | check_assist( | ||
583 | change_visibility, | ||
584 | r"mod foo { type Foo = (); } | ||
585 | fn main() { let x: foo::Foo<|>; } ", | ||
586 | r"mod foo { <|>pub(crate) type Foo = (); } | ||
587 | fn main() { let x: foo::Foo; } ", | ||
588 | ); | ||
589 | check_assist_not_applicable( | ||
590 | change_visibility, | ||
591 | r"mod foo { pub type Foo = (); } | ||
592 | fn main() { let x: foo::Foo<|>; } ", | ||
593 | ); | ||
594 | } | ||
595 | |||
596 | #[test] | ||
597 | fn change_visibility_of_module_via_path() { | ||
598 | check_assist( | ||
599 | change_visibility, | ||
600 | r"mod foo { mod bar { fn bar() {} } } | ||
601 | fn main() { foo::bar<|>::bar(); } ", | ||
602 | r"mod foo { <|>pub(crate) mod bar { fn bar() {} } } | ||
603 | fn main() { foo::bar::bar(); } ", | ||
604 | ); | ||
605 | |||
606 | check_assist( | ||
607 | change_visibility, | ||
608 | r" | ||
609 | //- /main.rs | ||
610 | mod foo; | ||
611 | fn main() { foo::bar<|>::baz(); } | ||
612 | |||
613 | //- /foo.rs | ||
614 | mod bar { | ||
615 | pub fn baz() {} | ||
616 | } | ||
617 | ", | ||
618 | r"<|>pub(crate) mod bar { | ||
619 | pub fn baz() {} | ||
620 | } | ||
621 | |||
622 | ", | ||
623 | ); | ||
624 | |||
625 | check_assist_not_applicable( | ||
626 | change_visibility, | ||
627 | r"mod foo { pub mod bar { pub fn bar() {} } } | ||
628 | fn main() { foo::bar<|>::bar(); } ", | ||
629 | ); | ||
630 | } | ||
631 | |||
632 | #[test] | ||
633 | fn change_visibility_of_inline_module_in_other_file_via_path() { | ||
634 | check_assist( | ||
635 | change_visibility, | ||
636 | r" | ||
637 | //- /main.rs | ||
638 | mod foo; | ||
639 | fn main() { foo::bar<|>::baz(); } | ||
640 | |||
641 | //- /foo.rs | ||
642 | mod bar; | ||
643 | |||
644 | //- /foo/bar.rs | ||
645 | pub fn baz() {} | ||
646 | } | ||
647 | ", | ||
648 | r"<|>pub(crate) mod bar; | ||
649 | ", | ||
650 | ); | ||
651 | } | ||
652 | |||
653 | #[test] | ||
654 | fn change_visibility_of_module_declaration_in_other_file_via_path() { | ||
655 | check_assist( | ||
656 | change_visibility, | ||
657 | r"//- /main.rs | ||
658 | mod foo; | ||
659 | fn main() { foo::bar<|>>::baz(); } | ||
660 | |||
661 | //- /foo.rs | ||
662 | mod bar { | ||
663 | pub fn baz() {} | ||
664 | }", | ||
665 | r"<|>pub(crate) mod bar { | ||
666 | pub fn baz() {} | ||
667 | } | ||
668 | ", | ||
669 | ); | ||
670 | } | ||
671 | |||
672 | #[test] | ||
673 | #[ignore] | ||
674 | // FIXME handle reexports properly | ||
675 | fn change_visibility_of_reexport() { | ||
676 | check_assist( | ||
677 | change_visibility, | ||
678 | r" | ||
679 | mod foo { | ||
680 | use bar::Baz; | ||
681 | mod bar { pub(super) struct Baz; } | ||
682 | } | ||
683 | foo::Baz<|> | ||
684 | ", | ||
685 | r" | ||
686 | mod foo { | ||
687 | <|>pub(crate) use bar::Baz; | ||
688 | mod bar { pub(super) struct Baz; } | ||
689 | } | ||
690 | foo::Baz | ||
691 | ", | ||
692 | ) | ||
693 | } | ||
694 | |||
695 | #[test] | ||
696 | fn adds_pub_when_target_is_in_another_crate() { | ||
697 | check_assist( | ||
698 | change_visibility, | ||
699 | r"//- /main.rs crate:a deps:foo | ||
700 | foo::Bar<|> | ||
701 | //- /lib.rs crate:foo | ||
702 | struct Bar;", | ||
703 | r"<|>pub struct Bar; | ||
704 | ", | ||
705 | ) | ||
706 | } | ||
707 | |||
708 | #[test] | ||
709 | fn change_visibility_target() { | 197 | fn change_visibility_target() { |
710 | check_assist_target(change_visibility, "<|>fn foo() {}", "fn"); | 198 | check_assist_target(change_visibility, "<|>fn foo() {}", "fn"); |
711 | check_assist_target(change_visibility, "pub(crate)<|> fn foo() {}", "pub(crate)"); | 199 | check_assist_target(change_visibility, "pub(crate)<|> fn foo() {}", "pub(crate)"); |
diff --git a/crates/ra_assists/src/handlers/early_return.rs b/crates/ra_assists/src/handlers/early_return.rs index 66b296081..4cc75a7ce 100644 --- a/crates/ra_assists/src/handlers/early_return.rs +++ b/crates/ra_assists/src/handlers/early_return.rs | |||
@@ -97,7 +97,6 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext) | |||
97 | } | 97 | } |
98 | 98 | ||
99 | then_block.syntax().last_child_or_token().filter(|t| t.kind() == R_CURLY)?; | 99 | then_block.syntax().last_child_or_token().filter(|t| t.kind() == R_CURLY)?; |
100 | let cursor_position = ctx.offset(); | ||
101 | 100 | ||
102 | let target = if_expr.syntax().text_range(); | 101 | let target = if_expr.syntax().text_range(); |
103 | acc.add(AssistId("convert_to_guarded_return"), "Convert to guarded return", target, |edit| { | 102 | acc.add(AssistId("convert_to_guarded_return"), "Convert to guarded return", target, |edit| { |
@@ -148,7 +147,6 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext) | |||
148 | } | 147 | } |
149 | }; | 148 | }; |
150 | edit.replace_ast(parent_block, ast::BlockExpr::cast(new_block).unwrap()); | 149 | edit.replace_ast(parent_block, ast::BlockExpr::cast(new_block).unwrap()); |
151 | edit.set_cursor(cursor_position); | ||
152 | 150 | ||
153 | fn replace( | 151 | fn replace( |
154 | new_expr: &SyntaxNode, | 152 | new_expr: &SyntaxNode, |
@@ -207,7 +205,7 @@ mod tests { | |||
207 | r#" | 205 | r#" |
208 | fn main() { | 206 | fn main() { |
209 | bar(); | 207 | bar(); |
210 | if<|> !true { | 208 | if !true { |
211 | return; | 209 | return; |
212 | } | 210 | } |
213 | foo(); | 211 | foo(); |
@@ -237,7 +235,7 @@ mod tests { | |||
237 | r#" | 235 | r#" |
238 | fn main(n: Option<String>) { | 236 | fn main(n: Option<String>) { |
239 | bar(); | 237 | bar(); |
240 | le<|>t n = match n { | 238 | let n = match n { |
241 | Some(it) => it, | 239 | Some(it) => it, |
242 | _ => return, | 240 | _ => return, |
243 | }; | 241 | }; |
@@ -263,7 +261,7 @@ mod tests { | |||
263 | "#, | 261 | "#, |
264 | r#" | 262 | r#" |
265 | fn main() { | 263 | fn main() { |
266 | le<|>t x = match Err(92) { | 264 | let x = match Err(92) { |
267 | Ok(it) => it, | 265 | Ok(it) => it, |
268 | _ => return, | 266 | _ => return, |
269 | }; | 267 | }; |
@@ -291,7 +289,7 @@ mod tests { | |||
291 | r#" | 289 | r#" |
292 | fn main(n: Option<String>) { | 290 | fn main(n: Option<String>) { |
293 | bar(); | 291 | bar(); |
294 | le<|>t n = match n { | 292 | let n = match n { |
295 | Ok(it) => it, | 293 | Ok(it) => it, |
296 | _ => return, | 294 | _ => return, |
297 | }; | 295 | }; |
@@ -321,7 +319,7 @@ mod tests { | |||
321 | r#" | 319 | r#" |
322 | fn main() { | 320 | fn main() { |
323 | while true { | 321 | while true { |
324 | if<|> !true { | 322 | if !true { |
325 | continue; | 323 | continue; |
326 | } | 324 | } |
327 | foo(); | 325 | foo(); |
@@ -349,7 +347,7 @@ mod tests { | |||
349 | r#" | 347 | r#" |
350 | fn main() { | 348 | fn main() { |
351 | while true { | 349 | while true { |
352 | le<|>t n = match n { | 350 | let n = match n { |
353 | Some(it) => it, | 351 | Some(it) => it, |
354 | _ => continue, | 352 | _ => continue, |
355 | }; | 353 | }; |
@@ -378,7 +376,7 @@ mod tests { | |||
378 | r#" | 376 | r#" |
379 | fn main() { | 377 | fn main() { |
380 | loop { | 378 | loop { |
381 | if<|> !true { | 379 | if !true { |
382 | continue; | 380 | continue; |
383 | } | 381 | } |
384 | foo(); | 382 | foo(); |
@@ -406,7 +404,7 @@ mod tests { | |||
406 | r#" | 404 | r#" |
407 | fn main() { | 405 | fn main() { |
408 | loop { | 406 | loop { |
409 | le<|>t n = match n { | 407 | let n = match n { |
410 | Some(it) => it, | 408 | Some(it) => it, |
411 | _ => continue, | 409 | _ => continue, |
412 | }; | 410 | }; |
diff --git a/crates/ra_assists/src/handlers/fill_match_arms.rs b/crates/ra_assists/src/handlers/fill_match_arms.rs index 13c1e7e80..cc303285b 100644 --- a/crates/ra_assists/src/handlers/fill_match_arms.rs +++ b/crates/ra_assists/src/handlers/fill_match_arms.rs | |||
@@ -4,8 +4,12 @@ use hir::{Adt, HasSource, ModuleDef, Semantics}; | |||
4 | use itertools::Itertools; | 4 | use itertools::Itertools; |
5 | use ra_ide_db::RootDatabase; | 5 | use ra_ide_db::RootDatabase; |
6 | use ra_syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat}; | 6 | use ra_syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat}; |
7 | use test_utils::mark; | ||
7 | 8 | ||
8 | use crate::{AssistContext, AssistId, Assists}; | 9 | use crate::{ |
10 | utils::{render_snippet, Cursor, FamousDefs}, | ||
11 | AssistContext, AssistId, Assists, | ||
12 | }; | ||
9 | 13 | ||
10 | // Assist: fill_match_arms | 14 | // Assist: fill_match_arms |
11 | // | 15 | // |
@@ -26,7 +30,7 @@ use crate::{AssistContext, AssistId, Assists}; | |||
26 | // | 30 | // |
27 | // fn handle(action: Action) { | 31 | // fn handle(action: Action) { |
28 | // match action { | 32 | // match action { |
29 | // Action::Move { distance } => {} | 33 | // $0Action::Move { distance } => {} |
30 | // Action::Stop => {} | 34 | // Action::Stop => {} |
31 | // } | 35 | // } |
32 | // } | 36 | // } |
@@ -49,12 +53,18 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
49 | let missing_arms: Vec<MatchArm> = if let Some(enum_def) = resolve_enum_def(&ctx.sema, &expr) { | 53 | let missing_arms: Vec<MatchArm> = if let Some(enum_def) = resolve_enum_def(&ctx.sema, &expr) { |
50 | let variants = enum_def.variants(ctx.db); | 54 | let variants = enum_def.variants(ctx.db); |
51 | 55 | ||
52 | variants | 56 | let mut variants = variants |
53 | .into_iter() | 57 | .into_iter() |
54 | .filter_map(|variant| build_pat(ctx.db, module, variant)) | 58 | .filter_map(|variant| build_pat(ctx.db, module, variant)) |
55 | .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat)) | 59 | .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat)) |
56 | .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) | 60 | .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) |
57 | .collect() | 61 | .collect::<Vec<_>>(); |
62 | if Some(enum_def) == FamousDefs(&ctx.sema, module.krate()).core_option_Option() { | ||
63 | // Match `Some` variant first. | ||
64 | mark::hit!(option_order); | ||
65 | variants.reverse() | ||
66 | } | ||
67 | variants | ||
58 | } else if let Some(enum_defs) = resolve_tuple_of_enum_def(&ctx.sema, &expr) { | 68 | } else if let Some(enum_defs) = resolve_tuple_of_enum_def(&ctx.sema, &expr) { |
59 | // Partial fill not currently supported for tuple of enums. | 69 | // Partial fill not currently supported for tuple of enums. |
60 | if !arms.is_empty() { | 70 | if !arms.is_empty() { |
@@ -93,10 +103,23 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
93 | } | 103 | } |
94 | 104 | ||
95 | let target = match_expr.syntax().text_range(); | 105 | let target = match_expr.syntax().text_range(); |
96 | acc.add(AssistId("fill_match_arms"), "Fill match arms", target, |edit| { | 106 | acc.add(AssistId("fill_match_arms"), "Fill match arms", target, |builder| { |
97 | let new_arm_list = match_arm_list.remove_placeholder().append_arms(missing_arms); | 107 | let new_arm_list = match_arm_list.remove_placeholder(); |
98 | edit.set_cursor(expr.syntax().text_range().start()); | 108 | let n_old_arms = new_arm_list.arms().count(); |
99 | edit.replace_ast(match_arm_list, new_arm_list); | 109 | let new_arm_list = new_arm_list.append_arms(missing_arms); |
110 | let first_new_arm = new_arm_list.arms().nth(n_old_arms); | ||
111 | let old_range = match_arm_list.syntax().text_range(); | ||
112 | match (first_new_arm, ctx.config.snippet_cap) { | ||
113 | (Some(first_new_arm), Some(cap)) => { | ||
114 | let snippet = render_snippet( | ||
115 | cap, | ||
116 | new_arm_list.syntax(), | ||
117 | Cursor::Before(first_new_arm.syntax()), | ||
118 | ); | ||
119 | builder.replace_snippet(cap, old_range, snippet); | ||
120 | } | ||
121 | _ => builder.replace(old_range, new_arm_list.to_string()), | ||
122 | } | ||
100 | }) | 123 | }) |
101 | } | 124 | } |
102 | 125 | ||
@@ -167,7 +190,12 @@ fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::EnumVariant) -> O | |||
167 | 190 | ||
168 | #[cfg(test)] | 191 | #[cfg(test)] |
169 | mod tests { | 192 | mod tests { |
170 | use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; | 193 | use test_utils::mark; |
194 | |||
195 | use crate::{ | ||
196 | tests::{check_assist, check_assist_not_applicable, check_assist_target}, | ||
197 | utils::FamousDefs, | ||
198 | }; | ||
171 | 199 | ||
172 | use super::fill_match_arms; | 200 | use super::fill_match_arms; |
173 | 201 | ||
@@ -214,12 +242,12 @@ mod tests { | |||
214 | r#" | 242 | r#" |
215 | enum A { | 243 | enum A { |
216 | As, | 244 | As, |
217 | Bs{x:i32, y:Option<i32>}, | 245 | Bs { x: i32, y: Option<i32> }, |
218 | Cs(i32, Option<i32>), | 246 | Cs(i32, Option<i32>), |
219 | } | 247 | } |
220 | fn main() { | 248 | fn main() { |
221 | match A::As<|> { | 249 | match A::As<|> { |
222 | A::Bs{x,y:Some(_)} => {} | 250 | A::Bs { x, y: Some(_) } => {} |
223 | A::Cs(_, Some(_)) => {} | 251 | A::Cs(_, Some(_)) => {} |
224 | } | 252 | } |
225 | } | 253 | } |
@@ -227,14 +255,14 @@ mod tests { | |||
227 | r#" | 255 | r#" |
228 | enum A { | 256 | enum A { |
229 | As, | 257 | As, |
230 | Bs{x:i32, y:Option<i32>}, | 258 | Bs { x: i32, y: Option<i32> }, |
231 | Cs(i32, Option<i32>), | 259 | Cs(i32, Option<i32>), |
232 | } | 260 | } |
233 | fn main() { | 261 | fn main() { |
234 | match <|>A::As { | 262 | match A::As { |
235 | A::Bs{x,y:Some(_)} => {} | 263 | A::Bs { x, y: Some(_) } => {} |
236 | A::Cs(_, Some(_)) => {} | 264 | A::Cs(_, Some(_)) => {} |
237 | A::As => {} | 265 | $0A::As => {} |
238 | } | 266 | } |
239 | } | 267 | } |
240 | "#, | 268 | "#, |
@@ -264,9 +292,9 @@ mod tests { | |||
264 | Cs(Option<i32>), | 292 | Cs(Option<i32>), |
265 | } | 293 | } |
266 | fn main() { | 294 | fn main() { |
267 | match <|>A::As { | 295 | match A::As { |
268 | A::Cs(_) | A::Bs => {} | 296 | A::Cs(_) | A::Bs => {} |
269 | A::As => {} | 297 | $0A::As => {} |
270 | } | 298 | } |
271 | } | 299 | } |
272 | "#, | 300 | "#, |
@@ -310,11 +338,11 @@ mod tests { | |||
310 | Ys, | 338 | Ys, |
311 | } | 339 | } |
312 | fn main() { | 340 | fn main() { |
313 | match <|>A::As { | 341 | match A::As { |
314 | A::Bs if 0 < 1 => {} | 342 | A::Bs if 0 < 1 => {} |
315 | A::Ds(_value) => { let x = 1; } | 343 | A::Ds(_value) => { let x = 1; } |
316 | A::Es(B::Xs) => (), | 344 | A::Es(B::Xs) => (), |
317 | A::As => {} | 345 | $0A::As => {} |
318 | A::Cs => {} | 346 | A::Cs => {} |
319 | } | 347 | } |
320 | } | 348 | } |
@@ -332,7 +360,7 @@ mod tests { | |||
332 | Bs, | 360 | Bs, |
333 | Cs(String), | 361 | Cs(String), |
334 | Ds(String, String), | 362 | Ds(String, String), |
335 | Es{ x: usize, y: usize } | 363 | Es { x: usize, y: usize } |
336 | } | 364 | } |
337 | 365 | ||
338 | fn main() { | 366 | fn main() { |
@@ -346,13 +374,13 @@ mod tests { | |||
346 | Bs, | 374 | Bs, |
347 | Cs(String), | 375 | Cs(String), |
348 | Ds(String, String), | 376 | Ds(String, String), |
349 | Es{ x: usize, y: usize } | 377 | Es { x: usize, y: usize } |
350 | } | 378 | } |
351 | 379 | ||
352 | fn main() { | 380 | fn main() { |
353 | let a = A::As; | 381 | let a = A::As; |
354 | match <|>a { | 382 | match a { |
355 | A::As => {} | 383 | $0A::As => {} |
356 | A::Bs => {} | 384 | A::Bs => {} |
357 | A::Cs(_) => {} | 385 | A::Cs(_) => {} |
358 | A::Ds(_, _) => {} | 386 | A::Ds(_, _) => {} |
@@ -368,14 +396,8 @@ mod tests { | |||
368 | check_assist( | 396 | check_assist( |
369 | fill_match_arms, | 397 | fill_match_arms, |
370 | r#" | 398 | r#" |
371 | enum A { | 399 | enum A { One, Two } |
372 | One, | 400 | enum B { One, Two } |
373 | Two, | ||
374 | } | ||
375 | enum B { | ||
376 | One, | ||
377 | Two, | ||
378 | } | ||
379 | 401 | ||
380 | fn main() { | 402 | fn main() { |
381 | let a = A::One; | 403 | let a = A::One; |
@@ -384,20 +406,14 @@ mod tests { | |||
384 | } | 406 | } |
385 | "#, | 407 | "#, |
386 | r#" | 408 | r#" |
387 | enum A { | 409 | enum A { One, Two } |
388 | One, | 410 | enum B { One, Two } |
389 | Two, | ||
390 | } | ||
391 | enum B { | ||
392 | One, | ||
393 | Two, | ||
394 | } | ||
395 | 411 | ||
396 | fn main() { | 412 | fn main() { |
397 | let a = A::One; | 413 | let a = A::One; |
398 | let b = B::One; | 414 | let b = B::One; |
399 | match <|>(a, b) { | 415 | match (a, b) { |
400 | (A::One, B::One) => {} | 416 | $0(A::One, B::One) => {} |
401 | (A::One, B::Two) => {} | 417 | (A::One, B::Two) => {} |
402 | (A::Two, B::One) => {} | 418 | (A::Two, B::One) => {} |
403 | (A::Two, B::Two) => {} | 419 | (A::Two, B::Two) => {} |
@@ -412,14 +428,8 @@ mod tests { | |||
412 | check_assist( | 428 | check_assist( |
413 | fill_match_arms, | 429 | fill_match_arms, |
414 | r#" | 430 | r#" |
415 | enum A { | 431 | enum A { One, Two } |
416 | One, | 432 | enum B { One, Two } |
417 | Two, | ||
418 | } | ||
419 | enum B { | ||
420 | One, | ||
421 | Two, | ||
422 | } | ||
423 | 433 | ||
424 | fn main() { | 434 | fn main() { |
425 | let a = A::One; | 435 | let a = A::One; |
@@ -428,20 +438,14 @@ mod tests { | |||
428 | } | 438 | } |
429 | "#, | 439 | "#, |
430 | r#" | 440 | r#" |
431 | enum A { | 441 | enum A { One, Two } |
432 | One, | 442 | enum B { One, Two } |
433 | Two, | ||
434 | } | ||
435 | enum B { | ||
436 | One, | ||
437 | Two, | ||
438 | } | ||
439 | 443 | ||
440 | fn main() { | 444 | fn main() { |
441 | let a = A::One; | 445 | let a = A::One; |
442 | let b = B::One; | 446 | let b = B::One; |
443 | match <|>(&a, &b) { | 447 | match (&a, &b) { |
444 | (A::One, B::One) => {} | 448 | $0(A::One, B::One) => {} |
445 | (A::One, B::Two) => {} | 449 | (A::One, B::Two) => {} |
446 | (A::Two, B::One) => {} | 450 | (A::Two, B::One) => {} |
447 | (A::Two, B::Two) => {} | 451 | (A::Two, B::Two) => {} |
@@ -456,14 +460,8 @@ mod tests { | |||
456 | check_assist_not_applicable( | 460 | check_assist_not_applicable( |
457 | fill_match_arms, | 461 | fill_match_arms, |
458 | r#" | 462 | r#" |
459 | enum A { | 463 | enum A { One, Two } |
460 | One, | 464 | enum B { One, Two } |
461 | Two, | ||
462 | } | ||
463 | enum B { | ||
464 | One, | ||
465 | Two, | ||
466 | } | ||
467 | 465 | ||
468 | fn main() { | 466 | fn main() { |
469 | let a = A::One; | 467 | let a = A::One; |
@@ -481,14 +479,8 @@ mod tests { | |||
481 | check_assist_not_applicable( | 479 | check_assist_not_applicable( |
482 | fill_match_arms, | 480 | fill_match_arms, |
483 | r#" | 481 | r#" |
484 | enum A { | 482 | enum A { One, Two } |
485 | One, | 483 | enum B { One, Two } |
486 | Two, | ||
487 | } | ||
488 | enum B { | ||
489 | One, | ||
490 | Two, | ||
491 | } | ||
492 | 484 | ||
493 | fn main() { | 485 | fn main() { |
494 | let a = A::One; | 486 | let a = A::One; |
@@ -512,10 +504,7 @@ mod tests { | |||
512 | check_assist_not_applicable( | 504 | check_assist_not_applicable( |
513 | fill_match_arms, | 505 | fill_match_arms, |
514 | r#" | 506 | r#" |
515 | enum A { | 507 | enum A { One, Two } |
516 | One, | ||
517 | Two, | ||
518 | } | ||
519 | 508 | ||
520 | fn main() { | 509 | fn main() { |
521 | let a = A::One; | 510 | let a = A::One; |
@@ -531,9 +520,7 @@ mod tests { | |||
531 | check_assist( | 520 | check_assist( |
532 | fill_match_arms, | 521 | fill_match_arms, |
533 | r#" | 522 | r#" |
534 | enum A { | 523 | enum A { As } |
535 | As, | ||
536 | } | ||
537 | 524 | ||
538 | fn foo(a: &A) { | 525 | fn foo(a: &A) { |
539 | match a<|> { | 526 | match a<|> { |
@@ -541,13 +528,11 @@ mod tests { | |||
541 | } | 528 | } |
542 | "#, | 529 | "#, |
543 | r#" | 530 | r#" |
544 | enum A { | 531 | enum A { As } |
545 | As, | ||
546 | } | ||
547 | 532 | ||
548 | fn foo(a: &A) { | 533 | fn foo(a: &A) { |
549 | match <|>a { | 534 | match a { |
550 | A::As => {} | 535 | $0A::As => {} |
551 | } | 536 | } |
552 | } | 537 | } |
553 | "#, | 538 | "#, |
@@ -557,7 +542,7 @@ mod tests { | |||
557 | fill_match_arms, | 542 | fill_match_arms, |
558 | r#" | 543 | r#" |
559 | enum A { | 544 | enum A { |
560 | Es{ x: usize, y: usize } | 545 | Es { x: usize, y: usize } |
561 | } | 546 | } |
562 | 547 | ||
563 | fn foo(a: &mut A) { | 548 | fn foo(a: &mut A) { |
@@ -567,12 +552,12 @@ mod tests { | |||
567 | "#, | 552 | "#, |
568 | r#" | 553 | r#" |
569 | enum A { | 554 | enum A { |
570 | Es{ x: usize, y: usize } | 555 | Es { x: usize, y: usize } |
571 | } | 556 | } |
572 | 557 | ||
573 | fn foo(a: &mut A) { | 558 | fn foo(a: &mut A) { |
574 | match <|>a { | 559 | match a { |
575 | A::Es { x, y } => {} | 560 | $0A::Es { x, y } => {} |
576 | } | 561 | } |
577 | } | 562 | } |
578 | "#, | 563 | "#, |
@@ -611,8 +596,8 @@ mod tests { | |||
611 | enum E { X, Y } | 596 | enum E { X, Y } |
612 | 597 | ||
613 | fn main() { | 598 | fn main() { |
614 | match <|>E::X { | 599 | match E::X { |
615 | E::X => {} | 600 | $0E::X => {} |
616 | E::Y => {} | 601 | E::Y => {} |
617 | } | 602 | } |
618 | } | 603 | } |
@@ -639,8 +624,8 @@ mod tests { | |||
639 | use foo::E::X; | 624 | use foo::E::X; |
640 | 625 | ||
641 | fn main() { | 626 | fn main() { |
642 | match <|>X { | 627 | match X { |
643 | X => {} | 628 | $0X => {} |
644 | foo::E::Y => {} | 629 | foo::E::Y => {} |
645 | } | 630 | } |
646 | } | 631 | } |
@@ -653,10 +638,7 @@ mod tests { | |||
653 | check_assist( | 638 | check_assist( |
654 | fill_match_arms, | 639 | fill_match_arms, |
655 | r#" | 640 | r#" |
656 | enum A { | 641 | enum A { One, Two } |
657 | One, | ||
658 | Two, | ||
659 | } | ||
660 | fn foo(a: A) { | 642 | fn foo(a: A) { |
661 | match a { | 643 | match a { |
662 | // foo bar baz<|> | 644 | // foo bar baz<|> |
@@ -666,16 +648,13 @@ mod tests { | |||
666 | } | 648 | } |
667 | "#, | 649 | "#, |
668 | r#" | 650 | r#" |
669 | enum A { | 651 | enum A { One, Two } |
670 | One, | ||
671 | Two, | ||
672 | } | ||
673 | fn foo(a: A) { | 652 | fn foo(a: A) { |
674 | match <|>a { | 653 | match a { |
675 | // foo bar baz | 654 | // foo bar baz |
676 | A::One => {} | 655 | A::One => {} |
677 | // This is where the rest should be | 656 | // This is where the rest should be |
678 | A::Two => {} | 657 | $0A::Two => {} |
679 | } | 658 | } |
680 | } | 659 | } |
681 | "#, | 660 | "#, |
@@ -687,10 +666,7 @@ mod tests { | |||
687 | check_assist( | 666 | check_assist( |
688 | fill_match_arms, | 667 | fill_match_arms, |
689 | r#" | 668 | r#" |
690 | enum A { | 669 | enum A { One, Two } |
691 | One, | ||
692 | Two, | ||
693 | } | ||
694 | fn foo(a: A) { | 670 | fn foo(a: A) { |
695 | match a { | 671 | match a { |
696 | // foo bar baz<|> | 672 | // foo bar baz<|> |
@@ -698,14 +674,11 @@ mod tests { | |||
698 | } | 674 | } |
699 | "#, | 675 | "#, |
700 | r#" | 676 | r#" |
701 | enum A { | 677 | enum A { One, Two } |
702 | One, | ||
703 | Two, | ||
704 | } | ||
705 | fn foo(a: A) { | 678 | fn foo(a: A) { |
706 | match <|>a { | 679 | match a { |
707 | // foo bar baz | 680 | // foo bar baz |
708 | A::One => {} | 681 | $0A::One => {} |
709 | A::Two => {} | 682 | A::Two => {} |
710 | } | 683 | } |
711 | } | 684 | } |
@@ -728,12 +701,37 @@ mod tests { | |||
728 | r#" | 701 | r#" |
729 | enum A { One, Two, } | 702 | enum A { One, Two, } |
730 | fn foo(a: A) { | 703 | fn foo(a: A) { |
731 | match <|>a { | 704 | match a { |
732 | A::One => {} | 705 | $0A::One => {} |
733 | A::Two => {} | 706 | A::Two => {} |
734 | } | 707 | } |
735 | } | 708 | } |
736 | "#, | 709 | "#, |
737 | ); | 710 | ); |
738 | } | 711 | } |
712 | |||
713 | #[test] | ||
714 | fn option_order() { | ||
715 | mark::check!(option_order); | ||
716 | let before = r#" | ||
717 | fn foo(opt: Option<i32>) { | ||
718 | match opt<|> { | ||
719 | } | ||
720 | }"#; | ||
721 | let before = | ||
722 | &format!("//- main.rs crate:main deps:core\n{}{}", before, FamousDefs::FIXTURE); | ||
723 | |||
724 | check_assist( | ||
725 | fill_match_arms, | ||
726 | before, | ||
727 | r#" | ||
728 | fn foo(opt: Option<i32>) { | ||
729 | match opt { | ||
730 | $0Some(_) => {} | ||
731 | None => {} | ||
732 | } | ||
733 | } | ||
734 | "#, | ||
735 | ); | ||
736 | } | ||
739 | } | 737 | } |
diff --git a/crates/ra_assists/src/handlers/fix_visibility.rs b/crates/ra_assists/src/handlers/fix_visibility.rs new file mode 100644 index 000000000..9ec42f568 --- /dev/null +++ b/crates/ra_assists/src/handlers/fix_visibility.rs | |||
@@ -0,0 +1,559 @@ | |||
1 | use hir::{db::HirDatabase, HasSource, HasVisibility, PathResolution}; | ||
2 | use ra_db::FileId; | ||
3 | use ra_syntax::{ | ||
4 | ast, AstNode, | ||
5 | SyntaxKind::{ATTR, COMMENT, WHITESPACE}, | ||
6 | SyntaxNode, TextRange, TextSize, | ||
7 | }; | ||
8 | |||
9 | use crate::{AssistContext, AssistId, Assists}; | ||
10 | |||
11 | // FIXME: this really should be a fix for diagnostic, rather than an assist. | ||
12 | |||
13 | // Assist: fix_visibility | ||
14 | // | ||
15 | // Makes inaccessible item public. | ||
16 | // | ||
17 | // ``` | ||
18 | // mod m { | ||
19 | // fn frobnicate() {} | ||
20 | // } | ||
21 | // fn main() { | ||
22 | // m::frobnicate<|>() {} | ||
23 | // } | ||
24 | // ``` | ||
25 | // -> | ||
26 | // ``` | ||
27 | // mod m { | ||
28 | // $0pub(crate) fn frobnicate() {} | ||
29 | // } | ||
30 | // fn main() { | ||
31 | // m::frobnicate() {} | ||
32 | // } | ||
33 | // ``` | ||
34 | pub(crate) fn fix_visibility(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
35 | add_vis_to_referenced_module_def(acc, ctx) | ||
36 | .or_else(|| add_vis_to_referenced_record_field(acc, ctx)) | ||
37 | } | ||
38 | |||
39 | fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
40 | let path: ast::Path = ctx.find_node_at_offset()?; | ||
41 | let path_res = ctx.sema.resolve_path(&path)?; | ||
42 | let def = match path_res { | ||
43 | PathResolution::Def(def) => def, | ||
44 | _ => return None, | ||
45 | }; | ||
46 | |||
47 | let current_module = ctx.sema.scope(&path.syntax()).module()?; | ||
48 | let target_module = def.module(ctx.db)?; | ||
49 | |||
50 | let vis = target_module.visibility_of(ctx.db, &def)?; | ||
51 | if vis.is_visible_from(ctx.db, current_module.into()) { | ||
52 | return None; | ||
53 | }; | ||
54 | |||
55 | let (offset, target, target_file, target_name) = target_data_for_def(ctx.db, def)?; | ||
56 | |||
57 | let missing_visibility = | ||
58 | if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" }; | ||
59 | |||
60 | let assist_label = match target_name { | ||
61 | None => format!("Change visibility to {}", missing_visibility), | ||
62 | Some(name) => format!("Change visibility of {} to {}", name, missing_visibility), | ||
63 | }; | ||
64 | |||
65 | acc.add(AssistId("fix_visibility"), assist_label, target, |builder| { | ||
66 | builder.set_file(target_file); | ||
67 | match ctx.config.snippet_cap { | ||
68 | Some(cap) => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)), | ||
69 | None => builder.insert(offset, format!("{} ", missing_visibility)), | ||
70 | } | ||
71 | }) | ||
72 | } | ||
73 | |||
74 | fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
75 | let record_field: ast::RecordField = ctx.find_node_at_offset()?; | ||
76 | let (record_field_def, _) = ctx.sema.resolve_record_field(&record_field)?; | ||
77 | |||
78 | let current_module = ctx.sema.scope(record_field.syntax()).module()?; | ||
79 | let visibility = record_field_def.visibility(ctx.db); | ||
80 | if visibility.is_visible_from(ctx.db, current_module.into()) { | ||
81 | return None; | ||
82 | } | ||
83 | |||
84 | let parent = record_field_def.parent_def(ctx.db); | ||
85 | let parent_name = parent.name(ctx.db); | ||
86 | let target_module = parent.module(ctx.db); | ||
87 | |||
88 | let in_file_source = record_field_def.source(ctx.db); | ||
89 | let (offset, target) = match in_file_source.value { | ||
90 | hir::FieldSource::Named(it) => { | ||
91 | let s = it.syntax(); | ||
92 | (vis_offset(s), s.text_range()) | ||
93 | } | ||
94 | hir::FieldSource::Pos(it) => { | ||
95 | let s = it.syntax(); | ||
96 | (vis_offset(s), s.text_range()) | ||
97 | } | ||
98 | }; | ||
99 | |||
100 | let missing_visibility = | ||
101 | if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" }; | ||
102 | let target_file = in_file_source.file_id.original_file(ctx.db); | ||
103 | |||
104 | let target_name = record_field_def.name(ctx.db); | ||
105 | let assist_label = | ||
106 | format!("Change visibility of {}.{} to {}", parent_name, target_name, missing_visibility); | ||
107 | |||
108 | acc.add(AssistId("fix_visibility"), assist_label, target, |builder| { | ||
109 | builder.set_file(target_file); | ||
110 | match ctx.config.snippet_cap { | ||
111 | Some(cap) => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)), | ||
112 | None => builder.insert(offset, format!("{} ", missing_visibility)), | ||
113 | } | ||
114 | }) | ||
115 | } | ||
116 | |||
117 | fn target_data_for_def( | ||
118 | db: &dyn HirDatabase, | ||
119 | def: hir::ModuleDef, | ||
120 | ) -> Option<(TextSize, TextRange, FileId, Option<hir::Name>)> { | ||
121 | fn offset_target_and_file_id<S, Ast>( | ||
122 | db: &dyn HirDatabase, | ||
123 | x: S, | ||
124 | ) -> (TextSize, TextRange, FileId) | ||
125 | where | ||
126 | S: HasSource<Ast = Ast>, | ||
127 | Ast: AstNode, | ||
128 | { | ||
129 | let source = x.source(db); | ||
130 | let in_file_syntax = source.syntax(); | ||
131 | let file_id = in_file_syntax.file_id; | ||
132 | let syntax = in_file_syntax.value; | ||
133 | (vis_offset(syntax), syntax.text_range(), file_id.original_file(db.upcast())) | ||
134 | } | ||
135 | |||
136 | let target_name; | ||
137 | let (offset, target, target_file) = match def { | ||
138 | hir::ModuleDef::Function(f) => { | ||
139 | target_name = Some(f.name(db)); | ||
140 | offset_target_and_file_id(db, f) | ||
141 | } | ||
142 | hir::ModuleDef::Adt(adt) => { | ||
143 | target_name = Some(adt.name(db)); | ||
144 | match adt { | ||
145 | hir::Adt::Struct(s) => offset_target_and_file_id(db, s), | ||
146 | hir::Adt::Union(u) => offset_target_and_file_id(db, u), | ||
147 | hir::Adt::Enum(e) => offset_target_and_file_id(db, e), | ||
148 | } | ||
149 | } | ||
150 | hir::ModuleDef::Const(c) => { | ||
151 | target_name = c.name(db); | ||
152 | offset_target_and_file_id(db, c) | ||
153 | } | ||
154 | hir::ModuleDef::Static(s) => { | ||
155 | target_name = s.name(db); | ||
156 | offset_target_and_file_id(db, s) | ||
157 | } | ||
158 | hir::ModuleDef::Trait(t) => { | ||
159 | target_name = Some(t.name(db)); | ||
160 | offset_target_and_file_id(db, t) | ||
161 | } | ||
162 | hir::ModuleDef::TypeAlias(t) => { | ||
163 | target_name = Some(t.name(db)); | ||
164 | offset_target_and_file_id(db, t) | ||
165 | } | ||
166 | hir::ModuleDef::Module(m) => { | ||
167 | target_name = m.name(db); | ||
168 | let in_file_source = m.declaration_source(db)?; | ||
169 | let file_id = in_file_source.file_id.original_file(db.upcast()); | ||
170 | let syntax = in_file_source.value.syntax(); | ||
171 | (vis_offset(syntax), syntax.text_range(), file_id) | ||
172 | } | ||
173 | // Enum variants can't be private, we can't modify builtin types | ||
174 | hir::ModuleDef::EnumVariant(_) | hir::ModuleDef::BuiltinType(_) => return None, | ||
175 | }; | ||
176 | |||
177 | Some((offset, target, target_file, target_name)) | ||
178 | } | ||
179 | |||
180 | fn vis_offset(node: &SyntaxNode) -> TextSize { | ||
181 | node.children_with_tokens() | ||
182 | .skip_while(|it| match it.kind() { | ||
183 | WHITESPACE | COMMENT | ATTR => true, | ||
184 | _ => false, | ||
185 | }) | ||
186 | .next() | ||
187 | .map(|it| it.text_range().start()) | ||
188 | .unwrap_or_else(|| node.text_range().start()) | ||
189 | } | ||
190 | |||
191 | #[cfg(test)] | ||
192 | mod tests { | ||
193 | use crate::tests::{check_assist, check_assist_not_applicable}; | ||
194 | |||
195 | use super::*; | ||
196 | |||
197 | #[test] | ||
198 | fn fix_visibility_of_fn() { | ||
199 | check_assist( | ||
200 | fix_visibility, | ||
201 | r"mod foo { fn foo() {} } | ||
202 | fn main() { foo::foo<|>() } ", | ||
203 | r"mod foo { $0pub(crate) fn foo() {} } | ||
204 | fn main() { foo::foo() } ", | ||
205 | ); | ||
206 | check_assist_not_applicable( | ||
207 | fix_visibility, | ||
208 | r"mod foo { pub fn foo() {} } | ||
209 | fn main() { foo::foo<|>() } ", | ||
210 | ) | ||
211 | } | ||
212 | |||
213 | #[test] | ||
214 | fn fix_visibility_of_adt_in_submodule() { | ||
215 | check_assist( | ||
216 | fix_visibility, | ||
217 | r"mod foo { struct Foo; } | ||
218 | fn main() { foo::Foo<|> } ", | ||
219 | r"mod foo { $0pub(crate) struct Foo; } | ||
220 | fn main() { foo::Foo } ", | ||
221 | ); | ||
222 | check_assist_not_applicable( | ||
223 | fix_visibility, | ||
224 | r"mod foo { pub struct Foo; } | ||
225 | fn main() { foo::Foo<|> } ", | ||
226 | ); | ||
227 | check_assist( | ||
228 | fix_visibility, | ||
229 | r"mod foo { enum Foo; } | ||
230 | fn main() { foo::Foo<|> } ", | ||
231 | r"mod foo { $0pub(crate) enum Foo; } | ||
232 | fn main() { foo::Foo } ", | ||
233 | ); | ||
234 | check_assist_not_applicable( | ||
235 | fix_visibility, | ||
236 | r"mod foo { pub enum Foo; } | ||
237 | fn main() { foo::Foo<|> } ", | ||
238 | ); | ||
239 | check_assist( | ||
240 | fix_visibility, | ||
241 | r"mod foo { union Foo; } | ||
242 | fn main() { foo::Foo<|> } ", | ||
243 | r"mod foo { $0pub(crate) union Foo; } | ||
244 | fn main() { foo::Foo } ", | ||
245 | ); | ||
246 | check_assist_not_applicable( | ||
247 | fix_visibility, | ||
248 | r"mod foo { pub union Foo; } | ||
249 | fn main() { foo::Foo<|> } ", | ||
250 | ); | ||
251 | } | ||
252 | |||
253 | #[test] | ||
254 | fn fix_visibility_of_adt_in_other_file() { | ||
255 | check_assist( | ||
256 | fix_visibility, | ||
257 | r" | ||
258 | //- /main.rs | ||
259 | mod foo; | ||
260 | fn main() { foo::Foo<|> } | ||
261 | |||
262 | //- /foo.rs | ||
263 | struct Foo; | ||
264 | ", | ||
265 | r"$0pub(crate) struct Foo; | ||
266 | |||
267 | ", | ||
268 | ); | ||
269 | } | ||
270 | |||
271 | #[test] | ||
272 | fn fix_visibility_of_struct_field() { | ||
273 | check_assist( | ||
274 | fix_visibility, | ||
275 | r"mod foo { pub struct Foo { bar: (), } } | ||
276 | fn main() { foo::Foo { <|>bar: () }; } ", | ||
277 | r"mod foo { pub struct Foo { $0pub(crate) bar: (), } } | ||
278 | fn main() { foo::Foo { bar: () }; } ", | ||
279 | ); | ||
280 | check_assist( | ||
281 | fix_visibility, | ||
282 | r"//- /lib.rs | ||
283 | mod foo; | ||
284 | fn main() { foo::Foo { <|>bar: () }; } | ||
285 | //- /foo.rs | ||
286 | pub struct Foo { bar: () } | ||
287 | ", | ||
288 | r"pub struct Foo { $0pub(crate) bar: () } | ||
289 | |||
290 | ", | ||
291 | ); | ||
292 | check_assist_not_applicable( | ||
293 | fix_visibility, | ||
294 | r"mod foo { pub struct Foo { pub bar: (), } } | ||
295 | fn main() { foo::Foo { <|>bar: () }; } ", | ||
296 | ); | ||
297 | check_assist_not_applicable( | ||
298 | fix_visibility, | ||
299 | r"//- /lib.rs | ||
300 | mod foo; | ||
301 | fn main() { foo::Foo { <|>bar: () }; } | ||
302 | //- /foo.rs | ||
303 | pub struct Foo { pub bar: () } | ||
304 | ", | ||
305 | ); | ||
306 | } | ||
307 | |||
308 | #[test] | ||
309 | fn fix_visibility_of_enum_variant_field() { | ||
310 | check_assist( | ||
311 | fix_visibility, | ||
312 | r"mod foo { pub enum Foo { Bar { bar: () } } } | ||
313 | fn main() { foo::Foo::Bar { <|>bar: () }; } ", | ||
314 | r"mod foo { pub enum Foo { Bar { $0pub(crate) bar: () } } } | ||
315 | fn main() { foo::Foo::Bar { bar: () }; } ", | ||
316 | ); | ||
317 | check_assist( | ||
318 | fix_visibility, | ||
319 | r"//- /lib.rs | ||
320 | mod foo; | ||
321 | fn main() { foo::Foo::Bar { <|>bar: () }; } | ||
322 | //- /foo.rs | ||
323 | pub enum Foo { Bar { bar: () } } | ||
324 | ", | ||
325 | r"pub enum Foo { Bar { $0pub(crate) bar: () } } | ||
326 | |||
327 | ", | ||
328 | ); | ||
329 | check_assist_not_applicable( | ||
330 | fix_visibility, | ||
331 | r"mod foo { pub struct Foo { pub bar: (), } } | ||
332 | fn main() { foo::Foo { <|>bar: () }; } ", | ||
333 | ); | ||
334 | check_assist_not_applicable( | ||
335 | fix_visibility, | ||
336 | r"//- /lib.rs | ||
337 | mod foo; | ||
338 | fn main() { foo::Foo { <|>bar: () }; } | ||
339 | //- /foo.rs | ||
340 | pub struct Foo { pub bar: () } | ||
341 | ", | ||
342 | ); | ||
343 | } | ||
344 | |||
345 | #[test] | ||
346 | #[ignore] | ||
347 | // FIXME reenable this test when `Semantics::resolve_record_field` works with union fields | ||
348 | fn fix_visibility_of_union_field() { | ||
349 | check_assist( | ||
350 | fix_visibility, | ||
351 | r"mod foo { pub union Foo { bar: (), } } | ||
352 | fn main() { foo::Foo { <|>bar: () }; } ", | ||
353 | r"mod foo { pub union Foo { $0pub(crate) bar: (), } } | ||
354 | fn main() { foo::Foo { bar: () }; } ", | ||
355 | ); | ||
356 | check_assist( | ||
357 | fix_visibility, | ||
358 | r"//- /lib.rs | ||
359 | mod foo; | ||
360 | fn main() { foo::Foo { <|>bar: () }; } | ||
361 | //- /foo.rs | ||
362 | pub union Foo { bar: () } | ||
363 | ", | ||
364 | r"pub union Foo { $0pub(crate) bar: () } | ||
365 | |||
366 | ", | ||
367 | ); | ||
368 | check_assist_not_applicable( | ||
369 | fix_visibility, | ||
370 | r"mod foo { pub union Foo { pub bar: (), } } | ||
371 | fn main() { foo::Foo { <|>bar: () }; } ", | ||
372 | ); | ||
373 | check_assist_not_applicable( | ||
374 | fix_visibility, | ||
375 | r"//- /lib.rs | ||
376 | mod foo; | ||
377 | fn main() { foo::Foo { <|>bar: () }; } | ||
378 | //- /foo.rs | ||
379 | pub union Foo { pub bar: () } | ||
380 | ", | ||
381 | ); | ||
382 | } | ||
383 | |||
384 | #[test] | ||
385 | fn fix_visibility_of_const() { | ||
386 | check_assist( | ||
387 | fix_visibility, | ||
388 | r"mod foo { const FOO: () = (); } | ||
389 | fn main() { foo::FOO<|> } ", | ||
390 | r"mod foo { $0pub(crate) const FOO: () = (); } | ||
391 | fn main() { foo::FOO } ", | ||
392 | ); | ||
393 | check_assist_not_applicable( | ||
394 | fix_visibility, | ||
395 | r"mod foo { pub const FOO: () = (); } | ||
396 | fn main() { foo::FOO<|> } ", | ||
397 | ); | ||
398 | } | ||
399 | |||
400 | #[test] | ||
401 | fn fix_visibility_of_static() { | ||
402 | check_assist( | ||
403 | fix_visibility, | ||
404 | r"mod foo { static FOO: () = (); } | ||
405 | fn main() { foo::FOO<|> } ", | ||
406 | r"mod foo { $0pub(crate) static FOO: () = (); } | ||
407 | fn main() { foo::FOO } ", | ||
408 | ); | ||
409 | check_assist_not_applicable( | ||
410 | fix_visibility, | ||
411 | r"mod foo { pub static FOO: () = (); } | ||
412 | fn main() { foo::FOO<|> } ", | ||
413 | ); | ||
414 | } | ||
415 | |||
416 | #[test] | ||
417 | fn fix_visibility_of_trait() { | ||
418 | check_assist( | ||
419 | fix_visibility, | ||
420 | r"mod foo { trait Foo { fn foo(&self) {} } } | ||
421 | fn main() { let x: &dyn foo::<|>Foo; } ", | ||
422 | r"mod foo { $0pub(crate) trait Foo { fn foo(&self) {} } } | ||
423 | fn main() { let x: &dyn foo::Foo; } ", | ||
424 | ); | ||
425 | check_assist_not_applicable( | ||
426 | fix_visibility, | ||
427 | r"mod foo { pub trait Foo { fn foo(&self) {} } } | ||
428 | fn main() { let x: &dyn foo::Foo<|>; } ", | ||
429 | ); | ||
430 | } | ||
431 | |||
432 | #[test] | ||
433 | fn fix_visibility_of_type_alias() { | ||
434 | check_assist( | ||
435 | fix_visibility, | ||
436 | r"mod foo { type Foo = (); } | ||
437 | fn main() { let x: foo::Foo<|>; } ", | ||
438 | r"mod foo { $0pub(crate) type Foo = (); } | ||
439 | fn main() { let x: foo::Foo; } ", | ||
440 | ); | ||
441 | check_assist_not_applicable( | ||
442 | fix_visibility, | ||
443 | r"mod foo { pub type Foo = (); } | ||
444 | fn main() { let x: foo::Foo<|>; } ", | ||
445 | ); | ||
446 | } | ||
447 | |||
448 | #[test] | ||
449 | fn fix_visibility_of_module() { | ||
450 | check_assist( | ||
451 | fix_visibility, | ||
452 | r"mod foo { mod bar { fn bar() {} } } | ||
453 | fn main() { foo::bar<|>::bar(); } ", | ||
454 | r"mod foo { $0pub(crate) mod bar { fn bar() {} } } | ||
455 | fn main() { foo::bar::bar(); } ", | ||
456 | ); | ||
457 | |||
458 | check_assist( | ||
459 | fix_visibility, | ||
460 | r" | ||
461 | //- /main.rs | ||
462 | mod foo; | ||
463 | fn main() { foo::bar<|>::baz(); } | ||
464 | |||
465 | //- /foo.rs | ||
466 | mod bar { | ||
467 | pub fn baz() {} | ||
468 | } | ||
469 | ", | ||
470 | r"$0pub(crate) mod bar { | ||
471 | pub fn baz() {} | ||
472 | } | ||
473 | |||
474 | ", | ||
475 | ); | ||
476 | |||
477 | check_assist_not_applicable( | ||
478 | fix_visibility, | ||
479 | r"mod foo { pub mod bar { pub fn bar() {} } } | ||
480 | fn main() { foo::bar<|>::bar(); } ", | ||
481 | ); | ||
482 | } | ||
483 | |||
484 | #[test] | ||
485 | fn fix_visibility_of_inline_module_in_other_file() { | ||
486 | check_assist( | ||
487 | fix_visibility, | ||
488 | r" | ||
489 | //- /main.rs | ||
490 | mod foo; | ||
491 | fn main() { foo::bar<|>::baz(); } | ||
492 | |||
493 | //- /foo.rs | ||
494 | mod bar; | ||
495 | |||
496 | //- /foo/bar.rs | ||
497 | pub fn baz() {} | ||
498 | } | ||
499 | ", | ||
500 | r"$0pub(crate) mod bar; | ||
501 | ", | ||
502 | ); | ||
503 | } | ||
504 | |||
505 | #[test] | ||
506 | fn fix_visibility_of_module_declaration_in_other_file() { | ||
507 | check_assist( | ||
508 | fix_visibility, | ||
509 | r"//- /main.rs | ||
510 | mod foo; | ||
511 | fn main() { foo::bar<|>>::baz(); } | ||
512 | |||
513 | //- /foo.rs | ||
514 | mod bar { | ||
515 | pub fn baz() {} | ||
516 | }", | ||
517 | r"$0pub(crate) mod bar { | ||
518 | pub fn baz() {} | ||
519 | } | ||
520 | ", | ||
521 | ); | ||
522 | } | ||
523 | |||
524 | #[test] | ||
525 | fn adds_pub_when_target_is_in_another_crate() { | ||
526 | check_assist( | ||
527 | fix_visibility, | ||
528 | r"//- /main.rs crate:a deps:foo | ||
529 | foo::Bar<|> | ||
530 | //- /lib.rs crate:foo | ||
531 | struct Bar;", | ||
532 | r"$0pub struct Bar; | ||
533 | ", | ||
534 | ) | ||
535 | } | ||
536 | |||
537 | #[test] | ||
538 | #[ignore] | ||
539 | // FIXME handle reexports properly | ||
540 | fn fix_visibility_of_reexport() { | ||
541 | check_assist( | ||
542 | fix_visibility, | ||
543 | r" | ||
544 | mod foo { | ||
545 | use bar::Baz; | ||
546 | mod bar { pub(super) struct Baz; } | ||
547 | } | ||
548 | foo::Baz<|> | ||
549 | ", | ||
550 | r" | ||
551 | mod foo { | ||
552 | $0pub(crate) use bar::Baz; | ||
553 | mod bar { pub(super) struct Baz; } | ||
554 | } | ||
555 | foo::Baz | ||
556 | ", | ||
557 | ) | ||
558 | } | ||
559 | } | ||
diff --git a/crates/ra_assists/src/handlers/flip_binexpr.rs b/crates/ra_assists/src/handlers/flip_binexpr.rs index 692ba4895..573196576 100644 --- a/crates/ra_assists/src/handlers/flip_binexpr.rs +++ b/crates/ra_assists/src/handlers/flip_binexpr.rs | |||
@@ -85,17 +85,13 @@ mod tests { | |||
85 | check_assist( | 85 | check_assist( |
86 | flip_binexpr, | 86 | flip_binexpr, |
87 | "fn f() { let res = 1 ==<|> 2; }", | 87 | "fn f() { let res = 1 ==<|> 2; }", |
88 | "fn f() { let res = 2 ==<|> 1; }", | 88 | "fn f() { let res = 2 == 1; }", |
89 | ) | 89 | ) |
90 | } | 90 | } |
91 | 91 | ||
92 | #[test] | 92 | #[test] |
93 | fn flip_binexpr_works_for_gt() { | 93 | fn flip_binexpr_works_for_gt() { |
94 | check_assist( | 94 | check_assist(flip_binexpr, "fn f() { let res = 1 ><|> 2; }", "fn f() { let res = 2 < 1; }") |
95 | flip_binexpr, | ||
96 | "fn f() { let res = 1 ><|> 2; }", | ||
97 | "fn f() { let res = 2 <<|> 1; }", | ||
98 | ) | ||
99 | } | 95 | } |
100 | 96 | ||
101 | #[test] | 97 | #[test] |
@@ -103,7 +99,7 @@ mod tests { | |||
103 | check_assist( | 99 | check_assist( |
104 | flip_binexpr, | 100 | flip_binexpr, |
105 | "fn f() { let res = 1 <=<|> 2; }", | 101 | "fn f() { let res = 1 <=<|> 2; }", |
106 | "fn f() { let res = 2 >=<|> 1; }", | 102 | "fn f() { let res = 2 >= 1; }", |
107 | ) | 103 | ) |
108 | } | 104 | } |
109 | 105 | ||
@@ -112,7 +108,7 @@ mod tests { | |||
112 | check_assist( | 108 | check_assist( |
113 | flip_binexpr, | 109 | flip_binexpr, |
114 | "fn f() { let res = (1 + 1) ==<|> (2 + 2); }", | 110 | "fn f() { let res = (1 + 1) ==<|> (2 + 2); }", |
115 | "fn f() { let res = (2 + 2) ==<|> (1 + 1); }", | 111 | "fn f() { let res = (2 + 2) == (1 + 1); }", |
116 | ) | 112 | ) |
117 | } | 113 | } |
118 | 114 | ||
@@ -132,7 +128,7 @@ mod tests { | |||
132 | fn dyn_eq(&self, other: &dyn Diagnostic) -> bool { | 128 | fn dyn_eq(&self, other: &dyn Diagnostic) -> bool { |
133 | match other.downcast_ref::<Self>() { | 129 | match other.downcast_ref::<Self>() { |
134 | None => false, | 130 | None => false, |
135 | Some(it) => self ==<|> it, | 131 | Some(it) => self == it, |
136 | } | 132 | } |
137 | } | 133 | } |
138 | "#, | 134 | "#, |
diff --git a/crates/ra_assists/src/handlers/flip_comma.rs b/crates/ra_assists/src/handlers/flip_comma.rs index dfe2a7fed..a57a1c463 100644 --- a/crates/ra_assists/src/handlers/flip_comma.rs +++ b/crates/ra_assists/src/handlers/flip_comma.rs | |||
@@ -45,7 +45,7 @@ mod tests { | |||
45 | check_assist( | 45 | check_assist( |
46 | flip_comma, | 46 | flip_comma, |
47 | "fn foo(x: i32,<|> y: Result<(), ()>) {}", | 47 | "fn foo(x: i32,<|> y: Result<(), ()>) {}", |
48 | "fn foo(y: Result<(), ()>,<|> x: i32) {}", | 48 | "fn foo(y: Result<(), ()>, x: i32) {}", |
49 | ) | 49 | ) |
50 | } | 50 | } |
51 | 51 | ||
diff --git a/crates/ra_assists/src/handlers/flip_trait_bound.rs b/crates/ra_assists/src/handlers/flip_trait_bound.rs index 8a08702ab..0115adc8b 100644 --- a/crates/ra_assists/src/handlers/flip_trait_bound.rs +++ b/crates/ra_assists/src/handlers/flip_trait_bound.rs | |||
@@ -60,7 +60,7 @@ mod tests { | |||
60 | check_assist( | 60 | check_assist( |
61 | flip_trait_bound, | 61 | flip_trait_bound, |
62 | "struct S<T> where T: A <|>+ B { }", | 62 | "struct S<T> where T: A <|>+ B { }", |
63 | "struct S<T> where T: B <|>+ A { }", | 63 | "struct S<T> where T: B + A { }", |
64 | ) | 64 | ) |
65 | } | 65 | } |
66 | 66 | ||
@@ -69,13 +69,13 @@ mod tests { | |||
69 | check_assist( | 69 | check_assist( |
70 | flip_trait_bound, | 70 | flip_trait_bound, |
71 | "impl X for S<T> where T: A +<|> B { }", | 71 | "impl X for S<T> where T: A +<|> B { }", |
72 | "impl X for S<T> where T: B +<|> A { }", | 72 | "impl X for S<T> where T: B + A { }", |
73 | ) | 73 | ) |
74 | } | 74 | } |
75 | 75 | ||
76 | #[test] | 76 | #[test] |
77 | fn flip_trait_bound_works_for_fn() { | 77 | fn flip_trait_bound_works_for_fn() { |
78 | check_assist(flip_trait_bound, "fn f<T: A <|>+ B>(t: T) { }", "fn f<T: B <|>+ A>(t: T) { }") | 78 | check_assist(flip_trait_bound, "fn f<T: A <|>+ B>(t: T) { }", "fn f<T: B + A>(t: T) { }") |
79 | } | 79 | } |
80 | 80 | ||
81 | #[test] | 81 | #[test] |
@@ -83,7 +83,7 @@ mod tests { | |||
83 | check_assist( | 83 | check_assist( |
84 | flip_trait_bound, | 84 | flip_trait_bound, |
85 | "fn f<T>(t: T) where T: A +<|> B { }", | 85 | "fn f<T>(t: T) where T: A +<|> B { }", |
86 | "fn f<T>(t: T) where T: B +<|> A { }", | 86 | "fn f<T>(t: T) where T: B + A { }", |
87 | ) | 87 | ) |
88 | } | 88 | } |
89 | 89 | ||
@@ -92,7 +92,7 @@ mod tests { | |||
92 | check_assist( | 92 | check_assist( |
93 | flip_trait_bound, | 93 | flip_trait_bound, |
94 | "fn f<T>(t: T) where T: A <|>+ 'static { }", | 94 | "fn f<T>(t: T) where T: A <|>+ 'static { }", |
95 | "fn f<T>(t: T) where T: 'static <|>+ A { }", | 95 | "fn f<T>(t: T) where T: 'static + A { }", |
96 | ) | 96 | ) |
97 | } | 97 | } |
98 | 98 | ||
@@ -101,7 +101,7 @@ mod tests { | |||
101 | check_assist( | 101 | check_assist( |
102 | flip_trait_bound, | 102 | flip_trait_bound, |
103 | "struct S<T> where T: A<T> <|>+ b_mod::B<T> + C<T> { }", | 103 | "struct S<T> where T: A<T> <|>+ b_mod::B<T> + C<T> { }", |
104 | "struct S<T> where T: b_mod::B<T> <|>+ A<T> + C<T> { }", | 104 | "struct S<T> where T: b_mod::B<T> + A<T> + C<T> { }", |
105 | ) | 105 | ) |
106 | } | 106 | } |
107 | 107 | ||
@@ -110,7 +110,7 @@ mod tests { | |||
110 | check_assist( | 110 | check_assist( |
111 | flip_trait_bound, | 111 | flip_trait_bound, |
112 | "struct S<T> where T: A + B + C + D + E + F +<|> G + H + I + J { }", | 112 | "struct S<T> where T: A + B + C + D + E + F +<|> G + H + I + J { }", |
113 | "struct S<T> where T: A + B + C + D + E + G +<|> F + H + I + J { }", | 113 | "struct S<T> where T: A + B + C + D + E + G + F + H + I + J { }", |
114 | ) | 114 | ) |
115 | } | 115 | } |
116 | } | 116 | } |
diff --git a/crates/ra_assists/src/handlers/inline_local_variable.rs b/crates/ra_assists/src/handlers/inline_local_variable.rs index 5b26814d3..d26e68847 100644 --- a/crates/ra_assists/src/handlers/inline_local_variable.rs +++ b/crates/ra_assists/src/handlers/inline_local_variable.rs | |||
@@ -3,7 +3,7 @@ use ra_syntax::{ | |||
3 | ast::{self, AstNode, AstToken}, | 3 | ast::{self, AstNode, AstToken}, |
4 | TextRange, | 4 | TextRange, |
5 | }; | 5 | }; |
6 | use test_utils::tested_by; | 6 | use test_utils::mark; |
7 | 7 | ||
8 | use crate::{ | 8 | use crate::{ |
9 | assist_context::{AssistContext, Assists}, | 9 | assist_context::{AssistContext, Assists}, |
@@ -33,11 +33,11 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O | |||
33 | _ => return None, | 33 | _ => return None, |
34 | }; | 34 | }; |
35 | if bind_pat.mut_token().is_some() { | 35 | if bind_pat.mut_token().is_some() { |
36 | tested_by!(test_not_inline_mut_variable); | 36 | mark::hit!(test_not_inline_mut_variable); |
37 | return None; | 37 | return None; |
38 | } | 38 | } |
39 | if !bind_pat.syntax().text_range().contains_inclusive(ctx.offset()) { | 39 | if !bind_pat.syntax().text_range().contains_inclusive(ctx.offset()) { |
40 | tested_by!(not_applicable_outside_of_bind_pat); | 40 | mark::hit!(not_applicable_outside_of_bind_pat); |
41 | return None; | 41 | return None; |
42 | } | 42 | } |
43 | let initializer_expr = let_stmt.initializer()?; | 43 | let initializer_expr = let_stmt.initializer()?; |
@@ -46,7 +46,7 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O | |||
46 | let def = Definition::Local(def); | 46 | let def = Definition::Local(def); |
47 | let refs = def.find_usages(ctx.db, None); | 47 | let refs = def.find_usages(ctx.db, None); |
48 | if refs.is_empty() { | 48 | if refs.is_empty() { |
49 | tested_by!(test_not_applicable_if_variable_unused); | 49 | mark::hit!(test_not_applicable_if_variable_unused); |
50 | return None; | 50 | return None; |
51 | }; | 51 | }; |
52 | 52 | ||
@@ -116,13 +116,12 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O | |||
116 | let replacement = if should_wrap { init_in_paren.clone() } else { init_str.clone() }; | 116 | let replacement = if should_wrap { init_in_paren.clone() } else { init_str.clone() }; |
117 | builder.replace(desc.file_range.range, replacement) | 117 | builder.replace(desc.file_range.range, replacement) |
118 | } | 118 | } |
119 | builder.set_cursor(delete_range.start()) | ||
120 | }) | 119 | }) |
121 | } | 120 | } |
122 | 121 | ||
123 | #[cfg(test)] | 122 | #[cfg(test)] |
124 | mod tests { | 123 | mod tests { |
125 | use test_utils::covers; | 124 | use test_utils::mark; |
126 | 125 | ||
127 | use crate::tests::{check_assist, check_assist_not_applicable}; | 126 | use crate::tests::{check_assist, check_assist_not_applicable}; |
128 | 127 | ||
@@ -149,7 +148,7 @@ fn foo() { | |||
149 | r" | 148 | r" |
150 | fn bar(a: usize) {} | 149 | fn bar(a: usize) {} |
151 | fn foo() { | 150 | fn foo() { |
152 | <|>1 + 1; | 151 | 1 + 1; |
153 | if 1 > 10 { | 152 | if 1 > 10 { |
154 | } | 153 | } |
155 | 154 | ||
@@ -183,7 +182,7 @@ fn foo() { | |||
183 | r" | 182 | r" |
184 | fn bar(a: usize) {} | 183 | fn bar(a: usize) {} |
185 | fn foo() { | 184 | fn foo() { |
186 | <|>(1 + 1) + 1; | 185 | (1 + 1) + 1; |
187 | if (1 + 1) > 10 { | 186 | if (1 + 1) > 10 { |
188 | } | 187 | } |
189 | 188 | ||
@@ -217,7 +216,7 @@ fn foo() { | |||
217 | r" | 216 | r" |
218 | fn bar(a: usize) {} | 217 | fn bar(a: usize) {} |
219 | fn foo() { | 218 | fn foo() { |
220 | <|>bar(1) + 1; | 219 | bar(1) + 1; |
221 | if bar(1) > 10 { | 220 | if bar(1) > 10 { |
222 | } | 221 | } |
223 | 222 | ||
@@ -251,7 +250,7 @@ fn foo() { | |||
251 | r" | 250 | r" |
252 | fn bar(a: usize): usize { a } | 251 | fn bar(a: usize): usize { a } |
253 | fn foo() { | 252 | fn foo() { |
254 | <|>(bar(1) as u64) + 1; | 253 | (bar(1) as u64) + 1; |
255 | if (bar(1) as u64) > 10 { | 254 | if (bar(1) as u64) > 10 { |
256 | } | 255 | } |
257 | 256 | ||
@@ -283,7 +282,7 @@ fn foo() { | |||
283 | }", | 282 | }", |
284 | r" | 283 | r" |
285 | fn foo() { | 284 | fn foo() { |
286 | <|>{ 10 + 1 } + 1; | 285 | { 10 + 1 } + 1; |
287 | if { 10 + 1 } > 10 { | 286 | if { 10 + 1 } > 10 { |
288 | } | 287 | } |
289 | 288 | ||
@@ -315,7 +314,7 @@ fn foo() { | |||
315 | }", | 314 | }", |
316 | r" | 315 | r" |
317 | fn foo() { | 316 | fn foo() { |
318 | <|>( 10 + 1 ) + 1; | 317 | ( 10 + 1 ) + 1; |
319 | if ( 10 + 1 ) > 10 { | 318 | if ( 10 + 1 ) > 10 { |
320 | } | 319 | } |
321 | 320 | ||
@@ -330,7 +329,7 @@ fn foo() { | |||
330 | 329 | ||
331 | #[test] | 330 | #[test] |
332 | fn test_not_inline_mut_variable() { | 331 | fn test_not_inline_mut_variable() { |
333 | covers!(test_not_inline_mut_variable); | 332 | mark::check!(test_not_inline_mut_variable); |
334 | check_assist_not_applicable( | 333 | check_assist_not_applicable( |
335 | inline_local_variable, | 334 | inline_local_variable, |
336 | r" | 335 | r" |
@@ -353,7 +352,7 @@ fn foo() { | |||
353 | }", | 352 | }", |
354 | r" | 353 | r" |
355 | fn foo() { | 354 | fn foo() { |
356 | <|>let b = bar(10 + 1) * 10; | 355 | let b = bar(10 + 1) * 10; |
357 | let c = bar(10 + 1) as usize; | 356 | let c = bar(10 + 1) as usize; |
358 | }", | 357 | }", |
359 | ); | 358 | ); |
@@ -373,7 +372,7 @@ fn foo() { | |||
373 | r" | 372 | r" |
374 | fn foo() { | 373 | fn foo() { |
375 | let x = vec![1, 2, 3]; | 374 | let x = vec![1, 2, 3]; |
376 | <|>let b = x[0] * 10; | 375 | let b = x[0] * 10; |
377 | let c = x[0] as usize; | 376 | let c = x[0] as usize; |
378 | }", | 377 | }", |
379 | ); | 378 | ); |
@@ -393,7 +392,7 @@ fn foo() { | |||
393 | r" | 392 | r" |
394 | fn foo() { | 393 | fn foo() { |
395 | let bar = vec![1]; | 394 | let bar = vec![1]; |
396 | <|>let b = bar.len() * 10; | 395 | let b = bar.len() * 10; |
397 | let c = bar.len() as usize; | 396 | let c = bar.len() as usize; |
398 | }", | 397 | }", |
399 | ); | 398 | ); |
@@ -421,7 +420,7 @@ struct Bar { | |||
421 | 420 | ||
422 | fn foo() { | 421 | fn foo() { |
423 | let bar = Bar { foo: 1 }; | 422 | let bar = Bar { foo: 1 }; |
424 | <|>let b = bar.foo * 10; | 423 | let b = bar.foo * 10; |
425 | let c = bar.foo as usize; | 424 | let c = bar.foo as usize; |
426 | }", | 425 | }", |
427 | ); | 426 | ); |
@@ -442,7 +441,7 @@ fn foo() -> Option<usize> { | |||
442 | r" | 441 | r" |
443 | fn foo() -> Option<usize> { | 442 | fn foo() -> Option<usize> { |
444 | let bar = Some(1); | 443 | let bar = Some(1); |
445 | <|>let b = bar? * 10; | 444 | let b = bar? * 10; |
446 | let c = bar? as usize; | 445 | let c = bar? as usize; |
447 | None | 446 | None |
448 | }", | 447 | }", |
@@ -462,7 +461,7 @@ fn foo() { | |||
462 | r" | 461 | r" |
463 | fn foo() { | 462 | fn foo() { |
464 | let bar = 10; | 463 | let bar = 10; |
465 | <|>let b = &bar * 10; | 464 | let b = &bar * 10; |
466 | }", | 465 | }", |
467 | ); | 466 | ); |
468 | } | 467 | } |
@@ -478,7 +477,7 @@ fn foo() { | |||
478 | }", | 477 | }", |
479 | r" | 478 | r" |
480 | fn foo() { | 479 | fn foo() { |
481 | <|>let b = (10, 20)[0]; | 480 | let b = (10, 20)[0]; |
482 | }", | 481 | }", |
483 | ); | 482 | ); |
484 | } | 483 | } |
@@ -494,7 +493,7 @@ fn foo() { | |||
494 | }", | 493 | }", |
495 | r" | 494 | r" |
496 | fn foo() { | 495 | fn foo() { |
497 | <|>let b = [1, 2, 3].len(); | 496 | let b = [1, 2, 3].len(); |
498 | }", | 497 | }", |
499 | ); | 498 | ); |
500 | } | 499 | } |
@@ -511,7 +510,7 @@ fn foo() { | |||
511 | }", | 510 | }", |
512 | r" | 511 | r" |
513 | fn foo() { | 512 | fn foo() { |
514 | <|>let b = (10 + 20) * 10; | 513 | let b = (10 + 20) * 10; |
515 | let c = (10 + 20) as usize; | 514 | let c = (10 + 20) as usize; |
516 | }", | 515 | }", |
517 | ); | 516 | ); |
@@ -531,7 +530,7 @@ fn foo() { | |||
531 | r" | 530 | r" |
532 | fn foo() { | 531 | fn foo() { |
533 | let d = 10; | 532 | let d = 10; |
534 | <|>let b = d * 10; | 533 | let b = d * 10; |
535 | let c = d as usize; | 534 | let c = d as usize; |
536 | }", | 535 | }", |
537 | ); | 536 | ); |
@@ -549,7 +548,7 @@ fn foo() { | |||
549 | }", | 548 | }", |
550 | r" | 549 | r" |
551 | fn foo() { | 550 | fn foo() { |
552 | <|>let b = { 10 } * 10; | 551 | let b = { 10 } * 10; |
553 | let c = { 10 } as usize; | 552 | let c = { 10 } as usize; |
554 | }", | 553 | }", |
555 | ); | 554 | ); |
@@ -569,7 +568,7 @@ fn foo() { | |||
569 | }", | 568 | }", |
570 | r" | 569 | r" |
571 | fn foo() { | 570 | fn foo() { |
572 | <|>let b = (10 + 20) * 10; | 571 | let b = (10 + 20) * 10; |
573 | let c = (10 + 20, 20); | 572 | let c = (10 + 20, 20); |
574 | let d = [10 + 20, 10]; | 573 | let d = [10 + 20, 10]; |
575 | let e = (10 + 20); | 574 | let e = (10 + 20); |
@@ -588,7 +587,7 @@ fn foo() { | |||
588 | }", | 587 | }", |
589 | r" | 588 | r" |
590 | fn foo() { | 589 | fn foo() { |
591 | <|>for i in vec![10, 20] {} | 590 | for i in vec![10, 20] {} |
592 | }", | 591 | }", |
593 | ); | 592 | ); |
594 | } | 593 | } |
@@ -604,7 +603,7 @@ fn foo() { | |||
604 | }", | 603 | }", |
605 | r" | 604 | r" |
606 | fn foo() { | 605 | fn foo() { |
607 | <|>while 1 > 0 {} | 606 | while 1 > 0 {} |
608 | }", | 607 | }", |
609 | ); | 608 | ); |
610 | } | 609 | } |
@@ -622,7 +621,7 @@ fn foo() { | |||
622 | }", | 621 | }", |
623 | r" | 622 | r" |
624 | fn foo() { | 623 | fn foo() { |
625 | <|>loop { | 624 | loop { |
626 | break 1 + 1; | 625 | break 1 + 1; |
627 | } | 626 | } |
628 | }", | 627 | }", |
@@ -640,7 +639,7 @@ fn foo() { | |||
640 | }", | 639 | }", |
641 | r" | 640 | r" |
642 | fn foo() { | 641 | fn foo() { |
643 | <|>return 1 > 0; | 642 | return 1 > 0; |
644 | }", | 643 | }", |
645 | ); | 644 | ); |
646 | } | 645 | } |
@@ -656,14 +655,14 @@ fn foo() { | |||
656 | }", | 655 | }", |
657 | r" | 656 | r" |
658 | fn foo() { | 657 | fn foo() { |
659 | <|>match 1 > 0 {} | 658 | match 1 > 0 {} |
660 | }", | 659 | }", |
661 | ); | 660 | ); |
662 | } | 661 | } |
663 | 662 | ||
664 | #[test] | 663 | #[test] |
665 | fn test_not_applicable_if_variable_unused() { | 664 | fn test_not_applicable_if_variable_unused() { |
666 | covers!(test_not_applicable_if_variable_unused); | 665 | mark::check!(test_not_applicable_if_variable_unused); |
667 | check_assist_not_applicable( | 666 | check_assist_not_applicable( |
668 | inline_local_variable, | 667 | inline_local_variable, |
669 | r" | 668 | r" |
@@ -676,7 +675,7 @@ fn foo() { | |||
676 | 675 | ||
677 | #[test] | 676 | #[test] |
678 | fn not_applicable_outside_of_bind_pat() { | 677 | fn not_applicable_outside_of_bind_pat() { |
679 | covers!(not_applicable_outside_of_bind_pat); | 678 | mark::check!(not_applicable_outside_of_bind_pat); |
680 | check_assist_not_applicable( | 679 | check_assist_not_applicable( |
681 | inline_local_variable, | 680 | inline_local_variable, |
682 | r" | 681 | r" |
diff --git a/crates/ra_assists/src/handlers/introduce_variable.rs b/crates/ra_assists/src/handlers/introduce_variable.rs index fdf3ada0d..31d6539f7 100644 --- a/crates/ra_assists/src/handlers/introduce_variable.rs +++ b/crates/ra_assists/src/handlers/introduce_variable.rs | |||
@@ -4,10 +4,10 @@ use ra_syntax::{ | |||
4 | BLOCK_EXPR, BREAK_EXPR, COMMENT, LAMBDA_EXPR, LOOP_EXPR, MATCH_ARM, PATH_EXPR, RETURN_EXPR, | 4 | BLOCK_EXPR, BREAK_EXPR, COMMENT, LAMBDA_EXPR, LOOP_EXPR, MATCH_ARM, PATH_EXPR, RETURN_EXPR, |
5 | WHITESPACE, | 5 | WHITESPACE, |
6 | }, | 6 | }, |
7 | SyntaxNode, TextSize, | 7 | SyntaxNode, |
8 | }; | 8 | }; |
9 | use stdx::format_to; | 9 | use stdx::format_to; |
10 | use test_utils::tested_by; | 10 | use test_utils::mark; |
11 | 11 | ||
12 | use crate::{AssistContext, AssistId, Assists}; | 12 | use crate::{AssistContext, AssistId, Assists}; |
13 | 13 | ||
@@ -23,7 +23,7 @@ use crate::{AssistContext, AssistId, Assists}; | |||
23 | // -> | 23 | // -> |
24 | // ``` | 24 | // ``` |
25 | // fn main() { | 25 | // fn main() { |
26 | // let var_name = (1 + 2); | 26 | // let $0var_name = (1 + 2); |
27 | // var_name * 4; | 27 | // var_name * 4; |
28 | // } | 28 | // } |
29 | // ``` | 29 | // ``` |
@@ -33,7 +33,7 @@ pub(crate) fn introduce_variable(acc: &mut Assists, ctx: &AssistContext) -> Opti | |||
33 | } | 33 | } |
34 | let node = ctx.covering_element(); | 34 | let node = ctx.covering_element(); |
35 | if node.kind() == COMMENT { | 35 | if node.kind() == COMMENT { |
36 | tested_by!(introduce_var_in_comment_is_not_applicable); | 36 | mark::hit!(introduce_var_in_comment_is_not_applicable); |
37 | return None; | 37 | return None; |
38 | } | 38 | } |
39 | let expr = node.ancestors().find_map(valid_target_expr)?; | 39 | let expr = node.ancestors().find_map(valid_target_expr)?; |
@@ -46,14 +46,13 @@ pub(crate) fn introduce_variable(acc: &mut Assists, ctx: &AssistContext) -> Opti | |||
46 | acc.add(AssistId("introduce_variable"), "Extract into variable", target, move |edit| { | 46 | acc.add(AssistId("introduce_variable"), "Extract into variable", target, 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 | if wrap_in_block { |
50 | buf.push_str("{ let var_name = "); | 50 | buf.push_str("{ let var_name = "); |
51 | TextSize::of("{ let ") | ||
52 | } else { | 51 | } else { |
53 | buf.push_str("let var_name = "); | 52 | buf.push_str("let var_name = "); |
54 | TextSize::of("let ") | ||
55 | }; | 53 | }; |
56 | format_to!(buf, "{}", expr.syntax()); | 54 | format_to!(buf, "{}", expr.syntax()); |
55 | |||
57 | let full_stmt = ast::ExprStmt::cast(anchor_stmt.clone()); | 56 | let full_stmt = ast::ExprStmt::cast(anchor_stmt.clone()); |
58 | let is_full_stmt = if let Some(expr_stmt) = &full_stmt { | 57 | let is_full_stmt = if let Some(expr_stmt) = &full_stmt { |
59 | Some(expr.syntax().clone()) == expr_stmt.expr().map(|e| e.syntax().clone()) | 58 | Some(expr.syntax().clone()) == expr_stmt.expr().map(|e| e.syntax().clone()) |
@@ -61,32 +60,47 @@ pub(crate) fn introduce_variable(acc: &mut Assists, ctx: &AssistContext) -> Opti | |||
61 | false | 60 | false |
62 | }; | 61 | }; |
63 | if is_full_stmt { | 62 | if is_full_stmt { |
64 | tested_by!(test_introduce_var_expr_stmt); | 63 | mark::hit!(test_introduce_var_expr_stmt); |
65 | if full_stmt.unwrap().semicolon_token().is_none() { | 64 | if full_stmt.unwrap().semicolon_token().is_none() { |
66 | buf.push_str(";"); | 65 | buf.push_str(";"); |
67 | } | 66 | } |
68 | edit.replace(expr.syntax().text_range(), buf); | 67 | let offset = expr.syntax().text_range(); |
69 | } else { | 68 | match ctx.config.snippet_cap { |
70 | buf.push_str(";"); | 69 | Some(cap) => { |
71 | 70 | let snip = buf.replace("let var_name", "let $0var_name"); | |
72 | // We want to maintain the indent level, | 71 | edit.replace_snippet(cap, offset, snip) |
73 | // but we do not want to duplicate possible | 72 | } |
74 | // extra newlines in the indent block | 73 | None => edit.replace(offset, buf), |
75 | let text = indent.text(); | ||
76 | if text.starts_with('\n') { | ||
77 | buf.push_str("\n"); | ||
78 | buf.push_str(text.trim_start_matches('\n')); | ||
79 | } else { | ||
80 | buf.push_str(text); | ||
81 | } | 74 | } |
75 | return; | ||
76 | } | ||
82 | 77 | ||
83 | edit.replace(expr.syntax().text_range(), "var_name".to_string()); | 78 | buf.push_str(";"); |
84 | edit.insert(anchor_stmt.text_range().start(), buf); | 79 | |
85 | if wrap_in_block { | 80 | // We want to maintain the indent level, |
86 | edit.insert(anchor_stmt.text_range().end(), " }"); | 81 | // but we do not want to duplicate possible |
82 | // extra newlines in the indent block | ||
83 | let text = indent.text(); | ||
84 | if text.starts_with('\n') { | ||
85 | buf.push_str("\n"); | ||
86 | buf.push_str(text.trim_start_matches('\n')); | ||
87 | } else { | ||
88 | buf.push_str(text); | ||
89 | } | ||
90 | |||
91 | edit.replace(expr.syntax().text_range(), "var_name".to_string()); | ||
92 | let offset = anchor_stmt.text_range().start(); | ||
93 | match ctx.config.snippet_cap { | ||
94 | Some(cap) => { | ||
95 | let snip = buf.replace("let var_name", "let $0var_name"); | ||
96 | edit.insert_snippet(cap, offset, snip) | ||
87 | } | 97 | } |
98 | None => edit.insert(offset, buf), | ||
99 | } | ||
100 | |||
101 | if wrap_in_block { | ||
102 | edit.insert(anchor_stmt.text_range().end(), " }"); | ||
88 | } | 103 | } |
89 | edit.set_cursor(anchor_stmt.text_range().start() + cursor_offset); | ||
90 | }) | 104 | }) |
91 | } | 105 | } |
92 | 106 | ||
@@ -113,7 +127,7 @@ fn anchor_stmt(expr: ast::Expr) -> Option<(SyntaxNode, bool)> { | |||
113 | expr.syntax().ancestors().find_map(|node| { | 127 | expr.syntax().ancestors().find_map(|node| { |
114 | if let Some(expr) = node.parent().and_then(ast::BlockExpr::cast).and_then(|it| it.expr()) { | 128 | if let Some(expr) = node.parent().and_then(ast::BlockExpr::cast).and_then(|it| it.expr()) { |
115 | if expr.syntax() == &node { | 129 | if expr.syntax() == &node { |
116 | tested_by!(test_introduce_var_last_expr); | 130 | mark::hit!(test_introduce_var_last_expr); |
117 | return Some((node, false)); | 131 | return Some((node, false)); |
118 | } | 132 | } |
119 | } | 133 | } |
@@ -134,7 +148,7 @@ fn anchor_stmt(expr: ast::Expr) -> Option<(SyntaxNode, bool)> { | |||
134 | 148 | ||
135 | #[cfg(test)] | 149 | #[cfg(test)] |
136 | mod tests { | 150 | mod tests { |
137 | use test_utils::covers; | 151 | use test_utils::mark; |
138 | 152 | ||
139 | use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; | 153 | use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; |
140 | 154 | ||
@@ -144,37 +158,37 @@ mod tests { | |||
144 | fn test_introduce_var_simple() { | 158 | fn test_introduce_var_simple() { |
145 | check_assist( | 159 | check_assist( |
146 | introduce_variable, | 160 | introduce_variable, |
147 | " | 161 | r#" |
148 | fn foo() { | 162 | fn foo() { |
149 | foo(<|>1 + 1<|>); | 163 | foo(<|>1 + 1<|>); |
150 | }", | 164 | }"#, |
151 | " | 165 | r#" |
152 | fn foo() { | 166 | fn foo() { |
153 | let <|>var_name = 1 + 1; | 167 | let $0var_name = 1 + 1; |
154 | foo(var_name); | 168 | foo(var_name); |
155 | }", | 169 | }"#, |
156 | ); | 170 | ); |
157 | } | 171 | } |
158 | 172 | ||
159 | #[test] | 173 | #[test] |
160 | fn introduce_var_in_comment_is_not_applicable() { | 174 | fn introduce_var_in_comment_is_not_applicable() { |
161 | covers!(introduce_var_in_comment_is_not_applicable); | 175 | mark::check!(introduce_var_in_comment_is_not_applicable); |
162 | check_assist_not_applicable(introduce_variable, "fn main() { 1 + /* <|>comment<|> */ 1; }"); | 176 | check_assist_not_applicable(introduce_variable, "fn main() { 1 + /* <|>comment<|> */ 1; }"); |
163 | } | 177 | } |
164 | 178 | ||
165 | #[test] | 179 | #[test] |
166 | fn test_introduce_var_expr_stmt() { | 180 | fn test_introduce_var_expr_stmt() { |
167 | covers!(test_introduce_var_expr_stmt); | 181 | mark::check!(test_introduce_var_expr_stmt); |
168 | check_assist( | 182 | check_assist( |
169 | introduce_variable, | 183 | introduce_variable, |
170 | " | 184 | r#" |
171 | fn foo() { | 185 | fn foo() { |
172 | <|>1 + 1<|>; | 186 | <|>1 + 1<|>; |
173 | }", | 187 | }"#, |
174 | " | 188 | r#" |
175 | fn foo() { | 189 | fn foo() { |
176 | let <|>var_name = 1 + 1; | 190 | let $0var_name = 1 + 1; |
177 | }", | 191 | }"#, |
178 | ); | 192 | ); |
179 | check_assist( | 193 | check_assist( |
180 | introduce_variable, | 194 | introduce_variable, |
@@ -185,7 +199,7 @@ fn foo() { | |||
185 | }", | 199 | }", |
186 | " | 200 | " |
187 | fn foo() { | 201 | fn foo() { |
188 | let <|>var_name = { let x = 0; x }; | 202 | let $0var_name = { let x = 0; x }; |
189 | something_else(); | 203 | something_else(); |
190 | }", | 204 | }", |
191 | ); | 205 | ); |
@@ -201,7 +215,7 @@ fn foo() { | |||
201 | }", | 215 | }", |
202 | " | 216 | " |
203 | fn foo() { | 217 | fn foo() { |
204 | let <|>var_name = 1; | 218 | let $0var_name = 1; |
205 | var_name + 1; | 219 | var_name + 1; |
206 | }", | 220 | }", |
207 | ); | 221 | ); |
@@ -209,7 +223,7 @@ fn foo() { | |||
209 | 223 | ||
210 | #[test] | 224 | #[test] |
211 | fn test_introduce_var_last_expr() { | 225 | fn test_introduce_var_last_expr() { |
212 | covers!(test_introduce_var_last_expr); | 226 | mark::check!(test_introduce_var_last_expr); |
213 | check_assist( | 227 | check_assist( |
214 | introduce_variable, | 228 | introduce_variable, |
215 | " | 229 | " |
@@ -218,7 +232,7 @@ fn foo() { | |||
218 | }", | 232 | }", |
219 | " | 233 | " |
220 | fn foo() { | 234 | fn foo() { |
221 | let <|>var_name = 1 + 1; | 235 | let $0var_name = 1 + 1; |
222 | bar(var_name) | 236 | bar(var_name) |
223 | }", | 237 | }", |
224 | ); | 238 | ); |
@@ -230,7 +244,7 @@ fn foo() { | |||
230 | }", | 244 | }", |
231 | " | 245 | " |
232 | fn foo() { | 246 | fn foo() { |
233 | let <|>var_name = bar(1 + 1); | 247 | let $0var_name = bar(1 + 1); |
234 | var_name | 248 | var_name |
235 | }", | 249 | }", |
236 | ) | 250 | ) |
@@ -253,7 +267,7 @@ fn main() { | |||
253 | fn main() { | 267 | fn main() { |
254 | let x = true; | 268 | let x = true; |
255 | let tuple = match x { | 269 | let tuple = match x { |
256 | true => { let <|>var_name = 2 + 2; (var_name, true) } | 270 | true => { let $0var_name = 2 + 2; (var_name, true) } |
257 | _ => (0, false) | 271 | _ => (0, false) |
258 | }; | 272 | }; |
259 | } | 273 | } |
@@ -283,7 +297,7 @@ fn main() { | |||
283 | let tuple = match x { | 297 | let tuple = match x { |
284 | true => { | 298 | true => { |
285 | let y = 1; | 299 | let y = 1; |
286 | let <|>var_name = 2 + y; | 300 | let $0var_name = 2 + y; |
287 | (var_name, true) | 301 | (var_name, true) |
288 | } | 302 | } |
289 | _ => (0, false) | 303 | _ => (0, false) |
@@ -304,7 +318,7 @@ fn main() { | |||
304 | ", | 318 | ", |
305 | " | 319 | " |
306 | fn main() { | 320 | fn main() { |
307 | let lambda = |x: u32| { let <|>var_name = x * 2; var_name }; | 321 | let lambda = |x: u32| { let $0var_name = x * 2; var_name }; |
308 | } | 322 | } |
309 | ", | 323 | ", |
310 | ); | 324 | ); |
@@ -321,7 +335,7 @@ fn main() { | |||
321 | ", | 335 | ", |
322 | " | 336 | " |
323 | fn main() { | 337 | fn main() { |
324 | let lambda = |x: u32| { let <|>var_name = x * 2; var_name }; | 338 | let lambda = |x: u32| { let $0var_name = x * 2; var_name }; |
325 | } | 339 | } |
326 | ", | 340 | ", |
327 | ); | 341 | ); |
@@ -338,7 +352,7 @@ fn main() { | |||
338 | ", | 352 | ", |
339 | " | 353 | " |
340 | fn main() { | 354 | fn main() { |
341 | let <|>var_name = Some(true); | 355 | let $0var_name = Some(true); |
342 | let o = var_name; | 356 | let o = var_name; |
343 | } | 357 | } |
344 | ", | 358 | ", |
@@ -356,7 +370,7 @@ fn main() { | |||
356 | ", | 370 | ", |
357 | " | 371 | " |
358 | fn main() { | 372 | fn main() { |
359 | let <|>var_name = bar.foo(); | 373 | let $0var_name = bar.foo(); |
360 | let v = var_name; | 374 | let v = var_name; |
361 | } | 375 | } |
362 | ", | 376 | ", |
@@ -374,7 +388,7 @@ fn foo() -> u32 { | |||
374 | ", | 388 | ", |
375 | " | 389 | " |
376 | fn foo() -> u32 { | 390 | fn foo() -> u32 { |
377 | let <|>var_name = 2 + 2; | 391 | let $0var_name = 2 + 2; |
378 | return var_name; | 392 | return var_name; |
379 | } | 393 | } |
380 | ", | 394 | ", |
@@ -396,7 +410,7 @@ fn foo() -> u32 { | |||
396 | fn foo() -> u32 { | 410 | fn foo() -> u32 { |
397 | 411 | ||
398 | 412 | ||
399 | let <|>var_name = 2 + 2; | 413 | let $0var_name = 2 + 2; |
400 | return var_name; | 414 | return var_name; |
401 | } | 415 | } |
402 | ", | 416 | ", |
@@ -413,7 +427,7 @@ fn foo() -> u32 { | |||
413 | " | 427 | " |
414 | fn foo() -> u32 { | 428 | fn foo() -> u32 { |
415 | 429 | ||
416 | let <|>var_name = 2 + 2; | 430 | let $0var_name = 2 + 2; |
417 | return var_name; | 431 | return var_name; |
418 | } | 432 | } |
419 | ", | 433 | ", |
@@ -438,7 +452,7 @@ fn foo() -> u32 { | |||
438 | // bar | 452 | // bar |
439 | 453 | ||
440 | 454 | ||
441 | let <|>var_name = 2 + 2; | 455 | let $0var_name = 2 + 2; |
442 | return var_name; | 456 | return var_name; |
443 | } | 457 | } |
444 | ", | 458 | ", |
@@ -459,7 +473,7 @@ fn main() { | |||
459 | " | 473 | " |
460 | fn main() { | 474 | fn main() { |
461 | let result = loop { | 475 | let result = loop { |
462 | let <|>var_name = 2 + 2; | 476 | let $0var_name = 2 + 2; |
463 | break var_name; | 477 | break var_name; |
464 | }; | 478 | }; |
465 | } | 479 | } |
@@ -478,7 +492,7 @@ fn main() { | |||
478 | ", | 492 | ", |
479 | " | 493 | " |
480 | fn main() { | 494 | fn main() { |
481 | let <|>var_name = 0f32 as u32; | 495 | let $0var_name = 0f32 as u32; |
482 | let v = var_name; | 496 | let v = var_name; |
483 | } | 497 | } |
484 | ", | 498 | ", |
diff --git a/crates/ra_assists/src/handlers/invert_if.rs b/crates/ra_assists/src/handlers/invert_if.rs index 527c7caef..59d278eb9 100644 --- a/crates/ra_assists/src/handlers/invert_if.rs +++ b/crates/ra_assists/src/handlers/invert_if.rs | |||
@@ -72,7 +72,7 @@ mod tests { | |||
72 | check_assist( | 72 | check_assist( |
73 | invert_if, | 73 | invert_if, |
74 | "fn f() { i<|>f x != 3 { 1 } else { 3 + 2 } }", | 74 | "fn f() { i<|>f x != 3 { 1 } else { 3 + 2 } }", |
75 | "fn f() { i<|>f x == 3 { 3 + 2 } else { 1 } }", | 75 | "fn f() { if x == 3 { 3 + 2 } else { 1 } }", |
76 | ) | 76 | ) |
77 | } | 77 | } |
78 | 78 | ||
@@ -81,7 +81,7 @@ mod tests { | |||
81 | check_assist( | 81 | check_assist( |
82 | invert_if, | 82 | invert_if, |
83 | "fn f() { <|>if !cond { 3 * 2 } else { 1 } }", | 83 | "fn f() { <|>if !cond { 3 * 2 } else { 1 } }", |
84 | "fn f() { <|>if cond { 1 } else { 3 * 2 } }", | 84 | "fn f() { if cond { 1 } else { 3 * 2 } }", |
85 | ) | 85 | ) |
86 | } | 86 | } |
87 | 87 | ||
@@ -90,7 +90,7 @@ mod tests { | |||
90 | check_assist( | 90 | check_assist( |
91 | invert_if, | 91 | invert_if, |
92 | "fn f() { i<|>f cond { 3 * 2 } else { 1 } }", | 92 | "fn f() { i<|>f cond { 3 * 2 } else { 1 } }", |
93 | "fn f() { i<|>f !cond { 1 } else { 3 * 2 } }", | 93 | "fn f() { if !cond { 1 } else { 3 * 2 } }", |
94 | ) | 94 | ) |
95 | } | 95 | } |
96 | 96 | ||
diff --git a/crates/ra_assists/src/handlers/merge_imports.rs b/crates/ra_assists/src/handlers/merge_imports.rs index ac3e53c27..972d16241 100644 --- a/crates/ra_assists/src/handlers/merge_imports.rs +++ b/crates/ra_assists/src/handlers/merge_imports.rs | |||
@@ -58,8 +58,6 @@ pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext) -> Option<() | |||
58 | let target = tree.syntax().text_range(); | 58 | let target = tree.syntax().text_range(); |
59 | acc.add(AssistId("merge_imports"), "Merge imports", target, |builder| { | 59 | acc.add(AssistId("merge_imports"), "Merge imports", target, |builder| { |
60 | builder.rewrite(rewriter); | 60 | builder.rewrite(rewriter); |
61 | // FIXME: we only need because our diff is imprecise | ||
62 | builder.set_cursor(offset); | ||
63 | }) | 61 | }) |
64 | } | 62 | } |
65 | 63 | ||
@@ -142,7 +140,7 @@ use std::fmt<|>::Debug; | |||
142 | use std::fmt::Display; | 140 | use std::fmt::Display; |
143 | ", | 141 | ", |
144 | r" | 142 | r" |
145 | use std::fmt<|>::{Debug, Display}; | 143 | use std::fmt::{Debug, Display}; |
146 | ", | 144 | ", |
147 | ) | 145 | ) |
148 | } | 146 | } |
@@ -156,7 +154,7 @@ use std::fmt::Debug; | |||
156 | use std::fmt<|>::Display; | 154 | use std::fmt<|>::Display; |
157 | ", | 155 | ", |
158 | r" | 156 | r" |
159 | use std::fmt:<|>:{Display, Debug}; | 157 | use std::fmt::{Display, Debug}; |
160 | ", | 158 | ", |
161 | ); | 159 | ); |
162 | } | 160 | } |
@@ -169,7 +167,7 @@ use std::fmt:<|>:{Display, Debug}; | |||
169 | use std::{fmt<|>::Debug, fmt::Display}; | 167 | use std::{fmt<|>::Debug, fmt::Display}; |
170 | ", | 168 | ", |
171 | r" | 169 | r" |
172 | use std::{fmt<|>::{Debug, Display}}; | 170 | use std::{fmt::{Debug, Display}}; |
173 | ", | 171 | ", |
174 | ); | 172 | ); |
175 | check_assist( | 173 | check_assist( |
@@ -178,7 +176,7 @@ use std::{fmt<|>::{Debug, Display}}; | |||
178 | use std::{fmt::Debug, fmt<|>::Display}; | 176 | use std::{fmt::Debug, fmt<|>::Display}; |
179 | ", | 177 | ", |
180 | r" | 178 | r" |
181 | use std::{fmt::<|>{Display, Debug}}; | 179 | use std::{fmt::{Display, Debug}}; |
182 | ", | 180 | ", |
183 | ); | 181 | ); |
184 | } | 182 | } |
@@ -192,7 +190,7 @@ use std<|>::cell::*; | |||
192 | use std::str; | 190 | use std::str; |
193 | ", | 191 | ", |
194 | r" | 192 | r" |
195 | use std<|>::{cell::*, str}; | 193 | use std::{cell::*, str}; |
196 | ", | 194 | ", |
197 | ) | 195 | ) |
198 | } | 196 | } |
@@ -206,7 +204,7 @@ use std<|>::cell::*; | |||
206 | use std::str::*; | 204 | use std::str::*; |
207 | ", | 205 | ", |
208 | r" | 206 | r" |
209 | use std<|>::{cell::*, str::*}; | 207 | use std::{cell::*, str::*}; |
210 | ", | 208 | ", |
211 | ) | 209 | ) |
212 | } | 210 | } |
@@ -222,7 +220,7 @@ use foo::baz; | |||
222 | /// Doc comment | 220 | /// Doc comment |
223 | ", | 221 | ", |
224 | r" | 222 | r" |
225 | use foo<|>::{bar, baz}; | 223 | use foo::{bar, baz}; |
226 | 224 | ||
227 | /// Doc comment | 225 | /// Doc comment |
228 | ", | 226 | ", |
@@ -241,7 +239,7 @@ use { | |||
241 | ", | 239 | ", |
242 | r" | 240 | r" |
243 | use { | 241 | use { |
244 | foo<|>::{bar, baz}, | 242 | foo::{bar, baz}, |
245 | }; | 243 | }; |
246 | ", | 244 | ", |
247 | ); | 245 | ); |
@@ -255,7 +253,7 @@ use { | |||
255 | ", | 253 | ", |
256 | r" | 254 | r" |
257 | use { | 255 | use { |
258 | foo::{bar<|>, baz}, | 256 | foo::{bar, baz}, |
259 | }; | 257 | }; |
260 | ", | 258 | ", |
261 | ); | 259 | ); |
@@ -272,7 +270,7 @@ use foo::<|>{ | |||
272 | }; | 270 | }; |
273 | ", | 271 | ", |
274 | r" | 272 | r" |
275 | use foo::{<|> | 273 | use foo::{ |
276 | FooBar, | 274 | FooBar, |
277 | bar::baz}; | 275 | bar::baz}; |
278 | ", | 276 | ", |
diff --git a/crates/ra_assists/src/handlers/merge_match_arms.rs b/crates/ra_assists/src/handlers/merge_match_arms.rs index d4e38aa6a..ca04ec671 100644 --- a/crates/ra_assists/src/handlers/merge_match_arms.rs +++ b/crates/ra_assists/src/handlers/merge_match_arms.rs | |||
@@ -3,7 +3,7 @@ use std::iter::successors; | |||
3 | use ra_syntax::{ | 3 | use ra_syntax::{ |
4 | algo::neighbor, | 4 | algo::neighbor, |
5 | ast::{self, AstNode}, | 5 | ast::{self, AstNode}, |
6 | Direction, TextSize, | 6 | Direction, |
7 | }; | 7 | }; |
8 | 8 | ||
9 | use crate::{AssistContext, AssistId, Assists, TextRange}; | 9 | use crate::{AssistContext, AssistId, Assists, TextRange}; |
@@ -41,17 +41,6 @@ pub(crate) fn merge_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option | |||
41 | let current_expr = current_arm.expr()?; | 41 | let current_expr = current_arm.expr()?; |
42 | let current_text_range = current_arm.syntax().text_range(); | 42 | let current_text_range = current_arm.syntax().text_range(); |
43 | 43 | ||
44 | enum CursorPos { | ||
45 | InExpr(TextSize), | ||
46 | InPat(TextSize), | ||
47 | } | ||
48 | let cursor_pos = ctx.offset(); | ||
49 | let cursor_pos = if current_expr.syntax().text_range().contains(cursor_pos) { | ||
50 | CursorPos::InExpr(current_text_range.end() - cursor_pos) | ||
51 | } else { | ||
52 | CursorPos::InPat(cursor_pos) | ||
53 | }; | ||
54 | |||
55 | // We check if the following match arms match this one. We could, but don't, | 44 | // We check if the following match arms match this one. We could, but don't, |
56 | // compare to the previous match arm as well. | 45 | // compare to the previous match arm as well. |
57 | let arms_to_merge = successors(Some(current_arm), |it| neighbor(it, Direction::Next)) | 46 | let arms_to_merge = successors(Some(current_arm), |it| neighbor(it, Direction::Next)) |
@@ -87,10 +76,6 @@ pub(crate) fn merge_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option | |||
87 | let start = arms_to_merge.first().unwrap().syntax().text_range().start(); | 76 | let start = arms_to_merge.first().unwrap().syntax().text_range().start(); |
88 | let end = arms_to_merge.last().unwrap().syntax().text_range().end(); | 77 | let end = arms_to_merge.last().unwrap().syntax().text_range().end(); |
89 | 78 | ||
90 | edit.set_cursor(match cursor_pos { | ||
91 | CursorPos::InExpr(back_offset) => start + TextSize::of(&arm) - back_offset, | ||
92 | CursorPos::InPat(offset) => offset, | ||
93 | }); | ||
94 | edit.replace(TextRange::new(start, end), arm); | 79 | edit.replace(TextRange::new(start, end), arm); |
95 | }) | 80 | }) |
96 | } | 81 | } |
@@ -132,7 +117,7 @@ mod tests { | |||
132 | fn main() { | 117 | fn main() { |
133 | let x = X::A; | 118 | let x = X::A; |
134 | let y = match x { | 119 | let y = match x { |
135 | X::A | X::B => { 1i32<|> } | 120 | X::A | X::B => { 1i32 } |
136 | X::C => { 2i32 } | 121 | X::C => { 2i32 } |
137 | } | 122 | } |
138 | } | 123 | } |
@@ -164,7 +149,7 @@ mod tests { | |||
164 | fn main() { | 149 | fn main() { |
165 | let x = X::A; | 150 | let x = X::A; |
166 | let y = match x { | 151 | let y = match x { |
167 | X::A | X::B | X::C | X::D => {<|> 1i32 }, | 152 | X::A | X::B | X::C | X::D => { 1i32 }, |
168 | X::E => { 2i32 }, | 153 | X::E => { 2i32 }, |
169 | } | 154 | } |
170 | } | 155 | } |
@@ -197,7 +182,7 @@ mod tests { | |||
197 | let x = X::A; | 182 | let x = X::A; |
198 | let y = match x { | 183 | let y = match x { |
199 | X::A => { 1i32 }, | 184 | X::A => { 1i32 }, |
200 | _ => { 2i<|>32 } | 185 | _ => { 2i32 } |
201 | } | 186 | } |
202 | } | 187 | } |
203 | "#, | 188 | "#, |
@@ -226,7 +211,7 @@ mod tests { | |||
226 | 211 | ||
227 | fn main() { | 212 | fn main() { |
228 | match X::A { | 213 | match X::A { |
229 | X::A<|> | X::B | X::C => 92, | 214 | X::A | X::B | X::C => 92, |
230 | X::D => 62, | 215 | X::D => 62, |
231 | _ => panic!(), | 216 | _ => panic!(), |
232 | } | 217 | } |
diff --git a/crates/ra_assists/src/handlers/move_bounds.rs b/crates/ra_assists/src/handlers/move_bounds.rs index a41aacfc3..be2a7eddc 100644 --- a/crates/ra_assists/src/handlers/move_bounds.rs +++ b/crates/ra_assists/src/handlers/move_bounds.rs | |||
@@ -99,7 +99,7 @@ mod tests { | |||
99 | fn foo<T: u32, <|>F: FnOnce(T) -> T>() {} | 99 | fn foo<T: u32, <|>F: FnOnce(T) -> T>() {} |
100 | "#, | 100 | "#, |
101 | r#" | 101 | r#" |
102 | fn foo<T, <|>F>() where T: u32, F: FnOnce(T) -> T {} | 102 | fn foo<T, F>() where T: u32, F: FnOnce(T) -> T {} |
103 | "#, | 103 | "#, |
104 | ); | 104 | ); |
105 | } | 105 | } |
@@ -112,7 +112,7 @@ mod tests { | |||
112 | impl<U: u32, <|>T> A<U, T> {} | 112 | impl<U: u32, <|>T> A<U, T> {} |
113 | "#, | 113 | "#, |
114 | r#" | 114 | r#" |
115 | impl<U, <|>T> A<U, T> where U: u32 {} | 115 | impl<U, T> A<U, T> where U: u32 {} |
116 | "#, | 116 | "#, |
117 | ); | 117 | ); |
118 | } | 118 | } |
@@ -125,7 +125,7 @@ mod tests { | |||
125 | struct A<<|>T: Iterator<Item = u32>> {} | 125 | struct A<<|>T: Iterator<Item = u32>> {} |
126 | "#, | 126 | "#, |
127 | r#" | 127 | r#" |
128 | struct A<<|>T> where T: Iterator<Item = u32> {} | 128 | struct A<T> where T: Iterator<Item = u32> {} |
129 | "#, | 129 | "#, |
130 | ); | 130 | ); |
131 | } | 131 | } |
@@ -138,7 +138,7 @@ mod tests { | |||
138 | struct Pair<<|>T: u32>(T, T); | 138 | struct Pair<<|>T: u32>(T, T); |
139 | "#, | 139 | "#, |
140 | r#" | 140 | r#" |
141 | struct Pair<<|>T>(T, T) where T: u32; | 141 | struct Pair<T>(T, T) where T: u32; |
142 | "#, | 142 | "#, |
143 | ); | 143 | ); |
144 | } | 144 | } |
diff --git a/crates/ra_assists/src/handlers/move_guard.rs b/crates/ra_assists/src/handlers/move_guard.rs index fc0335b57..7edcf0748 100644 --- a/crates/ra_assists/src/handlers/move_guard.rs +++ b/crates/ra_assists/src/handlers/move_guard.rs | |||
@@ -1,7 +1,6 @@ | |||
1 | use ra_syntax::{ | 1 | use ra_syntax::{ |
2 | ast, | 2 | ast::{AstNode, IfExpr, MatchArm}, |
3 | ast::{AstNode, AstToken, IfExpr, MatchArm}, | 3 | SyntaxKind::WHITESPACE, |
4 | TextSize, | ||
5 | }; | 4 | }; |
6 | 5 | ||
7 | use crate::{AssistContext, AssistId, Assists}; | 6 | use crate::{AssistContext, AssistId, Assists}; |
@@ -42,24 +41,15 @@ pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext) -> | |||
42 | 41 | ||
43 | let target = guard.syntax().text_range(); | 42 | let target = guard.syntax().text_range(); |
44 | acc.add(AssistId("move_guard_to_arm_body"), "Move guard to arm body", target, |edit| { | 43 | acc.add(AssistId("move_guard_to_arm_body"), "Move guard to arm body", target, |edit| { |
45 | let offseting_amount = match space_before_guard.and_then(|it| it.into_token()) { | 44 | match space_before_guard { |
46 | Some(tok) => { | 45 | Some(element) if element.kind() == WHITESPACE => { |
47 | if ast::Whitespace::cast(tok.clone()).is_some() { | 46 | edit.delete(element.text_range()); |
48 | let ele = tok.text_range(); | ||
49 | edit.delete(ele); | ||
50 | ele.len() | ||
51 | } else { | ||
52 | TextSize::from(0) | ||
53 | } | ||
54 | } | 47 | } |
55 | _ => TextSize::from(0), | 48 | _ => (), |
56 | }; | 49 | }; |
57 | 50 | ||
58 | edit.delete(guard.syntax().text_range()); | 51 | edit.delete(guard.syntax().text_range()); |
59 | edit.replace_node_and_indent(arm_expr.syntax(), buf); | 52 | edit.replace_node_and_indent(arm_expr.syntax(), buf); |
60 | edit.set_cursor( | ||
61 | arm_expr.syntax().text_range().start() + TextSize::from(3) - offseting_amount, | ||
62 | ); | ||
63 | }) | 53 | }) |
64 | } | 54 | } |
65 | 55 | ||
@@ -124,7 +114,6 @@ pub(crate) fn move_arm_cond_to_match_guard(acc: &mut Assists, ctx: &AssistContex | |||
124 | } | 114 | } |
125 | 115 | ||
126 | edit.insert(match_pat.syntax().text_range().end(), buf); | 116 | edit.insert(match_pat.syntax().text_range().end(), buf); |
127 | edit.set_cursor(match_pat.syntax().text_range().end() + TextSize::from(1)); | ||
128 | }, | 117 | }, |
129 | ) | 118 | ) |
130 | } | 119 | } |
@@ -172,7 +161,7 @@ mod tests { | |||
172 | let t = 'a'; | 161 | let t = 'a'; |
173 | let chars = "abcd"; | 162 | let chars = "abcd"; |
174 | match t { | 163 | match t { |
175 | '\r' => if chars.clone().next() == Some('\n') { <|>false }, | 164 | '\r' => if chars.clone().next() == Some('\n') { false }, |
176 | _ => true | 165 | _ => true |
177 | } | 166 | } |
178 | } | 167 | } |
@@ -195,7 +184,7 @@ mod tests { | |||
195 | r#" | 184 | r#" |
196 | fn f() { | 185 | fn f() { |
197 | match x { | 186 | match x { |
198 | y @ 4 | y @ 5 => if y > 5 { <|>true }, | 187 | y @ 4 | y @ 5 => if y > 5 { true }, |
199 | _ => false | 188 | _ => false |
200 | } | 189 | } |
201 | } | 190 | } |
@@ -222,7 +211,7 @@ mod tests { | |||
222 | let t = 'a'; | 211 | let t = 'a'; |
223 | let chars = "abcd"; | 212 | let chars = "abcd"; |
224 | match t { | 213 | match t { |
225 | '\r' <|>if chars.clone().next() == Some('\n') => false, | 214 | '\r' if chars.clone().next() == Some('\n') => false, |
226 | _ => true | 215 | _ => true |
227 | } | 216 | } |
228 | } | 217 | } |
@@ -266,7 +255,7 @@ mod tests { | |||
266 | let t = 'a'; | 255 | let t = 'a'; |
267 | let chars = "abcd"; | 256 | let chars = "abcd"; |
268 | match t { | 257 | match t { |
269 | '\r' <|>if chars.clone().next().is_some() => { }, | 258 | '\r' if chars.clone().next().is_some() => { }, |
270 | _ => true | 259 | _ => true |
271 | } | 260 | } |
272 | } | 261 | } |
@@ -296,7 +285,7 @@ mod tests { | |||
296 | let mut t = 'a'; | 285 | let mut t = 'a'; |
297 | let chars = "abcd"; | 286 | let chars = "abcd"; |
298 | match t { | 287 | match t { |
299 | '\r' <|>if chars.clone().next().is_some() => { | 288 | '\r' if chars.clone().next().is_some() => { |
300 | t = 'e'; | 289 | t = 'e'; |
301 | false | 290 | false |
302 | }, | 291 | }, |
diff --git a/crates/ra_assists/src/handlers/raw_string.rs b/crates/ra_assists/src/handlers/raw_string.rs index c20ffe0b3..16002d2ac 100644 --- a/crates/ra_assists/src/handlers/raw_string.rs +++ b/crates/ra_assists/src/handlers/raw_string.rs | |||
@@ -164,7 +164,7 @@ mod test { | |||
164 | "#, | 164 | "#, |
165 | r##" | 165 | r##" |
166 | fn f() { | 166 | fn f() { |
167 | let s = <|>r#"random | 167 | let s = r#"random |
168 | string"#; | 168 | string"#; |
169 | } | 169 | } |
170 | "##, | 170 | "##, |
@@ -182,7 +182,7 @@ string"#; | |||
182 | "#, | 182 | "#, |
183 | r##" | 183 | r##" |
184 | fn f() { | 184 | fn f() { |
185 | format!(<|>r#"x = {}"#, 92) | 185 | format!(r#"x = {}"#, 92) |
186 | } | 186 | } |
187 | "##, | 187 | "##, |
188 | ) | 188 | ) |
@@ -199,7 +199,7 @@ string"#; | |||
199 | "###, | 199 | "###, |
200 | r####" | 200 | r####" |
201 | fn f() { | 201 | fn f() { |
202 | let s = <|>r#"#random## | 202 | let s = r#"#random## |
203 | string"#; | 203 | string"#; |
204 | } | 204 | } |
205 | "####, | 205 | "####, |
@@ -217,7 +217,7 @@ string"#; | |||
217 | "###, | 217 | "###, |
218 | r####" | 218 | r####" |
219 | fn f() { | 219 | fn f() { |
220 | let s = <|>r###"#random"## | 220 | let s = r###"#random"## |
221 | string"###; | 221 | string"###; |
222 | } | 222 | } |
223 | "####, | 223 | "####, |
@@ -235,7 +235,7 @@ string"###; | |||
235 | "#, | 235 | "#, |
236 | r##" | 236 | r##" |
237 | fn f() { | 237 | fn f() { |
238 | let s = <|>r#"random string"#; | 238 | let s = r#"random string"#; |
239 | } | 239 | } |
240 | "##, | 240 | "##, |
241 | ) | 241 | ) |
@@ -289,7 +289,7 @@ string"###; | |||
289 | "#, | 289 | "#, |
290 | r##" | 290 | r##" |
291 | fn f() { | 291 | fn f() { |
292 | let s = <|>r#"random string"#; | 292 | let s = r#"random string"#; |
293 | } | 293 | } |
294 | "##, | 294 | "##, |
295 | ) | 295 | ) |
@@ -306,7 +306,7 @@ string"###; | |||
306 | "##, | 306 | "##, |
307 | r###" | 307 | r###" |
308 | fn f() { | 308 | fn f() { |
309 | let s = <|>r##"random"string"##; | 309 | let s = r##"random"string"##; |
310 | } | 310 | } |
311 | "###, | 311 | "###, |
312 | ) | 312 | ) |
@@ -348,7 +348,7 @@ string"###; | |||
348 | "##, | 348 | "##, |
349 | r#" | 349 | r#" |
350 | fn f() { | 350 | fn f() { |
351 | let s = <|>r"random string"; | 351 | let s = r"random string"; |
352 | } | 352 | } |
353 | "#, | 353 | "#, |
354 | ) | 354 | ) |
@@ -365,7 +365,7 @@ string"###; | |||
365 | "##, | 365 | "##, |
366 | r#" | 366 | r#" |
367 | fn f() { | 367 | fn f() { |
368 | let s = <|>r"random\"str\"ing"; | 368 | let s = r"random\"str\"ing"; |
369 | } | 369 | } |
370 | "#, | 370 | "#, |
371 | ) | 371 | ) |
@@ -382,7 +382,7 @@ string"###; | |||
382 | "###, | 382 | "###, |
383 | r##" | 383 | r##" |
384 | fn f() { | 384 | fn f() { |
385 | let s = <|>r#"random string"#; | 385 | let s = r#"random string"#; |
386 | } | 386 | } |
387 | "##, | 387 | "##, |
388 | ) | 388 | ) |
@@ -436,7 +436,7 @@ string"###; | |||
436 | "##, | 436 | "##, |
437 | r#" | 437 | r#" |
438 | fn f() { | 438 | fn f() { |
439 | let s = <|>"random string"; | 439 | let s = "random string"; |
440 | } | 440 | } |
441 | "#, | 441 | "#, |
442 | ) | 442 | ) |
@@ -453,7 +453,7 @@ string"###; | |||
453 | "##, | 453 | "##, |
454 | r#" | 454 | r#" |
455 | fn f() { | 455 | fn f() { |
456 | let s = <|>"random\"str\"ing"; | 456 | let s = "random\"str\"ing"; |
457 | } | 457 | } |
458 | "#, | 458 | "#, |
459 | ) | 459 | ) |
@@ -470,7 +470,7 @@ string"###; | |||
470 | "###, | 470 | "###, |
471 | r##" | 471 | r##" |
472 | fn f() { | 472 | fn f() { |
473 | let s = <|>"random string"; | 473 | let s = "random string"; |
474 | } | 474 | } |
475 | "##, | 475 | "##, |
476 | ) | 476 | ) |
diff --git a/crates/ra_assists/src/handlers/remove_dbg.rs b/crates/ra_assists/src/handlers/remove_dbg.rs index 8eef578cf..961ee1731 100644 --- a/crates/ra_assists/src/handlers/remove_dbg.rs +++ b/crates/ra_assists/src/handlers/remove_dbg.rs | |||
@@ -29,26 +29,6 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | |||
29 | 29 | ||
30 | let macro_range = macro_call.syntax().text_range(); | 30 | let macro_range = macro_call.syntax().text_range(); |
31 | 31 | ||
32 | // If the cursor is inside the macro call, we'll try to maintain the cursor | ||
33 | // position by subtracting the length of dbg!( from the start of the file | ||
34 | // range, otherwise we'll default to using the start of the macro call | ||
35 | let cursor_pos = { | ||
36 | let file_range = ctx.frange.range; | ||
37 | |||
38 | let offset_start = file_range | ||
39 | .start() | ||
40 | .checked_sub(macro_range.start()) | ||
41 | .unwrap_or_else(|| TextSize::from(0)); | ||
42 | |||
43 | let dbg_size = TextSize::of("dbg!("); | ||
44 | |||
45 | if offset_start > dbg_size { | ||
46 | file_range.start() - dbg_size | ||
47 | } else { | ||
48 | macro_range.start() | ||
49 | } | ||
50 | }; | ||
51 | |||
52 | let macro_content = { | 32 | let macro_content = { |
53 | let macro_args = macro_call.token_tree()?.syntax().clone(); | 33 | let macro_args = macro_call.token_tree()?.syntax().clone(); |
54 | 34 | ||
@@ -58,9 +38,8 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | |||
58 | }; | 38 | }; |
59 | 39 | ||
60 | let target = macro_call.syntax().text_range(); | 40 | let target = macro_call.syntax().text_range(); |
61 | acc.add(AssistId("remove_dbg"), "Remove dbg!()", target, |edit| { | 41 | acc.add(AssistId("remove_dbg"), "Remove dbg!()", target, |builder| { |
62 | edit.replace(macro_range, macro_content); | 42 | builder.replace(macro_range, macro_content); |
63 | edit.set_cursor(cursor_pos); | ||
64 | }) | 43 | }) |
65 | } | 44 | } |
66 | 45 | ||
@@ -94,13 +73,13 @@ mod tests { | |||
94 | 73 | ||
95 | #[test] | 74 | #[test] |
96 | fn test_remove_dbg() { | 75 | fn test_remove_dbg() { |
97 | check_assist(remove_dbg, "<|>dbg!(1 + 1)", "<|>1 + 1"); | 76 | check_assist(remove_dbg, "<|>dbg!(1 + 1)", "1 + 1"); |
98 | 77 | ||
99 | check_assist(remove_dbg, "dbg!<|>((1 + 1))", "<|>(1 + 1)"); | 78 | check_assist(remove_dbg, "dbg!<|>((1 + 1))", "(1 + 1)"); |
100 | 79 | ||
101 | check_assist(remove_dbg, "dbg!(1 <|>+ 1)", "1 <|>+ 1"); | 80 | check_assist(remove_dbg, "dbg!(1 <|>+ 1)", "1 + 1"); |
102 | 81 | ||
103 | check_assist(remove_dbg, "let _ = <|>dbg!(1 + 1)", "let _ = <|>1 + 1"); | 82 | check_assist(remove_dbg, "let _ = <|>dbg!(1 + 1)", "let _ = 1 + 1"); |
104 | 83 | ||
105 | check_assist( | 84 | check_assist( |
106 | remove_dbg, | 85 | remove_dbg, |
@@ -113,7 +92,7 @@ fn foo(n: usize) { | |||
113 | ", | 92 | ", |
114 | " | 93 | " |
115 | fn foo(n: usize) { | 94 | fn foo(n: usize) { |
116 | if let Some(_) = n.<|>checked_sub(4) { | 95 | if let Some(_) = n.checked_sub(4) { |
117 | // ... | 96 | // ... |
118 | } | 97 | } |
119 | } | 98 | } |
@@ -122,8 +101,8 @@ fn foo(n: usize) { | |||
122 | } | 101 | } |
123 | #[test] | 102 | #[test] |
124 | fn test_remove_dbg_with_brackets_and_braces() { | 103 | fn test_remove_dbg_with_brackets_and_braces() { |
125 | check_assist(remove_dbg, "dbg![<|>1 + 1]", "<|>1 + 1"); | 104 | check_assist(remove_dbg, "dbg![<|>1 + 1]", "1 + 1"); |
126 | check_assist(remove_dbg, "dbg!{<|>1 + 1}", "<|>1 + 1"); | 105 | check_assist(remove_dbg, "dbg!{<|>1 + 1}", "1 + 1"); |
127 | } | 106 | } |
128 | 107 | ||
129 | #[test] | 108 | #[test] |
diff --git a/crates/ra_assists/src/handlers/remove_mut.rs b/crates/ra_assists/src/handlers/remove_mut.rs index dce546db7..fe4eada03 100644 --- a/crates/ra_assists/src/handlers/remove_mut.rs +++ b/crates/ra_assists/src/handlers/remove_mut.rs | |||
@@ -26,8 +26,7 @@ pub(crate) fn remove_mut(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | |||
26 | }; | 26 | }; |
27 | 27 | ||
28 | let target = mut_token.text_range(); | 28 | let target = mut_token.text_range(); |
29 | acc.add(AssistId("remove_mut"), "Remove `mut` keyword", target, |edit| { | 29 | acc.add(AssistId("remove_mut"), "Remove `mut` keyword", target, |builder| { |
30 | edit.set_cursor(delete_from); | 30 | builder.delete(TextRange::new(delete_from, delete_to)); |
31 | edit.delete(TextRange::new(delete_from, delete_to)); | ||
32 | }) | 31 | }) |
33 | } | 32 | } |
diff --git a/crates/ra_assists/src/handlers/reorder_fields.rs b/crates/ra_assists/src/handlers/reorder_fields.rs index 757f6406e..30229edc2 100644 --- a/crates/ra_assists/src/handlers/reorder_fields.rs +++ b/crates/ra_assists/src/handlers/reorder_fields.rs | |||
@@ -140,7 +140,7 @@ mod tests { | |||
140 | "#, | 140 | "#, |
141 | r#" | 141 | r#" |
142 | struct Foo {foo: i32, bar: i32}; | 142 | struct Foo {foo: i32, bar: i32}; |
143 | const test: Foo = <|>Foo {foo: 1, bar: 0} | 143 | const test: Foo = Foo {foo: 1, bar: 0} |
144 | "#, | 144 | "#, |
145 | ) | 145 | ) |
146 | } | 146 | } |
@@ -164,7 +164,7 @@ mod tests { | |||
164 | 164 | ||
165 | fn f(f: Foo) -> { | 165 | fn f(f: Foo) -> { |
166 | match f { | 166 | match f { |
167 | <|>Foo { ref mut bar, baz: 0, .. } => (), | 167 | Foo { ref mut bar, baz: 0, .. } => (), |
168 | _ => () | 168 | _ => () |
169 | } | 169 | } |
170 | } | 170 | } |
@@ -202,7 +202,7 @@ mod tests { | |||
202 | impl Foo { | 202 | impl Foo { |
203 | fn new() -> Foo { | 203 | fn new() -> Foo { |
204 | let foo = String::new(); | 204 | let foo = String::new(); |
205 | <|>Foo { | 205 | Foo { |
206 | foo, | 206 | foo, |
207 | bar: foo.clone(), | 207 | bar: foo.clone(), |
208 | extra: "Extra field", | 208 | extra: "Extra field", |
diff --git a/crates/ra_assists/src/handlers/replace_if_let_with_match.rs b/crates/ra_assists/src/handlers/replace_if_let_with_match.rs index 65f5fc6ab..e016f51c3 100644 --- a/crates/ra_assists/src/handlers/replace_if_let_with_match.rs +++ b/crates/ra_assists/src/handlers/replace_if_let_with_match.rs | |||
@@ -68,7 +68,6 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext) | |||
68 | .indent(IndentLevel::from_node(if_expr.syntax())) | 68 | .indent(IndentLevel::from_node(if_expr.syntax())) |
69 | }; | 69 | }; |
70 | 70 | ||
71 | edit.set_cursor(if_expr.syntax().text_range().start()); | ||
72 | edit.replace_ast::<ast::Expr>(if_expr.into(), match_expr); | 71 | edit.replace_ast::<ast::Expr>(if_expr.into(), match_expr); |
73 | }) | 72 | }) |
74 | } | 73 | } |
@@ -83,7 +82,7 @@ mod tests { | |||
83 | fn test_replace_if_let_with_match_unwraps_simple_expressions() { | 82 | fn test_replace_if_let_with_match_unwraps_simple_expressions() { |
84 | check_assist( | 83 | check_assist( |
85 | replace_if_let_with_match, | 84 | replace_if_let_with_match, |
86 | " | 85 | r#" |
87 | impl VariantData { | 86 | impl VariantData { |
88 | pub fn is_struct(&self) -> bool { | 87 | pub fn is_struct(&self) -> bool { |
89 | if <|>let VariantData::Struct(..) = *self { | 88 | if <|>let VariantData::Struct(..) = *self { |
@@ -92,16 +91,16 @@ impl VariantData { | |||
92 | false | 91 | false |
93 | } | 92 | } |
94 | } | 93 | } |
95 | } ", | 94 | } "#, |
96 | " | 95 | r#" |
97 | impl VariantData { | 96 | impl VariantData { |
98 | pub fn is_struct(&self) -> bool { | 97 | pub fn is_struct(&self) -> bool { |
99 | <|>match *self { | 98 | match *self { |
100 | VariantData::Struct(..) => true, | 99 | VariantData::Struct(..) => true, |
101 | _ => false, | 100 | _ => false, |
102 | } | 101 | } |
103 | } | 102 | } |
104 | } ", | 103 | } "#, |
105 | ) | 104 | ) |
106 | } | 105 | } |
107 | 106 | ||
@@ -109,7 +108,7 @@ impl VariantData { | |||
109 | fn test_replace_if_let_with_match_doesnt_unwrap_multiline_expressions() { | 108 | fn test_replace_if_let_with_match_doesnt_unwrap_multiline_expressions() { |
110 | check_assist( | 109 | check_assist( |
111 | replace_if_let_with_match, | 110 | replace_if_let_with_match, |
112 | " | 111 | r#" |
113 | fn foo() { | 112 | fn foo() { |
114 | if <|>let VariantData::Struct(..) = a { | 113 | if <|>let VariantData::Struct(..) = a { |
115 | bar( | 114 | bar( |
@@ -118,10 +117,10 @@ fn foo() { | |||
118 | } else { | 117 | } else { |
119 | false | 118 | false |
120 | } | 119 | } |
121 | } ", | 120 | } "#, |
122 | " | 121 | r#" |
123 | fn foo() { | 122 | fn foo() { |
124 | <|>match a { | 123 | match a { |
125 | VariantData::Struct(..) => { | 124 | VariantData::Struct(..) => { |
126 | bar( | 125 | bar( |
127 | 123 | 126 | 123 |
@@ -129,7 +128,7 @@ fn foo() { | |||
129 | } | 128 | } |
130 | _ => false, | 129 | _ => false, |
131 | } | 130 | } |
132 | } ", | 131 | } "#, |
133 | ) | 132 | ) |
134 | } | 133 | } |
135 | 134 | ||
@@ -137,7 +136,7 @@ fn foo() { | |||
137 | fn replace_if_let_with_match_target() { | 136 | fn replace_if_let_with_match_target() { |
138 | check_assist_target( | 137 | check_assist_target( |
139 | replace_if_let_with_match, | 138 | replace_if_let_with_match, |
140 | " | 139 | r#" |
141 | impl VariantData { | 140 | impl VariantData { |
142 | pub fn is_struct(&self) -> bool { | 141 | pub fn is_struct(&self) -> bool { |
143 | if <|>let VariantData::Struct(..) = *self { | 142 | if <|>let VariantData::Struct(..) = *self { |
@@ -146,7 +145,7 @@ impl VariantData { | |||
146 | false | 145 | false |
147 | } | 146 | } |
148 | } | 147 | } |
149 | } ", | 148 | } "#, |
150 | "if let VariantData::Struct(..) = *self { | 149 | "if let VariantData::Struct(..) = *self { |
151 | true | 150 | true |
152 | } else { | 151 | } else { |
@@ -176,7 +175,7 @@ enum Option<T> { Some(T), None } | |||
176 | use Option::*; | 175 | use Option::*; |
177 | 176 | ||
178 | fn foo(x: Option<i32>) { | 177 | fn foo(x: Option<i32>) { |
179 | <|>match x { | 178 | match x { |
180 | Some(x) => println!("{}", x), | 179 | Some(x) => println!("{}", x), |
181 | None => println!("none"), | 180 | None => println!("none"), |
182 | } | 181 | } |
@@ -206,7 +205,7 @@ enum Result<T, E> { Ok(T), Err(E) } | |||
206 | use Result::*; | 205 | use Result::*; |
207 | 206 | ||
208 | fn foo(x: Result<i32, ()>) { | 207 | fn foo(x: Result<i32, ()>) { |
209 | <|>match x { | 208 | match x { |
210 | Ok(x) => println!("{}", x), | 209 | Ok(x) => println!("{}", x), |
211 | Err(_) => println!("none"), | 210 | Err(_) => println!("none"), |
212 | } | 211 | } |
diff --git a/crates/ra_assists/src/handlers/replace_let_with_if_let.rs b/crates/ra_assists/src/handlers/replace_let_with_if_let.rs index 482957dc6..761557ac0 100644 --- a/crates/ra_assists/src/handlers/replace_let_with_if_let.rs +++ b/crates/ra_assists/src/handlers/replace_let_with_if_let.rs | |||
@@ -58,12 +58,9 @@ pub(crate) fn replace_let_with_if_let(acc: &mut Assists, ctx: &AssistContext) -> | |||
58 | let stmt = make::expr_stmt(if_); | 58 | let stmt = make::expr_stmt(if_); |
59 | 59 | ||
60 | let placeholder = stmt.syntax().descendants().find_map(ast::PlaceholderPat::cast).unwrap(); | 60 | let placeholder = stmt.syntax().descendants().find_map(ast::PlaceholderPat::cast).unwrap(); |
61 | let target_offset = | ||
62 | let_stmt.syntax().text_range().start() + placeholder.syntax().text_range().start(); | ||
63 | let stmt = stmt.replace_descendant(placeholder.into(), original_pat); | 61 | let stmt = stmt.replace_descendant(placeholder.into(), original_pat); |
64 | 62 | ||
65 | edit.replace_ast(ast::Stmt::from(let_stmt), ast::Stmt::from(stmt)); | 63 | edit.replace_ast(ast::Stmt::from(let_stmt), ast::Stmt::from(stmt)); |
66 | edit.set_cursor(target_offset); | ||
67 | }) | 64 | }) |
68 | } | 65 | } |
69 | 66 | ||
@@ -88,7 +85,7 @@ fn main() { | |||
88 | enum E<T> { X(T), Y(T) } | 85 | enum E<T> { X(T), Y(T) } |
89 | 86 | ||
90 | fn main() { | 87 | fn main() { |
91 | if let <|>x = E::X(92) { | 88 | if let x = E::X(92) { |
92 | } | 89 | } |
93 | } | 90 | } |
94 | ", | 91 | ", |
diff --git a/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs b/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs index 1a81d8a0e..0197a8cf0 100644 --- a/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs +++ b/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs | |||
@@ -39,7 +39,7 @@ pub(crate) fn replace_qualified_name_with_use( | |||
39 | target, | 39 | target, |
40 | |builder| { | 40 | |builder| { |
41 | let path_to_import = hir_path.mod_path().clone(); | 41 | let path_to_import = hir_path.mod_path().clone(); |
42 | insert_use_statement(path.syntax(), &path_to_import, ctx, builder); | 42 | insert_use_statement(path.syntax(), &path_to_import, ctx, builder.text_edit_builder()); |
43 | 43 | ||
44 | if let Some(last) = path.segment() { | 44 | if let Some(last) = path.segment() { |
45 | // Here we are assuming the assist will provide a correct use statement | 45 | // Here we are assuming the assist will provide a correct use statement |
@@ -89,7 +89,7 @@ std::fmt::Debug<|> | |||
89 | " | 89 | " |
90 | use std::fmt::Debug; | 90 | use std::fmt::Debug; |
91 | 91 | ||
92 | Debug<|> | 92 | Debug |
93 | ", | 93 | ", |
94 | ); | 94 | ); |
95 | } | 95 | } |
@@ -106,7 +106,7 @@ fn main() { | |||
106 | " | 106 | " |
107 | use std::fmt::Debug; | 107 | use std::fmt::Debug; |
108 | 108 | ||
109 | Debug<|> | 109 | Debug |
110 | 110 | ||
111 | fn main() { | 111 | fn main() { |
112 | } | 112 | } |
@@ -130,7 +130,7 @@ use std::fmt::Debug; | |||
130 | fn main() { | 130 | fn main() { |
131 | } | 131 | } |
132 | 132 | ||
133 | Debug<|> | 133 | Debug |
134 | ", | 134 | ", |
135 | ); | 135 | ); |
136 | } | 136 | } |
@@ -145,7 +145,7 @@ std::fmt<|>::Debug | |||
145 | " | 145 | " |
146 | use std::fmt; | 146 | use std::fmt; |
147 | 147 | ||
148 | fmt<|>::Debug | 148 | fmt::Debug |
149 | ", | 149 | ", |
150 | ); | 150 | ); |
151 | } | 151 | } |
@@ -164,7 +164,7 @@ impl std::fmt::Debug<|> for Foo { | |||
164 | use stdx; | 164 | use stdx; |
165 | use std::fmt::Debug; | 165 | use std::fmt::Debug; |
166 | 166 | ||
167 | impl Debug<|> for Foo { | 167 | impl Debug for Foo { |
168 | } | 168 | } |
169 | ", | 169 | ", |
170 | ); | 170 | ); |
@@ -181,7 +181,7 @@ impl std::fmt::Debug<|> for Foo { | |||
181 | " | 181 | " |
182 | use std::fmt::Debug; | 182 | use std::fmt::Debug; |
183 | 183 | ||
184 | impl Debug<|> for Foo { | 184 | impl Debug for Foo { |
185 | } | 185 | } |
186 | ", | 186 | ", |
187 | ); | 187 | ); |
@@ -198,7 +198,7 @@ impl Debug<|> for Foo { | |||
198 | " | 198 | " |
199 | use std::fmt::Debug; | 199 | use std::fmt::Debug; |
200 | 200 | ||
201 | impl Debug<|> for Foo { | 201 | impl Debug for Foo { |
202 | } | 202 | } |
203 | ", | 203 | ", |
204 | ); | 204 | ); |
@@ -217,7 +217,7 @@ impl std::io<|> for Foo { | |||
217 | " | 217 | " |
218 | use std::{io, fmt}; | 218 | use std::{io, fmt}; |
219 | 219 | ||
220 | impl io<|> for Foo { | 220 | impl io for Foo { |
221 | } | 221 | } |
222 | ", | 222 | ", |
223 | ); | 223 | ); |
@@ -236,7 +236,7 @@ impl std::fmt::Debug<|> for Foo { | |||
236 | " | 236 | " |
237 | use std::fmt::{self, Debug, }; | 237 | use std::fmt::{self, Debug, }; |
238 | 238 | ||
239 | impl Debug<|> for Foo { | 239 | impl Debug for Foo { |
240 | } | 240 | } |
241 | ", | 241 | ", |
242 | ); | 242 | ); |
@@ -255,7 +255,7 @@ impl std::fmt<|> for Foo { | |||
255 | " | 255 | " |
256 | use std::fmt::{self, Debug}; | 256 | use std::fmt::{self, Debug}; |
257 | 257 | ||
258 | impl fmt<|> for Foo { | 258 | impl fmt for Foo { |
259 | } | 259 | } |
260 | ", | 260 | ", |
261 | ); | 261 | ); |
@@ -274,7 +274,7 @@ impl std::fmt::nested<|> for Foo { | |||
274 | " | 274 | " |
275 | use std::fmt::{Debug, nested::{Display, self}}; | 275 | use std::fmt::{Debug, nested::{Display, self}}; |
276 | 276 | ||
277 | impl nested<|> for Foo { | 277 | impl nested for Foo { |
278 | } | 278 | } |
279 | ", | 279 | ", |
280 | ); | 280 | ); |
@@ -293,7 +293,7 @@ impl std::fmt::nested<|> for Foo { | |||
293 | " | 293 | " |
294 | use std::fmt::{Debug, nested::{self, Display}}; | 294 | use std::fmt::{Debug, nested::{self, Display}}; |
295 | 295 | ||
296 | impl nested<|> for Foo { | 296 | impl nested for Foo { |
297 | } | 297 | } |
298 | ", | 298 | ", |
299 | ); | 299 | ); |
@@ -312,7 +312,7 @@ impl std::fmt::nested::Debug<|> for Foo { | |||
312 | " | 312 | " |
313 | use std::fmt::{Debug, nested::{Display, Debug}}; | 313 | use std::fmt::{Debug, nested::{Display, Debug}}; |
314 | 314 | ||
315 | impl Debug<|> for Foo { | 315 | impl Debug for Foo { |
316 | } | 316 | } |
317 | ", | 317 | ", |
318 | ); | 318 | ); |
@@ -331,7 +331,7 @@ impl std::fmt::nested::Display<|> for Foo { | |||
331 | " | 331 | " |
332 | use std::fmt::{nested::Display, Debug}; | 332 | use std::fmt::{nested::Display, Debug}; |
333 | 333 | ||
334 | impl Display<|> for Foo { | 334 | impl Display for Foo { |
335 | } | 335 | } |
336 | ", | 336 | ", |
337 | ); | 337 | ); |
@@ -350,7 +350,7 @@ impl std::fmt::Display<|> for Foo { | |||
350 | " | 350 | " |
351 | use std::fmt::{Display, nested::Debug}; | 351 | use std::fmt::{Display, nested::Debug}; |
352 | 352 | ||
353 | impl Display<|> for Foo { | 353 | impl Display for Foo { |
354 | } | 354 | } |
355 | ", | 355 | ", |
356 | ); | 356 | ); |
@@ -374,7 +374,7 @@ use crate::{ | |||
374 | AssocItem, | 374 | AssocItem, |
375 | }; | 375 | }; |
376 | 376 | ||
377 | fn foo() { lower<|>::trait_env() } | 377 | fn foo() { lower::trait_env() } |
378 | ", | 378 | ", |
379 | ); | 379 | ); |
380 | } | 380 | } |
@@ -392,7 +392,7 @@ impl foo::Debug<|> for Foo { | |||
392 | " | 392 | " |
393 | use std::fmt as foo; | 393 | use std::fmt as foo; |
394 | 394 | ||
395 | impl Debug<|> for Foo { | 395 | impl Debug for Foo { |
396 | } | 396 | } |
397 | ", | 397 | ", |
398 | ); | 398 | ); |
@@ -435,7 +435,7 @@ mod foo { | |||
435 | mod bar { | 435 | mod bar { |
436 | use std::fmt::Debug; | 436 | use std::fmt::Debug; |
437 | 437 | ||
438 | Debug<|> | 438 | Debug |
439 | } | 439 | } |
440 | } | 440 | } |
441 | ", | 441 | ", |
@@ -458,7 +458,7 @@ fn main() { | |||
458 | use std::fmt::Debug; | 458 | use std::fmt::Debug; |
459 | 459 | ||
460 | fn main() { | 460 | fn main() { |
461 | Debug<|> | 461 | Debug |
462 | } | 462 | } |
463 | ", | 463 | ", |
464 | ); | 464 | ); |
diff --git a/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs b/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs index c4b56f6e9..cff7dfb81 100644 --- a/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs +++ b/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs | |||
@@ -9,7 +9,10 @@ use ra_syntax::{ | |||
9 | AstNode, | 9 | AstNode, |
10 | }; | 10 | }; |
11 | 11 | ||
12 | use crate::{utils::TryEnum, AssistContext, AssistId, Assists}; | 12 | use crate::{ |
13 | utils::{render_snippet, Cursor, TryEnum}, | ||
14 | AssistContext, AssistId, Assists, | ||
15 | }; | ||
13 | 16 | ||
14 | // Assist: replace_unwrap_with_match | 17 | // Assist: replace_unwrap_with_match |
15 | // | 18 | // |
@@ -29,7 +32,7 @@ use crate::{utils::TryEnum, AssistContext, AssistId, Assists}; | |||
29 | // let x: Result<i32, i32> = Result::Ok(92); | 32 | // let x: Result<i32, i32> = Result::Ok(92); |
30 | // let y = match x { | 33 | // let y = match x { |
31 | // Ok(a) => a, | 34 | // Ok(a) => a, |
32 | // _ => unreachable!(), | 35 | // $0_ => unreachable!(), |
33 | // }; | 36 | // }; |
34 | // } | 37 | // } |
35 | // ``` | 38 | // ``` |
@@ -43,7 +46,7 @@ pub(crate) fn replace_unwrap_with_match(acc: &mut Assists, ctx: &AssistContext) | |||
43 | let ty = ctx.sema.type_of_expr(&caller)?; | 46 | let ty = ctx.sema.type_of_expr(&caller)?; |
44 | let happy_variant = TryEnum::from_ty(&ctx.sema, &ty)?.happy_case(); | 47 | let happy_variant = TryEnum::from_ty(&ctx.sema, &ty)?.happy_case(); |
45 | let target = method_call.syntax().text_range(); | 48 | let target = method_call.syntax().text_range(); |
46 | acc.add(AssistId("replace_unwrap_with_match"), "Replace unwrap with match", target, |edit| { | 49 | acc.add(AssistId("replace_unwrap_with_match"), "Replace unwrap with match", target, |builder| { |
47 | let ok_path = make::path_unqualified(make::path_segment(make::name_ref(happy_variant))); | 50 | let ok_path = make::path_unqualified(make::path_segment(make::name_ref(happy_variant))); |
48 | let it = make::bind_pat(make::name("a")).into(); | 51 | let it = make::bind_pat(make::name("a")).into(); |
49 | let ok_tuple = make::tuple_struct_pat(ok_path, iter::once(it)).into(); | 52 | let ok_tuple = make::tuple_struct_pat(ok_path, iter::once(it)).into(); |
@@ -51,23 +54,37 @@ pub(crate) fn replace_unwrap_with_match(acc: &mut Assists, ctx: &AssistContext) | |||
51 | let bind_path = make::path_unqualified(make::path_segment(make::name_ref("a"))); | 54 | let bind_path = make::path_unqualified(make::path_segment(make::name_ref("a"))); |
52 | let ok_arm = make::match_arm(iter::once(ok_tuple), make::expr_path(bind_path)); | 55 | let ok_arm = make::match_arm(iter::once(ok_tuple), make::expr_path(bind_path)); |
53 | 56 | ||
54 | let unreachable_call = make::unreachable_macro_call().into(); | 57 | let unreachable_call = make::expr_unreachable(); |
55 | let err_arm = make::match_arm(iter::once(make::placeholder_pat().into()), unreachable_call); | 58 | let err_arm = make::match_arm(iter::once(make::placeholder_pat().into()), unreachable_call); |
56 | 59 | ||
57 | let match_arm_list = make::match_arm_list(vec![ok_arm, err_arm]); | 60 | let match_arm_list = make::match_arm_list(vec![ok_arm, err_arm]); |
58 | let match_expr = make::expr_match(caller.clone(), match_arm_list) | 61 | let match_expr = make::expr_match(caller.clone(), match_arm_list) |
59 | .indent(IndentLevel::from_node(method_call.syntax())); | 62 | .indent(IndentLevel::from_node(method_call.syntax())); |
60 | 63 | ||
61 | edit.set_cursor(caller.syntax().text_range().start()); | 64 | let range = method_call.syntax().text_range(); |
62 | edit.replace_ast::<ast::Expr>(method_call.into(), match_expr); | 65 | match ctx.config.snippet_cap { |
66 | Some(cap) => { | ||
67 | let err_arm = match_expr | ||
68 | .syntax() | ||
69 | .descendants() | ||
70 | .filter_map(ast::MatchArm::cast) | ||
71 | .last() | ||
72 | .unwrap(); | ||
73 | let snippet = | ||
74 | render_snippet(cap, match_expr.syntax(), Cursor::Before(err_arm.syntax())); | ||
75 | builder.replace_snippet(cap, range, snippet) | ||
76 | } | ||
77 | None => builder.replace(range, match_expr.to_string()), | ||
78 | } | ||
63 | }) | 79 | }) |
64 | } | 80 | } |
65 | 81 | ||
66 | #[cfg(test)] | 82 | #[cfg(test)] |
67 | mod tests { | 83 | mod tests { |
68 | use super::*; | ||
69 | use crate::tests::{check_assist, check_assist_target}; | 84 | use crate::tests::{check_assist, check_assist_target}; |
70 | 85 | ||
86 | use super::*; | ||
87 | |||
71 | #[test] | 88 | #[test] |
72 | fn test_replace_result_unwrap_with_match() { | 89 | fn test_replace_result_unwrap_with_match() { |
73 | check_assist( | 90 | check_assist( |
@@ -85,9 +102,9 @@ enum Result<T, E> { Ok(T), Err(E) } | |||
85 | fn i<T>(a: T) -> T { a } | 102 | fn i<T>(a: T) -> T { a } |
86 | fn main() { | 103 | fn main() { |
87 | let x: Result<i32, i32> = Result::Ok(92); | 104 | let x: Result<i32, i32> = Result::Ok(92); |
88 | let y = <|>match i(x) { | 105 | let y = match i(x) { |
89 | Ok(a) => a, | 106 | Ok(a) => a, |
90 | _ => unreachable!(), | 107 | $0_ => unreachable!(), |
91 | }; | 108 | }; |
92 | } | 109 | } |
93 | ", | 110 | ", |
@@ -111,9 +128,9 @@ enum Option<T> { Some(T), None } | |||
111 | fn i<T>(a: T) -> T { a } | 128 | fn i<T>(a: T) -> T { a } |
112 | fn main() { | 129 | fn main() { |
113 | let x = Option::Some(92); | 130 | let x = Option::Some(92); |
114 | let y = <|>match i(x) { | 131 | let y = match i(x) { |
115 | Some(a) => a, | 132 | Some(a) => a, |
116 | _ => unreachable!(), | 133 | $0_ => unreachable!(), |
117 | }; | 134 | }; |
118 | } | 135 | } |
119 | ", | 136 | ", |
@@ -137,9 +154,9 @@ enum Result<T, E> { Ok(T), Err(E) } | |||
137 | fn i<T>(a: T) -> T { a } | 154 | fn i<T>(a: T) -> T { a } |
138 | fn main() { | 155 | fn main() { |
139 | let x: Result<i32, i32> = Result::Ok(92); | 156 | let x: Result<i32, i32> = Result::Ok(92); |
140 | let y = <|>match i(x) { | 157 | let y = match i(x) { |
141 | Ok(a) => a, | 158 | Ok(a) => a, |
142 | _ => unreachable!(), | 159 | $0_ => unreachable!(), |
143 | }.count_zeroes(); | 160 | }.count_zeroes(); |
144 | } | 161 | } |
145 | ", | 162 | ", |
diff --git a/crates/ra_assists/src/handlers/split_import.rs b/crates/ra_assists/src/handlers/split_import.rs index b2757e50c..c7a874480 100644 --- a/crates/ra_assists/src/handlers/split_import.rs +++ b/crates/ra_assists/src/handlers/split_import.rs | |||
@@ -26,12 +26,10 @@ pub(crate) fn split_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
26 | if new_tree == use_tree { | 26 | if new_tree == use_tree { |
27 | return None; | 27 | return None; |
28 | } | 28 | } |
29 | let cursor = ctx.offset(); | ||
30 | 29 | ||
31 | let target = colon_colon.text_range(); | 30 | let target = colon_colon.text_range(); |
32 | acc.add(AssistId("split_import"), "Split import", target, |edit| { | 31 | acc.add(AssistId("split_import"), "Split import", target, |edit| { |
33 | edit.replace_ast(use_tree, new_tree); | 32 | edit.replace_ast(use_tree, new_tree); |
34 | edit.set_cursor(cursor); | ||
35 | }) | 33 | }) |
36 | } | 34 | } |
37 | 35 | ||
@@ -46,7 +44,7 @@ mod tests { | |||
46 | check_assist( | 44 | check_assist( |
47 | split_import, | 45 | split_import, |
48 | "use crate::<|>db::RootDatabase;", | 46 | "use crate::<|>db::RootDatabase;", |
49 | "use crate::<|>{db::RootDatabase};", | 47 | "use crate::{db::RootDatabase};", |
50 | ) | 48 | ) |
51 | } | 49 | } |
52 | 50 | ||
@@ -55,7 +53,7 @@ mod tests { | |||
55 | check_assist( | 53 | check_assist( |
56 | split_import, | 54 | split_import, |
57 | "use crate:<|>:db::{RootDatabase, FileSymbol}", | 55 | "use crate:<|>:db::{RootDatabase, FileSymbol}", |
58 | "use crate:<|>:{db::{RootDatabase, FileSymbol}}", | 56 | "use crate::{db::{RootDatabase, FileSymbol}}", |
59 | ) | 57 | ) |
60 | } | 58 | } |
61 | 59 | ||
diff --git a/crates/ra_assists/src/handlers/unwrap_block.rs b/crates/ra_assists/src/handlers/unwrap_block.rs index e52ec557e..8440c7d0f 100644 --- a/crates/ra_assists/src/handlers/unwrap_block.rs +++ b/crates/ra_assists/src/handlers/unwrap_block.rs | |||
@@ -1,8 +1,10 @@ | |||
1 | use crate::{AssistContext, AssistId, Assists}; | ||
2 | |||
3 | use ast::{ElseBranch, Expr, LoopBodyOwner}; | ||
4 | use ra_fmt::unwrap_trivial_block; | 1 | use ra_fmt::unwrap_trivial_block; |
5 | use ra_syntax::{ast, match_ast, AstNode, TextRange, T}; | 2 | use ra_syntax::{ |
3 | ast::{self, ElseBranch, Expr, LoopBodyOwner}, | ||
4 | match_ast, AstNode, TextRange, T, | ||
5 | }; | ||
6 | |||
7 | use crate::{AssistContext, AssistId, Assists}; | ||
6 | 8 | ||
7 | // Assist: unwrap_block | 9 | // Assist: unwrap_block |
8 | // | 10 | // |
@@ -60,7 +62,6 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
60 | let range_to_del_else_if = TextRange::new(ancestor_then_branch.syntax().text_range().end(), l_curly_token.text_range().start()); | 62 | let range_to_del_else_if = TextRange::new(ancestor_then_branch.syntax().text_range().end(), l_curly_token.text_range().start()); |
61 | let range_to_del_rest = TextRange::new(then_branch.syntax().text_range().end(), if_expr.syntax().text_range().end()); | 63 | let range_to_del_rest = TextRange::new(then_branch.syntax().text_range().end(), if_expr.syntax().text_range().end()); |
62 | 64 | ||
63 | edit.set_cursor(ancestor_then_branch.syntax().text_range().end()); | ||
64 | edit.delete(range_to_del_rest); | 65 | edit.delete(range_to_del_rest); |
65 | edit.delete(range_to_del_else_if); | 66 | edit.delete(range_to_del_else_if); |
66 | edit.replace(target, update_expr_string(then_branch.to_string(), &[' ', '{'])); | 67 | edit.replace(target, update_expr_string(then_branch.to_string(), &[' ', '{'])); |
@@ -77,7 +78,6 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
77 | return acc.add(assist_id, assist_label, target, |edit| { | 78 | return acc.add(assist_id, assist_label, target, |edit| { |
78 | let range_to_del = TextRange::new(then_branch.syntax().text_range().end(), l_curly_token.text_range().start()); | 79 | let range_to_del = TextRange::new(then_branch.syntax().text_range().end(), l_curly_token.text_range().start()); |
79 | 80 | ||
80 | edit.set_cursor(then_branch.syntax().text_range().end()); | ||
81 | edit.delete(range_to_del); | 81 | edit.delete(range_to_del); |
82 | edit.replace(target, update_expr_string(else_block.to_string(), &[' ', '{'])); | 82 | edit.replace(target, update_expr_string(else_block.to_string(), &[' ', '{'])); |
83 | }); | 83 | }); |
@@ -95,8 +95,6 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
95 | 95 | ||
96 | let target = expr_to_unwrap.syntax().text_range(); | 96 | let target = expr_to_unwrap.syntax().text_range(); |
97 | acc.add(assist_id, assist_label, target, |edit| { | 97 | acc.add(assist_id, assist_label, target, |edit| { |
98 | edit.set_cursor(expr.syntax().text_range().start()); | ||
99 | |||
100 | edit.replace( | 98 | edit.replace( |
101 | expr.syntax().text_range(), | 99 | expr.syntax().text_range(), |
102 | update_expr_string(expr_to_unwrap.to_string(), &[' ', '{', '\n']), | 100 | update_expr_string(expr_to_unwrap.to_string(), &[' ', '{', '\n']), |
@@ -152,7 +150,7 @@ mod tests { | |||
152 | r#" | 150 | r#" |
153 | fn main() { | 151 | fn main() { |
154 | bar(); | 152 | bar(); |
155 | <|>foo(); | 153 | foo(); |
156 | 154 | ||
157 | //comment | 155 | //comment |
158 | bar(); | 156 | bar(); |
@@ -186,7 +184,7 @@ mod tests { | |||
186 | 184 | ||
187 | //comment | 185 | //comment |
188 | bar(); | 186 | bar(); |
189 | }<|> | 187 | } |
190 | println!("bar"); | 188 | println!("bar"); |
191 | } | 189 | } |
192 | "#, | 190 | "#, |
@@ -220,7 +218,7 @@ mod tests { | |||
220 | 218 | ||
221 | //comment | 219 | //comment |
222 | //bar(); | 220 | //bar(); |
223 | }<|> | 221 | } |
224 | println!("bar"); | 222 | println!("bar"); |
225 | } | 223 | } |
226 | "#, | 224 | "#, |
@@ -256,7 +254,7 @@ mod tests { | |||
256 | //bar(); | 254 | //bar(); |
257 | } else if false { | 255 | } else if false { |
258 | println!("bar"); | 256 | println!("bar"); |
259 | }<|> | 257 | } |
260 | println!("foo"); | 258 | println!("foo"); |
261 | } | 259 | } |
262 | "#, | 260 | "#, |
@@ -296,7 +294,7 @@ mod tests { | |||
296 | println!("bar"); | 294 | println!("bar"); |
297 | } else if true { | 295 | } else if true { |
298 | println!("foo"); | 296 | println!("foo"); |
299 | }<|> | 297 | } |
300 | println!("else"); | 298 | println!("else"); |
301 | } | 299 | } |
302 | "#, | 300 | "#, |
@@ -334,7 +332,7 @@ mod tests { | |||
334 | //bar(); | 332 | //bar(); |
335 | } else if false { | 333 | } else if false { |
336 | println!("bar"); | 334 | println!("bar"); |
337 | }<|> | 335 | } |
338 | println!("foo"); | 336 | println!("foo"); |
339 | } | 337 | } |
340 | "#, | 338 | "#, |
@@ -381,7 +379,7 @@ mod tests { | |||
381 | "#, | 379 | "#, |
382 | r#" | 380 | r#" |
383 | fn main() { | 381 | fn main() { |
384 | <|>if true { | 382 | if true { |
385 | foo(); | 383 | foo(); |
386 | 384 | ||
387 | //comment | 385 | //comment |
@@ -415,7 +413,7 @@ mod tests { | |||
415 | r#" | 413 | r#" |
416 | fn main() { | 414 | fn main() { |
417 | for i in 0..5 { | 415 | for i in 0..5 { |
418 | <|>foo(); | 416 | foo(); |
419 | 417 | ||
420 | //comment | 418 | //comment |
421 | bar(); | 419 | bar(); |
@@ -445,7 +443,7 @@ mod tests { | |||
445 | "#, | 443 | "#, |
446 | r#" | 444 | r#" |
447 | fn main() { | 445 | fn main() { |
448 | <|>if true { | 446 | if true { |
449 | foo(); | 447 | foo(); |
450 | 448 | ||
451 | //comment | 449 | //comment |
@@ -478,7 +476,7 @@ mod tests { | |||
478 | "#, | 476 | "#, |
479 | r#" | 477 | r#" |
480 | fn main() { | 478 | fn main() { |
481 | <|>if true { | 479 | if true { |
482 | foo(); | 480 | foo(); |
483 | 481 | ||
484 | //comment | 482 | //comment |
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index b6dc7cb1b..464bc03dd 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs | |||
@@ -10,8 +10,8 @@ macro_rules! eprintln { | |||
10 | ($($tt:tt)*) => { stdx::eprintln!($($tt)*) }; | 10 | ($($tt:tt)*) => { stdx::eprintln!($($tt)*) }; |
11 | } | 11 | } |
12 | 12 | ||
13 | mod assist_config; | ||
13 | mod assist_context; | 14 | mod assist_context; |
14 | mod marks; | ||
15 | #[cfg(test)] | 15 | #[cfg(test)] |
16 | mod tests; | 16 | mod tests; |
17 | pub mod utils; | 17 | pub mod utils; |
@@ -24,6 +24,8 @@ use ra_syntax::TextRange; | |||
24 | 24 | ||
25 | pub(crate) use crate::assist_context::{AssistContext, Assists}; | 25 | pub(crate) use crate::assist_context::{AssistContext, Assists}; |
26 | 26 | ||
27 | pub use assist_config::AssistConfig; | ||
28 | |||
27 | /// Unique identifier of the assist, should not be shown to the user | 29 | /// Unique identifier of the assist, should not be shown to the user |
28 | /// directly. | 30 | /// directly. |
29 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | 31 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
@@ -54,9 +56,9 @@ impl Assist { | |||
54 | /// | 56 | /// |
55 | /// Assists are returned in the "unresolved" state, that is only labels are | 57 | /// Assists are returned in the "unresolved" state, that is only labels are |
56 | /// returned, without actual edits. | 58 | /// returned, without actual edits. |
57 | pub fn unresolved(db: &RootDatabase, range: FileRange) -> Vec<Assist> { | 59 | pub fn unresolved(db: &RootDatabase, config: &AssistConfig, range: FileRange) -> Vec<Assist> { |
58 | let sema = Semantics::new(db); | 60 | let sema = Semantics::new(db); |
59 | let ctx = AssistContext::new(sema, range); | 61 | let ctx = AssistContext::new(sema, config, range); |
60 | let mut acc = Assists::new_unresolved(&ctx); | 62 | let mut acc = Assists::new_unresolved(&ctx); |
61 | handlers::all().iter().for_each(|handler| { | 63 | handlers::all().iter().for_each(|handler| { |
62 | handler(&mut acc, &ctx); | 64 | handler(&mut acc, &ctx); |
@@ -68,9 +70,13 @@ impl Assist { | |||
68 | /// | 70 | /// |
69 | /// Assists are returned in the "resolved" state, that is with edit fully | 71 | /// Assists are returned in the "resolved" state, that is with edit fully |
70 | /// computed. | 72 | /// computed. |
71 | pub fn resolved(db: &RootDatabase, range: FileRange) -> Vec<ResolvedAssist> { | 73 | pub fn resolved( |
74 | db: &RootDatabase, | ||
75 | config: &AssistConfig, | ||
76 | range: FileRange, | ||
77 | ) -> Vec<ResolvedAssist> { | ||
72 | let sema = Semantics::new(db); | 78 | let sema = Semantics::new(db); |
73 | let ctx = AssistContext::new(sema, range); | 79 | let ctx = AssistContext::new(sema, config, range); |
74 | let mut acc = Assists::new_resolved(&ctx); | 80 | let mut acc = Assists::new_resolved(&ctx); |
75 | handlers::all().iter().for_each(|handler| { | 81 | handlers::all().iter().for_each(|handler| { |
76 | handler(&mut acc, &ctx); | 82 | handler(&mut acc, &ctx); |
@@ -103,12 +109,14 @@ mod handlers { | |||
103 | mod add_impl; | 109 | mod add_impl; |
104 | mod add_missing_impl_members; | 110 | mod add_missing_impl_members; |
105 | mod add_new; | 111 | mod add_new; |
112 | mod add_turbo_fish; | ||
106 | mod apply_demorgan; | 113 | mod apply_demorgan; |
107 | mod auto_import; | 114 | mod auto_import; |
108 | mod change_return_type_to_result; | 115 | mod change_return_type_to_result; |
109 | mod change_visibility; | 116 | mod change_visibility; |
110 | mod early_return; | 117 | mod early_return; |
111 | mod fill_match_arms; | 118 | mod fill_match_arms; |
119 | mod fix_visibility; | ||
112 | mod flip_binexpr; | 120 | mod flip_binexpr; |
113 | mod flip_comma; | 121 | mod flip_comma; |
114 | mod flip_trait_bound; | 122 | mod flip_trait_bound; |
@@ -140,12 +148,14 @@ mod handlers { | |||
140 | add_function::add_function, | 148 | add_function::add_function, |
141 | add_impl::add_impl, | 149 | add_impl::add_impl, |
142 | add_new::add_new, | 150 | add_new::add_new, |
151 | add_turbo_fish::add_turbo_fish, | ||
143 | apply_demorgan::apply_demorgan, | 152 | apply_demorgan::apply_demorgan, |
144 | auto_import::auto_import, | 153 | auto_import::auto_import, |
145 | change_return_type_to_result::change_return_type_to_result, | 154 | change_return_type_to_result::change_return_type_to_result, |
146 | change_visibility::change_visibility, | 155 | change_visibility::change_visibility, |
147 | early_return::convert_to_guarded_return, | 156 | early_return::convert_to_guarded_return, |
148 | fill_match_arms::fill_match_arms, | 157 | fill_match_arms::fill_match_arms, |
158 | fix_visibility::fix_visibility, | ||
149 | flip_binexpr::flip_binexpr, | 159 | flip_binexpr::flip_binexpr, |
150 | flip_comma::flip_comma, | 160 | flip_comma::flip_comma, |
151 | flip_trait_bound::flip_trait_bound, | 161 | flip_trait_bound::flip_trait_bound, |
diff --git a/crates/ra_assists/src/marks.rs b/crates/ra_assists/src/marks.rs deleted file mode 100644 index 8d910205f..000000000 --- a/crates/ra_assists/src/marks.rs +++ /dev/null | |||
@@ -1,12 +0,0 @@ | |||
1 | //! See test_utils/src/marks.rs | ||
2 | |||
3 | test_utils::marks![ | ||
4 | introduce_var_in_comment_is_not_applicable | ||
5 | test_introduce_var_expr_stmt | ||
6 | test_introduce_var_last_expr | ||
7 | not_applicable_outside_of_bind_pat | ||
8 | test_not_inline_mut_variable | ||
9 | test_not_applicable_if_variable_unused | ||
10 | change_visibility_field_false_positive | ||
11 | test_add_from_impl_already_exists | ||
12 | ]; | ||
diff --git a/crates/ra_assists/src/tests.rs b/crates/ra_assists/src/tests.rs index a3eacb8f1..373a7f7cc 100644 --- a/crates/ra_assists/src/tests.rs +++ b/crates/ra_assists/src/tests.rs | |||
@@ -11,7 +11,7 @@ use test_utils::{ | |||
11 | RangeOrOffset, | 11 | RangeOrOffset, |
12 | }; | 12 | }; |
13 | 13 | ||
14 | use crate::{handlers::Handler, Assist, AssistContext, Assists}; | 14 | use crate::{handlers::Handler, Assist, AssistConfig, AssistContext, Assists}; |
15 | 15 | ||
16 | pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) { | 16 | pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) { |
17 | let (mut db, file_id) = RootDatabase::with_single_file(text); | 17 | let (mut db, file_id) = RootDatabase::with_single_file(text); |
@@ -41,14 +41,14 @@ fn check_doc_test(assist_id: &str, before: &str, after: &str) { | |||
41 | let (db, file_id) = crate::tests::with_single_file(&before); | 41 | let (db, file_id) = crate::tests::with_single_file(&before); |
42 | let frange = FileRange { file_id, range: selection.into() }; | 42 | let frange = FileRange { file_id, range: selection.into() }; |
43 | 43 | ||
44 | let mut assist = Assist::resolved(&db, frange) | 44 | let mut assist = Assist::resolved(&db, &AssistConfig::default(), frange) |
45 | .into_iter() | 45 | .into_iter() |
46 | .find(|assist| assist.assist.id.0 == assist_id) | 46 | .find(|assist| assist.assist.id.0 == assist_id) |
47 | .unwrap_or_else(|| { | 47 | .unwrap_or_else(|| { |
48 | panic!( | 48 | panic!( |
49 | "\n\nAssist is not applicable: {}\nAvailable assists: {}", | 49 | "\n\nAssist is not applicable: {}\nAvailable assists: {}", |
50 | assist_id, | 50 | assist_id, |
51 | Assist::resolved(&db, frange) | 51 | Assist::resolved(&db, &AssistConfig::default(), frange) |
52 | .into_iter() | 52 | .into_iter() |
53 | .map(|assist| assist.assist.id.0) | 53 | .map(|assist| assist.assist.id.0) |
54 | .collect::<Vec<_>>() | 54 | .collect::<Vec<_>>() |
@@ -90,7 +90,8 @@ fn check(handler: Handler, before: &str, expected: ExpectedResult) { | |||
90 | let frange = FileRange { file_id: file_with_caret_id, range: range_or_offset.into() }; | 90 | let frange = FileRange { file_id: file_with_caret_id, range: range_or_offset.into() }; |
91 | 91 | ||
92 | let sema = Semantics::new(&db); | 92 | let sema = Semantics::new(&db); |
93 | let ctx = AssistContext::new(sema, frange); | 93 | let config = AssistConfig::default(); |
94 | let ctx = AssistContext::new(sema, &config, frange); | ||
94 | let mut acc = Assists::new_resolved(&ctx); | 95 | let mut acc = Assists::new_resolved(&ctx); |
95 | handler(&mut acc, &ctx); | 96 | handler(&mut acc, &ctx); |
96 | let mut res = acc.finish_resolved(); | 97 | let mut res = acc.finish_resolved(); |
@@ -103,19 +104,11 @@ fn check(handler: Handler, before: &str, expected: ExpectedResult) { | |||
103 | let mut actual = db.file_text(change.file_id).as_ref().to_owned(); | 104 | let mut actual = db.file_text(change.file_id).as_ref().to_owned(); |
104 | change.edit.apply(&mut actual); | 105 | change.edit.apply(&mut actual); |
105 | 106 | ||
106 | match source_change.cursor_position { | 107 | if !source_change.is_snippet { |
107 | None => { | 108 | if let Some(off) = source_change.cursor_position { |
108 | if let RangeOrOffset::Offset(before_cursor_pos) = range_or_offset { | 109 | actual = add_cursor(&actual, off.offset) |
109 | let off = change | ||
110 | .edit | ||
111 | .apply_to_offset(before_cursor_pos) | ||
112 | .expect("cursor position is affected by the edit"); | ||
113 | actual = add_cursor(&actual, off) | ||
114 | } | ||
115 | } | 110 | } |
116 | Some(off) => actual = add_cursor(&actual, off.offset), | 111 | } |
117 | }; | ||
118 | |||
119 | assert_eq_text!(after, &actual); | 112 | assert_eq_text!(after, &actual); |
120 | } | 113 | } |
121 | (Some(assist), ExpectedResult::Target(target)) => { | 114 | (Some(assist), ExpectedResult::Target(target)) => { |
@@ -136,7 +129,7 @@ fn assist_order_field_struct() { | |||
136 | let (before_cursor_pos, before) = extract_offset(before); | 129 | let (before_cursor_pos, before) = extract_offset(before); |
137 | let (db, file_id) = with_single_file(&before); | 130 | let (db, file_id) = with_single_file(&before); |
138 | let frange = FileRange { file_id, range: TextRange::empty(before_cursor_pos) }; | 131 | let frange = FileRange { file_id, range: TextRange::empty(before_cursor_pos) }; |
139 | let assists = Assist::resolved(&db, frange); | 132 | let assists = Assist::resolved(&db, &AssistConfig::default(), frange); |
140 | let mut assists = assists.iter(); | 133 | let mut assists = assists.iter(); |
141 | 134 | ||
142 | assert_eq!( | 135 | assert_eq!( |
@@ -159,7 +152,7 @@ fn assist_order_if_expr() { | |||
159 | let (range, before) = extract_range(before); | 152 | let (range, before) = extract_range(before); |
160 | let (db, file_id) = with_single_file(&before); | 153 | let (db, file_id) = with_single_file(&before); |
161 | let frange = FileRange { file_id, range }; | 154 | let frange = FileRange { file_id, range }; |
162 | let assists = Assist::resolved(&db, frange); | 155 | let assists = Assist::resolved(&db, &AssistConfig::default(), frange); |
163 | let mut assists = assists.iter(); | 156 | let mut assists = assists.iter(); |
164 | 157 | ||
165 | assert_eq!(assists.next().expect("expected assist").assist.label, "Extract into variable"); | 158 | assert_eq!(assists.next().expect("expected assist").assist.label, "Extract into variable"); |
diff --git a/crates/ra_assists/src/tests/generated.rs b/crates/ra_assists/src/tests/generated.rs index 972dbd251..250e56a69 100644 --- a/crates/ra_assists/src/tests/generated.rs +++ b/crates/ra_assists/src/tests/generated.rs | |||
@@ -15,7 +15,7 @@ struct S; | |||
15 | struct S; | 15 | struct S; |
16 | 16 | ||
17 | impl Debug for S { | 17 | impl Debug for S { |
18 | 18 | $0 | |
19 | } | 19 | } |
20 | "#####, | 20 | "#####, |
21 | ) | 21 | ) |
@@ -32,7 +32,7 @@ struct Point { | |||
32 | } | 32 | } |
33 | "#####, | 33 | "#####, |
34 | r#####" | 34 | r#####" |
35 | #[derive()] | 35 | #[derive($0)] |
36 | struct Point { | 36 | struct Point { |
37 | x: u32, | 37 | x: u32, |
38 | y: u32, | 38 | y: u32, |
@@ -78,7 +78,7 @@ fn foo() { | |||
78 | } | 78 | } |
79 | 79 | ||
80 | fn bar(arg: &str, baz: Baz) { | 80 | fn bar(arg: &str, baz: Baz) { |
81 | todo!() | 81 | ${0:todo!()} |
82 | } | 82 | } |
83 | 83 | ||
84 | "#####, | 84 | "#####, |
@@ -108,16 +108,16 @@ fn doctest_add_impl() { | |||
108 | "add_impl", | 108 | "add_impl", |
109 | r#####" | 109 | r#####" |
110 | struct Ctx<T: Clone> { | 110 | struct Ctx<T: Clone> { |
111 | data: T,<|> | 111 | data: T,<|> |
112 | } | 112 | } |
113 | "#####, | 113 | "#####, |
114 | r#####" | 114 | r#####" |
115 | struct Ctx<T: Clone> { | 115 | struct Ctx<T: Clone> { |
116 | data: T, | 116 | data: T, |
117 | } | 117 | } |
118 | 118 | ||
119 | impl<T: Clone> Ctx<T> { | 119 | impl<T: Clone> Ctx<T> { |
120 | 120 | $0 | |
121 | } | 121 | } |
122 | "#####, | 122 | "#####, |
123 | ) | 123 | ) |
@@ -150,7 +150,7 @@ trait Trait { | |||
150 | impl Trait for () { | 150 | impl Trait for () { |
151 | Type X = (); | 151 | Type X = (); |
152 | fn foo(&self) {} | 152 | fn foo(&self) {} |
153 | fn bar(&self) {} | 153 | $0fn bar(&self) {} |
154 | 154 | ||
155 | } | 155 | } |
156 | "#####, | 156 | "#####, |
@@ -181,7 +181,7 @@ trait Trait<T> { | |||
181 | 181 | ||
182 | impl Trait<u32> for () { | 182 | impl Trait<u32> for () { |
183 | fn foo(&self) -> u32 { | 183 | fn foo(&self) -> u32 { |
184 | todo!() | 184 | ${0:todo!()} |
185 | } | 185 | } |
186 | 186 | ||
187 | } | 187 | } |
@@ -204,7 +204,7 @@ struct Ctx<T: Clone> { | |||
204 | } | 204 | } |
205 | 205 | ||
206 | impl<T: Clone> Ctx<T> { | 206 | impl<T: Clone> Ctx<T> { |
207 | fn new(data: T) -> Self { Self { data } } | 207 | fn $0new(data: T) -> Self { Self { data } } |
208 | } | 208 | } |
209 | 209 | ||
210 | "#####, | 210 | "#####, |
@@ -212,6 +212,25 @@ impl<T: Clone> Ctx<T> { | |||
212 | } | 212 | } |
213 | 213 | ||
214 | #[test] | 214 | #[test] |
215 | fn doctest_add_turbo_fish() { | ||
216 | check_doc_test( | ||
217 | "add_turbo_fish", | ||
218 | r#####" | ||
219 | fn make<T>() -> T { todo!() } | ||
220 | fn main() { | ||
221 | let x = make<|>(); | ||
222 | } | ||
223 | "#####, | ||
224 | r#####" | ||
225 | fn make<T>() -> T { todo!() } | ||
226 | fn main() { | ||
227 | let x = make::<${0:_}>(); | ||
228 | } | ||
229 | "#####, | ||
230 | ) | ||
231 | } | ||
232 | |||
233 | #[test] | ||
215 | fn doctest_apply_demorgan() { | 234 | fn doctest_apply_demorgan() { |
216 | check_doc_test( | 235 | check_doc_test( |
217 | "apply_demorgan", | 236 | "apply_demorgan", |
@@ -257,7 +276,7 @@ fn doctest_change_return_type_to_result() { | |||
257 | fn foo() -> i32<|> { 42i32 } | 276 | fn foo() -> i32<|> { 42i32 } |
258 | "#####, | 277 | "#####, |
259 | r#####" | 278 | r#####" |
260 | fn foo() -> Result<i32, > { Ok(42i32) } | 279 | fn foo() -> Result<i32, ${0:_}> { Ok(42i32) } |
261 | "#####, | 280 | "#####, |
262 | ) | 281 | ) |
263 | } | 282 | } |
@@ -317,7 +336,7 @@ enum Action { Move { distance: u32 }, Stop } | |||
317 | 336 | ||
318 | fn handle(action: Action) { | 337 | fn handle(action: Action) { |
319 | match action { | 338 | match action { |
320 | Action::Move { distance } => {} | 339 | $0Action::Move { distance } => {} |
321 | Action::Stop => {} | 340 | Action::Stop => {} |
322 | } | 341 | } |
323 | } | 342 | } |
@@ -326,6 +345,29 @@ fn handle(action: Action) { | |||
326 | } | 345 | } |
327 | 346 | ||
328 | #[test] | 347 | #[test] |
348 | fn doctest_fix_visibility() { | ||
349 | check_doc_test( | ||
350 | "fix_visibility", | ||
351 | r#####" | ||
352 | mod m { | ||
353 | fn frobnicate() {} | ||
354 | } | ||
355 | fn main() { | ||
356 | m::frobnicate<|>() {} | ||
357 | } | ||
358 | "#####, | ||
359 | r#####" | ||
360 | mod m { | ||
361 | $0pub(crate) fn frobnicate() {} | ||
362 | } | ||
363 | fn main() { | ||
364 | m::frobnicate() {} | ||
365 | } | ||
366 | "#####, | ||
367 | ) | ||
368 | } | ||
369 | |||
370 | #[test] | ||
329 | fn doctest_flip_binexpr() { | 371 | fn doctest_flip_binexpr() { |
330 | check_doc_test( | 372 | check_doc_test( |
331 | "flip_binexpr", | 373 | "flip_binexpr", |
@@ -401,7 +443,7 @@ fn main() { | |||
401 | "#####, | 443 | "#####, |
402 | r#####" | 444 | r#####" |
403 | fn main() { | 445 | fn main() { |
404 | let var_name = (1 + 2); | 446 | let $0var_name = (1 + 2); |
405 | var_name * 4; | 447 | var_name * 4; |
406 | } | 448 | } |
407 | "#####, | 449 | "#####, |
@@ -722,7 +764,7 @@ fn main() { | |||
722 | let x: Result<i32, i32> = Result::Ok(92); | 764 | let x: Result<i32, i32> = Result::Ok(92); |
723 | let y = match x { | 765 | let y = match x { |
724 | Ok(a) => a, | 766 | Ok(a) => a, |
725 | _ => unreachable!(), | 767 | $0_ => unreachable!(), |
726 | }; | 768 | }; |
727 | } | 769 | } |
728 | "#####, | 770 | "#####, |
diff --git a/crates/ra_assists/src/utils.rs b/crates/ra_assists/src/utils.rs index f3fc92ebf..0038a9764 100644 --- a/crates/ra_assists/src/utils.rs +++ b/crates/ra_assists/src/utils.rs | |||
@@ -1,18 +1,57 @@ | |||
1 | //! Assorted functions shared by several assists. | 1 | //! Assorted functions shared by several assists. |
2 | pub(crate) mod insert_use; | 2 | pub(crate) mod insert_use; |
3 | 3 | ||
4 | use std::iter; | 4 | use std::{iter, ops}; |
5 | 5 | ||
6 | use hir::{Adt, Crate, Semantics, Trait, Type}; | 6 | use hir::{Adt, Crate, Enum, ScopeDef, Semantics, Trait, Type}; |
7 | use ra_ide_db::RootDatabase; | 7 | use ra_ide_db::RootDatabase; |
8 | use ra_syntax::{ | 8 | use ra_syntax::{ |
9 | ast::{self, make, NameOwner}, | 9 | ast::{self, make, NameOwner}, |
10 | AstNode, T, | 10 | AstNode, SyntaxNode, T, |
11 | }; | 11 | }; |
12 | use rustc_hash::FxHashSet; | 12 | use rustc_hash::FxHashSet; |
13 | 13 | ||
14 | use crate::assist_config::SnippetCap; | ||
15 | |||
14 | pub(crate) use insert_use::insert_use_statement; | 16 | pub(crate) use insert_use::insert_use_statement; |
15 | 17 | ||
18 | #[derive(Clone, Copy, Debug)] | ||
19 | pub(crate) enum Cursor<'a> { | ||
20 | Replace(&'a SyntaxNode), | ||
21 | Before(&'a SyntaxNode), | ||
22 | } | ||
23 | |||
24 | impl<'a> Cursor<'a> { | ||
25 | fn node(self) -> &'a SyntaxNode { | ||
26 | match self { | ||
27 | Cursor::Replace(node) | Cursor::Before(node) => node, | ||
28 | } | ||
29 | } | ||
30 | } | ||
31 | |||
32 | pub(crate) fn render_snippet(_cap: SnippetCap, node: &SyntaxNode, cursor: Cursor) -> String { | ||
33 | assert!(cursor.node().ancestors().any(|it| it == *node)); | ||
34 | let range = cursor.node().text_range() - node.text_range().start(); | ||
35 | let range: ops::Range<usize> = range.into(); | ||
36 | |||
37 | let mut placeholder = cursor.node().to_string(); | ||
38 | escape(&mut placeholder); | ||
39 | let tab_stop = match cursor { | ||
40 | Cursor::Replace(placeholder) => format!("${{0:{}}}", placeholder), | ||
41 | Cursor::Before(placeholder) => format!("$0{}", placeholder), | ||
42 | }; | ||
43 | |||
44 | let mut buf = node.to_string(); | ||
45 | buf.replace_range(range, &tab_stop); | ||
46 | return buf; | ||
47 | |||
48 | fn escape(buf: &mut String) { | ||
49 | stdx::replace(buf, '{', r"\{"); | ||
50 | stdx::replace(buf, '}', r"\}"); | ||
51 | stdx::replace(buf, '$', r"\$"); | ||
52 | } | ||
53 | } | ||
54 | |||
16 | pub fn get_missing_assoc_items( | 55 | pub fn get_missing_assoc_items( |
17 | sema: &Semantics<RootDatabase>, | 56 | sema: &Semantics<RootDatabase>, |
18 | impl_def: &ast::ImplDef, | 57 | impl_def: &ast::ImplDef, |
@@ -161,13 +200,19 @@ impl FamousDefs<'_, '_> { | |||
161 | #[cfg(test)] | 200 | #[cfg(test)] |
162 | pub(crate) const FIXTURE: &'static str = r#" | 201 | pub(crate) const FIXTURE: &'static str = r#" |
163 | //- /libcore.rs crate:core | 202 | //- /libcore.rs crate:core |
164 | pub mod convert{ | 203 | pub mod convert { |
165 | pub trait From<T> { | 204 | pub trait From<T> { |
166 | fn from(T) -> Self; | 205 | fn from(T) -> Self; |
167 | } | 206 | } |
168 | } | 207 | } |
169 | 208 | ||
170 | pub mod prelude { pub use crate::convert::From } | 209 | pub mod option { |
210 | pub enum Option<T> { None, Some(T)} | ||
211 | } | ||
212 | |||
213 | pub mod prelude { | ||
214 | pub use crate::{convert::From, option::Option::{self, *}}; | ||
215 | } | ||
171 | #[prelude_import] | 216 | #[prelude_import] |
172 | pub use prelude::*; | 217 | pub use prelude::*; |
173 | "#; | 218 | "#; |
@@ -176,7 +221,25 @@ pub use prelude::*; | |||
176 | self.find_trait("core:convert:From") | 221 | self.find_trait("core:convert:From") |
177 | } | 222 | } |
178 | 223 | ||
224 | pub(crate) fn core_option_Option(&self) -> Option<Enum> { | ||
225 | self.find_enum("core:option:Option") | ||
226 | } | ||
227 | |||
179 | fn find_trait(&self, path: &str) -> Option<Trait> { | 228 | fn find_trait(&self, path: &str) -> Option<Trait> { |
229 | match self.find_def(path)? { | ||
230 | hir::ScopeDef::ModuleDef(hir::ModuleDef::Trait(it)) => Some(it), | ||
231 | _ => None, | ||
232 | } | ||
233 | } | ||
234 | |||
235 | fn find_enum(&self, path: &str) -> Option<Enum> { | ||
236 | match self.find_def(path)? { | ||
237 | hir::ScopeDef::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Enum(it))) => Some(it), | ||
238 | _ => None, | ||
239 | } | ||
240 | } | ||
241 | |||
242 | fn find_def(&self, path: &str) -> Option<ScopeDef> { | ||
180 | let db = self.0.db; | 243 | let db = self.0.db; |
181 | let mut path = path.split(':'); | 244 | let mut path = path.split(':'); |
182 | let trait_ = path.next_back()?; | 245 | let trait_ = path.next_back()?; |
@@ -201,9 +264,6 @@ pub use prelude::*; | |||
201 | } | 264 | } |
202 | let def = | 265 | let def = |
203 | module.scope(db, None).into_iter().find(|(name, _def)| &name.to_string() == trait_)?.1; | 266 | module.scope(db, None).into_iter().find(|(name, _def)| &name.to_string() == trait_)?.1; |
204 | match def { | 267 | Some(def) |
205 | hir::ScopeDef::ModuleDef(hir::ModuleDef::Trait(it)) => Some(it), | ||
206 | _ => None, | ||
207 | } | ||
208 | } | 268 | } |
209 | } | 269 | } |
diff --git a/crates/ra_assists/src/utils/insert_use.rs b/crates/ra_assists/src/utils/insert_use.rs index 1214e3cd4..0ee43482f 100644 --- a/crates/ra_assists/src/utils/insert_use.rs +++ b/crates/ra_assists/src/utils/insert_use.rs | |||
@@ -11,7 +11,7 @@ use ra_syntax::{ | |||
11 | }; | 11 | }; |
12 | use ra_text_edit::TextEditBuilder; | 12 | use ra_text_edit::TextEditBuilder; |
13 | 13 | ||
14 | use crate::assist_context::{AssistBuilder, AssistContext}; | 14 | use crate::assist_context::AssistContext; |
15 | 15 | ||
16 | /// Creates and inserts a use statement for the given path to import. | 16 | /// Creates and inserts a use statement for the given path to import. |
17 | /// The use statement is inserted in the scope most appropriate to the | 17 | /// The use statement is inserted in the scope most appropriate to the |
@@ -21,7 +21,7 @@ pub(crate) fn insert_use_statement( | |||
21 | position: &SyntaxNode, | 21 | position: &SyntaxNode, |
22 | path_to_import: &ModPath, | 22 | path_to_import: &ModPath, |
23 | ctx: &AssistContext, | 23 | ctx: &AssistContext, |
24 | builder: &mut AssistBuilder, | 24 | builder: &mut TextEditBuilder, |
25 | ) { | 25 | ) { |
26 | let target = path_to_import.to_string().split("::").map(SmolStr::new).collect::<Vec<_>>(); | 26 | let target = path_to_import.to_string().split("::").map(SmolStr::new).collect::<Vec<_>>(); |
27 | let container = ctx.sema.ancestors_with_macros(position.clone()).find_map(|n| { | 27 | let container = ctx.sema.ancestors_with_macros(position.clone()).find_map(|n| { |
@@ -33,7 +33,7 @@ pub(crate) fn insert_use_statement( | |||
33 | 33 | ||
34 | if let Some(container) = container { | 34 | if let Some(container) = container { |
35 | let action = best_action_for_target(container, position.clone(), &target); | 35 | let action = best_action_for_target(container, position.clone(), &target); |
36 | make_assist(&action, &target, builder.text_edit_builder()); | 36 | make_assist(&action, &target, builder); |
37 | } | 37 | } |
38 | } | 38 | } |
39 | 39 | ||
diff --git a/crates/ra_hir_def/src/body/lower.rs b/crates/ra_hir_def/src/body/lower.rs index 443b057ab..e08d62dd6 100644 --- a/crates/ra_hir_def/src/body/lower.rs +++ b/crates/ra_hir_def/src/body/lower.rs | |||
@@ -15,7 +15,7 @@ use ra_syntax::{ | |||
15 | }, | 15 | }, |
16 | AstNode, AstPtr, | 16 | AstNode, AstPtr, |
17 | }; | 17 | }; |
18 | use test_utils::tested_by; | 18 | use test_utils::mark; |
19 | 19 | ||
20 | use crate::{ | 20 | use crate::{ |
21 | adt::StructKind, | 21 | adt::StructKind, |
@@ -60,13 +60,10 @@ pub(super) fn lower( | |||
60 | params: Option<ast::ParamList>, | 60 | params: Option<ast::ParamList>, |
61 | body: Option<ast::Expr>, | 61 | body: Option<ast::Expr>, |
62 | ) -> (Body, BodySourceMap) { | 62 | ) -> (Body, BodySourceMap) { |
63 | let ctx = LowerCtx::new(db, expander.current_file_id.clone()); | ||
64 | |||
65 | ExprCollector { | 63 | ExprCollector { |
66 | db, | 64 | db, |
67 | def, | 65 | def, |
68 | expander, | 66 | expander, |
69 | ctx, | ||
70 | source_map: BodySourceMap::default(), | 67 | source_map: BodySourceMap::default(), |
71 | body: Body { | 68 | body: Body { |
72 | exprs: Arena::default(), | 69 | exprs: Arena::default(), |
@@ -83,7 +80,6 @@ struct ExprCollector<'a> { | |||
83 | db: &'a dyn DefDatabase, | 80 | db: &'a dyn DefDatabase, |
84 | def: DefWithBodyId, | 81 | def: DefWithBodyId, |
85 | expander: Expander, | 82 | expander: Expander, |
86 | ctx: LowerCtx, | ||
87 | body: Body, | 83 | body: Body, |
88 | source_map: BodySourceMap, | 84 | source_map: BodySourceMap, |
89 | } | 85 | } |
@@ -122,6 +118,10 @@ impl ExprCollector<'_> { | |||
122 | (self.body, self.source_map) | 118 | (self.body, self.source_map) |
123 | } | 119 | } |
124 | 120 | ||
121 | fn ctx(&self) -> LowerCtx { | ||
122 | LowerCtx::new(self.db, self.expander.current_file_id) | ||
123 | } | ||
124 | |||
125 | fn alloc_expr(&mut self, expr: Expr, ptr: AstPtr<ast::Expr>) -> ExprId { | 125 | fn alloc_expr(&mut self, expr: Expr, ptr: AstPtr<ast::Expr>) -> ExprId { |
126 | let src = self.expander.to_source(ptr); | 126 | let src = self.expander.to_source(ptr); |
127 | let id = self.make_expr(expr, Ok(src.clone())); | 127 | let id = self.make_expr(expr, Ok(src.clone())); |
@@ -226,7 +226,7 @@ impl ExprCollector<'_> { | |||
226 | None => self.collect_expr_opt(condition.expr()), | 226 | None => self.collect_expr_opt(condition.expr()), |
227 | // if let -- desugar to match | 227 | // if let -- desugar to match |
228 | Some(pat) => { | 228 | Some(pat) => { |
229 | tested_by!(infer_resolve_while_let); | 229 | mark::hit!(infer_resolve_while_let); |
230 | let pat = self.collect_pat(pat); | 230 | let pat = self.collect_pat(pat); |
231 | let match_expr = self.collect_expr_opt(condition.expr()); | 231 | let match_expr = self.collect_expr_opt(condition.expr()); |
232 | let placeholder_pat = self.missing_pat(); | 232 | let placeholder_pat = self.missing_pat(); |
@@ -268,7 +268,7 @@ impl ExprCollector<'_> { | |||
268 | }; | 268 | }; |
269 | let method_name = e.name_ref().map(|nr| nr.as_name()).unwrap_or_else(Name::missing); | 269 | let method_name = e.name_ref().map(|nr| nr.as_name()).unwrap_or_else(Name::missing); |
270 | let generic_args = | 270 | let generic_args = |
271 | e.type_arg_list().and_then(|it| GenericArgs::from_ast(&self.ctx, it)); | 271 | e.type_arg_list().and_then(|it| GenericArgs::from_ast(&self.ctx(), it)); |
272 | self.alloc_expr( | 272 | self.alloc_expr( |
273 | Expr::MethodCall { receiver, method_name, args, generic_args }, | 273 | Expr::MethodCall { receiver, method_name, args, generic_args }, |
274 | syntax_ptr, | 274 | syntax_ptr, |
@@ -373,7 +373,7 @@ impl ExprCollector<'_> { | |||
373 | } | 373 | } |
374 | ast::Expr::CastExpr(e) => { | 374 | ast::Expr::CastExpr(e) => { |
375 | let expr = self.collect_expr_opt(e.expr()); | 375 | let expr = self.collect_expr_opt(e.expr()); |
376 | let type_ref = TypeRef::from_ast_opt(&self.ctx, e.type_ref()); | 376 | let type_ref = TypeRef::from_ast_opt(&self.ctx(), e.type_ref()); |
377 | self.alloc_expr(Expr::Cast { expr, type_ref }, syntax_ptr) | 377 | self.alloc_expr(Expr::Cast { expr, type_ref }, syntax_ptr) |
378 | } | 378 | } |
379 | ast::Expr::RefExpr(e) => { | 379 | ast::Expr::RefExpr(e) => { |
@@ -396,7 +396,7 @@ impl ExprCollector<'_> { | |||
396 | for param in pl.params() { | 396 | for param in pl.params() { |
397 | let pat = self.collect_pat_opt(param.pat()); | 397 | let pat = self.collect_pat_opt(param.pat()); |
398 | let type_ref = | 398 | let type_ref = |
399 | param.ascribed_type().map(|it| TypeRef::from_ast(&self.ctx, it)); | 399 | param.ascribed_type().map(|it| TypeRef::from_ast(&self.ctx(), it)); |
400 | args.push(pat); | 400 | args.push(pat); |
401 | arg_types.push(type_ref); | 401 | arg_types.push(type_ref); |
402 | } | 402 | } |
@@ -404,7 +404,7 @@ impl ExprCollector<'_> { | |||
404 | let ret_type = e | 404 | let ret_type = e |
405 | .ret_type() | 405 | .ret_type() |
406 | .and_then(|r| r.type_ref()) | 406 | .and_then(|r| r.type_ref()) |
407 | .map(|it| TypeRef::from_ast(&self.ctx, it)); | 407 | .map(|it| TypeRef::from_ast(&self.ctx(), it)); |
408 | let body = self.collect_expr_opt(e.body()); | 408 | let body = self.collect_expr_opt(e.body()); |
409 | self.alloc_expr(Expr::Lambda { args, arg_types, ret_type, body }, syntax_ptr) | 409 | self.alloc_expr(Expr::Lambda { args, arg_types, ret_type, body }, syntax_ptr) |
410 | } | 410 | } |
@@ -507,7 +507,8 @@ impl ExprCollector<'_> { | |||
507 | .map(|s| match s { | 507 | .map(|s| match s { |
508 | ast::Stmt::LetStmt(stmt) => { | 508 | ast::Stmt::LetStmt(stmt) => { |
509 | let pat = self.collect_pat_opt(stmt.pat()); | 509 | let pat = self.collect_pat_opt(stmt.pat()); |
510 | let type_ref = stmt.ascribed_type().map(|it| TypeRef::from_ast(&self.ctx, it)); | 510 | let type_ref = |
511 | stmt.ascribed_type().map(|it| TypeRef::from_ast(&self.ctx(), it)); | ||
511 | let initializer = stmt.initializer().map(|e| self.collect_expr(e)); | 512 | let initializer = stmt.initializer().map(|e| self.collect_expr(e)); |
512 | Statement::Let { pat, type_ref, initializer } | 513 | Statement::Let { pat, type_ref, initializer } |
513 | } | 514 | } |
diff --git a/crates/ra_hir_def/src/body/scope.rs b/crates/ra_hir_def/src/body/scope.rs index 86f953c80..09e92b74e 100644 --- a/crates/ra_hir_def/src/body/scope.rs +++ b/crates/ra_hir_def/src/body/scope.rs | |||
@@ -174,7 +174,7 @@ mod tests { | |||
174 | use hir_expand::{name::AsName, InFile}; | 174 | use hir_expand::{name::AsName, InFile}; |
175 | use ra_db::{fixture::WithFixture, FileId, SourceDatabase}; | 175 | use ra_db::{fixture::WithFixture, FileId, SourceDatabase}; |
176 | use ra_syntax::{algo::find_node_at_offset, ast, AstNode}; | 176 | use ra_syntax::{algo::find_node_at_offset, ast, AstNode}; |
177 | use test_utils::{assert_eq_text, covers, extract_offset}; | 177 | use test_utils::{assert_eq_text, extract_offset, mark}; |
178 | 178 | ||
179 | use crate::{db::DefDatabase, test_db::TestDB, FunctionId, ModuleDefId}; | 179 | use crate::{db::DefDatabase, test_db::TestDB, FunctionId, ModuleDefId}; |
180 | 180 | ||
@@ -388,7 +388,7 @@ mod tests { | |||
388 | 388 | ||
389 | #[test] | 389 | #[test] |
390 | fn while_let_desugaring() { | 390 | fn while_let_desugaring() { |
391 | covers!(infer_resolve_while_let); | 391 | mark::check!(infer_resolve_while_let); |
392 | do_check_local_name( | 392 | do_check_local_name( |
393 | r#" | 393 | r#" |
394 | fn test() { | 394 | fn test() { |
diff --git a/crates/ra_hir_def/src/db.rs b/crates/ra_hir_def/src/db.rs index e665ab45d..945a0025e 100644 --- a/crates/ra_hir_def/src/db.rs +++ b/crates/ra_hir_def/src/db.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | //! Defines database & queries for name resolution. | 1 | //! Defines database & queries for name resolution. |
2 | use std::sync::Arc; | 2 | use std::sync::Arc; |
3 | 3 | ||
4 | use hir_expand::{db::AstDatabase, HirFileId}; | 4 | use hir_expand::{db::AstDatabase, name::Name, HirFileId}; |
5 | use ra_db::{salsa, CrateId, SourceDatabase, Upcast}; | 5 | use ra_db::{salsa, CrateId, SourceDatabase, Upcast}; |
6 | use ra_prof::profile; | 6 | use ra_prof::profile; |
7 | use ra_syntax::SmolStr; | 7 | use ra_syntax::SmolStr; |
@@ -12,9 +12,13 @@ use crate::{ | |||
12 | body::{scope::ExprScopes, Body, BodySourceMap}, | 12 | body::{scope::ExprScopes, Body, BodySourceMap}, |
13 | data::{ConstData, FunctionData, ImplData, StaticData, TraitData, TypeAliasData}, | 13 | data::{ConstData, FunctionData, ImplData, StaticData, TraitData, TypeAliasData}, |
14 | docs::Documentation, | 14 | docs::Documentation, |
15 | find_path, | ||
15 | generics::GenericParams, | 16 | generics::GenericParams, |
17 | item_scope::ItemInNs, | ||
16 | lang_item::{LangItemTarget, LangItems}, | 18 | lang_item::{LangItemTarget, LangItems}, |
17 | nameres::{raw::RawItems, CrateDefMap}, | 19 | nameres::{raw::RawItems, CrateDefMap}, |
20 | path::ModPath, | ||
21 | visibility::Visibility, | ||
18 | AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc, | 22 | AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc, |
19 | GenericDefId, ImplId, ImplLoc, ModuleId, StaticId, StaticLoc, StructId, StructLoc, TraitId, | 23 | GenericDefId, ImplId, ImplLoc, ModuleId, StaticId, StaticLoc, StructId, StructLoc, TraitId, |
20 | TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, | 24 | TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, |
@@ -108,6 +112,16 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> { | |||
108 | // Remove this query completely, in favor of `Attrs::docs` method | 112 | // Remove this query completely, in favor of `Attrs::docs` method |
109 | #[salsa::invoke(Documentation::documentation_query)] | 113 | #[salsa::invoke(Documentation::documentation_query)] |
110 | fn documentation(&self, def: AttrDefId) -> Option<Documentation>; | 114 | fn documentation(&self, def: AttrDefId) -> Option<Documentation>; |
115 | |||
116 | #[salsa::invoke(find_path::importable_locations_of_query)] | ||
117 | fn importable_locations_of( | ||
118 | &self, | ||
119 | item: ItemInNs, | ||
120 | krate: CrateId, | ||
121 | ) -> Arc<[(ModuleId, Name, Visibility)]>; | ||
122 | |||
123 | #[salsa::invoke(find_path::find_path_inner_query)] | ||
124 | fn find_path_inner(&self, item: ItemInNs, from: ModuleId, max_len: usize) -> Option<ModPath>; | ||
111 | } | 125 | } |
112 | 126 | ||
113 | fn crate_def_map_wait(db: &impl DefDatabase, krate: CrateId) -> Arc<CrateDefMap> { | 127 | fn crate_def_map_wait(db: &impl DefDatabase, krate: CrateId) -> Arc<CrateDefMap> { |
diff --git a/crates/ra_hir_def/src/find_path.rs b/crates/ra_hir_def/src/find_path.rs index 70dcb03e6..4db798473 100644 --- a/crates/ra_hir_def/src/find_path.rs +++ b/crates/ra_hir_def/src/find_path.rs | |||
@@ -1,5 +1,11 @@ | |||
1 | //! An algorithm to find a path to refer to a certain item. | 1 | //! An algorithm to find a path to refer to a certain item. |
2 | 2 | ||
3 | use std::sync::Arc; | ||
4 | |||
5 | use hir_expand::name::{known, AsName, Name}; | ||
6 | use ra_prof::profile; | ||
7 | use test_utils::mark; | ||
8 | |||
3 | use crate::{ | 9 | use crate::{ |
4 | db::DefDatabase, | 10 | db::DefDatabase, |
5 | item_scope::ItemInNs, | 11 | item_scope::ItemInNs, |
@@ -7,25 +13,28 @@ use crate::{ | |||
7 | visibility::Visibility, | 13 | visibility::Visibility, |
8 | CrateId, ModuleDefId, ModuleId, | 14 | CrateId, ModuleDefId, ModuleId, |
9 | }; | 15 | }; |
10 | use hir_expand::name::{known, AsName, Name}; | 16 | |
11 | use test_utils::tested_by; | 17 | // FIXME: handle local items |
18 | |||
19 | /// Find a path that can be used to refer to a certain item. This can depend on | ||
20 | /// *from where* you're referring to the item, hence the `from` parameter. | ||
21 | pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> { | ||
22 | let _p = profile("find_path"); | ||
23 | db.find_path_inner(item, from, MAX_PATH_LEN) | ||
24 | } | ||
12 | 25 | ||
13 | const MAX_PATH_LEN: usize = 15; | 26 | const MAX_PATH_LEN: usize = 15; |
14 | 27 | ||
15 | impl ModPath { | 28 | impl ModPath { |
16 | fn starts_with_std(&self) -> bool { | 29 | fn starts_with_std(&self) -> bool { |
17 | self.segments.first().filter(|&first_segment| first_segment == &known::std).is_some() | 30 | self.segments.first() == Some(&known::std) |
18 | } | 31 | } |
19 | 32 | ||
20 | // When std library is present, paths starting with `std::` | 33 | // When std library is present, paths starting with `std::` |
21 | // should be preferred over paths starting with `core::` and `alloc::` | 34 | // should be preferred over paths starting with `core::` and `alloc::` |
22 | fn can_start_with_std(&self) -> bool { | 35 | fn can_start_with_std(&self) -> bool { |
23 | self.segments | 36 | let first_segment = self.segments.first(); |
24 | .first() | 37 | first_segment == Some(&known::alloc) || first_segment == Some(&known::core) |
25 | .filter(|&first_segment| { | ||
26 | first_segment == &known::alloc || first_segment == &known::core | ||
27 | }) | ||
28 | .is_some() | ||
29 | } | 38 | } |
30 | 39 | ||
31 | fn len(&self) -> usize { | 40 | fn len(&self) -> usize { |
@@ -40,15 +49,7 @@ impl ModPath { | |||
40 | } | 49 | } |
41 | } | 50 | } |
42 | 51 | ||
43 | // FIXME: handle local items | 52 | pub(crate) fn find_path_inner_query( |
44 | |||
45 | /// Find a path that can be used to refer to a certain item. This can depend on | ||
46 | /// *from where* you're referring to the item, hence the `from` parameter. | ||
47 | pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> { | ||
48 | find_path_inner(db, item, from, MAX_PATH_LEN) | ||
49 | } | ||
50 | |||
51 | fn find_path_inner( | ||
52 | db: &dyn DefDatabase, | 53 | db: &dyn DefDatabase, |
53 | item: ItemInNs, | 54 | item: ItemInNs, |
54 | from: ModuleId, | 55 | from: ModuleId, |
@@ -139,8 +140,7 @@ fn find_path_inner( | |||
139 | let mut best_path = None; | 140 | let mut best_path = None; |
140 | let mut best_path_len = max_len; | 141 | let mut best_path_len = max_len; |
141 | for (module_id, name) in importable_locations { | 142 | for (module_id, name) in importable_locations { |
142 | let mut path = match find_path_inner( | 143 | let mut path = match db.find_path_inner( |
143 | db, | ||
144 | ItemInNs::Types(ModuleDefId::ModuleId(module_id)), | 144 | ItemInNs::Types(ModuleDefId::ModuleId(module_id)), |
145 | from, | 145 | from, |
146 | best_path_len - 1, | 146 | best_path_len - 1, |
@@ -163,17 +163,19 @@ fn find_path_inner( | |||
163 | 163 | ||
164 | fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -> ModPath { | 164 | fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -> ModPath { |
165 | if old_path.starts_with_std() && new_path.can_start_with_std() { | 165 | if old_path.starts_with_std() && new_path.can_start_with_std() { |
166 | tested_by!(prefer_std_paths); | ||
167 | if prefer_no_std { | 166 | if prefer_no_std { |
167 | mark::hit!(prefer_no_std_paths); | ||
168 | new_path | 168 | new_path |
169 | } else { | 169 | } else { |
170 | mark::hit!(prefer_std_paths); | ||
170 | old_path | 171 | old_path |
171 | } | 172 | } |
172 | } else if new_path.starts_with_std() && old_path.can_start_with_std() { | 173 | } else if new_path.starts_with_std() && old_path.can_start_with_std() { |
173 | tested_by!(prefer_std_paths); | ||
174 | if prefer_no_std { | 174 | if prefer_no_std { |
175 | mark::hit!(prefer_no_std_paths); | ||
175 | old_path | 176 | old_path |
176 | } else { | 177 | } else { |
178 | mark::hit!(prefer_std_paths); | ||
177 | new_path | 179 | new_path |
178 | } | 180 | } |
179 | } else if new_path.len() < old_path.len() { | 181 | } else if new_path.len() < old_path.len() { |
@@ -198,7 +200,7 @@ fn find_importable_locations( | |||
198 | .chain(crate_graph[from.krate].dependencies.iter().map(|dep| dep.crate_id)) | 200 | .chain(crate_graph[from.krate].dependencies.iter().map(|dep| dep.crate_id)) |
199 | { | 201 | { |
200 | result.extend( | 202 | result.extend( |
201 | importable_locations_in_crate(db, item, krate) | 203 | db.importable_locations_of(item, krate) |
202 | .iter() | 204 | .iter() |
203 | .filter(|(_, _, vis)| vis.is_visible_from(db, from)) | 205 | .filter(|(_, _, vis)| vis.is_visible_from(db, from)) |
204 | .map(|(m, n, _)| (*m, n.clone())), | 206 | .map(|(m, n, _)| (*m, n.clone())), |
@@ -213,11 +215,12 @@ fn find_importable_locations( | |||
213 | /// | 215 | /// |
214 | /// Note that the crate doesn't need to be the one in which the item is defined; | 216 | /// Note that the crate doesn't need to be the one in which the item is defined; |
215 | /// it might be re-exported in other crates. | 217 | /// it might be re-exported in other crates. |
216 | fn importable_locations_in_crate( | 218 | pub(crate) fn importable_locations_of_query( |
217 | db: &dyn DefDatabase, | 219 | db: &dyn DefDatabase, |
218 | item: ItemInNs, | 220 | item: ItemInNs, |
219 | krate: CrateId, | 221 | krate: CrateId, |
220 | ) -> Vec<(ModuleId, Name, Visibility)> { | 222 | ) -> Arc<[(ModuleId, Name, Visibility)]> { |
223 | let _p = profile("importable_locations_of_query"); | ||
221 | let def_map = db.crate_def_map(krate); | 224 | let def_map = db.crate_def_map(krate); |
222 | let mut result = Vec::new(); | 225 | let mut result = Vec::new(); |
223 | for (local_id, data) in def_map.modules.iter() { | 226 | for (local_id, data) in def_map.modules.iter() { |
@@ -243,17 +246,20 @@ fn importable_locations_in_crate( | |||
243 | result.push((ModuleId { krate, local_id }, name.clone(), vis)); | 246 | result.push((ModuleId { krate, local_id }, name.clone(), vis)); |
244 | } | 247 | } |
245 | } | 248 | } |
246 | result | 249 | |
250 | Arc::from(result) | ||
247 | } | 251 | } |
248 | 252 | ||
249 | #[cfg(test)] | 253 | #[cfg(test)] |
250 | mod tests { | 254 | mod tests { |
251 | use super::*; | ||
252 | use crate::test_db::TestDB; | ||
253 | use hir_expand::hygiene::Hygiene; | 255 | use hir_expand::hygiene::Hygiene; |
254 | use ra_db::fixture::WithFixture; | 256 | use ra_db::fixture::WithFixture; |
255 | use ra_syntax::ast::AstNode; | 257 | use ra_syntax::ast::AstNode; |
256 | use test_utils::covers; | 258 | use test_utils::mark; |
259 | |||
260 | use crate::test_db::TestDB; | ||
261 | |||
262 | use super::*; | ||
257 | 263 | ||
258 | /// `code` needs to contain a cursor marker; checks that `find_path` for the | 264 | /// `code` needs to contain a cursor marker; checks that `find_path` for the |
259 | /// item the `path` refers to returns that same path when called from the | 265 | /// item the `path` refers to returns that same path when called from the |
@@ -508,7 +514,7 @@ mod tests { | |||
508 | 514 | ||
509 | #[test] | 515 | #[test] |
510 | fn prefer_std_paths_over_alloc() { | 516 | fn prefer_std_paths_over_alloc() { |
511 | covers!(prefer_std_paths); | 517 | mark::check!(prefer_std_paths); |
512 | let code = r#" | 518 | let code = r#" |
513 | //- /main.rs crate:main deps:alloc,std | 519 | //- /main.rs crate:main deps:alloc,std |
514 | <|> | 520 | <|> |
@@ -527,51 +533,50 @@ mod tests { | |||
527 | } | 533 | } |
528 | 534 | ||
529 | #[test] | 535 | #[test] |
530 | fn prefer_alloc_paths_over_std() { | 536 | fn prefer_core_paths_over_std() { |
531 | covers!(prefer_std_paths); | 537 | mark::check!(prefer_no_std_paths); |
532 | let code = r#" | 538 | let code = r#" |
533 | //- /main.rs crate:main deps:alloc,std | 539 | //- /main.rs crate:main deps:core,std |
534 | #![no_std] | 540 | #![no_std] |
535 | 541 | ||
536 | <|> | 542 | <|> |
537 | 543 | ||
538 | //- /std.rs crate:std deps:alloc | 544 | //- /std.rs crate:std deps:core |
539 | 545 | ||
540 | pub mod sync { | 546 | pub mod fmt { |
541 | pub use alloc::sync::Arc; | 547 | pub use core::fmt::Error; |
542 | } | 548 | } |
543 | 549 | ||
544 | //- /zzz.rs crate:alloc | 550 | //- /zzz.rs crate:core |
545 | 551 | ||
546 | pub mod sync { | 552 | pub mod fmt { |
547 | pub struct Arc; | 553 | pub struct Error; |
548 | } | 554 | } |
549 | "#; | 555 | "#; |
550 | check_found_path(code, "alloc::sync::Arc"); | 556 | check_found_path(code, "core::fmt::Error"); |
551 | } | 557 | } |
552 | 558 | ||
553 | #[test] | 559 | #[test] |
554 | fn prefer_core_paths_over_std() { | 560 | fn prefer_alloc_paths_over_std() { |
555 | covers!(prefer_std_paths); | ||
556 | let code = r#" | 561 | let code = r#" |
557 | //- /main.rs crate:main deps:core,std | 562 | //- /main.rs crate:main deps:alloc,std |
558 | #![no_std] | 563 | #![no_std] |
559 | 564 | ||
560 | <|> | 565 | <|> |
561 | 566 | ||
562 | //- /std.rs crate:std deps:core | 567 | //- /std.rs crate:std deps:alloc |
563 | 568 | ||
564 | pub mod fmt { | 569 | pub mod sync { |
565 | pub use core::fmt::Error; | 570 | pub use alloc::sync::Arc; |
566 | } | 571 | } |
567 | 572 | ||
568 | //- /zzz.rs crate:core | 573 | //- /zzz.rs crate:alloc |
569 | 574 | ||
570 | pub mod fmt { | 575 | pub mod sync { |
571 | pub struct Error; | 576 | pub struct Arc; |
572 | } | 577 | } |
573 | "#; | 578 | "#; |
574 | check_found_path(code, "core::fmt::Error"); | 579 | check_found_path(code, "alloc::sync::Arc"); |
575 | } | 580 | } |
576 | 581 | ||
577 | #[test] | 582 | #[test] |
diff --git a/crates/ra_hir_def/src/lib.rs b/crates/ra_hir_def/src/lib.rs index 518772e8a..5325a2760 100644 --- a/crates/ra_hir_def/src/lib.rs +++ b/crates/ra_hir_def/src/lib.rs | |||
@@ -46,8 +46,6 @@ pub mod find_path; | |||
46 | 46 | ||
47 | #[cfg(test)] | 47 | #[cfg(test)] |
48 | mod test_db; | 48 | mod test_db; |
49 | #[cfg(test)] | ||
50 | mod marks; | ||
51 | 49 | ||
52 | use std::hash::Hash; | 50 | use std::hash::Hash; |
53 | 51 | ||
diff --git a/crates/ra_hir_def/src/marks.rs b/crates/ra_hir_def/src/marks.rs deleted file mode 100644 index daa49d5f1..000000000 --- a/crates/ra_hir_def/src/marks.rs +++ /dev/null | |||
@@ -1,17 +0,0 @@ | |||
1 | //! See test_utils/src/marks.rs | ||
2 | |||
3 | test_utils::marks!( | ||
4 | bogus_paths | ||
5 | name_res_works_for_broken_modules | ||
6 | can_import_enum_variant | ||
7 | glob_enum | ||
8 | glob_enum_group | ||
9 | glob_across_crates | ||
10 | std_prelude | ||
11 | macro_rules_from_other_crates_are_visible_with_macro_use | ||
12 | prelude_is_macro_use | ||
13 | macro_dollar_crate_self | ||
14 | macro_dollar_crate_other | ||
15 | infer_resolve_while_let | ||
16 | prefer_std_paths | ||
17 | ); | ||
diff --git a/crates/ra_hir_def/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs index db994122a..353a31ad4 100644 --- a/crates/ra_hir_def/src/nameres/collector.rs +++ b/crates/ra_hir_def/src/nameres/collector.rs | |||
@@ -14,7 +14,7 @@ use ra_cfg::CfgOptions; | |||
14 | use ra_db::{CrateId, FileId, ProcMacroId}; | 14 | use ra_db::{CrateId, FileId, ProcMacroId}; |
15 | use ra_syntax::ast; | 15 | use ra_syntax::ast; |
16 | use rustc_hash::FxHashMap; | 16 | use rustc_hash::FxHashMap; |
17 | use test_utils::tested_by; | 17 | use test_utils::mark; |
18 | 18 | ||
19 | use crate::{ | 19 | use crate::{ |
20 | attr::Attrs, | 20 | attr::Attrs, |
@@ -302,7 +302,7 @@ impl DefCollector<'_> { | |||
302 | ); | 302 | ); |
303 | 303 | ||
304 | if let Some(ModuleDefId::ModuleId(m)) = res.take_types() { | 304 | if let Some(ModuleDefId::ModuleId(m)) = res.take_types() { |
305 | tested_by!(macro_rules_from_other_crates_are_visible_with_macro_use); | 305 | mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use); |
306 | self.import_all_macros_exported(current_module_id, m.krate); | 306 | self.import_all_macros_exported(current_module_id, m.krate); |
307 | } | 307 | } |
308 | } | 308 | } |
@@ -412,10 +412,10 @@ impl DefCollector<'_> { | |||
412 | match def.take_types() { | 412 | match def.take_types() { |
413 | Some(ModuleDefId::ModuleId(m)) => { | 413 | Some(ModuleDefId::ModuleId(m)) => { |
414 | if import.is_prelude { | 414 | if import.is_prelude { |
415 | tested_by!(std_prelude); | 415 | mark::hit!(std_prelude); |
416 | self.def_map.prelude = Some(m); | 416 | self.def_map.prelude = Some(m); |
417 | } else if m.krate != self.def_map.krate { | 417 | } else if m.krate != self.def_map.krate { |
418 | tested_by!(glob_across_crates); | 418 | mark::hit!(glob_across_crates); |
419 | // glob import from other crate => we can just import everything once | 419 | // glob import from other crate => we can just import everything once |
420 | let item_map = self.db.crate_def_map(m.krate); | 420 | let item_map = self.db.crate_def_map(m.krate); |
421 | let scope = &item_map[m.local_id].scope; | 421 | let scope = &item_map[m.local_id].scope; |
@@ -461,7 +461,7 @@ impl DefCollector<'_> { | |||
461 | } | 461 | } |
462 | } | 462 | } |
463 | Some(ModuleDefId::AdtId(AdtId::EnumId(e))) => { | 463 | Some(ModuleDefId::AdtId(AdtId::EnumId(e))) => { |
464 | tested_by!(glob_enum); | 464 | mark::hit!(glob_enum); |
465 | // glob import from enum => just import all the variants | 465 | // glob import from enum => just import all the variants |
466 | 466 | ||
467 | // XXX: urgh, so this works by accident! Here, we look at | 467 | // XXX: urgh, so this works by accident! Here, we look at |
@@ -510,7 +510,7 @@ impl DefCollector<'_> { | |||
510 | 510 | ||
511 | self.update(module_id, &[(name, def)], vis); | 511 | self.update(module_id, &[(name, def)], vis); |
512 | } | 512 | } |
513 | None => tested_by!(bogus_paths), | 513 | None => mark::hit!(bogus_paths), |
514 | } | 514 | } |
515 | } | 515 | } |
516 | } | 516 | } |
@@ -683,7 +683,7 @@ impl ModCollector<'_, '_> { | |||
683 | // Prelude module is always considered to be `#[macro_use]`. | 683 | // Prelude module is always considered to be `#[macro_use]`. |
684 | if let Some(prelude_module) = self.def_collector.def_map.prelude { | 684 | if let Some(prelude_module) = self.def_collector.def_map.prelude { |
685 | if prelude_module.krate != self.def_collector.def_map.krate { | 685 | if prelude_module.krate != self.def_collector.def_map.krate { |
686 | tested_by!(prelude_is_macro_use); | 686 | mark::hit!(prelude_is_macro_use); |
687 | self.def_collector.import_all_macros_exported(self.module_id, prelude_module.krate); | 687 | self.def_collector.import_all_macros_exported(self.module_id, prelude_module.krate); |
688 | } | 688 | } |
689 | } | 689 | } |
diff --git a/crates/ra_hir_def/src/nameres/path_resolution.rs b/crates/ra_hir_def/src/nameres/path_resolution.rs index 35a0a0c98..19692e70c 100644 --- a/crates/ra_hir_def/src/nameres/path_resolution.rs +++ b/crates/ra_hir_def/src/nameres/path_resolution.rs | |||
@@ -14,7 +14,7 @@ use std::iter::successors; | |||
14 | 14 | ||
15 | use hir_expand::name::Name; | 15 | use hir_expand::name::Name; |
16 | use ra_db::Edition; | 16 | use ra_db::Edition; |
17 | use test_utils::tested_by; | 17 | use test_utils::mark; |
18 | 18 | ||
19 | use crate::{ | 19 | use crate::{ |
20 | db::DefDatabase, | 20 | db::DefDatabase, |
@@ -108,7 +108,7 @@ impl CrateDefMap { | |||
108 | let mut curr_per_ns: PerNs = match path.kind { | 108 | let mut curr_per_ns: PerNs = match path.kind { |
109 | PathKind::DollarCrate(krate) => { | 109 | PathKind::DollarCrate(krate) => { |
110 | if krate == self.krate { | 110 | if krate == self.krate { |
111 | tested_by!(macro_dollar_crate_self); | 111 | mark::hit!(macro_dollar_crate_self); |
112 | PerNs::types( | 112 | PerNs::types( |
113 | ModuleId { krate: self.krate, local_id: self.root }.into(), | 113 | ModuleId { krate: self.krate, local_id: self.root }.into(), |
114 | Visibility::Public, | 114 | Visibility::Public, |
@@ -116,7 +116,7 @@ impl CrateDefMap { | |||
116 | } else { | 116 | } else { |
117 | let def_map = db.crate_def_map(krate); | 117 | let def_map = db.crate_def_map(krate); |
118 | let module = ModuleId { krate, local_id: def_map.root }; | 118 | let module = ModuleId { krate, local_id: def_map.root }; |
119 | tested_by!(macro_dollar_crate_other); | 119 | mark::hit!(macro_dollar_crate_other); |
120 | PerNs::types(module.into(), Visibility::Public) | 120 | PerNs::types(module.into(), Visibility::Public) |
121 | } | 121 | } |
122 | } | 122 | } |
@@ -221,7 +221,7 @@ impl CrateDefMap { | |||
221 | } | 221 | } |
222 | ModuleDefId::AdtId(AdtId::EnumId(e)) => { | 222 | ModuleDefId::AdtId(AdtId::EnumId(e)) => { |
223 | // enum variant | 223 | // enum variant |
224 | tested_by!(can_import_enum_variant); | 224 | mark::hit!(can_import_enum_variant); |
225 | let enum_data = db.enum_data(e); | 225 | let enum_data = db.enum_data(e); |
226 | match enum_data.variant(&segment) { | 226 | match enum_data.variant(&segment) { |
227 | Some(local_id) => { | 227 | Some(local_id) => { |
diff --git a/crates/ra_hir_def/src/nameres/raw.rs b/crates/ra_hir_def/src/nameres/raw.rs index f2716a295..4e628b14d 100644 --- a/crates/ra_hir_def/src/nameres/raw.rs +++ b/crates/ra_hir_def/src/nameres/raw.rs | |||
@@ -18,7 +18,7 @@ use ra_syntax::{ | |||
18 | ast::{self, AttrsOwner, NameOwner, VisibilityOwner}, | 18 | ast::{self, AttrsOwner, NameOwner, VisibilityOwner}, |
19 | AstNode, | 19 | AstNode, |
20 | }; | 20 | }; |
21 | use test_utils::tested_by; | 21 | use test_utils::mark; |
22 | 22 | ||
23 | use crate::{ | 23 | use crate::{ |
24 | attr::Attrs, | 24 | attr::Attrs, |
@@ -346,7 +346,7 @@ impl RawItemsCollector { | |||
346 | self.push_item(current_module, attrs, RawItemKind::Module(item)); | 346 | self.push_item(current_module, attrs, RawItemKind::Module(item)); |
347 | return; | 347 | return; |
348 | } | 348 | } |
349 | tested_by!(name_res_works_for_broken_modules); | 349 | mark::hit!(name_res_works_for_broken_modules); |
350 | } | 350 | } |
351 | 351 | ||
352 | fn add_use_item(&mut self, current_module: Option<Idx<ModuleData>>, use_item: ast::UseItem) { | 352 | fn add_use_item(&mut self, current_module: Option<Idx<ModuleData>>, use_item: ast::UseItem) { |
diff --git a/crates/ra_hir_def/src/nameres/tests.rs b/crates/ra_hir_def/src/nameres/tests.rs index 1b66c1aac..05cd0297d 100644 --- a/crates/ra_hir_def/src/nameres/tests.rs +++ b/crates/ra_hir_def/src/nameres/tests.rs | |||
@@ -8,7 +8,7 @@ use std::sync::Arc; | |||
8 | 8 | ||
9 | use insta::assert_snapshot; | 9 | use insta::assert_snapshot; |
10 | use ra_db::{fixture::WithFixture, SourceDatabase}; | 10 | use ra_db::{fixture::WithFixture, SourceDatabase}; |
11 | use test_utils::covers; | 11 | use test_utils::mark; |
12 | 12 | ||
13 | use crate::{db::DefDatabase, nameres::*, test_db::TestDB}; | 13 | use crate::{db::DefDatabase, nameres::*, test_db::TestDB}; |
14 | 14 | ||
@@ -132,7 +132,7 @@ fn crate_def_map_fn_mod_same_name() { | |||
132 | 132 | ||
133 | #[test] | 133 | #[test] |
134 | fn bogus_paths() { | 134 | fn bogus_paths() { |
135 | covers!(bogus_paths); | 135 | mark::check!(bogus_paths); |
136 | let map = def_map( | 136 | let map = def_map( |
137 | " | 137 | " |
138 | //- /lib.rs | 138 | //- /lib.rs |
@@ -247,7 +247,7 @@ fn re_exports() { | |||
247 | 247 | ||
248 | #[test] | 248 | #[test] |
249 | fn std_prelude() { | 249 | fn std_prelude() { |
250 | covers!(std_prelude); | 250 | mark::check!(std_prelude); |
251 | let map = def_map( | 251 | let map = def_map( |
252 | " | 252 | " |
253 | //- /main.rs crate:main deps:test_crate | 253 | //- /main.rs crate:main deps:test_crate |
@@ -271,7 +271,7 @@ fn std_prelude() { | |||
271 | 271 | ||
272 | #[test] | 272 | #[test] |
273 | fn can_import_enum_variant() { | 273 | fn can_import_enum_variant() { |
274 | covers!(can_import_enum_variant); | 274 | mark::check!(can_import_enum_variant); |
275 | let map = def_map( | 275 | let map = def_map( |
276 | " | 276 | " |
277 | //- /lib.rs | 277 | //- /lib.rs |
diff --git a/crates/ra_hir_def/src/nameres/tests/globs.rs b/crates/ra_hir_def/src/nameres/tests/globs.rs index ee8df3a26..2b12c0daa 100644 --- a/crates/ra_hir_def/src/nameres/tests/globs.rs +++ b/crates/ra_hir_def/src/nameres/tests/globs.rs | |||
@@ -152,7 +152,7 @@ fn glob_privacy_2() { | |||
152 | 152 | ||
153 | #[test] | 153 | #[test] |
154 | fn glob_across_crates() { | 154 | fn glob_across_crates() { |
155 | covers!(glob_across_crates); | 155 | mark::check!(glob_across_crates); |
156 | let map = def_map( | 156 | let map = def_map( |
157 | r" | 157 | r" |
158 | //- /main.rs crate:main deps:test_crate | 158 | //- /main.rs crate:main deps:test_crate |
@@ -171,7 +171,6 @@ fn glob_across_crates() { | |||
171 | 171 | ||
172 | #[test] | 172 | #[test] |
173 | fn glob_privacy_across_crates() { | 173 | fn glob_privacy_across_crates() { |
174 | covers!(glob_across_crates); | ||
175 | let map = def_map( | 174 | let map = def_map( |
176 | r" | 175 | r" |
177 | //- /main.rs crate:main deps:test_crate | 176 | //- /main.rs crate:main deps:test_crate |
@@ -191,7 +190,7 @@ fn glob_privacy_across_crates() { | |||
191 | 190 | ||
192 | #[test] | 191 | #[test] |
193 | fn glob_enum() { | 192 | fn glob_enum() { |
194 | covers!(glob_enum); | 193 | mark::check!(glob_enum); |
195 | let map = def_map( | 194 | let map = def_map( |
196 | " | 195 | " |
197 | //- /lib.rs | 196 | //- /lib.rs |
@@ -212,7 +211,7 @@ fn glob_enum() { | |||
212 | 211 | ||
213 | #[test] | 212 | #[test] |
214 | fn glob_enum_group() { | 213 | fn glob_enum_group() { |
215 | covers!(glob_enum_group); | 214 | mark::check!(glob_enum_group); |
216 | let map = def_map( | 215 | let map = def_map( |
217 | r" | 216 | r" |
218 | //- /lib.rs | 217 | //- /lib.rs |
diff --git a/crates/ra_hir_def/src/nameres/tests/macros.rs b/crates/ra_hir_def/src/nameres/tests/macros.rs index 40289e3ca..84480d9f6 100644 --- a/crates/ra_hir_def/src/nameres/tests/macros.rs +++ b/crates/ra_hir_def/src/nameres/tests/macros.rs | |||
@@ -212,7 +212,7 @@ fn unexpanded_macro_should_expand_by_fixedpoint_loop() { | |||
212 | 212 | ||
213 | #[test] | 213 | #[test] |
214 | fn macro_rules_from_other_crates_are_visible_with_macro_use() { | 214 | fn macro_rules_from_other_crates_are_visible_with_macro_use() { |
215 | covers!(macro_rules_from_other_crates_are_visible_with_macro_use); | 215 | mark::check!(macro_rules_from_other_crates_are_visible_with_macro_use); |
216 | let map = def_map( | 216 | let map = def_map( |
217 | " | 217 | " |
218 | //- /main.rs crate:main deps:foo | 218 | //- /main.rs crate:main deps:foo |
@@ -262,7 +262,7 @@ fn macro_rules_from_other_crates_are_visible_with_macro_use() { | |||
262 | 262 | ||
263 | #[test] | 263 | #[test] |
264 | fn prelude_is_macro_use() { | 264 | fn prelude_is_macro_use() { |
265 | covers!(prelude_is_macro_use); | 265 | mark::check!(prelude_is_macro_use); |
266 | let map = def_map( | 266 | let map = def_map( |
267 | " | 267 | " |
268 | //- /main.rs crate:main deps:foo | 268 | //- /main.rs crate:main deps:foo |
@@ -544,8 +544,7 @@ fn path_qualified_macros() { | |||
544 | 544 | ||
545 | #[test] | 545 | #[test] |
546 | fn macro_dollar_crate_is_correct_in_item() { | 546 | fn macro_dollar_crate_is_correct_in_item() { |
547 | covers!(macro_dollar_crate_self); | 547 | mark::check!(macro_dollar_crate_self); |
548 | covers!(macro_dollar_crate_other); | ||
549 | let map = def_map( | 548 | let map = def_map( |
550 | " | 549 | " |
551 | //- /main.rs crate:main deps:foo | 550 | //- /main.rs crate:main deps:foo |
@@ -603,7 +602,7 @@ fn macro_dollar_crate_is_correct_in_item() { | |||
603 | 602 | ||
604 | #[test] | 603 | #[test] |
605 | fn macro_dollar_crate_is_correct_in_indirect_deps() { | 604 | fn macro_dollar_crate_is_correct_in_indirect_deps() { |
606 | covers!(macro_dollar_crate_other); | 605 | mark::check!(macro_dollar_crate_other); |
607 | // From std | 606 | // From std |
608 | let map = def_map( | 607 | let map = def_map( |
609 | r#" | 608 | r#" |
diff --git a/crates/ra_hir_def/src/nameres/tests/mod_resolution.rs b/crates/ra_hir_def/src/nameres/tests/mod_resolution.rs index 37fcdfb8c..b43b294ca 100644 --- a/crates/ra_hir_def/src/nameres/tests/mod_resolution.rs +++ b/crates/ra_hir_def/src/nameres/tests/mod_resolution.rs | |||
@@ -2,7 +2,7 @@ use super::*; | |||
2 | 2 | ||
3 | #[test] | 3 | #[test] |
4 | fn name_res_works_for_broken_modules() { | 4 | fn name_res_works_for_broken_modules() { |
5 | covers!(name_res_works_for_broken_modules); | 5 | mark::check!(name_res_works_for_broken_modules); |
6 | let map = def_map( | 6 | let map = def_map( |
7 | r" | 7 | r" |
8 | //- /lib.rs | 8 | //- /lib.rs |
diff --git a/crates/ra_hir_def/src/path/lower/lower_use.rs b/crates/ra_hir_def/src/path/lower/lower_use.rs index 5b6854b0f..7cc655487 100644 --- a/crates/ra_hir_def/src/path/lower/lower_use.rs +++ b/crates/ra_hir_def/src/path/lower/lower_use.rs | |||
@@ -6,7 +6,7 @@ use std::iter; | |||
6 | use either::Either; | 6 | use either::Either; |
7 | use hir_expand::{hygiene::Hygiene, name::AsName}; | 7 | use hir_expand::{hygiene::Hygiene, name::AsName}; |
8 | use ra_syntax::ast::{self, NameOwner}; | 8 | use ra_syntax::ast::{self, NameOwner}; |
9 | use test_utils::tested_by; | 9 | use test_utils::mark; |
10 | 10 | ||
11 | use crate::path::{ImportAlias, ModPath, PathKind}; | 11 | use crate::path::{ImportAlias, ModPath, PathKind}; |
12 | 12 | ||
@@ -54,7 +54,7 @@ pub(crate) fn lower_use_tree( | |||
54 | // FIXME: report errors somewhere | 54 | // FIXME: report errors somewhere |
55 | // We get here if we do | 55 | // We get here if we do |
56 | } else if is_glob { | 56 | } else if is_glob { |
57 | tested_by!(glob_enum_group); | 57 | mark::hit!(glob_enum_group); |
58 | if let Some(prefix) = prefix { | 58 | if let Some(prefix) = prefix { |
59 | cb(prefix, &tree, is_glob, None) | 59 | cb(prefix, &tree, is_glob, None) |
60 | } | 60 | } |
diff --git a/crates/ra_hir_ty/src/_match.rs b/crates/ra_hir_ty/src/_match.rs index 149f65042..3e6e1e333 100644 --- a/crates/ra_hir_ty/src/_match.rs +++ b/crates/ra_hir_ty/src/_match.rs | |||
@@ -1946,6 +1946,23 @@ mod tests { | |||
1946 | 1946 | ||
1947 | check_no_diagnostic(content); | 1947 | check_no_diagnostic(content); |
1948 | } | 1948 | } |
1949 | |||
1950 | #[test] | ||
1951 | fn expr_diverges_missing_arm() { | ||
1952 | let content = r" | ||
1953 | enum Either { | ||
1954 | A, | ||
1955 | B, | ||
1956 | } | ||
1957 | fn test_fn() { | ||
1958 | match loop {} { | ||
1959 | Either::A => (), | ||
1960 | } | ||
1961 | } | ||
1962 | "; | ||
1963 | |||
1964 | check_no_diagnostic(content); | ||
1965 | } | ||
1949 | } | 1966 | } |
1950 | 1967 | ||
1951 | #[cfg(test)] | 1968 | #[cfg(test)] |
@@ -1998,26 +2015,6 @@ mod false_negatives { | |||
1998 | } | 2015 | } |
1999 | 2016 | ||
2000 | #[test] | 2017 | #[test] |
2001 | fn expr_diverges_missing_arm() { | ||
2002 | let content = r" | ||
2003 | enum Either { | ||
2004 | A, | ||
2005 | B, | ||
2006 | } | ||
2007 | fn test_fn() { | ||
2008 | match loop {} { | ||
2009 | Either::A => (), | ||
2010 | } | ||
2011 | } | ||
2012 | "; | ||
2013 | |||
2014 | // This is a false negative. | ||
2015 | // Even though the match expression diverges, rustc fails | ||
2016 | // to compile here since `Either::B` is missing. | ||
2017 | check_no_diagnostic(content); | ||
2018 | } | ||
2019 | |||
2020 | #[test] | ||
2021 | fn expr_loop_missing_arm() { | 2018 | fn expr_loop_missing_arm() { |
2022 | let content = r" | 2019 | let content = r" |
2023 | enum Either { | 2020 | enum Either { |
@@ -2035,7 +2032,7 @@ mod false_negatives { | |||
2035 | // We currently infer the type of `loop { break Foo::A }` to `!`, which | 2032 | // We currently infer the type of `loop { break Foo::A }` to `!`, which |
2036 | // causes us to skip the diagnostic since `Either::A` doesn't type check | 2033 | // causes us to skip the diagnostic since `Either::A` doesn't type check |
2037 | // with `!`. | 2034 | // with `!`. |
2038 | check_no_diagnostic(content); | 2035 | check_diagnostic(content); |
2039 | } | 2036 | } |
2040 | 2037 | ||
2041 | #[test] | 2038 | #[test] |
diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs index 2876cb141..957d6e0b5 100644 --- a/crates/ra_hir_ty/src/infer.rs +++ b/crates/ra_hir_ty/src/infer.rs | |||
@@ -218,6 +218,7 @@ struct InferenceContext<'a> { | |||
218 | #[derive(Clone, Debug)] | 218 | #[derive(Clone, Debug)] |
219 | struct BreakableContext { | 219 | struct BreakableContext { |
220 | pub may_break: bool, | 220 | pub may_break: bool, |
221 | pub break_ty: Ty, | ||
221 | } | 222 | } |
222 | 223 | ||
223 | impl<'a> InferenceContext<'a> { | 224 | impl<'a> InferenceContext<'a> { |
diff --git a/crates/ra_hir_ty/src/infer/coerce.rs b/crates/ra_hir_ty/src/infer/coerce.rs index 173ec59ed..2ee9adb16 100644 --- a/crates/ra_hir_ty/src/infer/coerce.rs +++ b/crates/ra_hir_ty/src/infer/coerce.rs | |||
@@ -5,7 +5,7 @@ | |||
5 | //! See: https://doc.rust-lang.org/nomicon/coercions.html | 5 | //! See: https://doc.rust-lang.org/nomicon/coercions.html |
6 | 6 | ||
7 | use hir_def::{lang_item::LangItemTarget, type_ref::Mutability}; | 7 | use hir_def::{lang_item::LangItemTarget, type_ref::Mutability}; |
8 | use test_utils::tested_by; | 8 | use test_utils::mark; |
9 | 9 | ||
10 | use crate::{autoderef, traits::Solution, Obligation, Substs, TraitRef, Ty, TypeCtor}; | 10 | use crate::{autoderef, traits::Solution, Obligation, Substs, TraitRef, Ty, TypeCtor}; |
11 | 11 | ||
@@ -34,7 +34,7 @@ impl<'a> InferenceContext<'a> { | |||
34 | ty1.clone() | 34 | ty1.clone() |
35 | } else { | 35 | } else { |
36 | if let (ty_app!(TypeCtor::FnDef(_)), ty_app!(TypeCtor::FnDef(_))) = (ty1, ty2) { | 36 | if let (ty_app!(TypeCtor::FnDef(_)), ty_app!(TypeCtor::FnDef(_))) = (ty1, ty2) { |
37 | tested_by!(coerce_fn_reification); | 37 | mark::hit!(coerce_fn_reification); |
38 | // Special case: two function types. Try to coerce both to | 38 | // Special case: two function types. Try to coerce both to |
39 | // pointers to have a chance at getting a match. See | 39 | // pointers to have a chance at getting a match. See |
40 | // https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916 | 40 | // https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916 |
@@ -44,7 +44,7 @@ impl<'a> InferenceContext<'a> { | |||
44 | let ptr_ty2 = Ty::fn_ptr(sig2); | 44 | let ptr_ty2 = Ty::fn_ptr(sig2); |
45 | self.coerce_merge_branch(&ptr_ty1, &ptr_ty2) | 45 | self.coerce_merge_branch(&ptr_ty1, &ptr_ty2) |
46 | } else { | 46 | } else { |
47 | tested_by!(coerce_merge_fail_fallback); | 47 | mark::hit!(coerce_merge_fail_fallback); |
48 | // For incompatible types, we use the latter one as result | 48 | // For incompatible types, we use the latter one as result |
49 | // to be better recovery for `if` without `else`. | 49 | // to be better recovery for `if` without `else`. |
50 | ty2.clone() | 50 | ty2.clone() |
diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs index 0b67d216a..b28724f0e 100644 --- a/crates/ra_hir_ty/src/infer/expr.rs +++ b/crates/ra_hir_ty/src/infer/expr.rs | |||
@@ -93,22 +93,25 @@ impl<'a> InferenceContext<'a> { | |||
93 | Ty::Unknown | 93 | Ty::Unknown |
94 | } | 94 | } |
95 | Expr::Loop { body } => { | 95 | Expr::Loop { body } => { |
96 | self.breakables.push(BreakableContext { may_break: false }); | 96 | self.breakables.push(BreakableContext { |
97 | may_break: false, | ||
98 | break_ty: self.table.new_type_var(), | ||
99 | }); | ||
97 | self.infer_expr(*body, &Expectation::has_type(Ty::unit())); | 100 | self.infer_expr(*body, &Expectation::has_type(Ty::unit())); |
98 | 101 | ||
99 | let ctxt = self.breakables.pop().expect("breakable stack broken"); | 102 | let ctxt = self.breakables.pop().expect("breakable stack broken"); |
100 | if ctxt.may_break { | 103 | if ctxt.may_break { |
101 | self.diverges = Diverges::Maybe; | 104 | self.diverges = Diverges::Maybe; |
102 | } | 105 | } |
103 | // FIXME handle break with value | 106 | |
104 | if ctxt.may_break { | 107 | if ctxt.may_break { |
105 | Ty::unit() | 108 | ctxt.break_ty |
106 | } else { | 109 | } else { |
107 | Ty::simple(TypeCtor::Never) | 110 | Ty::simple(TypeCtor::Never) |
108 | } | 111 | } |
109 | } | 112 | } |
110 | Expr::While { condition, body } => { | 113 | Expr::While { condition, body } => { |
111 | self.breakables.push(BreakableContext { may_break: false }); | 114 | self.breakables.push(BreakableContext { may_break: false, break_ty: Ty::Unknown }); |
112 | // while let is desugared to a match loop, so this is always simple while | 115 | // while let is desugared to a match loop, so this is always simple while |
113 | self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool))); | 116 | self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool))); |
114 | self.infer_expr(*body, &Expectation::has_type(Ty::unit())); | 117 | self.infer_expr(*body, &Expectation::has_type(Ty::unit())); |
@@ -120,7 +123,7 @@ impl<'a> InferenceContext<'a> { | |||
120 | Expr::For { iterable, body, pat } => { | 123 | Expr::For { iterable, body, pat } => { |
121 | let iterable_ty = self.infer_expr(*iterable, &Expectation::none()); | 124 | let iterable_ty = self.infer_expr(*iterable, &Expectation::none()); |
122 | 125 | ||
123 | self.breakables.push(BreakableContext { may_break: false }); | 126 | self.breakables.push(BreakableContext { may_break: false, break_ty: Ty::Unknown }); |
124 | let pat_ty = | 127 | let pat_ty = |
125 | self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item()); | 128 | self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item()); |
126 | 129 | ||
@@ -229,17 +232,29 @@ impl<'a> InferenceContext<'a> { | |||
229 | } | 232 | } |
230 | Expr::Continue => Ty::simple(TypeCtor::Never), | 233 | Expr::Continue => Ty::simple(TypeCtor::Never), |
231 | Expr::Break { expr } => { | 234 | Expr::Break { expr } => { |
232 | if let Some(expr) = expr { | 235 | let val_ty = if let Some(expr) = expr { |
233 | // FIXME handle break with value | 236 | self.infer_expr(*expr, &Expectation::none()) |
234 | self.infer_expr(*expr, &Expectation::none()); | 237 | } else { |
235 | } | 238 | Ty::unit() |
239 | }; | ||
240 | |||
241 | let last_ty = if let Some(ctxt) = self.breakables.last() { | ||
242 | ctxt.break_ty.clone() | ||
243 | } else { | ||
244 | Ty::Unknown | ||
245 | }; | ||
246 | |||
247 | let merged_type = self.coerce_merge_branch(&last_ty, &val_ty); | ||
248 | |||
236 | if let Some(ctxt) = self.breakables.last_mut() { | 249 | if let Some(ctxt) = self.breakables.last_mut() { |
250 | ctxt.break_ty = merged_type; | ||
237 | ctxt.may_break = true; | 251 | ctxt.may_break = true; |
238 | } else { | 252 | } else { |
239 | self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop { | 253 | self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop { |
240 | expr: tgt_expr, | 254 | expr: tgt_expr, |
241 | }); | 255 | }); |
242 | } | 256 | } |
257 | |||
243 | Ty::simple(TypeCtor::Never) | 258 | Ty::simple(TypeCtor::Never) |
244 | } | 259 | } |
245 | Expr::Return { expr } => { | 260 | Expr::Return { expr } => { |
diff --git a/crates/ra_hir_ty/src/infer/pat.rs b/crates/ra_hir_ty/src/infer/pat.rs index 54ec870df..4006f595d 100644 --- a/crates/ra_hir_ty/src/infer/pat.rs +++ b/crates/ra_hir_ty/src/infer/pat.rs | |||
@@ -10,7 +10,7 @@ use hir_def::{ | |||
10 | FieldId, | 10 | FieldId, |
11 | }; | 11 | }; |
12 | use hir_expand::name::Name; | 12 | use hir_expand::name::Name; |
13 | use test_utils::tested_by; | 13 | use test_utils::mark; |
14 | 14 | ||
15 | use super::{BindingMode, Expectation, InferenceContext}; | 15 | use super::{BindingMode, Expectation, InferenceContext}; |
16 | use crate::{utils::variant_data, Substs, Ty, TypeCtor}; | 16 | use crate::{utils::variant_data, Substs, Ty, TypeCtor}; |
@@ -111,7 +111,7 @@ impl<'a> InferenceContext<'a> { | |||
111 | } | 111 | } |
112 | } | 112 | } |
113 | } else if let Pat::Ref { .. } = &body[pat] { | 113 | } else if let Pat::Ref { .. } = &body[pat] { |
114 | tested_by!(match_ergonomics_ref); | 114 | mark::hit!(match_ergonomics_ref); |
115 | // When you encounter a `&pat` pattern, reset to Move. | 115 | // When you encounter a `&pat` pattern, reset to Move. |
116 | // This is so that `w` is by value: `let (_, &w) = &(1, &2);` | 116 | // This is so that `w` is by value: `let (_, &w) = &(1, &2);` |
117 | default_bm = BindingMode::Move; | 117 | default_bm = BindingMode::Move; |
diff --git a/crates/ra_hir_ty/src/infer/unify.rs b/crates/ra_hir_ty/src/infer/unify.rs index ab0bc8b70..269495ca0 100644 --- a/crates/ra_hir_ty/src/infer/unify.rs +++ b/crates/ra_hir_ty/src/infer/unify.rs | |||
@@ -4,7 +4,7 @@ use std::borrow::Cow; | |||
4 | 4 | ||
5 | use ena::unify::{InPlaceUnificationTable, NoError, UnifyKey, UnifyValue}; | 5 | use ena::unify::{InPlaceUnificationTable, NoError, UnifyKey, UnifyValue}; |
6 | 6 | ||
7 | use test_utils::tested_by; | 7 | use test_utils::mark; |
8 | 8 | ||
9 | use super::{InferenceContext, Obligation}; | 9 | use super::{InferenceContext, Obligation}; |
10 | use crate::{ | 10 | use crate::{ |
@@ -313,7 +313,7 @@ impl InferenceTable { | |||
313 | // more than once | 313 | // more than once |
314 | for i in 0..3 { | 314 | for i in 0..3 { |
315 | if i > 0 { | 315 | if i > 0 { |
316 | tested_by!(type_var_resolves_to_int_var); | 316 | mark::hit!(type_var_resolves_to_int_var); |
317 | } | 317 | } |
318 | match &*ty { | 318 | match &*ty { |
319 | Ty::Infer(tv) => { | 319 | Ty::Infer(tv) => { |
@@ -342,7 +342,7 @@ impl InferenceTable { | |||
342 | Ty::Infer(tv) => { | 342 | Ty::Infer(tv) => { |
343 | let inner = tv.to_inner(); | 343 | let inner = tv.to_inner(); |
344 | if tv_stack.contains(&inner) { | 344 | if tv_stack.contains(&inner) { |
345 | tested_by!(type_var_cycles_resolve_as_possible); | 345 | mark::hit!(type_var_cycles_resolve_as_possible); |
346 | // recursive type | 346 | // recursive type |
347 | return tv.fallback_value(); | 347 | return tv.fallback_value(); |
348 | } | 348 | } |
@@ -369,7 +369,7 @@ impl InferenceTable { | |||
369 | Ty::Infer(tv) => { | 369 | Ty::Infer(tv) => { |
370 | let inner = tv.to_inner(); | 370 | let inner = tv.to_inner(); |
371 | if tv_stack.contains(&inner) { | 371 | if tv_stack.contains(&inner) { |
372 | tested_by!(type_var_cycles_resolve_completely); | 372 | mark::hit!(type_var_cycles_resolve_completely); |
373 | // recursive type | 373 | // recursive type |
374 | return tv.fallback_value(); | 374 | return tv.fallback_value(); |
375 | } | 375 | } |
diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs index ccc4348f4..c87ee06ce 100644 --- a/crates/ra_hir_ty/src/lib.rs +++ b/crates/ra_hir_ty/src/lib.rs | |||
@@ -42,7 +42,6 @@ pub mod expr; | |||
42 | mod tests; | 42 | mod tests; |
43 | #[cfg(test)] | 43 | #[cfg(test)] |
44 | mod test_db; | 44 | mod test_db; |
45 | mod marks; | ||
46 | mod _match; | 45 | mod _match; |
47 | 46 | ||
48 | use std::ops::Deref; | 47 | use std::ops::Deref; |
@@ -808,15 +807,13 @@ impl Ty { | |||
808 | } | 807 | } |
809 | } | 808 | } |
810 | 809 | ||
811 | /// If this is an `impl Trait` or `dyn Trait`, returns that trait. | 810 | /// If this is a `dyn Trait`, returns that trait. |
812 | pub fn inherent_trait(&self) -> Option<TraitId> { | 811 | pub fn dyn_trait(&self) -> Option<TraitId> { |
813 | match self { | 812 | match self { |
814 | Ty::Dyn(predicates) | Ty::Opaque(predicates) => { | 813 | Ty::Dyn(predicates) => predicates.iter().find_map(|pred| match pred { |
815 | predicates.iter().find_map(|pred| match pred { | 814 | GenericPredicate::Implemented(tr) => Some(tr.trait_), |
816 | GenericPredicate::Implemented(tr) => Some(tr.trait_), | 815 | _ => None, |
817 | _ => None, | 816 | }), |
818 | }) | ||
819 | } | ||
820 | _ => None, | 817 | _ => None, |
821 | } | 818 | } |
822 | } | 819 | } |
diff --git a/crates/ra_hir_ty/src/lower.rs b/crates/ra_hir_ty/src/lower.rs index 9ad6dbe07..35ac86a46 100644 --- a/crates/ra_hir_ty/src/lower.rs +++ b/crates/ra_hir_ty/src/lower.rs | |||
@@ -812,7 +812,7 @@ impl TraitEnvironment { | |||
812 | // add `Self: Trait<T1, T2, ...>` to the environment in trait | 812 | // add `Self: Trait<T1, T2, ...>` to the environment in trait |
813 | // function default implementations (and hypothetical code | 813 | // function default implementations (and hypothetical code |
814 | // inside consts or type aliases) | 814 | // inside consts or type aliases) |
815 | test_utils::tested_by!(trait_self_implements_self); | 815 | test_utils::mark::hit!(trait_self_implements_self); |
816 | let substs = Substs::type_params(db, trait_id); | 816 | let substs = Substs::type_params(db, trait_id); |
817 | let trait_ref = TraitRef { trait_: trait_id, substs }; | 817 | let trait_ref = TraitRef { trait_: trait_id, substs }; |
818 | let pred = GenericPredicate::Implemented(trait_ref); | 818 | let pred = GenericPredicate::Implemented(trait_ref); |
diff --git a/crates/ra_hir_ty/src/marks.rs b/crates/ra_hir_ty/src/marks.rs deleted file mode 100644 index a39740143..000000000 --- a/crates/ra_hir_ty/src/marks.rs +++ /dev/null | |||
@@ -1,12 +0,0 @@ | |||
1 | //! See test_utils/src/marks.rs | ||
2 | |||
3 | test_utils::marks!( | ||
4 | type_var_cycles_resolve_completely | ||
5 | type_var_cycles_resolve_as_possible | ||
6 | type_var_resolves_to_int_var | ||
7 | impl_self_type_match_without_receiver | ||
8 | match_ergonomics_ref | ||
9 | coerce_merge_fail_fallback | ||
10 | coerce_fn_reification | ||
11 | trait_self_implements_self | ||
12 | ); | ||
diff --git a/crates/ra_hir_ty/src/method_resolution.rs b/crates/ra_hir_ty/src/method_resolution.rs index 657284fd0..e19628fdf 100644 --- a/crates/ra_hir_ty/src/method_resolution.rs +++ b/crates/ra_hir_ty/src/method_resolution.rs | |||
@@ -408,8 +408,9 @@ fn iterate_trait_method_candidates<T>( | |||
408 | receiver_ty: Option<&Canonical<Ty>>, | 408 | receiver_ty: Option<&Canonical<Ty>>, |
409 | mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>, | 409 | mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>, |
410 | ) -> Option<T> { | 410 | ) -> Option<T> { |
411 | // if ty is `impl Trait` or `dyn Trait`, the trait doesn't need to be in scope | 411 | // if ty is `dyn Trait`, the trait doesn't need to be in scope |
412 | let inherent_trait = self_ty.value.inherent_trait().into_iter(); | 412 | let inherent_trait = |
413 | self_ty.value.dyn_trait().into_iter().flat_map(|t| all_super_traits(db.upcast(), t)); | ||
413 | let env_traits = if let Ty::Placeholder(_) = self_ty.value { | 414 | let env_traits = if let Ty::Placeholder(_) = self_ty.value { |
414 | // if we have `T: Trait` in the param env, the trait doesn't need to be in scope | 415 | // if we have `T: Trait` in the param env, the trait doesn't need to be in scope |
415 | env.trait_predicates_for_self_ty(&self_ty.value) | 416 | env.trait_predicates_for_self_ty(&self_ty.value) |
@@ -468,7 +469,7 @@ fn iterate_inherent_methods<T>( | |||
468 | // already happens in `is_valid_candidate` above; if not, we | 469 | // already happens in `is_valid_candidate` above; if not, we |
469 | // check it here | 470 | // check it here |
470 | if receiver_ty.is_none() && inherent_impl_substs(db, impl_def, self_ty).is_none() { | 471 | if receiver_ty.is_none() && inherent_impl_substs(db, impl_def, self_ty).is_none() { |
471 | test_utils::tested_by!(impl_self_type_match_without_receiver); | 472 | test_utils::mark::hit!(impl_self_type_match_without_receiver); |
472 | continue; | 473 | continue; |
473 | } | 474 | } |
474 | if let Some(result) = callback(&self_ty.value, item) { | 475 | if let Some(result) = callback(&self_ty.value, item) { |
@@ -601,11 +602,6 @@ pub fn implements_trait( | |||
601 | krate: CrateId, | 602 | krate: CrateId, |
602 | trait_: TraitId, | 603 | trait_: TraitId, |
603 | ) -> bool { | 604 | ) -> bool { |
604 | if ty.value.inherent_trait() == Some(trait_) { | ||
605 | // FIXME this is a bit of a hack, since Chalk should say the same thing | ||
606 | // anyway, but currently Chalk doesn't implement `dyn/impl Trait` yet | ||
607 | return true; | ||
608 | } | ||
609 | let goal = generic_implements_goal(db, env, trait_, ty.clone()); | 605 | let goal = generic_implements_goal(db, env, trait_, ty.clone()); |
610 | let solution = db.trait_solve(krate, goal); | 606 | let solution = db.trait_solve(krate, goal); |
611 | 607 | ||
diff --git a/crates/ra_hir_ty/src/tests/coercion.rs b/crates/ra_hir_ty/src/tests/coercion.rs index 6dc4b2cd1..2cc4f4bf9 100644 --- a/crates/ra_hir_ty/src/tests/coercion.rs +++ b/crates/ra_hir_ty/src/tests/coercion.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use super::infer_with_mismatches; | 1 | use super::infer_with_mismatches; |
2 | use insta::assert_snapshot; | 2 | use insta::assert_snapshot; |
3 | use test_utils::covers; | 3 | use test_utils::mark; |
4 | 4 | ||
5 | // Infer with some common definitions and impls. | 5 | // Infer with some common definitions and impls. |
6 | fn infer(source: &str) -> String { | 6 | fn infer(source: &str) -> String { |
@@ -339,7 +339,7 @@ fn test(i: i32) { | |||
339 | 339 | ||
340 | #[test] | 340 | #[test] |
341 | fn coerce_merge_one_by_one1() { | 341 | fn coerce_merge_one_by_one1() { |
342 | covers!(coerce_merge_fail_fallback); | 342 | mark::check!(coerce_merge_fail_fallback); |
343 | 343 | ||
344 | assert_snapshot!( | 344 | assert_snapshot!( |
345 | infer(r#" | 345 | infer(r#" |
@@ -547,7 +547,7 @@ fn test() { | |||
547 | 547 | ||
548 | #[test] | 548 | #[test] |
549 | fn coerce_fn_items_in_match_arms() { | 549 | fn coerce_fn_items_in_match_arms() { |
550 | covers!(coerce_fn_reification); | 550 | mark::check!(coerce_fn_reification); |
551 | assert_snapshot!( | 551 | assert_snapshot!( |
552 | infer_with_mismatches(r#" | 552 | infer_with_mismatches(r#" |
553 | fn foo1(x: u32) -> isize { 1 } | 553 | fn foo1(x: u32) -> isize { 1 } |
diff --git a/crates/ra_hir_ty/src/tests/method_resolution.rs b/crates/ra_hir_ty/src/tests/method_resolution.rs index 67f964ab5..558a70022 100644 --- a/crates/ra_hir_ty/src/tests/method_resolution.rs +++ b/crates/ra_hir_ty/src/tests/method_resolution.rs | |||
@@ -984,7 +984,7 @@ fn test() { S2.into()<|>; } | |||
984 | 984 | ||
985 | #[test] | 985 | #[test] |
986 | fn method_resolution_overloaded_method() { | 986 | fn method_resolution_overloaded_method() { |
987 | test_utils::covers!(impl_self_type_match_without_receiver); | 987 | test_utils::mark::check!(impl_self_type_match_without_receiver); |
988 | let t = type_at( | 988 | let t = type_at( |
989 | r#" | 989 | r#" |
990 | //- main.rs | 990 | //- main.rs |
@@ -1096,3 +1096,34 @@ fn test() { (S {}).method()<|>; } | |||
1096 | ); | 1096 | ); |
1097 | assert_eq!(t, "()"); | 1097 | assert_eq!(t, "()"); |
1098 | } | 1098 | } |
1099 | |||
1100 | #[test] | ||
1101 | fn dyn_trait_super_trait_not_in_scope() { | ||
1102 | assert_snapshot!( | ||
1103 | infer(r#" | ||
1104 | mod m { | ||
1105 | pub trait SuperTrait { | ||
1106 | fn foo(&self) -> u32 { 0 } | ||
1107 | } | ||
1108 | } | ||
1109 | trait Trait: m::SuperTrait {} | ||
1110 | |||
1111 | struct S; | ||
1112 | impl m::SuperTrait for S {} | ||
1113 | impl Trait for S {} | ||
1114 | |||
1115 | fn test(d: &dyn Trait) { | ||
1116 | d.foo(); | ||
1117 | } | ||
1118 | "#), | ||
1119 | @r###" | ||
1120 | 52..56 'self': &Self | ||
1121 | 65..70 '{ 0 }': u32 | ||
1122 | 67..68 '0': u32 | ||
1123 | 177..178 'd': &dyn Trait | ||
1124 | 192..208 '{ ...o(); }': () | ||
1125 | 198..199 'd': &dyn Trait | ||
1126 | 198..205 'd.foo()': u32 | ||
1127 | "### | ||
1128 | ); | ||
1129 | } | ||
diff --git a/crates/ra_hir_ty/src/tests/patterns.rs b/crates/ra_hir_ty/src/tests/patterns.rs index d83ff5e0e..0c5f972a2 100644 --- a/crates/ra_hir_ty/src/tests/patterns.rs +++ b/crates/ra_hir_ty/src/tests/patterns.rs | |||
@@ -1,5 +1,5 @@ | |||
1 | use insta::assert_snapshot; | 1 | use insta::assert_snapshot; |
2 | use test_utils::covers; | 2 | use test_utils::mark; |
3 | 3 | ||
4 | use super::{infer, infer_with_mismatches}; | 4 | use super::{infer, infer_with_mismatches}; |
5 | 5 | ||
@@ -197,7 +197,7 @@ fn test() { | |||
197 | 197 | ||
198 | #[test] | 198 | #[test] |
199 | fn infer_pattern_match_ergonomics_ref() { | 199 | fn infer_pattern_match_ergonomics_ref() { |
200 | covers!(match_ergonomics_ref); | 200 | mark::check!(match_ergonomics_ref); |
201 | assert_snapshot!( | 201 | assert_snapshot!( |
202 | infer(r#" | 202 | infer(r#" |
203 | fn test() { | 203 | fn test() { |
diff --git a/crates/ra_hir_ty/src/tests/regression.rs b/crates/ra_hir_ty/src/tests/regression.rs index 115ad8328..1f004bd63 100644 --- a/crates/ra_hir_ty/src/tests/regression.rs +++ b/crates/ra_hir_ty/src/tests/regression.rs | |||
@@ -1,9 +1,10 @@ | |||
1 | use insta::assert_snapshot; | 1 | use insta::assert_snapshot; |
2 | use test_utils::covers; | 2 | use ra_db::fixture::WithFixture; |
3 | use test_utils::mark; | ||
3 | 4 | ||
4 | use super::infer; | ||
5 | use crate::test_db::TestDB; | 5 | use crate::test_db::TestDB; |
6 | use ra_db::fixture::WithFixture; | 6 | |
7 | use super::infer; | ||
7 | 8 | ||
8 | #[test] | 9 | #[test] |
9 | fn bug_484() { | 10 | fn bug_484() { |
@@ -89,8 +90,8 @@ fn quux() { | |||
89 | 90 | ||
90 | #[test] | 91 | #[test] |
91 | fn recursive_vars() { | 92 | fn recursive_vars() { |
92 | covers!(type_var_cycles_resolve_completely); | 93 | mark::check!(type_var_cycles_resolve_completely); |
93 | covers!(type_var_cycles_resolve_as_possible); | 94 | mark::check!(type_var_cycles_resolve_as_possible); |
94 | assert_snapshot!( | 95 | assert_snapshot!( |
95 | infer(r#" | 96 | infer(r#" |
96 | fn test() { | 97 | fn test() { |
@@ -112,8 +113,6 @@ fn test() { | |||
112 | 113 | ||
113 | #[test] | 114 | #[test] |
114 | fn recursive_vars_2() { | 115 | fn recursive_vars_2() { |
115 | covers!(type_var_cycles_resolve_completely); | ||
116 | covers!(type_var_cycles_resolve_as_possible); | ||
117 | assert_snapshot!( | 116 | assert_snapshot!( |
118 | infer(r#" | 117 | infer(r#" |
119 | fn test() { | 118 | fn test() { |
@@ -170,7 +169,7 @@ fn write() { | |||
170 | 169 | ||
171 | #[test] | 170 | #[test] |
172 | fn infer_std_crash_2() { | 171 | fn infer_std_crash_2() { |
173 | covers!(type_var_resolves_to_int_var); | 172 | mark::check!(type_var_resolves_to_int_var); |
174 | // caused "equating two type variables, ...", taken from std | 173 | // caused "equating two type variables, ...", taken from std |
175 | assert_snapshot!( | 174 | assert_snapshot!( |
176 | infer(r#" | 175 | infer(r#" |
@@ -564,6 +563,37 @@ fn main() { | |||
564 | } | 563 | } |
565 | 564 | ||
566 | #[test] | 565 | #[test] |
566 | fn issue_4465_dollar_crate_at_type() { | ||
567 | assert_snapshot!( | ||
568 | infer(r#" | ||
569 | pub struct Foo {} | ||
570 | pub fn anything<T>() -> T { | ||
571 | loop {} | ||
572 | } | ||
573 | macro_rules! foo { | ||
574 | () => {{ | ||
575 | let r: $crate::Foo = anything(); | ||
576 | r | ||
577 | }}; | ||
578 | } | ||
579 | fn main() { | ||
580 | let _a = foo!(); | ||
581 | } | ||
582 | "#), @r###" | ||
583 | 45..60 '{ loop {} }': T | ||
584 | 51..58 'loop {}': ! | ||
585 | 56..58 '{}': () | ||
586 | !0..31 '{letr:...g();r}': Foo | ||
587 | !4..5 'r': Foo | ||
588 | !18..26 'anything': fn anything<Foo>() -> Foo | ||
589 | !18..28 'anything()': Foo | ||
590 | !29..30 'r': Foo | ||
591 | 164..188 '{ ...!(); }': () | ||
592 | 174..176 '_a': Foo | ||
593 | "###); | ||
594 | } | ||
595 | |||
596 | #[test] | ||
567 | fn issue_4053_diesel_where_clauses() { | 597 | fn issue_4053_diesel_where_clauses() { |
568 | assert_snapshot!( | 598 | assert_snapshot!( |
569 | infer(r#" | 599 | infer(r#" |
diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs index 72122c070..fd2208af2 100644 --- a/crates/ra_hir_ty/src/tests/simple.rs +++ b/crates/ra_hir_ty/src/tests/simple.rs | |||
@@ -1860,3 +1860,66 @@ fn test() { | |||
1860 | "### | 1860 | "### |
1861 | ); | 1861 | ); |
1862 | } | 1862 | } |
1863 | |||
1864 | #[test] | ||
1865 | fn infer_loop_break_with_val() { | ||
1866 | assert_snapshot!( | ||
1867 | infer(r#" | ||
1868 | enum Option<T> { Some(T), None } | ||
1869 | use Option::*; | ||
1870 | |||
1871 | fn test() { | ||
1872 | let x = loop { | ||
1873 | if false { | ||
1874 | break None; | ||
1875 | } | ||
1876 | |||
1877 | break Some(true); | ||
1878 | }; | ||
1879 | } | ||
1880 | "#), | ||
1881 | @r###" | ||
1882 | 60..169 '{ ... }; }': () | ||
1883 | 70..71 'x': Option<bool> | ||
1884 | 74..166 'loop {... }': Option<bool> | ||
1885 | 79..166 '{ ... }': () | ||
1886 | 89..133 'if fal... }': () | ||
1887 | 92..97 'false': bool | ||
1888 | 98..133 '{ ... }': () | ||
1889 | 112..122 'break None': ! | ||
1890 | 118..122 'None': Option<bool> | ||
1891 | 143..159 'break ...(true)': ! | ||
1892 | 149..153 'Some': Some<bool>(bool) -> Option<bool> | ||
1893 | 149..159 'Some(true)': Option<bool> | ||
1894 | 154..158 'true': bool | ||
1895 | "### | ||
1896 | ); | ||
1897 | } | ||
1898 | |||
1899 | #[test] | ||
1900 | fn infer_loop_break_without_val() { | ||
1901 | assert_snapshot!( | ||
1902 | infer(r#" | ||
1903 | enum Option<T> { Some(T), None } | ||
1904 | use Option::*; | ||
1905 | |||
1906 | fn test() { | ||
1907 | let x = loop { | ||
1908 | if false { | ||
1909 | break; | ||
1910 | } | ||
1911 | }; | ||
1912 | } | ||
1913 | "#), | ||
1914 | @r###" | ||
1915 | 60..137 '{ ... }; }': () | ||
1916 | 70..71 'x': () | ||
1917 | 74..134 'loop {... }': () | ||
1918 | 79..134 '{ ... }': () | ||
1919 | 89..128 'if fal... }': () | ||
1920 | 92..97 'false': bool | ||
1921 | 98..128 '{ ... }': () | ||
1922 | 112..117 'break': ! | ||
1923 | "### | ||
1924 | ); | ||
1925 | } | ||
diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs index 9d32cbc7a..34f4b9039 100644 --- a/crates/ra_hir_ty/src/tests/traits.rs +++ b/crates/ra_hir_ty/src/tests/traits.rs | |||
@@ -1,10 +1,11 @@ | |||
1 | use insta::assert_snapshot; | 1 | use insta::assert_snapshot; |
2 | |||
3 | use ra_db::fixture::WithFixture; | 2 | use ra_db::fixture::WithFixture; |
3 | use test_utils::mark; | ||
4 | 4 | ||
5 | use super::{infer, infer_with_mismatches, type_at, type_at_pos}; | ||
6 | use crate::test_db::TestDB; | 5 | use crate::test_db::TestDB; |
7 | 6 | ||
7 | use super::{infer, infer_with_mismatches, type_at, type_at_pos}; | ||
8 | |||
8 | #[test] | 9 | #[test] |
9 | fn infer_await() { | 10 | fn infer_await() { |
10 | let (db, pos) = TestDB::with_position( | 11 | let (db, pos) = TestDB::with_position( |
@@ -301,7 +302,7 @@ fn test() { | |||
301 | 302 | ||
302 | #[test] | 303 | #[test] |
303 | fn trait_default_method_self_bound_implements_trait() { | 304 | fn trait_default_method_self_bound_implements_trait() { |
304 | test_utils::covers!(trait_self_implements_self); | 305 | mark::check!(trait_self_implements_self); |
305 | assert_snapshot!( | 306 | assert_snapshot!( |
306 | infer(r#" | 307 | infer(r#" |
307 | trait Trait { | 308 | trait Trait { |
@@ -324,7 +325,6 @@ trait Trait { | |||
324 | 325 | ||
325 | #[test] | 326 | #[test] |
326 | fn trait_default_method_self_bound_implements_super_trait() { | 327 | fn trait_default_method_self_bound_implements_super_trait() { |
327 | test_utils::covers!(trait_self_implements_self); | ||
328 | assert_snapshot!( | 328 | assert_snapshot!( |
329 | infer(r#" | 329 | infer(r#" |
330 | trait SuperTrait { | 330 | trait SuperTrait { |
@@ -1617,6 +1617,138 @@ fn test<F: FnOnce(u32, u64) -> u128>(f: F) { | |||
1617 | } | 1617 | } |
1618 | 1618 | ||
1619 | #[test] | 1619 | #[test] |
1620 | fn fn_ptr_and_item() { | ||
1621 | assert_snapshot!( | ||
1622 | infer(r#" | ||
1623 | #[lang="fn_once"] | ||
1624 | trait FnOnce<Args> { | ||
1625 | type Output; | ||
1626 | |||
1627 | fn call_once(self, args: Args) -> Self::Output; | ||
1628 | } | ||
1629 | |||
1630 | trait Foo<T> { | ||
1631 | fn foo(&self) -> T; | ||
1632 | } | ||
1633 | |||
1634 | struct Bar<T>(T); | ||
1635 | |||
1636 | impl<A1, R, F: FnOnce(A1) -> R> Foo<(A1, R)> for Bar<F> { | ||
1637 | fn foo(&self) -> (A1, R) {} | ||
1638 | } | ||
1639 | |||
1640 | enum Opt<T> { None, Some(T) } | ||
1641 | impl<T> Opt<T> { | ||
1642 | fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Opt<U> {} | ||
1643 | } | ||
1644 | |||
1645 | fn test() { | ||
1646 | let bar: Bar<fn(u8) -> u32>; | ||
1647 | bar.foo(); | ||
1648 | |||
1649 | let opt: Opt<u8>; | ||
1650 | let f: fn(u8) -> u32; | ||
1651 | opt.map(f); | ||
1652 | } | ||
1653 | "#), | ||
1654 | @r###" | ||
1655 | 75..79 'self': Self | ||
1656 | 81..85 'args': Args | ||
1657 | 140..144 'self': &Self | ||
1658 | 244..248 'self': &Bar<F> | ||
1659 | 261..263 '{}': () | ||
1660 | 347..351 'self': Opt<T> | ||
1661 | 353..354 'f': F | ||
1662 | 369..371 '{}': () | ||
1663 | 385..501 '{ ...(f); }': () | ||
1664 | 395..398 'bar': Bar<fn(u8) -> u32> | ||
1665 | 424..427 'bar': Bar<fn(u8) -> u32> | ||
1666 | 424..433 'bar.foo()': {unknown} | ||
1667 | 444..447 'opt': Opt<u8> | ||
1668 | 466..467 'f': fn(u8) -> u32 | ||
1669 | 488..491 'opt': Opt<u8> | ||
1670 | 488..498 'opt.map(f)': Opt<FnOnce::Output<fn(u8) -> u32, (u8,)>> | ||
1671 | 496..497 'f': fn(u8) -> u32 | ||
1672 | "### | ||
1673 | ); | ||
1674 | } | ||
1675 | |||
1676 | #[test] | ||
1677 | fn fn_trait_deref_with_ty_default() { | ||
1678 | assert_snapshot!( | ||
1679 | infer(r#" | ||
1680 | #[lang = "deref"] | ||
1681 | trait Deref { | ||
1682 | type Target; | ||
1683 | |||
1684 | fn deref(&self) -> &Self::Target; | ||
1685 | } | ||
1686 | |||
1687 | #[lang="fn_once"] | ||
1688 | trait FnOnce<Args> { | ||
1689 | type Output; | ||
1690 | |||
1691 | fn call_once(self, args: Args) -> Self::Output; | ||
1692 | } | ||
1693 | |||
1694 | struct Foo; | ||
1695 | |||
1696 | impl Foo { | ||
1697 | fn foo(&self) -> usize {} | ||
1698 | } | ||
1699 | |||
1700 | struct Lazy<T, F = fn() -> T>(F); | ||
1701 | |||
1702 | impl<T, F> Lazy<T, F> { | ||
1703 | pub fn new(f: F) -> Lazy<T, F> {} | ||
1704 | } | ||
1705 | |||
1706 | impl<T, F: FnOnce() -> T> Deref for Lazy<T, F> { | ||
1707 | type Target = T; | ||
1708 | } | ||
1709 | |||
1710 | fn test() { | ||
1711 | let lazy1: Lazy<Foo, _> = Lazy::new(|| Foo); | ||
1712 | let r1 = lazy1.foo(); | ||
1713 | |||
1714 | fn make_foo_fn() -> Foo {} | ||
1715 | let make_foo_fn_ptr: fn() -> Foo = make_foo_fn; | ||
1716 | let lazy2: Lazy<Foo, _> = Lazy::new(make_foo_fn_ptr); | ||
1717 | let r2 = lazy2.foo(); | ||
1718 | } | ||
1719 | "#), | ||
1720 | @r###" | ||
1721 | 65..69 'self': &Self | ||
1722 | 166..170 'self': Self | ||
1723 | 172..176 'args': Args | ||
1724 | 240..244 'self': &Foo | ||
1725 | 255..257 '{}': () | ||
1726 | 335..336 'f': F | ||
1727 | 355..357 '{}': () | ||
1728 | 444..690 '{ ...o(); }': () | ||
1729 | 454..459 'lazy1': Lazy<Foo, fn() -> T> | ||
1730 | 476..485 'Lazy::new': fn new<Foo, fn() -> T>(fn() -> T) -> Lazy<Foo, fn() -> T> | ||
1731 | 476..493 'Lazy::...| Foo)': Lazy<Foo, fn() -> T> | ||
1732 | 486..492 '|| Foo': || -> T | ||
1733 | 489..492 'Foo': Foo | ||
1734 | 503..505 'r1': {unknown} | ||
1735 | 508..513 'lazy1': Lazy<Foo, fn() -> T> | ||
1736 | 508..519 'lazy1.foo()': {unknown} | ||
1737 | 561..576 'make_foo_fn_ptr': fn() -> Foo | ||
1738 | 592..603 'make_foo_fn': fn make_foo_fn() -> Foo | ||
1739 | 613..618 'lazy2': Lazy<Foo, fn() -> T> | ||
1740 | 635..644 'Lazy::new': fn new<Foo, fn() -> T>(fn() -> T) -> Lazy<Foo, fn() -> T> | ||
1741 | 635..661 'Lazy::...n_ptr)': Lazy<Foo, fn() -> T> | ||
1742 | 645..660 'make_foo_fn_ptr': fn() -> Foo | ||
1743 | 671..673 'r2': {unknown} | ||
1744 | 676..681 'lazy2': Lazy<Foo, fn() -> T> | ||
1745 | 676..687 'lazy2.foo()': {unknown} | ||
1746 | 550..552 '{}': () | ||
1747 | "### | ||
1748 | ); | ||
1749 | } | ||
1750 | |||
1751 | #[test] | ||
1620 | fn closure_1() { | 1752 | fn closure_1() { |
1621 | assert_snapshot!( | 1753 | assert_snapshot!( |
1622 | infer(r#" | 1754 | infer(r#" |
diff --git a/crates/ra_ide/src/call_info.rs b/crates/ra_ide/src/call_info.rs index 780a03c13..aa039e6fc 100644 --- a/crates/ra_ide/src/call_info.rs +++ b/crates/ra_ide/src/call_info.rs | |||
@@ -5,7 +5,7 @@ use ra_syntax::{ | |||
5 | ast::{self, ArgListOwner}, | 5 | ast::{self, ArgListOwner}, |
6 | match_ast, AstNode, SyntaxNode, SyntaxToken, | 6 | match_ast, AstNode, SyntaxNode, SyntaxToken, |
7 | }; | 7 | }; |
8 | use test_utils::tested_by; | 8 | use test_utils::mark; |
9 | 9 | ||
10 | use crate::{CallInfo, FilePosition, FunctionSignature}; | 10 | use crate::{CallInfo, FilePosition, FunctionSignature}; |
11 | 11 | ||
@@ -84,7 +84,7 @@ fn call_info_for_token(sema: &Semantics<RootDatabase>, token: SyntaxToken) -> Op | |||
84 | 84 | ||
85 | let arg_list_range = arg_list.syntax().text_range(); | 85 | let arg_list_range = arg_list.syntax().text_range(); |
86 | if !arg_list_range.contains_inclusive(token.text_range().start()) { | 86 | if !arg_list_range.contains_inclusive(token.text_range().start()) { |
87 | tested_by!(call_info_bad_offset); | 87 | mark::hit!(call_info_bad_offset); |
88 | return None; | 88 | return None; |
89 | } | 89 | } |
90 | 90 | ||
@@ -213,7 +213,7 @@ impl CallInfo { | |||
213 | 213 | ||
214 | #[cfg(test)] | 214 | #[cfg(test)] |
215 | mod tests { | 215 | mod tests { |
216 | use test_utils::covers; | 216 | use test_utils::mark; |
217 | 217 | ||
218 | use crate::mock_analysis::single_file_with_position; | 218 | use crate::mock_analysis::single_file_with_position; |
219 | 219 | ||
@@ -529,7 +529,7 @@ By default this method stops actor's `Context`."# | |||
529 | 529 | ||
530 | #[test] | 530 | #[test] |
531 | fn call_info_bad_offset() { | 531 | fn call_info_bad_offset() { |
532 | covers!(call_info_bad_offset); | 532 | mark::check!(call_info_bad_offset); |
533 | let (analysis, position) = single_file_with_position( | 533 | let (analysis, position) = single_file_with_position( |
534 | r#"fn foo(x: u32, y: u32) -> u32 {x + y} | 534 | r#"fn foo(x: u32, y: u32) -> u32 {x + y} |
535 | fn bar() { foo <|> (3, ); }"#, | 535 | fn bar() { foo <|> (3, ); }"#, |
diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs index 8bdc43b1a..191300704 100644 --- a/crates/ra_ide/src/completion.rs +++ b/crates/ra_ide/src/completion.rs | |||
@@ -59,8 +59,8 @@ pub use crate::completion::{ | |||
59 | /// with ordering of completions (currently this is done by the client). | 59 | /// with ordering of completions (currently this is done by the client). |
60 | pub(crate) fn completions( | 60 | pub(crate) fn completions( |
61 | db: &RootDatabase, | 61 | db: &RootDatabase, |
62 | position: FilePosition, | ||
63 | config: &CompletionConfig, | 62 | config: &CompletionConfig, |
63 | position: FilePosition, | ||
64 | ) -> Option<Completions> { | 64 | ) -> Option<Completions> { |
65 | let ctx = CompletionContext::new(db, position, config)?; | 65 | let ctx = CompletionContext::new(db, position, config)?; |
66 | 66 | ||
diff --git a/crates/ra_ide/src/completion/complete_qualified_path.rs b/crates/ra_ide/src/completion/complete_qualified_path.rs index db7430454..02ac0166b 100644 --- a/crates/ra_ide/src/completion/complete_qualified_path.rs +++ b/crates/ra_ide/src/completion/complete_qualified_path.rs | |||
@@ -3,7 +3,7 @@ | |||
3 | use hir::{Adt, HasVisibility, PathResolution, ScopeDef}; | 3 | use hir::{Adt, HasVisibility, PathResolution, ScopeDef}; |
4 | use ra_syntax::AstNode; | 4 | use ra_syntax::AstNode; |
5 | use rustc_hash::FxHashSet; | 5 | use rustc_hash::FxHashSet; |
6 | use test_utils::tested_by; | 6 | use test_utils::mark; |
7 | 7 | ||
8 | use crate::completion::{CompletionContext, Completions}; | 8 | use crate::completion::{CompletionContext, Completions}; |
9 | 9 | ||
@@ -40,7 +40,7 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
40 | if let Some(name_ref) = ctx.name_ref_syntax.as_ref() { | 40 | if let Some(name_ref) = ctx.name_ref_syntax.as_ref() { |
41 | if name_ref.syntax().text() == name.to_string().as_str() { | 41 | if name_ref.syntax().text() == name.to_string().as_str() { |
42 | // for `use self::foo<|>`, don't suggest `foo` as a completion | 42 | // for `use self::foo<|>`, don't suggest `foo` as a completion |
43 | tested_by!(dont_complete_current_use); | 43 | mark::hit!(dont_complete_current_use); |
44 | continue; | 44 | continue; |
45 | } | 45 | } |
46 | } | 46 | } |
@@ -147,7 +147,7 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
147 | 147 | ||
148 | #[cfg(test)] | 148 | #[cfg(test)] |
149 | mod tests { | 149 | mod tests { |
150 | use test_utils::covers; | 150 | use test_utils::mark; |
151 | 151 | ||
152 | use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; | 152 | use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; |
153 | use insta::assert_debug_snapshot; | 153 | use insta::assert_debug_snapshot; |
@@ -158,7 +158,7 @@ mod tests { | |||
158 | 158 | ||
159 | #[test] | 159 | #[test] |
160 | fn dont_complete_current_use() { | 160 | fn dont_complete_current_use() { |
161 | covers!(dont_complete_current_use); | 161 | mark::check!(dont_complete_current_use); |
162 | let completions = do_completion(r"use self::foo<|>;", CompletionKind::Reference); | 162 | let completions = do_completion(r"use self::foo<|>;", CompletionKind::Reference); |
163 | assert!(completions.is_empty()); | 163 | assert!(completions.is_empty()); |
164 | } | 164 | } |
diff --git a/crates/ra_ide/src/completion/complete_unqualified_path.rs b/crates/ra_ide/src/completion/complete_unqualified_path.rs index bd40af1cb..db791660a 100644 --- a/crates/ra_ide/src/completion/complete_unqualified_path.rs +++ b/crates/ra_ide/src/completion/complete_unqualified_path.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | //! Completion of names from the current scope, e.g. locals and imported items. | 1 | //! Completion of names from the current scope, e.g. locals and imported items. |
2 | 2 | ||
3 | use hir::ScopeDef; | 3 | use hir::ScopeDef; |
4 | use test_utils::tested_by; | 4 | use test_utils::mark; |
5 | 5 | ||
6 | use crate::completion::{CompletionContext, Completions}; | 6 | use crate::completion::{CompletionContext, Completions}; |
7 | use hir::{Adt, ModuleDef, Type}; | 7 | use hir::{Adt, ModuleDef, Type}; |
@@ -30,7 +30,7 @@ pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC | |||
30 | if ctx.use_item_syntax.is_some() { | 30 | if ctx.use_item_syntax.is_some() { |
31 | if let (ScopeDef::Unknown, Some(name_ref)) = (&res, &ctx.name_ref_syntax) { | 31 | if let (ScopeDef::Unknown, Some(name_ref)) = (&res, &ctx.name_ref_syntax) { |
32 | if name_ref.syntax().text() == name.to_string().as_str() { | 32 | if name_ref.syntax().text() == name.to_string().as_str() { |
33 | tested_by!(self_fulfilling_completion); | 33 | mark::hit!(self_fulfilling_completion); |
34 | return; | 34 | return; |
35 | } | 35 | } |
36 | } | 36 | } |
@@ -66,7 +66,7 @@ fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &T | |||
66 | #[cfg(test)] | 66 | #[cfg(test)] |
67 | mod tests { | 67 | mod tests { |
68 | use insta::assert_debug_snapshot; | 68 | use insta::assert_debug_snapshot; |
69 | use test_utils::covers; | 69 | use test_utils::mark; |
70 | 70 | ||
71 | use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; | 71 | use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; |
72 | 72 | ||
@@ -76,7 +76,7 @@ mod tests { | |||
76 | 76 | ||
77 | #[test] | 77 | #[test] |
78 | fn self_fulfilling_completion() { | 78 | fn self_fulfilling_completion() { |
79 | covers!(self_fulfilling_completion); | 79 | mark::check!(self_fulfilling_completion); |
80 | assert_debug_snapshot!( | 80 | assert_debug_snapshot!( |
81 | do_reference_completion( | 81 | do_reference_completion( |
82 | r#" | 82 | r#" |
diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs index 077cf9647..440ffa31d 100644 --- a/crates/ra_ide/src/completion/presentation.rs +++ b/crates/ra_ide/src/completion/presentation.rs | |||
@@ -3,7 +3,7 @@ | |||
3 | use hir::{Docs, HasAttrs, HasSource, HirDisplay, ModPath, ScopeDef, StructKind, Type}; | 3 | use hir::{Docs, HasAttrs, HasSource, HirDisplay, ModPath, ScopeDef, StructKind, Type}; |
4 | use ra_syntax::ast::NameOwner; | 4 | use ra_syntax::ast::NameOwner; |
5 | use stdx::SepBy; | 5 | use stdx::SepBy; |
6 | use test_utils::tested_by; | 6 | use test_utils::mark; |
7 | 7 | ||
8 | use crate::{ | 8 | use crate::{ |
9 | completion::{ | 9 | completion::{ |
@@ -121,7 +121,7 @@ impl Completions { | |||
121 | _ => false, | 121 | _ => false, |
122 | }; | 122 | }; |
123 | if has_non_default_type_params { | 123 | if has_non_default_type_params { |
124 | tested_by!(inserts_angle_brackets_for_generics); | 124 | mark::hit!(inserts_angle_brackets_for_generics); |
125 | completion_item = completion_item | 125 | completion_item = completion_item |
126 | .lookup_by(local_name.clone()) | 126 | .lookup_by(local_name.clone()) |
127 | .label(format!("{}<…>", local_name)) | 127 | .label(format!("{}<…>", local_name)) |
@@ -176,7 +176,7 @@ impl Completions { | |||
176 | } | 176 | } |
177 | None if needs_bang => builder.insert_text(format!("{}!", name)), | 177 | None if needs_bang => builder.insert_text(format!("{}!", name)), |
178 | _ => { | 178 | _ => { |
179 | tested_by!(dont_insert_macro_call_parens_unncessary); | 179 | mark::hit!(dont_insert_macro_call_parens_unncessary); |
180 | builder.insert_text(name) | 180 | builder.insert_text(name) |
181 | } | 181 | } |
182 | }; | 182 | }; |
@@ -330,14 +330,14 @@ pub(crate) fn compute_score( | |||
330 | // FIXME: this should not fall back to string equality. | 330 | // FIXME: this should not fall back to string equality. |
331 | let ty = &ty.display(ctx.db).to_string(); | 331 | let ty = &ty.display(ctx.db).to_string(); |
332 | let (active_name, active_type) = if let Some(record_field) = &ctx.record_field_syntax { | 332 | let (active_name, active_type) = if let Some(record_field) = &ctx.record_field_syntax { |
333 | tested_by!(test_struct_field_completion_in_record_lit); | 333 | mark::hit!(test_struct_field_completion_in_record_lit); |
334 | let (struct_field, _local) = ctx.sema.resolve_record_field(record_field)?; | 334 | let (struct_field, _local) = ctx.sema.resolve_record_field(record_field)?; |
335 | ( | 335 | ( |
336 | struct_field.name(ctx.db).to_string(), | 336 | struct_field.name(ctx.db).to_string(), |
337 | struct_field.signature_ty(ctx.db).display(ctx.db).to_string(), | 337 | struct_field.signature_ty(ctx.db).display(ctx.db).to_string(), |
338 | ) | 338 | ) |
339 | } else if let Some(active_parameter) = &ctx.active_parameter { | 339 | } else if let Some(active_parameter) = &ctx.active_parameter { |
340 | tested_by!(test_struct_field_completion_in_func_call); | 340 | mark::hit!(test_struct_field_completion_in_func_call); |
341 | (active_parameter.name.clone(), active_parameter.ty.clone()) | 341 | (active_parameter.name.clone(), active_parameter.ty.clone()) |
342 | } else { | 342 | } else { |
343 | return None; | 343 | return None; |
@@ -398,7 +398,7 @@ impl Builder { | |||
398 | None => return self, | 398 | None => return self, |
399 | }; | 399 | }; |
400 | // If not an import, add parenthesis automatically. | 400 | // If not an import, add parenthesis automatically. |
401 | tested_by!(inserts_parens_for_function_calls); | 401 | mark::hit!(inserts_parens_for_function_calls); |
402 | 402 | ||
403 | let (snippet, label) = if params.is_empty() { | 403 | let (snippet, label) = if params.is_empty() { |
404 | (format!("{}()$0", name), format!("{}()", name)) | 404 | (format!("{}()$0", name), format!("{}()", name)) |
@@ -457,7 +457,7 @@ fn guess_macro_braces(macro_name: &str, docs: &str) -> (&'static str, &'static s | |||
457 | #[cfg(test)] | 457 | #[cfg(test)] |
458 | mod tests { | 458 | mod tests { |
459 | use insta::assert_debug_snapshot; | 459 | use insta::assert_debug_snapshot; |
460 | use test_utils::covers; | 460 | use test_utils::mark; |
461 | 461 | ||
462 | use crate::completion::{ | 462 | use crate::completion::{ |
463 | test_utils::{do_completion, do_completion_with_options}, | 463 | test_utils::{do_completion, do_completion_with_options}, |
@@ -607,7 +607,7 @@ mod tests { | |||
607 | 607 | ||
608 | #[test] | 608 | #[test] |
609 | fn inserts_parens_for_function_calls() { | 609 | fn inserts_parens_for_function_calls() { |
610 | covers!(inserts_parens_for_function_calls); | 610 | mark::check!(inserts_parens_for_function_calls); |
611 | assert_debug_snapshot!( | 611 | assert_debug_snapshot!( |
612 | do_reference_completion( | 612 | do_reference_completion( |
613 | r" | 613 | r" |
@@ -992,7 +992,7 @@ mod tests { | |||
992 | 992 | ||
993 | #[test] | 993 | #[test] |
994 | fn inserts_angle_brackets_for_generics() { | 994 | fn inserts_angle_brackets_for_generics() { |
995 | covers!(inserts_angle_brackets_for_generics); | 995 | mark::check!(inserts_angle_brackets_for_generics); |
996 | assert_debug_snapshot!( | 996 | assert_debug_snapshot!( |
997 | do_reference_completion( | 997 | do_reference_completion( |
998 | r" | 998 | r" |
@@ -1115,7 +1115,7 @@ mod tests { | |||
1115 | 1115 | ||
1116 | #[test] | 1116 | #[test] |
1117 | fn dont_insert_macro_call_parens_unncessary() { | 1117 | fn dont_insert_macro_call_parens_unncessary() { |
1118 | covers!(dont_insert_macro_call_parens_unncessary); | 1118 | mark::check!(dont_insert_macro_call_parens_unncessary); |
1119 | assert_debug_snapshot!( | 1119 | assert_debug_snapshot!( |
1120 | do_reference_completion( | 1120 | do_reference_completion( |
1121 | r" | 1121 | r" |
@@ -1181,7 +1181,7 @@ mod tests { | |||
1181 | 1181 | ||
1182 | #[test] | 1182 | #[test] |
1183 | fn test_struct_field_completion_in_func_call() { | 1183 | fn test_struct_field_completion_in_func_call() { |
1184 | covers!(test_struct_field_completion_in_func_call); | 1184 | mark::check!(test_struct_field_completion_in_func_call); |
1185 | assert_debug_snapshot!( | 1185 | assert_debug_snapshot!( |
1186 | do_reference_completion( | 1186 | do_reference_completion( |
1187 | r" | 1187 | r" |
@@ -1271,7 +1271,7 @@ mod tests { | |||
1271 | 1271 | ||
1272 | #[test] | 1272 | #[test] |
1273 | fn test_struct_field_completion_in_record_lit() { | 1273 | fn test_struct_field_completion_in_record_lit() { |
1274 | covers!(test_struct_field_completion_in_record_lit); | 1274 | mark::check!(test_struct_field_completion_in_record_lit); |
1275 | assert_debug_snapshot!( | 1275 | assert_debug_snapshot!( |
1276 | do_reference_completion( | 1276 | do_reference_completion( |
1277 | r" | 1277 | r" |
diff --git a/crates/ra_ide/src/completion/test_utils.rs b/crates/ra_ide/src/completion/test_utils.rs index eb90b5279..bf22452a2 100644 --- a/crates/ra_ide/src/completion/test_utils.rs +++ b/crates/ra_ide/src/completion/test_utils.rs | |||
@@ -20,7 +20,7 @@ pub(crate) fn do_completion_with_options( | |||
20 | } else { | 20 | } else { |
21 | single_file_with_position(code) | 21 | single_file_with_position(code) |
22 | }; | 22 | }; |
23 | let completions = analysis.completions(position, options).unwrap().unwrap(); | 23 | let completions = analysis.completions(options, position).unwrap().unwrap(); |
24 | let completion_items: Vec<CompletionItem> = completions.into(); | 24 | let completion_items: Vec<CompletionItem> = completions.into(); |
25 | let mut kind_completions: Vec<CompletionItem> = | 25 | let mut kind_completions: Vec<CompletionItem> = |
26 | completion_items.into_iter().filter(|c| c.completion_kind == kind).collect(); | 26 | completion_items.into_iter().filter(|c| c.completion_kind == kind).collect(); |
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs index 87a0b80f1..54c2bcc09 100644 --- a/crates/ra_ide/src/diagnostics.rs +++ b/crates/ra_ide/src/diagnostics.rs | |||
@@ -629,6 +629,7 @@ mod tests { | |||
629 | }, | 629 | }, |
630 | ], | 630 | ], |
631 | cursor_position: None, | 631 | cursor_position: None, |
632 | is_snippet: false, | ||
632 | }, | 633 | }, |
633 | ), | 634 | ), |
634 | severity: Error, | 635 | severity: Error, |
@@ -685,6 +686,7 @@ mod tests { | |||
685 | ], | 686 | ], |
686 | file_system_edits: [], | 687 | file_system_edits: [], |
687 | cursor_position: None, | 688 | cursor_position: None, |
689 | is_snippet: false, | ||
688 | }, | 690 | }, |
689 | ), | 691 | ), |
690 | severity: Error, | 692 | severity: Error, |
diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs index 150895abb..90e85d419 100644 --- a/crates/ra_ide/src/goto_definition.rs +++ b/crates/ra_ide/src/goto_definition.rs | |||
@@ -93,7 +93,7 @@ pub(crate) fn reference_definition( | |||
93 | 93 | ||
94 | #[cfg(test)] | 94 | #[cfg(test)] |
95 | mod tests { | 95 | mod tests { |
96 | use test_utils::{assert_eq_text, covers}; | 96 | use test_utils::assert_eq_text; |
97 | 97 | ||
98 | use crate::mock_analysis::analysis_and_position; | 98 | use crate::mock_analysis::analysis_and_position; |
99 | 99 | ||
@@ -208,7 +208,6 @@ mod tests { | |||
208 | 208 | ||
209 | #[test] | 209 | #[test] |
210 | fn goto_def_for_macros() { | 210 | fn goto_def_for_macros() { |
211 | covers!(ra_ide_db::goto_def_for_macros); | ||
212 | check_goto( | 211 | check_goto( |
213 | " | 212 | " |
214 | //- /lib.rs | 213 | //- /lib.rs |
@@ -225,7 +224,6 @@ mod tests { | |||
225 | 224 | ||
226 | #[test] | 225 | #[test] |
227 | fn goto_def_for_macros_from_other_crates() { | 226 | fn goto_def_for_macros_from_other_crates() { |
228 | covers!(ra_ide_db::goto_def_for_macros); | ||
229 | check_goto( | 227 | check_goto( |
230 | " | 228 | " |
231 | //- /lib.rs | 229 | //- /lib.rs |
@@ -245,7 +243,6 @@ mod tests { | |||
245 | 243 | ||
246 | #[test] | 244 | #[test] |
247 | fn goto_def_for_use_alias() { | 245 | fn goto_def_for_use_alias() { |
248 | covers!(ra_ide_db::goto_def_for_use_alias); | ||
249 | check_goto( | 246 | check_goto( |
250 | " | 247 | " |
251 | //- /lib.rs | 248 | //- /lib.rs |
@@ -370,7 +367,6 @@ mod tests { | |||
370 | 367 | ||
371 | #[test] | 368 | #[test] |
372 | fn goto_def_for_methods() { | 369 | fn goto_def_for_methods() { |
373 | covers!(ra_ide_db::goto_def_for_methods); | ||
374 | check_goto( | 370 | check_goto( |
375 | " | 371 | " |
376 | //- /lib.rs | 372 | //- /lib.rs |
@@ -390,7 +386,6 @@ mod tests { | |||
390 | 386 | ||
391 | #[test] | 387 | #[test] |
392 | fn goto_def_for_fields() { | 388 | fn goto_def_for_fields() { |
393 | covers!(ra_ide_db::goto_def_for_fields); | ||
394 | check_goto( | 389 | check_goto( |
395 | r" | 390 | r" |
396 | //- /lib.rs | 391 | //- /lib.rs |
@@ -409,7 +404,6 @@ mod tests { | |||
409 | 404 | ||
410 | #[test] | 405 | #[test] |
411 | fn goto_def_for_record_fields() { | 406 | fn goto_def_for_record_fields() { |
412 | covers!(ra_ide_db::goto_def_for_record_fields); | ||
413 | check_goto( | 407 | check_goto( |
414 | r" | 408 | r" |
415 | //- /lib.rs | 409 | //- /lib.rs |
@@ -430,7 +424,6 @@ mod tests { | |||
430 | 424 | ||
431 | #[test] | 425 | #[test] |
432 | fn goto_def_for_record_pat_fields() { | 426 | fn goto_def_for_record_pat_fields() { |
433 | covers!(ra_ide_db::goto_def_for_record_field_pats); | ||
434 | check_goto( | 427 | check_goto( |
435 | r" | 428 | r" |
436 | //- /lib.rs | 429 | //- /lib.rs |
@@ -873,7 +866,6 @@ mod tests { | |||
873 | 866 | ||
874 | #[test] | 867 | #[test] |
875 | fn goto_def_for_field_init_shorthand() { | 868 | fn goto_def_for_field_init_shorthand() { |
876 | covers!(ra_ide_db::goto_def_for_field_init_shorthand); | ||
877 | check_goto( | 869 | check_goto( |
878 | " | 870 | " |
879 | //- /lib.rs | 871 | //- /lib.rs |
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index 78149ddfc..83cb498f7 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs | |||
@@ -43,8 +43,6 @@ mod expand_macro; | |||
43 | mod ssr; | 43 | mod ssr; |
44 | 44 | ||
45 | #[cfg(test)] | 45 | #[cfg(test)] |
46 | mod marks; | ||
47 | #[cfg(test)] | ||
48 | mod test_utils; | 46 | mod test_utils; |
49 | 47 | ||
50 | use std::sync::Arc; | 48 | use std::sync::Arc; |
@@ -82,7 +80,7 @@ pub use crate::{ | |||
82 | }; | 80 | }; |
83 | 81 | ||
84 | pub use hir::Documentation; | 82 | pub use hir::Documentation; |
85 | pub use ra_assists::AssistId; | 83 | pub use ra_assists::{AssistConfig, AssistId}; |
86 | pub use ra_db::{ | 84 | pub use ra_db::{ |
87 | Canceled, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRootId, | 85 | Canceled, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRootId, |
88 | }; | 86 | }; |
@@ -458,17 +456,17 @@ impl Analysis { | |||
458 | /// Computes completions at the given position. | 456 | /// Computes completions at the given position. |
459 | pub fn completions( | 457 | pub fn completions( |
460 | &self, | 458 | &self, |
461 | position: FilePosition, | ||
462 | config: &CompletionConfig, | 459 | config: &CompletionConfig, |
460 | position: FilePosition, | ||
463 | ) -> Cancelable<Option<Vec<CompletionItem>>> { | 461 | ) -> Cancelable<Option<Vec<CompletionItem>>> { |
464 | self.with_db(|db| completion::completions(db, position, config).map(Into::into)) | 462 | self.with_db(|db| completion::completions(db, config, position).map(Into::into)) |
465 | } | 463 | } |
466 | 464 | ||
467 | /// Computes assists (aka code actions aka intentions) for the given | 465 | /// Computes assists (aka code actions aka intentions) for the given |
468 | /// position. | 466 | /// position. |
469 | pub fn assists(&self, frange: FileRange) -> Cancelable<Vec<Assist>> { | 467 | pub fn assists(&self, config: &AssistConfig, frange: FileRange) -> Cancelable<Vec<Assist>> { |
470 | self.with_db(|db| { | 468 | self.with_db(|db| { |
471 | ra_assists::Assist::resolved(db, frange) | 469 | ra_assists::Assist::resolved(db, config, frange) |
472 | .into_iter() | 470 | .into_iter() |
473 | .map(|assist| Assist { | 471 | .map(|assist| Assist { |
474 | id: assist.assist.id, | 472 | id: assist.assist.id, |
diff --git a/crates/ra_ide/src/marks.rs b/crates/ra_ide/src/marks.rs deleted file mode 100644 index 51ca4dde3..000000000 --- a/crates/ra_ide/src/marks.rs +++ /dev/null | |||
@@ -1,16 +0,0 @@ | |||
1 | //! See test_utils/src/marks.rs | ||
2 | |||
3 | test_utils::marks!( | ||
4 | inserts_angle_brackets_for_generics | ||
5 | inserts_parens_for_function_calls | ||
6 | call_info_bad_offset | ||
7 | dont_complete_current_use | ||
8 | test_resolve_parent_module_on_module_decl | ||
9 | search_filters_by_range | ||
10 | dont_insert_macro_call_parens_unncessary | ||
11 | self_fulfilling_completion | ||
12 | test_struct_field_completion_in_func_call | ||
13 | test_struct_field_completion_in_record_lit | ||
14 | test_rename_struct_field_for_shorthand | ||
15 | test_rename_local_for_field_shorthand | ||
16 | ); | ||
diff --git a/crates/ra_ide/src/parent_module.rs b/crates/ra_ide/src/parent_module.rs index aaf4460df..a083fb1eb 100644 --- a/crates/ra_ide/src/parent_module.rs +++ b/crates/ra_ide/src/parent_module.rs | |||
@@ -7,7 +7,7 @@ use ra_syntax::{ | |||
7 | algo::find_node_at_offset, | 7 | algo::find_node_at_offset, |
8 | ast::{self, AstNode}, | 8 | ast::{self, AstNode}, |
9 | }; | 9 | }; |
10 | use test_utils::tested_by; | 10 | use test_utils::mark; |
11 | 11 | ||
12 | use crate::NavigationTarget; | 12 | use crate::NavigationTarget; |
13 | 13 | ||
@@ -25,7 +25,7 @@ pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<Na | |||
25 | .item_list() | 25 | .item_list() |
26 | .map_or(false, |it| it.syntax().text_range().contains_inclusive(position.offset)) | 26 | .map_or(false, |it| it.syntax().text_range().contains_inclusive(position.offset)) |
27 | { | 27 | { |
28 | tested_by!(test_resolve_parent_module_on_module_decl); | 28 | mark::hit!(test_resolve_parent_module_on_module_decl); |
29 | module = m.syntax().ancestors().skip(1).find_map(ast::Module::cast); | 29 | module = m.syntax().ancestors().skip(1).find_map(ast::Module::cast); |
30 | } | 30 | } |
31 | } | 31 | } |
@@ -57,7 +57,7 @@ pub(crate) fn crate_for(db: &RootDatabase, file_id: FileId) -> Vec<CrateId> { | |||
57 | mod tests { | 57 | mod tests { |
58 | use ra_cfg::CfgOptions; | 58 | use ra_cfg::CfgOptions; |
59 | use ra_db::Env; | 59 | use ra_db::Env; |
60 | use test_utils::covers; | 60 | use test_utils::mark; |
61 | 61 | ||
62 | use crate::{ | 62 | use crate::{ |
63 | mock_analysis::{analysis_and_position, MockAnalysis}, | 63 | mock_analysis::{analysis_and_position, MockAnalysis}, |
@@ -81,7 +81,7 @@ mod tests { | |||
81 | 81 | ||
82 | #[test] | 82 | #[test] |
83 | fn test_resolve_parent_module_on_module_decl() { | 83 | fn test_resolve_parent_module_on_module_decl() { |
84 | covers!(test_resolve_parent_module_on_module_decl); | 84 | mark::check!(test_resolve_parent_module_on_module_decl); |
85 | let (analysis, pos) = analysis_and_position( | 85 | let (analysis, pos) = analysis_and_position( |
86 | " | 86 | " |
87 | //- /lib.rs | 87 | //- /lib.rs |
diff --git a/crates/ra_ide/src/references.rs b/crates/ra_ide/src/references.rs index 074284b42..96444bf6a 100644 --- a/crates/ra_ide/src/references.rs +++ b/crates/ra_ide/src/references.rs | |||
@@ -190,8 +190,6 @@ fn get_struct_def_name_for_struct_literal_search( | |||
190 | 190 | ||
191 | #[cfg(test)] | 191 | #[cfg(test)] |
192 | mod tests { | 192 | mod tests { |
193 | use test_utils::covers; | ||
194 | |||
195 | use crate::{ | 193 | use crate::{ |
196 | mock_analysis::{analysis_and_position, single_file_with_position, MockAnalysis}, | 194 | mock_analysis::{analysis_and_position, single_file_with_position, MockAnalysis}, |
197 | Declaration, Reference, ReferenceSearchResult, SearchScope, | 195 | Declaration, Reference, ReferenceSearchResult, SearchScope, |
@@ -301,7 +299,6 @@ mod tests { | |||
301 | 299 | ||
302 | #[test] | 300 | #[test] |
303 | fn search_filters_by_range() { | 301 | fn search_filters_by_range() { |
304 | covers!(ra_ide_db::search_filters_by_range); | ||
305 | let code = r#" | 302 | let code = r#" |
306 | fn foo() { | 303 | fn foo() { |
307 | let spam<|> = 92; | 304 | let spam<|> = 92; |
diff --git a/crates/ra_ide/src/references/rename.rs b/crates/ra_ide/src/references/rename.rs index 410dae75c..62ec8d85d 100644 --- a/crates/ra_ide/src/references/rename.rs +++ b/crates/ra_ide/src/references/rename.rs | |||
@@ -9,7 +9,7 @@ use ra_syntax::{ | |||
9 | }; | 9 | }; |
10 | use ra_text_edit::TextEdit; | 10 | use ra_text_edit::TextEdit; |
11 | use std::convert::TryInto; | 11 | use std::convert::TryInto; |
12 | use test_utils::tested_by; | 12 | use test_utils::mark; |
13 | 13 | ||
14 | use crate::{ | 14 | use crate::{ |
15 | references::find_all_refs, FilePosition, FileSystemEdit, RangeInfo, Reference, ReferenceKind, | 15 | references::find_all_refs, FilePosition, FileSystemEdit, RangeInfo, Reference, ReferenceKind, |
@@ -57,13 +57,13 @@ fn source_edit_from_reference(reference: Reference, new_name: &str) -> SourceFil | |||
57 | let file_id = reference.file_range.file_id; | 57 | let file_id = reference.file_range.file_id; |
58 | let range = match reference.kind { | 58 | let range = match reference.kind { |
59 | ReferenceKind::FieldShorthandForField => { | 59 | ReferenceKind::FieldShorthandForField => { |
60 | tested_by!(test_rename_struct_field_for_shorthand); | 60 | mark::hit!(test_rename_struct_field_for_shorthand); |
61 | replacement_text.push_str(new_name); | 61 | replacement_text.push_str(new_name); |
62 | replacement_text.push_str(": "); | 62 | replacement_text.push_str(": "); |
63 | TextRange::new(reference.file_range.range.start(), reference.file_range.range.start()) | 63 | TextRange::new(reference.file_range.range.start(), reference.file_range.range.start()) |
64 | } | 64 | } |
65 | ReferenceKind::FieldShorthandForLocal => { | 65 | ReferenceKind::FieldShorthandForLocal => { |
66 | tested_by!(test_rename_local_for_field_shorthand); | 66 | mark::hit!(test_rename_local_for_field_shorthand); |
67 | replacement_text.push_str(": "); | 67 | replacement_text.push_str(": "); |
68 | replacement_text.push_str(new_name); | 68 | replacement_text.push_str(new_name); |
69 | TextRange::new(reference.file_range.range.end(), reference.file_range.range.end()) | 69 | TextRange::new(reference.file_range.range.end(), reference.file_range.range.end()) |
@@ -260,7 +260,7 @@ fn rename_reference( | |||
260 | mod tests { | 260 | mod tests { |
261 | use insta::assert_debug_snapshot; | 261 | use insta::assert_debug_snapshot; |
262 | use ra_text_edit::TextEditBuilder; | 262 | use ra_text_edit::TextEditBuilder; |
263 | use test_utils::{assert_eq_text, covers}; | 263 | use test_utils::{assert_eq_text, mark}; |
264 | 264 | ||
265 | use crate::{ | 265 | use crate::{ |
266 | mock_analysis::analysis_and_position, mock_analysis::single_file_with_position, FileId, | 266 | mock_analysis::analysis_and_position, mock_analysis::single_file_with_position, FileId, |
@@ -492,7 +492,7 @@ mod tests { | |||
492 | 492 | ||
493 | #[test] | 493 | #[test] |
494 | fn test_rename_struct_field_for_shorthand() { | 494 | fn test_rename_struct_field_for_shorthand() { |
495 | covers!(test_rename_struct_field_for_shorthand); | 495 | mark::check!(test_rename_struct_field_for_shorthand); |
496 | test_rename( | 496 | test_rename( |
497 | r#" | 497 | r#" |
498 | struct Foo { | 498 | struct Foo { |
@@ -522,7 +522,7 @@ mod tests { | |||
522 | 522 | ||
523 | #[test] | 523 | #[test] |
524 | fn test_rename_local_for_field_shorthand() { | 524 | fn test_rename_local_for_field_shorthand() { |
525 | covers!(test_rename_local_for_field_shorthand); | 525 | mark::check!(test_rename_local_for_field_shorthand); |
526 | test_rename( | 526 | test_rename( |
527 | r#" | 527 | r#" |
528 | struct Foo { | 528 | struct Foo { |
@@ -670,6 +670,7 @@ mod tests { | |||
670 | }, | 670 | }, |
671 | ], | 671 | ], |
672 | cursor_position: None, | 672 | cursor_position: None, |
673 | is_snippet: false, | ||
673 | }, | 674 | }, |
674 | }, | 675 | }, |
675 | ) | 676 | ) |
@@ -722,6 +723,7 @@ mod tests { | |||
722 | }, | 723 | }, |
723 | ], | 724 | ], |
724 | cursor_position: None, | 725 | cursor_position: None, |
726 | is_snippet: false, | ||
725 | }, | 727 | }, |
726 | }, | 728 | }, |
727 | ) | 729 | ) |
@@ -818,6 +820,7 @@ mod tests { | |||
818 | }, | 820 | }, |
819 | ], | 821 | ], |
820 | cursor_position: None, | 822 | cursor_position: None, |
823 | is_snippet: false, | ||
821 | }, | 824 | }, |
822 | }, | 825 | }, |
823 | ) | 826 | ) |
diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs index 4f7eb2c5b..3a3d0b0ac 100644 --- a/crates/ra_ide/src/runnables.rs +++ b/crates/ra_ide/src/runnables.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use hir::{Attrs, HirFileId, InFile, Semantics}; | 3 | use hir::{AsAssocItem, Attrs, HirFileId, InFile, Semantics}; |
4 | use itertools::Itertools; | 4 | use itertools::Itertools; |
5 | use ra_ide_db::RootDatabase; | 5 | use ra_ide_db::RootDatabase; |
6 | use ra_syntax::{ | 6 | use ra_syntax::{ |
@@ -70,14 +70,36 @@ fn runnable_fn( | |||
70 | RunnableKind::Bin | 70 | RunnableKind::Bin |
71 | } else { | 71 | } else { |
72 | let test_id = if let Some(module) = sema.to_def(&fn_def).map(|def| def.module(sema.db)) { | 72 | let test_id = if let Some(module) = sema.to_def(&fn_def).map(|def| def.module(sema.db)) { |
73 | let path = module | 73 | let def = sema.to_def(&fn_def)?; |
74 | let impl_trait_name = | ||
75 | def.as_assoc_item(sema.db).and_then(|assoc_item| { | ||
76 | match assoc_item.container(sema.db) { | ||
77 | hir::AssocItemContainer::Trait(trait_item) => { | ||
78 | Some(trait_item.name(sema.db).to_string()) | ||
79 | } | ||
80 | hir::AssocItemContainer::ImplDef(impl_def) => impl_def | ||
81 | .target_ty(sema.db) | ||
82 | .as_adt() | ||
83 | .map(|adt| adt.name(sema.db).to_string()), | ||
84 | } | ||
85 | }); | ||
86 | |||
87 | let path_iter = module | ||
74 | .path_to_root(sema.db) | 88 | .path_to_root(sema.db) |
75 | .into_iter() | 89 | .into_iter() |
76 | .rev() | 90 | .rev() |
77 | .filter_map(|it| it.name(sema.db)) | 91 | .filter_map(|it| it.name(sema.db)) |
78 | .map(|name| name.to_string()) | 92 | .map(|name| name.to_string()); |
79 | .chain(std::iter::once(name_string)) | 93 | |
80 | .join("::"); | 94 | let path = if let Some(impl_trait_name) = impl_trait_name { |
95 | path_iter | ||
96 | .chain(std::iter::once(impl_trait_name)) | ||
97 | .chain(std::iter::once(name_string)) | ||
98 | .join("::") | ||
99 | } else { | ||
100 | path_iter.chain(std::iter::once(name_string)).join("::") | ||
101 | }; | ||
102 | |||
81 | TestId::Path(path) | 103 | TestId::Path(path) |
82 | } else { | 104 | } else { |
83 | TestId::Name(name_string) | 105 | TestId::Name(name_string) |
@@ -279,6 +301,46 @@ mod tests { | |||
279 | } | 301 | } |
280 | 302 | ||
281 | #[test] | 303 | #[test] |
304 | fn test_runnables_doc_test_in_impl() { | ||
305 | let (analysis, pos) = analysis_and_position( | ||
306 | r#" | ||
307 | //- /lib.rs | ||
308 | <|> //empty | ||
309 | fn main() {} | ||
310 | |||
311 | struct Data; | ||
312 | impl Data { | ||
313 | /// ``` | ||
314 | /// let x = 5; | ||
315 | /// ``` | ||
316 | fn foo() {} | ||
317 | } | ||
318 | "#, | ||
319 | ); | ||
320 | let runnables = analysis.runnables(pos.file_id).unwrap(); | ||
321 | assert_debug_snapshot!(&runnables, | ||
322 | @r###" | ||
323 | [ | ||
324 | Runnable { | ||
325 | range: 1..21, | ||
326 | kind: Bin, | ||
327 | features_needed: None, | ||
328 | }, | ||
329 | Runnable { | ||
330 | range: 51..105, | ||
331 | kind: DocTest { | ||
332 | test_id: Path( | ||
333 | "Data::foo", | ||
334 | ), | ||
335 | }, | ||
336 | features_needed: None, | ||
337 | }, | ||
338 | ] | ||
339 | "### | ||
340 | ); | ||
341 | } | ||
342 | |||
343 | #[test] | ||
282 | fn test_runnables_module() { | 344 | fn test_runnables_module() { |
283 | let (analysis, pos) = analysis_and_position( | 345 | let (analysis, pos) = analysis_and_position( |
284 | r#" | 346 | r#" |
diff --git a/crates/ra_ide/src/typing.rs b/crates/ra_ide/src/typing.rs index 6f04f0be4..cd48cad93 100644 --- a/crates/ra_ide/src/typing.rs +++ b/crates/ra_ide/src/typing.rs | |||
@@ -82,7 +82,6 @@ fn on_eq_typed(file: &SourceFile, offset: TextSize) -> Option<SingleFileChange> | |||
82 | Some(SingleFileChange { | 82 | Some(SingleFileChange { |
83 | label: "add semicolon".to_string(), | 83 | label: "add semicolon".to_string(), |
84 | edit: TextEdit::insert(offset, ";".to_string()), | 84 | edit: TextEdit::insert(offset, ";".to_string()), |
85 | cursor_position: None, | ||
86 | }) | 85 | }) |
87 | } | 86 | } |
88 | 87 | ||
@@ -111,7 +110,6 @@ fn on_dot_typed(file: &SourceFile, offset: TextSize) -> Option<SingleFileChange> | |||
111 | Some(SingleFileChange { | 110 | Some(SingleFileChange { |
112 | label: "reindent dot".to_string(), | 111 | label: "reindent dot".to_string(), |
113 | edit: TextEdit::replace(TextRange::new(offset - current_indent_len, offset), target_indent), | 112 | edit: TextEdit::replace(TextRange::new(offset - current_indent_len, offset), target_indent), |
114 | cursor_position: Some(offset + target_indent_len - current_indent_len + TextSize::of('.')), | ||
115 | }) | 113 | }) |
116 | } | 114 | } |
117 | 115 | ||
@@ -130,7 +128,6 @@ fn on_arrow_typed(file: &SourceFile, offset: TextSize) -> Option<SingleFileChang | |||
130 | Some(SingleFileChange { | 128 | Some(SingleFileChange { |
131 | label: "add space after return type".to_string(), | 129 | label: "add space after return type".to_string(), |
132 | edit: TextEdit::insert(after_arrow, " ".to_string()), | 130 | edit: TextEdit::insert(after_arrow, " ".to_string()), |
133 | cursor_position: Some(after_arrow), | ||
134 | }) | 131 | }) |
135 | } | 132 | } |
136 | 133 | ||
@@ -140,7 +137,7 @@ mod tests { | |||
140 | 137 | ||
141 | use super::*; | 138 | use super::*; |
142 | 139 | ||
143 | fn do_type_char(char_typed: char, before: &str) -> Option<(String, SingleFileChange)> { | 140 | fn do_type_char(char_typed: char, before: &str) -> Option<String> { |
144 | let (offset, before) = extract_offset(before); | 141 | let (offset, before) = extract_offset(before); |
145 | let edit = TextEdit::insert(offset, char_typed.to_string()); | 142 | let edit = TextEdit::insert(offset, char_typed.to_string()); |
146 | let mut before = before.to_string(); | 143 | let mut before = before.to_string(); |
@@ -148,21 +145,15 @@ mod tests { | |||
148 | let parse = SourceFile::parse(&before); | 145 | let parse = SourceFile::parse(&before); |
149 | on_char_typed_inner(&parse.tree(), offset, char_typed).map(|it| { | 146 | on_char_typed_inner(&parse.tree(), offset, char_typed).map(|it| { |
150 | it.edit.apply(&mut before); | 147 | it.edit.apply(&mut before); |
151 | (before.to_string(), it) | 148 | before.to_string() |
152 | }) | 149 | }) |
153 | } | 150 | } |
154 | 151 | ||
155 | fn type_char(char_typed: char, before: &str, after: &str) { | 152 | fn type_char(char_typed: char, before: &str, after: &str) { |
156 | let (actual, file_change) = do_type_char(char_typed, before) | 153 | let actual = do_type_char(char_typed, before) |
157 | .unwrap_or_else(|| panic!("typing `{}` did nothing", char_typed)); | 154 | .unwrap_or_else(|| panic!("typing `{}` did nothing", char_typed)); |
158 | 155 | ||
159 | if after.contains("<|>") { | 156 | assert_eq_text!(after, &actual); |
160 | let (offset, after) = extract_offset(after); | ||
161 | assert_eq_text!(&after, &actual); | ||
162 | assert_eq!(file_change.cursor_position, Some(offset)) | ||
163 | } else { | ||
164 | assert_eq_text!(after, &actual); | ||
165 | } | ||
166 | } | 157 | } |
167 | 158 | ||
168 | fn type_char_noop(char_typed: char, before: &str) { | 159 | fn type_char_noop(char_typed: char, before: &str) { |
@@ -350,6 +341,6 @@ fn foo() { | |||
350 | 341 | ||
351 | #[test] | 342 | #[test] |
352 | fn adds_space_after_return_type() { | 343 | fn adds_space_after_return_type() { |
353 | type_char('>', "fn foo() -<|>{ 92 }", "fn foo() -><|> { 92 }") | 344 | type_char('>', "fn foo() -<|>{ 92 }", "fn foo() -> { 92 }") |
354 | } | 345 | } |
355 | } | 346 | } |
diff --git a/crates/ra_ide_db/src/defs.rs b/crates/ra_ide_db/src/defs.rs index 60c11178e..8b06cbfc5 100644 --- a/crates/ra_ide_db/src/defs.rs +++ b/crates/ra_ide_db/src/defs.rs | |||
@@ -14,7 +14,6 @@ use ra_syntax::{ | |||
14 | ast::{self, AstNode}, | 14 | ast::{self, AstNode}, |
15 | match_ast, | 15 | match_ast, |
16 | }; | 16 | }; |
17 | use test_utils::tested_by; | ||
18 | 17 | ||
19 | use crate::RootDatabase; | 18 | use crate::RootDatabase; |
20 | 19 | ||
@@ -118,7 +117,6 @@ fn classify_name_inner(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Opti | |||
118 | match_ast! { | 117 | match_ast! { |
119 | match parent { | 118 | match parent { |
120 | ast::Alias(it) => { | 119 | ast::Alias(it) => { |
121 | tested_by!(goto_def_for_use_alias; force); | ||
122 | let use_tree = it.syntax().parent().and_then(ast::UseTree::cast)?; | 120 | let use_tree = it.syntax().parent().and_then(ast::UseTree::cast)?; |
123 | let path = use_tree.path()?; | 121 | let path = use_tree.path()?; |
124 | let path_segment = path.segment()?; | 122 | let path_segment = path.segment()?; |
@@ -203,6 +201,8 @@ impl NameRefClass { | |||
203 | } | 201 | } |
204 | } | 202 | } |
205 | 203 | ||
204 | // Note: we don't have unit-tests for this rather important function. | ||
205 | // It is primarily exercised via goto definition tests in `ra_ide`. | ||
206 | pub fn classify_name_ref( | 206 | pub fn classify_name_ref( |
207 | sema: &Semantics<RootDatabase>, | 207 | sema: &Semantics<RootDatabase>, |
208 | name_ref: &ast::NameRef, | 208 | name_ref: &ast::NameRef, |
@@ -212,22 +212,18 @@ pub fn classify_name_ref( | |||
212 | let parent = name_ref.syntax().parent()?; | 212 | let parent = name_ref.syntax().parent()?; |
213 | 213 | ||
214 | if let Some(method_call) = ast::MethodCallExpr::cast(parent.clone()) { | 214 | if let Some(method_call) = ast::MethodCallExpr::cast(parent.clone()) { |
215 | tested_by!(goto_def_for_methods; force); | ||
216 | if let Some(func) = sema.resolve_method_call(&method_call) { | 215 | if let Some(func) = sema.resolve_method_call(&method_call) { |
217 | return Some(NameRefClass::Definition(Definition::ModuleDef(func.into()))); | 216 | return Some(NameRefClass::Definition(Definition::ModuleDef(func.into()))); |
218 | } | 217 | } |
219 | } | 218 | } |
220 | 219 | ||
221 | if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) { | 220 | if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) { |
222 | tested_by!(goto_def_for_fields; force); | ||
223 | if let Some(field) = sema.resolve_field(&field_expr) { | 221 | if let Some(field) = sema.resolve_field(&field_expr) { |
224 | return Some(NameRefClass::Definition(Definition::Field(field))); | 222 | return Some(NameRefClass::Definition(Definition::Field(field))); |
225 | } | 223 | } |
226 | } | 224 | } |
227 | 225 | ||
228 | if let Some(record_field) = ast::RecordField::for_field_name(name_ref) { | 226 | if let Some(record_field) = ast::RecordField::for_field_name(name_ref) { |
229 | tested_by!(goto_def_for_record_fields; force); | ||
230 | tested_by!(goto_def_for_field_init_shorthand; force); | ||
231 | if let Some((field, local)) = sema.resolve_record_field(&record_field) { | 227 | if let Some((field, local)) = sema.resolve_record_field(&record_field) { |
232 | let field = Definition::Field(field); | 228 | let field = Definition::Field(field); |
233 | let res = match local { | 229 | let res = match local { |
@@ -239,7 +235,6 @@ pub fn classify_name_ref( | |||
239 | } | 235 | } |
240 | 236 | ||
241 | if let Some(record_field_pat) = ast::RecordFieldPat::cast(parent.clone()) { | 237 | if let Some(record_field_pat) = ast::RecordFieldPat::cast(parent.clone()) { |
242 | tested_by!(goto_def_for_record_field_pats; force); | ||
243 | if let Some(field) = sema.resolve_record_field_pat(&record_field_pat) { | 238 | if let Some(field) = sema.resolve_record_field_pat(&record_field_pat) { |
244 | let field = Definition::Field(field); | 239 | let field = Definition::Field(field); |
245 | return Some(NameRefClass::Definition(field)); | 240 | return Some(NameRefClass::Definition(field)); |
@@ -247,7 +242,6 @@ pub fn classify_name_ref( | |||
247 | } | 242 | } |
248 | 243 | ||
249 | if let Some(macro_call) = parent.ancestors().find_map(ast::MacroCall::cast) { | 244 | if let Some(macro_call) = parent.ancestors().find_map(ast::MacroCall::cast) { |
250 | tested_by!(goto_def_for_macros; force); | ||
251 | if let Some(macro_def) = sema.resolve_macro_call(¯o_call) { | 245 | if let Some(macro_def) = sema.resolve_macro_call(¯o_call) { |
252 | return Some(NameRefClass::Definition(Definition::Macro(macro_def))); | 246 | return Some(NameRefClass::Definition(Definition::Macro(macro_def))); |
253 | } | 247 | } |
diff --git a/crates/ra_ide_db/src/lib.rs b/crates/ra_ide_db/src/lib.rs index 52fcd7b6f..4f37954bf 100644 --- a/crates/ra_ide_db/src/lib.rs +++ b/crates/ra_ide_db/src/lib.rs | |||
@@ -2,7 +2,6 @@ | |||
2 | //! | 2 | //! |
3 | //! It is mainly a `HirDatabase` for semantic analysis, plus a `SymbolsDatabase`, for fuzzy search. | 3 | //! It is mainly a `HirDatabase` for semantic analysis, plus a `SymbolsDatabase`, for fuzzy search. |
4 | 4 | ||
5 | pub mod marks; | ||
6 | pub mod line_index; | 5 | pub mod line_index; |
7 | pub mod line_index_utils; | 6 | pub mod line_index_utils; |
8 | pub mod symbol_index; | 7 | pub mod symbol_index; |
diff --git a/crates/ra_ide_db/src/marks.rs b/crates/ra_ide_db/src/marks.rs deleted file mode 100644 index 386fe605c..000000000 --- a/crates/ra_ide_db/src/marks.rs +++ /dev/null | |||
@@ -1,12 +0,0 @@ | |||
1 | //! See test_utils/src/marks.rs | ||
2 | |||
3 | test_utils::marks![ | ||
4 | goto_def_for_macros | ||
5 | goto_def_for_use_alias | ||
6 | goto_def_for_methods | ||
7 | goto_def_for_fields | ||
8 | goto_def_for_record_fields | ||
9 | goto_def_for_field_init_shorthand | ||
10 | goto_def_for_record_field_pats | ||
11 | search_filters_by_range | ||
12 | ]; | ||
diff --git a/crates/ra_ide_db/src/search.rs b/crates/ra_ide_db/src/search.rs index b464959fc..589f44771 100644 --- a/crates/ra_ide_db/src/search.rs +++ b/crates/ra_ide_db/src/search.rs | |||
@@ -12,7 +12,6 @@ use ra_db::{FileId, FileRange, SourceDatabaseExt}; | |||
12 | use ra_prof::profile; | 12 | use ra_prof::profile; |
13 | use ra_syntax::{ast, match_ast, AstNode, TextRange, TextSize}; | 13 | use ra_syntax::{ast, match_ast, AstNode, TextRange, TextSize}; |
14 | use rustc_hash::FxHashMap; | 14 | use rustc_hash::FxHashMap; |
15 | use test_utils::tested_by; | ||
16 | 15 | ||
17 | use crate::{ | 16 | use crate::{ |
18 | defs::{classify_name_ref, Definition, NameRefClass}, | 17 | defs::{classify_name_ref, Definition, NameRefClass}, |
@@ -209,7 +208,6 @@ impl Definition { | |||
209 | for (idx, _) in text.match_indices(pat) { | 208 | for (idx, _) in text.match_indices(pat) { |
210 | let offset: TextSize = idx.try_into().unwrap(); | 209 | let offset: TextSize = idx.try_into().unwrap(); |
211 | if !search_range.contains_inclusive(offset) { | 210 | if !search_range.contains_inclusive(offset) { |
212 | tested_by!(search_filters_by_range; force); | ||
213 | continue; | 211 | continue; |
214 | } | 212 | } |
215 | 213 | ||
diff --git a/crates/ra_ide_db/src/source_change.rs b/crates/ra_ide_db/src/source_change.rs index af81a91a4..94e118dd8 100644 --- a/crates/ra_ide_db/src/source_change.rs +++ b/crates/ra_ide_db/src/source_change.rs | |||
@@ -4,7 +4,7 @@ | |||
4 | //! It can be viewed as a dual for `AnalysisChange`. | 4 | //! It can be viewed as a dual for `AnalysisChange`. |
5 | 5 | ||
6 | use ra_db::{FileId, FilePosition, RelativePathBuf, SourceRootId}; | 6 | use ra_db::{FileId, FilePosition, RelativePathBuf, SourceRootId}; |
7 | use ra_text_edit::{TextEdit, TextSize}; | 7 | use ra_text_edit::TextEdit; |
8 | 8 | ||
9 | #[derive(Debug, Clone)] | 9 | #[derive(Debug, Clone)] |
10 | pub struct SourceChange { | 10 | pub struct SourceChange { |
@@ -13,6 +13,7 @@ pub struct SourceChange { | |||
13 | pub source_file_edits: Vec<SourceFileEdit>, | 13 | pub source_file_edits: Vec<SourceFileEdit>, |
14 | pub file_system_edits: Vec<FileSystemEdit>, | 14 | pub file_system_edits: Vec<FileSystemEdit>, |
15 | pub cursor_position: Option<FilePosition>, | 15 | pub cursor_position: Option<FilePosition>, |
16 | pub is_snippet: bool, | ||
16 | } | 17 | } |
17 | 18 | ||
18 | impl SourceChange { | 19 | impl SourceChange { |
@@ -28,6 +29,7 @@ impl SourceChange { | |||
28 | source_file_edits, | 29 | source_file_edits, |
29 | file_system_edits, | 30 | file_system_edits, |
30 | cursor_position: None, | 31 | cursor_position: None, |
32 | is_snippet: false, | ||
31 | } | 33 | } |
32 | } | 34 | } |
33 | 35 | ||
@@ -41,6 +43,7 @@ impl SourceChange { | |||
41 | source_file_edits: edits, | 43 | source_file_edits: edits, |
42 | file_system_edits: vec![], | 44 | file_system_edits: vec![], |
43 | cursor_position: None, | 45 | cursor_position: None, |
46 | is_snippet: false, | ||
44 | } | 47 | } |
45 | } | 48 | } |
46 | 49 | ||
@@ -52,6 +55,7 @@ impl SourceChange { | |||
52 | source_file_edits: vec![], | 55 | source_file_edits: vec![], |
53 | file_system_edits: edits, | 56 | file_system_edits: edits, |
54 | cursor_position: None, | 57 | cursor_position: None, |
58 | is_snippet: false, | ||
55 | } | 59 | } |
56 | } | 60 | } |
57 | 61 | ||
@@ -105,7 +109,6 @@ pub enum FileSystemEdit { | |||
105 | pub struct SingleFileChange { | 109 | pub struct SingleFileChange { |
106 | pub label: String, | 110 | pub label: String, |
107 | pub edit: TextEdit, | 111 | pub edit: TextEdit, |
108 | pub cursor_position: Option<TextSize>, | ||
109 | } | 112 | } |
110 | 113 | ||
111 | impl SingleFileChange { | 114 | impl SingleFileChange { |
@@ -114,7 +117,8 @@ impl SingleFileChange { | |||
114 | label: self.label, | 117 | label: self.label, |
115 | source_file_edits: vec![SourceFileEdit { file_id, edit: self.edit }], | 118 | source_file_edits: vec![SourceFileEdit { file_id, edit: self.edit }], |
116 | file_system_edits: Vec::new(), | 119 | file_system_edits: Vec::new(), |
117 | cursor_position: self.cursor_position.map(|offset| FilePosition { file_id, offset }), | 120 | cursor_position: None, |
121 | is_snippet: false, | ||
118 | } | 122 | } |
119 | } | 123 | } |
120 | } | 124 | } |
diff --git a/crates/ra_mbe/src/mbe_expander/transcriber.rs b/crates/ra_mbe/src/mbe_expander/transcriber.rs index 4b173edd3..7c9bb4d00 100644 --- a/crates/ra_mbe/src/mbe_expander/transcriber.rs +++ b/crates/ra_mbe/src/mbe_expander/transcriber.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | //! Transcraber takes a template, like `fn $ident() {}`, a set of bindings like | 1 | //! Transcriber takes a template, like `fn $ident() {}`, a set of bindings like |
2 | //! `$ident => foo`, interpolates variables in the template, to get `fn foo() {}` | 2 | //! `$ident => foo`, interpolates variables in the template, to get `fn foo() {}` |
3 | 3 | ||
4 | use ra_syntax::SmolStr; | 4 | use ra_syntax::SmolStr; |
@@ -53,7 +53,8 @@ impl Bindings { | |||
53 | pub(super) fn transcribe(template: &tt::Subtree, bindings: &Bindings) -> ExpandResult<tt::Subtree> { | 53 | pub(super) fn transcribe(template: &tt::Subtree, bindings: &Bindings) -> ExpandResult<tt::Subtree> { |
54 | assert!(template.delimiter == None); | 54 | assert!(template.delimiter == None); |
55 | let mut ctx = ExpandCtx { bindings: &bindings, nesting: Vec::new() }; | 55 | let mut ctx = ExpandCtx { bindings: &bindings, nesting: Vec::new() }; |
56 | expand_subtree(&mut ctx, template) | 56 | let mut arena: Vec<tt::TokenTree> = Vec::new(); |
57 | expand_subtree(&mut ctx, template, &mut arena) | ||
57 | } | 58 | } |
58 | 59 | ||
59 | #[derive(Debug)] | 60 | #[derive(Debug)] |
@@ -73,8 +74,13 @@ struct ExpandCtx<'a> { | |||
73 | nesting: Vec<NestingState>, | 74 | nesting: Vec<NestingState>, |
74 | } | 75 | } |
75 | 76 | ||
76 | fn expand_subtree(ctx: &mut ExpandCtx, template: &tt::Subtree) -> ExpandResult<tt::Subtree> { | 77 | fn expand_subtree( |
77 | let mut buf: Vec<tt::TokenTree> = Vec::new(); | 78 | ctx: &mut ExpandCtx, |
79 | template: &tt::Subtree, | ||
80 | arena: &mut Vec<tt::TokenTree>, | ||
81 | ) -> ExpandResult<tt::Subtree> { | ||
82 | // remember how many elements are in the arena now - when returning, we want to drain exactly how many elements we added. This way, the recursive uses of the arena get their own "view" of the arena, but will reuse the allocation | ||
83 | let start_elements = arena.len(); | ||
78 | let mut err = None; | 84 | let mut err = None; |
79 | for op in parse_template(template) { | 85 | for op in parse_template(template) { |
80 | let op = match op { | 86 | let op = match op { |
@@ -85,25 +91,27 @@ fn expand_subtree(ctx: &mut ExpandCtx, template: &tt::Subtree) -> ExpandResult<t | |||
85 | } | 91 | } |
86 | }; | 92 | }; |
87 | match op { | 93 | match op { |
88 | Op::TokenTree(tt @ tt::TokenTree::Leaf(..)) => buf.push(tt.clone()), | 94 | Op::TokenTree(tt @ tt::TokenTree::Leaf(..)) => arena.push(tt.clone()), |
89 | Op::TokenTree(tt::TokenTree::Subtree(tt)) => { | 95 | Op::TokenTree(tt::TokenTree::Subtree(tt)) => { |
90 | let ExpandResult(tt, e) = expand_subtree(ctx, tt); | 96 | let ExpandResult(tt, e) = expand_subtree(ctx, tt, arena); |
91 | err = err.or(e); | 97 | err = err.or(e); |
92 | buf.push(tt.into()); | 98 | arena.push(tt.into()); |
93 | } | 99 | } |
94 | Op::Var { name, kind: _ } => { | 100 | Op::Var { name, kind: _ } => { |
95 | let ExpandResult(fragment, e) = expand_var(ctx, name); | 101 | let ExpandResult(fragment, e) = expand_var(ctx, name); |
96 | err = err.or(e); | 102 | err = err.or(e); |
97 | push_fragment(&mut buf, fragment); | 103 | push_fragment(arena, fragment); |
98 | } | 104 | } |
99 | Op::Repeat { subtree, kind, separator } => { | 105 | Op::Repeat { subtree, kind, separator } => { |
100 | let ExpandResult(fragment, e) = expand_repeat(ctx, subtree, kind, separator); | 106 | let ExpandResult(fragment, e) = expand_repeat(ctx, subtree, kind, separator, arena); |
101 | err = err.or(e); | 107 | err = err.or(e); |
102 | push_fragment(&mut buf, fragment) | 108 | push_fragment(arena, fragment) |
103 | } | 109 | } |
104 | } | 110 | } |
105 | } | 111 | } |
106 | ExpandResult(tt::Subtree { delimiter: template.delimiter, token_trees: buf }, err) | 112 | // drain the elements added in this instance of expand_subtree |
113 | let tts = arena.drain(start_elements..arena.len()).collect(); | ||
114 | ExpandResult(tt::Subtree { delimiter: template.delimiter, token_trees: tts }, err) | ||
107 | } | 115 | } |
108 | 116 | ||
109 | fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr) -> ExpandResult<Fragment> { | 117 | fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr) -> ExpandResult<Fragment> { |
@@ -155,6 +163,7 @@ fn expand_repeat( | |||
155 | template: &tt::Subtree, | 163 | template: &tt::Subtree, |
156 | kind: RepeatKind, | 164 | kind: RepeatKind, |
157 | separator: Option<Separator>, | 165 | separator: Option<Separator>, |
166 | arena: &mut Vec<tt::TokenTree>, | ||
158 | ) -> ExpandResult<Fragment> { | 167 | ) -> ExpandResult<Fragment> { |
159 | let mut buf: Vec<tt::TokenTree> = Vec::new(); | 168 | let mut buf: Vec<tt::TokenTree> = Vec::new(); |
160 | ctx.nesting.push(NestingState { idx: 0, at_end: false, hit: false }); | 169 | ctx.nesting.push(NestingState { idx: 0, at_end: false, hit: false }); |
@@ -165,7 +174,7 @@ fn expand_repeat( | |||
165 | let mut counter = 0; | 174 | let mut counter = 0; |
166 | 175 | ||
167 | loop { | 176 | loop { |
168 | let ExpandResult(mut t, e) = expand_subtree(ctx, template); | 177 | let ExpandResult(mut t, e) = expand_subtree(ctx, template, arena); |
169 | let nesting_state = ctx.nesting.last_mut().unwrap(); | 178 | let nesting_state = ctx.nesting.last_mut().unwrap(); |
170 | if nesting_state.at_end || !nesting_state.hit { | 179 | if nesting_state.at_end || !nesting_state.hit { |
171 | break; | 180 | break; |
diff --git a/crates/ra_parser/src/lib.rs b/crates/ra_parser/src/lib.rs index e08ad4dae..eeb8ad66b 100644 --- a/crates/ra_parser/src/lib.rs +++ b/crates/ra_parser/src/lib.rs | |||
@@ -25,7 +25,7 @@ pub(crate) use token_set::TokenSet; | |||
25 | pub use syntax_kind::SyntaxKind; | 25 | pub use syntax_kind::SyntaxKind; |
26 | 26 | ||
27 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 27 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
28 | pub struct ParseError(pub String); | 28 | pub struct ParseError(pub Box<String>); |
29 | 29 | ||
30 | /// `TokenSource` abstracts the source of the tokens parser operates on. | 30 | /// `TokenSource` abstracts the source of the tokens parser operates on. |
31 | /// | 31 | /// |
diff --git a/crates/ra_parser/src/parser.rs b/crates/ra_parser/src/parser.rs index faa63d53f..4f59b0a23 100644 --- a/crates/ra_parser/src/parser.rs +++ b/crates/ra_parser/src/parser.rs | |||
@@ -192,7 +192,7 @@ impl<'t> Parser<'t> { | |||
192 | /// structured errors with spans and notes, like rustc | 192 | /// structured errors with spans and notes, like rustc |
193 | /// does. | 193 | /// does. |
194 | pub(crate) fn error<T: Into<String>>(&mut self, message: T) { | 194 | pub(crate) fn error<T: Into<String>>(&mut self, message: T) { |
195 | let msg = ParseError(message.into()); | 195 | let msg = ParseError(Box::new(message.into())); |
196 | self.push_event(Event::Error { msg }) | 196 | self.push_event(Event::Error { msg }) |
197 | } | 197 | } |
198 | 198 | ||
diff --git a/crates/ra_syntax/src/algo.rs b/crates/ra_syntax/src/algo.rs index 2a8dac757..664894d1f 100644 --- a/crates/ra_syntax/src/algo.rs +++ b/crates/ra_syntax/src/algo.rs | |||
@@ -266,6 +266,15 @@ impl<'a> SyntaxRewriter<'a> { | |||
266 | let replacement = Replacement::Single(with.clone().into()); | 266 | let replacement = Replacement::Single(with.clone().into()); |
267 | self.replacements.insert(what, replacement); | 267 | self.replacements.insert(what, replacement); |
268 | } | 268 | } |
269 | pub fn replace_with_many<T: Clone + Into<SyntaxElement>>( | ||
270 | &mut self, | ||
271 | what: &T, | ||
272 | with: Vec<SyntaxElement>, | ||
273 | ) { | ||
274 | let what = what.clone().into(); | ||
275 | let replacement = Replacement::Many(with); | ||
276 | self.replacements.insert(what, replacement); | ||
277 | } | ||
269 | pub fn replace_ast<T: AstNode>(&mut self, what: &T, with: &T) { | 278 | pub fn replace_ast<T: AstNode>(&mut self, what: &T, with: &T) { |
270 | self.replace(what.syntax(), with.syntax()) | 279 | self.replace(what.syntax(), with.syntax()) |
271 | } | 280 | } |
@@ -302,31 +311,41 @@ impl<'a> SyntaxRewriter<'a> { | |||
302 | 311 | ||
303 | fn rewrite_children(&self, node: &SyntaxNode) -> SyntaxNode { | 312 | fn rewrite_children(&self, node: &SyntaxNode) -> SyntaxNode { |
304 | // FIXME: this could be made much faster. | 313 | // FIXME: this could be made much faster. |
305 | let new_children = | 314 | let mut new_children = Vec::new(); |
306 | node.children_with_tokens().flat_map(|it| self.rewrite_self(&it)).collect::<Vec<_>>(); | 315 | for child in node.children_with_tokens() { |
316 | self.rewrite_self(&mut new_children, &child); | ||
317 | } | ||
307 | with_children(node, new_children) | 318 | with_children(node, new_children) |
308 | } | 319 | } |
309 | 320 | ||
310 | fn rewrite_self( | 321 | fn rewrite_self( |
311 | &self, | 322 | &self, |
323 | acc: &mut Vec<NodeOrToken<rowan::GreenNode, rowan::GreenToken>>, | ||
312 | element: &SyntaxElement, | 324 | element: &SyntaxElement, |
313 | ) -> Option<NodeOrToken<rowan::GreenNode, rowan::GreenToken>> { | 325 | ) { |
314 | if let Some(replacement) = self.replacement(&element) { | 326 | if let Some(replacement) = self.replacement(&element) { |
315 | return match replacement { | 327 | match replacement { |
316 | Replacement::Single(NodeOrToken::Node(it)) => { | 328 | Replacement::Single(NodeOrToken::Node(it)) => { |
317 | Some(NodeOrToken::Node(it.green().clone())) | 329 | acc.push(NodeOrToken::Node(it.green().clone())) |
318 | } | 330 | } |
319 | Replacement::Single(NodeOrToken::Token(it)) => { | 331 | Replacement::Single(NodeOrToken::Token(it)) => { |
320 | Some(NodeOrToken::Token(it.green().clone())) | 332 | acc.push(NodeOrToken::Token(it.green().clone())) |
321 | } | 333 | } |
322 | Replacement::Delete => None, | 334 | Replacement::Many(replacements) => { |
335 | acc.extend(replacements.iter().map(|it| match it { | ||
336 | NodeOrToken::Node(it) => NodeOrToken::Node(it.green().clone()), | ||
337 | NodeOrToken::Token(it) => NodeOrToken::Token(it.green().clone()), | ||
338 | })) | ||
339 | } | ||
340 | Replacement::Delete => (), | ||
323 | }; | 341 | }; |
342 | return; | ||
324 | } | 343 | } |
325 | let res = match element { | 344 | let res = match element { |
326 | NodeOrToken::Token(it) => NodeOrToken::Token(it.green().clone()), | 345 | NodeOrToken::Token(it) => NodeOrToken::Token(it.green().clone()), |
327 | NodeOrToken::Node(it) => NodeOrToken::Node(self.rewrite_children(it).green().clone()), | 346 | NodeOrToken::Node(it) => NodeOrToken::Node(self.rewrite_children(it).green().clone()), |
328 | }; | 347 | }; |
329 | Some(res) | 348 | acc.push(res) |
330 | } | 349 | } |
331 | } | 350 | } |
332 | 351 | ||
@@ -341,6 +360,7 @@ impl ops::AddAssign for SyntaxRewriter<'_> { | |||
341 | enum Replacement { | 360 | enum Replacement { |
342 | Delete, | 361 | Delete, |
343 | Single(SyntaxElement), | 362 | Single(SyntaxElement), |
363 | Many(Vec<SyntaxElement>), | ||
344 | } | 364 | } |
345 | 365 | ||
346 | fn with_children( | 366 | fn with_children( |
diff --git a/crates/ra_syntax/src/ast/edit.rs b/crates/ra_syntax/src/ast/edit.rs index 24a1e1d91..29eb3fcb9 100644 --- a/crates/ra_syntax/src/ast/edit.rs +++ b/crates/ra_syntax/src/ast/edit.rs | |||
@@ -1,7 +1,10 @@ | |||
1 | //! This module contains functions for editing syntax trees. As the trees are | 1 | //! This module contains functions for editing syntax trees. As the trees are |
2 | //! immutable, all function here return a fresh copy of the tree, instead of | 2 | //! immutable, all function here return a fresh copy of the tree, instead of |
3 | //! doing an in-place modification. | 3 | //! doing an in-place modification. |
4 | use std::{iter, ops::RangeInclusive}; | 4 | use std::{ |
5 | fmt, iter, | ||
6 | ops::{self, RangeInclusive}, | ||
7 | }; | ||
5 | 8 | ||
6 | use arrayvec::ArrayVec; | 9 | use arrayvec::ArrayVec; |
7 | 10 | ||
@@ -437,6 +440,28 @@ impl From<u8> for IndentLevel { | |||
437 | } | 440 | } |
438 | } | 441 | } |
439 | 442 | ||
443 | impl fmt::Display for IndentLevel { | ||
444 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
445 | let spaces = " "; | ||
446 | let buf; | ||
447 | let len = self.0 as usize * 4; | ||
448 | let indent = if len <= spaces.len() { | ||
449 | &spaces[..len] | ||
450 | } else { | ||
451 | buf = iter::repeat(' ').take(len).collect::<String>(); | ||
452 | &buf | ||
453 | }; | ||
454 | fmt::Display::fmt(indent, f) | ||
455 | } | ||
456 | } | ||
457 | |||
458 | impl ops::Add<u8> for IndentLevel { | ||
459 | type Output = IndentLevel; | ||
460 | fn add(self, rhs: u8) -> IndentLevel { | ||
461 | IndentLevel(self.0 + rhs) | ||
462 | } | ||
463 | } | ||
464 | |||
440 | impl IndentLevel { | 465 | impl IndentLevel { |
441 | pub fn from_node(node: &SyntaxNode) -> IndentLevel { | 466 | pub fn from_node(node: &SyntaxNode) -> IndentLevel { |
442 | let first_token = match node.first_token() { | 467 | let first_token = match node.first_token() { |
@@ -453,6 +478,14 @@ impl IndentLevel { | |||
453 | IndentLevel(0) | 478 | IndentLevel(0) |
454 | } | 479 | } |
455 | 480 | ||
481 | /// XXX: this intentionally doesn't change the indent of the very first token. | ||
482 | /// Ie, in something like | ||
483 | /// ``` | ||
484 | /// fn foo() { | ||
485 | /// 92 | ||
486 | /// } | ||
487 | /// ``` | ||
488 | /// if you indent the block, the `{` token would stay put. | ||
456 | fn increase_indent(self, node: SyntaxNode) -> SyntaxNode { | 489 | fn increase_indent(self, node: SyntaxNode) -> SyntaxNode { |
457 | let mut rewriter = SyntaxRewriter::default(); | 490 | let mut rewriter = SyntaxRewriter::default(); |
458 | node.descendants_with_tokens() | 491 | node.descendants_with_tokens() |
@@ -463,12 +496,7 @@ impl IndentLevel { | |||
463 | text.contains('\n') | 496 | text.contains('\n') |
464 | }) | 497 | }) |
465 | .for_each(|ws| { | 498 | .for_each(|ws| { |
466 | let new_ws = make::tokens::whitespace(&format!( | 499 | let new_ws = make::tokens::whitespace(&format!("{}{}", ws.syntax(), self,)); |
467 | "{}{:width$}", | ||
468 | ws.syntax().text(), | ||
469 | "", | ||
470 | width = self.0 as usize * 4 | ||
471 | )); | ||
472 | rewriter.replace(ws.syntax(), &new_ws) | 500 | rewriter.replace(ws.syntax(), &new_ws) |
473 | }); | 501 | }); |
474 | rewriter.rewrite(&node) | 502 | rewriter.rewrite(&node) |
@@ -485,7 +513,7 @@ impl IndentLevel { | |||
485 | }) | 513 | }) |
486 | .for_each(|ws| { | 514 | .for_each(|ws| { |
487 | let new_ws = make::tokens::whitespace( | 515 | let new_ws = make::tokens::whitespace( |
488 | &ws.syntax().text().replace(&format!("\n{:1$}", "", self.0 as usize * 4), "\n"), | 516 | &ws.syntax().text().replace(&format!("\n{}", self), "\n"), |
489 | ); | 517 | ); |
490 | rewriter.replace(ws.syntax(), &new_ws) | 518 | rewriter.replace(ws.syntax(), &new_ws) |
491 | }); | 519 | }); |
diff --git a/crates/ra_syntax/src/ast/make.rs b/crates/ra_syntax/src/ast/make.rs index d0e960fb4..da0eb0926 100644 --- a/crates/ra_syntax/src/ast/make.rs +++ b/crates/ra_syntax/src/ast/make.rs | |||
@@ -1,5 +1,9 @@ | |||
1 | //! This module contains free-standing functions for creating AST fragments out | 1 | //! This module contains free-standing functions for creating AST fragments out |
2 | //! of smaller pieces. | 2 | //! of smaller pieces. |
3 | //! | ||
4 | //! Note that all functions here intended to be stupid constructors, which just | ||
5 | //! assemble a finish node from immediate children. If you want to do something | ||
6 | //! smarter than that, it probably doesn't belong in this module. | ||
3 | use itertools::Itertools; | 7 | use itertools::Itertools; |
4 | use stdx::format_to; | 8 | use stdx::format_to; |
5 | 9 | ||
@@ -95,6 +99,9 @@ pub fn expr_empty_block() -> ast::Expr { | |||
95 | pub fn expr_unimplemented() -> ast::Expr { | 99 | pub fn expr_unimplemented() -> ast::Expr { |
96 | expr_from_text("unimplemented!()") | 100 | expr_from_text("unimplemented!()") |
97 | } | 101 | } |
102 | pub fn expr_unreachable() -> ast::Expr { | ||
103 | expr_from_text("unreachable!()") | ||
104 | } | ||
98 | pub fn expr_todo() -> ast::Expr { | 105 | pub fn expr_todo() -> ast::Expr { |
99 | expr_from_text("todo!()") | 106 | expr_from_text("todo!()") |
100 | } | 107 | } |
@@ -264,10 +271,6 @@ pub fn token(kind: SyntaxKind) -> SyntaxToken { | |||
264 | .unwrap_or_else(|| panic!("unhandled token: {:?}", kind)) | 271 | .unwrap_or_else(|| panic!("unhandled token: {:?}", kind)) |
265 | } | 272 | } |
266 | 273 | ||
267 | pub fn unreachable_macro_call() -> ast::MacroCall { | ||
268 | ast_from_text(&format!("unreachable!()")) | ||
269 | } | ||
270 | |||
271 | pub fn param(name: String, ty: String) -> ast::Param { | 274 | pub fn param(name: String, ty: String) -> ast::Param { |
272 | ast_from_text(&format!("fn f({}: {}) {{ }}", name, ty)) | 275 | ast_from_text(&format!("fn f({}: {}) {{ }}", name, ty)) |
273 | } | 276 | } |
@@ -277,7 +280,12 @@ pub fn param_list(pats: impl IntoIterator<Item = ast::Param>) -> ast::ParamList | |||
277 | ast_from_text(&format!("fn f({}) {{ }}", args)) | 280 | ast_from_text(&format!("fn f({}) {{ }}", args)) |
278 | } | 281 | } |
279 | 282 | ||
283 | pub fn visibility_pub_crate() -> ast::Visibility { | ||
284 | ast_from_text("pub(crate) struct S") | ||
285 | } | ||
286 | |||
280 | pub fn fn_def( | 287 | pub fn fn_def( |
288 | visibility: Option<ast::Visibility>, | ||
281 | fn_name: ast::Name, | 289 | fn_name: ast::Name, |
282 | type_params: Option<ast::TypeParamList>, | 290 | type_params: Option<ast::TypeParamList>, |
283 | params: ast::ParamList, | 291 | params: ast::ParamList, |
@@ -285,21 +293,11 @@ pub fn fn_def( | |||
285 | ) -> ast::FnDef { | 293 | ) -> ast::FnDef { |
286 | let type_params = | 294 | let type_params = |
287 | if let Some(type_params) = type_params { format!("<{}>", type_params) } else { "".into() }; | 295 | if let Some(type_params) = type_params { format!("<{}>", type_params) } else { "".into() }; |
288 | ast_from_text(&format!("fn {}{}{} {}", fn_name, type_params, params, body)) | 296 | let visibility = match visibility { |
289 | } | 297 | None => String::new(), |
290 | 298 | Some(it) => format!("{} ", it), | |
291 | pub fn add_leading_newlines(amount_of_newlines: usize, t: impl AstNode) -> ast::SourceFile { | 299 | }; |
292 | let newlines = "\n".repeat(amount_of_newlines); | 300 | ast_from_text(&format!("{}fn {}{}{} {}", visibility, fn_name, type_params, params, body)) |
293 | ast_from_text(&format!("{}{}", newlines, t.syntax())) | ||
294 | } | ||
295 | |||
296 | pub fn add_trailing_newlines(amount_of_newlines: usize, t: impl AstNode) -> ast::SourceFile { | ||
297 | let newlines = "\n".repeat(amount_of_newlines); | ||
298 | ast_from_text(&format!("{}{}", t.syntax(), newlines)) | ||
299 | } | ||
300 | |||
301 | pub fn add_pub_crate_modifier(fn_def: ast::FnDef) -> ast::FnDef { | ||
302 | ast_from_text(&format!("pub(crate) {}", fn_def)) | ||
303 | } | 301 | } |
304 | 302 | ||
305 | fn ast_from_text<N: AstNode>(text: &str) -> N { | 303 | fn ast_from_text<N: AstNode>(text: &str) -> N { |
diff --git a/crates/ra_syntax/src/syntax_node.rs b/crates/ra_syntax/src/syntax_node.rs index f9d379abf..e566af7e8 100644 --- a/crates/ra_syntax/src/syntax_node.rs +++ b/crates/ra_syntax/src/syntax_node.rs | |||
@@ -70,6 +70,6 @@ impl SyntaxTreeBuilder { | |||
70 | } | 70 | } |
71 | 71 | ||
72 | pub fn error(&mut self, error: ra_parser::ParseError, text_pos: TextSize) { | 72 | pub fn error(&mut self, error: ra_parser::ParseError, text_pos: TextSize) { |
73 | self.errors.push(SyntaxError::new_at_offset(error.0, text_pos)) | 73 | self.errors.push(SyntaxError::new_at_offset(*error.0, text_pos)) |
74 | } | 74 | } |
75 | } | 75 | } |
diff --git a/crates/ra_tt/src/buffer.rs b/crates/ra_tt/src/buffer.rs index 14b3f707d..5967f44cd 100644 --- a/crates/ra_tt/src/buffer.rs +++ b/crates/ra_tt/src/buffer.rs | |||
@@ -42,7 +42,9 @@ impl<'t> TokenBuffer<'t> { | |||
42 | buffers: &mut Vec<Box<[Entry<'t>]>>, | 42 | buffers: &mut Vec<Box<[Entry<'t>]>>, |
43 | next: Option<EntryPtr>, | 43 | next: Option<EntryPtr>, |
44 | ) -> usize { | 44 | ) -> usize { |
45 | let mut entries = vec![]; | 45 | // Must contain everything in tokens and then the Entry::End |
46 | let start_capacity = tokens.len() + 1; | ||
47 | let mut entries = Vec::with_capacity(start_capacity); | ||
46 | let mut children = vec![]; | 48 | let mut children = vec![]; |
47 | 49 | ||
48 | for (idx, tt) in tokens.iter().enumerate() { | 50 | for (idx, tt) in tokens.iter().enumerate() { |
diff --git a/crates/rust-analyzer/src/cli/analysis_bench.rs b/crates/rust-analyzer/src/cli/analysis_bench.rs index 6147ae207..b20efe98d 100644 --- a/crates/rust-analyzer/src/cli/analysis_bench.rs +++ b/crates/rust-analyzer/src/cli/analysis_bench.rs | |||
@@ -105,7 +105,7 @@ pub fn analysis_bench( | |||
105 | if is_completion { | 105 | if is_completion { |
106 | let options = CompletionConfig::default(); | 106 | let options = CompletionConfig::default(); |
107 | let res = do_work(&mut host, file_id, |analysis| { | 107 | let res = do_work(&mut host, file_id, |analysis| { |
108 | analysis.completions(file_position, &options) | 108 | analysis.completions(&options, file_position) |
109 | }); | 109 | }); |
110 | if verbosity.is_verbose() { | 110 | if verbosity.is_verbose() { |
111 | println!("\n{:#?}", res); | 111 | println!("\n{:#?}", res); |
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 53aee833d..d75c48597 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs | |||
@@ -11,7 +11,7 @@ use std::{ffi::OsString, path::PathBuf}; | |||
11 | 11 | ||
12 | use lsp_types::ClientCapabilities; | 12 | use lsp_types::ClientCapabilities; |
13 | use ra_flycheck::FlycheckConfig; | 13 | use ra_flycheck::FlycheckConfig; |
14 | use ra_ide::{CompletionConfig, InlayHintsConfig}; | 14 | use ra_ide::{AssistConfig, CompletionConfig, InlayHintsConfig}; |
15 | use ra_project_model::CargoConfig; | 15 | use ra_project_model::CargoConfig; |
16 | use serde::Deserialize; | 16 | use serde::Deserialize; |
17 | 17 | ||
@@ -32,7 +32,38 @@ pub struct Config { | |||
32 | 32 | ||
33 | pub inlay_hints: InlayHintsConfig, | 33 | pub inlay_hints: InlayHintsConfig, |
34 | pub completion: CompletionConfig, | 34 | pub completion: CompletionConfig, |
35 | pub assist: AssistConfig, | ||
35 | pub call_info_full: bool, | 36 | pub call_info_full: bool, |
37 | pub lens: LensConfig, | ||
38 | } | ||
39 | |||
40 | #[derive(Clone, Debug, PartialEq, Eq)] | ||
41 | pub struct LensConfig { | ||
42 | pub run: bool, | ||
43 | pub debug: bool, | ||
44 | pub impementations: bool, | ||
45 | } | ||
46 | |||
47 | impl Default for LensConfig { | ||
48 | fn default() -> Self { | ||
49 | Self { run: true, debug: true, impementations: true } | ||
50 | } | ||
51 | } | ||
52 | |||
53 | impl LensConfig { | ||
54 | pub const NO_LENS: LensConfig = Self { run: false, debug: false, impementations: false }; | ||
55 | |||
56 | pub fn any(&self) -> bool { | ||
57 | self.impementations || self.runnable() | ||
58 | } | ||
59 | |||
60 | pub fn none(&self) -> bool { | ||
61 | !self.any() | ||
62 | } | ||
63 | |||
64 | pub fn runnable(&self) -> bool { | ||
65 | self.run || self.debug | ||
66 | } | ||
36 | } | 67 | } |
37 | 68 | ||
38 | #[derive(Debug, Clone)] | 69 | #[derive(Debug, Clone)] |
@@ -106,7 +137,9 @@ impl Default for Config { | |||
106 | add_call_argument_snippets: true, | 137 | add_call_argument_snippets: true, |
107 | ..CompletionConfig::default() | 138 | ..CompletionConfig::default() |
108 | }, | 139 | }, |
140 | assist: AssistConfig::default(), | ||
109 | call_info_full: true, | 141 | call_info_full: true, |
142 | lens: LensConfig::default(), | ||
110 | } | 143 | } |
111 | } | 144 | } |
112 | } | 145 | } |
@@ -196,6 +229,16 @@ impl Config { | |||
196 | set(value, "/completion/addCallArgumentSnippets", &mut self.completion.add_call_argument_snippets); | 229 | set(value, "/completion/addCallArgumentSnippets", &mut self.completion.add_call_argument_snippets); |
197 | set(value, "/callInfo/full", &mut self.call_info_full); | 230 | set(value, "/callInfo/full", &mut self.call_info_full); |
198 | 231 | ||
232 | let mut lens_enabled = true; | ||
233 | set(value, "/lens/enable", &mut lens_enabled); | ||
234 | if lens_enabled { | ||
235 | set(value, "/lens/run", &mut self.lens.run); | ||
236 | set(value, "/lens/debug", &mut self.lens.debug); | ||
237 | set(value, "/lens/implementations", &mut self.lens.impementations); | ||
238 | } else { | ||
239 | self.lens = LensConfig::NO_LENS; | ||
240 | } | ||
241 | |||
199 | log::info!("Config::update() = {:#?}", self); | 242 | log::info!("Config::update() = {:#?}", self); |
200 | 243 | ||
201 | fn get<'a, T: Deserialize<'a>>(value: &'a serde_json::Value, pointer: &str) -> Option<T> { | 244 | fn get<'a, T: Deserialize<'a>>(value: &'a serde_json::Value, pointer: &str) -> Option<T> { |
@@ -232,6 +275,7 @@ impl Config { | |||
232 | { | 275 | { |
233 | self.client_caps.code_action_literals = value; | 276 | self.client_caps.code_action_literals = value; |
234 | } | 277 | } |
278 | |||
235 | self.completion.allow_snippets(false); | 279 | self.completion.allow_snippets(false); |
236 | if let Some(completion) = &doc_caps.completion { | 280 | if let Some(completion) = &doc_caps.completion { |
237 | if let Some(completion_item) = &completion.completion_item { | 281 | if let Some(completion_item) = &completion.completion_item { |
@@ -247,5 +291,12 @@ impl Config { | |||
247 | self.client_caps.work_done_progress = value; | 291 | self.client_caps.work_done_progress = value; |
248 | } | 292 | } |
249 | } | 293 | } |
294 | |||
295 | self.assist.allow_snippets(false); | ||
296 | if let Some(experimental) = &caps.experimental { | ||
297 | let enable = | ||
298 | experimental.get("snippetTextEdit").and_then(|it| it.as_bool()) == Some(true); | ||
299 | self.assist.allow_snippets(enable); | ||
300 | } | ||
250 | } | 301 | } |
251 | } | 302 | } |
diff --git a/crates/rust-analyzer/src/diagnostics.rs b/crates/rust-analyzer/src/diagnostics.rs index 4bdd45a7d..25856c543 100644 --- a/crates/rust-analyzer/src/diagnostics.rs +++ b/crates/rust-analyzer/src/diagnostics.rs | |||
@@ -3,9 +3,11 @@ pub(crate) mod to_proto; | |||
3 | 3 | ||
4 | use std::{collections::HashMap, sync::Arc}; | 4 | use std::{collections::HashMap, sync::Arc}; |
5 | 5 | ||
6 | use lsp_types::{CodeActionOrCommand, Diagnostic, Range}; | 6 | use lsp_types::{Diagnostic, Range}; |
7 | use ra_ide::FileId; | 7 | use ra_ide::FileId; |
8 | 8 | ||
9 | use crate::lsp_ext; | ||
10 | |||
9 | pub type CheckFixes = Arc<HashMap<FileId, Vec<Fix>>>; | 11 | pub type CheckFixes = Arc<HashMap<FileId, Vec<Fix>>>; |
10 | 12 | ||
11 | #[derive(Debug, Default, Clone)] | 13 | #[derive(Debug, Default, Clone)] |
@@ -18,13 +20,13 @@ pub struct DiagnosticCollection { | |||
18 | #[derive(Debug, Clone)] | 20 | #[derive(Debug, Clone)] |
19 | pub struct Fix { | 21 | pub struct Fix { |
20 | pub range: Range, | 22 | pub range: Range, |
21 | pub action: CodeActionOrCommand, | 23 | pub action: lsp_ext::CodeAction, |
22 | } | 24 | } |
23 | 25 | ||
24 | #[derive(Debug)] | 26 | #[derive(Debug)] |
25 | pub enum DiagnosticTask { | 27 | pub enum DiagnosticTask { |
26 | ClearCheck, | 28 | ClearCheck, |
27 | AddCheck(FileId, Diagnostic, Vec<CodeActionOrCommand>), | 29 | AddCheck(FileId, Diagnostic, Vec<lsp_ext::CodeAction>), |
28 | SetNative(FileId, Vec<Diagnostic>), | 30 | SetNative(FileId, Vec<Diagnostic>), |
29 | } | 31 | } |
30 | 32 | ||
@@ -38,7 +40,7 @@ impl DiagnosticCollection { | |||
38 | &mut self, | 40 | &mut self, |
39 | file_id: FileId, | 41 | file_id: FileId, |
40 | diagnostic: Diagnostic, | 42 | diagnostic: Diagnostic, |
41 | fixes: Vec<CodeActionOrCommand>, | 43 | fixes: Vec<lsp_ext::CodeAction>, |
42 | ) { | 44 | ) { |
43 | let diagnostics = self.check.entry(file_id).or_default(); | 45 | let diagnostics = self.check.entry(file_id).or_default(); |
44 | for existing_diagnostic in diagnostics.iter() { | 46 | for existing_diagnostic in diagnostics.iter() { |
diff --git a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap index 076b3cf27..96466b5c9 100644 --- a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap +++ b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap | |||
@@ -68,9 +68,9 @@ expression: diag | |||
68 | kind: Some( | 68 | kind: Some( |
69 | "quickfix", | 69 | "quickfix", |
70 | ), | 70 | ), |
71 | diagnostics: None, | 71 | command: None, |
72 | edit: Some( | 72 | edit: Some( |
73 | WorkspaceEdit { | 73 | SnippetWorkspaceEdit { |
74 | changes: Some( | 74 | changes: Some( |
75 | { | 75 | { |
76 | "file:///test/src/main.rs": [ | 76 | "file:///test/src/main.rs": [ |
@@ -106,8 +106,6 @@ expression: diag | |||
106 | document_changes: None, | 106 | document_changes: None, |
107 | }, | 107 | }, |
108 | ), | 108 | ), |
109 | command: None, | ||
110 | is_preferred: None, | ||
111 | }, | 109 | }, |
112 | ], | 110 | ], |
113 | }, | 111 | }, |
diff --git a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap index 69138c15b..8f962277f 100644 --- a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap +++ b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap | |||
@@ -53,9 +53,9 @@ expression: diag | |||
53 | kind: Some( | 53 | kind: Some( |
54 | "quickfix", | 54 | "quickfix", |
55 | ), | 55 | ), |
56 | diagnostics: None, | 56 | command: None, |
57 | edit: Some( | 57 | edit: Some( |
58 | WorkspaceEdit { | 58 | SnippetWorkspaceEdit { |
59 | changes: Some( | 59 | changes: Some( |
60 | { | 60 | { |
61 | "file:///test/driver/subcommand/repl.rs": [ | 61 | "file:///test/driver/subcommand/repl.rs": [ |
@@ -78,8 +78,6 @@ expression: diag | |||
78 | document_changes: None, | 78 | document_changes: None, |
79 | }, | 79 | }, |
80 | ), | 80 | ), |
81 | command: None, | ||
82 | is_preferred: None, | ||
83 | }, | 81 | }, |
84 | ], | 82 | ], |
85 | }, | 83 | }, |
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs index eabf4908f..afea59525 100644 --- a/crates/rust-analyzer/src/diagnostics/to_proto.rs +++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs | |||
@@ -7,13 +7,13 @@ use std::{ | |||
7 | }; | 7 | }; |
8 | 8 | ||
9 | use lsp_types::{ | 9 | use lsp_types::{ |
10 | CodeAction, Diagnostic, DiagnosticRelatedInformation, DiagnosticSeverity, DiagnosticTag, | 10 | Diagnostic, DiagnosticRelatedInformation, DiagnosticSeverity, DiagnosticTag, Location, |
11 | Location, NumberOrString, Position, Range, TextEdit, Url, WorkspaceEdit, | 11 | NumberOrString, Position, Range, TextEdit, Url, |
12 | }; | 12 | }; |
13 | use ra_flycheck::{Applicability, DiagnosticLevel, DiagnosticSpan, DiagnosticSpanMacroExpansion}; | 13 | use ra_flycheck::{Applicability, DiagnosticLevel, DiagnosticSpan, DiagnosticSpanMacroExpansion}; |
14 | use stdx::format_to; | 14 | use stdx::format_to; |
15 | 15 | ||
16 | use crate::Result; | 16 | use crate::{lsp_ext, Result}; |
17 | 17 | ||
18 | /// Converts a Rust level string to a LSP severity | 18 | /// Converts a Rust level string to a LSP severity |
19 | fn map_level_to_severity(val: DiagnosticLevel) -> Option<DiagnosticSeverity> { | 19 | fn map_level_to_severity(val: DiagnosticLevel) -> Option<DiagnosticSeverity> { |
@@ -110,7 +110,7 @@ fn is_deprecated(rd: &ra_flycheck::Diagnostic) -> bool { | |||
110 | 110 | ||
111 | enum MappedRustChildDiagnostic { | 111 | enum MappedRustChildDiagnostic { |
112 | Related(DiagnosticRelatedInformation), | 112 | Related(DiagnosticRelatedInformation), |
113 | SuggestedFix(CodeAction), | 113 | SuggestedFix(lsp_ext::CodeAction), |
114 | MessageLine(String), | 114 | MessageLine(String), |
115 | } | 115 | } |
116 | 116 | ||
@@ -143,13 +143,15 @@ fn map_rust_child_diagnostic( | |||
143 | message: rd.message.clone(), | 143 | message: rd.message.clone(), |
144 | }) | 144 | }) |
145 | } else { | 145 | } else { |
146 | MappedRustChildDiagnostic::SuggestedFix(CodeAction { | 146 | MappedRustChildDiagnostic::SuggestedFix(lsp_ext::CodeAction { |
147 | title: rd.message.clone(), | 147 | title: rd.message.clone(), |
148 | kind: Some("quickfix".to_string()), | 148 | kind: Some("quickfix".to_string()), |
149 | diagnostics: None, | 149 | edit: Some(lsp_ext::SnippetWorkspaceEdit { |
150 | edit: Some(WorkspaceEdit::new(edit_map)), | 150 | // FIXME: there's no good reason to use edit_map here.... |
151 | changes: Some(edit_map), | ||
152 | document_changes: None, | ||
153 | }), | ||
151 | command: None, | 154 | command: None, |
152 | is_preferred: None, | ||
153 | }) | 155 | }) |
154 | } | 156 | } |
155 | } | 157 | } |
@@ -158,7 +160,7 @@ fn map_rust_child_diagnostic( | |||
158 | pub(crate) struct MappedRustDiagnostic { | 160 | pub(crate) struct MappedRustDiagnostic { |
159 | pub location: Location, | 161 | pub location: Location, |
160 | pub diagnostic: Diagnostic, | 162 | pub diagnostic: Diagnostic, |
161 | pub fixes: Vec<CodeAction>, | 163 | pub fixes: Vec<lsp_ext::CodeAction>, |
162 | } | 164 | } |
163 | 165 | ||
164 | /// Converts a Rust root diagnostic to LSP form | 166 | /// Converts a Rust root diagnostic to LSP form |
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index 313a8c769..f75a26eb7 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | //! rust-analyzer extensions to the LSP. | 1 | //! rust-analyzer extensions to the LSP. |
2 | 2 | ||
3 | use std::path::PathBuf; | 3 | use std::{collections::HashMap, path::PathBuf}; |
4 | 4 | ||
5 | use lsp_types::request::Request; | 5 | use lsp_types::request::Request; |
6 | use lsp_types::{Location, Position, Range, TextDocumentIdentifier}; | 6 | use lsp_types::{Location, Position, Range, TextDocumentIdentifier}; |
@@ -137,7 +137,7 @@ pub struct Runnable { | |||
137 | #[serde(rename_all = "camelCase")] | 137 | #[serde(rename_all = "camelCase")] |
138 | pub struct SourceChange { | 138 | pub struct SourceChange { |
139 | pub label: String, | 139 | pub label: String, |
140 | pub workspace_edit: lsp_types::WorkspaceEdit, | 140 | pub workspace_edit: SnippetWorkspaceEdit, |
141 | pub cursor_position: Option<lsp_types::TextDocumentPositionParams>, | 141 | pub cursor_position: Option<lsp_types::TextDocumentPositionParams>, |
142 | } | 142 | } |
143 | 143 | ||
@@ -183,3 +183,54 @@ pub struct SsrParams { | |||
183 | pub query: String, | 183 | pub query: String, |
184 | pub parse_only: bool, | 184 | pub parse_only: bool, |
185 | } | 185 | } |
186 | |||
187 | pub enum CodeActionRequest {} | ||
188 | |||
189 | impl Request for CodeActionRequest { | ||
190 | type Params = lsp_types::CodeActionParams; | ||
191 | type Result = Option<Vec<CodeAction>>; | ||
192 | const METHOD: &'static str = "textDocument/codeAction"; | ||
193 | } | ||
194 | |||
195 | #[derive(Debug, PartialEq, Clone, Default, Deserialize, Serialize)] | ||
196 | pub struct CodeAction { | ||
197 | pub title: String, | ||
198 | #[serde(skip_serializing_if = "Option::is_none")] | ||
199 | pub kind: Option<String>, | ||
200 | #[serde(skip_serializing_if = "Option::is_none")] | ||
201 | pub command: Option<lsp_types::Command>, | ||
202 | #[serde(skip_serializing_if = "Option::is_none")] | ||
203 | pub edit: Option<SnippetWorkspaceEdit>, | ||
204 | } | ||
205 | |||
206 | #[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)] | ||
207 | #[serde(rename_all = "camelCase")] | ||
208 | pub struct SnippetWorkspaceEdit { | ||
209 | #[serde(skip_serializing_if = "Option::is_none")] | ||
210 | pub changes: Option<HashMap<lsp_types::Url, Vec<lsp_types::TextEdit>>>, | ||
211 | #[serde(skip_serializing_if = "Option::is_none")] | ||
212 | pub document_changes: Option<Vec<SnippetDocumentChangeOperation>>, | ||
213 | } | ||
214 | |||
215 | #[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)] | ||
216 | #[serde(untagged, rename_all = "lowercase")] | ||
217 | pub enum SnippetDocumentChangeOperation { | ||
218 | Op(lsp_types::ResourceOp), | ||
219 | Edit(SnippetTextDocumentEdit), | ||
220 | } | ||
221 | |||
222 | #[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)] | ||
223 | #[serde(rename_all = "camelCase")] | ||
224 | pub struct SnippetTextDocumentEdit { | ||
225 | pub text_document: lsp_types::VersionedTextDocumentIdentifier, | ||
226 | pub edits: Vec<SnippetTextEdit>, | ||
227 | } | ||
228 | |||
229 | #[derive(Debug, Eq, PartialEq, Clone, Default, Deserialize, Serialize)] | ||
230 | #[serde(rename_all = "camelCase")] | ||
231 | pub struct SnippetTextEdit { | ||
232 | pub range: Range, | ||
233 | pub new_text: String, | ||
234 | #[serde(skip_serializing_if = "Option::is_none")] | ||
235 | pub insert_text_format: Option<lsp_types::InsertTextFormat>, | ||
236 | } | ||
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 15e5bb354..87795fffb 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs | |||
@@ -518,6 +518,7 @@ fn on_request( | |||
518 | .on::<lsp_ext::ParentModule>(handlers::handle_parent_module)? | 518 | .on::<lsp_ext::ParentModule>(handlers::handle_parent_module)? |
519 | .on::<lsp_ext::Runnables>(handlers::handle_runnables)? | 519 | .on::<lsp_ext::Runnables>(handlers::handle_runnables)? |
520 | .on::<lsp_ext::InlayHints>(handlers::handle_inlay_hints)? | 520 | .on::<lsp_ext::InlayHints>(handlers::handle_inlay_hints)? |
521 | .on::<lsp_ext::CodeActionRequest>(handlers::handle_code_action)? | ||
521 | .on::<lsp_types::request::OnTypeFormatting>(handlers::handle_on_type_formatting)? | 522 | .on::<lsp_types::request::OnTypeFormatting>(handlers::handle_on_type_formatting)? |
522 | .on::<lsp_types::request::DocumentSymbolRequest>(handlers::handle_document_symbol)? | 523 | .on::<lsp_types::request::DocumentSymbolRequest>(handlers::handle_document_symbol)? |
523 | .on::<lsp_types::request::WorkspaceSymbol>(handlers::handle_workspace_symbol)? | 524 | .on::<lsp_types::request::WorkspaceSymbol>(handlers::handle_workspace_symbol)? |
@@ -525,7 +526,6 @@ fn on_request( | |||
525 | .on::<lsp_types::request::GotoImplementation>(handlers::handle_goto_implementation)? | 526 | .on::<lsp_types::request::GotoImplementation>(handlers::handle_goto_implementation)? |
526 | .on::<lsp_types::request::GotoTypeDefinition>(handlers::handle_goto_type_definition)? | 527 | .on::<lsp_types::request::GotoTypeDefinition>(handlers::handle_goto_type_definition)? |
527 | .on::<lsp_types::request::Completion>(handlers::handle_completion)? | 528 | .on::<lsp_types::request::Completion>(handlers::handle_completion)? |
528 | .on::<lsp_types::request::CodeActionRequest>(handlers::handle_code_action)? | ||
529 | .on::<lsp_types::request::CodeLensRequest>(handlers::handle_code_lens)? | 529 | .on::<lsp_types::request::CodeLensRequest>(handlers::handle_code_lens)? |
530 | .on::<lsp_types::request::CodeLensResolve>(handlers::handle_code_lens_resolve)? | 530 | .on::<lsp_types::request::CodeLensResolve>(handlers::handle_code_lens_resolve)? |
531 | .on::<lsp_types::request::FoldingRangeRequest>(handlers::handle_folding_range)? | 531 | .on::<lsp_types::request::FoldingRangeRequest>(handlers::handle_folding_range)? |
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index 0232cc6f0..cc9abd162 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs | |||
@@ -11,12 +11,11 @@ use lsp_server::ErrorCode; | |||
11 | use lsp_types::{ | 11 | use lsp_types::{ |
12 | CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem, | 12 | CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem, |
13 | CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams, | 13 | CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams, |
14 | CodeAction, CodeActionResponse, CodeLens, Command, CompletionItem, Diagnostic, | 14 | CodeLens, Command, CompletionItem, Diagnostic, DocumentFormattingParams, DocumentHighlight, |
15 | DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange, FoldingRangeParams, | 15 | DocumentSymbol, FoldingRange, FoldingRangeParams, Hover, HoverContents, Location, |
16 | Hover, HoverContents, Location, MarkupContent, MarkupKind, Position, PrepareRenameResponse, | 16 | MarkupContent, MarkupKind, Position, PrepareRenameResponse, Range, RenameParams, |
17 | Range, RenameParams, SemanticTokensParams, SemanticTokensRangeParams, | 17 | SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult, |
18 | SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, | 18 | SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, TextEdit, Url, WorkspaceEdit, |
19 | TextEdit, Url, WorkspaceEdit, | ||
20 | }; | 19 | }; |
21 | use ra_ide::{ | 20 | use ra_ide::{ |
22 | Assist, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, SearchScope, | 21 | Assist, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, SearchScope, |
@@ -476,7 +475,7 @@ pub fn handle_completion( | |||
476 | return Ok(None); | 475 | return Ok(None); |
477 | } | 476 | } |
478 | 477 | ||
479 | let items = match world.analysis().completions(position, &world.config.completion)? { | 478 | let items = match world.analysis().completions(&world.config.completion, position)? { |
480 | None => return Ok(None), | 479 | None => return Ok(None), |
481 | Some(items) => items, | 480 | Some(items) => items, |
482 | }; | 481 | }; |
@@ -585,9 +584,8 @@ pub fn handle_rename(world: WorldSnapshot, params: RenameParams) -> Result<Optio | |||
585 | None => return Ok(None), | 584 | None => return Ok(None), |
586 | Some(it) => it.info, | 585 | Some(it) => it.info, |
587 | }; | 586 | }; |
588 | 587 | let workspace_edit = to_proto::workspace_edit(&world, source_change)?; | |
589 | let source_change = to_proto::source_change(&world, source_change)?; | 588 | Ok(Some(workspace_edit)) |
590 | Ok(Some(source_change.workspace_edit)) | ||
591 | } | 589 | } |
592 | 590 | ||
593 | pub fn handle_references( | 591 | pub fn handle_references( |
@@ -696,14 +694,21 @@ pub fn handle_formatting( | |||
696 | pub fn handle_code_action( | 694 | pub fn handle_code_action( |
697 | world: WorldSnapshot, | 695 | world: WorldSnapshot, |
698 | params: lsp_types::CodeActionParams, | 696 | params: lsp_types::CodeActionParams, |
699 | ) -> Result<Option<CodeActionResponse>> { | 697 | ) -> Result<Option<Vec<lsp_ext::CodeAction>>> { |
700 | let _p = profile("handle_code_action"); | 698 | let _p = profile("handle_code_action"); |
699 | // We intentionally don't support command-based actions, as those either | ||
700 | // requires custom client-code anyway, or requires server-initiated edits. | ||
701 | // Server initiated edits break causality, so we avoid those as well. | ||
702 | if !world.config.client_caps.code_action_literals { | ||
703 | return Ok(None); | ||
704 | } | ||
705 | |||
701 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; | 706 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; |
702 | let line_index = world.analysis().file_line_index(file_id)?; | 707 | let line_index = world.analysis().file_line_index(file_id)?; |
703 | let range = from_proto::text_range(&line_index, params.range); | 708 | let range = from_proto::text_range(&line_index, params.range); |
704 | 709 | ||
705 | let diagnostics = world.analysis().diagnostics(file_id)?; | 710 | let diagnostics = world.analysis().diagnostics(file_id)?; |
706 | let mut res = CodeActionResponse::default(); | 711 | let mut res: Vec<lsp_ext::CodeAction> = Vec::new(); |
707 | 712 | ||
708 | let fixes_from_diagnostics = diagnostics | 713 | let fixes_from_diagnostics = diagnostics |
709 | .into_iter() | 714 | .into_iter() |
@@ -713,22 +718,9 @@ pub fn handle_code_action( | |||
713 | 718 | ||
714 | for source_edit in fixes_from_diagnostics { | 719 | for source_edit in fixes_from_diagnostics { |
715 | let title = source_edit.label.clone(); | 720 | let title = source_edit.label.clone(); |
716 | let edit = to_proto::source_change(&world, source_edit)?; | 721 | let edit = to_proto::snippet_workspace_edit(&world, source_edit)?; |
717 | 722 | let action = lsp_ext::CodeAction { title, kind: None, edit: Some(edit), command: None }; | |
718 | let command = Command { | 723 | res.push(action); |
719 | title, | ||
720 | command: "rust-analyzer.applySourceChange".to_string(), | ||
721 | arguments: Some(vec![to_value(edit).unwrap()]), | ||
722 | }; | ||
723 | let action = CodeAction { | ||
724 | title: command.title.clone(), | ||
725 | kind: None, | ||
726 | diagnostics: None, | ||
727 | edit: None, | ||
728 | command: Some(command), | ||
729 | is_preferred: None, | ||
730 | }; | ||
731 | res.push(action.into()); | ||
732 | } | 724 | } |
733 | 725 | ||
734 | for fix in world.check_fixes.get(&file_id).into_iter().flatten() { | 726 | for fix in world.check_fixes.get(&file_id).into_iter().flatten() { |
@@ -740,14 +732,21 @@ pub fn handle_code_action( | |||
740 | } | 732 | } |
741 | 733 | ||
742 | let mut grouped_assists: FxHashMap<String, (usize, Vec<Assist>)> = FxHashMap::default(); | 734 | let mut grouped_assists: FxHashMap<String, (usize, Vec<Assist>)> = FxHashMap::default(); |
743 | for assist in world.analysis().assists(FileRange { file_id, range })?.into_iter() { | 735 | for assist in |
736 | world.analysis().assists(&world.config.assist, FileRange { file_id, range })?.into_iter() | ||
737 | { | ||
744 | match &assist.group_label { | 738 | match &assist.group_label { |
745 | Some(label) => grouped_assists | 739 | Some(label) => grouped_assists |
746 | .entry(label.to_owned()) | 740 | .entry(label.to_owned()) |
747 | .or_insert_with(|| { | 741 | .or_insert_with(|| { |
748 | let idx = res.len(); | 742 | let idx = res.len(); |
749 | let dummy = Command::new(String::new(), String::new(), None); | 743 | let dummy = lsp_ext::CodeAction { |
750 | res.push(dummy.into()); | 744 | title: String::new(), |
745 | kind: None, | ||
746 | command: None, | ||
747 | edit: None, | ||
748 | }; | ||
749 | res.push(dummy); | ||
751 | (idx, Vec::new()) | 750 | (idx, Vec::new()) |
752 | }) | 751 | }) |
753 | .1 | 752 | .1 |
@@ -775,35 +774,10 @@ pub fn handle_code_action( | |||
775 | command: "rust-analyzer.selectAndApplySourceChange".to_string(), | 774 | command: "rust-analyzer.selectAndApplySourceChange".to_string(), |
776 | arguments: Some(vec![serde_json::Value::Array(arguments)]), | 775 | arguments: Some(vec![serde_json::Value::Array(arguments)]), |
777 | }); | 776 | }); |
778 | res[idx] = CodeAction { | 777 | res[idx] = lsp_ext::CodeAction { title, kind: None, edit: None, command }; |
779 | title, | ||
780 | kind: None, | ||
781 | diagnostics: None, | ||
782 | edit: None, | ||
783 | command, | ||
784 | is_preferred: None, | ||
785 | } | ||
786 | .into(); | ||
787 | } | 778 | } |
788 | } | 779 | } |
789 | 780 | ||
790 | // If the client only supports commands then filter the list | ||
791 | // and remove and actions that depend on edits. | ||
792 | if !world.config.client_caps.code_action_literals { | ||
793 | // FIXME: use drain_filter once it hits stable. | ||
794 | res = res | ||
795 | .into_iter() | ||
796 | .filter_map(|it| match it { | ||
797 | cmd @ lsp_types::CodeActionOrCommand::Command(_) => Some(cmd), | ||
798 | lsp_types::CodeActionOrCommand::CodeAction(action) => match action.command { | ||
799 | Some(cmd) if action.edit.is_none() => { | ||
800 | Some(lsp_types::CodeActionOrCommand::Command(cmd)) | ||
801 | } | ||
802 | _ => None, | ||
803 | }, | ||
804 | }) | ||
805 | .collect(); | ||
806 | } | ||
807 | Ok(Some(res)) | 781 | Ok(Some(res)) |
808 | } | 782 | } |
809 | 783 | ||
@@ -812,88 +786,108 @@ pub fn handle_code_lens( | |||
812 | params: lsp_types::CodeLensParams, | 786 | params: lsp_types::CodeLensParams, |
813 | ) -> Result<Option<Vec<CodeLens>>> { | 787 | ) -> Result<Option<Vec<CodeLens>>> { |
814 | let _p = profile("handle_code_lens"); | 788 | let _p = profile("handle_code_lens"); |
815 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; | ||
816 | let line_index = world.analysis().file_line_index(file_id)?; | ||
817 | |||
818 | let mut lenses: Vec<CodeLens> = Default::default(); | 789 | let mut lenses: Vec<CodeLens> = Default::default(); |
819 | 790 | ||
791 | if world.config.lens.none() { | ||
792 | // early return before any db query! | ||
793 | return Ok(Some(lenses)); | ||
794 | } | ||
795 | |||
796 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; | ||
797 | let line_index = world.analysis().file_line_index(file_id)?; | ||
820 | let cargo_spec = CargoTargetSpec::for_file(&world, file_id)?; | 798 | let cargo_spec = CargoTargetSpec::for_file(&world, file_id)?; |
821 | // Gather runnables | 799 | |
822 | for runnable in world.analysis().runnables(file_id)? { | 800 | if world.config.lens.runnable() { |
823 | let title = match &runnable.kind { | 801 | // Gather runnables |
824 | RunnableKind::Test { .. } | RunnableKind::TestMod { .. } => "â–¶\u{fe0e} Run Test", | 802 | for runnable in world.analysis().runnables(file_id)? { |
825 | RunnableKind::DocTest { .. } => "â–¶\u{fe0e} Run Doctest", | 803 | let (run_title, debugee) = match &runnable.kind { |
826 | RunnableKind::Bench { .. } => "Run Bench", | 804 | RunnableKind::Test { .. } | RunnableKind::TestMod { .. } => { |
827 | RunnableKind::Bin => { | 805 | ("â–¶\u{fe0e} Run Test", true) |
828 | // Do not suggest binary run on other target than binary | 806 | } |
829 | match &cargo_spec { | 807 | RunnableKind::DocTest { .. } => { |
830 | Some(spec) => match spec.target_kind { | 808 | // cargo does not support -no-run for doctests |
831 | TargetKind::Bin => "Run", | 809 | ("â–¶\u{fe0e} Run Doctest", false) |
832 | _ => continue, | ||
833 | }, | ||
834 | None => continue, | ||
835 | } | 810 | } |
811 | RunnableKind::Bench { .. } => { | ||
812 | // Nothing wrong with bench debugging | ||
813 | ("Run Bench", true) | ||
814 | } | ||
815 | RunnableKind::Bin => { | ||
816 | // Do not suggest binary run on other target than binary | ||
817 | match &cargo_spec { | ||
818 | Some(spec) => match spec.target_kind { | ||
819 | TargetKind::Bin => ("Run", true), | ||
820 | _ => continue, | ||
821 | }, | ||
822 | None => continue, | ||
823 | } | ||
824 | } | ||
825 | }; | ||
826 | |||
827 | let mut r = to_lsp_runnable(&world, file_id, runnable)?; | ||
828 | if world.config.lens.run { | ||
829 | let lens = CodeLens { | ||
830 | range: r.range, | ||
831 | command: Some(Command { | ||
832 | title: run_title.to_string(), | ||
833 | command: "rust-analyzer.runSingle".into(), | ||
834 | arguments: Some(vec![to_value(&r).unwrap()]), | ||
835 | }), | ||
836 | data: None, | ||
837 | }; | ||
838 | lenses.push(lens); | ||
836 | } | 839 | } |
837 | } | ||
838 | .to_string(); | ||
839 | let mut r = to_lsp_runnable(&world, file_id, runnable)?; | ||
840 | let lens = CodeLens { | ||
841 | range: r.range, | ||
842 | command: Some(Command { | ||
843 | title, | ||
844 | command: "rust-analyzer.runSingle".into(), | ||
845 | arguments: Some(vec![to_value(&r).unwrap()]), | ||
846 | }), | ||
847 | data: None, | ||
848 | }; | ||
849 | lenses.push(lens); | ||
850 | 840 | ||
851 | if r.args[0] == "run" { | 841 | if debugee && world.config.lens.debug { |
852 | r.args[0] = "build".into(); | 842 | if r.args[0] == "run" { |
853 | } else { | 843 | r.args[0] = "build".into(); |
854 | r.args.push("--no-run".into()); | 844 | } else { |
845 | r.args.push("--no-run".into()); | ||
846 | } | ||
847 | let debug_lens = CodeLens { | ||
848 | range: r.range, | ||
849 | command: Some(Command { | ||
850 | title: "Debug".into(), | ||
851 | command: "rust-analyzer.debugSingle".into(), | ||
852 | arguments: Some(vec![to_value(r).unwrap()]), | ||
853 | }), | ||
854 | data: None, | ||
855 | }; | ||
856 | lenses.push(debug_lens); | ||
857 | } | ||
855 | } | 858 | } |
856 | let debug_lens = CodeLens { | ||
857 | range: r.range, | ||
858 | command: Some(Command { | ||
859 | title: "Debug".into(), | ||
860 | command: "rust-analyzer.debugSingle".into(), | ||
861 | arguments: Some(vec![to_value(r).unwrap()]), | ||
862 | }), | ||
863 | data: None, | ||
864 | }; | ||
865 | lenses.push(debug_lens); | ||
866 | } | 859 | } |
867 | 860 | ||
868 | // Handle impls | 861 | if world.config.lens.impementations { |
869 | lenses.extend( | 862 | // Handle impls |
870 | world | 863 | lenses.extend( |
871 | .analysis() | 864 | world |
872 | .file_structure(file_id)? | 865 | .analysis() |
873 | .into_iter() | 866 | .file_structure(file_id)? |
874 | .filter(|it| match it.kind { | 867 | .into_iter() |
875 | SyntaxKind::TRAIT_DEF | SyntaxKind::STRUCT_DEF | SyntaxKind::ENUM_DEF => true, | 868 | .filter(|it| match it.kind { |
876 | _ => false, | 869 | SyntaxKind::TRAIT_DEF | SyntaxKind::STRUCT_DEF | SyntaxKind::ENUM_DEF => true, |
877 | }) | 870 | _ => false, |
878 | .map(|it| { | 871 | }) |
879 | let range = to_proto::range(&line_index, it.node_range); | 872 | .map(|it| { |
880 | let pos = range.start; | 873 | let range = to_proto::range(&line_index, it.node_range); |
881 | let lens_params = lsp_types::request::GotoImplementationParams { | 874 | let pos = range.start; |
882 | text_document_position_params: lsp_types::TextDocumentPositionParams::new( | 875 | let lens_params = lsp_types::request::GotoImplementationParams { |
883 | params.text_document.clone(), | 876 | text_document_position_params: lsp_types::TextDocumentPositionParams::new( |
884 | pos, | 877 | params.text_document.clone(), |
885 | ), | 878 | pos, |
886 | work_done_progress_params: Default::default(), | 879 | ), |
887 | partial_result_params: Default::default(), | 880 | work_done_progress_params: Default::default(), |
888 | }; | 881 | partial_result_params: Default::default(), |
889 | CodeLens { | 882 | }; |
890 | range, | 883 | CodeLens { |
891 | command: None, | 884 | range, |
892 | data: Some(to_value(CodeLensResolveData::Impls(lens_params)).unwrap()), | 885 | command: None, |
893 | } | 886 | data: Some(to_value(CodeLensResolveData::Impls(lens_params)).unwrap()), |
894 | }), | 887 | } |
895 | ); | 888 | }), |
896 | 889 | ); | |
890 | } | ||
897 | Ok(Some(lenses)) | 891 | Ok(Some(lenses)) |
898 | } | 892 | } |
899 | 893 | ||
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index a8e2e535f..af54f81b7 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs | |||
@@ -112,6 +112,22 @@ pub(crate) fn text_edit( | |||
112 | lsp_types::TextEdit { range, new_text } | 112 | lsp_types::TextEdit { range, new_text } |
113 | } | 113 | } |
114 | 114 | ||
115 | pub(crate) fn snippet_text_edit( | ||
116 | line_index: &LineIndex, | ||
117 | line_endings: LineEndings, | ||
118 | is_snippet: bool, | ||
119 | indel: Indel, | ||
120 | ) -> lsp_ext::SnippetTextEdit { | ||
121 | let text_edit = text_edit(line_index, line_endings, indel); | ||
122 | let insert_text_format = | ||
123 | if is_snippet { Some(lsp_types::InsertTextFormat::Snippet) } else { None }; | ||
124 | lsp_ext::SnippetTextEdit { | ||
125 | range: text_edit.range, | ||
126 | new_text: text_edit.new_text, | ||
127 | insert_text_format, | ||
128 | } | ||
129 | } | ||
130 | |||
115 | pub(crate) fn text_edit_vec( | 131 | pub(crate) fn text_edit_vec( |
116 | line_index: &LineIndex, | 132 | line_index: &LineIndex, |
117 | line_endings: LineEndings, | 133 | line_endings: LineEndings, |
@@ -441,10 +457,11 @@ pub(crate) fn goto_definition_response( | |||
441 | } | 457 | } |
442 | } | 458 | } |
443 | 459 | ||
444 | pub(crate) fn text_document_edit( | 460 | pub(crate) fn snippet_text_document_edit( |
445 | world: &WorldSnapshot, | 461 | world: &WorldSnapshot, |
462 | is_snippet: bool, | ||
446 | source_file_edit: SourceFileEdit, | 463 | source_file_edit: SourceFileEdit, |
447 | ) -> Result<lsp_types::TextDocumentEdit> { | 464 | ) -> Result<lsp_ext::SnippetTextDocumentEdit> { |
448 | let text_document = versioned_text_document_identifier(world, source_file_edit.file_id, None)?; | 465 | let text_document = versioned_text_document_identifier(world, source_file_edit.file_id, None)?; |
449 | let line_index = world.analysis().file_line_index(source_file_edit.file_id)?; | 466 | let line_index = world.analysis().file_line_index(source_file_edit.file_id)?; |
450 | let line_endings = world.file_line_endings(source_file_edit.file_id); | 467 | let line_endings = world.file_line_endings(source_file_edit.file_id); |
@@ -452,9 +469,9 @@ pub(crate) fn text_document_edit( | |||
452 | .edit | 469 | .edit |
453 | .as_indels() | 470 | .as_indels() |
454 | .iter() | 471 | .iter() |
455 | .map(|it| text_edit(&line_index, line_endings, it.clone())) | 472 | .map(|it| snippet_text_edit(&line_index, line_endings, is_snippet, it.clone())) |
456 | .collect(); | 473 | .collect(); |
457 | Ok(lsp_types::TextDocumentEdit { text_document, edits }) | 474 | Ok(lsp_ext::SnippetTextDocumentEdit { text_document, edits }) |
458 | } | 475 | } |
459 | 476 | ||
460 | pub(crate) fn resource_op( | 477 | pub(crate) fn resource_op( |
@@ -500,20 +517,70 @@ pub(crate) fn source_change( | |||
500 | }) | 517 | }) |
501 | } | 518 | } |
502 | }; | 519 | }; |
503 | let mut document_changes: Vec<lsp_types::DocumentChangeOperation> = Vec::new(); | 520 | let label = source_change.label.clone(); |
521 | let workspace_edit = self::snippet_workspace_edit(world, source_change)?; | ||
522 | Ok(lsp_ext::SourceChange { label, workspace_edit, cursor_position }) | ||
523 | } | ||
524 | |||
525 | pub(crate) fn snippet_workspace_edit( | ||
526 | world: &WorldSnapshot, | ||
527 | source_change: SourceChange, | ||
528 | ) -> Result<lsp_ext::SnippetWorkspaceEdit> { | ||
529 | let mut document_changes: Vec<lsp_ext::SnippetDocumentChangeOperation> = Vec::new(); | ||
504 | for op in source_change.file_system_edits { | 530 | for op in source_change.file_system_edits { |
505 | let op = resource_op(&world, op)?; | 531 | let op = resource_op(&world, op)?; |
506 | document_changes.push(lsp_types::DocumentChangeOperation::Op(op)); | 532 | document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Op(op)); |
507 | } | 533 | } |
508 | for edit in source_change.source_file_edits { | 534 | for edit in source_change.source_file_edits { |
509 | let edit = text_document_edit(&world, edit)?; | 535 | let edit = snippet_text_document_edit(&world, source_change.is_snippet, edit)?; |
510 | document_changes.push(lsp_types::DocumentChangeOperation::Edit(edit)); | 536 | document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Edit(edit)); |
537 | } | ||
538 | let workspace_edit = | ||
539 | lsp_ext::SnippetWorkspaceEdit { changes: None, document_changes: Some(document_changes) }; | ||
540 | Ok(workspace_edit) | ||
541 | } | ||
542 | |||
543 | pub(crate) fn workspace_edit( | ||
544 | world: &WorldSnapshot, | ||
545 | source_change: SourceChange, | ||
546 | ) -> Result<lsp_types::WorkspaceEdit> { | ||
547 | assert!(!source_change.is_snippet); | ||
548 | snippet_workspace_edit(world, source_change).map(|it| it.into()) | ||
549 | } | ||
550 | |||
551 | impl From<lsp_ext::SnippetWorkspaceEdit> for lsp_types::WorkspaceEdit { | ||
552 | fn from(snippet_workspace_edit: lsp_ext::SnippetWorkspaceEdit) -> lsp_types::WorkspaceEdit { | ||
553 | lsp_types::WorkspaceEdit { | ||
554 | changes: None, | ||
555 | document_changes: snippet_workspace_edit.document_changes.map(|changes| { | ||
556 | lsp_types::DocumentChanges::Operations( | ||
557 | changes | ||
558 | .into_iter() | ||
559 | .map(|change| match change { | ||
560 | lsp_ext::SnippetDocumentChangeOperation::Op(op) => { | ||
561 | lsp_types::DocumentChangeOperation::Op(op) | ||
562 | } | ||
563 | lsp_ext::SnippetDocumentChangeOperation::Edit(edit) => { | ||
564 | lsp_types::DocumentChangeOperation::Edit( | ||
565 | lsp_types::TextDocumentEdit { | ||
566 | text_document: edit.text_document, | ||
567 | edits: edit | ||
568 | .edits | ||
569 | .into_iter() | ||
570 | .map(|edit| lsp_types::TextEdit { | ||
571 | range: edit.range, | ||
572 | new_text: edit.new_text, | ||
573 | }) | ||
574 | .collect(), | ||
575 | }, | ||
576 | ) | ||
577 | } | ||
578 | }) | ||
579 | .collect(), | ||
580 | ) | ||
581 | }), | ||
582 | } | ||
511 | } | 583 | } |
512 | let workspace_edit = lsp_types::WorkspaceEdit { | ||
513 | changes: None, | ||
514 | document_changes: Some(lsp_types::DocumentChanges::Operations(document_changes)), | ||
515 | }; | ||
516 | Ok(lsp_ext::SourceChange { label: source_change.label, workspace_edit, cursor_position }) | ||
517 | } | 584 | } |
518 | 585 | ||
519 | pub fn call_hierarchy_item( | 586 | pub fn call_hierarchy_item( |
@@ -571,22 +638,26 @@ fn main() <fold>{ | |||
571 | } | 638 | } |
572 | } | 639 | } |
573 | 640 | ||
574 | pub(crate) fn code_action(world: &WorldSnapshot, assist: Assist) -> Result<lsp_types::CodeAction> { | 641 | pub(crate) fn code_action(world: &WorldSnapshot, assist: Assist) -> Result<lsp_ext::CodeAction> { |
575 | let source_change = source_change(&world, assist.source_change)?; | 642 | let res = if assist.source_change.cursor_position.is_none() { |
576 | let arg = serde_json::to_value(source_change)?; | 643 | lsp_ext::CodeAction { |
577 | let title = assist.label; | 644 | title: assist.label, |
578 | let command = lsp_types::Command { | 645 | kind: Some(String::new()), |
579 | title: title.clone(), | 646 | edit: Some(snippet_workspace_edit(world, assist.source_change)?), |
580 | command: "rust-analyzer.applySourceChange".to_string(), | 647 | command: None, |
581 | arguments: Some(vec![arg]), | 648 | } |
582 | }; | 649 | } else { |
650 | assert!(!assist.source_change.is_snippet); | ||
651 | let source_change = source_change(&world, assist.source_change)?; | ||
652 | let arg = serde_json::to_value(source_change)?; | ||
653 | let title = assist.label; | ||
654 | let command = lsp_types::Command { | ||
655 | title: title.clone(), | ||
656 | command: "rust-analyzer.applySourceChange".to_string(), | ||
657 | arguments: Some(vec![arg]), | ||
658 | }; | ||
583 | 659 | ||
584 | Ok(lsp_types::CodeAction { | 660 | lsp_ext::CodeAction { title, kind: Some(String::new()), edit: None, command: Some(command) } |
585 | title, | 661 | }; |
586 | kind: Some(String::new()), | 662 | Ok(res) |
587 | diagnostics: None, | ||
588 | edit: None, | ||
589 | command: Some(command), | ||
590 | is_preferred: None, | ||
591 | }) | ||
592 | } | 663 | } |
diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs index 5011cc273..74676b3ee 100644 --- a/crates/rust-analyzer/tests/heavy_tests/main.rs +++ b/crates/rust-analyzer/tests/heavy_tests/main.rs | |||
@@ -333,29 +333,17 @@ fn main() {} | |||
333 | partial_result_params: PartialResultParams::default(), | 333 | partial_result_params: PartialResultParams::default(), |
334 | work_done_progress_params: WorkDoneProgressParams::default(), | 334 | work_done_progress_params: WorkDoneProgressParams::default(), |
335 | }, | 335 | }, |
336 | json!([ | 336 | json!([{ |
337 | { | 337 | "edit": { |
338 | "command": { | 338 | "documentChanges": [ |
339 | "arguments": [ | ||
340 | { | 339 | { |
341 | "cursorPosition": null, | 340 | "kind": "create", |
342 | "label": "Create module", | 341 | "uri": "file:///[..]/src/bar.rs" |
343 | "workspaceEdit": { | ||
344 | "documentChanges": [ | ||
345 | { | ||
346 | "kind": "create", | ||
347 | "uri": "file:///[..]/src/bar.rs" | ||
348 | } | ||
349 | ] | ||
350 | } | ||
351 | } | 342 | } |
352 | ], | 343 | ] |
353 | "command": "rust-analyzer.applySourceChange", | ||
354 | "title": "Create module" | ||
355 | }, | 344 | }, |
356 | "title": "Create module" | 345 | "title": "Create module" |
357 | } | 346 | }]), |
358 | ]), | ||
359 | ); | 347 | ); |
360 | 348 | ||
361 | server.request::<CodeActionRequest>( | 349 | server.request::<CodeActionRequest>( |
@@ -416,29 +404,17 @@ fn main() {{}} | |||
416 | partial_result_params: PartialResultParams::default(), | 404 | partial_result_params: PartialResultParams::default(), |
417 | work_done_progress_params: WorkDoneProgressParams::default(), | 405 | work_done_progress_params: WorkDoneProgressParams::default(), |
418 | }, | 406 | }, |
419 | json!([ | 407 | json!([{ |
420 | { | 408 | "edit": { |
421 | "command": { | 409 | "documentChanges": [ |
422 | "arguments": [ | ||
423 | { | 410 | { |
424 | "cursorPosition": null, | 411 | "kind": "create", |
425 | "label": "Create module", | 412 | "uri": "file://[..]/src/bar.rs" |
426 | "workspaceEdit": { | ||
427 | "documentChanges": [ | ||
428 | { | ||
429 | "kind": "create", | ||
430 | "uri": "file:///[..]/src/bar.rs" | ||
431 | } | ||
432 | ] | ||
433 | } | ||
434 | } | 413 | } |
435 | ], | 414 | ] |
436 | "command": "rust-analyzer.applySourceChange", | ||
437 | "title": "Create module" | ||
438 | }, | 415 | }, |
439 | "title": "Create module" | 416 | "title": "Create module" |
440 | } | 417 | }]), |
441 | ]), | ||
442 | ); | 418 | ); |
443 | 419 | ||
444 | server.request::<CodeActionRequest>( | 420 | server.request::<CodeActionRequest>( |
diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs index 0f34ce70e..71a57fba2 100644 --- a/crates/stdx/src/lib.rs +++ b/crates/stdx/src/lib.rs | |||
@@ -116,3 +116,11 @@ pub fn to_lower_snake_case(s: &str) -> String { | |||
116 | } | 116 | } |
117 | buf | 117 | buf |
118 | } | 118 | } |
119 | |||
120 | pub fn replace(buf: &mut String, from: char, to: &str) { | ||
121 | if !buf.contains(from) { | ||
122 | return; | ||
123 | } | ||
124 | // FIXME: do this in place. | ||
125 | *buf = buf.replace(from, to) | ||
126 | } | ||
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs index b1e3c328f..be2cfbaa2 100644 --- a/crates/test_utils/src/lib.rs +++ b/crates/test_utils/src/lib.rs | |||
@@ -7,7 +7,7 @@ | |||
7 | //! * marks (see the eponymous module). | 7 | //! * marks (see the eponymous module). |
8 | 8 | ||
9 | #[macro_use] | 9 | #[macro_use] |
10 | pub mod marks; | 10 | pub mod mark; |
11 | 11 | ||
12 | use std::{ | 12 | use std::{ |
13 | fs, | 13 | fs, |
diff --git a/crates/test_utils/src/marks.rs b/crates/test_utils/src/mark.rs index c3185e860..7c309a894 100644 --- a/crates/test_utils/src/marks.rs +++ b/crates/test_utils/src/mark.rs | |||
@@ -7,18 +7,18 @@ | |||
7 | //! ``` | 7 | //! ``` |
8 | //! #[test] | 8 | //! #[test] |
9 | //! fn test_foo() { | 9 | //! fn test_foo() { |
10 | //! covers!(test_foo); | 10 | //! mark::check!(test_foo); |
11 | //! } | 11 | //! } |
12 | //! ``` | 12 | //! ``` |
13 | //! | 13 | //! |
14 | //! and in the code under test you write | 14 | //! and in the code under test you write |
15 | //! | 15 | //! |
16 | //! ``` | 16 | //! ``` |
17 | //! # use test_utils::tested_by; | 17 | //! # use test_utils::mark; |
18 | //! # fn some_condition() -> bool { true } | 18 | //! # fn some_condition() -> bool { true } |
19 | //! fn foo() { | 19 | //! fn foo() { |
20 | //! if some_condition() { | 20 | //! if some_condition() { |
21 | //! tested_by!(test_foo); | 21 | //! mark::hit!(test_foo); |
22 | //! } | 22 | //! } |
23 | //! } | 23 | //! } |
24 | //! ``` | 24 | //! ``` |
@@ -29,43 +29,31 @@ | |||
29 | use std::sync::atomic::{AtomicUsize, Ordering}; | 29 | use std::sync::atomic::{AtomicUsize, Ordering}; |
30 | 30 | ||
31 | #[macro_export] | 31 | #[macro_export] |
32 | macro_rules! tested_by { | 32 | macro_rules! _hit { |
33 | ($ident:ident; force) => {{ | ||
34 | { | ||
35 | // sic! use call-site crate | ||
36 | crate::marks::$ident.fetch_add(1, std::sync::atomic::Ordering::SeqCst); | ||
37 | } | ||
38 | }}; | ||
39 | ($ident:ident) => {{ | 33 | ($ident:ident) => {{ |
40 | #[cfg(test)] | 34 | #[cfg(test)] |
41 | { | 35 | { |
42 | // sic! use call-site crate | 36 | extern "C" { |
43 | crate::marks::$ident.fetch_add(1, std::sync::atomic::Ordering::SeqCst); | 37 | #[no_mangle] |
38 | static $ident: std::sync::atomic::AtomicUsize; | ||
39 | } | ||
40 | unsafe { | ||
41 | $ident.fetch_add(1, std::sync::atomic::Ordering::SeqCst); | ||
42 | } | ||
44 | } | 43 | } |
45 | }}; | 44 | }}; |
46 | } | 45 | } |
46 | pub use _hit as hit; | ||
47 | 47 | ||
48 | #[macro_export] | 48 | #[macro_export] |
49 | macro_rules! covers { | 49 | macro_rules! _check { |
50 | // sic! use call-site crate | ||
51 | ($ident:ident) => { | 50 | ($ident:ident) => { |
52 | $crate::covers!(crate::$ident) | 51 | #[no_mangle] |
53 | }; | 52 | static $ident: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0); |
54 | ($krate:ident :: $ident:ident) => { | 53 | let _checker = $crate::mark::MarkChecker::new(&$ident); |
55 | let _checker = $crate::marks::MarkChecker::new(&$krate::marks::$ident); | ||
56 | }; | ||
57 | } | ||
58 | |||
59 | #[macro_export] | ||
60 | macro_rules! marks { | ||
61 | ($($ident:ident)*) => { | ||
62 | $( | ||
63 | #[allow(bad_style)] | ||
64 | pub static $ident: std::sync::atomic::AtomicUsize = | ||
65 | std::sync::atomic::AtomicUsize::new(0); | ||
66 | )* | ||
67 | }; | 54 | }; |
68 | } | 55 | } |
56 | pub use _check as check; | ||
69 | 57 | ||
70 | pub struct MarkChecker { | 58 | pub struct MarkChecker { |
71 | mark: &'static AtomicUsize, | 59 | mark: &'static AtomicUsize, |
diff --git a/docs/dev/README.md b/docs/dev/README.md index a20ead0b6..65cc9fc12 100644 --- a/docs/dev/README.md +++ b/docs/dev/README.md | |||
@@ -74,7 +74,7 @@ relevant test and execute it (VS Code includes an action for running a single | |||
74 | test). | 74 | test). |
75 | 75 | ||
76 | However, launching a VS Code instance with locally build language server is | 76 | However, launching a VS Code instance with locally build language server is |
77 | possible. There's **"Run Extension (Dev Server)"** launch configuration for this. | 77 | possible. There's **"Run Extension (Debug Build)"** launch configuration for this. |
78 | 78 | ||
79 | In general, I use one of the following workflows for fixing bugs and | 79 | In general, I use one of the following workflows for fixing bugs and |
80 | implementing features. | 80 | implementing features. |
@@ -86,7 +86,7 @@ then just do printf-driven development/debugging. As a sanity check after I'm | |||
86 | done, I use `cargo xtask install --server` and **Reload Window** action in VS | 86 | done, I use `cargo xtask install --server` and **Reload Window** action in VS |
87 | Code to sanity check that the thing works as I expect. | 87 | Code to sanity check that the thing works as I expect. |
88 | 88 | ||
89 | If the problem concerns only the VS Code extension, I use **Run Extension** | 89 | If the problem concerns only the VS Code extension, I use **Run Installed Extension** |
90 | launch configuration from `launch.json`. Notably, this uses the usual | 90 | launch configuration from `launch.json`. Notably, this uses the usual |
91 | `rust-analyzer` binary from `PATH`. For this it is important to have the following | 91 | `rust-analyzer` binary from `PATH`. For this it is important to have the following |
92 | in `setting.json` file: | 92 | in `setting.json` file: |
diff --git a/docs/dev/debugging.md b/docs/dev/debugging.md index 1aa392935..59a83f7d7 100644 --- a/docs/dev/debugging.md +++ b/docs/dev/debugging.md | |||
@@ -22,8 +22,8 @@ where **only** the `rust-analyzer` extension being debugged is enabled. | |||
22 | 22 | ||
23 | ## Debug TypeScript VSCode extension | 23 | ## Debug TypeScript VSCode extension |
24 | 24 | ||
25 | - `Run Extension` - runs the extension with the globally installed `rust-analyzer` binary. | 25 | - `Run Installed Extension` - runs the extension with the globally installed `rust-analyzer` binary. |
26 | - `Run Extension (Dev Server)` - runs extension with the locally built LSP server (`target/debug/rust-analyzer`). | 26 | - `Run Extension (Debug Build)` - runs extension with the locally built LSP server (`target/debug/rust-analyzer`). |
27 | 27 | ||
28 | TypeScript debugging is configured to watch your source edits and recompile. | 28 | TypeScript debugging is configured to watch your source edits and recompile. |
29 | To apply changes to an already running debug process, press <kbd>Ctrl+Shift+P</kbd> and run the following command in your `[Extension Development Host]` | 29 | To apply changes to an already running debug process, press <kbd>Ctrl+Shift+P</kbd> and run the following command in your `[Extension Development Host]` |
@@ -47,7 +47,7 @@ To apply changes to an already running debug process, press <kbd>Ctrl+Shift+P</k | |||
47 | debug = 2 | 47 | debug = 2 |
48 | ``` | 48 | ``` |
49 | 49 | ||
50 | - Select `Run Extension (Dev Server)` to run your locally built `target/debug/rust-analyzer`. | 50 | - Select `Run Extension (Debug Build)` to run your locally built `target/debug/rust-analyzer`. |
51 | 51 | ||
52 | - In the original VSCode window once again select the `Attach To Server` debug configuration. | 52 | - In the original VSCode window once again select the `Attach To Server` debug configuration. |
53 | 53 | ||
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md new file mode 100644 index 000000000..d2ec6c021 --- /dev/null +++ b/docs/dev/lsp-extensions.md | |||
@@ -0,0 +1,34 @@ | |||
1 | # LSP Extensions | ||
2 | |||
3 | This document describes LSP extensions used by rust-analyzer. | ||
4 | It's a best effort document, when in doubt, consult the source (and send a PR with clarification ;-) ). | ||
5 | We aim to upstream all non Rust-specific extensions to the protocol, but this is not a top priority. | ||
6 | All capabilities are enabled via `experimental` field of `ClientCapabilities`. | ||
7 | |||
8 | ## `SnippetTextEdit` | ||
9 | |||
10 | **Capability** | ||
11 | |||
12 | ```typescript | ||
13 | { | ||
14 | "snippetTextEdit": boolean | ||
15 | } | ||
16 | ``` | ||
17 | |||
18 | If this capability is set, `WorkspaceEdit`s returned from `codeAction` requests might contain `SnippetTextEdit`s instead of usual `TextEdit`s: | ||
19 | |||
20 | ```typescript | ||
21 | interface SnippetTextEdit extends TextEdit { | ||
22 | insertTextFormat?: InsertTextFormat; | ||
23 | } | ||
24 | ``` | ||
25 | |||
26 | ```typescript | ||
27 | export interface TextDocumentEdit { | ||
28 | textDocument: VersionedTextDocumentIdentifier; | ||
29 | edits: (TextEdit | SnippetTextEdit)[]; | ||
30 | } | ||
31 | ``` | ||
32 | |||
33 | When applying such code action, the editor should insert snippet, with tab stops and placeholder. | ||
34 | At the moment, rust-analyzer guarantees that only a single edit will have `InsertTextFormat.Snippet`. | ||
diff --git a/docs/user/assists.md b/docs/user/assists.md index 692fd4f52..4ad7ea59d 100644 --- a/docs/user/assists.md +++ b/docs/user/assists.md | |||
@@ -17,7 +17,7 @@ struct S; | |||
17 | struct S; | 17 | struct S; |
18 | 18 | ||
19 | impl Debug for S { | 19 | impl Debug for S { |
20 | 20 | $0 | |
21 | } | 21 | } |
22 | ``` | 22 | ``` |
23 | 23 | ||
@@ -33,7 +33,7 @@ struct Point { | |||
33 | } | 33 | } |
34 | 34 | ||
35 | // AFTER | 35 | // AFTER |
36 | #[derive()] | 36 | #[derive($0)] |
37 | struct Point { | 37 | struct Point { |
38 | x: u32, | 38 | x: u32, |
39 | y: u32, | 39 | y: u32, |
@@ -77,7 +77,7 @@ fn foo() { | |||
77 | } | 77 | } |
78 | 78 | ||
79 | fn bar(arg: &str, baz: Baz) { | 79 | fn bar(arg: &str, baz: Baz) { |
80 | todo!() | 80 | ${0:todo!()} |
81 | } | 81 | } |
82 | 82 | ||
83 | ``` | 83 | ``` |
@@ -105,16 +105,16 @@ Adds a new inherent impl for a type. | |||
105 | ```rust | 105 | ```rust |
106 | // BEFORE | 106 | // BEFORE |
107 | struct Ctx<T: Clone> { | 107 | struct Ctx<T: Clone> { |
108 | data: T,┃ | 108 | data: T,┃ |
109 | } | 109 | } |
110 | 110 | ||
111 | // AFTER | 111 | // AFTER |
112 | struct Ctx<T: Clone> { | 112 | struct Ctx<T: Clone> { |
113 | data: T, | 113 | data: T, |
114 | } | 114 | } |
115 | 115 | ||
116 | impl<T: Clone> Ctx<T> { | 116 | impl<T: Clone> Ctx<T> { |
117 | 117 | $0 | |
118 | } | 118 | } |
119 | ``` | 119 | ``` |
120 | 120 | ||
@@ -146,7 +146,7 @@ trait Trait { | |||
146 | impl Trait for () { | 146 | impl Trait for () { |
147 | Type X = (); | 147 | Type X = (); |
148 | fn foo(&self) {} | 148 | fn foo(&self) {} |
149 | fn bar(&self) {} | 149 | $0fn bar(&self) {} |
150 | 150 | ||
151 | } | 151 | } |
152 | ``` | 152 | ``` |
@@ -176,7 +176,7 @@ trait Trait<T> { | |||
176 | 176 | ||
177 | impl Trait<u32> for () { | 177 | impl Trait<u32> for () { |
178 | fn foo(&self) -> u32 { | 178 | fn foo(&self) -> u32 { |
179 | todo!() | 179 | ${0:todo!()} |
180 | } | 180 | } |
181 | 181 | ||
182 | } | 182 | } |
@@ -198,11 +198,29 @@ struct Ctx<T: Clone> { | |||
198 | } | 198 | } |
199 | 199 | ||
200 | impl<T: Clone> Ctx<T> { | 200 | impl<T: Clone> Ctx<T> { |
201 | fn new(data: T) -> Self { Self { data } } | 201 | fn $0new(data: T) -> Self { Self { data } } |
202 | } | 202 | } |
203 | 203 | ||
204 | ``` | 204 | ``` |
205 | 205 | ||
206 | ## `add_turbo_fish` | ||
207 | |||
208 | Adds `::<_>` to a call of a generic method or function. | ||
209 | |||
210 | ```rust | ||
211 | // BEFORE | ||
212 | fn make<T>() -> T { todo!() } | ||
213 | fn main() { | ||
214 | let x = make┃(); | ||
215 | } | ||
216 | |||
217 | // AFTER | ||
218 | fn make<T>() -> T { todo!() } | ||
219 | fn main() { | ||
220 | let x = make::<${0:_}>(); | ||
221 | } | ||
222 | ``` | ||
223 | |||
206 | ## `apply_demorgan` | 224 | ## `apply_demorgan` |
207 | 225 | ||
208 | Apply [De Morgan's law](https://en.wikipedia.org/wiki/De_Morgan%27s_laws). | 226 | Apply [De Morgan's law](https://en.wikipedia.org/wiki/De_Morgan%27s_laws). |
@@ -250,7 +268,7 @@ Change the function's return type to Result. | |||
250 | fn foo() -> i32┃ { 42i32 } | 268 | fn foo() -> i32┃ { 42i32 } |
251 | 269 | ||
252 | // AFTER | 270 | // AFTER |
253 | fn foo() -> Result<i32, > { Ok(42i32) } | 271 | fn foo() -> Result<i32, ${0:_}> { Ok(42i32) } |
254 | ``` | 272 | ``` |
255 | 273 | ||
256 | ## `change_visibility` | 274 | ## `change_visibility` |
@@ -307,12 +325,34 @@ enum Action { Move { distance: u32 }, Stop } | |||
307 | 325 | ||
308 | fn handle(action: Action) { | 326 | fn handle(action: Action) { |
309 | match action { | 327 | match action { |
310 | Action::Move { distance } => {} | 328 | $0Action::Move { distance } => {} |
311 | Action::Stop => {} | 329 | Action::Stop => {} |
312 | } | 330 | } |
313 | } | 331 | } |
314 | ``` | 332 | ``` |
315 | 333 | ||
334 | ## `fix_visibility` | ||
335 | |||
336 | Makes inaccessible item public. | ||
337 | |||
338 | ```rust | ||
339 | // BEFORE | ||
340 | mod m { | ||
341 | fn frobnicate() {} | ||
342 | } | ||
343 | fn main() { | ||
344 | m::frobnicate┃() {} | ||
345 | } | ||
346 | |||
347 | // AFTER | ||
348 | mod m { | ||
349 | $0pub(crate) fn frobnicate() {} | ||
350 | } | ||
351 | fn main() { | ||
352 | m::frobnicate() {} | ||
353 | } | ||
354 | ``` | ||
355 | |||
316 | ## `flip_binexpr` | 356 | ## `flip_binexpr` |
317 | 357 | ||
318 | Flips operands of a binary expression. | 358 | Flips operands of a binary expression. |
@@ -386,7 +426,7 @@ fn main() { | |||
386 | 426 | ||
387 | // AFTER | 427 | // AFTER |
388 | fn main() { | 428 | fn main() { |
389 | let var_name = (1 + 2); | 429 | let $0var_name = (1 + 2); |
390 | var_name * 4; | 430 | var_name * 4; |
391 | } | 431 | } |
392 | ``` | 432 | ``` |
@@ -693,7 +733,7 @@ fn main() { | |||
693 | let x: Result<i32, i32> = Result::Ok(92); | 733 | let x: Result<i32, i32> = Result::Ok(92); |
694 | let y = match x { | 734 | let y = match x { |
695 | Ok(a) => a, | 735 | Ok(a) => a, |
696 | _ => unreachable!(), | 736 | $0_ => unreachable!(), |
697 | }; | 737 | }; |
698 | } | 738 | } |
699 | ``` | 739 | ``` |
diff --git a/docs/user/readme.adoc b/docs/user/readme.adoc index d750c7705..40ed54809 100644 --- a/docs/user/readme.adoc +++ b/docs/user/readme.adoc | |||
@@ -63,7 +63,7 @@ The server binary is stored in: | |||
63 | * macOS: `~/Library/Application Support/Code/User/globalStorage/matklad.rust-analyzer` | 63 | * macOS: `~/Library/Application Support/Code/User/globalStorage/matklad.rust-analyzer` |
64 | * Windows: `%APPDATA%\Code\User\globalStorage\matklad.rust-analyzer` | 64 | * Windows: `%APPDATA%\Code\User\globalStorage\matklad.rust-analyzer` |
65 | 65 | ||
66 | Note that we only support the latest version of VS Code. | 66 | Note that we only support two most recent versions of VS Code. |
67 | 67 | ||
68 | ==== Updates | 68 | ==== Updates |
69 | 69 | ||
@@ -249,7 +249,7 @@ If it worked, you should see "rust-analyzer, Line X, Column Y" on the left side | |||
249 | 249 | ||
250 | If you get an error saying `No such file or directory: 'rust-analyzer'`, see the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>> section on installing the language server binary. | 250 | If you get an error saying `No such file or directory: 'rust-analyzer'`, see the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>> section on installing the language server binary. |
251 | 251 | ||
252 | === Gnome Builder | 252 | === GNOME Builder |
253 | 253 | ||
254 | Prerequisites: You have installed the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>>. | 254 | Prerequisites: You have installed the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>>. |
255 | 255 | ||
diff --git a/editors/code/package.json b/editors/code/package.json index 4e7e3faf7..d899f60e3 100644 --- a/editors/code/package.json +++ b/editors/code/package.json | |||
@@ -21,7 +21,7 @@ | |||
21 | "Programming Languages" | 21 | "Programming Languages" |
22 | ], | 22 | ], |
23 | "engines": { | 23 | "engines": { |
24 | "vscode": "^1.45.0" | 24 | "vscode": "^1.44.0" |
25 | }, | 25 | }, |
26 | "enableProposedApi": true, | 26 | "enableProposedApi": true, |
27 | "scripts": { | 27 | "scripts": { |
@@ -41,7 +41,7 @@ | |||
41 | "@rollup/plugin-node-resolve": "^7.1.3", | 41 | "@rollup/plugin-node-resolve": "^7.1.3", |
42 | "@types/node": "^12.12.39", | 42 | "@types/node": "^12.12.39", |
43 | "@types/node-fetch": "^2.5.7", | 43 | "@types/node-fetch": "^2.5.7", |
44 | "@types/vscode": "^1.45.0", | 44 | "@types/vscode": "^1.44.0", |
45 | "@typescript-eslint/eslint-plugin": "^2.33.0", | 45 | "@typescript-eslint/eslint-plugin": "^2.33.0", |
46 | "@typescript-eslint/parser": "^2.33.0", | 46 | "@typescript-eslint/parser": "^2.33.0", |
47 | "eslint": "^6.8.0", | 47 | "eslint": "^6.8.0", |
@@ -443,6 +443,26 @@ | |||
443 | "type": "object", | 443 | "type": "object", |
444 | "default": {}, | 444 | "default": {}, |
445 | "description": "Optional settings passed to the debug engine. Example:\n{ \"lldb\": { \"terminal\":\"external\"} }" | 445 | "description": "Optional settings passed to the debug engine. Example:\n{ \"lldb\": { \"terminal\":\"external\"} }" |
446 | }, | ||
447 | "rust-analyzer.lens.enable": { | ||
448 | "description": "Whether to show CodeLens in Rust files.", | ||
449 | "type": "boolean", | ||
450 | "default": true | ||
451 | }, | ||
452 | "rust-analyzer.lens.run": { | ||
453 | "markdownDescription": "Whether to show Run lens. Only applies when `#rust-analyzer.lens.enable#` is set.", | ||
454 | "type": "boolean", | ||
455 | "default": true | ||
456 | }, | ||
457 | "rust-analyzer.lens.debug": { | ||
458 | "markdownDescription": "Whether to show Debug lens. Only applies when `#rust-analyzer.lens.enable#` is set.", | ||
459 | "type": "boolean", | ||
460 | "default": true | ||
461 | }, | ||
462 | "rust-analyzer.lens.implementations": { | ||
463 | "markdownDescription": "Whether to show Implementations lens. Only applies when `#rust-analyzer.lens.enable#` is set.", | ||
464 | "type": "boolean", | ||
465 | "default": true | ||
446 | } | 466 | } |
447 | } | 467 | } |
448 | }, | 468 | }, |
@@ -604,6 +624,9 @@ | |||
604 | { | 624 | { |
605 | "language": "rust", | 625 | "language": "rust", |
606 | "scopes": { | 626 | "scopes": { |
627 | "macro": [ | ||
628 | "entity.name.function.macro.rust" | ||
629 | ], | ||
607 | "attribute": [ | 630 | "attribute": [ |
608 | "meta.attribute.rust" | 631 | "meta.attribute.rust" |
609 | ], | 632 | ], |
diff --git a/editors/code/src/cargo.ts b/editors/code/src/cargo.ts index 28c7de992..6a41873d0 100644 --- a/editors/code/src/cargo.ts +++ b/editors/code/src/cargo.ts | |||
@@ -51,10 +51,14 @@ export class Cargo { | |||
51 | 51 | ||
52 | // arguments for a runnable from the quick pick should be updated. | 52 | // arguments for a runnable from the quick pick should be updated. |
53 | // see crates\rust-analyzer\src\main_loop\handlers.rs, handle_code_lens | 53 | // see crates\rust-analyzer\src\main_loop\handlers.rs, handle_code_lens |
54 | if (cargoArgs[0] === "run") { | 54 | switch (cargoArgs[0]) { |
55 | cargoArgs[0] = "build"; | 55 | case "run": cargoArgs[0] = "build"; break; |
56 | } else if (cargoArgs.indexOf("--no-run") === -1) { | 56 | case "test": { |
57 | cargoArgs.push("--no-run"); | 57 | if (cargoArgs.indexOf("--no-run") === -1) { |
58 | cargoArgs.push("--no-run"); | ||
59 | } | ||
60 | break; | ||
61 | } | ||
58 | } | 62 | } |
59 | 63 | ||
60 | let artifacts = await this.artifactsFromArgs(cargoArgs); | 64 | let artifacts = await this.artifactsFromArgs(cargoArgs); |
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts index cffdcf11a..fac1a0be3 100644 --- a/editors/code/src/client.ts +++ b/editors/code/src/client.ts | |||
@@ -31,24 +31,79 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient | |||
31 | const res = await next(document, token); | 31 | const res = await next(document, token); |
32 | if (res === undefined) throw new Error('busy'); | 32 | if (res === undefined) throw new Error('busy'); |
33 | return res; | 33 | return res; |
34 | }, | ||
35 | async provideCodeActions(document: vscode.TextDocument, range: vscode.Range, context: vscode.CodeActionContext, token: vscode.CancellationToken, _next: lc.ProvideCodeActionsSignature) { | ||
36 | const params: lc.CodeActionParams = { | ||
37 | textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document), | ||
38 | range: client.code2ProtocolConverter.asRange(range), | ||
39 | context: client.code2ProtocolConverter.asCodeActionContext(context) | ||
40 | }; | ||
41 | return client.sendRequest(lc.CodeActionRequest.type, params, token).then((values) => { | ||
42 | if (values === null) return undefined; | ||
43 | const result: (vscode.CodeAction | vscode.Command)[] = []; | ||
44 | for (const item of values) { | ||
45 | if (lc.CodeAction.is(item)) { | ||
46 | const action = client.protocol2CodeConverter.asCodeAction(item); | ||
47 | if (isSnippetEdit(item)) { | ||
48 | action.command = { | ||
49 | command: "rust-analyzer.applySnippetWorkspaceEdit", | ||
50 | title: "", | ||
51 | arguments: [action.edit], | ||
52 | }; | ||
53 | action.edit = undefined; | ||
54 | } | ||
55 | result.push(action); | ||
56 | } else { | ||
57 | const command = client.protocol2CodeConverter.asCommand(item); | ||
58 | result.push(command); | ||
59 | } | ||
60 | } | ||
61 | return result; | ||
62 | }, | ||
63 | (_error) => undefined | ||
64 | ); | ||
34 | } | 65 | } |
66 | |||
35 | } as any | 67 | } as any |
36 | }; | 68 | }; |
37 | 69 | ||
38 | const res = new lc.LanguageClient( | 70 | const client = new lc.LanguageClient( |
39 | 'rust-analyzer', | 71 | 'rust-analyzer', |
40 | 'Rust Analyzer Language Server', | 72 | 'Rust Analyzer Language Server', |
41 | serverOptions, | 73 | serverOptions, |
42 | clientOptions, | 74 | clientOptions, |
43 | ); | 75 | ); |
44 | 76 | ||
45 | // To turn on all proposed features use: res.registerProposedFeatures(); | 77 | // To turn on all proposed features use: client.registerProposedFeatures(); |
46 | // Here we want to enable CallHierarchyFeature and SemanticTokensFeature | 78 | // Here we want to enable CallHierarchyFeature and SemanticTokensFeature |
47 | // since they are available on stable. | 79 | // since they are available on stable. |
48 | // Note that while these features are stable in vscode their LSP protocol | 80 | // Note that while these features are stable in vscode their LSP protocol |
49 | // implementations are still in the "proposed" category for 3.16. | 81 | // implementations are still in the "proposed" category for 3.16. |
50 | res.registerFeature(new CallHierarchyFeature(res)); | 82 | client.registerFeature(new CallHierarchyFeature(client)); |
51 | res.registerFeature(new SemanticTokensFeature(res)); | 83 | client.registerFeature(new SemanticTokensFeature(client)); |
84 | client.registerFeature(new SnippetTextEditFeature()); | ||
85 | |||
86 | return client; | ||
87 | } | ||
52 | 88 | ||
53 | return res; | 89 | class SnippetTextEditFeature implements lc.StaticFeature { |
90 | fillClientCapabilities(capabilities: lc.ClientCapabilities): void { | ||
91 | const caps: any = capabilities.experimental ?? {}; | ||
92 | caps.snippetTextEdit = true; | ||
93 | capabilities.experimental = caps; | ||
94 | } | ||
95 | initialize(_capabilities: lc.ServerCapabilities<any>, _documentSelector: lc.DocumentSelector | undefined): void { | ||
96 | } | ||
97 | } | ||
98 | |||
99 | function isSnippetEdit(action: lc.CodeAction): boolean { | ||
100 | const documentChanges = action.edit?.documentChanges ?? []; | ||
101 | for (const edit of documentChanges) { | ||
102 | if (lc.TextDocumentEdit.is(edit)) { | ||
103 | if (edit.edits.some((indel) => (indel as any).insertTextFormat === lc.InsertTextFormat.Snippet)) { | ||
104 | return true; | ||
105 | } | ||
106 | } | ||
107 | } | ||
108 | return false; | ||
54 | } | 109 | } |
diff --git a/editors/code/src/commands/index.ts b/editors/code/src/commands/index.ts index bdb7fc3b0..0937b495c 100644 --- a/editors/code/src/commands/index.ts +++ b/editors/code/src/commands/index.ts | |||
@@ -4,6 +4,7 @@ import * as ra from '../rust-analyzer-api'; | |||
4 | 4 | ||
5 | import { Ctx, Cmd } from '../ctx'; | 5 | import { Ctx, Cmd } from '../ctx'; |
6 | import * as sourceChange from '../source_change'; | 6 | import * as sourceChange from '../source_change'; |
7 | import { assert } from '../util'; | ||
7 | 8 | ||
8 | export * from './analyzer_status'; | 9 | export * from './analyzer_status'; |
9 | export * from './matching_brace'; | 10 | export * from './matching_brace'; |
@@ -51,3 +52,37 @@ export function selectAndApplySourceChange(ctx: Ctx): Cmd { | |||
51 | } | 52 | } |
52 | }; | 53 | }; |
53 | } | 54 | } |
55 | |||
56 | export function applySnippetWorkspaceEdit(_ctx: Ctx): Cmd { | ||
57 | return async (edit: vscode.WorkspaceEdit) => { | ||
58 | assert(edit.entries().length === 1, `bad ws edit: ${JSON.stringify(edit)}`); | ||
59 | const [uri, edits] = edit.entries()[0]; | ||
60 | |||
61 | const editor = vscode.window.visibleTextEditors.find((it) => it.document.uri.toString() === uri.toString()); | ||
62 | if (!editor) return; | ||
63 | |||
64 | let editWithSnippet: vscode.TextEdit | undefined = undefined; | ||
65 | let lineDelta = 0; | ||
66 | await editor.edit((builder) => { | ||
67 | for (const indel of edits) { | ||
68 | const isSnippet = indel.newText.indexOf('$0') !== -1 || indel.newText.indexOf('${') !== -1; | ||
69 | if (isSnippet) { | ||
70 | editWithSnippet = indel; | ||
71 | } else { | ||
72 | if (!editWithSnippet) { | ||
73 | lineDelta = (indel.newText.match(/\n/g) || []).length - (indel.range.end.line - indel.range.start.line); | ||
74 | } | ||
75 | builder.replace(indel.range, indel.newText); | ||
76 | } | ||
77 | } | ||
78 | }); | ||
79 | if (editWithSnippet) { | ||
80 | const snip = editWithSnippet as vscode.TextEdit; | ||
81 | const range = snip.range.with( | ||
82 | snip.range.start.with(snip.range.start.line + lineDelta), | ||
83 | snip.range.end.with(snip.range.end.line + lineDelta), | ||
84 | ); | ||
85 | await editor.insertSnippet(new vscode.SnippetString(snip.newText), range); | ||
86 | } | ||
87 | }; | ||
88 | } | ||
diff --git a/editors/code/src/commands/runnables.ts b/editors/code/src/commands/runnables.ts index b1d93fc34..0bd30fb07 100644 --- a/editors/code/src/commands/runnables.ts +++ b/editors/code/src/commands/runnables.ts | |||
@@ -7,7 +7,7 @@ import { startDebugSession, getDebugConfiguration } from '../debug'; | |||
7 | 7 | ||
8 | const quickPickButtons = [{ iconPath: new vscode.ThemeIcon("save"), tooltip: "Save as a launch.json configurtation." }]; | 8 | const quickPickButtons = [{ iconPath: new vscode.ThemeIcon("save"), tooltip: "Save as a launch.json configurtation." }]; |
9 | 9 | ||
10 | async function selectRunnable(ctx: Ctx, prevRunnable?: RunnableQuickPick, showButtons: boolean = true): Promise<RunnableQuickPick | undefined> { | 10 | async function selectRunnable(ctx: Ctx, prevRunnable?: RunnableQuickPick, debuggeeOnly = false, showButtons: boolean = true): Promise<RunnableQuickPick | undefined> { |
11 | const editor = ctx.activeRustEditor; | 11 | const editor = ctx.activeRustEditor; |
12 | const client = ctx.client; | 12 | const client = ctx.client; |
13 | if (!editor || !client) return; | 13 | if (!editor || !client) return; |
@@ -33,9 +33,20 @@ async function selectRunnable(ctx: Ctx, prevRunnable?: RunnableQuickPick, showBu | |||
33 | ) { | 33 | ) { |
34 | continue; | 34 | continue; |
35 | } | 35 | } |
36 | |||
37 | if (debuggeeOnly && (r.label.startsWith('doctest') || r.label.startsWith('cargo'))) { | ||
38 | continue; | ||
39 | } | ||
36 | items.push(new RunnableQuickPick(r)); | 40 | items.push(new RunnableQuickPick(r)); |
37 | } | 41 | } |
38 | 42 | ||
43 | if (items.length === 0) { | ||
44 | // it is the debug case, run always has at least 'cargo check ...' | ||
45 | // see crates\rust-analyzer\src\main_loop\handlers.rs, handle_runnables | ||
46 | vscode.window.showErrorMessage("There's no debug target!"); | ||
47 | return; | ||
48 | } | ||
49 | |||
39 | return await new Promise((resolve) => { | 50 | return await new Promise((resolve) => { |
40 | const disposables: vscode.Disposable[] = []; | 51 | const disposables: vscode.Disposable[] = []; |
41 | const close = (result?: RunnableQuickPick) => { | 52 | const close = (result?: RunnableQuickPick) => { |
@@ -107,7 +118,7 @@ export function debug(ctx: Ctx): Cmd { | |||
107 | let prevDebuggee: RunnableQuickPick | undefined; | 118 | let prevDebuggee: RunnableQuickPick | undefined; |
108 | 119 | ||
109 | return async () => { | 120 | return async () => { |
110 | const item = await selectRunnable(ctx, prevDebuggee); | 121 | const item = await selectRunnable(ctx, prevDebuggee, true); |
111 | if (!item) return; | 122 | if (!item) return; |
112 | 123 | ||
113 | item.detail = 'restart'; | 124 | item.detail = 'restart'; |
@@ -147,7 +158,7 @@ async function makeDebugConfig(ctx: Ctx, item: RunnableQuickPick): Promise<void> | |||
147 | 158 | ||
148 | export function newDebugConfig(ctx: Ctx): Cmd { | 159 | export function newDebugConfig(ctx: Ctx): Cmd { |
149 | return async () => { | 160 | return async () => { |
150 | const item = await selectRunnable(ctx, undefined, false); | 161 | const item = await selectRunnable(ctx, undefined, true, false); |
151 | if (!item) return; | 162 | if (!item) return; |
152 | 163 | ||
153 | await makeDebugConfig(ctx, item); | 164 | await makeDebugConfig(ctx, item); |
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index 1652827c3..ee294fbe3 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts | |||
@@ -16,6 +16,10 @@ export class Config { | |||
16 | "files", | 16 | "files", |
17 | "highlighting", | 17 | "highlighting", |
18 | "updates.channel", | 18 | "updates.channel", |
19 | "lens.enable", | ||
20 | "lens.run", | ||
21 | "lens.debug", | ||
22 | "lens.implementations", | ||
19 | ] | 23 | ] |
20 | .map(opt => `${this.rootSection}.${opt}`); | 24 | .map(opt => `${this.rootSection}.${opt}`); |
21 | 25 | ||
@@ -119,4 +123,13 @@ export class Config { | |||
119 | sourceFileMap: sourceFileMap | 123 | sourceFileMap: sourceFileMap |
120 | }; | 124 | }; |
121 | } | 125 | } |
126 | |||
127 | get lens() { | ||
128 | return { | ||
129 | enable: this.get<boolean>("lens.enable"), | ||
130 | run: this.get<boolean>("lens.run"), | ||
131 | debug: this.get<boolean>("lens.debug"), | ||
132 | implementations: this.get<boolean>("lens.implementations"), | ||
133 | }; | ||
134 | } | ||
122 | } | 135 | } |
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index c015460b8..ac3bb365e 100644 --- a/editors/code/src/main.ts +++ b/editors/code/src/main.ts | |||
@@ -91,6 +91,7 @@ export async function activate(context: vscode.ExtensionContext) { | |||
91 | ctx.registerCommand('debugSingle', commands.debugSingle); | 91 | ctx.registerCommand('debugSingle', commands.debugSingle); |
92 | ctx.registerCommand('showReferences', commands.showReferences); | 92 | ctx.registerCommand('showReferences', commands.showReferences); |
93 | ctx.registerCommand('applySourceChange', commands.applySourceChange); | 93 | ctx.registerCommand('applySourceChange', commands.applySourceChange); |
94 | ctx.registerCommand('applySnippetWorkspaceEdit', commands.applySnippetWorkspaceEdit); | ||
94 | ctx.registerCommand('selectAndApplySourceChange', commands.selectAndApplySourceChange); | 95 | ctx.registerCommand('selectAndApplySourceChange', commands.selectAndApplySourceChange); |
95 | 96 | ||
96 | ctx.pushCleanup(activateTaskProvider(workspaceFolder)); | 97 | ctx.pushCleanup(activateTaskProvider(workspaceFolder)); |
diff --git a/xtask/tests/tidy.rs b/xtask/tests/tidy.rs index b8e8860ba..2e9fcf07c 100644 --- a/xtask/tests/tidy.rs +++ b/xtask/tests/tidy.rs | |||
@@ -57,6 +57,7 @@ fn check_todo(path: &Path, text: &str) { | |||
57 | "tests/generated.rs", | 57 | "tests/generated.rs", |
58 | "handlers/add_missing_impl_members.rs", | 58 | "handlers/add_missing_impl_members.rs", |
59 | "handlers/add_function.rs", | 59 | "handlers/add_function.rs", |
60 | "handlers/add_turbo_fish.rs", | ||
60 | // To support generating `todo!()` in assists, we have `expr_todo()` in ast::make. | 61 | // To support generating `todo!()` in assists, we have `expr_todo()` in ast::make. |
61 | "ast/make.rs", | 62 | "ast/make.rs", |
62 | ]; | 63 | ]; |