diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2021-06-14 17:46:25 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2021-06-14 17:46:25 +0100 |
commit | 38ae18b7592f97a7058d97928307bccbd881a582 (patch) | |
tree | 5f6f59f48f05999495654bf2e4250e029e6f010f /crates/ide_assists/src | |
parent | 401d79ac0674ec62689949c3a531836420cb9beb (diff) | |
parent | 4768e5fb23c058eba90f0a1dcd6e9d5c0ecdee1b (diff) |
Merge #9272
9272: internal: move diagnostics to a dedicated crate r=matklad a=matklad
bors r+
Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates/ide_assists/src')
-rw-r--r-- | crates/ide_assists/src/lib.rs | 163 | ||||
-rw-r--r-- | crates/ide_assists/src/tests.rs | 26 |
2 files changed, 32 insertions, 157 deletions
diff --git a/crates/ide_assists/src/lib.rs b/crates/ide_assists/src/lib.rs index 331a6df2b..fa378a622 100644 --- a/crates/ide_assists/src/lib.rs +++ b/crates/ide_assists/src/lib.rs | |||
@@ -17,156 +17,31 @@ mod tests; | |||
17 | pub mod utils; | 17 | pub mod utils; |
18 | pub mod path_transform; | 18 | pub mod path_transform; |
19 | 19 | ||
20 | use std::str::FromStr; | ||
21 | |||
22 | use hir::Semantics; | 20 | use hir::Semantics; |
23 | use ide_db::{base_db::FileRange, label::Label, source_change::SourceChange, RootDatabase}; | 21 | use ide_db::{base_db::FileRange, RootDatabase}; |
24 | use syntax::TextRange; | 22 | use syntax::TextRange; |
25 | 23 | ||
26 | pub(crate) use crate::assist_context::{AssistContext, Assists}; | 24 | pub(crate) use crate::assist_context::{AssistContext, Assists}; |
27 | 25 | ||
28 | pub use assist_config::AssistConfig; | 26 | pub use assist_config::AssistConfig; |
29 | 27 | pub use ide_db::assists::{ | |
30 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | 28 | Assist, AssistId, AssistKind, AssistResolveStrategy, GroupLabel, SingleResolve, |
31 | pub enum AssistKind { | 29 | }; |
32 | // FIXME: does the None variant make sense? Probably not. | 30 | |
33 | None, | 31 | /// Return all the assists applicable at the given position. |
34 | 32 | pub fn assists( | |
35 | QuickFix, | 33 | db: &RootDatabase, |
36 | Generate, | 34 | config: &AssistConfig, |
37 | Refactor, | 35 | resolve: AssistResolveStrategy, |
38 | RefactorExtract, | 36 | range: FileRange, |
39 | RefactorInline, | 37 | ) -> Vec<Assist> { |
40 | RefactorRewrite, | 38 | let sema = Semantics::new(db); |
41 | } | 39 | let ctx = AssistContext::new(sema, config, range); |
42 | 40 | let mut acc = Assists::new(&ctx, resolve); | |
43 | impl AssistKind { | 41 | handlers::all().iter().for_each(|handler| { |
44 | pub fn contains(self, other: AssistKind) -> bool { | 42 | handler(&mut acc, &ctx); |
45 | if self == other { | 43 | }); |
46 | return true; | 44 | acc.finish() |
47 | } | ||
48 | |||
49 | match self { | ||
50 | AssistKind::None | AssistKind::Generate => true, | ||
51 | AssistKind::Refactor => match other { | ||
52 | AssistKind::RefactorExtract | ||
53 | | AssistKind::RefactorInline | ||
54 | | AssistKind::RefactorRewrite => true, | ||
55 | _ => false, | ||
56 | }, | ||
57 | _ => false, | ||
58 | } | ||
59 | } | ||
60 | |||
61 | pub fn name(&self) -> &str { | ||
62 | match self { | ||
63 | AssistKind::None => "None", | ||
64 | AssistKind::QuickFix => "QuickFix", | ||
65 | AssistKind::Generate => "Generate", | ||
66 | AssistKind::Refactor => "Refactor", | ||
67 | AssistKind::RefactorExtract => "RefactorExtract", | ||
68 | AssistKind::RefactorInline => "RefactorInline", | ||
69 | AssistKind::RefactorRewrite => "RefactorRewrite", | ||
70 | } | ||
71 | } | ||
72 | } | ||
73 | |||
74 | impl FromStr for AssistKind { | ||
75 | type Err = String; | ||
76 | |||
77 | fn from_str(s: &str) -> Result<Self, Self::Err> { | ||
78 | match s { | ||
79 | "None" => Ok(AssistKind::None), | ||
80 | "QuickFix" => Ok(AssistKind::QuickFix), | ||
81 | "Generate" => Ok(AssistKind::Generate), | ||
82 | "Refactor" => Ok(AssistKind::Refactor), | ||
83 | "RefactorExtract" => Ok(AssistKind::RefactorExtract), | ||
84 | "RefactorInline" => Ok(AssistKind::RefactorInline), | ||
85 | "RefactorRewrite" => Ok(AssistKind::RefactorRewrite), | ||
86 | unknown => Err(format!("Unknown AssistKind: '{}'", unknown)), | ||
87 | } | ||
88 | } | ||
89 | } | ||
90 | |||
91 | /// Unique identifier of the assist, should not be shown to the user | ||
92 | /// directly. | ||
93 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
94 | pub struct AssistId(pub &'static str, pub AssistKind); | ||
95 | |||
96 | /// A way to control how many asssist to resolve during the assist resolution. | ||
97 | /// When an assist is resolved, its edits are calculated that might be costly to always do by default. | ||
98 | #[derive(Debug)] | ||
99 | pub enum AssistResolveStrategy { | ||
100 | /// No assists should be resolved. | ||
101 | None, | ||
102 | /// All assists should be resolved. | ||
103 | All, | ||
104 | /// Only a certain assist should be resolved. | ||
105 | Single(SingleResolve), | ||
106 | } | ||
107 | |||
108 | /// Hold the [`AssistId`] data of a certain assist to resolve. | ||
109 | /// The original id object cannot be used due to a `'static` lifetime | ||
110 | /// and the requirement to construct this struct dynamically during the resolve handling. | ||
111 | #[derive(Debug)] | ||
112 | pub struct SingleResolve { | ||
113 | /// The id of the assist. | ||
114 | pub assist_id: String, | ||
115 | // The kind of the assist. | ||
116 | pub assist_kind: AssistKind, | ||
117 | } | ||
118 | |||
119 | impl AssistResolveStrategy { | ||
120 | pub fn should_resolve(&self, id: &AssistId) -> bool { | ||
121 | match self { | ||
122 | AssistResolveStrategy::None => false, | ||
123 | AssistResolveStrategy::All => true, | ||
124 | AssistResolveStrategy::Single(single_resolve) => { | ||
125 | single_resolve.assist_id == id.0 && single_resolve.assist_kind == id.1 | ||
126 | } | ||
127 | } | ||
128 | } | ||
129 | } | ||
130 | |||
131 | #[derive(Clone, Debug)] | ||
132 | pub struct GroupLabel(pub String); | ||
133 | |||
134 | #[derive(Debug, Clone)] | ||
135 | pub struct Assist { | ||
136 | pub id: AssistId, | ||
137 | /// Short description of the assist, as shown in the UI. | ||
138 | pub label: Label, | ||
139 | pub group: Option<GroupLabel>, | ||
140 | /// Target ranges are used to sort assists: the smaller the target range, | ||
141 | /// the more specific assist is, and so it should be sorted first. | ||
142 | pub target: TextRange, | ||
143 | /// Computing source change sometimes is much more costly then computing the | ||
144 | /// other fields. Additionally, the actual change is not required to show | ||
145 | /// the lightbulb UI, it only is needed when the user tries to apply an | ||
146 | /// assist. So, we compute it lazily: the API allow requesting assists with | ||
147 | /// or without source change. We could (and in fact, used to) distinguish | ||
148 | /// between resolved and unresolved assists at the type level, but this is | ||
149 | /// cumbersome, especially if you want to embed an assist into another data | ||
150 | /// structure, such as a diagnostic. | ||
151 | pub source_change: Option<SourceChange>, | ||
152 | } | ||
153 | |||
154 | impl Assist { | ||
155 | /// Return all the assists applicable at the given position. | ||
156 | pub fn get( | ||
157 | db: &RootDatabase, | ||
158 | config: &AssistConfig, | ||
159 | resolve: AssistResolveStrategy, | ||
160 | range: FileRange, | ||
161 | ) -> Vec<Assist> { | ||
162 | let sema = Semantics::new(db); | ||
163 | let ctx = AssistContext::new(sema, config, range); | ||
164 | let mut acc = Assists::new(&ctx, resolve); | ||
165 | handlers::all().iter().for_each(|handler| { | ||
166 | handler(&mut acc, &ctx); | ||
167 | }); | ||
168 | acc.finish() | ||
169 | } | ||
170 | } | 45 | } |
171 | 46 | ||
172 | mod handlers { | 47 | mod handlers { |
diff --git a/crates/ide_assists/src/tests.rs b/crates/ide_assists/src/tests.rs index bdf9cb71c..60cecd94c 100644 --- a/crates/ide_assists/src/tests.rs +++ b/crates/ide_assists/src/tests.rs | |||
@@ -16,8 +16,8 @@ use syntax::TextRange; | |||
16 | use test_utils::{assert_eq_text, extract_offset}; | 16 | use test_utils::{assert_eq_text, extract_offset}; |
17 | 17 | ||
18 | use crate::{ | 18 | use crate::{ |
19 | handlers::Handler, Assist, AssistConfig, AssistContext, AssistKind, AssistResolveStrategy, | 19 | assists, handlers::Handler, Assist, AssistConfig, AssistContext, AssistKind, |
20 | Assists, SingleResolve, | 20 | AssistResolveStrategy, Assists, SingleResolve, |
21 | }; | 21 | }; |
22 | 22 | ||
23 | pub(crate) const TEST_CONFIG: AssistConfig = AssistConfig { | 23 | pub(crate) const TEST_CONFIG: AssistConfig = AssistConfig { |
@@ -78,14 +78,14 @@ fn check_doc_test(assist_id: &str, before: &str, after: &str) { | |||
78 | let before = db.file_text(file_id).to_string(); | 78 | let before = db.file_text(file_id).to_string(); |
79 | let frange = FileRange { file_id, range: selection.into() }; | 79 | let frange = FileRange { file_id, range: selection.into() }; |
80 | 80 | ||
81 | let assist = Assist::get(&db, &TEST_CONFIG, AssistResolveStrategy::All, frange) | 81 | let assist = assists(&db, &TEST_CONFIG, AssistResolveStrategy::All, frange) |
82 | .into_iter() | 82 | .into_iter() |
83 | .find(|assist| assist.id.0 == assist_id) | 83 | .find(|assist| assist.id.0 == assist_id) |
84 | .unwrap_or_else(|| { | 84 | .unwrap_or_else(|| { |
85 | panic!( | 85 | panic!( |
86 | "\n\nAssist is not applicable: {}\nAvailable assists: {}", | 86 | "\n\nAssist is not applicable: {}\nAvailable assists: {}", |
87 | assist_id, | 87 | assist_id, |
88 | Assist::get(&db, &TEST_CONFIG, AssistResolveStrategy::None, frange) | 88 | assists(&db, &TEST_CONFIG, AssistResolveStrategy::None, frange) |
89 | .into_iter() | 89 | .into_iter() |
90 | .map(|assist| assist.id.0) | 90 | .map(|assist| assist.id.0) |
91 | .collect::<Vec<_>>() | 91 | .collect::<Vec<_>>() |
@@ -210,7 +210,7 @@ fn assist_order_field_struct() { | |||
210 | let (before_cursor_pos, before) = extract_offset(before); | 210 | let (before_cursor_pos, before) = extract_offset(before); |
211 | let (db, file_id) = with_single_file(&before); | 211 | let (db, file_id) = with_single_file(&before); |
212 | let frange = FileRange { file_id, range: TextRange::empty(before_cursor_pos) }; | 212 | let frange = FileRange { file_id, range: TextRange::empty(before_cursor_pos) }; |
213 | let assists = Assist::get(&db, &TEST_CONFIG, AssistResolveStrategy::None, frange); | 213 | let assists = assists(&db, &TEST_CONFIG, AssistResolveStrategy::None, frange); |
214 | let mut assists = assists.iter(); | 214 | let mut assists = assists.iter(); |
215 | 215 | ||
216 | assert_eq!(assists.next().expect("expected assist").label, "Change visibility to pub(crate)"); | 216 | assert_eq!(assists.next().expect("expected assist").label, "Change visibility to pub(crate)"); |
@@ -235,7 +235,7 @@ pub fn test_some_range(a: int) -> bool { | |||
235 | "#, | 235 | "#, |
236 | ); | 236 | ); |
237 | 237 | ||
238 | let assists = Assist::get(&db, &TEST_CONFIG, AssistResolveStrategy::None, frange); | 238 | let assists = assists(&db, &TEST_CONFIG, AssistResolveStrategy::None, frange); |
239 | let expected = labels(&assists); | 239 | let expected = labels(&assists); |
240 | 240 | ||
241 | expect![[r#" | 241 | expect![[r#" |
@@ -264,7 +264,7 @@ pub fn test_some_range(a: int) -> bool { | |||
264 | let mut cfg = TEST_CONFIG; | 264 | let mut cfg = TEST_CONFIG; |
265 | cfg.allowed = Some(vec![AssistKind::Refactor]); | 265 | cfg.allowed = Some(vec![AssistKind::Refactor]); |
266 | 266 | ||
267 | let assists = Assist::get(&db, &cfg, AssistResolveStrategy::None, frange); | 267 | let assists = assists(&db, &cfg, AssistResolveStrategy::None, frange); |
268 | let expected = labels(&assists); | 268 | let expected = labels(&assists); |
269 | 269 | ||
270 | expect![[r#" | 270 | expect![[r#" |
@@ -279,7 +279,7 @@ pub fn test_some_range(a: int) -> bool { | |||
279 | { | 279 | { |
280 | let mut cfg = TEST_CONFIG; | 280 | let mut cfg = TEST_CONFIG; |
281 | cfg.allowed = Some(vec![AssistKind::RefactorExtract]); | 281 | cfg.allowed = Some(vec![AssistKind::RefactorExtract]); |
282 | let assists = Assist::get(&db, &cfg, AssistResolveStrategy::None, frange); | 282 | let assists = assists(&db, &cfg, AssistResolveStrategy::None, frange); |
283 | let expected = labels(&assists); | 283 | let expected = labels(&assists); |
284 | 284 | ||
285 | expect![[r#" | 285 | expect![[r#" |
@@ -292,7 +292,7 @@ pub fn test_some_range(a: int) -> bool { | |||
292 | { | 292 | { |
293 | let mut cfg = TEST_CONFIG; | 293 | let mut cfg = TEST_CONFIG; |
294 | cfg.allowed = Some(vec![AssistKind::QuickFix]); | 294 | cfg.allowed = Some(vec![AssistKind::QuickFix]); |
295 | let assists = Assist::get(&db, &cfg, AssistResolveStrategy::None, frange); | 295 | let assists = assists(&db, &cfg, AssistResolveStrategy::None, frange); |
296 | let expected = labels(&assists); | 296 | let expected = labels(&assists); |
297 | 297 | ||
298 | expect![[r#""#]].assert_eq(&expected); | 298 | expect![[r#""#]].assert_eq(&expected); |
@@ -317,7 +317,7 @@ pub fn test_some_range(a: int) -> bool { | |||
317 | cfg.allowed = Some(vec![AssistKind::RefactorExtract]); | 317 | cfg.allowed = Some(vec![AssistKind::RefactorExtract]); |
318 | 318 | ||
319 | { | 319 | { |
320 | let assists = Assist::get(&db, &cfg, AssistResolveStrategy::None, frange); | 320 | let assists = assists(&db, &cfg, AssistResolveStrategy::None, frange); |
321 | assert_eq!(2, assists.len()); | 321 | assert_eq!(2, assists.len()); |
322 | let mut assists = assists.into_iter(); | 322 | let mut assists = assists.into_iter(); |
323 | 323 | ||
@@ -353,7 +353,7 @@ pub fn test_some_range(a: int) -> bool { | |||
353 | } | 353 | } |
354 | 354 | ||
355 | { | 355 | { |
356 | let assists = Assist::get( | 356 | let assists = assists( |
357 | &db, | 357 | &db, |
358 | &cfg, | 358 | &cfg, |
359 | AssistResolveStrategy::Single(SingleResolve { | 359 | AssistResolveStrategy::Single(SingleResolve { |
@@ -397,7 +397,7 @@ pub fn test_some_range(a: int) -> bool { | |||
397 | } | 397 | } |
398 | 398 | ||
399 | { | 399 | { |
400 | let assists = Assist::get( | 400 | let assists = assists( |
401 | &db, | 401 | &db, |
402 | &cfg, | 402 | &cfg, |
403 | AssistResolveStrategy::Single(SingleResolve { | 403 | AssistResolveStrategy::Single(SingleResolve { |
@@ -462,7 +462,7 @@ pub fn test_some_range(a: int) -> bool { | |||
462 | } | 462 | } |
463 | 463 | ||
464 | { | 464 | { |
465 | let assists = Assist::get(&db, &cfg, AssistResolveStrategy::All, frange); | 465 | let assists = assists(&db, &cfg, AssistResolveStrategy::All, frange); |
466 | assert_eq!(2, assists.len()); | 466 | assert_eq!(2, assists.len()); |
467 | let mut assists = assists.into_iter(); | 467 | let mut assists = assists.into_iter(); |
468 | 468 | ||