A beautiful and general design pattern eliminates tab-completion special cases

2014-08-20 Thread Edward K. Ream
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 :tabnewtab'''
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.


Re: A beautiful and general design pattern eliminates tab-completion special cases

2014-08-20 Thread Edward K. Ream
On Wednesday, August 20, 2014 6:38:50 AM UTC-5, Edward K. Ream wrote:

 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.

The code got mangled.  The class is the TabNew class, but the opening lines 
came from the LoadFileAtCursor class.  The correct opening lines are::

class Tabnew:
'''
A class to handle Vim's :tabnew command.
This class supports the do_tab callback.
'''
def __init__(self,vc):
'''Ctor for VimCommands.tabnew class.'''
self.vc = vc
__name__ = ':tabnew'
# Required.
@others

-- 
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.


Re: A beautiful and general design pattern eliminates tab-completion special cases

2014-08-20 Thread Zoltan Benedek
Excellent.

On Wednesday, August 20, 2014 2:38:50 PM UTC+3, Edward K. Ream wrote:

 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 :tabnewtab'''
 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.