Skip to content

Commit

Permalink
kresctl: tab-completion: implement suggestions/completion for first a…
Browse files Browse the repository at this point in the history
…rgument
  • Loading branch information
Frantisek Tobias committed Oct 8, 2024
1 parent 64f66e5 commit 46f46e0
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 37 deletions.
12 changes: 11 additions & 1 deletion python/knot_resolver/client/commands/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from knot_resolver.utils.modeling.exceptions import AggregateDataValidationError, DataValidationError
from knot_resolver.utils.modeling.parsing import DataFormat, parse_json
from knot_resolver.utils.requests import request
from argparse import _SubParsersAction


class CacheOperations(Enum):
Expand Down Expand Up @@ -99,7 +100,16 @@ def register_args_subparser(

@staticmethod
def completion(args: List[str], parser: argparse.ArgumentParser) -> CompWords:
return {}
words = dict()
for action in parser._actions:
if isinstance(action, _SubParsersAction):
if action.choices is not None:
for choice in action.choices:
words[choice] = action.choices.get(choice)
else:
for opt in action.option_strings:
words[opt] = action.help
return words

def run(self, args: CommandArgs) -> None:
if not self.operation:
Expand Down
106 changes: 70 additions & 36 deletions python/knot_resolver/client/commands/completion.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,46 @@
from typing import List, Tuple, Type

from knot_resolver.client.command import Command, CommandArgs, CompWords, register_command
from argparse import _SubParsersAction


class Shells(Enum):
BASH = 0
FISH = 1


def parser_words(actions):
words = dict()
for action in actions:
if isinstance(action, _SubParsersAction):
if action.choices is not None:
for choice in action.choices:
words[choice] = action.choices.get(choice)
else:
for opt in action.option_strings:
words[opt] = action.help

return words


def subparser_by_name(uarg: str, actions) -> argparse.ArgumentParser | None:
for action in actions:
if isinstance(action, _SubParsersAction):
if action.choices is not None:
for choice in action.choices:
# if uarg == choice:
if uarg in choice:
return action.choices.get(choice)
return None


def subparser_command(subparser: argparse.ArgumentParser) -> Command:
com_class: Command | None = subparser._defaults.get("command")
# NOTE: This is just a temporary bandage to silence pyright
assert(com_class is not None)
return com_class


@register_command
class CompletionCommand(Command):
def __init__(self, namespace: argparse.Namespace) -> None:
Expand Down Expand Up @@ -57,39 +90,40 @@ def completion(args: List[str], parser: argparse.ArgumentParser) -> CompWords:
return words

def run(self, args: CommandArgs) -> None:
pass
# subparsers = args.parser._subparsers
# words: CompWords = {}

# if subparsers:
# words = parser_words(subparsers._actions)

# uargs = iter(self.comp_args)
# for uarg in uargs:
# subparser = subparser_by_name(uarg, subparsers._actions) # pylint: disable=W0212

# if subparser:
# cmd: Command = subparser_command(subparser)
# subparser_args = self.comp_args[self.comp_args.index(uarg) + 1 :]
# if subparser_args:
# words = cmd.completion(subparser_args, subparser)
# break
# elif uarg in ["-s", "--socket"]:
# # if arg is socket config, skip next arg
# next(uargs)
# continue
# elif uarg in words:
# # uarg is walid arg, continue
# continue
# else:
# raise ValueError(f"unknown argument: {uarg}")

# # print completion words
# # based on required bash/fish shell format
# if self.shell == Shells.BASH:
# print(" ".join(words))
# elif self.shell == Shells.FISH:
# # TODO: FISH completion implementation
# pass
# else:
# raise ValueError(f"unexpected value of {Shells}: {self.shell}")
subparsers = args.parser._subparsers
words: CompWords = {}

if subparsers:
words = parser_words(subparsers._actions)

uargs = iter(self.comp_args)
# skip kresctl
next(uargs)
for uarg in uargs:
subparser = subparser_by_name(uarg, subparsers._actions) # pylint: disable=W0212

if subparser:
cmd: Command = subparser_command(subparser)
subparser_args = self.comp_args[self.comp_args.index(uarg) + 1 :]
if subparser_args:
words = cmd.completion(subparser_args, subparser)
break
elif uarg in ["-s", "--socket"]:
# if arg is socket config, skip next arg
next(uargs)
continue
elif uarg in words:
# uarg is walid arg, continue
continue
else:
raise ValueError(f"unknown argument: {uarg}")

# print completion words
# based on required bash/fish shell format
if self.shell == Shells.BASH:
print(" ".join(words))
elif self.shell == Shells.FISH:
# TODO: FISH completion implementation
pass
else:
raise ValueError(f"unexpected value of {Shells}: {self.shell}")

0 comments on commit 46f46e0

Please sign in to comment.