diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/assists/src/assist_context.rs | 16 | ||||
-rw-r--r-- | crates/assists/src/tests.rs | 19 | ||||
-rw-r--r-- | crates/ide/src/diagnostics.rs | 19 | ||||
-rw-r--r-- | crates/ide/src/diagnostics/field_shorthand.rs | 7 | ||||
-rw-r--r-- | crates/ide/src/diagnostics/fixes.rs | 18 | ||||
-rw-r--r-- | crates/ide/src/lib.rs | 4 | ||||
-rw-r--r-- | crates/ide/src/references/rename.rs | 138 | ||||
-rw-r--r-- | crates/ide/src/typing.rs | 9 | ||||
-rw-r--r-- | crates/ide_db/src/source_change.rs | 54 | ||||
-rw-r--r-- | crates/rust-analyzer/src/cli/ssr.rs | 8 | ||||
-rw-r--r-- | crates/rust-analyzer/src/handlers.rs | 14 | ||||
-rw-r--r-- | crates/rust-analyzer/src/to_proto.rs | 19 | ||||
-rw-r--r-- | crates/ssr/src/lib.rs | 28 | ||||
-rw-r--r-- | crates/ssr/src/matching.rs | 4 | ||||
-rw-r--r-- | crates/ssr/src/tests.rs | 3 |
15 files changed, 184 insertions, 176 deletions
diff --git a/crates/assists/src/assist_context.rs b/crates/assists/src/assist_context.rs index 91cc63427..de4b32573 100644 --- a/crates/assists/src/assist_context.rs +++ b/crates/assists/src/assist_context.rs | |||
@@ -10,7 +10,7 @@ use ide_db::{ | |||
10 | }; | 10 | }; |
11 | use ide_db::{ | 11 | use ide_db::{ |
12 | label::Label, | 12 | label::Label, |
13 | source_change::{FileSystemEdit, SourceChange, SourceFileEdit}, | 13 | source_change::{FileSystemEdit, SourceChange, SourceFileEdits}, |
14 | RootDatabase, | 14 | RootDatabase, |
15 | }; | 15 | }; |
16 | use syntax::{ | 16 | use syntax::{ |
@@ -181,7 +181,7 @@ pub(crate) struct AssistBuilder { | |||
181 | edit: TextEditBuilder, | 181 | edit: TextEditBuilder, |
182 | file_id: FileId, | 182 | file_id: FileId, |
183 | is_snippet: bool, | 183 | is_snippet: bool, |
184 | source_file_edits: Vec<SourceFileEdit>, | 184 | source_file_edits: SourceFileEdits, |
185 | file_system_edits: Vec<FileSystemEdit>, | 185 | file_system_edits: Vec<FileSystemEdit>, |
186 | } | 186 | } |
187 | 187 | ||
@@ -191,7 +191,7 @@ impl AssistBuilder { | |||
191 | edit: TextEdit::builder(), | 191 | edit: TextEdit::builder(), |
192 | file_id, | 192 | file_id, |
193 | is_snippet: false, | 193 | is_snippet: false, |
194 | source_file_edits: Vec::default(), | 194 | source_file_edits: SourceFileEdits::default(), |
195 | file_system_edits: Vec::default(), | 195 | file_system_edits: Vec::default(), |
196 | } | 196 | } |
197 | } | 197 | } |
@@ -204,15 +204,7 @@ impl AssistBuilder { | |||
204 | fn commit(&mut self) { | 204 | fn commit(&mut self) { |
205 | let edit = mem::take(&mut self.edit).finish(); | 205 | let edit = mem::take(&mut self.edit).finish(); |
206 | if !edit.is_empty() { | 206 | if !edit.is_empty() { |
207 | match self.source_file_edits.binary_search_by_key(&self.file_id, |edit| edit.file_id) { | 207 | self.source_file_edits.insert(self.file_id, edit); |
208 | Ok(idx) => self.source_file_edits[idx] | ||
209 | .edit | ||
210 | .union(edit) | ||
211 | .expect("overlapping edits for same file"), | ||
212 | Err(idx) => self | ||
213 | .source_file_edits | ||
214 | .insert(idx, SourceFileEdit { file_id: self.file_id, edit }), | ||
215 | } | ||
216 | } | 208 | } |
217 | } | 209 | } |
218 | 210 | ||
diff --git a/crates/assists/src/tests.rs b/crates/assists/src/tests.rs index fef29a0b8..d13d6ad31 100644 --- a/crates/assists/src/tests.rs +++ b/crates/assists/src/tests.rs | |||
@@ -80,10 +80,8 @@ fn check_doc_test(assist_id: &str, before: &str, after: &str) { | |||
80 | let actual = { | 80 | let actual = { |
81 | let source_change = assist.source_change.unwrap(); | 81 | let source_change = assist.source_change.unwrap(); |
82 | let mut actual = before; | 82 | let mut actual = before; |
83 | for source_file_edit in source_change.source_file_edits { | 83 | if let Some(source_file_edit) = source_change.source_file_edits.edits.get(&file_id) { |
84 | if source_file_edit.file_id == file_id { | 84 | source_file_edit.apply(&mut actual); |
85 | source_file_edit.edit.apply(&mut actual) | ||
86 | } | ||
87 | } | 85 | } |
88 | actual | 86 | actual |
89 | }; | 87 | }; |
@@ -116,20 +114,19 @@ fn check(handler: Handler, before: &str, expected: ExpectedResult, assist_label: | |||
116 | 114 | ||
117 | match (assist, expected) { | 115 | match (assist, expected) { |
118 | (Some(assist), ExpectedResult::After(after)) => { | 116 | (Some(assist), ExpectedResult::After(after)) => { |
119 | let mut source_change = assist.source_change.unwrap(); | 117 | let source_change = assist.source_change.unwrap(); |
120 | assert!(!source_change.source_file_edits.is_empty()); | 118 | assert!(!source_change.source_file_edits.is_empty()); |
121 | let skip_header = source_change.source_file_edits.len() == 1 | 119 | let skip_header = source_change.source_file_edits.len() == 1 |
122 | && source_change.file_system_edits.len() == 0; | 120 | && source_change.file_system_edits.len() == 0; |
123 | source_change.source_file_edits.sort_by_key(|it| it.file_id); | ||
124 | 121 | ||
125 | let mut buf = String::new(); | 122 | let mut buf = String::new(); |
126 | for source_file_edit in source_change.source_file_edits { | 123 | for (file_id, edit) in source_change.source_file_edits.edits { |
127 | let mut text = db.file_text(source_file_edit.file_id).as_ref().to_owned(); | 124 | let mut text = db.file_text(file_id).as_ref().to_owned(); |
128 | source_file_edit.edit.apply(&mut text); | 125 | edit.apply(&mut text); |
129 | if !skip_header { | 126 | if !skip_header { |
130 | let sr = db.file_source_root(source_file_edit.file_id); | 127 | let sr = db.file_source_root(file_id); |
131 | let sr = db.source_root(sr); | 128 | let sr = db.source_root(sr); |
132 | let path = sr.path_for_file(&source_file_edit.file_id).unwrap(); | 129 | let path = sr.path_for_file(&file_id).unwrap(); |
133 | format_to!(buf, "//- {}\n", path) | 130 | format_to!(buf, "//- {}\n", path) |
134 | } | 131 | } |
135 | buf.push_str(&text); | 132 | buf.push_str(&text); |
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index 055c0a79c..a43188fb0 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs | |||
@@ -13,8 +13,7 @@ use hir::{ | |||
13 | diagnostics::{Diagnostic as _, DiagnosticCode, DiagnosticSinkBuilder}, | 13 | diagnostics::{Diagnostic as _, DiagnosticCode, DiagnosticSinkBuilder}, |
14 | Semantics, | 14 | Semantics, |
15 | }; | 15 | }; |
16 | use ide_db::base_db::SourceDatabase; | 16 | use ide_db::{base_db::SourceDatabase, source_change::SourceFileEdits, RootDatabase}; |
17 | use ide_db::RootDatabase; | ||
18 | use itertools::Itertools; | 17 | use itertools::Itertools; |
19 | use rustc_hash::FxHashSet; | 18 | use rustc_hash::FxHashSet; |
20 | use syntax::{ | 19 | use syntax::{ |
@@ -23,7 +22,7 @@ use syntax::{ | |||
23 | }; | 22 | }; |
24 | use text_edit::TextEdit; | 23 | use text_edit::TextEdit; |
25 | 24 | ||
26 | use crate::{FileId, Label, SourceChange, SourceFileEdit}; | 25 | use crate::{FileId, Label, SourceChange}; |
27 | 26 | ||
28 | use self::fixes::DiagnosticWithFix; | 27 | use self::fixes::DiagnosticWithFix; |
29 | 28 | ||
@@ -220,7 +219,7 @@ fn check_unnecessary_braces_in_use_statement( | |||
220 | Diagnostic::hint(use_range, "Unnecessary braces in use statement".to_string()) | 219 | Diagnostic::hint(use_range, "Unnecessary braces in use statement".to_string()) |
221 | .with_fix(Some(Fix::new( | 220 | .with_fix(Some(Fix::new( |
222 | "Remove unnecessary braces", | 221 | "Remove unnecessary braces", |
223 | SourceFileEdit { file_id, edit }.into(), | 222 | SourceFileEdits::from_text_edit(file_id, edit).into(), |
224 | use_range, | 223 | use_range, |
225 | ))), | 224 | ))), |
226 | ); | 225 | ); |
@@ -265,13 +264,11 @@ mod tests { | |||
265 | .unwrap(); | 264 | .unwrap(); |
266 | let fix = diagnostic.fix.unwrap(); | 265 | let fix = diagnostic.fix.unwrap(); |
267 | let actual = { | 266 | let actual = { |
268 | let file_id = fix.source_change.source_file_edits.first().unwrap().file_id; | 267 | let file_id = *fix.source_change.source_file_edits.edits.keys().next().unwrap(); |
269 | let mut actual = analysis.file_text(file_id).unwrap().to_string(); | 268 | let mut actual = analysis.file_text(file_id).unwrap().to_string(); |
270 | 269 | ||
271 | // Go from the last one to the first one, so that ranges won't be affected by previous edits. | 270 | for edit in fix.source_change.source_file_edits.edits.values() { |
272 | // FIXME: https://github.com/rust-analyzer/rust-analyzer/issues/4901#issuecomment-644675309 | 271 | edit.apply(&mut actual); |
273 | for edit in fix.source_change.source_file_edits.iter().rev() { | ||
274 | edit.edit.apply(&mut actual); | ||
275 | } | 272 | } |
276 | actual | 273 | actual |
277 | }; | 274 | }; |
@@ -616,7 +613,9 @@ fn test_fn() { | |||
616 | Fix { | 613 | Fix { |
617 | label: "Create module", | 614 | label: "Create module", |
618 | source_change: SourceChange { | 615 | source_change: SourceChange { |
619 | source_file_edits: [], | 616 | source_file_edits: SourceFileEdits { |
617 | edits: {}, | ||
618 | }, | ||
620 | file_system_edits: [ | 619 | file_system_edits: [ |
621 | CreateFile { | 620 | CreateFile { |
622 | dst: AnchoredPathBuf { | 621 | dst: AnchoredPathBuf { |
diff --git a/crates/ide/src/diagnostics/field_shorthand.rs b/crates/ide/src/diagnostics/field_shorthand.rs index 16c6ea827..f4ec51b64 100644 --- a/crates/ide/src/diagnostics/field_shorthand.rs +++ b/crates/ide/src/diagnostics/field_shorthand.rs | |||
@@ -1,8 +1,7 @@ | |||
1 | //! Suggests shortening `Foo { field: field }` to `Foo { field }` in both | 1 | //! Suggests shortening `Foo { field: field }` to `Foo { field }` in both |
2 | //! expressions and patterns. | 2 | //! expressions and patterns. |
3 | 3 | ||
4 | use ide_db::base_db::FileId; | 4 | use ide_db::{base_db::FileId, source_change::SourceFileEdits}; |
5 | use ide_db::source_change::SourceFileEdit; | ||
6 | use syntax::{ast, match_ast, AstNode, SyntaxNode}; | 5 | use syntax::{ast, match_ast, AstNode, SyntaxNode}; |
7 | use text_edit::TextEdit; | 6 | use text_edit::TextEdit; |
8 | 7 | ||
@@ -50,7 +49,7 @@ fn check_expr_field_shorthand( | |||
50 | Diagnostic::hint(field_range, "Shorthand struct initialization".to_string()).with_fix( | 49 | Diagnostic::hint(field_range, "Shorthand struct initialization".to_string()).with_fix( |
51 | Some(Fix::new( | 50 | Some(Fix::new( |
52 | "Use struct shorthand initialization", | 51 | "Use struct shorthand initialization", |
53 | SourceFileEdit { file_id, edit }.into(), | 52 | SourceFileEdits::from_text_edit(file_id, edit).into(), |
54 | field_range, | 53 | field_range, |
55 | )), | 54 | )), |
56 | ), | 55 | ), |
@@ -89,7 +88,7 @@ fn check_pat_field_shorthand( | |||
89 | acc.push(Diagnostic::hint(field_range, "Shorthand struct pattern".to_string()).with_fix( | 88 | acc.push(Diagnostic::hint(field_range, "Shorthand struct pattern".to_string()).with_fix( |
90 | Some(Fix::new( | 89 | Some(Fix::new( |
91 | "Use struct field shorthand", | 90 | "Use struct field shorthand", |
92 | SourceFileEdit { file_id, edit }.into(), | 91 | SourceFileEdits::from_text_edit(file_id, edit).into(), |
93 | field_range, | 92 | field_range, |
94 | )), | 93 | )), |
95 | )); | 94 | )); |
diff --git a/crates/ide/src/diagnostics/fixes.rs b/crates/ide/src/diagnostics/fixes.rs index d7ad88ed5..b04964ccd 100644 --- a/crates/ide/src/diagnostics/fixes.rs +++ b/crates/ide/src/diagnostics/fixes.rs | |||
@@ -8,9 +8,9 @@ use hir::{ | |||
8 | }, | 8 | }, |
9 | HasSource, HirDisplay, InFile, Semantics, VariantDef, | 9 | HasSource, HirDisplay, InFile, Semantics, VariantDef, |
10 | }; | 10 | }; |
11 | use ide_db::base_db::{AnchoredPathBuf, FileId}; | ||
12 | use ide_db::{ | 11 | use ide_db::{ |
13 | source_change::{FileSystemEdit, SourceFileEdit}, | 12 | base_db::{AnchoredPathBuf, FileId}, |
13 | source_change::{FileSystemEdit, SourceFileEdits}, | ||
14 | RootDatabase, | 14 | RootDatabase, |
15 | }; | 15 | }; |
16 | use syntax::{ | 16 | use syntax::{ |
@@ -88,7 +88,7 @@ impl DiagnosticWithFix for MissingFields { | |||
88 | }; | 88 | }; |
89 | Some(Fix::new( | 89 | Some(Fix::new( |
90 | "Fill struct fields", | 90 | "Fill struct fields", |
91 | SourceFileEdit { file_id: self.file.original_file(sema.db), edit }.into(), | 91 | SourceFileEdits::from_text_edit(self.file.original_file(sema.db), edit).into(), |
92 | sema.original_range(&field_list_parent.syntax()).range, | 92 | sema.original_range(&field_list_parent.syntax()).range, |
93 | )) | 93 | )) |
94 | } | 94 | } |
@@ -102,7 +102,7 @@ impl DiagnosticWithFix for MissingOkOrSomeInTailExpr { | |||
102 | let replacement = format!("{}({})", self.required, tail_expr.syntax()); | 102 | let replacement = format!("{}({})", self.required, tail_expr.syntax()); |
103 | let edit = TextEdit::replace(tail_expr_range, replacement); | 103 | let edit = TextEdit::replace(tail_expr_range, replacement); |
104 | let source_change = | 104 | let source_change = |
105 | SourceFileEdit { file_id: self.file.original_file(sema.db), edit }.into(); | 105 | SourceFileEdits::from_text_edit(self.file.original_file(sema.db), edit).into(); |
106 | let name = if self.required == "Ok" { "Wrap with Ok" } else { "Wrap with Some" }; | 106 | let name = if self.required == "Ok" { "Wrap with Ok" } else { "Wrap with Some" }; |
107 | Some(Fix::new(name, source_change, tail_expr_range)) | 107 | Some(Fix::new(name, source_change, tail_expr_range)) |
108 | } | 108 | } |
@@ -123,7 +123,7 @@ impl DiagnosticWithFix for RemoveThisSemicolon { | |||
123 | 123 | ||
124 | let edit = TextEdit::delete(semicolon); | 124 | let edit = TextEdit::delete(semicolon); |
125 | let source_change = | 125 | let source_change = |
126 | SourceFileEdit { file_id: self.file.original_file(sema.db), edit }.into(); | 126 | SourceFileEdits::from_text_edit(self.file.original_file(sema.db), edit).into(); |
127 | 127 | ||
128 | Some(Fix::new("Remove this semicolon", source_change, semicolon)) | 128 | Some(Fix::new("Remove this semicolon", source_change, semicolon)) |
129 | } | 129 | } |
@@ -204,10 +204,10 @@ fn missing_record_expr_field_fix( | |||
204 | new_field = format!(",{}", new_field); | 204 | new_field = format!(",{}", new_field); |
205 | } | 205 | } |
206 | 206 | ||
207 | let source_change = SourceFileEdit { | 207 | let source_change = SourceFileEdits::from_text_edit( |
208 | file_id: def_file_id, | 208 | def_file_id, |
209 | edit: TextEdit::insert(last_field_syntax.text_range().end(), new_field), | 209 | TextEdit::insert(last_field_syntax.text_range().end(), new_field), |
210 | }; | 210 | ); |
211 | return Some(Fix::new( | 211 | return Some(Fix::new( |
212 | "Create field", | 212 | "Create field", |
213 | source_change.into(), | 213 | source_change.into(), |
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 1e03832ec..110920e58 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs | |||
@@ -98,7 +98,7 @@ pub use ide_db::{ | |||
98 | label::Label, | 98 | label::Label, |
99 | line_index::{LineCol, LineIndex}, | 99 | line_index::{LineCol, LineIndex}, |
100 | search::SearchScope, | 100 | search::SearchScope, |
101 | source_change::{FileSystemEdit, SourceChange, SourceFileEdit}, | 101 | source_change::{FileSystemEdit, SourceChange, SourceFileEdits}, |
102 | symbol_index::Query, | 102 | symbol_index::Query, |
103 | RootDatabase, | 103 | RootDatabase, |
104 | }; | 104 | }; |
@@ -553,7 +553,7 @@ impl Analysis { | |||
553 | let rule: ssr::SsrRule = query.parse()?; | 553 | let rule: ssr::SsrRule = query.parse()?; |
554 | let mut match_finder = ssr::MatchFinder::in_context(db, resolve_context, selections); | 554 | let mut match_finder = ssr::MatchFinder::in_context(db, resolve_context, selections); |
555 | match_finder.add_rule(rule)?; | 555 | match_finder.add_rule(rule)?; |
556 | let edits = if parse_only { Vec::new() } else { match_finder.edits() }; | 556 | let edits = if parse_only { Default::default() } else { match_finder.edits() }; |
557 | Ok(SourceChange::from(edits)) | 557 | Ok(SourceChange::from(edits)) |
558 | }) | 558 | }) |
559 | } | 559 | } |
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs index c3ae568c2..cd9c7c7e5 100644 --- a/crates/ide/src/references/rename.rs +++ b/crates/ide/src/references/rename.rs | |||
@@ -21,7 +21,7 @@ use text_edit::TextEdit; | |||
21 | 21 | ||
22 | use crate::{ | 22 | use crate::{ |
23 | FilePosition, FileSystemEdit, RangeInfo, ReferenceKind, ReferenceSearchResult, SourceChange, | 23 | FilePosition, FileSystemEdit, RangeInfo, ReferenceKind, ReferenceSearchResult, SourceChange, |
24 | SourceFileEdit, TextRange, TextSize, | 24 | SourceFileEdits, TextRange, TextSize, |
25 | }; | 25 | }; |
26 | 26 | ||
27 | type RenameResult<T> = Result<T, RenameError>; | 27 | type RenameResult<T> = Result<T, RenameError>; |
@@ -58,7 +58,7 @@ pub(crate) fn prepare_rename( | |||
58 | rename_self_to_param(&sema, position, self_token, "dummy") | 58 | rename_self_to_param(&sema, position, self_token, "dummy") |
59 | } else { | 59 | } else { |
60 | let RangeInfo { range, .. } = find_all_refs(&sema, position)?; | 60 | let RangeInfo { range, .. } = find_all_refs(&sema, position)?; |
61 | Ok(RangeInfo::new(range, SourceChange::from(vec![]))) | 61 | Ok(RangeInfo::new(range, SourceChange::default())) |
62 | } | 62 | } |
63 | .map(|info| RangeInfo::new(info.range, ())) | 63 | .map(|info| RangeInfo::new(info.range, ())) |
64 | } | 64 | } |
@@ -176,7 +176,7 @@ fn source_edit_from_references( | |||
176 | file_id: FileId, | 176 | file_id: FileId, |
177 | references: &[FileReference], | 177 | references: &[FileReference], |
178 | new_name: &str, | 178 | new_name: &str, |
179 | ) -> SourceFileEdit { | 179 | ) -> (FileId, TextEdit) { |
180 | let mut edit = TextEdit::builder(); | 180 | let mut edit = TextEdit::builder(); |
181 | for reference in references { | 181 | for reference in references { |
182 | let mut replacement_text = String::new(); | 182 | let mut replacement_text = String::new(); |
@@ -209,8 +209,7 @@ fn source_edit_from_references( | |||
209 | }; | 209 | }; |
210 | edit.replace(range, replacement_text); | 210 | edit.replace(range, replacement_text); |
211 | } | 211 | } |
212 | 212 | (file_id, edit.finish()) | |
213 | SourceFileEdit { file_id, edit: edit.finish() } | ||
214 | } | 213 | } |
215 | 214 | ||
216 | fn edit_text_range_for_record_field_expr_or_pat( | 215 | fn edit_text_range_for_record_field_expr_or_pat( |
@@ -250,7 +249,7 @@ fn rename_mod( | |||
250 | if IdentifierKind::Ident != check_identifier(new_name)? { | 249 | if IdentifierKind::Ident != check_identifier(new_name)? { |
251 | bail!("Invalid name `{0}`: cannot rename module to {0}", new_name); | 250 | bail!("Invalid name `{0}`: cannot rename module to {0}", new_name); |
252 | } | 251 | } |
253 | let mut source_file_edits = Vec::new(); | 252 | let mut source_file_edits = SourceFileEdits::default(); |
254 | let mut file_system_edits = Vec::new(); | 253 | let mut file_system_edits = Vec::new(); |
255 | 254 | ||
256 | let src = module.definition_source(sema.db); | 255 | let src = module.definition_source(sema.db); |
@@ -273,11 +272,8 @@ fn rename_mod( | |||
273 | if let Some(src) = module.declaration_source(sema.db) { | 272 | if let Some(src) = module.declaration_source(sema.db) { |
274 | let file_id = src.file_id.original_file(sema.db); | 273 | let file_id = src.file_id.original_file(sema.db); |
275 | let name = src.value.name().unwrap(); | 274 | let name = src.value.name().unwrap(); |
276 | let edit = SourceFileEdit { | 275 | source_file_edits |
277 | file_id, | 276 | .insert(file_id, TextEdit::replace(name.syntax().text_range(), new_name.into())); |
278 | edit: TextEdit::replace(name.syntax().text_range(), new_name.into()), | ||
279 | }; | ||
280 | source_file_edits.push(edit); | ||
281 | } | 277 | } |
282 | 278 | ||
283 | let RangeInfo { range, info: refs } = find_all_refs(sema, position)?; | 279 | let RangeInfo { range, info: refs } = find_all_refs(sema, position)?; |
@@ -335,20 +331,13 @@ fn rename_to_self( | |||
335 | 331 | ||
336 | let RangeInfo { range, info: refs } = find_all_refs(sema, position)?; | 332 | let RangeInfo { range, info: refs } = find_all_refs(sema, position)?; |
337 | 333 | ||
338 | let mut edits = refs | 334 | let mut edits = SourceFileEdits::default(); |
339 | .references() | 335 | edits.extend(refs.references().iter().map(|(&file_id, references)| { |
340 | .iter() | 336 | source_edit_from_references(sema, file_id, references, "self") |
341 | .map(|(&file_id, references)| { | 337 | })); |
342 | source_edit_from_references(sema, file_id, references, "self") | 338 | edits.insert(position.file_id, TextEdit::replace(param_range, String::from(self_param))); |
343 | }) | ||
344 | .collect::<Vec<_>>(); | ||
345 | 339 | ||
346 | edits.push(SourceFileEdit { | 340 | Ok(RangeInfo::new(range, edits.into())) |
347 | file_id: position.file_id, | ||
348 | edit: TextEdit::replace(param_range, String::from(self_param)), | ||
349 | }); | ||
350 | |||
351 | Ok(RangeInfo::new(range, SourceChange::from(edits))) | ||
352 | } | 341 | } |
353 | 342 | ||
354 | fn text_edit_from_self_param( | 343 | fn text_edit_from_self_param( |
@@ -402,7 +391,7 @@ fn rename_self_to_param( | |||
402 | .ok_or_else(|| format_err!("No surrounding method declaration found"))?; | 391 | .ok_or_else(|| format_err!("No surrounding method declaration found"))?; |
403 | let search_range = fn_def.syntax().text_range(); | 392 | let search_range = fn_def.syntax().text_range(); |
404 | 393 | ||
405 | let mut edits: Vec<SourceFileEdit> = vec![]; | 394 | let mut edits = SourceFileEdits::default(); |
406 | 395 | ||
407 | for (idx, _) in text.match_indices("self") { | 396 | for (idx, _) in text.match_indices("self") { |
408 | let offset: TextSize = idx.try_into().unwrap(); | 397 | let offset: TextSize = idx.try_into().unwrap(); |
@@ -416,7 +405,7 @@ fn rename_self_to_param( | |||
416 | } else { | 405 | } else { |
417 | TextEdit::replace(usage.text_range(), String::from(new_name)) | 406 | TextEdit::replace(usage.text_range(), String::from(new_name)) |
418 | }; | 407 | }; |
419 | edits.push(SourceFileEdit { file_id: position.file_id, edit }); | 408 | edits.insert(position.file_id, edit); |
420 | } | 409 | } |
421 | } | 410 | } |
422 | 411 | ||
@@ -427,7 +416,7 @@ fn rename_self_to_param( | |||
427 | let range = ast::SelfParam::cast(self_token.parent()) | 416 | let range = ast::SelfParam::cast(self_token.parent()) |
428 | .map_or(self_token.text_range(), |p| p.syntax().text_range()); | 417 | .map_or(self_token.text_range(), |p| p.syntax().text_range()); |
429 | 418 | ||
430 | Ok(RangeInfo::new(range, SourceChange::from(edits))) | 419 | Ok(RangeInfo::new(range, edits.into())) |
431 | } | 420 | } |
432 | 421 | ||
433 | fn rename_reference( | 422 | fn rename_reference( |
@@ -464,14 +453,12 @@ fn rename_reference( | |||
464 | (IdentifierKind::Ident, _) | (IdentifierKind::Underscore, _) => mark::hit!(rename_ident), | 453 | (IdentifierKind::Ident, _) | (IdentifierKind::Underscore, _) => mark::hit!(rename_ident), |
465 | } | 454 | } |
466 | 455 | ||
467 | let edit = refs | 456 | let mut edits = SourceFileEdits::default(); |
468 | .into_iter() | 457 | edits.extend(refs.into_iter().map(|(file_id, references)| { |
469 | .map(|(file_id, references)| { | 458 | source_edit_from_references(sema, file_id, &references, new_name) |
470 | source_edit_from_references(sema, file_id, &references, new_name) | 459 | })); |
471 | }) | ||
472 | .collect::<Vec<_>>(); | ||
473 | 460 | ||
474 | Ok(RangeInfo::new(range, SourceChange::from(edit))) | 461 | Ok(RangeInfo::new(range, edits.into())) |
475 | } | 462 | } |
476 | 463 | ||
477 | #[cfg(test)] | 464 | #[cfg(test)] |
@@ -493,9 +480,9 @@ mod tests { | |||
493 | Ok(source_change) => { | 480 | Ok(source_change) => { |
494 | let mut text_edit_builder = TextEdit::builder(); | 481 | let mut text_edit_builder = TextEdit::builder(); |
495 | let mut file_id: Option<FileId> = None; | 482 | let mut file_id: Option<FileId> = None; |
496 | for edit in source_change.info.source_file_edits { | 483 | for edit in source_change.info.source_file_edits.edits { |
497 | file_id = Some(edit.file_id); | 484 | file_id = Some(edit.0); |
498 | for indel in edit.edit.into_iter() { | 485 | for indel in edit.1.into_iter() { |
499 | text_edit_builder.replace(indel.delete, indel.insert); | 486 | text_edit_builder.replace(indel.delete, indel.insert); |
500 | } | 487 | } |
501 | } | 488 | } |
@@ -895,12 +882,11 @@ mod foo$0; | |||
895 | RangeInfo { | 882 | RangeInfo { |
896 | range: 4..7, | 883 | range: 4..7, |
897 | info: SourceChange { | 884 | info: SourceChange { |
898 | source_file_edits: [ | 885 | source_file_edits: SourceFileEdits { |
899 | SourceFileEdit { | 886 | edits: { |
900 | file_id: FileId( | 887 | FileId( |
901 | 1, | 888 | 1, |
902 | ), | 889 | ): TextEdit { |
903 | edit: TextEdit { | ||
904 | indels: [ | 890 | indels: [ |
905 | Indel { | 891 | Indel { |
906 | insert: "foo2", | 892 | insert: "foo2", |
@@ -909,7 +895,7 @@ mod foo$0; | |||
909 | ], | 895 | ], |
910 | }, | 896 | }, |
911 | }, | 897 | }, |
912 | ], | 898 | }, |
913 | file_system_edits: [ | 899 | file_system_edits: [ |
914 | MoveFile { | 900 | MoveFile { |
915 | src: FileId( | 901 | src: FileId( |
@@ -950,12 +936,11 @@ use crate::foo$0::FooContent; | |||
950 | RangeInfo { | 936 | RangeInfo { |
951 | range: 11..14, | 937 | range: 11..14, |
952 | info: SourceChange { | 938 | info: SourceChange { |
953 | source_file_edits: [ | 939 | source_file_edits: SourceFileEdits { |
954 | SourceFileEdit { | 940 | edits: { |
955 | file_id: FileId( | 941 | FileId( |
956 | 0, | 942 | 0, |
957 | ), | 943 | ): TextEdit { |
958 | edit: TextEdit { | ||
959 | indels: [ | 944 | indels: [ |
960 | Indel { | 945 | Indel { |
961 | insert: "quux", | 946 | insert: "quux", |
@@ -963,12 +948,9 @@ use crate::foo$0::FooContent; | |||
963 | }, | 948 | }, |
964 | ], | 949 | ], |
965 | }, | 950 | }, |
966 | }, | 951 | FileId( |
967 | SourceFileEdit { | ||
968 | file_id: FileId( | ||
969 | 2, | 952 | 2, |
970 | ), | 953 | ): TextEdit { |
971 | edit: TextEdit { | ||
972 | indels: [ | 954 | indels: [ |
973 | Indel { | 955 | Indel { |
974 | insert: "quux", | 956 | insert: "quux", |
@@ -977,7 +959,7 @@ use crate::foo$0::FooContent; | |||
977 | ], | 959 | ], |
978 | }, | 960 | }, |
979 | }, | 961 | }, |
980 | ], | 962 | }, |
981 | file_system_edits: [ | 963 | file_system_edits: [ |
982 | MoveFile { | 964 | MoveFile { |
983 | src: FileId( | 965 | src: FileId( |
@@ -1012,12 +994,11 @@ mod fo$0o; | |||
1012 | RangeInfo { | 994 | RangeInfo { |
1013 | range: 4..7, | 995 | range: 4..7, |
1014 | info: SourceChange { | 996 | info: SourceChange { |
1015 | source_file_edits: [ | 997 | source_file_edits: SourceFileEdits { |
1016 | SourceFileEdit { | 998 | edits: { |
1017 | file_id: FileId( | 999 | FileId( |
1018 | 0, | 1000 | 0, |
1019 | ), | 1001 | ): TextEdit { |
1020 | edit: TextEdit { | ||
1021 | indels: [ | 1002 | indels: [ |
1022 | Indel { | 1003 | Indel { |
1023 | insert: "foo2", | 1004 | insert: "foo2", |
@@ -1026,7 +1007,7 @@ mod fo$0o; | |||
1026 | ], | 1007 | ], |
1027 | }, | 1008 | }, |
1028 | }, | 1009 | }, |
1029 | ], | 1010 | }, |
1030 | file_system_edits: [ | 1011 | file_system_edits: [ |
1031 | MoveFile { | 1012 | MoveFile { |
1032 | src: FileId( | 1013 | src: FileId( |
@@ -1062,12 +1043,11 @@ mod outer { mod fo$0o; } | |||
1062 | RangeInfo { | 1043 | RangeInfo { |
1063 | range: 16..19, | 1044 | range: 16..19, |
1064 | info: SourceChange { | 1045 | info: SourceChange { |
1065 | source_file_edits: [ | 1046 | source_file_edits: SourceFileEdits { |
1066 | SourceFileEdit { | 1047 | edits: { |
1067 | file_id: FileId( | 1048 | FileId( |
1068 | 0, | 1049 | 0, |
1069 | ), | 1050 | ): TextEdit { |
1070 | edit: TextEdit { | ||
1071 | indels: [ | 1051 | indels: [ |
1072 | Indel { | 1052 | Indel { |
1073 | insert: "bar", | 1053 | insert: "bar", |
@@ -1076,7 +1056,7 @@ mod outer { mod fo$0o; } | |||
1076 | ], | 1056 | ], |
1077 | }, | 1057 | }, |
1078 | }, | 1058 | }, |
1079 | ], | 1059 | }, |
1080 | file_system_edits: [ | 1060 | file_system_edits: [ |
1081 | MoveFile { | 1061 | MoveFile { |
1082 | src: FileId( | 1062 | src: FileId( |
@@ -1135,34 +1115,30 @@ pub mod foo$0; | |||
1135 | RangeInfo { | 1115 | RangeInfo { |
1136 | range: 8..11, | 1116 | range: 8..11, |
1137 | info: SourceChange { | 1117 | info: SourceChange { |
1138 | source_file_edits: [ | 1118 | source_file_edits: SourceFileEdits { |
1139 | SourceFileEdit { | 1119 | edits: { |
1140 | file_id: FileId( | 1120 | FileId( |
1141 | 1, | 1121 | 0, |
1142 | ), | 1122 | ): TextEdit { |
1143 | edit: TextEdit { | ||
1144 | indels: [ | 1123 | indels: [ |
1145 | Indel { | 1124 | Indel { |
1146 | insert: "foo2", | 1125 | insert: "foo2", |
1147 | delete: 8..11, | 1126 | delete: 27..30, |
1148 | }, | 1127 | }, |
1149 | ], | 1128 | ], |
1150 | }, | 1129 | }, |
1151 | }, | 1130 | FileId( |
1152 | SourceFileEdit { | 1131 | 1, |
1153 | file_id: FileId( | 1132 | ): TextEdit { |
1154 | 0, | ||
1155 | ), | ||
1156 | edit: TextEdit { | ||
1157 | indels: [ | 1133 | indels: [ |
1158 | Indel { | 1134 | Indel { |
1159 | insert: "foo2", | 1135 | insert: "foo2", |
1160 | delete: 27..30, | 1136 | delete: 8..11, |
1161 | }, | 1137 | }, |
1162 | ], | 1138 | ], |
1163 | }, | 1139 | }, |
1164 | }, | 1140 | }, |
1165 | ], | 1141 | }, |
1166 | file_system_edits: [ | 1142 | file_system_edits: [ |
1167 | MoveFile { | 1143 | MoveFile { |
1168 | src: FileId( | 1144 | src: FileId( |
diff --git a/crates/ide/src/typing.rs b/crates/ide/src/typing.rs index 88c905003..b3fc32645 100644 --- a/crates/ide/src/typing.rs +++ b/crates/ide/src/typing.rs | |||
@@ -15,8 +15,11 @@ | |||
15 | 15 | ||
16 | mod on_enter; | 16 | mod on_enter; |
17 | 17 | ||
18 | use ide_db::base_db::{FilePosition, SourceDatabase}; | 18 | use ide_db::{ |
19 | use ide_db::{source_change::SourceFileEdit, RootDatabase}; | 19 | base_db::{FilePosition, SourceDatabase}, |
20 | source_change::SourceFileEdits, | ||
21 | RootDatabase, | ||
22 | }; | ||
20 | use syntax::{ | 23 | use syntax::{ |
21 | algo::find_node_at_offset, | 24 | algo::find_node_at_offset, |
22 | ast::{self, edit::IndentLevel, AstToken}, | 25 | ast::{self, edit::IndentLevel, AstToken}, |
@@ -56,7 +59,7 @@ pub(crate) fn on_char_typed( | |||
56 | let file = &db.parse(position.file_id).tree(); | 59 | let file = &db.parse(position.file_id).tree(); |
57 | assert_eq!(file.syntax().text().char_at(position.offset), Some(char_typed)); | 60 | assert_eq!(file.syntax().text().char_at(position.offset), Some(char_typed)); |
58 | let edit = on_char_typed_inner(file, position.offset, char_typed)?; | 61 | let edit = on_char_typed_inner(file, position.offset, char_typed)?; |
59 | Some(SourceFileEdit { file_id: position.file_id, edit }.into()) | 62 | Some(SourceFileEdits::from_text_edit(position.file_id, edit).into()) |
60 | } | 63 | } |
61 | 64 | ||
62 | fn on_char_typed_inner(file: &SourceFile, offset: TextSize, char_typed: char) -> Option<TextEdit> { | 65 | fn on_char_typed_inner(file: &SourceFile, offset: TextSize, char_typed: char) -> Option<TextEdit> { |
diff --git a/crates/ide_db/src/source_change.rs b/crates/ide_db/src/source_change.rs index 10c0abdac..516d7859a 100644 --- a/crates/ide_db/src/source_change.rs +++ b/crates/ide_db/src/source_change.rs | |||
@@ -3,12 +3,18 @@ | |||
3 | //! | 3 | //! |
4 | //! It can be viewed as a dual for `AnalysisChange`. | 4 | //! It can be viewed as a dual for `AnalysisChange`. |
5 | 5 | ||
6 | use std::{ | ||
7 | collections::hash_map::Entry, | ||
8 | iter::{self, FromIterator}, | ||
9 | }; | ||
10 | |||
6 | use base_db::{AnchoredPathBuf, FileId}; | 11 | use base_db::{AnchoredPathBuf, FileId}; |
12 | use rustc_hash::FxHashMap; | ||
7 | use text_edit::TextEdit; | 13 | use text_edit::TextEdit; |
8 | 14 | ||
9 | #[derive(Default, Debug, Clone)] | 15 | #[derive(Default, Debug, Clone)] |
10 | pub struct SourceChange { | 16 | pub struct SourceChange { |
11 | pub source_file_edits: Vec<SourceFileEdit>, | 17 | pub source_file_edits: SourceFileEdits, |
12 | pub file_system_edits: Vec<FileSystemEdit>, | 18 | pub file_system_edits: Vec<FileSystemEdit>, |
13 | pub is_snippet: bool, | 19 | pub is_snippet: bool, |
14 | } | 20 | } |
@@ -17,27 +23,51 @@ impl SourceChange { | |||
17 | /// Creates a new SourceChange with the given label | 23 | /// Creates a new SourceChange with the given label |
18 | /// from the edits. | 24 | /// from the edits. |
19 | pub fn from_edits( | 25 | pub fn from_edits( |
20 | source_file_edits: Vec<SourceFileEdit>, | 26 | source_file_edits: SourceFileEdits, |
21 | file_system_edits: Vec<FileSystemEdit>, | 27 | file_system_edits: Vec<FileSystemEdit>, |
22 | ) -> Self { | 28 | ) -> Self { |
23 | SourceChange { source_file_edits, file_system_edits, is_snippet: false } | 29 | SourceChange { source_file_edits, file_system_edits, is_snippet: false } |
24 | } | 30 | } |
25 | } | 31 | } |
26 | 32 | ||
27 | #[derive(Debug, Clone)] | 33 | #[derive(Default, Debug, Clone)] |
28 | pub struct SourceFileEdit { | 34 | pub struct SourceFileEdits { |
29 | pub file_id: FileId, | 35 | pub edits: FxHashMap<FileId, TextEdit>, |
30 | pub edit: TextEdit, | 36 | } |
37 | |||
38 | impl SourceFileEdits { | ||
39 | pub fn from_text_edit(file_id: FileId, edit: TextEdit) -> Self { | ||
40 | SourceFileEdits { edits: FxHashMap::from_iter(iter::once((file_id, edit))) } | ||
41 | } | ||
42 | |||
43 | pub fn len(&self) -> usize { | ||
44 | self.edits.len() | ||
45 | } | ||
46 | |||
47 | pub fn is_empty(&self) -> bool { | ||
48 | self.edits.is_empty() | ||
49 | } | ||
50 | |||
51 | pub fn insert(&mut self, file_id: FileId, edit: TextEdit) { | ||
52 | match self.edits.entry(file_id) { | ||
53 | Entry::Occupied(mut entry) => { | ||
54 | entry.get_mut().union(edit).expect("overlapping edits for same file"); | ||
55 | } | ||
56 | Entry::Vacant(entry) => { | ||
57 | entry.insert(edit); | ||
58 | } | ||
59 | } | ||
60 | } | ||
31 | } | 61 | } |
32 | 62 | ||
33 | impl From<SourceFileEdit> for SourceChange { | 63 | impl Extend<(FileId, TextEdit)> for SourceFileEdits { |
34 | fn from(edit: SourceFileEdit) -> SourceChange { | 64 | fn extend<T: IntoIterator<Item = (FileId, TextEdit)>>(&mut self, iter: T) { |
35 | vec![edit].into() | 65 | iter.into_iter().for_each(|(file_id, edit)| self.insert(file_id, edit)); |
36 | } | 66 | } |
37 | } | 67 | } |
38 | 68 | ||
39 | impl From<Vec<SourceFileEdit>> for SourceChange { | 69 | impl From<SourceFileEdits> for SourceChange { |
40 | fn from(source_file_edits: Vec<SourceFileEdit>) -> SourceChange { | 70 | fn from(source_file_edits: SourceFileEdits) -> SourceChange { |
41 | SourceChange { source_file_edits, file_system_edits: Vec::new(), is_snippet: false } | 71 | SourceChange { source_file_edits, file_system_edits: Vec::new(), is_snippet: false } |
42 | } | 72 | } |
43 | } | 73 | } |
@@ -51,7 +81,7 @@ pub enum FileSystemEdit { | |||
51 | impl From<FileSystemEdit> for SourceChange { | 81 | impl From<FileSystemEdit> for SourceChange { |
52 | fn from(edit: FileSystemEdit) -> SourceChange { | 82 | fn from(edit: FileSystemEdit) -> SourceChange { |
53 | SourceChange { | 83 | SourceChange { |
54 | source_file_edits: Vec::new(), | 84 | source_file_edits: Default::default(), |
55 | file_system_edits: vec![edit], | 85 | file_system_edits: vec![edit], |
56 | is_snippet: false, | 86 | is_snippet: false, |
57 | } | 87 | } |
diff --git a/crates/rust-analyzer/src/cli/ssr.rs b/crates/rust-analyzer/src/cli/ssr.rs index a06631dac..100331c37 100644 --- a/crates/rust-analyzer/src/cli/ssr.rs +++ b/crates/rust-analyzer/src/cli/ssr.rs | |||
@@ -12,10 +12,10 @@ pub fn apply_ssr_rules(rules: Vec<SsrRule>) -> Result<()> { | |||
12 | match_finder.add_rule(rule)?; | 12 | match_finder.add_rule(rule)?; |
13 | } | 13 | } |
14 | let edits = match_finder.edits(); | 14 | let edits = match_finder.edits(); |
15 | for edit in edits { | 15 | for (file_id, edit) in edits.edits { |
16 | if let Some(path) = vfs.file_path(edit.file_id).as_path() { | 16 | if let Some(path) = vfs.file_path(file_id).as_path() { |
17 | let mut contents = db.file_text(edit.file_id).to_string(); | 17 | let mut contents = db.file_text(file_id).to_string(); |
18 | edit.edit.apply(&mut contents); | 18 | edit.apply(&mut contents); |
19 | std::fs::write(path, contents)?; | 19 | std::fs::write(path, contents)?; |
20 | } | 20 | } |
21 | } | 21 | } |
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index dc81f55d6..839466e4f 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs | |||
@@ -260,15 +260,15 @@ pub(crate) fn handle_on_type_formatting( | |||
260 | } | 260 | } |
261 | 261 | ||
262 | let edit = snap.analysis.on_char_typed(position, char_typed)?; | 262 | let edit = snap.analysis.on_char_typed(position, char_typed)?; |
263 | let mut edit = match edit { | 263 | let edit = match edit { |
264 | Some(it) => it, | 264 | Some(it) => it, |
265 | None => return Ok(None), | 265 | None => return Ok(None), |
266 | }; | 266 | }; |
267 | 267 | ||
268 | // This should be a single-file edit | 268 | // This should be a single-file edit |
269 | let edit = edit.source_file_edits.pop().unwrap(); | 269 | let (_, edit) = edit.source_file_edits.edits.into_iter().next().unwrap(); |
270 | 270 | ||
271 | let change = to_proto::text_edit_vec(&line_index, line_endings, edit.edit); | 271 | let change = to_proto::text_edit_vec(&line_index, line_endings, edit); |
272 | Ok(Some(change)) | 272 | Ok(Some(change)) |
273 | } | 273 | } |
274 | 274 | ||
@@ -463,8 +463,12 @@ pub(crate) fn handle_will_rename_files( | |||
463 | .collect(); | 463 | .collect(); |
464 | 464 | ||
465 | // Drop file system edits since we're just renaming things on the same level | 465 | // Drop file system edits since we're just renaming things on the same level |
466 | let edits = source_changes.into_iter().map(|it| it.source_file_edits).flatten().collect(); | 466 | let mut source_changes = source_changes.into_iter(); |
467 | let source_change = SourceChange::from_edits(edits, Vec::new()); | 467 | let mut source_file_edits = |
468 | source_changes.next().map_or_else(Default::default, |it| it.source_file_edits); | ||
469 | // no collect here because we want to merge text edits on same file ids | ||
470 | source_file_edits.extend(source_changes.map(|it| it.source_file_edits.edits).flatten()); | ||
471 | let source_change = SourceChange::from_edits(source_file_edits, Vec::new()); | ||
468 | 472 | ||
469 | let workspace_edit = to_proto::workspace_edit(&snap, source_change)?; | 473 | let workspace_edit = to_proto::workspace_edit(&snap, source_change)?; |
470 | Ok(Some(workspace_edit)) | 474 | Ok(Some(workspace_edit)) |
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index a7ff8975a..34f510e73 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs | |||
@@ -8,8 +8,7 @@ use ide::{ | |||
8 | Assist, AssistKind, CallInfo, CompletionItem, CompletionItemKind, Documentation, FileId, | 8 | Assist, AssistKind, CallInfo, CompletionItem, CompletionItemKind, Documentation, FileId, |
9 | FileRange, FileSystemEdit, Fold, FoldKind, Highlight, HlMod, HlPunct, HlRange, HlTag, Indel, | 9 | FileRange, FileSystemEdit, Fold, FoldKind, Highlight, HlMod, HlPunct, HlRange, HlTag, Indel, |
10 | InlayHint, InlayKind, InsertTextFormat, LineIndex, Markup, NavigationTarget, ReferenceAccess, | 10 | InlayHint, InlayKind, InsertTextFormat, LineIndex, Markup, NavigationTarget, ReferenceAccess, |
11 | RenameError, Runnable, Severity, SourceChange, SourceFileEdit, SymbolKind, TextEdit, TextRange, | 11 | RenameError, Runnable, Severity, SourceChange, SymbolKind, TextEdit, TextRange, TextSize, |
12 | TextSize, | ||
13 | }; | 12 | }; |
14 | use itertools::Itertools; | 13 | use itertools::Itertools; |
15 | 14 | ||
@@ -634,13 +633,13 @@ pub(crate) fn goto_definition_response( | |||
634 | pub(crate) fn snippet_text_document_edit( | 633 | pub(crate) fn snippet_text_document_edit( |
635 | snap: &GlobalStateSnapshot, | 634 | snap: &GlobalStateSnapshot, |
636 | is_snippet: bool, | 635 | is_snippet: bool, |
637 | source_file_edit: SourceFileEdit, | 636 | file_id: FileId, |
637 | edit: TextEdit, | ||
638 | ) -> Result<lsp_ext::SnippetTextDocumentEdit> { | 638 | ) -> Result<lsp_ext::SnippetTextDocumentEdit> { |
639 | let text_document = optional_versioned_text_document_identifier(snap, source_file_edit.file_id); | 639 | let text_document = optional_versioned_text_document_identifier(snap, file_id); |
640 | let line_index = snap.analysis.file_line_index(source_file_edit.file_id)?; | 640 | let line_index = snap.analysis.file_line_index(file_id)?; |
641 | let line_endings = snap.file_line_endings(source_file_edit.file_id); | 641 | let line_endings = snap.file_line_endings(file_id); |
642 | let edits = source_file_edit | 642 | let edits = edit |
643 | .edit | ||
644 | .into_iter() | 643 | .into_iter() |
645 | .map(|it| snippet_text_edit(&line_index, line_endings, is_snippet, it)) | 644 | .map(|it| snippet_text_edit(&line_index, line_endings, is_snippet, it)) |
646 | .collect(); | 645 | .collect(); |
@@ -699,8 +698,8 @@ pub(crate) fn snippet_workspace_edit( | |||
699 | let ops = snippet_text_document_ops(snap, op); | 698 | let ops = snippet_text_document_ops(snap, op); |
700 | document_changes.extend_from_slice(&ops); | 699 | document_changes.extend_from_slice(&ops); |
701 | } | 700 | } |
702 | for edit in source_change.source_file_edits { | 701 | for (file_id, edit) in source_change.source_file_edits.edits { |
703 | let edit = snippet_text_document_edit(&snap, source_change.is_snippet, edit)?; | 702 | let edit = snippet_text_document_edit(&snap, source_change.is_snippet, file_id, edit)?; |
704 | document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Edit(edit)); | 703 | document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Edit(edit)); |
705 | } | 704 | } |
706 | let workspace_edit = | 705 | let workspace_edit = |
diff --git a/crates/ssr/src/lib.rs b/crates/ssr/src/lib.rs index 747ce495d..d99ebefb1 100644 --- a/crates/ssr/src/lib.rs +++ b/crates/ssr/src/lib.rs | |||
@@ -74,8 +74,10 @@ pub use crate::errors::SsrError; | |||
74 | pub use crate::matching::Match; | 74 | pub use crate::matching::Match; |
75 | use crate::matching::MatchFailureReason; | 75 | use crate::matching::MatchFailureReason; |
76 | use hir::Semantics; | 76 | use hir::Semantics; |
77 | use ide_db::base_db::{FileId, FilePosition, FileRange}; | 77 | use ide_db::{ |
78 | use ide_db::source_change::SourceFileEdit; | 78 | base_db::{FileId, FilePosition, FileRange}, |
79 | source_change::SourceFileEdits, | ||
80 | }; | ||
79 | use resolving::ResolvedRule; | 81 | use resolving::ResolvedRule; |
80 | use rustc_hash::FxHashMap; | 82 | use rustc_hash::FxHashMap; |
81 | use syntax::{ast, AstNode, SyntaxNode, TextRange}; | 83 | use syntax::{ast, AstNode, SyntaxNode, TextRange}; |
@@ -159,7 +161,7 @@ impl<'db> MatchFinder<'db> { | |||
159 | } | 161 | } |
160 | 162 | ||
161 | /// Finds matches for all added rules and returns edits for all found matches. | 163 | /// Finds matches for all added rules and returns edits for all found matches. |
162 | pub fn edits(&self) -> Vec<SourceFileEdit> { | 164 | pub fn edits(&self) -> SourceFileEdits { |
163 | use ide_db::base_db::SourceDatabaseExt; | 165 | use ide_db::base_db::SourceDatabaseExt; |
164 | let mut matches_by_file = FxHashMap::default(); | 166 | let mut matches_by_file = FxHashMap::default(); |
165 | for m in self.matches().matches { | 167 | for m in self.matches().matches { |
@@ -169,13 +171,21 @@ impl<'db> MatchFinder<'db> { | |||
169 | .matches | 171 | .matches |
170 | .push(m); | 172 | .push(m); |
171 | } | 173 | } |
172 | let mut edits = vec![]; | 174 | SourceFileEdits { |
173 | for (file_id, matches) in matches_by_file { | 175 | edits: matches_by_file |
174 | let edit = | 176 | .into_iter() |
175 | replacing::matches_to_edit(&matches, &self.sema.db.file_text(file_id), &self.rules); | 177 | .map(|(file_id, matches)| { |
176 | edits.push(SourceFileEdit { file_id, edit }); | 178 | ( |
179 | file_id, | ||
180 | replacing::matches_to_edit( | ||
181 | &matches, | ||
182 | &self.sema.db.file_text(file_id), | ||
183 | &self.rules, | ||
184 | ), | ||
185 | ) | ||
186 | }) | ||
187 | .collect(), | ||
177 | } | 188 | } |
178 | edits | ||
179 | } | 189 | } |
180 | 190 | ||
181 | /// Adds a search pattern. For use if you intend to only call `find_matches_in_file`. If you | 191 | /// Adds a search pattern. For use if you intend to only call `find_matches_in_file`. If you |
diff --git a/crates/ssr/src/matching.rs b/crates/ssr/src/matching.rs index 6cf831431..5888bf8f8 100644 --- a/crates/ssr/src/matching.rs +++ b/crates/ssr/src/matching.rs | |||
@@ -810,9 +810,9 @@ mod tests { | |||
810 | 810 | ||
811 | let edits = match_finder.edits(); | 811 | let edits = match_finder.edits(); |
812 | assert_eq!(edits.len(), 1); | 812 | assert_eq!(edits.len(), 1); |
813 | let edit = &edits[0]; | 813 | let edit = &edits.edits[&position.file_id]; |
814 | let mut after = input.to_string(); | 814 | let mut after = input.to_string(); |
815 | edit.edit.apply(&mut after); | 815 | edit.apply(&mut after); |
816 | assert_eq!(after, "fn foo() {} fn bar() {} fn main() { bar(1+2); }"); | 816 | assert_eq!(after, "fn foo() {} fn bar() {} fn main() { bar(1+2); }"); |
817 | } | 817 | } |
818 | } | 818 | } |
diff --git a/crates/ssr/src/tests.rs b/crates/ssr/src/tests.rs index d6918c22d..8ba783526 100644 --- a/crates/ssr/src/tests.rs +++ b/crates/ssr/src/tests.rs | |||
@@ -103,11 +103,10 @@ fn assert_ssr_transforms(rules: &[&str], input: &str, expected: Expect) { | |||
103 | if edits.is_empty() { | 103 | if edits.is_empty() { |
104 | panic!("No edits were made"); | 104 | panic!("No edits were made"); |
105 | } | 105 | } |
106 | assert_eq!(edits[0].file_id, position.file_id); | ||
107 | // Note, db.file_text is not necessarily the same as `input`, since fixture parsing alters | 106 | // Note, db.file_text is not necessarily the same as `input`, since fixture parsing alters |
108 | // stuff. | 107 | // stuff. |
109 | let mut actual = db.file_text(position.file_id).to_string(); | 108 | let mut actual = db.file_text(position.file_id).to_string(); |
110 | edits[0].edit.apply(&mut actual); | 109 | edits.edits[&position.file_id].apply(&mut actual); |
111 | expected.assert_eq(&actual); | 110 | expected.assert_eq(&actual); |
112 | } | 111 | } |
113 | 112 | ||