@@ -171,7 +171,7 @@ def _env_var(prog_name: str) -> str:
171171 return f"_{ prog_name } _COMPLETE" .replace ("-" , "_" ).upper ()
172172
173173
174- def _dynamic_action (prog_name : str ) -> str :
174+ def _dynamic_action (command_path : tuple [ str , ...] ) -> str :
175175 """A Carapace shell-macro action that calls back into the CLI for completion.
176176
177177 Carapace's default ``$(...)`` macro runs ``sh -c '<script>' -- <words>``,
@@ -181,14 +181,27 @@ def _dynamic_action(prog_name: str) -> str:
181181 shell), which prints exactly that. Carapace prefix-filters the result, so the
182182 callback returns the full candidate set.
183183
184- .. warning::
185- The macro string is derived from ``carapace-spec``'s ``core.go`` shell
186- macro rather than a live run. It is the one part of the export that
187- warrants a smoke test against an installed ``carapace`` binary.
184+ ``command_path`` is the chain of command names from the root program down to
185+ the command owning the completed parameter, like ``("weather", "forecast")``.
186+ It is baked into ``COMP_WORDS`` so :class:`CarapaceComplete` can rebuild the
187+ command line Click needs to resolve the subcommand.
188+
189+ .. note::
190+ Baking the whole path is required, not cosmetic. Carapace's ``traverse``
191+ descends into each subcommand with only the remaining words, stripping the
192+ parent command names from the ``c.Args`` it hands the macro. So ``$*``
193+ carries only the leaf command's own words: for ``weather forecast --city``
194+ Carapace passes just ``--city``. Without the baked ``weather forecast``
195+ prefix, the callback would reconstruct ``weather --city`` and resolve
196+ against the root command, completing the wrong thing. The env var and the
197+ invoked binary stay rooted at ``command_path[0]``, the executable Carapace
198+ dispatches and the name Click derives its completion variable from.
188199 """
200+ root = command_path [0 ]
201+ words = " " .join (command_path )
189202 return (
190- f'$(env "{ _env_var (prog_name )} =carapace_complete" '
191- f'"COMP_WORDS={ prog_name } $*" { prog_name } 2>/dev/null)'
203+ f'$(env "{ _env_var (root )} =carapace_complete" '
204+ f'"COMP_WORDS={ words } $*" { root } 2>/dev/null)'
192205 )
193206
194207
@@ -221,7 +234,7 @@ def _overrides_shell_complete(param_type: click.ParamType) -> bool:
221234 return type (param_type ).shell_complete is not click .ParamType .shell_complete
222235
223236
224- def _param_action (param : Parameter , prog_name : str ) -> list [str ]:
237+ def _param_action (param : Parameter , command_path : tuple [ str , ...] ) -> list [str ]:
225238 """Resolve the Carapace completion action for one parameter.
226239
227240 An explicit ``shell_complete=`` callback always routes to the dynamic macro;
@@ -230,12 +243,12 @@ def _param_action(param: Parameter, prog_name: str) -> list[str]:
230243 yields an empty action (no completion offered).
231244 """
232245 if getattr (param , "_custom_shell_complete" , None ) is not None :
233- return [_dynamic_action (prog_name )]
246+ return [_dynamic_action (command_path )]
234247 static = _static_action (param .type )
235248 if static is not None :
236249 return static
237250 if _overrides_shell_complete (param .type ):
238- return [_dynamic_action (prog_name )]
251+ return [_dynamic_action (command_path )]
239252 return []
240253
241254
@@ -378,14 +391,15 @@ def _add_option(
378391 param : Parameter ,
379392 * ,
380393 persistent : bool ,
381- root_name : str ,
394+ command_path : tuple [ str , ...] ,
382395) -> None :
383396 """Encode one Click option into ``flags``/``persistentflags`` and completion.
384397
385398 A boolean flag with a secondary spelling (``--foo`` / ``--no-foo``) is split
386399 into two independent Carapace flags, since the spec has no negation primitive.
387400 Only value-taking options contribute a ``completion.flag`` action. Dynamic
388- actions reference ``root_name``, the binary Carapace dispatches on.
401+ actions reference ``command_path``, the chain of command names down to this
402+ command (see :func:`_dynamic_action`).
389403 """
390404 flags = node .persistentflags if persistent else node .flags
391405 description = _clean_description (getattr (param , "help" , None ))
@@ -404,7 +418,7 @@ def _add_option(
404418 )
405419
406420 if value :
407- action = _param_action (param , root_name )
421+ action = _param_action (param , command_path )
408422 if action :
409423 node .completion .flag [_flag_name (param .opts )] = action
410424
@@ -416,7 +430,7 @@ def extract_carapace_command(
416430 is_root : bool ,
417431 default_opts : frozenset [str ],
418432 inherited_opts : frozenset [str ],
419- root_name : str ,
433+ command_path : tuple [ str , ...] ,
420434) -> CarapaceCommand :
421435 """Build a :class:`CarapaceCommand` from a Click command and its context.
422436
@@ -428,8 +442,10 @@ def extract_carapace_command(
428442 command; on the root, options drawn from it become ``persistentflags``.
429443 ``inherited_opts`` is what an ancestor actually published as persistent, so a
430444 subcommand drops exactly those (Carapace already offers them) and keeps the
431- rest, including a same-named option the root never carried. ``root_name`` is
432- the binary Carapace dispatches on, used to build dynamic callback macros.
445+ rest, including a same-named option the root never carried. ``command_path``
446+ is the chain of command names from the root down to this command, grown by one
447+ name per recursion and baked into dynamic callback macros (see
448+ :func:`_dynamic_action`).
433449 """
434450 node = CarapaceCommand (
435451 name = ctx .info_name or command .name or "" ,
@@ -442,7 +458,7 @@ def extract_carapace_command(
442458 persistent_spellings = set (inherited_opts )
443459 for param in command .get_params (ctx ):
444460 if isinstance (param , click .Argument ):
445- action = _param_action (param , root_name )
461+ action = _param_action (param , command_path )
446462 if param .nargs == - 1 :
447463 node .completion .positionalany = action
448464 else :
@@ -452,13 +468,13 @@ def extract_carapace_command(
452468 if is_root and set (param .opts ) <= default_opts :
453469 # A root default option: publish it once as persistent so every
454470 # subcommand inherits it, and remember its spellings to skip below.
455- _add_option (node , param , persistent = True , root_name = root_name )
471+ _add_option (node , param , persistent = True , command_path = command_path )
456472 persistent_spellings .update (param_spellings (param ))
457473 elif set (param .opts ) <= inherited_opts :
458474 # Already offered by an ancestor's persistent flags: do not repeat.
459475 continue
460476 else :
461- _add_option (node , param , persistent = False , root_name = root_name )
477+ _add_option (node , param , persistent = False , command_path = command_path )
462478
463479 node .completion .positional = positional
464480 node .exclusiveflags = _exclusive_flag_groups (command )
@@ -473,7 +489,7 @@ def extract_carapace_command(
473489 is_root = False ,
474490 default_opts = default_opts ,
475491 inherited_opts = child_inherited ,
476- root_name = root_name ,
492+ command_path = command_path + ( sub_name ,) ,
477493 )
478494 )
479495
@@ -500,7 +516,7 @@ def to_carapace_spec(
500516 is_root = True ,
501517 default_opts = _default_param_opts (),
502518 inherited_opts = frozenset (),
503- root_name = name ,
519+ command_path = ( name ,) ,
504520 )
505521 # The root node's name follows the program name, not the context's.
506522 node .name = name
0 commit comments