diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_assists/src/assist_context.rs | 10 | ||||
-rw-r--r-- | crates/ra_ide/src/diagnostics.rs | 101 | ||||
-rw-r--r-- | crates/ra_ide/src/lib.rs | 18 | ||||
-rw-r--r-- | crates/ra_ide/src/references/rename.rs | 11 | ||||
-rw-r--r-- | crates/ra_ide/src/typing.rs | 35 | ||||
-rw-r--r-- | crates/ra_ide/src/typing/on_enter.rs | 5 | ||||
-rw-r--r-- | crates/ra_ide_db/src/source_change.rs | 79 | ||||
-rw-r--r-- | crates/rust-analyzer/src/config.rs | 9 | ||||
-rw-r--r-- | crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap | 1 | ||||
-rw-r--r-- | crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap | 1 | ||||
-rw-r--r-- | crates/rust-analyzer/src/diagnostics/to_proto.rs | 1 | ||||
-rw-r--r-- | crates/rust-analyzer/src/lsp_ext.rs | 10 | ||||
-rw-r--r-- | crates/rust-analyzer/src/main_loop/handlers.rs | 60 | ||||
-rw-r--r-- | crates/rust-analyzer/src/to_proto.rs | 10 |
14 files changed, 131 insertions, 220 deletions
diff --git a/crates/ra_assists/src/assist_context.rs b/crates/ra_assists/src/assist_context.rs index f3af70a3e..5b1a4680b 100644 --- a/crates/ra_assists/src/assist_context.rs +++ b/crates/ra_assists/src/assist_context.rs | |||
@@ -5,7 +5,7 @@ use hir::Semantics; | |||
5 | use ra_db::{FileId, FileRange}; | 5 | use ra_db::{FileId, FileRange}; |
6 | use ra_fmt::{leading_indent, reindent}; | 6 | use ra_fmt::{leading_indent, reindent}; |
7 | use ra_ide_db::{ | 7 | use ra_ide_db::{ |
8 | source_change::{SingleFileChange, SourceChange}, | 8 | source_change::{SourceChange, SourceFileEdit}, |
9 | RootDatabase, | 9 | RootDatabase, |
10 | }; | 10 | }; |
11 | use ra_syntax::{ | 11 | use ra_syntax::{ |
@@ -150,11 +150,10 @@ impl Assists { | |||
150 | self.add_impl(label, f) | 150 | self.add_impl(label, f) |
151 | } | 151 | } |
152 | fn add_impl(&mut self, label: Assist, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> { | 152 | fn add_impl(&mut self, label: Assist, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> { |
153 | let change_label = label.label.clone(); | ||
154 | let source_change = if self.resolve { | 153 | let source_change = if self.resolve { |
155 | let mut builder = AssistBuilder::new(self.file); | 154 | let mut builder = AssistBuilder::new(self.file); |
156 | f(&mut builder); | 155 | f(&mut builder); |
157 | Some(builder.finish(change_label)) | 156 | Some(builder.finish()) |
158 | } else { | 157 | } else { |
159 | None | 158 | None |
160 | }; | 159 | }; |
@@ -246,9 +245,10 @@ impl AssistBuilder { | |||
246 | &mut self.edit | 245 | &mut self.edit |
247 | } | 246 | } |
248 | 247 | ||
249 | fn finish(self, change_label: String) -> SourceChange { | 248 | fn finish(self) -> SourceChange { |
250 | let edit = self.edit.finish(); | 249 | let edit = self.edit.finish(); |
251 | let mut res = SingleFileChange { label: change_label, edit }.into_source_change(self.file); | 250 | let source_file_edit = SourceFileEdit { file_id: self.file, edit }; |
251 | let mut res: SourceChange = source_file_edit.into(); | ||
252 | if self.is_snippet { | 252 | if self.is_snippet { |
253 | res.is_snippet = true; | 253 | res.is_snippet = true; |
254 | } | 254 | } |
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs index c2819bbf7..3d83c0f71 100644 --- a/crates/ra_ide/src/diagnostics.rs +++ b/crates/ra_ide/src/diagnostics.rs | |||
@@ -21,7 +21,7 @@ use ra_syntax::{ | |||
21 | }; | 21 | }; |
22 | use ra_text_edit::{TextEdit, TextEditBuilder}; | 22 | use ra_text_edit::{TextEdit, TextEditBuilder}; |
23 | 23 | ||
24 | use crate::{Diagnostic, FileId, FileSystemEdit, SourceChange, SourceFileEdit}; | 24 | use crate::{Diagnostic, FileId, FileSystemEdit, Fix, SourceChange, SourceFileEdit}; |
25 | 25 | ||
26 | #[derive(Debug, Copy, Clone)] | 26 | #[derive(Debug, Copy, Clone)] |
27 | pub enum Severity { | 27 | pub enum Severity { |
@@ -63,8 +63,8 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic> | |||
63 | .parent() | 63 | .parent() |
64 | .unwrap_or_else(|| RelativePath::new("")) | 64 | .unwrap_or_else(|| RelativePath::new("")) |
65 | .join(&d.candidate); | 65 | .join(&d.candidate); |
66 | let create_file = FileSystemEdit::CreateFile { source_root, path }; | 66 | let fix = |
67 | let fix = SourceChange::file_system_edit("Create module", create_file); | 67 | Fix::new("Create module", FileSystemEdit::CreateFile { source_root, path }.into()); |
68 | res.borrow_mut().push(Diagnostic { | 68 | res.borrow_mut().push(Diagnostic { |
69 | range: sema.diagnostics_range(d).range, | 69 | range: sema.diagnostics_range(d).range, |
70 | message: d.message(), | 70 | message: d.message(), |
@@ -88,14 +88,12 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic> | |||
88 | field_list = field_list.append_field(&field); | 88 | field_list = field_list.append_field(&field); |
89 | } | 89 | } |
90 | 90 | ||
91 | let mut builder = TextEditBuilder::default(); | 91 | let edit = { |
92 | algo::diff(&d.ast(db).syntax(), &field_list.syntax()).into_text_edit(&mut builder); | 92 | let mut builder = TextEditBuilder::default(); |
93 | 93 | algo::diff(&d.ast(db).syntax(), &field_list.syntax()).into_text_edit(&mut builder); | |
94 | Some(SourceChange::source_file_edit_from( | 94 | builder.finish() |
95 | "Fill struct fields", | 95 | }; |
96 | file_id, | 96 | Some(Fix::new("Fill struct fields", SourceFileEdit { file_id, edit }.into())) |
97 | builder.finish(), | ||
98 | )) | ||
99 | }; | 97 | }; |
100 | 98 | ||
101 | res.borrow_mut().push(Diagnostic { | 99 | res.borrow_mut().push(Diagnostic { |
@@ -117,7 +115,8 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic> | |||
117 | let node = d.ast(db); | 115 | let node = d.ast(db); |
118 | let replacement = format!("Ok({})", node.syntax()); | 116 | let replacement = format!("Ok({})", node.syntax()); |
119 | let edit = TextEdit::replace(node.syntax().text_range(), replacement); | 117 | let edit = TextEdit::replace(node.syntax().text_range(), replacement); |
120 | let fix = SourceChange::source_file_edit_from("Wrap with ok", file_id, edit); | 118 | let source_change = SourceChange::source_file_edit_from(file_id, edit); |
119 | let fix = Fix::new("Wrap with ok", source_change); | ||
121 | res.borrow_mut().push(Diagnostic { | 120 | res.borrow_mut().push(Diagnostic { |
122 | range: sema.diagnostics_range(d).range, | 121 | range: sema.diagnostics_range(d).range, |
123 | message: d.message(), | 122 | message: d.message(), |
@@ -154,9 +153,9 @@ fn check_unnecessary_braces_in_use_statement( | |||
154 | range, | 153 | range, |
155 | message: "Unnecessary braces in use statement".to_string(), | 154 | message: "Unnecessary braces in use statement".to_string(), |
156 | severity: Severity::WeakWarning, | 155 | severity: Severity::WeakWarning, |
157 | fix: Some(SourceChange::source_file_edit( | 156 | fix: Some(Fix::new( |
158 | "Remove unnecessary braces", | 157 | "Remove unnecessary braces", |
159 | SourceFileEdit { file_id, edit }, | 158 | SourceFileEdit { file_id, edit }.into(), |
160 | )), | 159 | )), |
161 | }); | 160 | }); |
162 | } | 161 | } |
@@ -198,9 +197,9 @@ fn check_struct_shorthand_initialization( | |||
198 | range: record_field.syntax().text_range(), | 197 | range: record_field.syntax().text_range(), |
199 | message: "Shorthand struct initialization".to_string(), | 198 | message: "Shorthand struct initialization".to_string(), |
200 | severity: Severity::WeakWarning, | 199 | severity: Severity::WeakWarning, |
201 | fix: Some(SourceChange::source_file_edit( | 200 | fix: Some(Fix::new( |
202 | "Use struct shorthand initialization", | 201 | "Use struct shorthand initialization", |
203 | SourceFileEdit { file_id, edit }, | 202 | SourceFileEdit { file_id, edit }.into(), |
204 | )), | 203 | )), |
205 | }); | 204 | }); |
206 | } | 205 | } |
@@ -240,7 +239,7 @@ mod tests { | |||
240 | let diagnostic = | 239 | let diagnostic = |
241 | diagnostics.pop().unwrap_or_else(|| panic!("no diagnostics for:\n{}\n", before)); | 240 | diagnostics.pop().unwrap_or_else(|| panic!("no diagnostics for:\n{}\n", before)); |
242 | let mut fix = diagnostic.fix.unwrap(); | 241 | let mut fix = diagnostic.fix.unwrap(); |
243 | let edit = fix.source_file_edits.pop().unwrap().edit; | 242 | let edit = fix.source_change.source_file_edits.pop().unwrap().edit; |
244 | let actual = { | 243 | let actual = { |
245 | let mut actual = before.to_string(); | 244 | let mut actual = before.to_string(); |
246 | edit.apply(&mut actual); | 245 | edit.apply(&mut actual); |
@@ -258,7 +257,7 @@ mod tests { | |||
258 | let (analysis, file_position) = analysis_and_position(fixture); | 257 | let (analysis, file_position) = analysis_and_position(fixture); |
259 | let diagnostic = analysis.diagnostics(file_position.file_id).unwrap().pop().unwrap(); | 258 | let diagnostic = analysis.diagnostics(file_position.file_id).unwrap().pop().unwrap(); |
260 | let mut fix = diagnostic.fix.unwrap(); | 259 | let mut fix = diagnostic.fix.unwrap(); |
261 | let edit = fix.source_file_edits.pop().unwrap().edit; | 260 | let edit = fix.source_change.source_file_edits.pop().unwrap().edit; |
262 | let target_file_contents = analysis.file_text(file_position.file_id).unwrap(); | 261 | let target_file_contents = analysis.file_text(file_position.file_id).unwrap(); |
263 | let actual = { | 262 | let actual = { |
264 | let mut actual = target_file_contents.to_string(); | 263 | let mut actual = target_file_contents.to_string(); |
@@ -295,7 +294,7 @@ mod tests { | |||
295 | let (analysis, file_id) = single_file(before); | 294 | let (analysis, file_id) = single_file(before); |
296 | let diagnostic = analysis.diagnostics(file_id).unwrap().pop().unwrap(); | 295 | let diagnostic = analysis.diagnostics(file_id).unwrap().pop().unwrap(); |
297 | let mut fix = diagnostic.fix.unwrap(); | 296 | let mut fix = diagnostic.fix.unwrap(); |
298 | let edit = fix.source_file_edits.pop().unwrap().edit; | 297 | let edit = fix.source_change.source_file_edits.pop().unwrap().edit; |
299 | let actual = { | 298 | let actual = { |
300 | let mut actual = before.to_string(); | 299 | let mut actual = before.to_string(); |
301 | edit.apply(&mut actual); | 300 | edit.apply(&mut actual); |
@@ -616,22 +615,24 @@ mod tests { | |||
616 | Diagnostic { | 615 | Diagnostic { |
617 | message: "unresolved module", | 616 | message: "unresolved module", |
618 | range: 0..8, | 617 | range: 0..8, |
618 | severity: Error, | ||
619 | fix: Some( | 619 | fix: Some( |
620 | SourceChange { | 620 | Fix { |
621 | label: "Create module", | 621 | label: "Create module", |
622 | source_file_edits: [], | 622 | source_change: SourceChange { |
623 | file_system_edits: [ | 623 | source_file_edits: [], |
624 | CreateFile { | 624 | file_system_edits: [ |
625 | source_root: SourceRootId( | 625 | CreateFile { |
626 | 0, | 626 | source_root: SourceRootId( |
627 | ), | 627 | 0, |
628 | path: "foo.rs", | 628 | ), |
629 | }, | 629 | path: "foo.rs", |
630 | ], | 630 | }, |
631 | is_snippet: false, | 631 | ], |
632 | is_snippet: false, | ||
633 | }, | ||
632 | }, | 634 | }, |
633 | ), | 635 | ), |
634 | severity: Error, | ||
635 | }, | 636 | }, |
636 | ] | 637 | ] |
637 | "###); | 638 | "###); |
@@ -665,29 +666,31 @@ mod tests { | |||
665 | Diagnostic { | 666 | Diagnostic { |
666 | message: "Missing structure fields:\n- b", | 667 | message: "Missing structure fields:\n- b", |
667 | range: 224..233, | 668 | range: 224..233, |
669 | severity: Error, | ||
668 | fix: Some( | 670 | fix: Some( |
669 | SourceChange { | 671 | Fix { |
670 | label: "Fill struct fields", | 672 | label: "Fill struct fields", |
671 | source_file_edits: [ | 673 | source_change: SourceChange { |
672 | SourceFileEdit { | 674 | source_file_edits: [ |
673 | file_id: FileId( | 675 | SourceFileEdit { |
674 | 1, | 676 | file_id: FileId( |
675 | ), | 677 | 1, |
676 | edit: TextEdit { | 678 | ), |
677 | indels: [ | 679 | edit: TextEdit { |
678 | Indel { | 680 | indels: [ |
679 | insert: "{a:42, b: ()}", | 681 | Indel { |
680 | delete: 3..9, | 682 | insert: "{a:42, b: ()}", |
681 | }, | 683 | delete: 3..9, |
682 | ], | 684 | }, |
685 | ], | ||
686 | }, | ||
683 | }, | 687 | }, |
684 | }, | 688 | ], |
685 | ], | 689 | file_system_edits: [], |
686 | file_system_edits: [], | 690 | is_snippet: false, |
687 | is_snippet: false, | 691 | }, |
688 | }, | 692 | }, |
689 | ), | 693 | ), |
690 | severity: Error, | ||
691 | }, | 694 | }, |
692 | ] | 695 | ] |
693 | "###); | 696 | "###); |
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index 97ff67ee8..5ac002d82 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs | |||
@@ -97,8 +97,22 @@ pub type Cancelable<T> = Result<T, Canceled>; | |||
97 | pub struct Diagnostic { | 97 | pub struct Diagnostic { |
98 | pub message: String, | 98 | pub message: String, |
99 | pub range: TextRange, | 99 | pub range: TextRange, |
100 | pub fix: Option<SourceChange>, | ||
101 | pub severity: Severity, | 100 | pub severity: Severity, |
101 | pub fix: Option<Fix>, | ||
102 | } | ||
103 | |||
104 | #[derive(Debug)] | ||
105 | pub struct Fix { | ||
106 | pub label: String, | ||
107 | pub source_change: SourceChange, | ||
108 | } | ||
109 | |||
110 | impl Fix { | ||
111 | pub fn new(label: impl Into<String>, source_change: SourceChange) -> Self { | ||
112 | let label = label.into(); | ||
113 | assert!(label.starts_with(char::is_uppercase) && !label.ends_with('.')); | ||
114 | Self { label, source_change } | ||
115 | } | ||
102 | } | 116 | } |
103 | 117 | ||
104 | /// Info associated with a text range. | 118 | /// Info associated with a text range. |
@@ -493,7 +507,7 @@ impl Analysis { | |||
493 | ) -> Cancelable<Result<SourceChange, SsrError>> { | 507 | ) -> Cancelable<Result<SourceChange, SsrError>> { |
494 | self.with_db(|db| { | 508 | self.with_db(|db| { |
495 | let edits = ssr::parse_search_replace(query, parse_only, db)?; | 509 | let edits = ssr::parse_search_replace(query, parse_only, db)?; |
496 | Ok(SourceChange::source_file_edits("Structural Search Replace", edits)) | 510 | Ok(SourceChange::source_file_edits(edits)) |
497 | }) | 511 | }) |
498 | } | 512 | } |
499 | 513 | ||
diff --git a/crates/ra_ide/src/references/rename.rs b/crates/ra_ide/src/references/rename.rs index fd2163dad..28c6349b1 100644 --- a/crates/ra_ide/src/references/rename.rs +++ b/crates/ra_ide/src/references/rename.rs | |||
@@ -128,7 +128,7 @@ fn rename_mod( | |||
128 | source_file_edits.extend(ref_edits); | 128 | source_file_edits.extend(ref_edits); |
129 | } | 129 | } |
130 | 130 | ||
131 | Some(SourceChange::from_edits("Rename", source_file_edits, file_system_edits)) | 131 | Some(SourceChange::from_edits(source_file_edits, file_system_edits)) |
132 | } | 132 | } |
133 | 133 | ||
134 | fn rename_to_self(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<SourceChange>> { | 134 | fn rename_to_self(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<SourceChange>> { |
@@ -171,7 +171,7 @@ fn rename_to_self(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo | |||
171 | ), | 171 | ), |
172 | }); | 172 | }); |
173 | 173 | ||
174 | Some(RangeInfo::new(range, SourceChange::source_file_edits("Rename", edits))) | 174 | Some(RangeInfo::new(range, SourceChange::source_file_edits(edits))) |
175 | } | 175 | } |
176 | 176 | ||
177 | fn text_edit_from_self_param( | 177 | fn text_edit_from_self_param( |
@@ -234,7 +234,7 @@ fn rename_self_to_param( | |||
234 | let range = ast::SelfParam::cast(self_token.parent()) | 234 | let range = ast::SelfParam::cast(self_token.parent()) |
235 | .map_or(self_token.text_range(), |p| p.syntax().text_range()); | 235 | .map_or(self_token.text_range(), |p| p.syntax().text_range()); |
236 | 236 | ||
237 | Some(RangeInfo::new(range, SourceChange::source_file_edits("Rename", edits))) | 237 | Some(RangeInfo::new(range, SourceChange::source_file_edits(edits))) |
238 | } | 238 | } |
239 | 239 | ||
240 | fn rename_reference( | 240 | fn rename_reference( |
@@ -253,7 +253,7 @@ fn rename_reference( | |||
253 | return None; | 253 | return None; |
254 | } | 254 | } |
255 | 255 | ||
256 | Some(RangeInfo::new(range, SourceChange::source_file_edits("Rename", edit))) | 256 | Some(RangeInfo::new(range, SourceChange::source_file_edits(edit))) |
257 | } | 257 | } |
258 | 258 | ||
259 | #[cfg(test)] | 259 | #[cfg(test)] |
@@ -642,7 +642,6 @@ mod tests { | |||
642 | RangeInfo { | 642 | RangeInfo { |
643 | range: 4..7, | 643 | range: 4..7, |
644 | info: SourceChange { | 644 | info: SourceChange { |
645 | label: "Rename", | ||
646 | source_file_edits: [ | 645 | source_file_edits: [ |
647 | SourceFileEdit { | 646 | SourceFileEdit { |
648 | file_id: FileId( | 647 | file_id: FileId( |
@@ -694,7 +693,6 @@ mod tests { | |||
694 | RangeInfo { | 693 | RangeInfo { |
695 | range: 4..7, | 694 | range: 4..7, |
696 | info: SourceChange { | 695 | info: SourceChange { |
697 | label: "Rename", | ||
698 | source_file_edits: [ | 696 | source_file_edits: [ |
699 | SourceFileEdit { | 697 | SourceFileEdit { |
700 | file_id: FileId( | 698 | file_id: FileId( |
@@ -777,7 +775,6 @@ mod tests { | |||
777 | RangeInfo { | 775 | RangeInfo { |
778 | range: 8..11, | 776 | range: 8..11, |
779 | info: SourceChange { | 777 | info: SourceChange { |
780 | label: "Rename", | ||
781 | source_file_edits: [ | 778 | source_file_edits: [ |
782 | SourceFileEdit { | 779 | SourceFileEdit { |
783 | file_id: FileId( | 780 | file_id: FileId( |
diff --git a/crates/ra_ide/src/typing.rs b/crates/ra_ide/src/typing.rs index cd48cad93..39bb3b357 100644 --- a/crates/ra_ide/src/typing.rs +++ b/crates/ra_ide/src/typing.rs | |||
@@ -17,7 +17,7 @@ mod on_enter; | |||
17 | 17 | ||
18 | use ra_db::{FilePosition, SourceDatabase}; | 18 | use ra_db::{FilePosition, SourceDatabase}; |
19 | use ra_fmt::leading_indent; | 19 | use ra_fmt::leading_indent; |
20 | use ra_ide_db::{source_change::SingleFileChange, RootDatabase}; | 20 | use ra_ide_db::RootDatabase; |
21 | use ra_syntax::{ | 21 | use ra_syntax::{ |
22 | algo::find_node_at_offset, | 22 | algo::find_node_at_offset, |
23 | ast::{self, AstToken}, | 23 | ast::{self, AstToken}, |
@@ -40,15 +40,11 @@ pub(crate) fn on_char_typed( | |||
40 | assert!(TRIGGER_CHARS.contains(char_typed)); | 40 | assert!(TRIGGER_CHARS.contains(char_typed)); |
41 | let file = &db.parse(position.file_id).tree(); | 41 | let file = &db.parse(position.file_id).tree(); |
42 | assert_eq!(file.syntax().text().char_at(position.offset), Some(char_typed)); | 42 | assert_eq!(file.syntax().text().char_at(position.offset), Some(char_typed)); |
43 | let single_file_change = on_char_typed_inner(file, position.offset, char_typed)?; | 43 | let text_edit = on_char_typed_inner(file, position.offset, char_typed)?; |
44 | Some(single_file_change.into_source_change(position.file_id)) | 44 | Some(SourceChange::source_file_edit_from(position.file_id, text_edit)) |
45 | } | 45 | } |
46 | 46 | ||
47 | fn on_char_typed_inner( | 47 | fn on_char_typed_inner(file: &SourceFile, offset: TextSize, char_typed: char) -> Option<TextEdit> { |
48 | file: &SourceFile, | ||
49 | offset: TextSize, | ||
50 | char_typed: char, | ||
51 | ) -> Option<SingleFileChange> { | ||
52 | assert!(TRIGGER_CHARS.contains(char_typed)); | 48 | assert!(TRIGGER_CHARS.contains(char_typed)); |
53 | match char_typed { | 49 | match char_typed { |
54 | '.' => on_dot_typed(file, offset), | 50 | '.' => on_dot_typed(file, offset), |
@@ -61,7 +57,7 @@ fn on_char_typed_inner( | |||
61 | /// Returns an edit which should be applied after `=` was typed. Primarily, | 57 | /// Returns an edit which should be applied after `=` was typed. Primarily, |
62 | /// this works when adding `let =`. | 58 | /// this works when adding `let =`. |
63 | // FIXME: use a snippet completion instead of this hack here. | 59 | // FIXME: use a snippet completion instead of this hack here. |
64 | fn on_eq_typed(file: &SourceFile, offset: TextSize) -> Option<SingleFileChange> { | 60 | fn on_eq_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> { |
65 | assert_eq!(file.syntax().text().char_at(offset), Some('=')); | 61 | assert_eq!(file.syntax().text().char_at(offset), Some('=')); |
66 | let let_stmt: ast::LetStmt = find_node_at_offset(file.syntax(), offset)?; | 62 | let let_stmt: ast::LetStmt = find_node_at_offset(file.syntax(), offset)?; |
67 | if let_stmt.semicolon_token().is_some() { | 63 | if let_stmt.semicolon_token().is_some() { |
@@ -79,14 +75,11 @@ fn on_eq_typed(file: &SourceFile, offset: TextSize) -> Option<SingleFileChange> | |||
79 | return None; | 75 | return None; |
80 | } | 76 | } |
81 | let offset = let_stmt.syntax().text_range().end(); | 77 | let offset = let_stmt.syntax().text_range().end(); |
82 | Some(SingleFileChange { | 78 | Some(TextEdit::insert(offset, ";".to_string())) |
83 | label: "add semicolon".to_string(), | ||
84 | edit: TextEdit::insert(offset, ";".to_string()), | ||
85 | }) | ||
86 | } | 79 | } |
87 | 80 | ||
88 | /// Returns an edit which should be applied when a dot ('.') is typed on a blank line, indenting the line appropriately. | 81 | /// Returns an edit which should be applied when a dot ('.') is typed on a blank line, indenting the line appropriately. |
89 | fn on_dot_typed(file: &SourceFile, offset: TextSize) -> Option<SingleFileChange> { | 82 | fn on_dot_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> { |
90 | assert_eq!(file.syntax().text().char_at(offset), Some('.')); | 83 | assert_eq!(file.syntax().text().char_at(offset), Some('.')); |
91 | let whitespace = | 84 | let whitespace = |
92 | file.syntax().token_at_offset(offset).left_biased().and_then(ast::Whitespace::cast)?; | 85 | file.syntax().token_at_offset(offset).left_biased().and_then(ast::Whitespace::cast)?; |
@@ -107,14 +100,11 @@ fn on_dot_typed(file: &SourceFile, offset: TextSize) -> Option<SingleFileChange> | |||
107 | return None; | 100 | return None; |
108 | } | 101 | } |
109 | 102 | ||
110 | Some(SingleFileChange { | 103 | Some(TextEdit::replace(TextRange::new(offset - current_indent_len, offset), target_indent)) |
111 | label: "reindent dot".to_string(), | ||
112 | edit: TextEdit::replace(TextRange::new(offset - current_indent_len, offset), target_indent), | ||
113 | }) | ||
114 | } | 104 | } |
115 | 105 | ||
116 | /// Adds a space after an arrow when `fn foo() { ... }` is turned into `fn foo() -> { ... }` | 106 | /// Adds a space after an arrow when `fn foo() { ... }` is turned into `fn foo() -> { ... }` |
117 | fn on_arrow_typed(file: &SourceFile, offset: TextSize) -> Option<SingleFileChange> { | 107 | fn on_arrow_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> { |
118 | let file_text = file.syntax().text(); | 108 | let file_text = file.syntax().text(); |
119 | assert_eq!(file_text.char_at(offset), Some('>')); | 109 | assert_eq!(file_text.char_at(offset), Some('>')); |
120 | let after_arrow = offset + TextSize::of('>'); | 110 | let after_arrow = offset + TextSize::of('>'); |
@@ -125,10 +115,7 @@ fn on_arrow_typed(file: &SourceFile, offset: TextSize) -> Option<SingleFileChang | |||
125 | return None; | 115 | return None; |
126 | } | 116 | } |
127 | 117 | ||
128 | Some(SingleFileChange { | 118 | Some(TextEdit::insert(after_arrow, " ".to_string())) |
129 | label: "add space after return type".to_string(), | ||
130 | edit: TextEdit::insert(after_arrow, " ".to_string()), | ||
131 | }) | ||
132 | } | 119 | } |
133 | 120 | ||
134 | #[cfg(test)] | 121 | #[cfg(test)] |
@@ -144,7 +131,7 @@ mod tests { | |||
144 | edit.apply(&mut before); | 131 | edit.apply(&mut before); |
145 | let parse = SourceFile::parse(&before); | 132 | let parse = SourceFile::parse(&before); |
146 | on_char_typed_inner(&parse.tree(), offset, char_typed).map(|it| { | 133 | on_char_typed_inner(&parse.tree(), offset, char_typed).map(|it| { |
147 | it.edit.apply(&mut before); | 134 | it.apply(&mut before); |
148 | before.to_string() | 135 | before.to_string() |
149 | }) | 136 | }) |
150 | } | 137 | } |
diff --git a/crates/ra_ide/src/typing/on_enter.rs b/crates/ra_ide/src/typing/on_enter.rs index 85be14ad3..e7d64b4f6 100644 --- a/crates/ra_ide/src/typing/on_enter.rs +++ b/crates/ra_ide/src/typing/on_enter.rs | |||
@@ -41,10 +41,7 @@ pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<Sour | |||
41 | let inserted = format!("\n{}{} $0", indent, prefix); | 41 | let inserted = format!("\n{}{} $0", indent, prefix); |
42 | let edit = TextEdit::insert(position.offset, inserted); | 42 | let edit = TextEdit::insert(position.offset, inserted); |
43 | 43 | ||
44 | let mut res = SourceChange::source_file_edit( | 44 | let mut res = SourceChange::from(SourceFileEdit { edit, file_id: position.file_id }); |
45 | "On enter", | ||
46 | SourceFileEdit { edit, file_id: position.file_id }, | ||
47 | ); | ||
48 | res.is_snippet = true; | 45 | res.is_snippet = true; |
49 | Some(res) | 46 | Some(res) |
50 | } | 47 | } |
diff --git a/crates/ra_ide_db/src/source_change.rs b/crates/ra_ide_db/src/source_change.rs index 3484f5588..e713f4b7e 100644 --- a/crates/ra_ide_db/src/source_change.rs +++ b/crates/ra_ide_db/src/source_change.rs | |||
@@ -8,8 +8,6 @@ use ra_text_edit::TextEdit; | |||
8 | 8 | ||
9 | #[derive(Debug, Clone)] | 9 | #[derive(Debug, Clone)] |
10 | pub struct SourceChange { | 10 | pub struct SourceChange { |
11 | /// For display in the undo log in the editor | ||
12 | pub label: String, | ||
13 | pub source_file_edits: Vec<SourceFileEdit>, | 11 | pub source_file_edits: Vec<SourceFileEdit>, |
14 | pub file_system_edits: Vec<FileSystemEdit>, | 12 | pub file_system_edits: Vec<FileSystemEdit>, |
15 | pub is_snippet: bool, | 13 | pub is_snippet: bool, |
@@ -18,63 +16,22 @@ pub struct SourceChange { | |||
18 | impl SourceChange { | 16 | impl SourceChange { |
19 | /// Creates a new SourceChange with the given label | 17 | /// Creates a new SourceChange with the given label |
20 | /// from the edits. | 18 | /// from the edits. |
21 | pub fn from_edits<L: Into<String>>( | 19 | pub fn from_edits( |
22 | label: L, | ||
23 | source_file_edits: Vec<SourceFileEdit>, | 20 | source_file_edits: Vec<SourceFileEdit>, |
24 | file_system_edits: Vec<FileSystemEdit>, | 21 | file_system_edits: Vec<FileSystemEdit>, |
25 | ) -> Self { | 22 | ) -> Self { |
26 | SourceChange { | 23 | SourceChange { source_file_edits, file_system_edits, is_snippet: false } |
27 | label: label.into(), | ||
28 | source_file_edits, | ||
29 | file_system_edits, | ||
30 | is_snippet: false, | ||
31 | } | ||
32 | } | 24 | } |
33 | 25 | ||
34 | /// Creates a new SourceChange with the given label, | 26 | /// Creates a new SourceChange with the given label, |
35 | /// containing only the given `SourceFileEdits`. | 27 | /// containing only the given `SourceFileEdits`. |
36 | pub fn source_file_edits<L: Into<String>>(label: L, edits: Vec<SourceFileEdit>) -> Self { | 28 | pub fn source_file_edits(edits: Vec<SourceFileEdit>) -> Self { |
37 | let label = label.into(); | 29 | SourceChange { source_file_edits: edits, file_system_edits: vec![], is_snippet: false } |
38 | assert!(label.starts_with(char::is_uppercase)); | ||
39 | SourceChange { | ||
40 | label: label, | ||
41 | source_file_edits: edits, | ||
42 | file_system_edits: vec![], | ||
43 | is_snippet: false, | ||
44 | } | ||
45 | } | ||
46 | |||
47 | /// Creates a new SourceChange with the given label, | ||
48 | /// containing only the given `FileSystemEdits`. | ||
49 | pub(crate) fn file_system_edits<L: Into<String>>(label: L, edits: Vec<FileSystemEdit>) -> Self { | ||
50 | SourceChange { | ||
51 | label: label.into(), | ||
52 | source_file_edits: vec![], | ||
53 | file_system_edits: edits, | ||
54 | is_snippet: false, | ||
55 | } | ||
56 | } | ||
57 | |||
58 | /// Creates a new SourceChange with the given label, | ||
59 | /// containing only a single `SourceFileEdit`. | ||
60 | pub fn source_file_edit<L: Into<String>>(label: L, edit: SourceFileEdit) -> Self { | ||
61 | SourceChange::source_file_edits(label, vec![edit]) | ||
62 | } | ||
63 | |||
64 | /// Creates a new SourceChange with the given label | ||
65 | /// from the given `FileId` and `TextEdit` | ||
66 | pub fn source_file_edit_from<L: Into<String>>( | ||
67 | label: L, | ||
68 | file_id: FileId, | ||
69 | edit: TextEdit, | ||
70 | ) -> Self { | ||
71 | SourceChange::source_file_edit(label, SourceFileEdit { file_id, edit }) | ||
72 | } | 30 | } |
73 | |||
74 | /// Creates a new SourceChange with the given label | 31 | /// Creates a new SourceChange with the given label |
75 | /// from the given `FileId` and `TextEdit` | 32 | /// from the given `FileId` and `TextEdit` |
76 | pub fn file_system_edit<L: Into<String>>(label: L, edit: FileSystemEdit) -> Self { | 33 | pub fn source_file_edit_from(file_id: FileId, edit: TextEdit) -> Self { |
77 | SourceChange::file_system_edits(label, vec![edit]) | 34 | SourceFileEdit { file_id, edit }.into() |
78 | } | 35 | } |
79 | } | 36 | } |
80 | 37 | ||
@@ -84,23 +41,27 @@ pub struct SourceFileEdit { | |||
84 | pub edit: TextEdit, | 41 | pub edit: TextEdit, |
85 | } | 42 | } |
86 | 43 | ||
44 | impl From<SourceFileEdit> for SourceChange { | ||
45 | fn from(edit: SourceFileEdit) -> SourceChange { | ||
46 | SourceChange { | ||
47 | source_file_edits: vec![edit], | ||
48 | file_system_edits: Vec::new(), | ||
49 | is_snippet: false, | ||
50 | } | ||
51 | } | ||
52 | } | ||
53 | |||
87 | #[derive(Debug, Clone)] | 54 | #[derive(Debug, Clone)] |
88 | pub enum FileSystemEdit { | 55 | pub enum FileSystemEdit { |
89 | CreateFile { source_root: SourceRootId, path: RelativePathBuf }, | 56 | CreateFile { source_root: SourceRootId, path: RelativePathBuf }, |
90 | MoveFile { src: FileId, dst_source_root: SourceRootId, dst_path: RelativePathBuf }, | 57 | MoveFile { src: FileId, dst_source_root: SourceRootId, dst_path: RelativePathBuf }, |
91 | } | 58 | } |
92 | 59 | ||
93 | pub struct SingleFileChange { | 60 | impl From<FileSystemEdit> for SourceChange { |
94 | pub label: String, | 61 | fn from(edit: FileSystemEdit) -> SourceChange { |
95 | pub edit: TextEdit, | ||
96 | } | ||
97 | |||
98 | impl SingleFileChange { | ||
99 | pub fn into_source_change(self, file_id: FileId) -> SourceChange { | ||
100 | SourceChange { | 62 | SourceChange { |
101 | label: self.label, | 63 | source_file_edits: Vec::new(), |
102 | source_file_edits: vec![SourceFileEdit { file_id, edit: self.edit }], | 64 | file_system_edits: vec![edit], |
103 | file_system_edits: Vec::new(), | ||
104 | is_snippet: false, | 65 | is_snippet: false, |
105 | } | 66 | } |
106 | } | 67 | } |
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index d75c48597..0e4412ade 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs | |||
@@ -102,6 +102,7 @@ pub struct ClientCapsConfig { | |||
102 | pub hierarchical_symbols: bool, | 102 | pub hierarchical_symbols: bool, |
103 | pub code_action_literals: bool, | 103 | pub code_action_literals: bool, |
104 | pub work_done_progress: bool, | 104 | pub work_done_progress: bool, |
105 | pub code_action_group: bool, | ||
105 | } | 106 | } |
106 | 107 | ||
107 | impl Default for Config { | 108 | impl Default for Config { |
@@ -294,9 +295,13 @@ impl Config { | |||
294 | 295 | ||
295 | self.assist.allow_snippets(false); | 296 | self.assist.allow_snippets(false); |
296 | if let Some(experimental) = &caps.experimental { | 297 | if let Some(experimental) = &caps.experimental { |
297 | let enable = | 298 | let snippet_text_edit = |
298 | experimental.get("snippetTextEdit").and_then(|it| it.as_bool()) == Some(true); | 299 | experimental.get("snippetTextEdit").and_then(|it| it.as_bool()) == Some(true); |
299 | self.assist.allow_snippets(enable); | 300 | self.assist.allow_snippets(snippet_text_edit); |
301 | |||
302 | let code_action_group = | ||
303 | experimental.get("codeActionGroup").and_then(|it| it.as_bool()) == Some(true); | ||
304 | self.client_caps.code_action_group = code_action_group | ||
300 | } | 305 | } |
301 | } | 306 | } |
302 | } | 307 | } |
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 96466b5c9..c40cfdcdc 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 | |||
@@ -65,6 +65,7 @@ expression: diag | |||
65 | fixes: [ | 65 | fixes: [ |
66 | CodeAction { | 66 | CodeAction { |
67 | title: "return the expression directly", | 67 | title: "return the expression directly", |
68 | group: None, | ||
68 | kind: Some( | 69 | kind: Some( |
69 | "quickfix", | 70 | "quickfix", |
70 | ), | 71 | ), |
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 8f962277f..6dd3fcb2e 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 | |||
@@ -50,6 +50,7 @@ expression: diag | |||
50 | fixes: [ | 50 | fixes: [ |
51 | CodeAction { | 51 | CodeAction { |
52 | title: "consider prefixing with an underscore", | 52 | title: "consider prefixing with an underscore", |
53 | group: None, | ||
53 | kind: Some( | 54 | kind: Some( |
54 | "quickfix", | 55 | "quickfix", |
55 | ), | 56 | ), |
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs index afea59525..a500d670a 100644 --- a/crates/rust-analyzer/src/diagnostics/to_proto.rs +++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs | |||
@@ -145,6 +145,7 @@ fn map_rust_child_diagnostic( | |||
145 | } else { | 145 | } else { |
146 | MappedRustChildDiagnostic::SuggestedFix(lsp_ext::CodeAction { | 146 | MappedRustChildDiagnostic::SuggestedFix(lsp_ext::CodeAction { |
147 | title: rd.message.clone(), | 147 | title: rd.message.clone(), |
148 | group: None, | ||
148 | kind: Some("quickfix".to_string()), | 149 | kind: Some("quickfix".to_string()), |
149 | edit: Some(lsp_ext::SnippetWorkspaceEdit { | 150 | edit: Some(lsp_ext::SnippetWorkspaceEdit { |
150 | // FIXME: there's no good reason to use edit_map here.... | 151 | // FIXME: there's no good reason to use edit_map here.... |
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index 0fd60caf4..c25d90a50 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs | |||
@@ -133,14 +133,6 @@ pub struct Runnable { | |||
133 | pub cwd: Option<PathBuf>, | 133 | pub cwd: Option<PathBuf>, |
134 | } | 134 | } |
135 | 135 | ||
136 | #[derive(Deserialize, Serialize, Debug)] | ||
137 | #[serde(rename_all = "camelCase")] | ||
138 | pub struct SourceChange { | ||
139 | pub label: String, | ||
140 | pub workspace_edit: SnippetWorkspaceEdit, | ||
141 | pub cursor_position: Option<lsp_types::TextDocumentPositionParams>, | ||
142 | } | ||
143 | |||
144 | pub enum InlayHints {} | 136 | pub enum InlayHints {} |
145 | 137 | ||
146 | impl Request for InlayHints { | 138 | impl Request for InlayHints { |
@@ -196,6 +188,8 @@ impl Request for CodeActionRequest { | |||
196 | pub struct CodeAction { | 188 | pub struct CodeAction { |
197 | pub title: String, | 189 | pub title: String, |
198 | #[serde(skip_serializing_if = "Option::is_none")] | 190 | #[serde(skip_serializing_if = "Option::is_none")] |
191 | pub group: Option<String>, | ||
192 | #[serde(skip_serializing_if = "Option::is_none")] | ||
199 | pub kind: Option<String>, | 193 | pub kind: Option<String>, |
200 | #[serde(skip_serializing_if = "Option::is_none")] | 194 | #[serde(skip_serializing_if = "Option::is_none")] |
201 | pub command: Option<lsp_types::Command>, | 195 | pub command: Option<lsp_types::Command>, |
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index 25e660bd5..a9703e1d6 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs | |||
@@ -18,7 +18,7 @@ use lsp_types::{ | |||
18 | SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, Url, WorkspaceEdit, | 18 | SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, Url, WorkspaceEdit, |
19 | }; | 19 | }; |
20 | use ra_ide::{ | 20 | use ra_ide::{ |
21 | Assist, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, SearchScope, | 21 | FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, SearchScope, |
22 | TextEdit, | 22 | TextEdit, |
23 | }; | 23 | }; |
24 | use ra_prof::profile; | 24 | use ra_prof::profile; |
@@ -720,6 +720,7 @@ pub fn handle_code_action( | |||
720 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; | 720 | let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?; |
721 | let line_index = world.analysis().file_line_index(file_id)?; | 721 | let line_index = world.analysis().file_line_index(file_id)?; |
722 | let range = from_proto::text_range(&line_index, params.range); | 722 | let range = from_proto::text_range(&line_index, params.range); |
723 | let frange = FileRange { file_id, range }; | ||
723 | 724 | ||
724 | let diagnostics = world.analysis().diagnostics(file_id)?; | 725 | let diagnostics = world.analysis().diagnostics(file_id)?; |
725 | let mut res: Vec<lsp_ext::CodeAction> = Vec::new(); | 726 | let mut res: Vec<lsp_ext::CodeAction> = Vec::new(); |
@@ -730,10 +731,11 @@ pub fn handle_code_action( | |||
730 | .filter(|(diag_range, _fix)| diag_range.intersect(range).is_some()) | 731 | .filter(|(diag_range, _fix)| diag_range.intersect(range).is_some()) |
731 | .map(|(_range, fix)| fix); | 732 | .map(|(_range, fix)| fix); |
732 | 733 | ||
733 | for source_edit in fixes_from_diagnostics { | 734 | for fix in fixes_from_diagnostics { |
734 | let title = source_edit.label.clone(); | 735 | let title = fix.label; |
735 | let edit = to_proto::snippet_workspace_edit(&world, source_edit)?; | 736 | let edit = to_proto::snippet_workspace_edit(&world, fix.source_change)?; |
736 | let action = lsp_ext::CodeAction { title, kind: None, edit: Some(edit), command: None }; | 737 | let action = |
738 | lsp_ext::CodeAction { title, group: None, kind: None, edit: Some(edit), command: None }; | ||
737 | res.push(action); | 739 | res.push(action); |
738 | } | 740 | } |
739 | 741 | ||
@@ -745,53 +747,9 @@ pub fn handle_code_action( | |||
745 | res.push(fix.action.clone()); | 747 | res.push(fix.action.clone()); |
746 | } | 748 | } |
747 | 749 | ||
748 | let mut grouped_assists: FxHashMap<String, (usize, Vec<Assist>)> = FxHashMap::default(); | 750 | for assist in world.analysis().assists(&world.config.assist, frange)?.into_iter() { |
749 | for assist in | 751 | res.push(to_proto::code_action(&world, assist)?.into()); |
750 | world.analysis().assists(&world.config.assist, FileRange { file_id, range })?.into_iter() | ||
751 | { | ||
752 | match &assist.group_label { | ||
753 | Some(label) => grouped_assists | ||
754 | .entry(label.to_owned()) | ||
755 | .or_insert_with(|| { | ||
756 | let idx = res.len(); | ||
757 | let dummy = lsp_ext::CodeAction { | ||
758 | title: String::new(), | ||
759 | kind: None, | ||
760 | command: None, | ||
761 | edit: None, | ||
762 | }; | ||
763 | res.push(dummy); | ||
764 | (idx, Vec::new()) | ||
765 | }) | ||
766 | .1 | ||
767 | .push(assist), | ||
768 | None => { | ||
769 | res.push(to_proto::code_action(&world, assist)?.into()); | ||
770 | } | ||
771 | } | ||
772 | } | ||
773 | |||
774 | for (group_label, (idx, assists)) in grouped_assists { | ||
775 | if assists.len() == 1 { | ||
776 | res[idx] = to_proto::code_action(&world, assists.into_iter().next().unwrap())?.into(); | ||
777 | } else { | ||
778 | let title = group_label; | ||
779 | |||
780 | let mut arguments = Vec::with_capacity(assists.len()); | ||
781 | for assist in assists { | ||
782 | let source_change = to_proto::source_change(&world, assist.source_change)?; | ||
783 | arguments.push(to_value(source_change)?); | ||
784 | } | ||
785 | |||
786 | let command = Some(Command { | ||
787 | title: title.clone(), | ||
788 | command: "rust-analyzer.selectAndApplySourceChange".to_string(), | ||
789 | arguments: Some(vec![serde_json::Value::Array(arguments)]), | ||
790 | }); | ||
791 | res[idx] = lsp_ext::CodeAction { title, kind: None, edit: None, command }; | ||
792 | } | ||
793 | } | 752 | } |
794 | |||
795 | Ok(Some(res)) | 753 | Ok(Some(res)) |
796 | } | 754 | } |
797 | 755 | ||
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index f6f4bb134..461944ada 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs | |||
@@ -478,15 +478,6 @@ pub(crate) fn resource_op( | |||
478 | Ok(res) | 478 | Ok(res) |
479 | } | 479 | } |
480 | 480 | ||
481 | pub(crate) fn source_change( | ||
482 | world: &WorldSnapshot, | ||
483 | source_change: SourceChange, | ||
484 | ) -> Result<lsp_ext::SourceChange> { | ||
485 | let label = source_change.label.clone(); | ||
486 | let workspace_edit = self::snippet_workspace_edit(world, source_change)?; | ||
487 | Ok(lsp_ext::SourceChange { label, workspace_edit, cursor_position: None }) | ||
488 | } | ||
489 | |||
490 | pub(crate) fn snippet_workspace_edit( | 481 | pub(crate) fn snippet_workspace_edit( |
491 | world: &WorldSnapshot, | 482 | world: &WorldSnapshot, |
492 | source_change: SourceChange, | 483 | source_change: SourceChange, |
@@ -606,6 +597,7 @@ fn main() <fold>{ | |||
606 | pub(crate) fn code_action(world: &WorldSnapshot, assist: Assist) -> Result<lsp_ext::CodeAction> { | 597 | pub(crate) fn code_action(world: &WorldSnapshot, assist: Assist) -> Result<lsp_ext::CodeAction> { |
607 | let res = lsp_ext::CodeAction { | 598 | let res = lsp_ext::CodeAction { |
608 | title: assist.label, | 599 | title: assist.label, |
600 | group: if world.config.client_caps.code_action_group { assist.group_label } else { None }, | ||
609 | kind: Some(String::new()), | 601 | kind: Some(String::new()), |
610 | edit: Some(snippet_workspace_edit(world, assist.source_change)?), | 602 | edit: Some(snippet_workspace_edit(world, assist.source_change)?), |
611 | command: None, | 603 | command: None, |