From: Adam Kupczyk Date: Thu, 18 Feb 2016 09:47:56 +0000 (+0100) Subject: tools: Auto complete feature for CLI. X-Git-Tag: v10.1.1~23^2~1 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=a891d94e7628709edad443f3b5ae4c523cc1db60;p=ceph.git tools: Auto complete feature for CLI. Now logic moved from bash to python. Not bind to bash yet. Use as 'ceph --comp osd ls'. Able to fulfill commands and print command line help. Signed-off-by: Adam Kupczyk --- diff --git a/src/ceph.in b/src/ceph.in index 79c226351253..302165a794cf 100755 --- a/src/ceph.in +++ b/src/ceph.in @@ -474,6 +474,7 @@ def new_style_command(parsed_args, cmdargs, target, sigdict, inbuf, verbose): return json_command(cluster_handle, target=target, argdict=valid_dict, inbuf=inbuf) + def complete(sigdict, args, target): """ Command completion. Match as much of [args] as possible, @@ -491,46 +492,38 @@ def complete(sigdict, args, target): args = args[2:] # look for best match, accumulate possibles in bestcmds # (so we can maybe give a more-useful error message) - best_match_cnt = 0 - bestcmds = [] + + match_count = 0 + comps = [] for cmdtag, cmd in sigdict.iteritems(): sig = cmd['sig'] - matched = matchnum(args, sig, partial=True) - if (matched > best_match_cnt): - if complete_verbose: - print("better match: {0} > {1}: {2}:{3} ".format(matched, - best_match_cnt, cmdtag, concise_sig(sig)), file=sys.stderr) - best_match_cnt = matched - bestcmds = [{cmdtag:cmd}] - elif matched == best_match_cnt: - if complete_verbose: - print("equal match: {0} > {1}: {2}:{3} ".format(matched, - best_match_cnt, cmdtag, concise_sig(sig)), file=sys.stderr) - bestcmds.append({cmdtag:cmd}) - - # look through all matching sigs - comps = [] - for cmddict in bestcmds: - for cmd in cmddict.itervalues(): - sig = cmd['sig'] - # either: - # we match everything fully, so we want the next desc, or - # we match more partially, so we want the partial match - fullindex = matchnum(args, sig, partial=False) - 1 - partindex = matchnum(args, sig, partial=True) - 1 - if complete_verbose: - print('{}: f {} p {} len {}'.format(sig, fullindex, partindex, len(sig)), file=sys.stderr) - if fullindex == partindex and fullindex + 1 < len(sig): - d = sig[fullindex + 1] - else: - d = sig[partindex] - comps.append(str(d)) - if complete_verbose: - print('\n'.join(comps), file=sys.stderr) - print('\n'.join(comps)) + j = 0 + # iterate over all arguments, except last one + for arg in args[0:-1]: + if j > len(sig)-1: + # an out of argument definitions + break + found_match = arg in sig[j].complete(arg) + if not found_match and sig[j].req: + # no elements that match + break + if not sig[j].N: + j += 1 + else: + # successfully matched all - except last one - arguments + if j < len(sig) and len(args) > 0: + comps += sig[j].complete(args[-1]) + + match_count += 1 + match_cmd = cmd + if match_count == 1 and len(comps) == 0: + # only one command matched and no hints yet => add help + comps = comps + [' ', '#'+match_cmd['help']] + print('\n'.join(sorted(set(comps)))) return 0 + ### # ping a monitor ### @@ -712,9 +705,6 @@ def main(): file=sys.stderr) return 1 - if childargs in [['mon'], ['osd']]: - parsed_args.help = True - if parsed_args.help: # short default timeout for -h if not timeout: @@ -727,7 +717,9 @@ def main(): if len(childargs) < 2: print('"ping" requires a monitor name as argument: "ping mon."', file=sys.stderr) return 1 - + if parsed_args.completion: + #for completion let timeout be really small + timeout = 3 try: if childargs and childargs[0] == 'ping': return ping_monitor(cluster_handle, childargs[1], timeout) diff --git a/src/pybind/ceph_argparse.py b/src/pybind/ceph_argparse.py index 7d5f4cfd3da0..021c53d8bcd5 100644 --- a/src/pybind/ceph_argparse.py +++ b/src/pybind/ceph_argparse.py @@ -120,6 +120,9 @@ class CephArgtype(object): """ return '<{0}>'.format(self.__class__.__name__) + def complete(self, s): + return [] + class CephInt(CephArgtype): """ @@ -219,6 +222,12 @@ class CephString(CephArgtype): b += '(goodchars {0})'.format(self.goodchars) return ''.format(b) + def complete(self, s): + if s == '': + return [] + else: + return [s] + class CephSocketpath(CephArgtype): """ @@ -450,6 +459,10 @@ class CephChoices(CephArgtype): else: return '{0}'.format('|'.join(self.strings)) + def complete(self, s): + all_elems = [token for token in self.strings if token.startswith(s)] + return all_elems + class CephFilepath(CephArgtype): """ @@ -536,6 +549,12 @@ class CephPrefix(CephArgtype): def __str__(self): return self.prefix + def complete(self, s): + if self.prefix.startswith(s): + return [self.prefix.rstrip(' ')] + else: + return [] + class argdesc(object): """ @@ -618,6 +637,9 @@ class argdesc(object): s = '{' + s + '}' return s + def complete(self, s): + return self.instance.complete(s) + def concise_sig(sig): """