@@ -75,6 +75,27 @@ fn strip_placeholder<'a>(working_set: &'a StateWorkingSet, span: &Span) -> (Span
7575 ( new_span, prefix)
7676}
7777
78+ /// Given a span with noise,
79+ /// 1. Call `rsplit` to get the last token
80+ /// 2. Strip the last placeholder from the token
81+ fn strip_placeholder_with_rsplit < ' a > (
82+ working_set : & ' a StateWorkingSet ,
83+ span : & Span ,
84+ predicate : impl FnMut ( & u8 ) -> bool ,
85+ ) -> ( Span , & ' a [ u8 ] ) {
86+ let flag_equals_str = working_set. get_span_contents ( * span) ;
87+ let mut true_prefix = flag_equals_str
88+ . rsplit ( predicate)
89+ . next ( )
90+ . unwrap_or ( flag_equals_str) ;
91+ let true_start = span. end . saturating_sub ( true_prefix. len ( ) ) ;
92+ if !true_prefix. is_empty ( ) {
93+ true_prefix = & true_prefix[ ..true_prefix. len ( ) - 1 ] ;
94+ }
95+ let true_end = true_start + true_prefix. len ( ) ;
96+ ( Span :: new ( true_start, true_end) , true_prefix)
97+ }
98+
7899#[ derive( Clone ) ]
79100pub struct NuCompleter {
80101 engine_state : Arc < EngineState > ,
@@ -267,6 +288,8 @@ impl NuCompleter {
267288 return vec ! [ ] ;
268289 } ;
269290
291+ let mut suggestions: Vec < SemanticSuggestion > = vec ! [ ] ;
292+
270293 match & element_expression. expr {
271294 Expr :: Var ( _) => {
272295 return self . variable_names_completion_helper (
@@ -314,36 +337,33 @@ impl NuCompleter {
314337 }
315338 }
316339 }
340+ // NOTE: user defined internal commands can have any length
341+ // e.g. `def "foo -f --ff bar"`, complete by line text
342+ // instead of relying on the parsing result in that case
343+ Expr :: Call ( _) | Expr :: ExternalCall ( _, _) => {
344+ suggestions. extend ( self . command_completion_helper (
345+ & working_set,
346+ element_expression. span ,
347+ fake_offset,
348+ true ,
349+ ) )
350+ }
351+ _ => ( ) ,
352+ }
353+
354+ // unfinished argument completion for commands
355+ match & element_expression. expr {
317356 Expr :: Call ( call) => {
318- // at command head, e.g. `l<tab>`
319- // still not sure user want internal or external
320- // complete both internal and external commands
321- if call. head . contains ( pos) {
322- return self . command_completion_helper (
323- & working_set,
324- call. head ,
325- fake_offset,
326- true ,
327- ) ;
328- }
329- for ( i, arg) in call. arguments . iter ( ) . enumerate ( ) {
357+ for arg in call. arguments . iter ( ) {
330358 let span = arg. span ( ) ;
331359 if span. contains ( pos) {
332360 // if customized completion specified, it has highest priority
333361 if let Some ( decl_id) = arg. expr ( ) . and_then ( |e| e. custom_completion ) {
334362 // for `--foo <tab>` and `--foo=<tab>`, the arg span should be trimmed
335363 let ( new_span, prefix) = if matches ! ( arg, Argument :: Named ( _) ) {
336- let flag_equals_str = working_set. get_span_contents ( span) ;
337- let mut true_prefix = flag_equals_str
338- . rsplit ( |c| * c == b'=' || * c == b' ' )
339- . next ( )
340- . unwrap_or ( flag_equals_str) ;
341- let true_start = span. end . saturating_sub ( true_prefix. len ( ) ) ;
342- if !true_prefix. is_empty ( ) {
343- true_prefix = & true_prefix[ ..true_prefix. len ( ) - 1 ] ;
344- }
345- let true_end = true_start + true_prefix. len ( ) ;
346- ( Span :: new ( true_start, true_end) , true_prefix)
364+ strip_placeholder_with_rsplit ( & working_set, & span, |b| {
365+ * b == b'=' || * b == b' '
366+ } )
347367 } else {
348368 strip_placeholder ( & working_set, & span)
349369 } ;
@@ -355,14 +375,15 @@ impl NuCompleter {
355375 FileCompletion :: new ( ) ,
356376 ) ;
357377
358- return self . process_completion (
378+ suggestions . extend ( self . process_completion (
359379 & mut completer,
360380 & working_set,
361381 prefix,
362382 new_span,
363383 fake_offset,
364384 pos,
365- ) ;
385+ ) ) ;
386+ break ;
366387 }
367388 // complete module file/directory
368389 // TODO: if module file already specified,
@@ -374,7 +395,7 @@ impl NuCompleter {
374395 || command_head == b"source-env"
375396 {
376397 let mut completer = DotNuCompletion :: new ( ) ;
377- let ( new_span, prefix) = strip_placeholder ( & working_set, & call . head ) ;
398+ let ( new_span, prefix) = strip_placeholder ( & working_set, & span ) ;
378399 return self . process_completion (
379400 & mut completer,
380401 & working_set,
@@ -384,89 +405,55 @@ impl NuCompleter {
384405 pos,
385406 ) ;
386407 }
387- // if at the first arguments, e.g. `help co<tab>`
388- // still need to complete internal commands if any
389- if i == 0 {
390- let subcommands = self . command_completion_helper (
391- & working_set,
392- call. head . merge ( span) ,
393- offset,
394- false ,
395- ) ;
396- if !subcommands. is_empty ( ) {
397- return subcommands;
398- }
399- }
400408 // normal arguments completion
401- match arg {
409+ suggestions . extend ( match arg {
402410 // flags
403411 Argument :: Named ( _) => {
404412 let mut flag_completions = FlagCompletion {
405413 decl_id : call. decl_id ,
406414 } ;
407415 let ( new_span, prefix) = strip_placeholder ( & working_set, & span) ;
408- return self . process_completion (
416+ self . process_completion (
409417 & mut flag_completions,
410418 & working_set,
411419 prefix,
412420 new_span,
413421 fake_offset,
414422 pos,
415- ) ;
423+ )
416424 }
417425 // complete according to expression type
418- Argument :: Positional ( expr) => {
419- return self . argument_completion_helper (
420- expr,
421- & working_set,
422- span,
423- fake_offset,
424- ) ;
425- }
426+ Argument :: Positional ( expr) => self . argument_completion_helper (
427+ expr,
428+ & working_set,
429+ span,
430+ fake_offset,
431+ ) ,
426432 Argument :: Unknown ( _) => {
427433 let ( new_span, prefix) = strip_placeholder ( & working_set, & span) ;
428434 if prefix. starts_with ( b"-" ) {
429435 let mut flag_completions = FlagCompletion {
430436 decl_id : call. decl_id ,
431437 } ;
432- return self . process_completion (
438+ self . process_completion (
433439 & mut flag_completions,
434440 & working_set,
435441 prefix,
436442 new_span,
437443 fake_offset,
438444 pos,
439- ) ;
445+ )
440446 } else {
441- let mut completer = FileCompletion :: new ( ) ;
442- return self . process_completion (
443- & mut completer,
444- & working_set,
445- prefix,
446- new_span,
447- offset,
448- 0 ,
449- ) ;
447+ vec ! [ ]
450448 }
451449 }
452- _ => ( ) ,
453- } ;
450+ _ => vec ! [ ] ,
451+ } ) ;
454452 break ;
455453 }
456454 }
457455 }
458456 Expr :: ExternalCall ( head, arguments) => {
459- // at command head, e.g. `l<tab>`
460- // still not sure user want internal or external
461- // complete both internal and external commands
462- if head. span . contains ( pos) {
463- return self . command_completion_helper (
464- & working_set,
465- head. span ,
466- fake_offset,
467- true ,
468- ) ;
469- }
470457 for ( i, arg) in arguments. iter ( ) . enumerate ( ) {
471458 let span = arg. expr ( ) . span ;
472459 if span. contains ( pos) {
@@ -504,19 +491,8 @@ impl NuCompleter {
504491 fake_offset,
505492 Span :: new ( span. start , span. end . saturating_sub ( 1 ) ) ,
506493 ) {
507- return external_result;
508- } else {
509- // fallback to file completion
510- let mut file_completer = FileCompletion :: new ( ) ;
511- let ( new_span, prefix) = strip_placeholder ( & working_set, & span) ;
512- return self . process_completion (
513- & mut file_completer,
514- & working_set,
515- prefix,
516- new_span,
517- fake_offset,
518- pos,
519- ) ;
494+ suggestions. extend ( external_result) ;
495+ return suggestions;
520496 }
521497 }
522498 break ;
@@ -526,7 +502,22 @@ impl NuCompleter {
526502 _ => ( ) ,
527503 }
528504
529- vec ! [ ]
505+ if suggestions. is_empty ( ) {
506+ let mut file_completions = FileCompletion :: new ( ) ;
507+ let ( new_span, prefix) =
508+ strip_placeholder_with_rsplit ( & working_set, & element_expression. span , |c| {
509+ * c == b' '
510+ } ) ;
511+ suggestions. extend ( self . process_completion (
512+ & mut file_completions,
513+ & working_set,
514+ prefix,
515+ new_span,
516+ fake_offset,
517+ pos,
518+ ) ) ;
519+ }
520+ suggestions
530521 }
531522}
532523
0 commit comments