summaryrefslogtreecommitdiffstats
path: root/completion.py
diff options
context:
space:
mode:
authorJan Pokorný <jpokorny@redhat.com>2014-02-26 14:34:17 +0100
committerJan Pokorný <jpokorny@redhat.com>2014-02-26 16:25:12 +0100
commitdc10559ef1236ec285ff6bb45fe6292a436e3693 (patch)
treeb0bbceb33bbe8b6ac7b10aba9a86f9289cd04273 /completion.py
parent5fc1a74d0b484eb1a2d28689a294e9eebcd97c78 (diff)
downloadclufter-dc10559ef1236ec285ff6bb45fe6292a436e3693.tar.gz
clufter-dc10559ef1236ec285ff6bb45fe6292a436e3693.tar.xz
clufter-dc10559ef1236ec285ff6bb45fe6292a436e3693.zip
completion: way to get bash (and perhaps others) autocompletions
Currently it provides WIP standalone bash completion, but should be fairly easy to implement support for, e.g. bash-completion dependent version, zsh-specific one, etc. Signed-off-by: Jan Pokorný <jpokorny@redhat.com>
Diffstat (limited to 'completion.py')
-rw-r--r--completion.py127
1 files changed, 127 insertions, 0 deletions
diff --git a/completion.py b/completion.py
new file mode 100644
index 0000000..c187188
--- /dev/null
+++ b/completion.py
@@ -0,0 +1,127 @@
+# -*- coding: UTF-8 -*-
+# Copyright 2014 Red Hat, Inc.
+# Part of clufter project
+# Licensed under GPLv2 (a copy included | http://gnu.org/licenses/gpl-2.0.txt)
+"""Shell completion formatters"""
+__author__ = "Jan Pokorný <jpokorny @at@ Red Hat .dot. com>"
+
+from os.path import basename
+
+from .utils import args2sgpl
+
+
+class Completion(object):
+ mapping = {}
+
+ def __init__(self, prog, opts_common, opts_main, opts_nonmain):
+ self._prog = prog
+ self._opts_common = opts_common
+ self._opts_main = opts_main
+ self._opts_nonmain = opts_nonmain
+
+ def scripts_prologue(self):
+ return ''
+
+ def handle_script(self, command):
+ raise RuntimeError('subclasses ought to override this')
+
+ def scripts_epilogue(self, handles):
+ return ''
+
+ def __call__(self, commands):
+ handles, scripts = reduce(
+ lambda (acc_handle, acc_script), cmd:
+ (lambda handle, script: (
+ acc_handle + [(cmd.__class__.__name__, handle)],
+ acc_script + [script]
+ ))(*self.handle_script(cmd)),
+ commands,
+ ([], [])
+ )
+ scripts = [self.scripts_prologue(), ] + scripts
+ scripts.append(self.scripts_epilogue(handles))
+ return '\n\n'.join(scripts)
+
+ @classmethod
+ def deco(basecls, which):
+ def deco(cls):
+ basecls.mapping[which] = cls
+ return cls
+ return deco
+
+ @classmethod
+ def get_completion(cls, which, *args):
+ return cls.mapping[which](*args)
+
+
+@Completion.deco("bash")
+class BashCompletion(Completion):
+ def __init__(self, prog, *args):
+ prog = basename(prog)
+ super(BashCompletion, self).__init__(prog, *args)
+ self._name = prog.replace('-', '_')
+
+ @staticmethod
+ def _namespaced_identifier(namespace, name=None):
+ return '_'.join(filter(lambda x: x is not None, ('', namespace, name)))
+
+ @staticmethod
+ def _format_function(name, bodylines):
+ bodylines = args2sgpl(bodylines)
+ return ("{0}() {{\n\t{1}\n}}"
+ .format(name, '\n\t'.join(bodylines).rstrip('\t')))
+
+ @staticmethod
+ def scripts_prologue():
+ return """\
+# bash completion start
+# add me to ~/.profile persistently or eval on-the-fly in bash"""
+
+ def handle_script(self, cmd):
+ clsname = cmd.__class__.__name__
+ handle = self._namespaced_identifier(self._name, clsname)
+ _, opts = cmd.parser_desc_opts
+ main = """\
+local opts="{0}"
+
+[[ "$1" =~ -.* ]] \\
+ && compgen -W "${{opts}}" -- $1"""\
+ .format(
+ ' '.join(reduce(lambda a, b: a + list(b[0]), opts, []))
+ ).splitlines()
+
+ return handle, self._format_function(handle, main)
+
+ def scripts_epilogue(self, handles):
+ handle = self._namespaced_identifier(self._name)
+ handles_dict = dict(handles)
+ opts = self._opts_common, self._opts_main, self._opts_nonmain
+ main = """\
+local commands="{1}"
+local opts_common="{2}"
+local opts_main="{3}"
+local opts_nonmain="{4}"
+
+local cur fnc i=${{COMP_CWORD}}
+while true; do
+ test ${{i}} -eq 0 && break || let i-=1
+ cur=${{COMP_WORDS[${{i}}]}}
+ [[ "${{cur}}" =~ -.* ]] && continue
+ fnc=_main_boostrap_${{cur}}
+ declare -f ${{fnc}} >/dev/null && COMPREPLY+=( $(${{fnc}} $2) )
+ [[ "$2" =~ -.* ]] \\
+ && COMPREPLY+=( $(compgen -W "${{opts_common}} ${{opts_nonmain}}" -- $2) )
+ return
+done
+
+case "$2" in
+-*) COMPREPLY=( $(compgen -W "${{opts_common}} ${{opts_main}}" -- $2) );;
+*) COMPREPLY=( $(compgen -W "${{commands}}" -- $2) );;
+esac""" .format(
+ self._name,
+ ' '.join(handles_dict.keys()),
+ *(' '.join(reduce(lambda a, b: a + list(b[0]), o, []))
+ for o in opts)
+ ).splitlines()
+ epilogue = "complete -o default -F {0} {1}".format(handle, self._prog)
+ return '\n\n'.join([self._format_function(handle, main), epilogue])