diff options
-rw-r--r-- | crates/assists/src/assist_context.rs | 51 | ||||
-rw-r--r-- | crates/assists/src/lib.rs | 41 | ||||
-rw-r--r-- | crates/assists/src/tests.rs | 48 | ||||
-rw-r--r-- | crates/ide/src/lib.rs | 22 | ||||
-rw-r--r-- | crates/rust-analyzer/src/handlers.rs | 8 | ||||
-rw-r--r-- | crates/rust-analyzer/src/to_proto.rs | 19 |
6 files changed, 69 insertions, 120 deletions
diff --git a/crates/assists/src/assist_context.rs b/crates/assists/src/assist_context.rs index 80cf9aba1..4f59d39a9 100644 --- a/crates/assists/src/assist_context.rs +++ b/crates/assists/src/assist_context.rs | |||
@@ -19,7 +19,7 @@ use text_edit::{TextEdit, TextEditBuilder}; | |||
19 | 19 | ||
20 | use crate::{ | 20 | use crate::{ |
21 | assist_config::{AssistConfig, SnippetCap}, | 21 | assist_config::{AssistConfig, SnippetCap}, |
22 | Assist, AssistId, AssistKind, GroupLabel, ResolvedAssist, | 22 | Assist, AssistId, AssistKind, GroupLabel, |
23 | }; | 23 | }; |
24 | 24 | ||
25 | /// `AssistContext` allows to apply an assist or check if it could be applied. | 25 | /// `AssistContext` allows to apply an assist or check if it could be applied. |
@@ -105,46 +105,23 @@ impl<'a> AssistContext<'a> { | |||
105 | pub(crate) struct Assists { | 105 | pub(crate) struct Assists { |
106 | resolve: bool, | 106 | resolve: bool, |
107 | file: FileId, | 107 | file: FileId, |
108 | buf: Vec<(Assist, Option<SourceChange>)>, | 108 | buf: Vec<Assist>, |
109 | allowed: Option<Vec<AssistKind>>, | 109 | allowed: Option<Vec<AssistKind>>, |
110 | } | 110 | } |
111 | 111 | ||
112 | impl Assists { | 112 | impl Assists { |
113 | pub(crate) fn new_resolved(ctx: &AssistContext) -> Assists { | 113 | pub(crate) fn new(ctx: &AssistContext, resolve: bool) -> Assists { |
114 | Assists { | 114 | Assists { |
115 | resolve: true, | 115 | resolve, |
116 | file: ctx.frange.file_id, | 116 | file: ctx.frange.file_id, |
117 | buf: Vec::new(), | 117 | buf: Vec::new(), |
118 | allowed: ctx.config.allowed.clone(), | 118 | allowed: ctx.config.allowed.clone(), |
119 | } | 119 | } |
120 | } | 120 | } |
121 | 121 | ||
122 | pub(crate) fn new_unresolved(ctx: &AssistContext) -> Assists { | 122 | pub(crate) fn finish(mut self) -> Vec<Assist> { |
123 | Assists { | 123 | self.buf.sort_by_key(|assist| assist.target.len()); |
124 | resolve: false, | 124 | self.buf |
125 | file: ctx.frange.file_id, | ||
126 | buf: Vec::new(), | ||
127 | allowed: ctx.config.allowed.clone(), | ||
128 | } | ||
129 | } | ||
130 | |||
131 | pub(crate) fn finish_unresolved(self) -> Vec<Assist> { | ||
132 | assert!(!self.resolve); | ||
133 | self.finish() | ||
134 | .into_iter() | ||
135 | .map(|(label, edit)| { | ||
136 | assert!(edit.is_none()); | ||
137 | label | ||
138 | }) | ||
139 | .collect() | ||
140 | } | ||
141 | |||
142 | pub(crate) fn finish_resolved(self) -> Vec<ResolvedAssist> { | ||
143 | assert!(self.resolve); | ||
144 | self.finish() | ||
145 | .into_iter() | ||
146 | .map(|(label, edit)| ResolvedAssist { assist: label, source_change: edit.unwrap() }) | ||
147 | .collect() | ||
148 | } | 125 | } |
149 | 126 | ||
150 | pub(crate) fn add( | 127 | pub(crate) fn add( |
@@ -158,7 +135,7 @@ impl Assists { | |||
158 | return None; | 135 | return None; |
159 | } | 136 | } |
160 | let label = Label::new(label.into()); | 137 | let label = Label::new(label.into()); |
161 | let assist = Assist { id, label, group: None, target }; | 138 | let assist = Assist { id, label, group: None, target, source_change: None }; |
162 | self.add_impl(assist, f) | 139 | self.add_impl(assist, f) |
163 | } | 140 | } |
164 | 141 | ||
@@ -174,11 +151,11 @@ impl Assists { | |||
174 | return None; | 151 | return None; |
175 | } | 152 | } |
176 | let label = Label::new(label.into()); | 153 | let label = Label::new(label.into()); |
177 | let assist = Assist { id, label, group: Some(group.clone()), target }; | 154 | let assist = Assist { id, label, group: Some(group.clone()), target, source_change: None }; |
178 | self.add_impl(assist, f) | 155 | self.add_impl(assist, f) |
179 | } | 156 | } |
180 | 157 | ||
181 | fn add_impl(&mut self, assist: Assist, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> { | 158 | fn add_impl(&mut self, mut assist: Assist, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> { |
182 | let source_change = if self.resolve { | 159 | let source_change = if self.resolve { |
183 | let mut builder = AssistBuilder::new(self.file); | 160 | let mut builder = AssistBuilder::new(self.file); |
184 | f(&mut builder); | 161 | f(&mut builder); |
@@ -186,16 +163,12 @@ impl Assists { | |||
186 | } else { | 163 | } else { |
187 | None | 164 | None |
188 | }; | 165 | }; |
166 | assist.source_change = source_change.clone(); | ||
189 | 167 | ||
190 | self.buf.push((assist, source_change)); | 168 | self.buf.push(assist); |
191 | Some(()) | 169 | Some(()) |
192 | } | 170 | } |
193 | 171 | ||
194 | fn finish(mut self) -> Vec<(Assist, Option<SourceChange>)> { | ||
195 | self.buf.sort_by_key(|(label, _edit)| label.target.len()); | ||
196 | self.buf | ||
197 | } | ||
198 | |||
199 | fn is_allowed(&self, id: &AssistId) -> bool { | 172 | fn is_allowed(&self, id: &AssistId) -> bool { |
200 | match &self.allowed { | 173 | match &self.allowed { |
201 | Some(allowed) => allowed.iter().any(|kind| kind.contains(id.1)), | 174 | Some(allowed) => allowed.iter().any(|kind| kind.contains(id.1)), |
diff --git a/crates/assists/src/lib.rs b/crates/assists/src/lib.rs index 6b89b2d04..fdec886e9 100644 --- a/crates/assists/src/lib.rs +++ b/crates/assists/src/lib.rs | |||
@@ -73,45 +73,32 @@ pub struct Assist { | |||
73 | /// Target ranges are used to sort assists: the smaller the target range, | 73 | /// Target ranges are used to sort assists: the smaller the target range, |
74 | /// the more specific assist is, and so it should be sorted first. | 74 | /// the more specific assist is, and so it should be sorted first. |
75 | pub target: TextRange, | 75 | pub target: TextRange, |
76 | } | 76 | /// Computing source change sometimes is much more costly then computing the |
77 | 77 | /// other fields. Additionally, the actual change is not required to show | |
78 | #[derive(Debug, Clone)] | 78 | /// the lightbulb UI, it only is needed when the user tries to apply an |
79 | pub struct ResolvedAssist { | 79 | /// assist. So, we compute it lazily: the API allow requesting assists with |
80 | pub assist: Assist, | 80 | /// or without source change. We could (and in fact, used to) distinguish |
81 | pub source_change: SourceChange, | 81 | /// between resolved and unresolved assists at the type level, but this is |
82 | /// cumbersome, especially if you want to embed an assist into another data | ||
83 | /// structure, such as a diagnostic. | ||
84 | pub source_change: Option<SourceChange>, | ||
82 | } | 85 | } |
83 | 86 | ||
84 | impl Assist { | 87 | impl Assist { |
85 | /// Return all the assists applicable at the given position. | 88 | /// Return all the assists applicable at the given position. |
86 | /// | 89 | pub fn get( |
87 | /// Assists are returned in the "unresolved" state, that is only labels are | ||
88 | /// returned, without actual edits. | ||
89 | pub fn unresolved(db: &RootDatabase, config: &AssistConfig, range: FileRange) -> Vec<Assist> { | ||
90 | let sema = Semantics::new(db); | ||
91 | let ctx = AssistContext::new(sema, config, range); | ||
92 | let mut acc = Assists::new_unresolved(&ctx); | ||
93 | handlers::all().iter().for_each(|handler| { | ||
94 | handler(&mut acc, &ctx); | ||
95 | }); | ||
96 | acc.finish_unresolved() | ||
97 | } | ||
98 | |||
99 | /// Return all the assists applicable at the given position. | ||
100 | /// | ||
101 | /// Assists are returned in the "resolved" state, that is with edit fully | ||
102 | /// computed. | ||
103 | pub fn resolved( | ||
104 | db: &RootDatabase, | 90 | db: &RootDatabase, |
105 | config: &AssistConfig, | 91 | config: &AssistConfig, |
92 | resolve: bool, | ||
106 | range: FileRange, | 93 | range: FileRange, |
107 | ) -> Vec<ResolvedAssist> { | 94 | ) -> Vec<Assist> { |
108 | let sema = Semantics::new(db); | 95 | let sema = Semantics::new(db); |
109 | let ctx = AssistContext::new(sema, config, range); | 96 | let ctx = AssistContext::new(sema, config, range); |
110 | let mut acc = Assists::new_resolved(&ctx); | 97 | let mut acc = Assists::new(&ctx, resolve); |
111 | handlers::all().iter().for_each(|handler| { | 98 | handlers::all().iter().for_each(|handler| { |
112 | handler(&mut acc, &ctx); | 99 | handler(&mut acc, &ctx); |
113 | }); | 100 | }); |
114 | acc.finish_resolved() | 101 | acc.finish() |
115 | } | 102 | } |
116 | } | 103 | } |
117 | 104 | ||
diff --git a/crates/assists/src/tests.rs b/crates/assists/src/tests.rs index b41f4874a..21e448fb8 100644 --- a/crates/assists/src/tests.rs +++ b/crates/assists/src/tests.rs | |||
@@ -48,24 +48,25 @@ fn check_doc_test(assist_id: &str, before: &str, after: &str) { | |||
48 | let before = db.file_text(file_id).to_string(); | 48 | let before = db.file_text(file_id).to_string(); |
49 | let frange = FileRange { file_id, range: selection.into() }; | 49 | let frange = FileRange { file_id, range: selection.into() }; |
50 | 50 | ||
51 | let assist = Assist::resolved(&db, &AssistConfig::default(), frange) | 51 | let assist = Assist::get(&db, &AssistConfig::default(), true, frange) |
52 | .into_iter() | 52 | .into_iter() |
53 | .find(|assist| assist.assist.id.0 == assist_id) | 53 | .find(|assist| assist.id.0 == assist_id) |
54 | .unwrap_or_else(|| { | 54 | .unwrap_or_else(|| { |
55 | panic!( | 55 | panic!( |
56 | "\n\nAssist is not applicable: {}\nAvailable assists: {}", | 56 | "\n\nAssist is not applicable: {}\nAvailable assists: {}", |
57 | assist_id, | 57 | assist_id, |
58 | Assist::resolved(&db, &AssistConfig::default(), frange) | 58 | Assist::get(&db, &AssistConfig::default(), false, frange) |
59 | .into_iter() | 59 | .into_iter() |
60 | .map(|assist| assist.assist.id.0) | 60 | .map(|assist| assist.id.0) |
61 | .collect::<Vec<_>>() | 61 | .collect::<Vec<_>>() |
62 | .join(", ") | 62 | .join(", ") |
63 | ) | 63 | ) |
64 | }); | 64 | }); |
65 | 65 | ||
66 | let actual = { | 66 | let actual = { |
67 | let source_change = assist.source_change.unwrap(); | ||
67 | let mut actual = before; | 68 | let mut actual = before; |
68 | for source_file_edit in assist.source_change.source_file_edits { | 69 | for source_file_edit in source_change.source_file_edits { |
69 | if source_file_edit.file_id == file_id { | 70 | if source_file_edit.file_id == file_id { |
70 | source_file_edit.edit.apply(&mut actual) | 71 | source_file_edit.edit.apply(&mut actual) |
71 | } | 72 | } |
@@ -90,18 +91,18 @@ fn check(handler: Handler, before: &str, expected: ExpectedResult, assist_label: | |||
90 | let sema = Semantics::new(&db); | 91 | let sema = Semantics::new(&db); |
91 | let config = AssistConfig::default(); | 92 | let config = AssistConfig::default(); |
92 | let ctx = AssistContext::new(sema, &config, frange); | 93 | let ctx = AssistContext::new(sema, &config, frange); |
93 | let mut acc = Assists::new_resolved(&ctx); | 94 | let mut acc = Assists::new(&ctx, true); |
94 | handler(&mut acc, &ctx); | 95 | handler(&mut acc, &ctx); |
95 | let mut res = acc.finish_resolved(); | 96 | let mut res = acc.finish(); |
96 | 97 | ||
97 | let assist = match assist_label { | 98 | let assist = match assist_label { |
98 | Some(label) => res.into_iter().find(|resolved| resolved.assist.label == label), | 99 | Some(label) => res.into_iter().find(|resolved| resolved.label == label), |
99 | None => res.pop(), | 100 | None => res.pop(), |
100 | }; | 101 | }; |
101 | 102 | ||
102 | match (assist, expected) { | 103 | match (assist, expected) { |
103 | (Some(assist), ExpectedResult::After(after)) => { | 104 | (Some(assist), ExpectedResult::After(after)) => { |
104 | let mut source_change = assist.source_change; | 105 | let mut source_change = assist.source_change.unwrap(); |
105 | assert!(!source_change.source_file_edits.is_empty()); | 106 | assert!(!source_change.source_file_edits.is_empty()); |
106 | let skip_header = source_change.source_file_edits.len() == 1 | 107 | let skip_header = source_change.source_file_edits.len() == 1 |
107 | && source_change.file_system_edits.len() == 0; | 108 | && source_change.file_system_edits.len() == 0; |
@@ -138,7 +139,7 @@ fn check(handler: Handler, before: &str, expected: ExpectedResult, assist_label: | |||
138 | assert_eq_text!(after, &buf); | 139 | assert_eq_text!(after, &buf); |
139 | } | 140 | } |
140 | (Some(assist), ExpectedResult::Target(target)) => { | 141 | (Some(assist), ExpectedResult::Target(target)) => { |
141 | let range = assist.assist.target; | 142 | let range = assist.target; |
142 | assert_eq_text!(&text_without_caret[range], target); | 143 | assert_eq_text!(&text_without_caret[range], target); |
143 | } | 144 | } |
144 | (Some(_), ExpectedResult::NotApplicable) => panic!("assist should not be applicable!"), | 145 | (Some(_), ExpectedResult::NotApplicable) => panic!("assist should not be applicable!"), |
@@ -155,14 +156,11 @@ fn assist_order_field_struct() { | |||
155 | let (before_cursor_pos, before) = extract_offset(before); | 156 | let (before_cursor_pos, before) = extract_offset(before); |
156 | let (db, file_id) = with_single_file(&before); | 157 | let (db, file_id) = with_single_file(&before); |
157 | let frange = FileRange { file_id, range: TextRange::empty(before_cursor_pos) }; | 158 | let frange = FileRange { file_id, range: TextRange::empty(before_cursor_pos) }; |
158 | let assists = Assist::resolved(&db, &AssistConfig::default(), frange); | 159 | let assists = Assist::get(&db, &AssistConfig::default(), false, frange); |
159 | let mut assists = assists.iter(); | 160 | let mut assists = assists.iter(); |
160 | 161 | ||
161 | assert_eq!( | 162 | assert_eq!(assists.next().expect("expected assist").label, "Change visibility to pub(crate)"); |
162 | assists.next().expect("expected assist").assist.label, | 163 | assert_eq!(assists.next().expect("expected assist").label, "Add `#[derive]`"); |
163 | "Change visibility to pub(crate)" | ||
164 | ); | ||
165 | assert_eq!(assists.next().expect("expected assist").assist.label, "Add `#[derive]`"); | ||
166 | } | 164 | } |
167 | 165 | ||
168 | #[test] | 166 | #[test] |
@@ -178,11 +176,11 @@ fn assist_order_if_expr() { | |||
178 | let (range, before) = extract_range(before); | 176 | let (range, before) = extract_range(before); |
179 | let (db, file_id) = with_single_file(&before); | 177 | let (db, file_id) = with_single_file(&before); |
180 | let frange = FileRange { file_id, range }; | 178 | let frange = FileRange { file_id, range }; |
181 | let assists = Assist::resolved(&db, &AssistConfig::default(), frange); | 179 | let assists = Assist::get(&db, &AssistConfig::default(), false, frange); |
182 | let mut assists = assists.iter(); | 180 | let mut assists = assists.iter(); |
183 | 181 | ||
184 | assert_eq!(assists.next().expect("expected assist").assist.label, "Extract into variable"); | 182 | assert_eq!(assists.next().expect("expected assist").label, "Extract into variable"); |
185 | assert_eq!(assists.next().expect("expected assist").assist.label, "Replace with match"); | 183 | assert_eq!(assists.next().expect("expected assist").label, "Replace with match"); |
186 | } | 184 | } |
187 | 185 | ||
188 | #[test] | 186 | #[test] |
@@ -203,27 +201,27 @@ fn assist_filter_works() { | |||
203 | let mut cfg = AssistConfig::default(); | 201 | let mut cfg = AssistConfig::default(); |
204 | cfg.allowed = Some(vec![AssistKind::Refactor]); | 202 | cfg.allowed = Some(vec![AssistKind::Refactor]); |
205 | 203 | ||
206 | let assists = Assist::resolved(&db, &cfg, frange); | 204 | let assists = Assist::get(&db, &cfg, false, frange); |
207 | let mut assists = assists.iter(); | 205 | let mut assists = assists.iter(); |
208 | 206 | ||
209 | assert_eq!(assists.next().expect("expected assist").assist.label, "Extract into variable"); | 207 | assert_eq!(assists.next().expect("expected assist").label, "Extract into variable"); |
210 | assert_eq!(assists.next().expect("expected assist").assist.label, "Replace with match"); | 208 | assert_eq!(assists.next().expect("expected assist").label, "Replace with match"); |
211 | } | 209 | } |
212 | 210 | ||
213 | { | 211 | { |
214 | let mut cfg = AssistConfig::default(); | 212 | let mut cfg = AssistConfig::default(); |
215 | cfg.allowed = Some(vec![AssistKind::RefactorExtract]); | 213 | cfg.allowed = Some(vec![AssistKind::RefactorExtract]); |
216 | let assists = Assist::resolved(&db, &cfg, frange); | 214 | let assists = Assist::get(&db, &cfg, false, frange); |
217 | assert_eq!(assists.len(), 1); | 215 | assert_eq!(assists.len(), 1); |
218 | 216 | ||
219 | let mut assists = assists.iter(); | 217 | let mut assists = assists.iter(); |
220 | assert_eq!(assists.next().expect("expected assist").assist.label, "Extract into variable"); | 218 | assert_eq!(assists.next().expect("expected assist").label, "Extract into variable"); |
221 | } | 219 | } |
222 | 220 | ||
223 | { | 221 | { |
224 | let mut cfg = AssistConfig::default(); | 222 | let mut cfg = AssistConfig::default(); |
225 | cfg.allowed = Some(vec![AssistKind::QuickFix]); | 223 | cfg.allowed = Some(vec![AssistKind::QuickFix]); |
226 | let assists = Assist::resolved(&db, &cfg, frange); | 224 | let assists = Assist::get(&db, &cfg, false, frange); |
227 | assert!(assists.is_empty(), "All asserts but quickfixes should be filtered out"); | 225 | assert!(assists.is_empty(), "All asserts but quickfixes should be filtered out"); |
228 | } | 226 | } |
229 | } | 227 | } |
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index a75cc85b6..41eb139d1 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs | |||
@@ -79,7 +79,7 @@ pub use crate::{ | |||
79 | HighlightedRange, | 79 | HighlightedRange, |
80 | }, | 80 | }, |
81 | }; | 81 | }; |
82 | pub use assists::{Assist, AssistConfig, AssistId, AssistKind, ResolvedAssist}; | 82 | pub use assists::{Assist, AssistConfig, AssistId, AssistKind}; |
83 | pub use completion::{ | 83 | pub use completion::{ |
84 | CompletionConfig, CompletionItem, CompletionItemKind, CompletionResolveCapability, | 84 | CompletionConfig, CompletionItem, CompletionItemKind, CompletionResolveCapability, |
85 | CompletionScore, ImportEdit, InsertTextFormat, | 85 | CompletionScore, ImportEdit, InsertTextFormat, |
@@ -491,22 +491,16 @@ impl Analysis { | |||
491 | } | 491 | } |
492 | 492 | ||
493 | /// Computes assists (aka code actions aka intentions) for the given | 493 | /// Computes assists (aka code actions aka intentions) for the given |
494 | /// position. Computes enough info to show the lightbulb list in the editor, | 494 | /// position. If `resolve == false`, computes enough info to show the |
495 | /// but doesn't compute actual edits, to improve performance. | 495 | /// lightbulb list in the editor, but doesn't compute actual edits, to |
496 | /// | 496 | /// improve performance. |
497 | /// When the user clicks on the assist, call `resolve_assists` to get the | 497 | pub fn assists( |
498 | /// edit. | ||
499 | pub fn assists(&self, config: &AssistConfig, frange: FileRange) -> Cancelable<Vec<Assist>> { | ||
500 | self.with_db(|db| Assist::unresolved(db, config, frange)) | ||
501 | } | ||
502 | |||
503 | /// Computes resolved assists with source changes for the given position. | ||
504 | pub fn resolve_assists( | ||
505 | &self, | 498 | &self, |
506 | config: &AssistConfig, | 499 | config: &AssistConfig, |
500 | resolve: bool, | ||
507 | frange: FileRange, | 501 | frange: FileRange, |
508 | ) -> Cancelable<Vec<ResolvedAssist>> { | 502 | ) -> Cancelable<Vec<Assist>> { |
509 | self.with_db(|db| assists::Assist::resolved(db, config, frange)) | 503 | self.with_db(|db| Assist::get(db, config, resolve, frange)) |
510 | } | 504 | } |
511 | 505 | ||
512 | /// Computes the set of diagnostics for the given file. | 506 | /// Computes the set of diagnostics for the given file. |
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index 1207b31c4..374fb5302 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs | |||
@@ -946,12 +946,12 @@ pub(crate) fn handle_code_action( | |||
946 | 946 | ||
947 | if snap.config.client_caps.code_action_resolve { | 947 | if snap.config.client_caps.code_action_resolve { |
948 | for (index, assist) in | 948 | for (index, assist) in |
949 | snap.analysis.assists(&assists_config, frange)?.into_iter().enumerate() | 949 | snap.analysis.assists(&assists_config, false, frange)?.into_iter().enumerate() |
950 | { | 950 | { |
951 | res.push(to_proto::unresolved_code_action(&snap, params.clone(), assist, index)?); | 951 | res.push(to_proto::unresolved_code_action(&snap, params.clone(), assist, index)?); |
952 | } | 952 | } |
953 | } else { | 953 | } else { |
954 | for assist in snap.analysis.resolve_assists(&assists_config, frange)?.into_iter() { | 954 | for assist in snap.analysis.assists(&assists_config, true, frange)?.into_iter() { |
955 | res.push(to_proto::resolved_code_action(&snap, assist)?); | 955 | res.push(to_proto::resolved_code_action(&snap, assist)?); |
956 | } | 956 | } |
957 | } | 957 | } |
@@ -1014,11 +1014,11 @@ pub(crate) fn handle_code_action_resolve( | |||
1014 | .only | 1014 | .only |
1015 | .map(|it| it.into_iter().filter_map(from_proto::assist_kind).collect()); | 1015 | .map(|it| it.into_iter().filter_map(from_proto::assist_kind).collect()); |
1016 | 1016 | ||
1017 | let assists = snap.analysis.resolve_assists(&snap.config.assist, frange)?; | 1017 | let assists = snap.analysis.assists(&snap.config.assist, true, frange)?; |
1018 | let (id, index) = split_once(¶ms.id, ':').unwrap(); | 1018 | let (id, index) = split_once(¶ms.id, ':').unwrap(); |
1019 | let index = index.parse::<usize>().unwrap(); | 1019 | let index = index.parse::<usize>().unwrap(); |
1020 | let assist = &assists[index]; | 1020 | let assist = &assists[index]; |
1021 | assert!(assist.assist.id.0 == id); | 1021 | assert!(assist.id.0 == id); |
1022 | let edit = to_proto::resolved_code_action(&snap, assist.clone())?.edit; | 1022 | let edit = to_proto::resolved_code_action(&snap, assist.clone())?.edit; |
1023 | code_action.edit = edit; | 1023 | code_action.edit = edit; |
1024 | Ok(code_action) | 1024 | Ok(code_action) |
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 753aad628..1a38e79f0 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs | |||
@@ -8,8 +8,8 @@ use ide::{ | |||
8 | Assist, AssistKind, CallInfo, CompletionItem, CompletionItemKind, Documentation, FileId, | 8 | Assist, AssistKind, CallInfo, CompletionItem, CompletionItemKind, Documentation, FileId, |
9 | FileRange, FileSystemEdit, Fold, FoldKind, Highlight, HighlightModifier, HighlightTag, | 9 | FileRange, FileSystemEdit, Fold, FoldKind, Highlight, HighlightModifier, HighlightTag, |
10 | HighlightedRange, Indel, InlayHint, InlayKind, InsertTextFormat, LineIndex, Markup, | 10 | HighlightedRange, Indel, InlayHint, InlayKind, InsertTextFormat, LineIndex, Markup, |
11 | NavigationTarget, ReferenceAccess, ResolvedAssist, Runnable, Severity, SourceChange, | 11 | NavigationTarget, ReferenceAccess, Runnable, Severity, SourceChange, SourceFileEdit, |
12 | SourceFileEdit, SymbolKind, TextEdit, TextRange, TextSize, | 12 | SymbolKind, TextEdit, TextRange, TextSize, |
13 | }; | 13 | }; |
14 | use itertools::Itertools; | 14 | use itertools::Itertools; |
15 | 15 | ||
@@ -780,6 +780,7 @@ pub(crate) fn unresolved_code_action( | |||
780 | assist: Assist, | 780 | assist: Assist, |
781 | index: usize, | 781 | index: usize, |
782 | ) -> Result<lsp_ext::CodeAction> { | 782 | ) -> Result<lsp_ext::CodeAction> { |
783 | assert!(assist.source_change.is_none()); | ||
783 | let res = lsp_ext::CodeAction { | 784 | let res = lsp_ext::CodeAction { |
784 | title: assist.label.to_string(), | 785 | title: assist.label.to_string(), |
785 | group: assist.group.filter(|_| snap.config.client_caps.code_action_group).map(|gr| gr.0), | 786 | group: assist.group.filter(|_| snap.config.client_caps.code_action_group).map(|gr| gr.0), |
@@ -796,18 +797,14 @@ pub(crate) fn unresolved_code_action( | |||
796 | 797 | ||
797 | pub(crate) fn resolved_code_action( | 798 | pub(crate) fn resolved_code_action( |
798 | snap: &GlobalStateSnapshot, | 799 | snap: &GlobalStateSnapshot, |
799 | assist: ResolvedAssist, | 800 | assist: Assist, |
800 | ) -> Result<lsp_ext::CodeAction> { | 801 | ) -> Result<lsp_ext::CodeAction> { |
801 | let change = assist.source_change; | 802 | let change = assist.source_change.unwrap(); |
802 | let res = lsp_ext::CodeAction { | 803 | let res = lsp_ext::CodeAction { |
803 | edit: Some(snippet_workspace_edit(snap, change)?), | 804 | edit: Some(snippet_workspace_edit(snap, change)?), |
804 | title: assist.assist.label.to_string(), | 805 | title: assist.label.to_string(), |
805 | group: assist | 806 | group: assist.group.filter(|_| snap.config.client_caps.code_action_group).map(|gr| gr.0), |
806 | .assist | 807 | kind: Some(code_action_kind(assist.id.1)), |
807 | .group | ||
808 | .filter(|_| snap.config.client_caps.code_action_group) | ||
809 | .map(|gr| gr.0), | ||
810 | kind: Some(code_action_kind(assist.assist.id.1)), | ||
811 | is_preferred: None, | 808 | is_preferred: None, |
812 | data: None, | 809 | data: None, |
813 | }; | 810 | }; |