From c706ac893c3c3f15333574d5488accfb13c1898f Mon Sep 17 00:00:00 2001 From: fluzzi Date: Mon, 18 Apr 2022 19:19:25 -0300 Subject: [PATCH] add fzf and change completion --- README.md | 5 +- connpy/__init__.py | 5 +- connpy/_version.py | 2 +- connpy/completion.py | 77 ++++++++++++++++++++ connpy/configfile.py | 2 +- connpy/connapp.py | 148 +++++++++++---------------------------- docs/connpy/index.html | 155 ++++++++++++----------------------------- setup.cfg | 4 ++ 8 files changed, 171 insertions(+), 227 deletions(-) create mode 100644 connpy/completion.py diff --git a/README.md b/README.md index 07b8cae..9411239 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ mynodes.router1.run(["term len 0". "show run"], folder = "/home/user/logs") - Nodes can be stored on @folder or @subfolder@folder to organize your devices. Then can be referenced using node@subfolder@folder or node@folder - If you have too many nodes. Get completion script using: conn config --completion. - Or use fzf installing pyfzf and running conn -f + Or use fzf installing pyfzf and running conn config --fzf true - Much more! ### Usage: @@ -85,13 +85,12 @@ positional arguments: ### Options: ``` -h, --help show this help message and exit + -v, --version Show version -a, --add Add new node[@subfolder][@folder] or [@subfolder]@folder -r, --del, --rm Delete node[@subfolder][@folder] or [@subfolder]@folder -e, --mod, --edit Modify node[@subfolder][@folder] -s, --show Show node[@subfolder][@folder] -d, --debug Display all conections steps - -v, --version Show version - ``` ### Commands: diff --git a/connpy/__init__.py b/connpy/__init__.py index aaf273c..3b3424f 100644 --- a/connpy/__init__.py +++ b/connpy/__init__.py @@ -10,6 +10,8 @@ Connpy is a connection manager that allows you to store nodes to connect them fa information. - Nodes can be stored on @folder or @subfolder@folder to organize your devices. Then can be referenced using node@subfolder@folder or node@folder + - If you have too many nodes. Get completion script using: conn config + --completion, or use fzf installing pyfzf and running conn config --fzf true - Much more! ### Usage @@ -24,12 +26,12 @@ positional arguments: Show all available connections globaly or in specified path Options: -h, --help show this help message and exit + -v, --version Show version -a, --add Add new node[@subfolder][@folder] or [@subfolder]@folder -r, --del, --rm Delete node[@subfolder][@folder] or [@subfolder]@folder -e, --mod, --edit Modify node[@subfolder][@folder] -s, --show Show node[@subfolder][@folder] -d, --debug Display all conections steps - -v, --version Show version Commands: profile Manage profiles @@ -131,4 +133,5 @@ __all__ = ["node", "nodes", "configfile", "connapp"] __author__ = "Federico Luzzi" __pdoc__ = { 'core': False, + 'completion': False, } diff --git a/connpy/_version.py b/connpy/_version.py index de5baf7..3d5fd8a 100644 --- a/connpy/_version.py +++ b/connpy/_version.py @@ -1,2 +1,2 @@ -__version__ = "2.0.4" +__version__ = "2.0.5" diff --git a/connpy/completion.py b/connpy/completion.py new file mode 100644 index 0000000..c791110 --- /dev/null +++ b/connpy/completion.py @@ -0,0 +1,77 @@ +import sys +import os +import json + +def _getallnodes(config): + #get all nodes on configfile + nodes = [] + layer1 = [k for k,v in config["connections"].items() if isinstance(v, dict) and v["type"] == "connection"] + folders = [k for k,v in config["connections"].items() if isinstance(v, dict) and v["type"] == "folder"] + nodes.extend(layer1) + for f in folders: + layer2 = [k + "@" + f for k,v in config["connections"][f].items() if isinstance(v, dict) and v["type"] == "connection"] + nodes.extend(layer2) + subfolders = [k for k,v in config["connections"][f].items() if isinstance(v, dict) and v["type"] == "subfolder"] + for s in subfolders: + layer3 = [k + "@" + s + "@" + f for k,v in config["connections"][f][s].items() if isinstance(v, dict) and v["type"] == "connection"] + nodes.extend(layer3) + return nodes + +def _getallfolders(config): + #get all folders on configfile + folders = ["@" + k for k,v in config["connections"].items() if isinstance(v, dict) and v["type"] == "folder"] + subfolders = [] + for f in folders: + s = ["@" + k + f for k,v in config["connections"][f[1:]].items() if isinstance(v, dict) and v["type"] == "subfolder"] + subfolders.extend(s) + folders.extend(subfolders) + return folders + +def main(): + home = os.path.expanduser("~") + defaultdir = home + '/.config/conn' + defaultfile = defaultdir + '/config.json' + jsonconf = open(defaultfile) + config = json.load(jsonconf) + nodes = _getallnodes(config) + folders = _getallfolders(config) + profiles = list(config["profiles"].keys()) + wordsnumber = int(sys.argv[1]) + words = sys.argv[3:] + if wordsnumber == 2: + strings=["--add", "--del", "--rm", "--edit", "--mod", "--show", "mv", "move", "ls", "list", "cp", "copy", "profile", "bulk", "config", "--help"] + strings.extend(nodes) + strings.extend(folders) + + elif wordsnumber == 3: + strings=[] + if words[0] == "profile": + strings=["--add", "--rm", "--del", "--edit", "--mod", "--show", "--help"] + if words[0] == "config": + strings=["--allow-uppercase", "--keepalive", "--completion", "--fzf", "--help"] + if words[0] in ["--mod", "--edit", "-e", "--show", "-s", "--add", "-a", "--rm", "--del", "-r"]: + strings=["profile"] + if words[0] in ["list", "ls"]: + strings=["profiles", "nodes", "folders"] + if words[0] in ["bulk", "mv", "cp", "copy"]: + strings=["--help"] + if words[0] in ["--rm", "--del", "-r"]: + strings.extend(folders) + if words[0] in ["--rm", "--del", "-r", "--mod", "--edit", "-e", "--show", "-s", "mv", "move", "cp", "copy"]: + strings.extend(nodes) + + elif wordsnumber == 4: + strings=[] + if words[0] == "profile" and words[1] in ["--rm", "--del", "-r", "--mod", "--edit", "-e", "--show", "-s"]: + strings.extend(profiles) + if words[1] == "profile" and words[0] in ["--rm", "--del", "-r", "--mod", "--edit", "-e", "--show", "-s"]: + strings.extend(profiles) + if words[0] == "config" and words[1] == "--completion": + strings=["bash", "zsh"] + if words[0] == "config" and words[1] in ["--fzf", "--allow-uppercase"]: + strings=["true", "false"] + + print(*strings) + +if __name__ == '__main__': + sys.exit(main()) diff --git a/connpy/configfile.py b/connpy/configfile.py index 9f2ea44..6a932b4 100755 --- a/connpy/configfile.py +++ b/connpy/configfile.py @@ -79,7 +79,7 @@ class configfile: def _createconfig(self, conf): #Create config file - defaultconfig = {'config': {'case': False, 'idletime': 30}, 'connections': {}, 'profiles': { "default": { "host":"", "protocol":"ssh", "port":"", "user":"", "password":"", "options":"", "logs":"" }}} + defaultconfig = {'config': {'case': False, 'idletime': 30, 'fzf': False}, 'connections': {}, 'profiles': { "default": { "host":"", "protocol":"ssh", "port":"", "user":"", "password":"", "options":"", "logs":"" }}} if not os.path.exists(conf): with open(conf, "w") as f: json.dump(defaultconfig, f, indent = 4) diff --git a/connpy/connapp.py b/connpy/connapp.py index 01d7e80..bea2b64 100755 --- a/connpy/connapp.py +++ b/connpy/connapp.py @@ -10,6 +10,10 @@ import sys import inquirer from .core import node from ._version import __version__ +try: + from pyfzf.pyfzf import FzfPrompt +except: + FzfPrompt = None #functions and classes @@ -34,6 +38,10 @@ class connapp: self.folders = self._getallfolders() self.profiles = list(self.config.profiles.keys()) self.case = self.config.config["case"] + try: + self.fzf = self.config.config["fzf"] + except: + self.fzf = False #DEFAULTPARSER defaultparser = argparse.ArgumentParser(prog = "conn", description = "SSH and Telnet connection manager", formatter_class=argparse.RawTextHelpFormatter) subparsers = defaultparser.add_subparsers(title="Commands") @@ -41,12 +49,12 @@ class connapp: nodeparser = subparsers.add_parser("node",usage=self._help("usage"), help=self._help("node"),epilog=self._help("end"), formatter_class=argparse.RawTextHelpFormatter) nodecrud = nodeparser.add_mutually_exclusive_group() nodeparser.add_argument("node", metavar="node|folder", nargs='?', default=None, action=self._store_type, type=self._type_node, help=self._help("node")) + nodecrud.add_argument("-v","--version", dest="action", action="store_const", help="Show version", const="version", default="connect") nodecrud.add_argument("-a","--add", dest="action", action="store_const", help="Add new node[@subfolder][@folder] or [@subfolder]@folder", const="add", default="connect") nodecrud.add_argument("-r","--del", "--rm", dest="action", action="store_const", help="Delete node[@subfolder][@folder] or [@subfolder]@folder", const="del", default="connect") nodecrud.add_argument("-e","--mod", "--edit", dest="action", action="store_const", help="Modify node[@subfolder][@folder]", const="mod", default="connect") nodecrud.add_argument("-s","--show", dest="action", action="store_const", help="Show node[@subfolder][@folder]", const="show", default="connect") nodecrud.add_argument("-d","--debug", dest="action", action="store_const", help="Display all conections steps", const="debug", default="connect") - nodecrud.add_argument("-v","--version", dest="action", action="store_const", help="Show version", const="version", default="connect") nodeparser.set_defaults(func=self._func_node) #PROFILEPARSER profileparser = subparsers.add_parser("profile", help="Manage profiles") @@ -75,9 +83,11 @@ class connapp: bulkparser.set_defaults(func=self._func_others) #CONFIGPARSER configparser = subparsers.add_parser("config", help="Manage app config") - configparser.add_argument("--allow-uppercase", dest="case", nargs=1, action=self._store_type, help="Allow case sensitive names", choices=["true","false"]) - configparser.add_argument("--keepalive", dest="idletime", nargs=1, action=self._store_type, help="Set keepalive time in seconds, 0 to disable", type=int, metavar="INT") - configparser.add_argument("--completion", dest="completion", nargs=1, choices=["bash","zsh"], action=self._store_type, help="Get terminal completion configuration for conn") + configcrud = configparser.add_mutually_exclusive_group(required=True) + configcrud.add_argument("--allow-uppercase", dest="case", nargs=1, action=self._store_type, help="Allow case sensitive names", choices=["true","false"]) + configcrud.add_argument("--fzf", dest="fzf", nargs=1, action=self._store_type, help="Use fzf for lists", choices=["true","false"]) + configcrud.add_argument("--keepalive", dest="idletime", nargs=1, action=self._store_type, help="Set keepalive time in seconds, 0 to disable", type=int, metavar="INT") + configcrud.add_argument("--completion", dest="completion", nargs=1, choices=["bash","zsh"], action=self._store_type, help="Get terminal completion configuration for conn") configparser.set_defaults(func=self._func_others) #Set default subparser and tune arguments commands = ["node", "profile", "mv", "move","copy", "cp", "bulk", "ls", "list", "config"] @@ -404,6 +414,11 @@ class connapp: args.data[0] = True elif args.data[0] == "false": args.data[0] = False + if args.command == "fzf": + if args.data[0] == "true": + args.data[0] = True + elif args.data[0] == "false": + args.data[0] = False if args.command == "idletime": if args.data[0] < 0: args.data[0] = 0 @@ -413,12 +428,20 @@ class connapp: def _choose(self, list, name, action): #Generates an inquirer list to pick - questions = [inquirer.List(name, message="Pick {} to {}:".format(name,action), choices=list, carousel=True)] - answer = inquirer.prompt(questions) - if answer == None: - return + if FzfPrompt and self.fzf: + fzf = FzfPrompt(executable_path="fzf-tmux") + answer = fzf.prompt(list, fzf_options="-d 25%") + if len(answer) == 0: + return + else: + return answer[0] else: - return answer[name] + questions = [inquirer.List(name, message="Pick {} to {}:".format(name,action), choices=list, carousel=True)] + answer = inquirer.prompt(questions) + if answer == None: + return + else: + return answer[name] def _host_validation(self, answers, current, regex = "^.+$"): #Validate hostname in inquirer when managing nodes @@ -689,121 +712,28 @@ class connapp: if type == "bashcompletion": return ''' #Here starts bash completion for conn -#You need jq installed in order to use this _conn() { - - DATADIR=$HOME/.config/conn - command -v jq >/dev/null 2>&1 && { - mapfile -t connections < <(jq -r ' .["connections"] | paths as $path | select(getpath($path) == "connection") | $path | [map(select(. != "type"))[-1,-2,-3]] | map(select(. !=null)) | join("@")' $DATADIR/config.json) - mapfile -t folders < <(jq -r ' .["connections"] | paths as $path | select(getpath($path) == "folder" or getpath($path) == "subfolder") | $path | [map(select(. != "type"))[-1,-2]] | map(select(. !=null)) | join("@")' $DATADIR/config.json) - mapfile -t profiles < <(jq -r '.["profiles"] | keys[]' $DATADIR/config.json) - } - if [ "${#COMP_WORDS[@]}" = "2" ]; then - strings="--add --del --rm --edit --mod --show mv move ls list cp copy profile bulk config --help" - strings="$strings ${connections[@]} ${folders[@]/#/@}" - COMPREPLY=($(compgen -W "$strings" -- "${COMP_WORDS[1]}")) - fi - if [ "${#COMP_WORDS[@]}" = "3" ]; then - strings="" - if [ "${COMP_WORDS[1]}" = "profile" ]; then strings="--add --rm --del --edit --mod --show --help"; fi - if [ "${COMP_WORDS[1]}" = "config" ]; then strings="--allow-uppercase --keepalive --completion --help"; fi - if [[ "${COMP_WORDS[1]}" =~ --mod|--edit|-e|--show|-s|--add|-a|--rm|--del|-r ]]; then strings="profile"; fi - if [[ "${COMP_WORDS[1]}" =~ list|ls ]]; then strings="profiles nodes folders"; fi - if [[ "${COMP_WORDS[1]}" =~ bulk|mv|move|cp|copy ]]; then strings="--help"; fi - if [[ "${COMP_WORDS[1]}" =~ --rm|--del|-r ]]; then strings="$strings ${folders[@]/#/@}"; fi - if [[ "${COMP_WORDS[1]}" =~ --rm|--del|-r|--mod|--edit|-e|mv|move|cp|copy|--show|-s ]]; then - strings="$strings ${connections[@]}" - fi - COMPREPLY=($(compgen -W "$strings" -- "${COMP_WORDS[2]}")) - fi - if [ "${#COMP_WORDS[@]}" = "4" ]; then - strings="" - if [ "${COMP_WORDS[1]}" = "profile" ]; then - if [[ "${COMP_WORDS[2]}" =~ --rm|--del|-r|--mod|--edit|-e|--show|-s ]] ; then - strings="$strings ${profiles[@]}" - fi - fi - if [ "${COMP_WORDS[2]}" = "profile" ]; then - if [[ "${COMP_WORDS[1]}" =~ --rm|--del|-r|--mod|--edit|-e|--show|-s ]] ; then - strings="$strings ${profiles[@]}" - fi - fi - if [ "${COMP_WORDS[1]}" = "config" ]; then - if [[ "${COMP_WORDS[2]}" = "--completion" ]] ; then - strings="bash zsh" - fi - fi - COMPREPLY=($(compgen -W "$strings" -- "${COMP_WORDS[3]}")) - fi + strings="$(connpy-completion-helper ${#COMP_WORDS[@]} ${COMP_WORDS[@]})" + COMPREPLY=($(compgen -W "$strings" -- "${COMP_WORDS[-1]}")) } complete -o nosort -F _conn conn complete -o nosort -F _conn connpy +#Here ends bash completion for conn ''' if type == "zshcompletion": return ''' -#Here starts zsh completion for conn -#You need jq installed in order to use this +#Here starts zsh completion for conn autoload -U compinit && compinit _conn() { - DATADIR=$HOME/.config/conn - local COMP_WORDS num - COMP_WORDS=( $words ) - num=${#COMP_WORDS[@]} - if [[ $words =~ '.* $' ]]; then - num=$(($num + 1)) - fi - command -v jq >/dev/null 2>&1 && { - x=`jq -r ' .["connections"] | paths as $path | select(getpath($path) == "connection") | $path | [map(select(. != "type"))[-1,-2,-3]] | map(select(. !=null)) | join("@")' $DATADIR/config.json` - connections=( $x ) - x=`jq -r ' .["connections"] | paths as $path | select(getpath($path) == "folder" or getpath($path) == "subfolder") | $path | [map(select(. != "type"))[-1,-2]] | map(select(. !=null)) | join("@")' $DATADIR/config.json | sed -e 's/^/@/'` - folders=( $x ) - x=`jq -r '.["profiles"] | keys[]' $DATADIR/config.json` - profiles=( $x ) - } - if [ "${num}" = "2" ]; then - strings="--add --del --rm --edit --mod --show mv move ls list cp copy profile bulk config --help" - strings="$strings ${connections[@]} ${folders[@]}" - compadd "$@" -- `echo $strings` - fi - if [ "${num}" = "3" ]; then - strings="" - if [ "${COMP_WORDS[2]}" = "profile" ]; then strings="--add --rm --del --edit --mod --show --help"; fi - if [ "${COMP_WORDS[2]}" = "config" ]; then strings="--allow-uppercase --keepalive --completion --help"; fi - if [[ "${COMP_WORDS[2]}" =~ '--mod|--edit|-e|--show|-s|--add|-a|--rm|--del|-r' ]]; then strings="profile"; fi - if [[ "${COMP_WORDS[2]}" =~ 'list|ls' ]]; then strings="profiles nodes folders"; fi - if [[ "${COMP_WORDS[2]}" =~ 'bulk|mv|move|cp|copy' ]]; then strings="--help"; fi - if [[ "${COMP_WORDS[2]}" =~ '--rm|--del|-r' ]]; then strings="$strings ${folders[@]}"; fi - if [[ "${COMP_WORDS[2]}" =~ '--rm|--del|-r|--mod|--edit|-e|mv|move|cp|copy|--show|-s' ]]; then - strings="$strings ${connections[@]}" - fi - compadd "$@" -- `echo $strings` - fi - if [ "${num}" = "4" ]; then - strings="" - if [ "${COMP_WORDS[2]}" = "profile" ]; then - if [[ "${COMP_WORDS[3]}" =~ '--rm|--del|-r|--mod|--edit|-e|--show|-s' ]] ; then - strings="$strings ${profiles[@]}" - fi - fi - if [ "${COMP_WORDS[3]}" = "profile" ]; then - if [[ "${COMP_WORDS[2]}" =~ '--rm|--del|-r|--mod|--edit|-e|--show|-s' ]] ; then - strings="$strings ${profiles[@]}" - fi - fi - if [ "${COMP_WORDS[2]}" = "config" ]; then - if [[ "${COMP_WORDS[3]}" = '--completion' ]] ; then - strings="bash zsh" - fi - fi - - compadd "$@" -- `echo $strings` - fi + strings=($(connpy-completion-helper ${#words} $words)) + compadd "$@" -- `echo $strings` } compdef _conn conn compdef _conn connpy +#Here ends zsh completion for conn ''' def _getallnodes(self): diff --git a/docs/connpy/index.html b/docs/connpy/index.html index c1982c5..baa6821 100644 --- a/docs/connpy/index.html +++ b/docs/connpy/index.html @@ -30,6 +30,8 @@ information. - Nodes can be stored on @folder or @subfolder@folder to organize your devices. Then can be referenced using node@subfolder@folder or node@folder +- If you have too many nodes. Get completion script using: conn config + --completion, or use fzf installing pyfzf and running conn config --fzf true - Much more!

Usage

@@ -43,12 +45,12 @@ positional arguments: Show all available connections globaly or in specified path Options: -h, --help show this help message and exit + -v, --version Show version -a, --add Add new node[@subfolder][@folder] or [@subfolder]@folder -r, --del, --rm Delete node[@subfolder][@folder] or [@subfolder]@folder -e, --mod, --edit Modify node[@subfolder][@folder] -s, --show Show node[@subfolder][@folder] -d, --debug Display all conections steps - -v, --version Show version Commands: profile Manage profiles @@ -144,6 +146,8 @@ Connpy is a connection manager that allows you to store nodes to connect them fa information. - Nodes can be stored on @folder or @subfolder@folder to organize your devices. Then can be referenced using node@subfolder@folder or node@folder + - If you have too many nodes. Get completion script using: conn config + --completion, or use fzf installing pyfzf and running conn config --fzf true - Much more! ### Usage @@ -158,12 +162,12 @@ positional arguments: Show all available connections globaly or in specified path Options: -h, --help show this help message and exit + -v, --version Show version -a, --add Add new node[@subfolder][@folder] or [@subfolder]@folder -r, --del, --rm Delete node[@subfolder][@folder] or [@subfolder]@folder -e, --mod, --edit Modify node[@subfolder][@folder] -s, --show Show node[@subfolder][@folder] -d, --debug Display all conections steps - -v, --version Show version Commands: profile Manage profiles @@ -265,6 +269,7 @@ __all__ = ["node", "nodes", "configfile", "connapp&# __author__ = "Federico Luzzi" __pdoc__ = { 'core': False, + 'completion': False, } @@ -384,7 +389,7 @@ __pdoc__ = { def _createconfig(self, conf): #Create config file - defaultconfig = {'config': {'case': False, 'idletime': 30}, 'connections': {}, 'profiles': { "default": { "host":"", "protocol":"ssh", "port":"", "user":"", "password":"", "options":"", "logs":"" }}} + defaultconfig = {'config': {'case': False, 'idletime': 30, 'fzf': False}, 'connections': {}, 'profiles': { "default": { "host":"", "protocol":"ssh", "port":"", "user":"", "password":"", "options":"", "logs":"" }}} if not os.path.exists(conf): with open(conf, "w") as f: json.dump(defaultconfig, f, indent = 4) @@ -644,6 +649,10 @@ __pdoc__ = { self.folders = self._getallfolders() self.profiles = list(self.config.profiles.keys()) self.case = self.config.config["case"] + try: + self.fzf = self.config.config["fzf"] + except: + self.fzf = False #DEFAULTPARSER defaultparser = argparse.ArgumentParser(prog = "conn", description = "SSH and Telnet connection manager", formatter_class=argparse.RawTextHelpFormatter) subparsers = defaultparser.add_subparsers(title="Commands") @@ -651,12 +660,12 @@ __pdoc__ = { nodeparser = subparsers.add_parser("node",usage=self._help("usage"), help=self._help("node"),epilog=self._help("end"), formatter_class=argparse.RawTextHelpFormatter) nodecrud = nodeparser.add_mutually_exclusive_group() nodeparser.add_argument("node", metavar="node|folder", nargs='?', default=None, action=self._store_type, type=self._type_node, help=self._help("node")) + nodecrud.add_argument("-v","--version", dest="action", action="store_const", help="Show version", const="version", default="connect") nodecrud.add_argument("-a","--add", dest="action", action="store_const", help="Add new node[@subfolder][@folder] or [@subfolder]@folder", const="add", default="connect") nodecrud.add_argument("-r","--del", "--rm", dest="action", action="store_const", help="Delete node[@subfolder][@folder] or [@subfolder]@folder", const="del", default="connect") nodecrud.add_argument("-e","--mod", "--edit", dest="action", action="store_const", help="Modify node[@subfolder][@folder]", const="mod", default="connect") nodecrud.add_argument("-s","--show", dest="action", action="store_const", help="Show node[@subfolder][@folder]", const="show", default="connect") nodecrud.add_argument("-d","--debug", dest="action", action="store_const", help="Display all conections steps", const="debug", default="connect") - nodecrud.add_argument("-v","--version", dest="action", action="store_const", help="Show version", const="version", default="connect") nodeparser.set_defaults(func=self._func_node) #PROFILEPARSER profileparser = subparsers.add_parser("profile", help="Manage profiles") @@ -685,9 +694,11 @@ __pdoc__ = { bulkparser.set_defaults(func=self._func_others) #CONFIGPARSER configparser = subparsers.add_parser("config", help="Manage app config") - configparser.add_argument("--allow-uppercase", dest="case", nargs=1, action=self._store_type, help="Allow case sensitive names", choices=["true","false"]) - configparser.add_argument("--keepalive", dest="idletime", nargs=1, action=self._store_type, help="Set keepalive time in seconds, 0 to disable", type=int, metavar="INT") - configparser.add_argument("--completion", dest="completion", nargs=1, choices=["bash","zsh"], action=self._store_type, help="Get terminal completion configuration for conn") + configcrud = configparser.add_mutually_exclusive_group(required=True) + configcrud.add_argument("--allow-uppercase", dest="case", nargs=1, action=self._store_type, help="Allow case sensitive names", choices=["true","false"]) + configcrud.add_argument("--fzf", dest="fzf", nargs=1, action=self._store_type, help="Use fzf for lists", choices=["true","false"]) + configcrud.add_argument("--keepalive", dest="idletime", nargs=1, action=self._store_type, help="Set keepalive time in seconds, 0 to disable", type=int, metavar="INT") + configcrud.add_argument("--completion", dest="completion", nargs=1, choices=["bash","zsh"], action=self._store_type, help="Get terminal completion configuration for conn") configparser.set_defaults(func=self._func_others) #Set default subparser and tune arguments commands = ["node", "profile", "mv", "move","copy", "cp", "bulk", "ls", "list", "config"] @@ -1014,6 +1025,11 @@ __pdoc__ = { args.data[0] = True elif args.data[0] == "false": args.data[0] = False + if args.command == "fzf": + if args.data[0] == "true": + args.data[0] = True + elif args.data[0] == "false": + args.data[0] = False if args.command == "idletime": if args.data[0] < 0: args.data[0] = 0 @@ -1023,12 +1039,20 @@ __pdoc__ = { def _choose(self, list, name, action): #Generates an inquirer list to pick - questions = [inquirer.List(name, message="Pick {} to {}:".format(name,action), choices=list, carousel=True)] - answer = inquirer.prompt(questions) - if answer == None: - return + if FzfPrompt and self.fzf: + fzf = FzfPrompt(executable_path="fzf-tmux") + answer = fzf.prompt(list, fzf_options="-d 25%") + if len(answer) == 0: + return + else: + return answer[0] else: - return answer[name] + questions = [inquirer.List(name, message="Pick {} to {}:".format(name,action), choices=list, carousel=True)] + answer = inquirer.prompt(questions) + if answer == None: + return + else: + return answer[name] def _host_validation(self, answers, current, regex = "^.+$"): #Validate hostname in inquirer when managing nodes @@ -1299,121 +1323,28 @@ __pdoc__ = { if type == "bashcompletion": return ''' #Here starts bash completion for conn -#You need jq installed in order to use this _conn() { - - DATADIR=$HOME/.config/conn - command -v jq >/dev/null 2>&1 && { - mapfile -t connections < <(jq -r ' .["connections"] | paths as $path | select(getpath($path) == "connection") | $path | [map(select(. != "type"))[-1,-2,-3]] | map(select(. !=null)) | join("@")' $DATADIR/config.json) - mapfile -t folders < <(jq -r ' .["connections"] | paths as $path | select(getpath($path) == "folder" or getpath($path) == "subfolder") | $path | [map(select(. != "type"))[-1,-2]] | map(select(. !=null)) | join("@")' $DATADIR/config.json) - mapfile -t profiles < <(jq -r '.["profiles"] | keys[]' $DATADIR/config.json) - } - if [ "${#COMP_WORDS[@]}" = "2" ]; then - strings="--add --del --rm --edit --mod --show mv move ls list cp copy profile bulk config --help" - strings="$strings ${connections[@]} ${folders[@]/#/@}" - COMPREPLY=($(compgen -W "$strings" -- "${COMP_WORDS[1]}")) - fi - if [ "${#COMP_WORDS[@]}" = "3" ]; then - strings="" - if [ "${COMP_WORDS[1]}" = "profile" ]; then strings="--add --rm --del --edit --mod --show --help"; fi - if [ "${COMP_WORDS[1]}" = "config" ]; then strings="--allow-uppercase --keepalive --completion --help"; fi - if [[ "${COMP_WORDS[1]}" =~ --mod|--edit|-e|--show|-s|--add|-a|--rm|--del|-r ]]; then strings="profile"; fi - if [[ "${COMP_WORDS[1]}" =~ list|ls ]]; then strings="profiles nodes folders"; fi - if [[ "${COMP_WORDS[1]}" =~ bulk|mv|move|cp|copy ]]; then strings="--help"; fi - if [[ "${COMP_WORDS[1]}" =~ --rm|--del|-r ]]; then strings="$strings ${folders[@]/#/@}"; fi - if [[ "${COMP_WORDS[1]}" =~ --rm|--del|-r|--mod|--edit|-e|mv|move|cp|copy|--show|-s ]]; then - strings="$strings ${connections[@]}" - fi - COMPREPLY=($(compgen -W "$strings" -- "${COMP_WORDS[2]}")) - fi - if [ "${#COMP_WORDS[@]}" = "4" ]; then - strings="" - if [ "${COMP_WORDS[1]}" = "profile" ]; then - if [[ "${COMP_WORDS[2]}" =~ --rm|--del|-r|--mod|--edit|-e|--show|-s ]] ; then - strings="$strings ${profiles[@]}" - fi - fi - if [ "${COMP_WORDS[2]}" = "profile" ]; then - if [[ "${COMP_WORDS[1]}" =~ --rm|--del|-r|--mod|--edit|-e|--show|-s ]] ; then - strings="$strings ${profiles[@]}" - fi - fi - if [ "${COMP_WORDS[1]}" = "config" ]; then - if [[ "${COMP_WORDS[2]}" = "--completion" ]] ; then - strings="bash zsh" - fi - fi - COMPREPLY=($(compgen -W "$strings" -- "${COMP_WORDS[3]}")) - fi + strings="$(connpy-completion-helper ${#COMP_WORDS[@]} ${COMP_WORDS[@]})" + COMPREPLY=($(compgen -W "$strings" -- "${COMP_WORDS[-1]}")) } complete -o nosort -F _conn conn complete -o nosort -F _conn connpy +#Here ends bash completion for conn ''' if type == "zshcompletion": return ''' -#Here starts zsh completion for conn -#You need jq installed in order to use this +#Here starts zsh completion for conn autoload -U compinit && compinit _conn() { - DATADIR=$HOME/.config/conn - local COMP_WORDS num - COMP_WORDS=( $words ) - num=${#COMP_WORDS[@]} - if [[ $words =~ '.* $' ]]; then - num=$(($num + 1)) - fi - command -v jq >/dev/null 2>&1 && { - x=`jq -r ' .["connections"] | paths as $path | select(getpath($path) == "connection") | $path | [map(select(. != "type"))[-1,-2,-3]] | map(select(. !=null)) | join("@")' $DATADIR/config.json` - connections=( $x ) - x=`jq -r ' .["connections"] | paths as $path | select(getpath($path) == "folder" or getpath($path) == "subfolder") | $path | [map(select(. != "type"))[-1,-2]] | map(select(. !=null)) | join("@")' $DATADIR/config.json | sed -e 's/^/@/'` - folders=( $x ) - x=`jq -r '.["profiles"] | keys[]' $DATADIR/config.json` - profiles=( $x ) - } - if [ "${num}" = "2" ]; then - strings="--add --del --rm --edit --mod --show mv move ls list cp copy profile bulk config --help" - strings="$strings ${connections[@]} ${folders[@]}" - compadd "$@" -- `echo $strings` - fi - if [ "${num}" = "3" ]; then - strings="" - if [ "${COMP_WORDS[2]}" = "profile" ]; then strings="--add --rm --del --edit --mod --show --help"; fi - if [ "${COMP_WORDS[2]}" = "config" ]; then strings="--allow-uppercase --keepalive --completion --help"; fi - if [[ "${COMP_WORDS[2]}" =~ '--mod|--edit|-e|--show|-s|--add|-a|--rm|--del|-r' ]]; then strings="profile"; fi - if [[ "${COMP_WORDS[2]}" =~ 'list|ls' ]]; then strings="profiles nodes folders"; fi - if [[ "${COMP_WORDS[2]}" =~ 'bulk|mv|move|cp|copy' ]]; then strings="--help"; fi - if [[ "${COMP_WORDS[2]}" =~ '--rm|--del|-r' ]]; then strings="$strings ${folders[@]}"; fi - if [[ "${COMP_WORDS[2]}" =~ '--rm|--del|-r|--mod|--edit|-e|mv|move|cp|copy|--show|-s' ]]; then - strings="$strings ${connections[@]}" - fi - compadd "$@" -- `echo $strings` - fi - if [ "${num}" = "4" ]; then - strings="" - if [ "${COMP_WORDS[2]}" = "profile" ]; then - if [[ "${COMP_WORDS[3]}" =~ '--rm|--del|-r|--mod|--edit|-e|--show|-s' ]] ; then - strings="$strings ${profiles[@]}" - fi - fi - if [ "${COMP_WORDS[3]}" = "profile" ]; then - if [[ "${COMP_WORDS[2]}" =~ '--rm|--del|-r|--mod|--edit|-e|--show|-s' ]] ; then - strings="$strings ${profiles[@]}" - fi - fi - if [ "${COMP_WORDS[2]}" = "config" ]; then - if [[ "${COMP_WORDS[3]}" = '--completion' ]] ; then - strings="bash zsh" - fi - fi - - compadd "$@" -- `echo $strings` - fi + strings=($(connpy-completion-helper ${#words} $words)) + compadd "$@" -- `echo $strings` } compdef _conn conn compdef _conn connpy +#Here ends zsh completion for conn ''' def _getallnodes(self): diff --git a/setup.cfg b/setup.cfg index daee861..87e830b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -29,7 +29,11 @@ install_requires = pexpect pycryptodome +[options.extras_require] +fuzzysearch = pyfzf + [options.entry_points] console_scripts = conn = connpy.__main__:main connpy = connpy.__main__:main + connpy-completion-helper = connpy.completion:main