aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/assists/src/assist_context.rs51
-rw-r--r--crates/assists/src/lib.rs41
-rw-r--r--crates/assists/src/tests.rs48
-rw-r--r--crates/ide/src/lib.rs22
-rw-r--r--crates/rust-analyzer/src/handlers.rs8
-rw-r--r--crates/rust-analyzer/src/to_proto.rs19
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
20use crate::{ 20use 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> {
105pub(crate) struct Assists { 105pub(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
112impl Assists { 112impl 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
79pub 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
84impl Assist { 87impl 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};
82pub use assists::{Assist, AssistConfig, AssistId, AssistKind, ResolvedAssist}; 82pub use assists::{Assist, AssistConfig, AssistId, AssistKind};
83pub use completion::{ 83pub 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(&params.id, ':').unwrap(); 1018 let (id, index) = split_once(&params.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};
14use itertools::Itertools; 14use 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
797pub(crate) fn resolved_code_action( 798pub(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 };