@@ -62,11 +62,11 @@ impl FileSystemService {
62
62
let expanded_path = expand_home ( requested_path. to_path_buf ( ) ) ;
63
63
64
64
// Resolve the absolute path
65
- let absolute_path = expanded_path
66
- . as_path ( )
67
- . is_absolute ( )
68
- . then ( || expanded_path . clone ( ) )
69
- . unwrap_or_else ( || env :: current_dir ( ) . unwrap ( ) . join ( & expanded_path ) ) ;
65
+ let absolute_path = if expanded_path. as_path ( ) . is_absolute ( ) {
66
+ expanded_path . clone ( )
67
+ } else {
68
+ env :: current_dir ( ) . unwrap ( ) . join ( & expanded_path )
69
+ } ;
70
70
71
71
// Normalize the path
72
72
let normalized_requested = normalize_path ( & absolute_path) ;
@@ -121,6 +121,16 @@ impl FileSystemService {
121
121
} )
122
122
}
123
123
124
+ fn detect_line_ending ( & self , text : & str ) -> & str {
125
+ if text. contains ( "\r \n " ) {
126
+ "\r \n "
127
+ } else if text. contains ( '\r' ) {
128
+ "\r "
129
+ } else {
130
+ "\n "
131
+ }
132
+ }
133
+
124
134
pub async fn zip_directory (
125
135
& self ,
126
136
input_dir : String ,
@@ -472,6 +482,7 @@ impl FileSystemService {
472
482
473
483
// Read file content and normalize line endings
474
484
let content_str = tokio:: fs:: read_to_string ( & valid_path) . await ?;
485
+ let original_line_ending = self . detect_line_ending ( & content_str) ;
475
486
let content_str = normalize_line_endings ( & content_str) ;
476
487
477
488
// Apply edits sequentially
@@ -480,15 +491,13 @@ impl FileSystemService {
480
491
for edit in edits {
481
492
let normalized_old = normalize_line_endings ( & edit. old_text ) ;
482
493
let normalized_new = normalize_line_endings ( & edit. new_text ) ;
483
-
484
494
// If exact match exists, use it
485
495
if modified_content. contains ( & normalized_old) {
486
496
modified_content = modified_content. replacen ( & normalized_old, & normalized_new, 1 ) ;
487
497
continue ;
488
498
}
489
499
490
500
// Otherwise, try line-by-line matching with flexibility for whitespace
491
- // trim ends help to avoid inconsistencies empty lines at the end that may break the comparison
492
501
let old_lines: Vec < String > = normalized_old
493
502
. trim_end ( )
494
503
. split ( '\n' )
@@ -514,7 +523,6 @@ impl FileSystemService {
514
523
515
524
if is_match {
516
525
// Preserve original indentation of first line
517
- // leading spaces
518
526
let original_indent = content_lines[ i]
519
527
. chars ( )
520
528
. take_while ( |& c| c. is_whitespace ( ) )
@@ -524,12 +532,12 @@ impl FileSystemService {
524
532
. split ( '\n' )
525
533
. enumerate ( )
526
534
. map ( |( j, line) | {
527
- // keep indentation of the first line
535
+ // Keep indentation of the first line
528
536
if j == 0 {
529
537
return format ! ( "{}{}" , original_indent, line. trim_start( ) ) ;
530
538
}
531
539
532
- // For subsequent lines, try to preserve relative indentation
540
+ // For subsequent lines, preserve relative indentation and original whitespace type
533
541
let old_indent = old_lines
534
542
. get ( j)
535
543
. map ( |line| {
@@ -544,12 +552,22 @@ impl FileSystemService {
544
552
. take_while ( |& c| c. is_whitespace ( ) )
545
553
. collect :: < String > ( ) ;
546
554
547
- let relative_indent = new_indent. len ( ) - old_indent. len ( ) ;
548
-
555
+ // Use the same whitespace character as original_indent (tabs or spaces)
556
+ let indent_char = if original_indent. contains ( '\t' ) {
557
+ "\t "
558
+ } else {
559
+ " "
560
+ } ;
561
+ let relative_indent = if new_indent. len ( ) >= old_indent. len ( ) {
562
+ new_indent. len ( ) - old_indent. len ( )
563
+ } else {
564
+ 0 // Don't reduce indentation below original
565
+ } ;
549
566
format ! (
550
- "{}{}" ,
551
- original_indent,
552
- " " . repeat( relative_indent. max( 0 ) ) + line. trim_start( )
567
+ "{}{}{}" ,
568
+ & original_indent,
569
+ & indent_char. repeat( relative_indent) ,
570
+ line. trim_start( )
553
571
)
554
572
} )
555
573
. collect ( ) ;
@@ -593,6 +611,7 @@ impl FileSystemService {
593
611
594
612
if !is_dry_run {
595
613
let target = save_to. unwrap_or ( valid_path. as_path ( ) ) ;
614
+ let modified_content = modified_content. replace ( "\n " , original_line_ending) ;
596
615
tokio:: fs:: write ( target, modified_content) . await ?;
597
616
}
598
617
0 commit comments