From 56bf368ac66c632a7497944ee4c3431137b648e3 Mon Sep 17 00:00:00 2001 From: Frantisek Tobias Date: Fri, 11 Oct 2024 09:12:34 +0200 Subject: utils/shell-completion/client.bash: skip program name in COMP_WORDS --- utils/shell-completion/client.bash | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'utils/shell-completion/client.bash') diff --git a/utils/shell-completion/client.bash b/utils/shell-completion/client.bash index b3c19419..3a946dc4 100644 --- a/utils/shell-completion/client.bash +++ b/utils/shell-completion/client.bash @@ -13,9 +13,9 @@ _kresctl_completion() if [[ -z "$cur" ]] then # no word to complete, return all posible options - opts=$(kresctl completion --bash --space "${COMP_WORDS}") + opts=$(kresctl completion --bash --space "${cmd_words[@]:1}") else - opts=$(kresctl completion --bash "${COMP_WORDS}") + opts=$(kresctl completion --bash "${cmd_words[@]:1}") fi # if there is no completion from kresctl -- cgit v1.2.3 From ae416c62217a5421638440996462dd432742a83b Mon Sep 17 00:00:00 2001 From: Frantisek Tobias Date: Tue, 15 Oct 2024 12:41:52 +0200 Subject: utils/shell-completion/client.bash: compgen escape '--' arguments and minor adjustments --- utils/shell-completion/client.bash | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) (limited to 'utils/shell-completion/client.bash') diff --git a/utils/shell-completion/client.bash b/utils/shell-completion/client.bash index 3a946dc4..a1c8290b 100644 --- a/utils/shell-completion/client.bash +++ b/utils/shell-completion/client.bash @@ -3,31 +3,22 @@ _kresctl_completion() { COMPREPLY=() - local cur prev opts + local words="" + local space_arg="" + local cur="${COMP_WORDS[COMP_CWORD]}" - cur="${COMP_WORDS[COMP_CWORD]}" - prev="${COMP_WORDS[COMP_CWORD-1]}" - - # check if there is a word is empty - # that means there is a space after last non-empty word - if [[ -z "$cur" ]] - then - # no word to complete, return all posible options - opts=$(kresctl completion --bash --space "${cmd_words[@]:1}") - else - opts=$(kresctl completion --bash "${cmd_words[@]:1}") + # if the current word is empty + # we need to inform the kresctl client about it + if [[ -z "$cur" ]]; then + space_arg="--space" fi - # if there is no completion from kresctl - # auto-complete just directories and files - if [[ -z "$opts" ]] - then - COMPREPLY=($(compgen -d -f "${cur}")) - else - COMPREPLY=( $(compgen -W "${opts}" ${cur}) ) - fi + # get words from the kresctl client + words=$(kresctl completion --bash ${space_arg} --args "${COMP_WORDS[@]:1}") + + COMPREPLY=($(compgen -W "${words}" -- "${cur}")) return 0 } -complete -o filenames -o dirnames -F _kresctl_completion kresctl +complete -o filenames -o dirnames -o nosort -F _kresctl_completion kresctl -- cgit v1.2.3 From 06f06caa559b46931b8be63bffe8bd79baf0b483 Mon Sep 17 00:00:00 2001 From: Aleš Mrázek Date: Mon, 16 Dec 2024 18:04:47 +0100 Subject: python: client: command: move the getting of completion words to function - #dirnames# and #filenames# words to indicate that we want complete also files and dirs --- python/knot_resolver/client/command.py | 95 ++++++++++++++++++---- python/knot_resolver/client/commands/cache.py | 4 +- python/knot_resolver/client/commands/completion.py | 67 ++------------- python/knot_resolver/client/commands/config.py | 4 +- python/knot_resolver/client/commands/convert.py | 6 +- python/knot_resolver/client/commands/debug.py | 19 +++-- python/knot_resolver/client/commands/help.py | 4 +- python/knot_resolver/client/commands/metrics.py | 4 +- python/knot_resolver/client/commands/schema.py | 4 +- python/knot_resolver/client/commands/validate.py | 4 +- utils/shell-completion/client.bash | 27 +++--- 11 files changed, 123 insertions(+), 115 deletions(-) (limited to 'utils/shell-completion/client.bash') diff --git a/python/knot_resolver/client/command.py b/python/knot_resolver/client/command.py index 5a0d11a7..e4eddf08 100644 --- a/python/knot_resolver/client/command.py +++ b/python/knot_resolver/client/command.py @@ -1,7 +1,7 @@ import argparse from abc import ABC, abstractmethod # pylint: disable=[no-name-in-module] from pathlib import Path -from typing import Any, Dict, List, Optional, Tuple, Type, TypeVar +from typing import Dict, List, Optional, Tuple, Type, TypeVar from urllib.parse import quote from knot_resolver.constants import API_SOCK_FILE, CONFIG_FILE @@ -14,33 +14,96 @@ T = TypeVar("T", bound=Type["Command"]) CompWords = Dict[str, Optional[str]] +COMP_DIRNAMES = "#dirnames#" +COMP_FILENAMES = "#filenames#" + _registered_commands: List[Type["Command"]] = [] -def get_subparsers_words(subparser_actions: List[argparse.Action]) -> CompWords: +def get_parser_action(name: str, parser_actions: List[argparse.Action]) -> Optional[argparse.Action]: + for action in parser_actions: + if (action.choices and name in action.choices) or (action.option_strings and name in action.option_strings): + return action + return None + + +def get_subparser_command(subparser: argparse.ArgumentParser) -> Optional["Command"]: + if "command" in subparser._defaults: # noqa: SLF001 + return subparser._defaults["command"] # noqa: SLF001 + return None + + +def comp_get_actions_words(parser_actions: List[argparse.Action]) -> CompWords: words: CompWords = {} - for action in subparser_actions: + for action in parser_actions: if isinstance(action, argparse._SubParsersAction) and action.choices: # noqa: SLF001 for choice, parser in action.choices.items(): - words[choice] = parser.description - else: + words[choice] = parser.description if isinstance(parser, argparse.ArgumentParser) else None + elif action.option_strings: for opt in action.option_strings: words[opt] = action.help + elif not action.option_strings and action.choices: + for choice in action.choices: + words[choice] = action.help + elif not action.option_strings and not action.choices: + words[COMP_DIRNAMES] = None + words[COMP_FILENAMES] = None return words -def get_action_by_name(name: str, parser_actions: List[argparse.Action]) -> Optional[argparse.Action]: - for action in parser_actions: - if (action.choices and name in action.choices) or (action.option_strings and name in action.option_strings): - return action - return None - +def comp_get_words(args: List[str], parser_actions: List[argparse.Action]) -> CompWords: # noqa: PLR0912 + words: CompWords = comp_get_actions_words(parser_actions) + nargs = len(args) + + skip_arg = False + for i, arg in enumerate(args): + action: Optional[argparse.Action] = get_parser_action(arg, parser_actions) # noqa: SLF001 + + if skip_arg: + skip_arg = False + continue + + if not action: + words = comp_get_actions_words(parser_actions) + continue + + if i + 1 >= nargs: + return words + + # if not action or action is HelpAction or VersionAction + if isinstance(action, (argparse._HelpAction, argparse._VersionAction)): # noqa: SLF001 + words = {} + break + + # if action is StoreTrueAction or StoreFalseAction + if isinstance(action, argparse._StoreConstAction): # noqa: SLF001 + continue + + # if action is StoreAction + if isinstance(action, argparse._StoreAction): # noqa: SLF001 + choices = {} + if action.choices: + for choice in action.choices: + choices[choice] = action.help + else: + choices[COMP_DIRNAMES] = None + choices[COMP_FILENAMES] = None + words = choices + skip_arg = True + continue + + # if action is SubParserAction + if isinstance(action, argparse._SubParsersAction): # noqa: SLF001 + subparser = action.choices[arg] + cmd = get_subparser_command(subparser) + return cmd.completion(args[i + 1 :], subparser) if cmd else {} + + # delete already used args + for arg in args: + if arg in words.keys(): + del words[arg] -def get_subparser_command(subparser: argparse.ArgumentParser) -> "Command": - defaults: Dict[str, Any] = subparser._defaults # noqa: SLF001 - if "command" in defaults: - return defaults["command"] - raise ValueError(f"missing 'command' default for '{subparser.prog}' parser") + return words def register_command(cls: T) -> T: diff --git a/python/knot_resolver/client/commands/cache.py b/python/knot_resolver/client/commands/cache.py index d1116580..66ee77c9 100644 --- a/python/knot_resolver/client/commands/cache.py +++ b/python/knot_resolver/client/commands/cache.py @@ -3,7 +3,7 @@ import sys from enum import Enum from typing import Any, Dict, List, Optional, Tuple, Type -from knot_resolver.client.command import Command, CommandArgs, CompWords, get_subparsers_words, register_command +from knot_resolver.client.command import Command, CommandArgs, CompWords, comp_get_words, register_command from knot_resolver.datamodel.cache_schema import CacheClearRPCSchema from knot_resolver.utils.modeling.exceptions import AggregateDataValidationError, DataValidationError from knot_resolver.utils.modeling.parsing import DataFormat, parse_json @@ -99,7 +99,7 @@ class CacheCommand(Command): @staticmethod def completion(args: List[str], parser: argparse.ArgumentParser) -> CompWords: - return get_subparsers_words(parser._actions) # noqa: SLF001 + return comp_get_words(args, parser._actions) # noqa: SLF001 def run(self, args: CommandArgs) -> None: if not self.operation: diff --git a/python/knot_resolver/client/commands/completion.py b/python/knot_resolver/client/commands/completion.py index 1da8c516..f3101c19 100644 --- a/python/knot_resolver/client/commands/completion.py +++ b/python/knot_resolver/client/commands/completion.py @@ -1,14 +1,12 @@ import argparse from enum import Enum -from typing import List, Optional, Tuple, Type +from typing import List, Tuple, Type from knot_resolver.client.command import ( Command, CommandArgs, CompWords, - get_action_by_name, - get_subparser_command, - get_subparsers_words, + comp_get_words, register_command, ) @@ -23,12 +21,8 @@ class CompletionCommand(Command): def __init__(self, namespace: argparse.Namespace) -> None: super().__init__(namespace) self.shell: Shells = namespace.shell - self.space = namespace.space self.args: List[str] = namespace.args - if self.space: - self.args.append("") - @staticmethod def register_args_subparser( subparser: "argparse._SubParsersAction[argparse.ArgumentParser]", @@ -37,13 +31,6 @@ class CompletionCommand(Command): "completion", help="commands auto-completion", ) - completion.add_argument( - "--space", - help="space after last word, returns all possible folowing options", - dest="space", - action="store_true", - default=False, - ) shells_dest = "shell" shells = completion.add_mutually_exclusive_group() @@ -56,58 +43,14 @@ class CompletionCommand(Command): @staticmethod def completion(args: List[str], parser: argparse.ArgumentParser) -> CompWords: - return get_subparsers_words(parser._actions) # noqa: SLF001 + return comp_get_words(args, parser._actions) # noqa: SLF001 def run(self, args: CommandArgs) -> None: # noqa: PLR0912 - subparsers = args.parser._subparsers # noqa: SLF001 words: CompWords = {} + subparsers = args.parser._subparsers # noqa: SLF001 if subparsers: - words = get_subparsers_words(subparsers._actions) # noqa: SLF001 - - args_iter = iter(self.args) - for arg in args_iter: - action: Optional[argparse.Action] = get_action_by_name(arg, subparsers._actions) # noqa: SLF001 - - # if action is SubParserAction; complete using the command - if isinstance(action, argparse._SubParsersAction) and arg in action.choices: # noqa: SLF001 - # remove from words - for choice in action.choices: - del words[choice] - - subparser = action.choices[arg] - cmd = get_subparser_command(subparser) - - nargs = len(self.args) - index = self.args.index(arg) + 1 - # check that index is not out of args length - if index > nargs: - break - - # complete using the command - words = cmd.completion(self.args[index:], subparser) - break - - # if action is StoreAction; skip number of arguments - if isinstance(action, argparse._StoreAction) and arg in action.option_strings: # noqa: SLF001 - # remove from words - for option_string in action.option_strings: - del words[option_string] - - if action.nargs and isinstance(action.nargs, int): - for _ in range(action.nargs): - next(args_iter) - continue - - # remove other options from words - if action and action.option_strings: - for option_string in action.option_strings: - del words[option_string] - - # if 'arg' is not found in actions - # there is nothing to complete - if not action: - break + words = comp_get_words(self.args, subparsers._actions) # noqa: SLF001 # print completion words # based on required bash/fish shell format diff --git a/python/knot_resolver/client/commands/config.py b/python/knot_resolver/client/commands/config.py index dbf6ccaa..3ebc96ec 100644 --- a/python/knot_resolver/client/commands/config.py +++ b/python/knot_resolver/client/commands/config.py @@ -3,7 +3,7 @@ import sys from enum import Enum from typing import Any, Dict, List, Literal, Optional, Tuple, Type -from knot_resolver.client.command import Command, CommandArgs, CompWords, get_subparsers_words, register_command +from knot_resolver.client.command import Command, CommandArgs, CompWords, comp_get_words, register_command from knot_resolver.datamodel import KresConfig from knot_resolver.utils.modeling.parsing import DataFormat, parse_json, try_to_parse from knot_resolver.utils.requests import request @@ -170,7 +170,7 @@ class ConfigCommand(Command): @staticmethod def completion(args: List[str], parser: argparse.ArgumentParser) -> CompWords: - words = get_subparsers_words(parser._actions) # noqa: SLF001 + words = comp_get_words(args, parser._actions) # noqa: SLF001 if args is None: return words diff --git a/python/knot_resolver/client/commands/convert.py b/python/knot_resolver/client/commands/convert.py index b72af758..28a5be95 100644 --- a/python/knot_resolver/client/commands/convert.py +++ b/python/knot_resolver/client/commands/convert.py @@ -3,7 +3,7 @@ import sys from pathlib import Path from typing import List, Optional, Tuple, Type -from knot_resolver.client.command import Command, CommandArgs, CompWords, get_subparsers_words, register_command +from knot_resolver.client.command import Command, CommandArgs, CompWords, comp_get_words, register_command from knot_resolver.datamodel import KresConfig from knot_resolver.datamodel.globals import Context, reset_global_validation_context, set_global_validation_context from knot_resolver.utils.modeling import try_to_parse @@ -39,7 +39,6 @@ class ConvertCommand(Command): type=str, help="File with configuration in YAML or JSON format.", ) - convert.add_argument( "output_file", type=str, @@ -47,12 +46,11 @@ class ConvertCommand(Command): help="Optional, output file for converted configuration in Lua script. If not specified, converted configuration is printed.", default=None, ) - return convert, ConvertCommand @staticmethod def completion(args: List[str], parser: argparse.ArgumentParser) -> CompWords: - return get_subparsers_words(parser._actions) # noqa: SLF001 + return comp_get_words(args, parser._actions) # noqa: SLF001 def run(self, args: CommandArgs) -> None: with open(self.input_file, "r") as f: diff --git a/python/knot_resolver/client/commands/debug.py b/python/knot_resolver/client/commands/debug.py index 5d9a81df..00853c03 100644 --- a/python/knot_resolver/client/commands/debug.py +++ b/python/knot_resolver/client/commands/debug.py @@ -5,7 +5,7 @@ import sys from pathlib import Path from typing import List, Optional, Tuple, Type -from knot_resolver.client.command import Command, CommandArgs, CompWords, register_command +from knot_resolver.client.command import Command, CommandArgs, CompWords, comp_get_words, register_command from knot_resolver.utils import which from knot_resolver.utils.requests import request @@ -30,13 +30,6 @@ class DebugCommand(Command): "debug", help="Run GDB on the manager's subprocesses", ) - debug.add_argument( - "proc_type", - help="Optional, the type of process to debug. May be 'kresd' (default), 'gc', or 'all'.", - type=str, - nargs="?", - default="kresd", - ) debug.add_argument( "--sudo", dest="sudo", @@ -56,11 +49,19 @@ class DebugCommand(Command): action="store_true", default=False, ) + debug.add_argument( + "proc_type", + help="Optional, the type of process to debug. May be 'kresd', 'gc', or 'all'.", + choices=["kresd", "gc", "all"], + type=str, + nargs="?", + default="kresd", + ) return debug, DebugCommand @staticmethod def completion(args: List[str], parser: argparse.ArgumentParser) -> CompWords: - return {} + return comp_get_words(args, parser._actions) # noqa: SLF001 def run(self, args: CommandArgs) -> None: # noqa: PLR0912, PLR0915 if self.gdb is None: diff --git a/python/knot_resolver/client/commands/help.py b/python/knot_resolver/client/commands/help.py index 87306c2a..1db83fb6 100644 --- a/python/knot_resolver/client/commands/help.py +++ b/python/knot_resolver/client/commands/help.py @@ -1,7 +1,7 @@ import argparse from typing import List, Tuple, Type -from knot_resolver.client.command import Command, CommandArgs, CompWords, register_command +from knot_resolver.client.command import Command, CommandArgs, CompWords, comp_get_words, register_command @register_command @@ -14,7 +14,7 @@ class HelpCommand(Command): @staticmethod def completion(args: List[str], parser: argparse.ArgumentParser) -> CompWords: - return {} + return comp_get_words(args, parser._actions) # noqa: SLF001 @staticmethod def register_args_subparser( diff --git a/python/knot_resolver/client/commands/metrics.py b/python/knot_resolver/client/commands/metrics.py index 85cff258..eaf83090 100644 --- a/python/knot_resolver/client/commands/metrics.py +++ b/python/knot_resolver/client/commands/metrics.py @@ -2,7 +2,7 @@ import argparse import sys from typing import List, Optional, Tuple, Type -from knot_resolver.client.command import Command, CommandArgs, CompWords, get_subparsers_words, register_command +from knot_resolver.client.command import Command, CommandArgs, CompWords, comp_get_words, register_command from knot_resolver.utils.modeling.parsing import DataFormat, parse_json from knot_resolver.utils.requests import request @@ -44,7 +44,7 @@ class MetricsCommand(Command): @staticmethod def completion(args: List[str], parser: argparse.ArgumentParser) -> CompWords: - return get_subparsers_words(parser._actions) # noqa: SLF001 + return comp_get_words(args, parser._actions) # noqa: SLF001 def run(self, args: CommandArgs) -> None: response = request(args.socket, "GET", "metrics/prometheus" if self.prometheus else "metrics/json") diff --git a/python/knot_resolver/client/commands/schema.py b/python/knot_resolver/client/commands/schema.py index fa7465c1..fdb65bde 100644 --- a/python/knot_resolver/client/commands/schema.py +++ b/python/knot_resolver/client/commands/schema.py @@ -3,7 +3,7 @@ import json import sys from typing import List, Optional, Tuple, Type -from knot_resolver.client.command import Command, CommandArgs, CompWords, get_subparsers_words, register_command +from knot_resolver.client.command import Command, CommandArgs, CompWords, comp_get_words, register_command from knot_resolver.datamodel import kres_config_json_schema from knot_resolver.utils.requests import request @@ -35,7 +35,7 @@ class SchemaCommand(Command): @staticmethod def completion(args: List[str], parser: argparse.ArgumentParser) -> CompWords: - return get_subparsers_words(parser._actions) # noqa: SLF001 + return comp_get_words(args, parser._actions) # noqa: SLF001 def run(self, args: CommandArgs) -> None: if self.live: diff --git a/python/knot_resolver/client/commands/validate.py b/python/knot_resolver/client/commands/validate.py index 141a2003..2347519e 100644 --- a/python/knot_resolver/client/commands/validate.py +++ b/python/knot_resolver/client/commands/validate.py @@ -3,7 +3,7 @@ import sys from pathlib import Path from typing import List, Tuple, Type -from knot_resolver.client.command import Command, CommandArgs, CompWords, get_subparsers_words, register_command +from knot_resolver.client.command import Command, CommandArgs, CompWords, comp_get_words, register_command from knot_resolver.datamodel import KresConfig from knot_resolver.datamodel.globals import Context, reset_global_validation_context, set_global_validation_context from knot_resolver.utils.modeling import try_to_parse @@ -41,7 +41,7 @@ class ValidateCommand(Command): @staticmethod def completion(args: List[str], parser: argparse.ArgumentParser) -> CompWords: - return get_subparsers_words(parser._actions) # noqa: SLF001 + return comp_get_words(args, parser._actions) # noqa: SLF001 def run(self, args: CommandArgs) -> None: if self.input_file: diff --git a/utils/shell-completion/client.bash b/utils/shell-completion/client.bash index a1c8290b..0df57f31 100644 --- a/utils/shell-completion/client.bash +++ b/utils/shell-completion/client.bash @@ -1,24 +1,27 @@ -#/usr/bin/env bash +#!/usr/bin/env bash _kresctl_completion() { COMPREPLY=() + local args="" local words="" - local space_arg="" local cur="${COMP_WORDS[COMP_CWORD]}" + local opts=$(kresctl completion --bash --args "${COMP_WORDS[@]:1}") - # if the current word is empty - # we need to inform the kresctl client about it - if [[ -z "$cur" ]]; then - space_arg="--space" + # filter special opts + for opt in $opts + do + if [[ "$opt" == "#dirnames#" ]]; then + args="$args${args:+ }-d" + elif [[ "$opt" == "#filenames#" ]]; then + args="$args${args:+ }-f" + else + words="$words${words:+ }$opt" fi + done - # get words from the kresctl client - words=$(kresctl completion --bash ${space_arg} --args "${COMP_WORDS[@]:1}") - - COMPREPLY=($(compgen -W "${words}" -- "${cur}")) - + COMPREPLY=($(compgen $args -W "${words}" -- "${cur}")) return 0 } -complete -o filenames -o dirnames -o nosort -F _kresctl_completion kresctl +complete -o nosort -F _kresctl_completion kresctl -- cgit v1.2.3 From a48d032e8e3e1d66e0c9c050c710a410ea8cec7b Mon Sep 17 00:00:00 2001 From: Frantisek Tobias Date: Tue, 10 Dec 2024 08:23:44 +0100 Subject: kresctl: tab-completion: stop appending space after one config layer is completed --- python/knot_resolver/client/command.py | 42 +++++++++++++++++++++++++- python/knot_resolver/client/commands/config.py | 4 ++- utils/shell-completion/client.bash | 4 ++- 3 files changed, 47 insertions(+), 3 deletions(-) (limited to 'utils/shell-completion/client.bash') diff --git a/python/knot_resolver/client/command.py b/python/knot_resolver/client/command.py index e4eddf08..79dd9bec 100644 --- a/python/knot_resolver/client/command.py +++ b/python/knot_resolver/client/command.py @@ -1,7 +1,7 @@ import argparse from abc import ABC, abstractmethod # pylint: disable=[no-name-in-module] from pathlib import Path -from typing import Dict, List, Optional, Tuple, Type, TypeVar +from typing import Dict, List, Optional, Set, Tuple, Type, TypeVar from urllib.parse import quote from knot_resolver.constants import API_SOCK_FILE, CONFIG_FILE @@ -16,10 +16,50 @@ CompWords = Dict[str, Optional[str]] COMP_DIRNAMES = "#dirnames#" COMP_FILENAMES = "#filenames#" +COMP_NOSPACE = "#nospace#" _registered_commands: List[Type["Command"]] = [] +def get_mutually_exclusive_commands(parser: argparse.ArgumentParser) -> List[Set[str]]: + command_names: List[Set[str]] = [] + for group in parser._mutually_exclusive_groups: # noqa: SLF001 + command_names.append(set()) + for action in group._group_actions: # noqa: SLF001 + if action.option_strings: + command_names[-1].update(action.option_strings) + return command_names + + +def is_unique_and_new(arg: str, args: Set[str], exclusive: List[Set[str]], last: str) -> bool: + if arg not in args: + for excl in exclusive: + if arg in excl: + for cmd in excl: + if cmd in args: + return False + return True + + return arg == last + + +def get_subparsers_words( + subparser_actions: List[argparse.Action], args: Set[str], exclusive: List[Set[str]], last: str +) -> CompWords: + words: CompWords = {} + for action in subparser_actions: + if isinstance(action, argparse._SubParsersAction) and action.choices: # noqa: SLF001 + for choice, parser in action.choices.items(): + if is_unique_and_new(choice, args, exclusive, last): + words[choice] = parser.description + else: + for opt in action.option_strings: + if is_unique_and_new(opt, args, exclusive, last): + words[opt] = action.help + + return words + + def get_parser_action(name: str, parser_actions: List[argparse.Action]) -> Optional[argparse.Action]: for action in parser_actions: if (action.choices and name in action.choices) or (action.option_strings and name in action.option_strings): diff --git a/python/knot_resolver/client/commands/config.py b/python/knot_resolver/client/commands/config.py index de25f30a..fef3ad79 100644 --- a/python/knot_resolver/client/commands/config.py +++ b/python/knot_resolver/client/commands/config.py @@ -3,7 +3,7 @@ import sys from enum import Enum from typing import List, Literal, Optional, Tuple, Type -from knot_resolver.client.command import Command, CommandArgs, CompWords, comp_get_words, register_command +from knot_resolver.client.command import COMP_NOSPACE, Command, CommandArgs, CompWords, comp_get_words, register_command from knot_resolver.datamodel import KresConfig from knot_resolver.utils.modeling.parsing import DataFormat, parse_json, try_to_parse from knot_resolver.utils.requests import request @@ -124,6 +124,8 @@ class ConfigCommand(Command): if nargs > 1 and args[-2] in ["-p", "--path"]: words: CompWords = {} + words[COMP_NOSPACE] = None + path = args[-1] path_nodes = path.split("/") diff --git a/utils/shell-completion/client.bash b/utils/shell-completion/client.bash index 0df57f31..5cf66723 100644 --- a/utils/shell-completion/client.bash +++ b/utils/shell-completion/client.bash @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#/usr/bin/env bash _kresctl_completion() { @@ -15,6 +15,8 @@ _kresctl_completion() args="$args${args:+ }-d" elif [[ "$opt" == "#filenames#" ]]; then args="$args${args:+ }-f" + elif [[ "$opt" == "#nospace#" ]]; then + compopt -o nospace else words="$words${words:+ }$opt" fi -- cgit v1.2.3