In the thread, "vim mode is a great success, even when not enabled", I said,
"The code for every kind of completion tab is hairy, but a filename
completion/browsing tab would be useful not just for vim commands.
Imo,
Leo must have this."
Well, it turns out that Leo already has such code: k.getFileName. It is used by the open-outline-by-name command. Alas, k.getFileName is pretty much broken. Apparently, nobody uses open-outline-by-name, or they have used it once and given up on
Leo :-(
Last night I found a way to remove *all* the behind-the-scenes complexity with tab completion in k.getFileName. This Aha should apply to Leo's workhorse k.getArg method as well. I'll discuss the (interesting!) coding details in the P.S.
Some real work remains: the code that actually computes the completion list needs a lot of work. I expect to finish it today, if birthday revels do not interfere ;-)
Edward
P.S. Engineering Note Book: feel free to ignore.
I began the task of fixing k.getFileName by creating a FileNameChooser class. Not pushed yet.
The original intention of the FileNameChooser class was to hide the details of the code. Like this::
def getFileName():
'''Create a FileNameChooser and use it to get a file name.'''
fnc = FileNameChooser(c,handler,filterExt='.py')
# handler=None,prefix='Enter File Name:',tabName='Dired')
fnc.get_file_name()
I have often found that creating a helper class like this clarifies the code in unexpected ways. Rather than dealing with a set of a set of helper methods of k
(class KeyHandlerClass), grouping the methods into a new class simplifies everything.
The old code in both k.getFileName and k.getArg keeps track of
several truly ugly and hard-to-understand ivars, whose only real purpose is to keep track of the
"protected" label in the minibuffer, so as either ignore it or prevent
the label from being deleted by a backspace character. In the new class it was much easier to see that none of this
complexity is needed! The trick is to define the following methods::
def extend_label(fnc,s):
'''Extend the label by s.'''
fnc.c.k.extendLabel(s,select=False,protect=False)
def get_label(fnc):
'''Return the label, not including the prompt.'''
return fnc.c.k.getLabel(ignorePrompt=True)
def set_label(fnc,s):
'''Set the label after the prompt to s. The prompt never changes.'''
fnc.c.k.setLabel(fnc.prompt,protect=True)
fnc.c.k.extendLabel(s or '',select=False,protect=False
As you can see, these are simply thin wrappers on the corresponding k methods, but they deal only with what follows the prompt. All the apparent complexity of tab completion simply disappears! We use fnc.get_label to get the text to be completed, and use fnc.set_label/extend_label to update the label.
All this is obvious in retrospect, but I would never have seen it without creating the new class. Pretty amazing.
P.P.S. This collapse in complexity might apply to k.getArg as well, but there are complications. k.getArg has accreted over the years, and it contains several hacks (keyword arguments) not related to tab completion. It might not be possible for k.getArg to create a new instance of, say, a GetArg class without having unpleasant ripple effects throughout Leo.
If k.getArg *can* create one-off instances of the GetArg class, then both the GetArg and FileNameChooser classes would likely become a subclass of a BaseGetArg class.
EKR