Added label completer (on the command line) + updated opster.py to 1ce5c137b3e5
authorDmitriy Morozov <dmitriy@mrzv.org>
Mon, 31 May 2010 19:53:56 -0700
changeset 35 1055e4bef0e6
parent 34 f5109280e7fe
child 36 78a9c77198b2
Added label completer (on the command line) + updated opster.py to 1ce5c137b3e5
alexandria.py
opster.py
--- a/alexandria.py	Mon Apr 12 12:30:37 2010 -0700
+++ b/alexandria.py	Mon May 31 19:53:56 2010 -0700
@@ -60,7 +60,11 @@
     initDatabase(path, not found)
 
 
-laopts = [('l', 'label',  [], 'label of the document'),
+# cfg because of the middleware
+def _complete_label(cfg, prefix, **opts):
+    return [t.name for t in Tag.query.filter(Tag.name.startswith(unicode(prefix))).order_by(asc(Tag.name)).all()]
+
+laopts = [('l', 'label',  [], 'label of the document', _complete_label),
           ('a', 'author', [], 'author of the document')]
 
 latopts = laopts + [('t', 'title',  '', 'paper title')]
@@ -371,7 +375,7 @@
         if 'database' in kwargs:
             del kwargs['database']
 
-        func(cfg, *args, **kwargs)
+        return func(cfg, *args, **kwargs)
 
     return inner
 
--- a/opster.py	Mon Apr 12 12:30:37 2010 -0700
+++ b/opster.py	Mon May 31 19:53:56 2010 -0700
@@ -2,7 +2,7 @@
 '''Command line arguments parser
 '''
 
-import sys, traceback, getopt, types, textwrap, inspect
+import sys, traceback, getopt, types, textwrap, inspect, os
 from itertools import imap
 
 __all__ = ['command', 'dispatch']
@@ -123,13 +123,24 @@
     cmdtable['help'] = (help_(cmdtable, globaloptions), [], '[TOPIC]')
     help_func = cmdtable['help'][0]
 
+    cmdtable['_completion'] = (completion_,
+                              [('b', 'bash', False, 'Output bash competion'), 
+                               ('z', 'zsh',  False, 'Output zsh completion')], 
+                              '--bash OR --zsh')
+    autocomplete(cmdtable, args, middleware)
+
     try:
         name, func, args, kwargs = catcher(
             lambda: _dispatch(args, cmdtable, globaloptions),
             help_func)
-        return catcher(
-            lambda: call_cmd(name, middleware(func))(*args, **kwargs),
-            help_func)
+        if name == '_completion':       # skip middleware
+            return catcher(
+                lambda: call_cmd(name, func)(*args, **kwargs),
+                help_func)
+        else:
+            return catcher(
+                lambda: call_cmd(name, middleware(func))(*args, **kwargs),
+                help_func)
     except Abort:
         return -1
 
@@ -271,7 +282,8 @@
     argmap, defmap, state = {}, {}, {}
     shortlist, namelist, funlist = '', [], []
 
-    for short, name, default, comment in options:
+    for o in options:
+        short, name, default, comment = o[:4]       # might have the fifth completer element
         if short and len(short) != 1:
             raise FOError('Short option should be only a single'
                           ' character: %s' % short)
@@ -312,6 +324,8 @@
             state[name] = defmap[name](val)
         elif t is types.IntType:
             state[name] = int(val)
+        elif t is types.FloatType:
+            state[name] = float(val)
         elif t is types.StringType:
             state[name] = val
         elif t is types.ListType:
@@ -324,6 +338,89 @@
 
     return state, args
 
+# --------
+# Autocomplete system
+# --------
+
+# Borrowed from PIP
+def autocomplete(cmdtable, args, middleware):
+    """Command and option completion.
+
+    Enable by sourcing one of the completion shell scripts (bash or zsh).
+    """
+    
+    # Don't complete if user hasn't sourced bash_completion file.
+    if not os.environ.has_key('OPSTER_AUTO_COMPLETE'):
+        return
+    cwords = os.environ['COMP_WORDS'].split()[1:]
+    cword = int(os.environ['COMP_CWORD'])
+
+    try:
+        current = cwords[cword-1]
+    except IndexError:
+        current = ''
+
+    commands = []
+    for k in cmdtable.keys():
+        commands += aliases_(k)
+
+    # command
+    if cword == 1:
+        print ' '.join(filter(lambda x: x.startswith(current), commands))
+    
+    # command options
+    elif cwords[0] in commands:
+        idx = -2 if current else -1
+        options = []
+        aliases, (cmd, opts, usage) = findcmd(cwords[0], cmdtable)
+
+        for o in opts:
+            short, long, default, help = o[:4]
+            completer = o[4] if len(o) > 4 else None
+            short, long = '-%s' % short, '--%s' % long
+            options += [short,long]
+
+            if (cwords[idx] in [short,long]) and completer:
+                args = middleware(completer)(current)
+                print ' '.join(args),
+        
+        print ' '.join((o for o in options if o.startswith(current)))
+    
+    sys.exit(1)
+
+def completion_(**opts):
+    """Outputs the completion script for bash or zsh."""
+
+    (head, prog_name) = os.path.split(sys.argv[0])
+
+    if opts['bash']:
+        print """
+# opster bash completion start
+_opster_completion()
+{
+    COMPREPLY=( $( COMP_WORDS="${COMP_WORDS[*]}" \\
+                   COMP_CWORD=$COMP_CWORD \\
+                   OPSTER_AUTO_COMPLETE=1 $1 ) )
+}
+complete -o default -F _opster_completion %s
+# opster bash completion end
+              """ % prog_name
+
+    if opts['zsh']:
+        print """
+# opster zsh completion start
+function _opster_completion {
+  local words cword
+  read -Ac words
+  read -cn cword
+  reply=( $( COMP_WORDS="$words[*]" \\ 
+             COMP_CWORD=$(( cword-1 )) \\
+             OPSTER_AUTO_COMPLETE=1 $words[1] ) )
+}
+compctl -K _opster_completion %s
+# opster zsh completion end
+              """ % prog_name
+
 
 # --------
 # Subcommand system
@@ -365,6 +462,9 @@
 
     return (cmd, cmd and info[0] or None, args, options)
 
+def aliases_(cmdtable_key):
+    return cmdtable_key.lstrip("^~").split("|")
+
 def findpossible(cmd, table):
     """
     Return cmd -> (aliases, command table entry)
@@ -372,7 +472,7 @@
     """
     choice = {}
     for e in table.keys():
-        aliases = e.lstrip("^~").split("|")
+        aliases = aliases_(e)
         found = None
         if cmd in aliases:
             found = cmd
@@ -411,8 +511,9 @@
     args, varargs, varkw, defaults = inspect.getargspec(func)
     for name, option in zip(args[-len(defaults):], defaults):
         try:
-            sname, default, hlp = option
-            yield (sname, name.replace('_', '-'), default, hlp)
+            sname, default, hlp = option[:3]
+            completer = option[3] if len(option) > 3 else None
+            yield (sname, name.replace('_', '-'), default, hlp, completer)
         except TypeError:
             pass