@@ -352,10 +352,73 @@ impl LineCompleter {
352352 }
353353}
354354
355+ mod parse {
356+ use std:: borrow:: Cow ;
357+
358+ use nom:: {
359+ branch:: alt,
360+ bytes:: complete:: { escaped_transform, is_not, tag} ,
361+ character:: complete:: anychar,
362+ combinator:: { eof, opt, value} ,
363+ IResult ,
364+ } ;
365+
366+ use crate :: editing:: { cursor:: Cursor , rope:: EditRope } ;
367+
368+ fn filepath_char ( input : & str ) -> IResult < & str , & str > {
369+ is_not ( "\t \n \\ |\" " ) ( input)
370+ }
371+
372+ fn parse_filepath ( input : & str ) -> IResult < & str , String > {
373+ escaped_transform (
374+ filepath_char,
375+ '\\' ,
376+ alt ( (
377+ value ( "\\ " , tag ( "\\ " ) ) ,
378+ value ( " " , tag ( " " ) ) ,
379+ value ( "#" , tag ( "#" ) ) ,
380+ value ( "%" , tag ( "%" ) ) ,
381+ value ( "|" , tag ( "|" ) ) ,
382+ value ( "\" " , tag ( "\" " ) ) ,
383+ ) ) ,
384+ ) ( input)
385+ }
386+
387+ fn parse_filepath_end ( input : & str ) -> IResult < & str , String > {
388+ let ( input, res) = parse_filepath ( input) ?;
389+ let ( input, _) = eof ( input) ?;
390+
391+ Ok ( ( input, res) )
392+ }
393+
394+ fn filepath_at_end ( mut input : & str ) -> IResult < & str , String > {
395+ loop {
396+ let ( i, res) = opt ( parse_filepath_end) ( input) ?;
397+ if let Some ( res) = res {
398+ return Ok ( ( i, res) ) ;
399+ }
400+ while let Ok ( ( i, _) ) = filepath_char ( input) {
401+ input = i;
402+ }
403+ input = anychar ( i) ?. 0 ;
404+ }
405+ }
406+
407+ pub fn filepath_suffix ( input : & EditRope , cursor : & Cursor ) -> Option < String > {
408+ let mut start = cursor. clone ( ) ;
409+ start. left ( 4096 ) ; // no good way to access NAME_MAX in rust
410+ let prefix = input. slice ( input. cursor_to_offset ( & start) ..input. cursor_to_offset ( cursor) ) ;
411+ let prefix = Cow :: from ( & prefix) ;
412+
413+ filepath_at_end ( prefix. as_ref ( ) ) . ok ( ) . map ( |( _, path) | path)
414+ }
415+ }
416+
355417/// Complete filenames within a path leading up to the cursor.
356418pub fn complete_path ( input : & EditRope , cursor : & mut Cursor ) -> Vec < String > {
357- let filepath = input. get_prefix_word ( cursor, & WordStyle :: FilePath ) ;
358- let filepath = filepath. unwrap_or_else ( EditRope :: empty) ;
419+ let Some ( filepath) = parse:: filepath_suffix ( input, cursor) else {
420+ return vec ! [ ] ;
421+ } ;
359422
360423 let filepath = Cow :: from ( & filepath) ;
361424 let Ok ( filepath_unexpanded) = shellexpand:: env ( filepath. as_ref ( ) ) else {
@@ -402,7 +465,7 @@ pub fn complete_path(input: &EditRope, cursor: &mut Cursor) -> Vec<String> {
402465
403466 // The .parent() and .file_name() methods treat . especially, so we
404467 // have to special-case completion of hidden files here.
405- let _ = input. get_prefix_word_mut ( cursor, & WordStyle :: FileName ) ;
468+ let _ = input. get_prefix_word_mut ( cursor, & WordStyle :: CharSet ( |c| c != MAIN_SEPARATOR ) ) ; // TODO: fix for windows
406469
407470 if let Ok ( dir) = path. read_dir ( ) {
408471 let filter = |entry : DirEntry | {
@@ -429,7 +492,7 @@ pub fn complete_path(input: &EditRope, cursor: &mut Cursor) -> Vec<String> {
429492 } else {
430493 // complete a path
431494
432- let _ = input. get_prefix_word_mut ( cursor, & WordStyle :: FileName ) ;
495+ let _ = input. get_prefix_word_mut ( cursor, & WordStyle :: CharSet ( |c| c != MAIN_SEPARATOR ) ) ; // TODO: fix for windows
433496
434497 let Some ( prefix) = path. components ( ) . next_back ( ) else {
435498 return vec ! [ ] ;
@@ -497,6 +560,14 @@ pub fn complete_path(input: &EditRope, cursor: &mut Cursor) -> Vec<String> {
497560 a. cmp ( b)
498561 } ) ;
499562
563+ for comp in & mut res {
564+ for c in [ "\\ \\ " , "\\ " , "\\ #" , "\\ %" , "\\ |" , "\\ \" " ] {
565+ if comp. contains ( & c[ 1 ..2 ] ) {
566+ * comp = comp. replace ( & c[ 1 ..2 ] , c) ;
567+ }
568+ }
569+ }
570+
500571 return res;
501572}
502573
0 commit comments