|
1 | 1 | #[macro_use]
|
2 | 2 | extern crate serde_derive;
|
3 | 3 | extern crate serde_json;
|
| 4 | +#[macro_use] |
| 5 | +extern crate failure; |
| 6 | +#[cfg(test)] |
| 7 | +#[macro_use] |
| 8 | +extern crate proptest; |
4 | 9 |
|
5 | 10 | use std::collections::HashSet;
|
| 11 | +use std::ops::Range; |
| 12 | + |
| 13 | +use failure::Error; |
6 | 14 |
|
7 | 15 | pub mod diagnostics;
|
8 | 16 | use diagnostics::{Diagnostic, DiagnosticSpan};
|
| 17 | +mod replace; |
9 | 18 |
|
10 | 19 | pub fn get_suggestions_from_json<S: ::std::hash::BuildHasher>(
|
11 | 20 | input: &str,
|
@@ -61,6 +70,7 @@ pub struct Solution {
|
61 | 70 | pub struct Snippet {
|
62 | 71 | pub file_name: String,
|
63 | 72 | pub line_range: LineRange,
|
| 73 | + pub range: Range<usize>, |
64 | 74 | /// leading surrounding text, text to replace, trailing surrounding text
|
65 | 75 | ///
|
66 | 76 | /// This split is useful for higlighting the part that gets replaced
|
@@ -109,6 +119,7 @@ fn parse_snippet(span: &DiagnosticSpan) -> Snippet {
|
109 | 119 | column: span.column_end,
|
110 | 120 | },
|
111 | 121 | },
|
| 122 | + range: (span.byte_start as usize)..(span.byte_end as usize), |
112 | 123 | text: (lead, body, tail),
|
113 | 124 | }
|
114 | 125 | }
|
@@ -166,58 +177,22 @@ pub fn collect_suggestions<S: ::std::hash::BuildHasher>(diagnostic: &Diagnostic,
|
166 | 177 | }
|
167 | 178 | }
|
168 | 179 |
|
169 |
| -pub fn apply_suggestion(file_content: &mut String, suggestion: &Replacement) -> String { |
170 |
| - use std::cmp::max; |
171 |
| - |
172 |
| - let mut new_content = String::new(); |
173 |
| - |
174 |
| - // Add the lines before the section we want to replace |
175 |
| - new_content.push_str(&file_content.lines() |
176 |
| - .take(max(suggestion.snippet.line_range.start.line - 1, 0) as usize) |
177 |
| - .collect::<Vec<_>>() |
178 |
| - .join("\n")); |
179 |
| - new_content.push_str("\n"); |
180 |
| - |
181 |
| - // Parts of line before replacement |
182 |
| - new_content.push_str(&file_content.lines() |
183 |
| - .nth(suggestion.snippet.line_range.start.line - 1) |
184 |
| - .unwrap_or("") |
185 |
| - .chars() |
186 |
| - .take(suggestion.snippet.line_range.start.column - 1) |
187 |
| - .collect::<String>()); |
188 |
| - |
189 |
| - // Insert new content! Finally! |
190 |
| - new_content.push_str(&suggestion.replacement); |
191 |
| - |
192 |
| - // Parts of line after replacement |
193 |
| - new_content.push_str(&file_content.lines() |
194 |
| - .nth(suggestion.snippet.line_range.end.line - 1) |
195 |
| - .unwrap_or("") |
196 |
| - .chars() |
197 |
| - .skip(suggestion.snippet.line_range.end.column - 1) |
198 |
| - .collect::<String>()); |
199 |
| - |
200 |
| - // Add the lines after the section we want to replace |
201 |
| - new_content.push_str("\n"); |
202 |
| - new_content.push_str(&file_content.lines() |
203 |
| - .skip(suggestion.snippet.line_range.end.line as usize) |
204 |
| - .collect::<Vec<_>>() |
205 |
| - .join("\n")); |
206 |
| - new_content.push_str("\n"); |
207 |
| - |
208 |
| - new_content |
209 |
| -} |
| 180 | +pub fn apply_suggestions(code: &str, suggestions: &[Suggestion]) -> Result<String, Error> { |
| 181 | + use replace::Data; |
210 | 182 |
|
211 |
| -pub fn apply_suggestions(code: &str, suggestions: &[Suggestion]) -> String { |
212 |
| - let mut fixed = code.to_string(); |
| 183 | + let mut fixed = Data::new(code.as_bytes()); |
213 | 184 |
|
214 | 185 | for sug in suggestions.iter().rev() {
|
215 | 186 | for sol in &sug.solutions {
|
216 | 187 | for r in &sol.replacements {
|
217 |
| - fixed = apply_suggestion(&mut fixed, r); |
| 188 | + fixed.replace_range( |
| 189 | + r.snippet.range.start, |
| 190 | + r.snippet.range.end.saturating_sub(1), |
| 191 | + r.replacement.as_bytes(), |
| 192 | + )?; |
218 | 193 | }
|
219 | 194 | }
|
220 | 195 | }
|
221 | 196 |
|
222 |
| - fixed |
| 197 | + Ok(String::from_utf8(fixed.to_vec())?) |
223 | 198 | }
|
0 commit comments