From 78879d111c5ff8233c400694915ac39837cdee15 Mon Sep 17 00:00:00 2001 From: rsteube Date: Tue, 3 Oct 2023 12:34:00 +0200 Subject: [PATCH] expose ActionCommands Co-authored-by: maxlandon --- defaultActions.go | 34 +++++++ docs/src/SUMMARY.md | 1 + .../defaultActions/actionCommands.cast | 92 +++++++++++++++++++ .../carapace/defaultActions/actionCommands.md | 17 ++++ example/cmd/action.go | 2 + example/cmd/action_test.go | 38 ++++++++ example/cmd/subcommand.go | 22 +++++ example/cmd/subcommand_alias.go | 19 ++++ example/cmd/subcommand_group.go | 19 ++++ example/cmd/subcommand_hidden.go | 19 ++++ example/cmd/subcommand_hidden_visible.go | 19 ++++ internalActions.go | 25 +---- traverse.go | 60 ++++++------ 13 files changed, 313 insertions(+), 54 deletions(-) create mode 100644 docs/src/carapace/defaultActions/actionCommands.cast create mode 100644 docs/src/carapace/defaultActions/actionCommands.md create mode 100644 example/cmd/subcommand.go create mode 100644 example/cmd/subcommand_alias.go create mode 100644 example/cmd/subcommand_group.go create mode 100644 example/cmd/subcommand_hidden.go create mode 100644 example/cmd/subcommand_hidden_visible.go diff --git a/defaultActions.go b/defaultActions.go index 75799e386..e2c9a45d4 100644 --- a/defaultActions.go +++ b/defaultActions.go @@ -11,6 +11,7 @@ import ( "github.com/rsteube/carapace/internal/common" "github.com/rsteube/carapace/internal/config" + "github.com/rsteube/carapace/internal/env" "github.com/rsteube/carapace/internal/export" "github.com/rsteube/carapace/internal/man" "github.com/rsteube/carapace/pkg/match" @@ -480,3 +481,36 @@ func ActionPositional(cmd *cobra.Command) Action { return a.Invoke(c).ToA() }) } + +// ActionCommands completes (sub)commands of given command. +// `Context.Args` is used to traverse the command tree further down. Use `Action.Shift` to avoid this. +// +// carapace.Gen(helpCmd).PositionalAnyCompletion( +// carapace.ActionCommands(rootCmd), +// ) +func ActionCommands(cmd *cobra.Command) Action { + return ActionCallback(func(c Context) Action { + if len(c.Args) > 0 { + for _, subCommand := range cmd.Commands() { + for _, name := range append(subCommand.Aliases, subCommand.Name()) { + if name == c.Args[0] { // cmd.Find is too lenient + return ActionCommands(subCommand).Shift(1) + } + } + } + return ActionMessage("unknown subcommand %#v for %#v", c.Args[0], cmd.Name()) + } + + batch := Batch() + for _, subcommand := range cmd.Commands() { + if (!subcommand.Hidden || env.Hidden()) && subcommand.Deprecated == "" { + group := common.Group{Cmd: subcommand} + batch = append(batch, ActionStyledValuesDescribed(subcommand.Name(), subcommand.Short, group.Style()).Tag(group.Tag())) + for _, alias := range subcommand.Aliases { + batch = append(batch, ActionStyledValuesDescribed(alias, subcommand.Short, group.Style()).Tag(group.Tag())) + } + } + } + return batch.ToA() + }) +} diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index 062a6dc6e..ae439ba81 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -55,6 +55,7 @@ - [ToMultiPartsA](./carapace/invokedAction/toMultiPartsA.md) - [DefaultActions](./carapace/defaultActions.md) - [ActionCallback](./carapace/defaultActions/actionCallback.md) + - [ActionCommands](./carapace/defaultActions/actionCommands.md) - [ActionDirectories](./carapace/defaultActions/actionDirectories.md) - [ActionExecCommand](./carapace/defaultActions/actionExecCommand.md) - [ActionExecCommandE](./carapace/defaultActions/actionExecCommandE.md) diff --git a/docs/src/carapace/defaultActions/actionCommands.cast b/docs/src/carapace/defaultActions/actionCommands.cast new file mode 100644 index 000000000..f9190f842 --- /dev/null +++ b/docs/src/carapace/defaultActions/actionCommands.cast @@ -0,0 +1,92 @@ +{"version": 2, "width": 137, "height": 41, "timestamp": 1696332546, "env": {"SHELL": "elvish", "TERM": "tmux-256color"}} +[0.094236, "o", "\u001b[?7h\u001b[7m⏎\u001b[m \r \r\u001b[?7l\u001b[?2004h"] +[0.094797, "o", "\u001b[?25l\r???> ???> \r\u001b[5C\u001b[?25h\u001b[?25l\r\u001b[5C\u001b[K\r\u001b[5C\u001b[?25h"] +[0.108616, "o", "\u001b[?25l\r\r\u001b[5C\u001b[?25h"] +[0.108696, "o", "\u001b[?25l\r\u001b[K\r\n\u001b[0;1;36mcarapace\u001b[0;m on \u001b[0;1;35m expose-actioncommands\u001b[0;m \u001b[0;1;31m[!?]\u001b[0;m via \u001b[0;1;36m🐹 v1.21.1 \r\n\u001b[0;1;37mesh\u001b[0;m \u001b[0;1;32m❯\u001b[0;m \r\u001b[6C\u001b[?25h"] +[0.600642, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\u001b[6C\u001b[0;31me\u001b[0;m\r\u001b[7C\u001b[?25h"] +[0.600799, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\r\u001b[7C\u001b[?25h"] +[0.6021, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\r\u001b[7C\u001b[?25h\u001b[?25l\u001b[2A\r\r\n\r\n\r\u001b[7C\u001b[?25h"] +[0.603258, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\r\u001b[7C\u001b[?25h"] +[0.617333, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\r\u001b[7C\u001b[?25h"] +[0.617468, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\r\u001b[7C\u001b[?25h"] +[0.820823, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\u001b[7C\u001b[0;31mx\u001b[0;m\r\u001b[8C\u001b[?25h"] +[0.92734, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\u001b[6C\u001b[K\u001b[0;32mexa\u001b[0;m\r\u001b[9C\u001b[?25h"] +[0.927892, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\r\u001b[9C\u001b[?25h"] +[1.041719, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\u001b[6C\u001b[K\u001b[0;31mexam\u001b[0;m\r\u001b[10C\u001b[?25h\u001b[?25l\u001b[2A\r\r\n\r\n\r\u001b[10C\u001b[?25h"] +[1.109002, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\u001b[10C\u001b[0;31mp\u001b[0;m\r\u001b[11C\u001b[?25h\u001b[?25l\u001b[2A\r\r\n\r\n\r\u001b[11C\u001b[?25h"] +[1.209395, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\u001b[11C\u001b[0;31ml\u001b[0;m\r\u001b[12C\u001b[?25h\u001b[?25l\u001b[2A\r\r\n\r\n\r\u001b[12C\u001b[?25h"] +[1.310631, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\u001b[6C\u001b[K\u001b[0;32mexample\u001b[0;m\r\u001b[13C\u001b[?25h"] +[1.310753, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\r\u001b[13C\u001b[?25h"] +[1.393126, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\u001b[13C \r\u001b[14C\u001b[?25h\u001b[?25l\u001b[2A\r\r\n\r\n\r\u001b[14C\u001b[?25h"] +[1.564969, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\u001b[14C\u001b[0;4maction \r\n\u001b[0;1;37;45m COMPLETING argument \u001b[0;m \r\n\u001b[0;7;34maction\u001b[0;2;7m (action example) \r\n\u001b[0;34malias\u001b[0;2m (action example) \r\n\u001b[0;mchain\u001b[0;2m (shorthand chain) \r\n\u001b[0;mcompletion\u001b[0;2m (Generate the autocompletion script for the specified shell)\r\n\u001b[0;34mflag\u001b[0;2m (flag example) \r\n\u001b[0;mgroup\u001b[0;2m (group example) \r\n\u001b[0;mhelp\u001b[0;2m (Help about any command) \r\n\u001b[0;minterspersed\u001b[0;2m (interspersed example) \r\n\u001b[0;33mmodifier\u001b[0;2m (modifier example) \r\n\u001b[0;mmultiparts\u001b[0;2m (multiparts example) \r\n\u001b[0;35mplugin\u001b[0;2m (dynamic plugin command) \r\n\u001b[0;mspecial \r\nsubcommand\u001b[0;2m (subcommand example) \u001b[0;m\u001b[13A\r\u001b[22C\u001b[?25h"] +[1.565805, "o", "\u001b[?25l\u001b[3A\r\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\u001b[13A\r\u001b[22C\u001b[?25h"] +[2.387067, "o", "\u001b[?25l\u001b[3A\r\r\n\r\n\u001b[14C\u001b[K\u001b[0;4mchain \r\n\u001b[22C\u001b[0;mh\r\n\u001b[K\u001b[0;7mchain\u001b[0;2;7m (shorthand chain) \r\n\u001b[0;m\u001b[Kcompletion\u001b[0;2m (Generate the autocompletion script for the specified shell)\r\n\u001b[0;m\u001b[Khelp\u001b[0;2m (Help about any command) \u001b[0;m\r\n\u001b[J\u001b[A\u001b[3A\r\u001b[23C\u001b[?25h"] +[2.467791, "o", "\u001b[?25l\u001b[3A\r\r\n\r\n\u001b[15C\u001b[K\u001b[0;4mompletion \r\n\u001b[23C\u001b[0;me\r\n\u001b[1C\u001b[K\u001b[0;7mompletion\u001b[0;2;7m (Generate the autocompletion script for the specified shell)\r\n\u001b[0;m\u001b[Khelp\u001b[0;2m (Help about any command) \u001b[0;m\r\n\u001b[J\u001b[A\u001b[2A\r\u001b[24C\u001b[?25h"] +[2.467925, "o", "\u001b[?25l\u001b[3A\r\r\n\r\n\r\n\r\n\r\n\u001b[2A\r\u001b[24C\u001b[?25h"] +[3.031697, "o", "\u001b[?25l\u001b[3A\r\r\n\r\n\u001b[14C\u001b[K\u001b[0;4mhelp \r\n\r\n\u001b[0;m\u001b[Kcompletion\u001b[0;2m (Generate the autocompletion script for the specified shell)\r\n\u001b[0;m\u001b[K\u001b[0;7mhelp\u001b[0;2;7m (Help about any command) \u001b[0;m\u001b[2A\r\u001b[24C\u001b[?25h"] +[3.266605, "o", "\u001b[?25l\u001b[3A\r\r\n\r\n\u001b[14C\u001b[Khelp \r\n\u001b[J\u001b[A\r\u001b[19C\u001b[?25h"] +[3.779308, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\u001b[19C\u001b[0;4maction \r\n\u001b[0;1;37;45m COMPLETING argument \u001b[0;m \r\n\u001b[0;7;34maction\u001b[0;2;7m (action example) \r\n\u001b[0;34malias\u001b[0;2m (action example) \r\n\u001b[0;mchain\u001b[0;2m (shorthand chain) \r\n\u001b[0;mcompletion\u001b[0;2m (Generate the autocompletion script for the specified shell)\r\n\u001b[0;34mflag\u001b[0;2m (flag example) \r\n\u001b[0;mgroup\u001b[0;2m (group example) \r\n\u001b[0;mhelp\u001b[0;2m (Help about any command) \r\n\u001b[0;minterspersed\u001b[0;2m (interspersed example) \r\n\u001b[0;33mmodifier\u001b[0;2m (modifier example) \r\n\u001b[0;mmultiparts\u001b[0;2m (multiparts example) \r\n\u001b[0;35mplugin\u001b[0;2m (dynamic plugin command) \r\n\u001b[0;mspecial \r\nsubcommand\u001b[0;2m (subcommand example) \u001b[0;m\u001b[13A\r\u001b[22C\u001b[?25h"] +[3.780158, "o", "\u001b[?25l\u001b[3A\r\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\u001b[13A\r\u001b[22C\u001b[?25h"] +[4.80428, "o", "\u001b[?25l\u001b[3A\r\r\n\r\n\u001b[20C\u001b[K\u001b[0;4mlias \r\n\u001b[22C\u001b[0;ms\r\n\u001b[1C\u001b[K\u001b[0;7;34mlias\u001b[0;2;7m (action example) \r\n\u001b[0;m\u001b[Kchain\u001b[0;2m (shorthand chain) \r\n\u001b[1C\u001b[0;m\u001b[Kompletion\u001b[0;2m (Generate the autocompletion script for the specified shell)\r\n\u001b[0;m\u001b[Kinterspersed\u001b[0;2m (interspersed example) \r\n\u001b[0;m\u001b[Kmultiparts\u001b[0;2m (multiparts example) \r\n\u001b[0;m\u001b[Kspecial \r\n\u001b[Ksubcommand\u001b[0;2m (subcommand example) \u001b[0;m\r\n\u001b[J\u001b[A\u001b[7A\r\u001b[23C\u001b[?25h"] +[4.955258, "o", "\u001b[?25l\u001b[3A\r\r\n\r\n\u001b[19C\u001b[K\u001b[0;4msubcommand \r\n\u001b[23C\u001b[0;mu\r\n\u001b[K\u001b[0;7msubcommand\u001b[0;2;7m (subcommand example)\u001b[0;m\r\n\u001b[J\u001b[A\u001b[1A\r\u001b[24C\u001b[?25h\u001b[?25l\u001b[3A\r\r\n\r\n\r\n\r\n\u001b[1A\r\u001b[24C\u001b[?25h"] +[5.617643, "o", "\u001b[?25l\u001b[3A\r\r\n\r\n\u001b[19C\u001b[Ksubcommand \r\n\u001b[J\u001b[A\r\u001b[30C\u001b[?25h\u001b[?25l\u001b[2A\r\r\n\r\n\r\u001b[30C\u001b[?25h"] +[6.588664, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\u001b[30C\u001b[0;4ma1 \r\n\u001b[0;1;37;45m COMPLETING argument \u001b[0;m \r\n\u001b[0;7ma1\u001b[0;2;7m (subcommand with alias)\u001b[0;m a2\u001b[0;2m (subcommand with alias)\u001b[0;m alias\u001b[0;2m (subcommand with alias)\u001b[0;m \u001b[0;34mgroup\u001b[0;2m (subcommand with group)\u001b[0;m\u001b[1A\r\u001b[22C\u001b[?25h"] +[7.575675, "o", "\u001b[?25l\u001b[3A\r\r\n\r\n\u001b[31C\u001b[K\u001b[0;4m2 \r\n\r\n\u001b[0;m\u001b[Ka1\u001b[0;2m (subcommand with alias)\u001b[0;m \u001b[0;7ma2\u001b[0;2;7m (subcommand with alias)\u001b[0;m alias\u001b[0;2m (subcommand with alias)\u001b[0;m \u001b[0;34mgroup\u001b[0;2m (subcommand with group)\u001b[0;m\u001b[1A\r\u001b[22C\u001b[?25h"] +[7.738443, "o", "\u001b[?25l\u001b[3A\r\r\n\r\n\u001b[31C\u001b[K\u001b[0;4mlias \r\n\r\n\u001b[28C\u001b[0;m\u001b[Ka2\u001b[0;2m (subcommand with alias)\u001b[0;m \u001b[0;7malias\u001b[0;2;7m (subcommand with alias)\u001b[0;m \u001b[0;34mgroup\u001b[0;2m (subcommand with group)\u001b[0;m\u001b[1A\r\u001b[22C\u001b[?25h"] +[7.876879, "o", "\u001b[?25l\u001b[3A\r\r\n\r\n\u001b[30C\u001b[K\u001b[0;4mgroup \r\n\r\n\u001b[56C\u001b[0;m\u001b[Kalias\u001b[0;2m (subcommand with alias)\u001b[0;m \u001b[0;7;34mgroup\u001b[0;2;7m (subcommand with group)\u001b[0;m\u001b[1A\r\u001b[22C\u001b[?25h"] +[8.139634, "o", "\u001b[?25l\u001b[3A\r\r\n\r\n\u001b[30C\u001b[Kgroup \r\n\u001b[J\u001b[A\r\u001b[36C\u001b[?25h\u001b[?25l\u001b[2A\r\r\n\r\n\r\u001b[36C\u001b[?25h"] +[8.345816, "o", "\u001b[?25l\u001b[2A\r\u001b[0;2musage: \u001b[0;mhelp [command]\u001b[K\r\n\u001b[0;31merror:\u001b[0;m no candidates\u001b[K\r\n\u001b[K\r\n\u001b[K\u001b[0;1;36mcarapace\u001b[0;m on \u001b[0;1;35m expose-actioncommands\u001b[0;m \u001b[0;1;31m[!?]\u001b[0;m via \u001b[0;1;36m🐹 v1.21.1 \r\n\u001b[0;1;37mesh\u001b[0;m \u001b[0;1;32m❯\u001b[0;m \u001b[0;32mexample\u001b[0;m help subcommand group \r\u001b[36C\u001b[?25h\u001b[?25l\u001b[2A\r\r\n\r\n\r\u001b[36C\u001b[?25h"] +[9.182447, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\u001b[35C\u001b[K\r\u001b[35C\u001b[?25h"] +[9.310809, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\u001b[34C\u001b[K\r\u001b[34C\u001b[?25h"] +[9.463969, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\u001b[33C\u001b[K\r\u001b[33C\u001b[?25h"] +[9.611221, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\u001b[32C\u001b[K\r\u001b[32C\u001b[?25h"] +[9.779251, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\u001b[31C\u001b[K\r\u001b[31C\u001b[?25h"] +[9.779495, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\r\u001b[31C\u001b[?25h"] +[9.781015, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\r\u001b[31C\u001b[?25h"] +[9.781267, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\r\u001b[31C\u001b[?25h"] +[9.966292, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\u001b[30C\u001b[K\r\u001b[30C\u001b[?25h"] +[10.210334, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\u001b[30Ch\r\u001b[31C\u001b[?25h\u001b[?25l\u001b[2A\r\r\n\r\n\r\u001b[31C\u001b[?25h"] +[10.296325, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\u001b[31Ci\r\u001b[32C\u001b[?25h"] +[10.460204, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\u001b[32Cd\r\u001b[33C\u001b[?25h\u001b[?25l\u001b[2A\r\r\n\r\n\r\u001b[33C\u001b[?25h"] +[10.640842, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\u001b[33Cd\r\u001b[34C\u001b[?25h\u001b[?25l\u001b[2A\r\r\n\r\n\r\u001b[34C\u001b[?25h"] +[10.812994, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\u001b[34Ce\r\u001b[35C\u001b[?25h\u001b[?25l\u001b[2A\r\r\n\r\n\r\u001b[35C\u001b[?25h"] +[10.935553, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\u001b[35Cn\r\u001b[36C\u001b[?25h\u001b[?25l\u001b[2A\r\r\n\r\n\r\u001b[36C\u001b[?25h"] +[11.069793, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\u001b[36C \r\u001b[37C\u001b[?25h\u001b[?25l\u001b[2A\r\r\n\r\n\r\u001b[37C\u001b[?25h"] +[11.221802, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\u001b[37Cvisible \r\u001b[45C\u001b[?25h"] +[12.050845, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\u001b[44C\u001b[K\r\u001b[44C\u001b[?25h"] +[12.651453, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\u001b[43C\u001b[K\r\u001b[43C\u001b[?25h"] +[12.691589, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\u001b[42C\u001b[K\r\u001b[42C\u001b[?25h"] +[12.731299, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\u001b[41C\u001b[K\r\u001b[41C\u001b[?25h"] +[12.771711, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\u001b[40C\u001b[K\r\u001b[40C\u001b[?25h"] +[12.810859, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\u001b[39C\u001b[K\r\u001b[39C\u001b[?25h"] +[12.851552, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\u001b[38C\u001b[K\r\u001b[38C\u001b[?25h"] +[12.890893, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\u001b[37C\u001b[K\r\u001b[37C\u001b[?25h"] +[12.931143, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\u001b[36C\u001b[K\r\u001b[36C\u001b[?25h"] +[12.970876, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\u001b[35C\u001b[K\r\u001b[35C\u001b[?25h"] +[13.010886, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\u001b[34C\u001b[K\r\u001b[34C\u001b[?25h"] +[13.212033, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\u001b[33C\u001b[K\r\u001b[33C\u001b[?25h"] +[13.383856, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\u001b[32C\u001b[K\r\u001b[32C\u001b[?25h"] +[13.549261, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\u001b[31C\u001b[K\r\u001b[31C\u001b[?25h"] +[13.688414, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\u001b[30C\u001b[K\r\u001b[30C\u001b[?25h"] +[13.999022, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\u001b[30Cu\r\u001b[31C\u001b[?25h\u001b[?25l\u001b[2A\r\r\n\r\n\r\u001b[31C\u001b[?25h"] +[14.16606, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\u001b[31Cn\r\u001b[32C\u001b[?25h\u001b[?25l\u001b[2A\r\r\n\r\n\r\u001b[32C\u001b[?25h"] +[14.166814, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\r\u001b[32C\u001b[?25h"] +[14.167524, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\r\u001b[32C\u001b[?25h"] +[14.167796, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\r\u001b[32C\u001b[?25h"] +[14.378884, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\u001b[32Ck\r\u001b[33C\u001b[?25h"] +[14.516593, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\u001b[33Cn\r\u001b[34C\u001b[?25h\u001b[?25l\u001b[2A\r\r\n\r\n\r\u001b[34C\u001b[?25h"] +[14.618915, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\u001b[34Co\r\u001b[35C\u001b[?25h\u001b[?25l\u001b[2A\r\r\n\r\n\r\u001b[35C\u001b[?25h"] +[14.748914, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\u001b[35Cw\r\u001b[36C\u001b[?25h\u001b[?25l\u001b[2A\r\r\n\r\n\r\u001b[36C\u001b[?25h"] +[14.863251, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\u001b[36Cn\r\u001b[37C\u001b[?25h\u001b[?25l\u001b[2A\r\r\n\r\n\r\u001b[37C\u001b[?25h"] +[14.969171, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\u001b[37C \r\u001b[38C\u001b[?25h\u001b[?25l\u001b[2A\r\r\n\r\n\r\u001b[38C\u001b[?25h"] +[15.260303, "o", "\u001b[?25l\u001b[2A\r\u001b[0;31merror: \u001b[0;munknown subcommand \"unknown\" for \"subcommand\"\u001b[K\r\n\u001b[0;2musage: \u001b[0;mhelp [command]\u001b[K\r\n\u001b[0;31merror:\u001b[0;m no candidates\u001b[K\r\n\u001b[K\r\n\u001b[K\u001b[0;1;36mcarapace\u001b[0;m on \u001b[0;1;35m expose-actioncommands\u001b[0;m \u001b[0;1;31m[!?]\u001b[0;m via \u001b[0;1;36m🐹 v1.21.1 \r\n\u001b[0;1;37mesh\u001b[0;m \u001b[0;1;32m❯\u001b[0;m \u001b[0;32mexample\u001b[0;m help subcommand unknown \r\u001b[38C\u001b[?25h\u001b[?25l\u001b[2A\r\r\n\r\n\r\u001b[38C\u001b[?25h"] +[17.583113, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\u001b[6C\u001b[K\r\u001b[6C\u001b[?25h\u001b[?25l\u001b[2A\r\r\n\r\n\r\u001b[6C\u001b[?25h"] +[17.583276, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\r\u001b[6C\u001b[?25h"] +[17.584402, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\r\u001b[6C\u001b[?25h"] +[17.608591, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\r\u001b[6C\u001b[?25h"] +[17.608683, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\r\u001b[6C\u001b[?25h"] +[17.882376, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\u001b[6C\u001b[0;31me\u001b[0;m\r\u001b[7C\u001b[?25h"] +[18.144484, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\u001b[7C\u001b[0;31mx\u001b[0;m\r\u001b[8C\u001b[?25h\u001b[?25l\u001b[2A\r\r\n\r\n\r\u001b[8C\u001b[?25h"] +[18.299889, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\u001b[8C\u001b[0;31mi\u001b[0;m\r\u001b[9C\u001b[?25h\u001b[?25l\u001b[2A\r\r\n\r\n\r\u001b[9C\u001b[?25h"] +[18.394181, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\u001b[6C\u001b[K\u001b[0;32mexit\u001b[0;m\r\u001b[10C\u001b[?25h\u001b[?25l\u001b[2A\r\r\n\r\n\r\u001b[10C\u001b[?25h"] +[18.501852, "o", "\u001b[?25l\u001b[2A\r\r\n\r\n\r\n\r\u001b[?25h\u001b[?7h\u001b[?2004l\r"] diff --git a/docs/src/carapace/defaultActions/actionCommands.md b/docs/src/carapace/defaultActions/actionCommands.md new file mode 100644 index 000000000..b078bc2e3 --- /dev/null +++ b/docs/src/carapace/defaultActions/actionCommands.md @@ -0,0 +1,17 @@ +# ActionCommands + +[`ActionCommands`] completes (sub)commands of given command. + +> `Context.Args` is used to traverse the command tree further down. +> Use [Shift](../action/shift.md) to avoid this. + + +```go +carapace.Gen(helpCmd).PositionalAnyCompletion( + carapace.ActionCommands(cmd), +) +``` + +![](./actionCommands.cast) + +[`ActionCommands`]:https://pkg.go.dev/github.com/rsteube/carapace#ActionCommands diff --git a/example/cmd/action.go b/example/cmd/action.go index bd7acc9e0..95684c846 100644 --- a/example/cmd/action.go +++ b/example/cmd/action.go @@ -21,6 +21,7 @@ func init() { rootCmd.AddCommand(actionCmd) actionCmd.Flags().String("callback", "", "ActionCallback()") + actionCmd.Flags().String("commands", "", "ActionCommands()") actionCmd.Flags().String("directories", "", "ActionDirectories()") actionCmd.Flags().String("execcommand", "", "ActionExecCommand()") actionCmd.Flags().String("execcommandE", "", "ActionExecCommand()") @@ -48,6 +49,7 @@ func init() { } return carapace.ActionMessage("values flag is not set") }), + "commands": carapace.ActionCommands(rootCmd).Split(), "directories": carapace.ActionDirectories(), "execcommand": carapace.ActionExecCommand("git", "remote")(func(output []byte) carapace.Action { lines := strings.Split(string(output), "\n") diff --git a/example/cmd/action_test.go b/example/cmd/action_test.go index 4058c7a15..b280690f5 100644 --- a/example/cmd/action_test.go +++ b/example/cmd/action_test.go @@ -24,6 +24,44 @@ func TestAction(t *testing.T) { Expect(carapace.ActionMessage("values flag is not set"). Usage("ActionCallback()")) + s.Run("action", "--commands", "s"). + Expect(carapace.ActionValuesDescribed( + "special", "", + "subcommand", "subcommand example", + ).Suffix(" "). + NoSpace(). + Tag("other commands"). + Usage("ActionCommands()")) + + s.Run("action", "--commands", "subcommand "). + Expect(carapace.Batch( + carapace.ActionValuesDescribed( + "a1", "subcommand with alias", + "a2", "subcommand with alias", + "alias", "subcommand with alias", + ).Tag("other commands"), + carapace.ActionValuesDescribed( + "group", "subcommand with group", + ).Style(style.Blue).Tag("group commands"), + ).ToA(). + Prefix("subcommand "). + Suffix(" "). + NoSpace(). + Usage("ActionCommands()")) + + s.Run("action", "--commands", "subcommand unknown "). + Expect(carapace.ActionMessage(`unknown subcommand "unknown" for "subcommand"`).NoSpace(). + Usage("ActionCommands()")) + + s.Run("action", "--commands", "subcommand hidden "). + Expect(carapace.ActionValuesDescribed( + "visible", "visible subcommand of a hidden command", + ).Prefix("subcommand hidden "). + Suffix(" "). + NoSpace(). + Tag("commands"). + Usage("ActionCommands()")) + s.Run("action", "--values", "first", "--callback", ""). Expect(carapace.ActionMessage("values flag is set to: 'first'"). Usage("ActionCallback()")) diff --git a/example/cmd/subcommand.go b/example/cmd/subcommand.go new file mode 100644 index 000000000..a8b8d6dfc --- /dev/null +++ b/example/cmd/subcommand.go @@ -0,0 +1,22 @@ +package cmd + +import ( + "github.com/rsteube/carapace" + "github.com/spf13/cobra" +) + +var subcommandCmd = &cobra.Command{ + Use: "subcommand", + Short: "subcommand example", + Run: func(cmd *cobra.Command, args []string) {}, +} + +func init() { + carapace.Gen(subcommandCmd).Standalone() + + subcommandCmd.AddGroup( + &cobra.Group{ID: "group", Title: ""}, + ) + + rootCmd.AddCommand(subcommandCmd) +} diff --git a/example/cmd/subcommand_alias.go b/example/cmd/subcommand_alias.go new file mode 100644 index 000000000..5f706f4b7 --- /dev/null +++ b/example/cmd/subcommand_alias.go @@ -0,0 +1,19 @@ +package cmd + +import ( + "github.com/rsteube/carapace" + "github.com/spf13/cobra" +) + +var subcommand_aliasCmd = &cobra.Command{ + Use: "alias", + Short: "subcommand with alias", + Aliases: []string{"a1", "a2"}, + Run: func(cmd *cobra.Command, args []string) {}, +} + +func init() { + carapace.Gen(subcommand_aliasCmd).Standalone() + + subcommandCmd.AddCommand(subcommand_aliasCmd) +} diff --git a/example/cmd/subcommand_group.go b/example/cmd/subcommand_group.go new file mode 100644 index 000000000..f0b6285ad --- /dev/null +++ b/example/cmd/subcommand_group.go @@ -0,0 +1,19 @@ +package cmd + +import ( + "github.com/rsteube/carapace" + "github.com/spf13/cobra" +) + +var subcommand_groupCmd = &cobra.Command{ + Use: "group", + Short: "subcommand with group", + GroupID: "group", + Run: func(cmd *cobra.Command, args []string) {}, +} + +func init() { + carapace.Gen(subcommand_groupCmd).Standalone() + + subcommandCmd.AddCommand(subcommand_groupCmd) +} diff --git a/example/cmd/subcommand_hidden.go b/example/cmd/subcommand_hidden.go new file mode 100644 index 000000000..0478b8f62 --- /dev/null +++ b/example/cmd/subcommand_hidden.go @@ -0,0 +1,19 @@ +package cmd + +import ( + "github.com/rsteube/carapace" + "github.com/spf13/cobra" +) + +var subcommand_hiddenCmd = &cobra.Command{ + Use: "hidden", + Short: "hidden subcommand", + Hidden: true, + Run: func(cmd *cobra.Command, args []string) {}, +} + +func init() { + carapace.Gen(subcommand_hiddenCmd).Standalone() + + subcommandCmd.AddCommand(subcommand_hiddenCmd) +} diff --git a/example/cmd/subcommand_hidden_visible.go b/example/cmd/subcommand_hidden_visible.go new file mode 100644 index 000000000..a6433694b --- /dev/null +++ b/example/cmd/subcommand_hidden_visible.go @@ -0,0 +1,19 @@ +package cmd + +import ( + "github.com/rsteube/carapace" + "github.com/spf13/cobra" +) + +var subcommand_hidden_visibleCmd = &cobra.Command{ + Use: "visible", + Short: "visible subcommand of a hidden command", + Run: func(cmd *cobra.Command, args []string) {}, +} + +func init() { + carapace.Gen(subcommand_hidden_visibleCmd).Standalone() + + + subcommand_hiddenCmd.AddCommand(subcommand_hidden_visibleCmd) +} diff --git a/internalActions.go b/internalActions.go index 3a52e660a..e5b753aea 100644 --- a/internalActions.go +++ b/internalActions.go @@ -6,7 +6,6 @@ import ( "path/filepath" "strings" - "github.com/rsteube/carapace/internal/common" "github.com/rsteube/carapace/internal/env" "github.com/rsteube/carapace/internal/pflagfork" "github.com/rsteube/carapace/pkg/style" @@ -133,22 +132,6 @@ func actionFlags(cmd *cobra.Command) Action { }).Tag("flags") } -func actionSubcommands(cmd *cobra.Command) Action { - return ActionCallback(func(c Context) Action { - batch := Batch() - for _, subcommand := range cmd.Commands() { - if (!subcommand.Hidden || env.Hidden()) && subcommand.Deprecated == "" { - group := common.Group{Cmd: subcommand} - batch = append(batch, ActionStyledValuesDescribed(subcommand.Name(), subcommand.Short, group.Style()).Tag(group.Tag())) - for _, alias := range subcommand.Aliases { - batch = append(batch, ActionStyledValuesDescribed(alias, subcommand.Short, group.Style()).Tag(group.Tag())) - } - } - } - return batch.ToA() - }) -} - func initHelpCompletion(cmd *cobra.Command) { helpCmd, _, err := cmd.Find([]string{"help"}) if err != nil { @@ -162,12 +145,6 @@ func initHelpCompletion(cmd *cobra.Command) { } Gen(helpCmd).PositionalAnyCompletion( - ActionCallback(func(c Context) Action { - lastCmd, _, err := cmd.Find(c.Args) - if err != nil { - return ActionMessage(err.Error()) - } - return actionSubcommands(lastCmd) - }), + ActionCommands(cmd), ) } diff --git a/traverse.go b/traverse.go index 900b63844..2a5447718 100644 --- a/traverse.go +++ b/traverse.go @@ -10,20 +10,20 @@ import ( "github.com/spf13/cobra" ) -func traverse(c *cobra.Command, args []string) (Action, Context) { - LOG.Printf("traverse called for %#v with args %#v\n", c.Name(), args) - storage.preRun(c, args) +func traverse(cmd *cobra.Command, args []string) (Action, Context) { + LOG.Printf("traverse called for %#v with args %#v\n", cmd.Name(), args) + storage.preRun(cmd, args) if env.Lenient() { LOG.Printf("allowing unknown flags") - c.FParseErrWhitelist.UnknownFlags = true + cmd.FParseErrWhitelist.UnknownFlags = true } inArgs := []string{} // args consumed by current command inPositionals := []string{} // positionals consumed by current command var inFlag *pflagfork.Flag // last encountered flag that still expects arguments - c.LocalFlags() // TODO force c.mergePersistentFlags() which is missing from c.Flags() - fs := pflagfork.FlagSet{FlagSet: c.Flags()} + cmd.LocalFlags() // TODO force c.mergePersistentFlags() which is missing from c.Flags() + fs := pflagfork.FlagSet{FlagSet: cmd.Flags()} context := NewContext(args...) loop: @@ -47,7 +47,7 @@ loop: break loop // flag - case !c.DisableFlagParsing && strings.HasPrefix(arg, "-") && (fs.IsInterspersed() || len(inPositionals) == 0): + case !cmd.DisableFlagParsing && strings.HasPrefix(arg, "-") && (fs.IsInterspersed() || len(inPositionals) == 0): LOG.Printf("arg %#v is a flag\n", arg) inArgs = append(inArgs, arg) inFlag = fs.LookupArg(arg) @@ -58,22 +58,22 @@ loop: continue // subcommand - case subcommand(c, arg) != nil: + case subcommand(cmd, arg) != nil: LOG.Printf("arg %#v is a subcommand\n", arg) switch { - case c.DisableFlagParsing: - LOG.Printf("flag parsing disabled for %#v\n", c.Name()) + case cmd.DisableFlagParsing: + LOG.Printf("flag parsing disabled for %#v\n", cmd.Name()) default: - LOG.Printf("parsing flags for %#v with args %#v\n", c.Name(), inArgs) - if err := c.ParseFlags(inArgs); err != nil { + LOG.Printf("parsing flags for %#v with args %#v\n", cmd.Name(), inArgs) + if err := cmd.ParseFlags(inArgs); err != nil { return ActionMessage(err.Error()), context } - context.Args = c.Flags().Args() + context.Args = cmd.Flags().Args() } - return traverse(subcommand(c, arg), args[i+1:]) + return traverse(subcommand(cmd, arg), args[i+1:]) // positional default: @@ -105,34 +105,34 @@ loop: // TODO duplicated code switch { - case c.DisableFlagParsing: - LOG.Printf("flag parsing is disabled for %#v\n", c.Name()) + case cmd.DisableFlagParsing: + LOG.Printf("flag parsing is disabled for %#v\n", cmd.Name()) default: - LOG.Printf("parsing flags for %#v with args %#v\n", c.Name(), toParse) - if err := c.ParseFlags(toParse); err != nil { + LOG.Printf("parsing flags for %#v with args %#v\n", cmd.Name(), toParse) + if err := cmd.ParseFlags(toParse); err != nil { return ActionMessage(err.Error()), context } - context.Args = c.Flags().Args() + context.Args = cmd.Flags().Args() } switch { // dash argument - case common.IsDash(c): + case common.IsDash(cmd): LOG.Printf("completing dash for arg %#v\n", context.Value) - context.Args = c.Flags().Args()[c.ArgsLenAtDash():] + context.Args = cmd.Flags().Args()[cmd.ArgsLenAtDash():] LOG.Printf("context: %#v\n", context.Args) - return storage.getPositional(c, len(context.Args)), context + return storage.getPositional(cmd, len(context.Args)), context // flag argument case inFlag != nil && inFlag.Consumes(context.Value): LOG.Printf("completing flag argument of %#v for arg %#v\n", inFlag.Name, context.Value) context.Parts = inFlag.Args - return storage.getFlag(c, inFlag.Name), context + return storage.getFlag(cmd, inFlag.Name), context // flag - case !c.DisableFlagParsing && strings.HasPrefix(context.Value, "-") && (fs.IsInterspersed() || len(inPositionals) == 0): + case !cmd.DisableFlagParsing && strings.HasPrefix(context.Value, "-") && (fs.IsInterspersed() || len(inPositionals) == 0): if f := fs.LookupArg(context.Value); f != nil && len(f.Args) > 0 { LOG.Printf("completing optional flag argument for arg %#v with prefix %#v\n", context.Value, f.Prefix) @@ -140,21 +140,21 @@ loop: case "bool": return ActionValues("true", "false").StyleF(style.ForKeyword).Usage(f.Usage).Prefix(f.Prefix), context default: - return storage.getFlag(c, f.Name).Prefix(f.Prefix), context + return storage.getFlag(cmd, f.Name).Prefix(f.Prefix), context } } else if f != nil && fs.IsPosix() && !strings.HasPrefix(context.Value, "--") && !f.IsOptarg() && f.Prefix == context.Value { LOG.Printf("completing attached flag argument for arg %#v with prefix %#v\n", context.Value, f.Prefix) - return storage.getFlag(c, f.Name).Prefix(f.Prefix), context + return storage.getFlag(cmd, f.Name).Prefix(f.Prefix), context } LOG.Printf("completing flags for arg %#v\n", context.Value) - return actionFlags(c), context + return actionFlags(cmd), context // positional or subcommand default: LOG.Printf("completing positionals and subcommands for arg %#v\n", context.Value) - batch := Batch(storage.getPositional(c, len(context.Args))) - if c.HasAvailableSubCommands() && len(context.Args) == 0 { - batch = append(batch, actionSubcommands(c)) + batch := Batch(storage.getPositional(cmd, len(context.Args))) + if cmd.HasAvailableSubCommands() && len(context.Args) == 0 { + batch = append(batch, ActionCommands(cmd)) } return batch.ToA(), context }