1
- use core :: num ;
1
+ use std :: any :: type_name ;
2
2
use std:: ffi:: OsStr ;
3
3
use std:: ffi:: OsString ;
4
+ use std:: sync:: Arc ;
4
5
6
+ use clap:: builder:: ArgExt ;
5
7
use clap:: builder:: StyledStr ;
6
8
use clap_lex:: OsStrExt as _;
7
9
@@ -198,7 +200,7 @@ fn complete_arg(
198
200
comp. get_content( ) . to_string_lossy( )
199
201
) )
200
202
. help ( comp. get_help ( ) . cloned ( ) )
201
- . visible ( comp. is_visible ( ) )
203
+ . hide ( comp. is_hide_set ( ) )
202
204
} ) ,
203
205
) ;
204
206
}
@@ -241,7 +243,6 @@ fn complete_arg(
241
243
comp. get_content( ) . to_string_lossy( )
242
244
) )
243
245
. help ( comp. get_help ( ) . cloned ( ) )
244
- . visible ( true )
245
246
} ) ,
246
247
) ;
247
248
} else if let Some ( short) = arg. to_short ( ) {
@@ -271,7 +272,7 @@ fn complete_arg(
271
272
comp. get_content( ) . to_string_lossy( )
272
273
) )
273
274
. help ( comp. get_help ( ) . cloned ( ) )
274
- . visible ( comp. is_visible ( ) )
275
+ . hide ( comp. is_hide_set ( ) )
275
276
} ) ,
276
277
) ;
277
278
} else {
@@ -283,7 +284,7 @@ fn complete_arg(
283
284
comp. get_content( ) . to_string_lossy( )
284
285
) )
285
286
. help ( comp. get_help ( ) . cloned ( ) )
286
- . visible ( comp. is_visible ( ) )
287
+ . hide ( comp. is_hide_set ( ) )
287
288
} ,
288
289
) ) ;
289
290
}
@@ -324,8 +325,8 @@ fn complete_arg(
324
325
}
325
326
}
326
327
}
327
- if completions. iter ( ) . any ( |a| a . is_visible ( ) ) {
328
- completions. retain ( |a| a . is_visible ( ) ) ;
328
+ if completions. iter ( ) . any ( |a| !a . is_hide_set ( ) ) {
329
+ completions. retain ( |a| !a . is_hide_set ( ) ) ;
329
330
}
330
331
331
332
Ok ( completions)
@@ -346,10 +347,16 @@ fn complete_arg_value(
346
347
name. starts_with ( value) . then ( || {
347
348
CompletionCandidate :: new ( OsString :: from ( name) )
348
349
. help ( p. get_help ( ) . cloned ( ) )
349
- . visible ( ! p. is_hide_set ( ) )
350
+ . hide ( p. is_hide_set ( ) )
350
351
} )
351
352
} ) ) ;
352
353
}
354
+ } else if let Some ( completer) = arg. get :: < ArgValueCompleter > ( ) {
355
+ let value_os = match value {
356
+ Ok ( value) => OsStr :: new ( value) ,
357
+ Err ( value_os) => value_os,
358
+ } ;
359
+ values. extend ( complete_custom_arg_value ( value_os, completer) ) ;
353
360
} else {
354
361
let value_os = match value {
355
362
Ok ( value) => OsStr :: new ( value) ,
@@ -386,6 +393,7 @@ fn complete_arg_value(
386
393
values. extend ( complete_path ( value_os, current_dir, |_| true ) ) ;
387
394
}
388
395
}
396
+
389
397
values. sort ( ) ;
390
398
}
391
399
@@ -428,27 +436,36 @@ fn complete_path(
428
436
let path = entry. path ( ) ;
429
437
let mut suggestion = pathdiff:: diff_paths ( & path, current_dir) . unwrap_or ( path) ;
430
438
suggestion. push ( "" ) ; // Ensure trailing `/`
431
- completions. push (
432
- CompletionCandidate :: new ( suggestion. as_os_str ( ) . to_owned ( ) )
433
- . help ( None )
434
- . visible ( true ) ,
435
- ) ;
439
+ completions
440
+ . push ( CompletionCandidate :: new ( suggestion. as_os_str ( ) . to_owned ( ) ) . help ( None ) ) ;
436
441
} else {
437
442
let path = entry. path ( ) ;
438
443
if is_wanted ( & path) {
439
444
let suggestion = pathdiff:: diff_paths ( & path, current_dir) . unwrap_or ( path) ;
440
- completions. push (
441
- CompletionCandidate :: new ( suggestion. as_os_str ( ) . to_owned ( ) )
442
- . help ( None )
443
- . visible ( true ) ,
444
- ) ;
445
+ completions
446
+ . push ( CompletionCandidate :: new ( suggestion. as_os_str ( ) . to_owned ( ) ) . help ( None ) ) ;
445
447
}
446
448
}
447
449
}
448
450
449
451
completions
450
452
}
451
453
454
+ fn complete_custom_arg_value (
455
+ value : & OsStr ,
456
+ completer : & ArgValueCompleter ,
457
+ ) -> Vec < CompletionCandidate > {
458
+ debug ! ( "complete_custom_arg_value: completer={completer:?}, value={value:?}" ) ;
459
+
460
+ let mut values = Vec :: new ( ) ;
461
+ let custom_arg_values = completer. 0 . completions ( ) ;
462
+ values. extend ( custom_arg_values) ;
463
+
464
+ values. retain ( |comp| comp. get_content ( ) . starts_with ( & value. to_string_lossy ( ) ) ) ;
465
+
466
+ values
467
+ }
468
+
452
469
fn complete_subcommand ( value : & str , cmd : & clap:: Command ) -> Vec < CompletionCandidate > {
453
470
debug ! (
454
471
"complete_subcommand: cmd={:?}, value={:?}" ,
@@ -476,7 +493,7 @@ fn longs_and_visible_aliases(p: &clap::Command) -> Vec<CompletionCandidate> {
476
493
longs. into_iter ( ) . map ( |s| {
477
494
CompletionCandidate :: new ( format ! ( "--{}" , s) )
478
495
. help ( a. get_help ( ) . cloned ( ) )
479
- . visible ( ! a. is_hide_set ( ) )
496
+ . hide ( a. is_hide_set ( ) )
480
497
} )
481
498
} )
482
499
} )
@@ -494,7 +511,7 @@ fn hidden_longs_aliases(p: &clap::Command) -> Vec<CompletionCandidate> {
494
511
longs. into_iter ( ) . map ( |s| {
495
512
CompletionCandidate :: new ( format ! ( "--{}" , s) )
496
513
. help ( a. get_help ( ) . cloned ( ) )
497
- . visible ( false )
514
+ . hide ( true )
498
515
} )
499
516
} )
500
517
} )
@@ -513,7 +530,7 @@ fn shorts_and_visible_aliases(p: &clap::Command) -> Vec<CompletionCandidate> {
513
530
shorts. into_iter ( ) . map ( |s| {
514
531
CompletionCandidate :: new ( s. to_string ( ) )
515
532
. help ( a. get_help ( ) . cloned ( ) )
516
- . visible ( ! a. is_hide_set ( ) )
533
+ . hide ( a. is_hide_set ( ) )
517
534
} )
518
535
} )
519
536
} )
@@ -546,12 +563,12 @@ fn subcommands(p: &clap::Command) -> Vec<CompletionCandidate> {
546
563
. map ( |s| {
547
564
CompletionCandidate :: new ( s. to_string ( ) )
548
565
. help ( sc. get_about ( ) . cloned ( ) )
549
- . visible ( ! sc. is_hide_set ( ) )
566
+ . hide ( sc. is_hide_set ( ) )
550
567
} )
551
568
. chain ( sc. get_aliases ( ) . map ( |s| {
552
569
CompletionCandidate :: new ( s. to_string ( ) )
553
570
. help ( sc. get_about ( ) . cloned ( ) )
554
- . visible ( false )
571
+ . hide ( true )
555
572
} ) )
556
573
} )
557
574
. collect ( )
@@ -667,8 +684,8 @@ pub struct CompletionCandidate {
667
684
/// Help message with a completion candidate
668
685
help : Option < StyledStr > ,
669
686
670
- /// Whether the completion candidate is visible
671
- visible : bool ,
687
+ /// Whether the completion candidate is hidden
688
+ hidden : bool ,
672
689
}
673
690
674
691
impl CompletionCandidate {
@@ -688,8 +705,8 @@ impl CompletionCandidate {
688
705
}
689
706
690
707
/// Set the visibility of the completion candidate
691
- pub fn visible ( mut self , visible : bool ) -> Self {
692
- self . visible = visible ;
708
+ pub fn hide ( mut self , hidden : bool ) -> Self {
709
+ self . hidden = hidden ;
693
710
self
694
711
}
695
712
@@ -704,7 +721,65 @@ impl CompletionCandidate {
704
721
}
705
722
706
723
/// Get the visibility of the completion candidate
707
- pub fn is_visible ( & self ) -> bool {
708
- self . visible
724
+ pub fn is_hide_set ( & self ) -> bool {
725
+ self . hidden
709
726
}
710
727
}
728
+
729
+ /// User-provided completion candidates for an argument.
730
+ ///
731
+ /// This is useful when predefined value hints are not enough.
732
+ pub trait CustomCompleter : Send + Sync {
733
+ /// All potential candidates for an argument.
734
+ ///
735
+ /// See [`CompletionCandidate`] for more information.
736
+ fn completions ( & self ) -> Vec < CompletionCandidate > ;
737
+ }
738
+
739
+ impl < F > CustomCompleter for F
740
+ where
741
+ F : Fn ( ) -> Vec < CompletionCandidate > + Send + Sync ,
742
+ {
743
+ fn completions ( & self ) -> Vec < CompletionCandidate > {
744
+ self ( )
745
+ }
746
+ }
747
+
748
+ /// A wrapper for custom completer
749
+ ///
750
+ /// # Example
751
+ ///
752
+ /// ```rust
753
+ /// use clap::Parser;
754
+ /// use clap_complete::dynamic::{ArgValueCompleter, CompletionCandidate};
755
+ ///
756
+ /// #[derive(Debug, Parser)]
757
+ /// struct Cli {
758
+ /// #[arg(long, add = ArgValueCompleter::new(|| { vec![
759
+ /// CompletionCandidate::new("foo"),
760
+ /// CompletionCandidate::new("bar"),
761
+ /// CompletionCandidate::new("baz")] }))]
762
+ /// custom: Option<String>,
763
+ /// }
764
+ ///
765
+ /// ```
766
+ #[ derive( Clone ) ]
767
+ pub struct ArgValueCompleter ( Arc < dyn CustomCompleter > ) ;
768
+
769
+ impl ArgValueCompleter {
770
+ /// Create a new `ArgValueCompleter` with a custom completer
771
+ pub fn new < C : CustomCompleter > ( completer : C ) -> Self
772
+ where
773
+ C : ' static + CustomCompleter ,
774
+ {
775
+ Self ( Arc :: new ( completer) )
776
+ }
777
+ }
778
+
779
+ impl std:: fmt:: Debug for ArgValueCompleter {
780
+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
781
+ f. write_str ( type_name :: < Self > ( ) )
782
+ }
783
+ }
784
+
785
+ impl ArgExt for ArgValueCompleter { }
0 commit comments