@@ -224,6 +224,13 @@ pub struct SubstitutionPart {
224224 pub snippet : String ,
225225}
226226
227+ #[ derive( Clone , Debug , PartialEq , Hash , Encodable , Decodable ) ]
228+ pub struct TrimmedSubstitutionPart {
229+ pub original_span : Span ,
230+ pub span : Span ,
231+ pub snippet : String ,
232+ }
233+
227234/// Used to translate between `Span`s and byte positions within a single output line in highlighted
228235/// code of structured suggestions.
229236#[ derive( Debug , Clone , Copy ) ]
@@ -233,6 +240,35 @@ pub(crate) struct SubstitutionHighlight {
233240}
234241
235242impl SubstitutionPart {
243+ /// Try to turn a replacement into an addition when the span that is being
244+ /// overwritten matches either the prefix or suffix of the replacement.
245+ fn trim_trivial_replacements ( self , sm : & SourceMap ) -> TrimmedSubstitutionPart {
246+ let mut trimmed_part = TrimmedSubstitutionPart {
247+ original_span : self . span ,
248+ span : self . span ,
249+ snippet : self . snippet ,
250+ } ;
251+ if trimmed_part. snippet . is_empty ( ) {
252+ return trimmed_part;
253+ }
254+ let Ok ( snippet) = sm. span_to_snippet ( trimmed_part. span ) else {
255+ return trimmed_part;
256+ } ;
257+
258+ if let Some ( ( prefix, substr, suffix) ) = as_substr ( & snippet, & trimmed_part. snippet ) {
259+ trimmed_part. span = Span :: new (
260+ trimmed_part. span . lo ( ) + BytePos ( prefix as u32 ) ,
261+ trimmed_part. span . hi ( ) - BytePos ( suffix as u32 ) ,
262+ trimmed_part. span . ctxt ( ) ,
263+ trimmed_part. span . parent ( ) ,
264+ ) ;
265+ trimmed_part. snippet = substr. to_string ( ) ;
266+ }
267+ trimmed_part
268+ }
269+ }
270+
271+ impl TrimmedSubstitutionPart {
236272 pub fn is_addition ( & self , sm : & SourceMap ) -> bool {
237273 !self . snippet . is_empty ( ) && !self . replaces_meaningful_content ( sm)
238274 }
@@ -260,27 +296,6 @@ impl SubstitutionPart {
260296 sm. span_to_snippet ( self . span )
261297 . map_or ( !self . span . is_empty ( ) , |snippet| !snippet. trim ( ) . is_empty ( ) )
262298 }
263-
264- /// Try to turn a replacement into an addition when the span that is being
265- /// overwritten matches either the prefix or suffix of the replacement.
266- fn trim_trivial_replacements ( & mut self , sm : & SourceMap ) {
267- if self . snippet . is_empty ( ) {
268- return ;
269- }
270- let Ok ( snippet) = sm. span_to_snippet ( self . span ) else {
271- return ;
272- } ;
273-
274- if let Some ( ( prefix, substr, suffix) ) = as_substr ( & snippet, & self . snippet ) {
275- self . span = Span :: new (
276- self . span . lo ( ) + BytePos ( prefix as u32 ) ,
277- self . span . hi ( ) - BytePos ( suffix as u32 ) ,
278- self . span . ctxt ( ) ,
279- self . span . parent ( ) ,
280- ) ;
281- self . snippet = substr. to_string ( ) ;
282- }
283- }
284299}
285300
286301/// Given an original string like `AACC`, and a suggestion like `AABBCC`, try to detect
@@ -310,7 +325,8 @@ impl CodeSuggestion {
310325 pub ( crate ) fn splice_lines (
311326 & self ,
312327 sm : & SourceMap ,
313- ) -> Vec < ( String , Vec < SubstitutionPart > , Vec < Vec < SubstitutionHighlight > > , ConfusionType ) > {
328+ ) -> Vec < ( String , Vec < TrimmedSubstitutionPart > , Vec < Vec < SubstitutionHighlight > > , ConfusionType ) >
329+ {
314330 // For the `Vec<Vec<SubstitutionHighlight>>` value, the first level of the vector
315331 // corresponds to the output snippet's lines, while the second level corresponds to the
316332 // substrings within that line that should be highlighted.
@@ -428,12 +444,17 @@ impl CodeSuggestion {
428444 // or deleted code in order to point at the correct column *after* substitution.
429445 let mut acc = 0 ;
430446 let mut confusion_type = ConfusionType :: None ;
431- for part in & mut substitution. parts {
447+
448+ let trimmed_parts = substitution
449+ . parts
450+ . into_iter ( )
432451 // If this is a replacement of, e.g. `"a"` into `"ab"`, adjust the
433452 // suggestion and snippet to look as if we just suggested to add
434453 // `"b"`, which is typically much easier for the user to understand.
435- part. trim_trivial_replacements ( sm) ;
454+ . map ( |part| part. trim_trivial_replacements ( sm) )
455+ . collect :: < Vec < _ > > ( ) ;
436456
457+ for part in & trimmed_parts {
437458 let part_confusion = detect_confusion_type ( sm, & part. snippet , part. span ) ;
438459 confusion_type = confusion_type. combine ( part_confusion) ;
439460 let cur_lo = sm. lookup_char_pos ( part. span . lo ( ) ) ;
@@ -521,7 +542,7 @@ impl CodeSuggestion {
521542 if highlights. iter ( ) . all ( |parts| parts. is_empty ( ) ) {
522543 None
523544 } else {
524- Some ( ( buf, substitution . parts , highlights, confusion_type) )
545+ Some ( ( buf, trimmed_parts , highlights, confusion_type) )
525546 }
526547 } )
527548 . collect ( )
0 commit comments