@@ -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.
@@ -423,17 +439,20 @@ impl CodeSuggestion {
423439 lines. lines . get ( 0 ) . and_then ( |line0| sf. get_line ( line0. line_index ) ) ;
424440 let mut buf = String :: new ( ) ;
425441
442+ let trimmed_parts = substitution
443+ . parts
444+ . into_iter ( )
445+ // If this is a replacement of, e.g. `"a"` into `"ab"`, adjust the
446+ // suggestion and snippet to look as if we just suggested to add
447+ // `"b"`, which is typically much easier for the user to understand.
448+ . map ( |part| part. trim_trivial_replacements ( sm) )
449+ . collect :: < Vec < _ > > ( ) ;
426450 let mut line_highlight = vec ! [ ] ;
427451 // We need to keep track of the difference between the existing code and the added
428452 // or deleted code in order to point at the correct column *after* substitution.
429453 let mut acc = 0 ;
430454 let mut confusion_type = ConfusionType :: None ;
431- for part in & mut substitution. parts {
432- // If this is a replacement of, e.g. `"a"` into `"ab"`, adjust the
433- // suggestion and snippet to look as if we just suggested to add
434- // `"b"`, which is typically much easier for the user to understand.
435- part. trim_trivial_replacements ( sm) ;
436-
455+ for part in & trimmed_parts {
437456 let part_confusion = detect_confusion_type ( sm, & part. snippet , part. span ) ;
438457 confusion_type = confusion_type. combine ( part_confusion) ;
439458 let cur_lo = sm. lookup_char_pos ( part. span . lo ( ) ) ;
@@ -521,7 +540,7 @@ impl CodeSuggestion {
521540 if highlights. iter ( ) . all ( |parts| parts. is_empty ( ) ) {
522541 None
523542 } else {
524- Some ( ( buf, substitution . parts , highlights, confusion_type) )
543+ Some ( ( buf, trimmed_parts , highlights, confusion_type) )
525544 }
526545 } )
527546 . collect ( )
0 commit comments