In an earlier post I said:

QQQ
Strictly speaking, tab cycling doesn't have to be disabled for all colon 
commands.  One could imaging a list of commands that use tab to complete 
file names. Otoh, maintaining that list would be clumsy, and it would add 
still more special cases to ga.do_tab.
QQQ

This post describes a much better way, done at rev 3994eca...Leo build:  
20140820060754.

The new code is gorgeous, if I do say so myself. Even better, the solution 
is a design pattern with many applications.

The trick is to replace functions implementing commands by classes having 
__call__ methods.  See the P.P.S for details.

Yes, this has all been done before.  Several of Leo's commands are 
implemented this way.  But read on...

What's new is that classes can "advertise" their ability to do various 
things.  Here, the classes implementing vim commands advertise, by having a 
tab_callback method, that they want to handle a tab that follows their 
name.  ga.do_tab then defers to the vim command.

There are several really cool things about this code.

1.  ga.do_tab, and its helper, ga.do_tab_callback, no longer know 
*anything* about colon commands, or what any command intends to do with the 
tab(!!).  If the command handler has a tab_callback attribute, 
ga.do_tab_callback just does::

     ga.reset_tab_cycling()
     k.functionTail = tail # For k.getFileName.
     handler.tab_callback()

Nothing could be simpler, or more general.

2. The code in the command classes is simplified as well.  No need for a 
kludgy test event.get_arg_value.  See the P.S. for the full implementation 
of the :tabnew command.

The overall result is a spectacular collapse in complexity, which the 
attendant increase in power and generality.

Edward

P.S.  Here is the flattened form of the class that handles the :tabnew 
command (does not require vim-mode).  In particular, note that the __call__ 
and tab_callback methods are trivial.  This is the way it is written in The 
Book.

class LoadFileAtCursor:
    '''
    A class to handle Vim's :tabnew command.
    This class supports the do_tab callback.
    '''
    def __init__(self,vc):
        '''Ctor for VimCommands.LoadFileAtCursor class.'''
        self.vc = vc

    __name__ = ':r'
        # Required.

    def __call__(self,event=None):
        '''Prompt for a file name, the open a new Leo tab.'''
        self.vc.c.k.getFileName(event,callback=self.open_file_by_name)
        
    def tab_callback(self):
        '''Called when the user types :tabnew<tab>'''
        self.vc.c.k.getFileName(event=None,callback=self.open_file_by_name)
        
    def open_file_by_name(self,fn):
        c = self.vc.c
        if fn and not g.os_path_isdir(fn):
            c2 = g.openWithFileName(fn,old_c=c)
            try:
                g.app.gui.runAtIdle(c2.treeWantsFocusNow)
            except Exception:
                pass
        else:
            c.new()

P.P.S. This pattern is particularly well suited to Leo, because the various 
getPublicCommands methods reference those functions when create command 
dictionaries.  Here, we replace just two entries in the dict::

    ':r':   vc.LoadFileAtCursor(vc),
    ':tabnew': vc.Tabnew(vc),

This creates instances of the LoadFileAtCursor and Tabnew classes. In other 
words, we replace a function by an instance of a class.  All such classes 
must have __call__ methods, so that Leo can "call" the instance as if it 
were a function.

EKR

-- 
You received this message because you are subscribed to the Google Groups 
"leo-editor" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to leo-editor+unsubscr...@googlegroups.com.
To post to this group, send email to leo-editor@googlegroups.com.
Visit this group at http://groups.google.com/group/leo-editor.
For more options, visit https://groups.google.com/d/optout.

Reply via email to