From c31d4772a3e7281cd7167b0daf6b9b45b1c6ebcd Mon Sep 17 00:00:00 2001 From: Rui Chen Date: Wed, 1 May 2024 18:53:56 -0400 Subject: [PATCH 1/2] feat(completion): add zsh support Signed-off-by: Rui Chen --- linodecli/completion.py | 60 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 3 deletions(-) diff --git a/linodecli/completion.py b/linodecli/completion.py index 6a402eecf..3ec994103 100644 --- a/linodecli/completion.py +++ b/linodecli/completion.py @@ -15,19 +15,73 @@ def get_completions(ops, help_flag, action): return ( "linode-cli completion [SHELL]\n\n" "Prints shell completions for the requested shell to stdout.\n" - "Currently, only completions for bash and fish are available." + "Currently, only completions for bash, fish, and zsh are available." ) if action == "bash": return get_bash_completions(ops) if action == "fish": return get_fish_completions(ops) + if action == "zsh": + return get_zsh_completions(ops) return ( - "Completions are only available for bash and fish at this time.\n\n" + "Completions are only available for bash, fish, and zsh at this time.\n\n" "To retrieve these, please invoke as\n" - "`linode-cli completion bash` or `linode-cli completion fish`" + "`linode-cli completion bash`, `linode-cli completion fish`, or `linode-cli completion zsh`" ) +def get_zsh_completions(ops): + """ + Generates and returns Zsh shell completions based on the baked spec + """ + completion_template = Template( + """#compdef linode-cli linode lin + +# This is a generated file by Linode-CLI! Do not modify! +local -a subcommands +subcommands=( +'$subcommands --help' +) + +local -a command +local -a opts + +_arguments -C \\ + "1: :($subcommands)" \\ + '*:: :->subcmds' && return 0 + +if (( CURRENT == 2 )); then + case $words[1] in + $command_items + esac +fi +""" + ) + + command_template = Template( + """$command) + command=( + '$actions --help' + ) + _describe -t commands "$command command" command + ;;""" + ) + + command_blocks = [ + command_template.safe_substitute( + command=op, actions=" ".join(list(actions.keys())) + ) + for op, actions in ops.items() + ] + + rendered = completion_template.safe_substitute( + subcommands=" ".join(ops.keys()), + command_items="\n".join(command_blocks), + ) + + return rendered + + def get_fish_completions(ops): """ Generates and returns fish shell completions based on the baked spec From 1c9a81e3378fa468b82cc6e280c12ab2d37762b9 Mon Sep 17 00:00:00 2001 From: Rui Chen Date: Wed, 1 May 2024 18:54:08 -0400 Subject: [PATCH 2/2] feat(completion): add zsh completion test Signed-off-by: Rui Chen --- tests/unit/test_completion.py | 36 +++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/tests/unit/test_completion.py b/tests/unit/test_completion.py index 912bdd095..5b9894b1c 100644 --- a/tests/unit/test_completion.py +++ b/tests/unit/test_completion.py @@ -44,6 +44,32 @@ class TestCompletion: complete -F _linode_cli linode-cli complete -F _linode_cli linode complete -F _linode_cli lin""" + zsh_expected = """#compdef linode-cli linode lin + +# This is a generated file by Linode-CLI! Do not modify! +local -a subcommands +subcommands=( +'temp_key --help' +) + +local -a command +local -a opts + +_arguments -C \\ + "1: :(temp_key)" \\ + '*:: :->subcmds' && return 0 + +if (( CURRENT == 2 )); then + case $words[1] in + temp_key) + command=( + 'temp_action --help' + ) + _describe -t commands "temp_key command" command + ;; + esac +fi +""" def test_fish_completion(self, mocker): """ @@ -60,6 +86,13 @@ def test_bash_completion(self, mocker): actual = completion.get_bash_completions(self.ops) assert actual == self.bash_expected + def test_zsh_completion(self): + """ + Test if the Zsh completion renders correctly + """ + actual = completion.get_zsh_completions(self.ops) + assert actual == self.zsh_expected + def test_get_completions(self): """ Test get_completions for arg parse @@ -70,6 +103,9 @@ def test_get_completions(self): actual = completion.get_completions(self.ops, False, "fish") assert actual == self.fish_expected + actual = completion.get_completions(self.ops, False, "zsh") + assert actual == self.zsh_expected + actual = completion.get_completions(self.ops, False, "notrealshell") assert "invoke" in actual