@@ -369,16 +369,16 @@ fn emit_completion_script(shell: clap_complete::Shell) {
369369
370370/// Print dynamic completion candidates (one per line) for the `__complete`
371371/// helper. Reads only the local cache / hardcoded lists — never connects.
372- fn emit_dynamic_completions ( args : & CompleteArgs ) {
372+ fn emit_dynamic_completions ( kind : CompletionKind , partial : & str ) {
373373 use std:: io:: Write ;
374374
375375 // Split a possibly comma-separated token into the committed prefix
376376 // (everything up to and including the last comma) and the fragment being
377377 // completed. Candidates are printed as `prefix + candidate` so the shell
378378 // replaces the whole word with a valid, fully-qualified token.
379- let ( prefix, fragment) = match args . partial . rfind ( ',' ) {
380- Some ( i) => ( & args . partial [ ..=i] , & args . partial [ i + 1 ..] ) ,
381- None => ( "" , args . partial . as_str ( ) ) ,
379+ let ( prefix, fragment) = match partial. rfind ( ',' ) {
380+ Some ( i) => ( & partial[ ..=i] , & partial[ i + 1 ..] ) ,
381+ None => ( "" , partial) ,
382382 } ;
383383
384384 // In key=value lists (e.g. `--bin target=file.bin`) the text after '=' is
@@ -387,7 +387,7 @@ fn emit_dynamic_completions(args: &CompleteArgs) {
387387 return ;
388388 }
389389
390- let candidates: Vec < String > = match args . kind {
390+ let candidates: Vec < String > = match kind {
391391 CompletionKind :: ParamNames => {
392392 load_completion_cache ( ) . param . into_iter ( ) . map ( |p| p. name ) . collect ( )
393393 }
@@ -420,20 +420,25 @@ fn emit_dynamic_completions(args: &CompleteArgs) {
420420}
421421
422422async fn run ( ) -> Result < ( ) > {
423+ // The `__complete` dynamic-completion helper is dispatched before clap so
424+ // it never appears as a subcommand in `--help` or the generated completion
425+ // scripts. It is called only by the installed completion scripts, with
426+ // known arguments, so anything unparseable just emits no candidates.
427+ let argv: Vec < String > = std:: env:: args ( ) . collect ( ) ;
428+ if argv. get ( 1 ) . is_some_and ( |a| a == "__complete" ) {
429+ if let Some ( kind) = argv. get ( 2 ) . and_then ( |s| CompletionKind :: from_str ( s, false ) . ok ( ) ) {
430+ emit_dynamic_completions ( kind, argv. get ( 3 ) . map ( String :: as_str) . unwrap_or ( "" ) ) ;
431+ }
432+ return Ok ( ( ) ) ;
433+ }
434+
423435 let args = CliArgs :: parse ( ) ;
424436
425- // Completion subcommands are handled up front: they never connect, and
426- // `completions` needs no config at all.
427- match & args. command {
428- Commands :: Completions { shell } => {
429- emit_completion_script ( * shell) ;
430- return Ok ( ( ) ) ;
431- }
432- Commands :: Complete ( complete_args) => {
433- emit_dynamic_completions ( complete_args) ;
434- return Ok ( ( ) ) ;
435- }
436- _ => { }
437+ // The `completions` subcommand is handled up front: it never connects and
438+ // needs no config at all.
439+ if let Commands :: Completions { shell } = & args. command {
440+ emit_completion_script ( * shell) ;
441+ return Ok ( ( ) ) ;
437442 }
438443
439444 let mut config: Config = confy:: load ( "cf-cli" , None ) . unwrap_or_else ( |err| {
@@ -471,7 +476,7 @@ async fn run() -> Result<()> {
471476 let body = async {
472477 match & args. command {
473478 // Handled before connection setup via early return in run().
474- Commands :: Completions { .. } | Commands :: Complete ( _ ) => unreachable ! ( ) ,
479+ Commands :: Completions { .. } => unreachable ! ( ) ,
475480 Commands :: Scan ( scan_options) => {
476481 let addresses = match & scan_options. address {
477482 Some ( addr) => vec ! [ addr. clone( ) ] ,
0 commit comments