Mercurial > enso_core
view enso/ui/commands/manager.py @ 25:8f4663bc7486
Added implementation of enso.ui.commands.manager.
author | Atul Varma <varmaa@toolness.com> |
---|---|
date | Sat, 23 Feb 2008 08:40:35 -0600 |
parents | |
children |
line wrap: on
line source
# Copyright (c) 2008, Humanized, Inc. # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # 3. Neither the name of Enso nor the names of its contributors may # be used to endorse or promote products derived from this # software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY Humanized, Inc. ``AS IS'' AND ANY # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL Humanized, Inc. BE LIABLE FOR ANY # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- # # enso.ui.commands.manager # # ---------------------------------------------------------------------------- """ The CommandManager singleton. """ # ---------------------------------------------------------------------------- # Imports # ---------------------------------------------------------------------------- import logging from enso.ui.commands.interfaces import CommandExpression, CommandObject from enso.ui.commands.interfaces import AbstractCommandFactory from enso.ui.commands.factories import GenericPrefixFactory # ---------------------------------------------------------------------------- # The Command Manager # ---------------------------------------------------------------------------- class _TheCommandManager: """ Provides an interface to register and retrieve all commands. Allows client code to register new command implementations, find suggestions, and retrieve command objects. """ CMD_KEY = CommandExpression( "{all named commands}" ) def __init__( self ): """ Initializes the command manager. """ self.__cmdObjReg = CommandObjectRegistry() self.__cmdFactoryDict = { self.CMD_KEY : self.__cmdObjReg, } def registerCommand( self, cmdName, cmdObj ): """ Called to register a new command with the command manager. """ try: cmdExpr = CommandExpression( cmdName ) except AssertionError, why: logging.error( "Could not register %s : %s " % ( cmdName, why ) ) raise if cmdExpr.hasArgument(): # The command expression has an argument; it is a command # with an argument. assert isinstance( cmdObj, AbstractCommandFactory ) assert not self.__cmdFactoryDict.has_key( cmdExpr ) self.__cmdFactoryDict[ cmdExpr ] = cmdObj else: # The command expression has no argument; it is a # simple command with an exact name. assert isinstance( cmdObj, CommandObject ), \ "Could not register %s" % cmdName self.__cmdObjReg.addCommandObj( cmdObj, cmdExpr ) def unregisterCommand( self, cmdName ): cmdFound = False for cmdExpr in self.__cmdFactoryDict.keys(): if str(cmdExpr) == cmdName: del self.__cmdFactoryDict[cmdExpr] cmdFound = True break if not cmdFound: self.__cmdObjReg.removeCommandObj( cmdName ) cmdFound = True if not cmdFound: raise RuntimeError( "Command '%s' does not exist." % cmdName ) def getCommandExpression( self, commandName ): """ Returns the unique command expression that is assosciated with commandName. For example, if commandName is 'open emacs', and the command expression was 'open {file}', then a command expression object for 'open {file}' will be returned. """ commands = [] for expr in self.__cmdFactoryDict.iterkeys(): if expr.matches( commandName ): # This expression matches commandName; try to fetch a # command object from the corresponding factory. cmd = self.__cmdFactoryDict[expr].getCommandObj( commandName ) if expr == self.CMD_KEY and cmd != None: commands.append( ( commandName, commandName ) ) elif cmd != None: # The factory returned a non-nil command object. # Make sure that nothing else has matched this # commandName. commands.append( (expr.getPrefix(), expr) ) if len(commands) == 0: return None else: # If there are several matching commands, return only # the alphabetically first. commands.sort( lambda a,b : cmp( a[0], b[0] ) ) return commands[0][1] def getCommand( self, commandName ): """ Returns the unique command with commandName, based on the registered CommandObjects and the registered CommandFactories. If no command matches, returns None explicitly. """ commands = [] for expr in self.__cmdFactoryDict.iterkeys(): if expr.matches( commandName ): # This expression matches commandName; try to fetch a # command object from the corresponding factory. cmd = self.__cmdFactoryDict[expr].getCommandObj( commandName ) if cmd != None: # The factory returned a non-nil command object. commands.append( ( expr, cmd ) ) if len( commands ) == 0: return None else: # If there are several matching commands, return only # the alphabetically first. prefixes = [ (e.getPrefix(),c) for (e,c) in commands ] prefixes.sort( lambda a,b : cmp( a[0], b[0] ) ) return prefixes[0][1] def autoComplete( self, userText ): """ Returns the best match it can find to userText, or None. """ completions = [] # Check each of the command factories for a match. for expr in self.__cmdFactoryDict.iterkeys(): if expr.matches( userText ): cmdFact = self.__cmdFactoryDict[expr] completion = cmdFact.autoComplete( userText ) if completion != None: completions.append( completion ) if len( completions ) == 0: return None else: completions.sort( lambda a,b : cmp( a.toText(), b.toText() ) ) return completions[0] def retrieveSuggestions( self, userText ): """ Returns an unsorted list of suggestions. """ suggestions = [] # Extend the suggestions using each of the command factories for expr in self.__cmdFactoryDict.iterkeys(): if expr.matches( userText ): factory = self.__cmdFactoryDict[expr] suggestions += factory.retrieveSuggestions( userText ) return suggestions def getCommands( self ): """ Returns a dictionary of command expression strings and their associated implementations (command objects or factories). """ # Get a dictionary form of the command object registry: cmdDict = self.__cmdObjReg.getDict() # Extend the dictionary to cover the command factories. for expr in self.__cmdFactoryDict.keys(): if expr == self.CMD_KEY: # This is the command object registry; pass. pass else: # Cast the expression as a string. cmdDict[ str(expr) ] = self.__cmdFactoryDict[expr] return cmdDict # ---------------------------------------------------------------------------- # A CommandObject Registry # ---------------------------------------------------------------------------- class CommandAlreadyRegisteredError( Exception ): """ Error raised when someone tries to register two commands under the same name with the registry. """ pass class CommandObjectRegistry( GenericPrefixFactory ): """ Class for efficiently storing and searching a large number of commands (where each command is an object with a corresponding command name). """ PREFIX = "" def __init__( self ): """ Initialize the command registry. """ GenericPrefixFactory.__init__( self ) self.__cmdObjDict = {} self.__dictTouched = False def update( self ): pass def getDict( self ): return self.__cmdObjDict def addCommandObj( self, command, cmdExpr ): """ Adds command to the registry under the name str(cmdExpr). """ assert isinstance( cmdExpr, CommandExpression ) assert not cmdExpr.hasArgument() cmdName = str(cmdExpr) if self.__cmdObjDict.has_key( cmdName ): raise CommandAlreadyRegisteredError() self.__cmdObjDict[ cmdName ] = command self.__dictTouched = True self._addPostfix( cmdName ) def removeCommandObj( self, cmdExpr ): cmdFound = False if self.__cmdObjDict.has_key( cmdExpr ): del self.__cmdObjDict[cmdExpr] cmdFound = True if cmdFound: self.__dictTouched = True self._removePostfix( cmdExpr ) else: raise RuntimeError( "Command object '%s' not found." % cmdExpr ) def getCommandObj( self, cmdNameString ): """ Returns the object corresponding to cmdNameString. NOTE: This will raise a KeyError if cmdNameString is not a valid command name. """ try: return self.__cmdObjDict[ cmdNameString ] except KeyError: return None