Re: tab completion in the curses ui

Eugene <[email protected]> Tue, 01 Mar 2005 08:42:33 +0300
Newsgroups gmane.comp.games.mud.client.lyntin
Message-ID <[email protected]>
On Mon, 28 Feb 2005 12:50:18 -0600 (CST)
  will guaraldi <[email protected]> wrote:
> 
> I'm tossing around adding tab-completion to the curses 
>ui.  I think I'm going to do it via a callback so people 
>can add their own tab-completion plugins that return 
>possibilities of what the term expands to. Theoretically 
>it could be used for the tkui as well, but I haven't 
>touched that in a year, so I don't know for sure.
> 
> Anyhow, so my question is two-fold:
> 
> 1. does anyone have tab completion already coded?

I think it's checked in some time ago. Some basic support 
is in the cursesui and in the tkui already. Also, 
additional module is needed - I did not include it into 
the distribution yet, but it was sent to the list. I send 
it again here.

The first file in the attachment is the manager itself; 
the second is an example of how real completion hooks 
could be coded.

--
Eugene
---
Professional hosting for everyone - http://www.host.ru
completion.py (text/x-python, 2.5 KB)
#######################################################################
# This file is part of Lyntin.
# copyright (c) Free Software Foundation 2004
# copyright (c) glasssnake <[email protected]> 2004
#
# Lyntin is distributed under the GNU General Public License license.  See the
# file LICENSE for distribution details.
# $Id$
#######################################################################
"""
This module defines a manager for input line completion. 
A user interface module could call this manager to receive
a list of possible completions for any given user input.

Real completion work is being done by hooks registered for
"completer_hook". The hook receives a tuple of (text, position)
and returns a list [(completion_text1, completion_position1)... ].
The manager gathers all the lists from all registered hooks and returns
to UI module completion tuples one by one.
"""
from lyntin import exported, manager


class CompletionManager(manager.Manager):
  """
  This manager should help to an UI module to build
  a completion list for given user input.
  """
  def __init__(self, *args, **kargs):
    manager.Manager.__init__(self, *args, **kargs)
    self.reset()

  def reset(self):
    """
    Resets the completer object, so the completion list
    will be rebuilt on next get_completion call.
    """
    self.list_ = []

  def _mapping(self, x, y):
    self.list_ += y
    return x
    
  def get_completion(self, text='', position=None):
    """
    This method should be called when some hotkey (usually "Tab", but it
    is up to the UI module) is being pressed.

    @param text: the text to be completed
    @type  text: string

    @param position: cursor position in the text
    @type  position: int

    @returns: next possible completion pair (newtext, newposition)
    @rtype:   (string, int)
    """
    if not self.list_:
      #
      # Rebuild the completion list if it is empty after reset.
      #
      if position==None or position<0 or position>len(text):
        position = len(text)
      #
      # Gather all the completions from registered completers:
      #
      exported.hook_spam("completer_hook",
                         { 'text': text, 'position': position },
                         self._mapping
                        )
      self.list_.append((text, position))  # put an anchor at the end of list
      
    completion = self.list_[0]
    self.list_[:1] = []
    self.list_.append(completion)
    return completion


def load():
  exported.add_manager("completion", CompletionManager())


def unload():
  exported.remove_manager("completion")
completer.py (text/x-python, 2.7 KB)
from lyntin import exported 


def make_alias_completer(args):
  """Builds and returns the alias completion list."""
  text = args['text']
  position = args['position']
  cmdchar = exported.get_config("commandchar")
  if not text.startswith(cmdchar):
    al = exported.get_manager("alias")
    aliases = filter(lambda x: x.startswith(text[:position]),
        al.getAliasData(exported.get_current_session()).getAliases() )
    return [ (x+' ', len(x)+1) for x in aliases ]
  return []        


def make_command_completer(args):
  """Builds and returns the command completion list."""
  text = args['text']
  position = args['position']
  cmdchar = exported.get_config("commandchar")
  if text.startswith(cmdchar):
    cm = exported.get_manager("command")
    return map(lambda x: (''.join( (cmdchar, x, ' ') ), len(x)+2),
               filter(lambda x: x.startswith(text[1:]),
                      cm.getCommands()))
  return []


import re

def make_var_completer(args):
  """
  Builds and returns the variable completion list,
  if the text at the given position looks like a variable
  """
  text = args['text']
  position = args['position']

  #
  # Checking for "#showme $foo" form:
  #
  mch = re.match('(.*)\$(\S*)', text[:position])
  if mch:
    tail = re.match('(\S*)(.*)$', text[position:])
    varname = mch.group(2)+tail.group(1)
    list = []
    for var in filter( lambda x: x.startswith(varname), 
                       exported.get_current_session()._vars.keys() ):
      start = ''.join( (mch.group(1), '$', var) )
      list.append( (start+tail.group(2), len(start)) )
    return list
  #
  # TODO process the "#showme ${foo}" form
  #

  #
  # Processing the "#unvar" command:
  #
  cmdchar = exported.get_config("commandchar")
  mch = re.match(cmdchar+'unvariable (\S*)$', text)
  if mch:
    list = []
    for var in filter( lambda x: x.startswith(mch.group(1)), 
                       exported.get_current_session()._vars.keys() ):
      command = '#unvariable ' + var                       
      list.append( (command, len(command)) )
    return list
    
  return []  
    

def load():
  """
  These 3 completer hooks below could be united into one big hook function
  as well, but for demonstration purposes they are divided.
  Later they could be moved in their own modules.
  """
  exported.hook_register("completer_hook", make_command_completer)
  exported.hook_register("completer_hook", make_alias_completer)
  exported.hook_register("completer_hook", make_var_completer)


def unload():
  exported.hook_unregister("completer_hook", make_var_completer)
  exported.hook_unregister("completer_hook", make_command_completer)
  exported.hook_unregister("completer_hook", make_alias_completer)