In another thread I said, "I'm going to do #852 
<https://github.com/leo-editor/leo-editor/issues/852>: (active settings 
outline) first.  I think I can do this in a few hours [sic] to a few days.  
It's a frequently requested feature."

I spent about 15 hours on yesterday, from 2 am to 7pm, with just a few 
breaks.  And here I am again, at 2:30 am. This is a difficult, fascinating, 
project. It took all yesterday to come up to speed on Leo's configuration 
code.  The "settings" branch contains the work.

The following are some notes, feel free to ignore.  They might find their 
way into an info issue dealing with settings. I am writing this now to 
summarize yesterday's researches.

*About the active-settings-outline command*

The ActiveSettingsOutline class implements this command. At present, the 
code creates an new, *real, *outline illustrating the overall form of the 
final outline.  You can save this outline if you want.  It's name is 
x-active-settings.leo, where x is the name of the outline that executed the 
active-setting-outline command.  This was the easy part ;-)

The aso.start method, and its helpers, now appears to recreate the required 
initing of settings *exactly* as is done in Leo's startup code. *Subtle 
bugs will abound if it doesn't*. The code is deceptively simple. There are 
a frightening number of constraints on this code.

The aso.create_active_settings method shows the difficulties involved in 
traversing the various @settings trees while simultaneously seeing where 
the settings come from. Despite the present complications, I am pretty sure 
the code can be simplified. 

*About the configuration code*

The c.config.get* methods are the one and only interface between this code 
and the rest of Leo.  This interface hides the truly frightening complexity 
of the underlying code.  

The LoadManager class, in leoApp.py, does the heavy lifting involved with 
reading *settings files*, leoSettings.leo, myLeoSettings.leo, the *theme 
file*, specified by the @string theme-name setting (usually in 
myLeoSettings.leo) and the *local files*, the actual outline files.  Leo 
can load leoSettings.leo, myLeoSettings.leo and any theme file, so that has 
to work too. That is, local files can also be settings files or theme files.

I'll abbreviate the singleton g.app.loadManager instance by LM, which I 
also use to denote the LoadManager class.

Leo *already* caches settings.  There are two *global settings dicts*, 
LM.globalSettingsDict and LM.globalBindingsDict.  And there is *a* 
corresponding *local settings dict*, c.config.settingsDict. Not sure 
whether there is a local bindings dict :-)

The global dicts represent the settings in *both* leoSettings.leo and 
myLeoSettings.leo. Not sure whether I would do things the same way if I 
were starting from scratch.  *Important*: the local settings dict contains 
*all* the settings available to the outline.  That is, 
c.config.settingsDict is the *union* of all settings from settings files, 
the theme file and the local file.  This union gives first priority to the 
settings in the local file, then the settings in the theme file, and last, 
the settings from the settings files (the global settings dict).

This union is an important optimization. The fundamental c.config.get 
method needs only access *one* dict, namely c.config.settingsDict.  In 
other words, the get method knows nothing about search order.  That has 
already been resolved.

Each value in the c.config.settingsDict is a g.GeneralSetting (GS) 
instance.  GS objects contain a path ivar, which is the full path of the 
.leo file that is responsible for the setting.  This path allows 
gcm.config_iter to determine where the setting came from.  As I write this, 
I see that active settings outline will use gcm.config_iter as a model for 
assigning settings to files.

That's all I want to say about the configuration code.  More details would 
be useless.  Most people, including myself, have no chance of remembering 
anything more.

*About changing the configuration code*

I plan to make *zero* substantive changes to the configuration code while 
working on #852.  The only changes I have made are to docstrings.  I did 
make one "hidden" change to readGlobalSettingsFiles.  It now sets LM ivars 
needed for the ActiveSettingsOutline class. Even this tiny change was 
worrisome enough that I created a new git branch for the work.

It's extremely difficult to know what the code does, *in exact detail*.  A 
trace in LM.openSettingsFile shows that reading settings files happens in 
two stages.  LM.doPrePluginsInit does the first stage, LM.doPostPluginsInit 
does the second stage.  The ActiveSettingsOutline code must use *exactly* 
this load order in order to faithfully recreate settings.  Btw, the 
--theme=name command-line argument changes the move order.

Given the complexities of the code, I simply do not understand how anyone 
can be confident that proposed changes would work *exactly* as before.  
After 15 hours of intense study, I don't understand the code well enough to 
have any confidence in *any* real changes to the code.

*Global settings and problems*

The node "c.config.Getters: redirect to g.app.config" contains problematic 
code.  It contains four methods:  

def getButtons(self):
    '''Return a list of tuples (x,y) for common @button nodes.'''
    return g.app.config.atCommonButtonsList # unusual.

def getCommands(self):
    '''Return the list of tuples (headline,script) for common @command 
nodes.'''
    return g.app.config.atCommonCommandsList # unusual.

def getEnabledPlugins(self):
    '''Return the body text of the @enabled-plugins node.'''
    return g.app.config.enabledPluginsString # unusual.

def getRecentFiles(self):
    '''Return the list of recently opened files.'''
    return g.app.config.getRecentFiles() # unusual

The first two probably don't cause great problems.  The second two are 
somewhat dubious.  I'll leave it at that for now. Anyone seriously 
considering changes to the config code should be aware of these hacks.

*A possible source of theme misery*

While studying LM.readGlobalSettingsFiles I saw the following code:

if 0:
    # Not necessary **provided** that theme .leo files
    # set @string theme-name to the name of the .leo file.
    g.app.theme_color = settings_d.get_string_setting('color-theme')
    g.app.theme_name = settings_d.get_string_setting('theme-name')

Whoa!  What's going on here?  There is almost zero chance I'll enable this 
code.  But I may well issue a warning (somewhere!) if a theme file does not 
contain an @string theme-name setting.  This would be oh so hard to find.  
It's a dog that isn't barking.

*Summary*

Work on the active-settings-outline command is progressing well.  A day or 
two more should suffice. All work is being done in the settings branch. I 
shall make *no* substantive changes to Leo's configuration code.

The active-settings-outline command must recreate Leo's startup code 
*exactly*.  Subtle bugs will abound if it doesn't.  If the command *does* 
init settings correctly, gcm.config_iter shows how to find where settings 
come from. This is possible because settings contain the path of the file 
that define them.

*All theme files should defined @string theme-name to be the name of the 
theme file itself*.  If not, the theme file may not work properly. I plan 
to add a warning if this is not so.

Edward

-- 
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 [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/leo-editor/f899f49e-6fcd-4430-8b0b-45a4f8bcc440%40googlegroups.com.

Reply via email to