@@ -188,31 +188,13 @@ fn is_executable(entry: &fs::DirEntry) -> bool {
188188}
189189
190190fn resolve_dir_path ( dir_path : & str ) -> PathBuf {
191- // On Windows, we need to distinguish between:
192- // 1. System paths like "C:\Users\..." where backslashes are path separators
193- // 2. User-typed paths like "some\ dir/" where backslashes are escape characters
194- //
195- // Windows absolute paths start with a drive letter (C:\) or UNC path (\\server\share)
196- let is_windows_absolute = cfg ! ( windows)
197- && ( dir_path. len ( ) >= 3 && dir_path. chars ( ) . nth ( 1 ) == Some ( ':' ) || // C:\...
198- dir_path. starts_with ( "\\ \\ " ) ) ; // UNC path
199-
200- // Only unescape user-typed relative paths to handle escaped spaces
201- // Don't unescape Windows absolute paths - their backslashes are path separators
202- let unescaped = if is_windows_absolute {
203- dir_path. to_string ( )
204- } else {
205- unescape_for_completion ( dir_path)
206- } ;
191+ // Unescape the directory path to handle spaces and other special characters
192+ let unescaped = unescape_for_completion ( dir_path) ;
207193
208- if dir_path. starts_with ( '/' ) || is_windows_absolute {
194+ if dir_path. starts_with ( '/' ) {
209195 PathBuf :: from ( unescaped)
210196 } else if let Some ( stripped) = dir_path. strip_prefix ( '~' ) {
211- let unescaped_stripped = if is_windows_absolute {
212- stripped. to_string ( )
213- } else {
214- unescape_for_completion ( stripped)
215- } ;
197+ let unescaped_stripped = unescape_for_completion ( stripped) ;
216198 dirs:: home_dir ( )
217199 . map ( |h| {
218200 h. join (
@@ -336,8 +318,14 @@ mod tests {
336318 use super :: * ;
337319 use rustyline:: history:: DefaultHistory ;
338320 use std:: fs;
321+ use std:: path:: Path ;
339322 use tempfile:: TempDir ;
340323
324+ // Helper function to convert a path to a shell-escaped string
325+ fn path_to_escaped_string ( path : & Path ) -> String {
326+ escape_for_shell ( & path. display ( ) . to_string ( ) )
327+ }
328+
341329 #[ tokio:: test]
342330 async fn test_complete_hidden_files_when_starting_with_dot ( ) {
343331 let temp_dir = TempDir :: new ( ) . unwrap ( ) ;
@@ -352,7 +340,8 @@ mod tests {
352340 // Test completion with "." prefix
353341 let completer = ShellCompleter :: new ( HashSet :: new ( ) ) ;
354342 let history = DefaultHistory :: new ( ) ;
355- let line = format ! ( "cat {}/.gi" , temp_path. display( ) ) ;
343+ let escaped_path = path_to_escaped_string ( temp_path) ;
344+ let line = format ! ( "cat {}/.gi" , escaped_path) ;
356345 let pos = line. len ( ) ;
357346 let ( _start, matches) = completer
358347 . complete ( & line, pos, & Context :: new ( & history) )
@@ -379,7 +368,8 @@ mod tests {
379368 // Test completion without "." prefix
380369 let completer = ShellCompleter :: new ( HashSet :: new ( ) ) ;
381370 let history = DefaultHistory :: new ( ) ;
382- let line = format ! ( "cat {}/" , temp_path. display( ) ) ;
371+ let escaped_path = path_to_escaped_string ( temp_path) ;
372+ let line = format ! ( "cat {}/" , escaped_path) ;
383373 let pos = line. len ( ) ;
384374 let ( _start, matches) = completer
385375 . complete ( & line, pos, & Context :: new ( & history) )
@@ -404,7 +394,8 @@ mod tests {
404394 // Test completion with ".gith" prefix
405395 let completer = ShellCompleter :: new ( HashSet :: new ( ) ) ;
406396 let history = DefaultHistory :: new ( ) ;
407- let line = format ! ( "cd {}/.gith" , temp_path. display( ) ) ;
397+ let escaped_path = path_to_escaped_string ( temp_path) ;
398+ let line = format ! ( "cd {}/.gith" , escaped_path) ;
408399 let pos = line. len ( ) ;
409400 let ( _start, matches) = completer
410401 . complete ( & line, pos, & Context :: new ( & history) )
@@ -428,7 +419,8 @@ mod tests {
428419 // Test completion with just "." prefix
429420 let completer = ShellCompleter :: new ( HashSet :: new ( ) ) ;
430421 let history = DefaultHistory :: new ( ) ;
431- let line = format ! ( "ls {}/." , temp_path. display( ) ) ;
422+ let escaped_path = path_to_escaped_string ( temp_path) ;
423+ let line = format ! ( "ls {}/." , escaped_path) ;
432424 let pos = line. len ( ) ;
433425 let ( _start, matches) = completer
434426 . complete ( & line, pos, & Context :: new ( & history) )
@@ -453,41 +445,42 @@ mod tests {
453445
454446 let completer = ShellCompleter :: new ( HashSet :: new ( ) ) ;
455447 let history = DefaultHistory :: new ( ) ;
448+ let escaped_path = path_to_escaped_string ( temp_path) ;
456449
457450 // Test 1: completion of "s" should suggest both files
458- let line = format ! ( "cat {}/s" , temp_path . display ( ) ) ;
451+ let line = format ! ( "cat {}/s" , escaped_path ) ;
459452 let pos = line. len ( ) ;
460453 let ( _start, matches) = completer
461454 . complete ( & line, pos, & Context :: new ( & history) )
462455 . unwrap ( ) ;
463456 assert_eq ! ( matches. len( ) , 2 ) ;
464457
465458 // Test 2: completion of "some\ fi" (escaped space) should complete to full path
466- let line = format ! ( "cat {}/some\\ fi" , temp_path . display ( ) ) ;
459+ let line = format ! ( "cat {}/some\\ fi" , escaped_path ) ;
467460 let pos = line. len ( ) ;
468461 let ( _start, matches) = completer
469462 . complete ( & line, pos, & Context :: new ( & history) )
470463 . unwrap ( ) ;
471464 assert_eq ! ( matches. len( ) , 1 ) ;
472465 assert_eq ! (
473466 matches[ 0 ] . replacement,
474- format!( "{}/some\\ file.txt" , temp_path . display ( ) )
467+ format!( "{}/some\\ file.txt" , escaped_path )
475468 ) ;
476469
477470 // Test 3: completion of "some\ fa" (escaped space) should complete to full path
478- let line = format ! ( "cat {}/some\\ fa" , temp_path . display ( ) ) ;
471+ let line = format ! ( "cat {}/some\\ fa" , escaped_path ) ;
479472 let pos = line. len ( ) ;
480473 let ( _start, matches) = completer
481474 . complete ( & line, pos, & Context :: new ( & history) )
482475 . unwrap ( ) ;
483476 assert_eq ! ( matches. len( ) , 1 ) ;
484477 assert_eq ! (
485478 matches[ 0 ] . replacement,
486- format!( "{}/some\\ fact.txt" , temp_path . display ( ) )
479+ format!( "{}/some\\ fact.txt" , escaped_path )
487480 ) ;
488481
489482 // Test 4: completion of "some\ fx" (escaped space) should return no matches
490- let line = format ! ( "cat {}/some\\ fx" , temp_path . display ( ) ) ;
483+ let line = format ! ( "cat {}/some\\ fx" , escaped_path ) ;
491484 let pos = line. len ( ) ;
492485 let ( _start, matches) = completer
493486 . complete ( & line, pos, & Context :: new ( & history) )
@@ -507,37 +500,38 @@ mod tests {
507500
508501 let completer = ShellCompleter :: new ( HashSet :: new ( ) ) ;
509502 let history = DefaultHistory :: new ( ) ;
503+ let escaped_path = path_to_escaped_string ( temp_path) ;
510504
511505 // Test 1: completion of "some\ d" should suggest the directory
512- let line = format ! ( "cd {}/some\\ d" , temp_path . display ( ) ) ;
506+ let line = format ! ( "cd {}/some\\ d" , escaped_path ) ;
513507 let pos = line. len ( ) ;
514508 let ( _start, matches) = completer
515509 . complete ( & line, pos, & Context :: new ( & history) )
516510 . unwrap ( ) ;
517511 assert_eq ! ( matches. len( ) , 1 ) ;
518512 assert_eq ! (
519513 matches[ 0 ] . replacement,
520- format!( "{}/some\\ dir/" , temp_path . display ( ) )
514+ format!( "{}/some\\ dir/" , escaped_path )
521515 ) ;
522516
523517 // Test 2: completion of "some\ dir/f" should suggest both files
524- let line = format ! ( "cat {}/some\\ dir/f" , temp_path . display ( ) ) ;
518+ let line = format ! ( "cat {}/some\\ dir/f" , escaped_path ) ;
525519 let pos = line. len ( ) ;
526520 let ( _start, matches) = completer
527521 . complete ( & line, pos, & Context :: new ( & history) )
528522 . unwrap ( ) ;
529523 assert_eq ! ( matches. len( ) , 2 ) ;
530524
531525 // Test 3: completion of "some\ dir/file1" should complete to file1.txt
532- let line = format ! ( "cat {}/some\\ dir/file1" , temp_path . display ( ) ) ;
526+ let line = format ! ( "cat {}/some\\ dir/file1" , escaped_path ) ;
533527 let pos = line. len ( ) ;
534528 let ( _start, matches) = completer
535529 . complete ( & line, pos, & Context :: new ( & history) )
536530 . unwrap ( ) ;
537531 assert_eq ! ( matches. len( ) , 1 ) ;
538532 assert_eq ! (
539533 matches[ 0 ] . replacement,
540- format!( "{}/some\\ dir/file1.txt" , temp_path . display ( ) )
534+ format!( "{}/some\\ dir/file1.txt" , escaped_path )
541535 ) ;
542536 }
543537}
0 commit comments