11use std:: { io, path:: PathBuf } ;
22
3+ use clap:: builder:: StyledStr ;
34use clap:: { CommandFactory , Parser , Subcommand } ;
4- use clap_complete:: { Generator , Shell , generate} ;
5+ use clap_complete:: engine:: { ArgValueCompleter , CompletionCandidate } ;
6+ use clap_complete:: env:: Shells ;
7+ use clap_complete:: { CompleteEnv , Shell , generate} ;
8+
9+ use crate :: config:: ConfigService ;
10+ use crate :: config:: FileConfig ;
511
612#[ derive( Debug , Parser ) ]
713#[ command( name = "git-squad" ) ]
@@ -16,23 +22,41 @@ pub struct Cli {
1622}
1723
1824impl Cli {
25+ pub fn new ( ) -> Self {
26+ CompleteEnv :: with_factory ( Cli :: command) . complete ( ) ;
27+ Self :: parse ( )
28+ }
29+
1930 pub fn get_command ( & self ) -> Command {
2031 self . command . clone ( ) . unwrap_or ( Command :: Info )
2132 }
2233}
2334
24- pub fn print_completions < G : Generator > ( generator : G ) {
25- let mut cmd = Cli :: command ( ) ;
26- let name = cmd. get_name ( ) . to_string ( ) ;
27- generate ( generator, & mut cmd, name, & mut io:: stdout ( ) ) ;
35+ pub fn print_completions ( shell : Shell ) -> anyhow:: Result < ( ) > {
36+ print_completions_internal ( shell, & mut Cli :: command ( ) )
37+ }
38+
39+ fn print_completions_internal ( shell : Shell , cmd : & mut clap:: Command ) -> anyhow:: Result < ( ) > {
40+ generate ( shell, cmd, cmd. get_name ( ) . to_string ( ) , & mut io:: stdout ( ) ) ;
41+
42+ println ! ( ) ;
43+
44+ let name = cmd. get_name ( ) ;
45+ if let Some ( completer) = Shells :: builtins ( ) . completer ( shell. to_string ( ) . as_str ( ) ) {
46+ completer. write_registration ( "COMPLETE" , name, name, name, & mut io:: stdout ( ) ) ?;
47+ }
48+
49+ Ok ( ( ) )
2850}
2951
3052#[ derive( Debug , Subcommand , Clone ) ]
3153pub enum Command {
3254 /// Add buddies to the current session
3355 With {
3456 /// The aliases of the buddies to add
35- #[ arg( required = true , num_args = 1 .., ) ]
57+ #[ arg( required = true ,
58+ num_args = 1 ..,
59+ add = ArgValueCompleter :: new( alias_completer) ) ]
3660 // TODO: I would rather use NonEmpty<String> here but clap makes
3761 // this really cumbersome
3862 aliases : Vec < String > ,
@@ -41,7 +65,9 @@ pub enum Command {
4165 /// Remove buddies from the current session
4266 Without {
4367 /// The aliases of the buddies to remove
44- #[ arg( required = true , num_args = 1 .., ) ]
68+ #[ arg( required = true ,
69+ num_args = 1 ..,
70+ add = ArgValueCompleter :: new( alias_completer) ) ]
4571 // TODO: I would rather use NonEmpty<String> here but clap makes
4672 // this really cumbersome
4773 aliases : Vec < String > ,
@@ -59,6 +85,7 @@ pub enum Command {
5985 /// Delete a buddy from the list of available buddies
6086 Forget {
6187 /// The alias for the buddy to delete
88+ #[ arg( add = ArgValueCompleter :: new( alias_completer) ) ]
6289 alias : String ,
6390 } ,
6491
@@ -75,6 +102,24 @@ pub enum Command {
75102 Completions { shell : Shell } ,
76103}
77104
78- pub fn parse ( ) -> Cli {
79- Cli :: parse ( )
105+ fn alias_completer ( current : & std:: ffi:: OsStr ) -> Vec < CompletionCandidate > {
106+ // TODO: support completions with custom buddies_file locations
107+ let conf = FileConfig { buddies_file : None } ;
108+
109+ if let Ok ( buddies) = conf. load_buddies ( ) {
110+ let current = current. to_str ( ) . unwrap_or_default ( ) ;
111+ return buddies
112+ . buddies
113+ . iter ( )
114+ . filter ( |s| {
115+ s. alias . starts_with ( current) || s. name . starts_with ( current) || s. email . starts_with ( current)
116+ } )
117+ . map ( |b| {
118+ let help = Some ( StyledStr :: from ( b. format_buddy ( ) ) ) ;
119+ CompletionCandidate :: new ( b. alias . clone ( ) ) . help ( help)
120+ } )
121+ . collect ( ) ;
122+ }
123+
124+ return vec ! [ ] ;
80125}
0 commit comments