<?xml version="1.0" encoding="utf-8"?>
<!-- Created by Leo: http://leoeditor.com/leo_toc.html -->
<leo_file xmlns:leo="http://leoeditor.com/namespaces/leo-python-editor/1.1" >
<leo_header file_format="2" tnodes="0" max_tnode_index="0" clone_windows="0"/>
<globals body_outline_ratio="0.5" body_secondary_ratio="0.5">
	<global_window_position top="50" left="50" height="500" width="700"/>
	<global_log_window_position top="0" left="0" height="0" width="0"/>
</globals>
<preferences/>
<find_panel_settings/>
<vnodes>
<v t="ekr.20051031040240"><vh>Startup</vh>
<v t="ekr.20080412053100.5"><vh>@settings</vh>
<v t="ekr.20160122104332.1"><vh>Buttons</vh>
<v t="ekr.20150507170849.1"><vh>@@button create decorators</vh>
<v t="ekr.20150508063538.1"><vh>create_d</vh></v>
<v t="ekr.20150508071622.1"><vh>create_decorator</vh></v>
<v t="ekr.20150508063412.1"><vh>create_decorators</vh></v>
<v t="ekr.20150508074623.1"><vh>create_fixups</vh></v>
<v t="ekr.20150508063926.1"><vh>find_class</vh></v>
<v t="ekr.20150507174711.1"><vh>find_next_clone</vh></v>
<v t="ekr.20150507175246.1"><vh>munge_lines</vh></v>
<v t="ekr.20150508062944.1"><vh>run</vh></v>
</v>
<v t="ekr.20150509183433.1"><vh>@@button make-decorators2</vh>
<v t="ekr.20150509183433.2"><vh>create_d</vh></v>
<v t="ekr.20150509183433.3"><vh>create_decorator</vh></v>
<v t="ekr.20150509183433.4"><vh>create_decorators V2</vh></v>
<v t="ekr.20150509183832.1"><vh>define_s</vh></v>
<v t="ekr.20150509183433.8"><vh>munge_lines</vh></v>
<v t="ekr.20150509183433.9"><vh>run V2</vh></v>
</v>
<v t="ekr.20150703061709.1"><vh>@button run-pylint</vh></v>
<v t="ekr.20140902155015.18674"><vh>@bool qt-use-scintilla = False</vh></v>
<v t="ekr.20150531102337.1"><vh>@bool tidy-keep-blank-lines = False</vh></v>
<v t="ekr.20150617060607.1"><vh>@bool tidy_autobeautify = False</vh></v>
<v t="ekr.20150502050609.1"><vh>@button backup</vh></v>
<v t="ekr.20150413091056.1"><vh>@button check-clones</vh></v>
<v t="ekr.20131121084830.16362"><vh>@button toggle-debug</vh></v>
</v>
<v t="ekr.20150425145248.1"><vh>@data history-list</vh></v>
<v t="ekr.20140920064112.17946"><vh>@int fts_max_hits = 100</vh></v>
<v t="ekr.20140919093158.17876"><vh>@int max-pre-loaded-body-chars = 0</vh></v>
<v t="ekr.20131111060930.18010"><vh>@shortcuts</vh></v>
<v t="ekr.20140916101314.19538"><vh>@string target_language = python</vh></v>
</v>
<v t="ekr.20140103105930.16446"><vh>@views</vh>
<v t="ekr.20140102162014.16438"><vh>@view added strip_comments keyword arg to two config.getData methods</vh></v>
</v>
<v t="ekr.20140808103117.18035"><vh>@persistence</vh>
<v t="ekr.20140808103117.18038"><vh>@data:@auto ../plugins/qt_main.py</vh>
<v t="ekr.20160416133006.1"><vh>@gnxs</vh></v>
</v>
<v t="ekr.20140808103117.18040"><vh>@data:@auto ../plugins/qt_main.ui</vh>
<v t="ekr.20160415140808.2"><vh>@gnxs</vh></v>
</v>
<v t="ekr.20140808103117.18042"><vh>@data:@auto ../plugins/qt_quicksearch.py</vh>
<v t="ekr.20160406074930.4"><vh>@gnxs</vh></v>
</v>
<v t="ekr.20140808103117.18044"><vh>@data:@auto ../plugins/qt_quicksearch.ui</vh>
<v t="ekr.20160406074930.5"><vh>@gnxs</vh></v>
</v>
<v t="ekr.20150604130353.1"><vh>@data:@auto test/unit_tests.py</vh>
<v t="ekr.20150604130353.2"><vh>@gnxs</vh></v>
</v>
</v>
<v t="ekr.20140923085942.17943"><vh>Recent scripts</vh>
<v t="ekr.20140905060158.18560"><vh>Script: change body.x to body.wrapper.x</vh>
<v t="ekr.20140905060158.18561"><vh>test</vh></v>
</v>
<v t="ekr.20140918124632.19450"><vh>Script: print Qt color names</vh></v>
<v t="ekr.20160315161104.1"><vh>@test rt</vh>
<v t="ekr.20160315163533.1"><vh>&lt;&lt; imports &gt;&gt;</vh></v>
</v>
<v t="ekr.20160316065700.1"><vh>@@file ../test/report_test.py</vh>
<v t="ekr.20160316065936.1"><vh>&lt;&lt; includes &gt;&gt;</vh></v>
<v t="ekr.20160316070146.1"><vh>class TestClass</vh>
<v t="ekr.20160316070255.1"><vh>if_tests</vh></v>
<v t="ekr.20160316070709.1"><vh>comprehension_tests</vh></v>
<v t="ekr.20160316081359.1"><vh>try_tests</vh></v>
</v>
</v>
<v t="ekr.20160316100329.1"><vh>@button py2cs</vh>
<v t="ekr.20160316100524.1"><vh>cmd</vh></v>
<v t="ekr.20160316091923.1"><vh>ccc.python-to-coffeescript</vh>
<v t="ekr.20160316092837.1"><vh>class Python_To_Coffeescript_Adapter</vh>
<v t="ekr.20160316112717.1"><vh>py2cs.ctor</vh></v>
<v t="ekr.20160316093019.1"><vh>py2cs.main</vh></v>
<v t="ekr.20160316094011.7"><vh>py2cs.finalize</vh></v>
<v t="ekr.20160316094011.8"><vh>py2cs.to_coffeescript</vh></v>
<v t="ekr.20160316094011.9"><vh>py2cs.run</vh></v>
<v t="ekr.20160316141812.1"><vh>py2cs.strip_sentinels</vh></v>
</v>
</v>
</v>
</v>
</v>
<v t="EKR.20040430162943"><vh>Notes</vh>
<v t="ekr.20031218072017.329" descendentVnodeUnknownAttributes="7d71005805000000302e312e3071017d710228580b0000006c696e65594f666673657471034b005808000000616e6e6f7461746571047d710528580a000000707269736574646174657106580a000000323031352d30352d3237710758080000007072696f7269747971084d0f277575732e"><vh>@file ../doc/leoNotes.txt</vh></v>
</v>
<v t="ekr.20100120072650.6088"><vh>Projects</vh>
<v t="ekr.20100120072650.6089"><vh>@file ../doc/leoProjects.txt</vh></v>
</v>
<v t="ekr.20031218072017.2406"><vh>Code</vh>
<v t="ekr.20140902032918.18591"><vh> About this file</vh>
<v t="ekr.20140831085423.18639"><vh>About widgets and wrappers</vh></v>
<v t="ekr.20140831085423.18630"><vh>Terminology</vh></v>
<v t="ekr.20140831085423.18631"><vh>Official ivars</vh></v>
</v>
<v t="ekr.20031218072017.2604"><vh>Core classes</vh>
<v t="ekr.20031218072017.2608"><vh>@file leoApp.py</vh></v>
<v t="ekr.20141012064706.18389"><vh>@file leoAst.py</vh></v>
<v t="ekr.20150323150718.1"><vh>@file leoAtFile.py</vh></v>
<v t="ekr.20150521115018.1"><vh>@file leoBeautify.py</vh></v>
<v t="ekr.20070227091955.1"><vh>@file leoBridge.py</vh></v>
<v t="ekr.20100208065621.5894"><vh>@file leoCache.py</vh></v>
<v t="ekr.20070317085508.1"><vh>@file leoChapters.py</vh></v>
<v t="ekr.20150605175037.1"><vh>@file leoCheck.py</vh></v>
<v t="ekr.20031218072017.2794"><vh>@file leoColor.py</vh></v>
<v t="ekr.20140827092102.18574"><vh>@file leoColorizer.py</vh></v>
<v t="ekr.20031218072017.2810"><vh>@file leoCommands.py</vh></v>
<v t="ekr.20130925160837.11429"><vh>@file leoConfig.py</vh></v>
<v t="ekr.20160306114544.1"><vh>@file leoExternalFiles.py</vh></v>
<v t="ekr.20031218072017.3018"><vh>@file leoFileCommands.py</vh></v>
<v t="ekr.20031218072017.3093" descendentVnodeUnknownAttributes="7d71005806000000302e31372e3071017d71025808000000616e6e6f7461746571037d710473732e"><vh>@file leoGlobals.py</vh></v>
<v t="ekr.20150514154159.1"><vh>@file leoHistory.py</vh></v>
<v t="ekr.20031218072017.3206"><vh>@file leoImport.py</vh></v>
<v t="ekr.20120401063816.10072"><vh>@file leoIPython.py</vh></v>
<v t="ekr.20031218072017.3320"><vh>@file leoNodes.py</vh></v>
<v t="ekr.20140821055201.18331"><vh>@file leoPersistence.py</vh></v>
<v t="ekr.20031218072017.3439"><vh>@file leoPlugins.py</vh></v>
<v t="ekr.20150419124739.1"><vh>@file leoPrinting.py</vh></v>
<v t="ekr.20061024060248.1"><vh>@file leoPymacs.py</vh></v>
<v t="ekr.20140810053602.18074"><vh>@file leoQt.py</vh></v>
<v t="ekr.20140526082700.18440"><vh>@file leoRope.py</vh></v>
<v t="ekr.20090502071837.3"><vh>@file leoRst.py</vh></v>
<v t="ekr.20120420054855.14241" descendentVnodeUnknownAttributes="7d7100285805000000302e332e3371017d71022858090000007374725f6d74696d657103580d000000313332303432323639302e3534710458090000007374725f6174696d657105580d000000313332303433343235372e3336710658090000007374725f6374696d657107580c000000313331393436303438332e307108755805000000302e332e3571097d710a2858090000007374725f6d74696d65710b580c000000313331393634313131372e39710c58090000007374725f6174696d65710d580d000000313331393634313435352e3937710e58090000007374725f6374696d65710f580c000000313331393633383634382e307110755805000000302e332e3271117d71122858090000007374725f6d74696d657113580d000000313331393436373035302e3438711458090000007374725f6174696d657115580d000000313331393436373035302e3438711658090000007374725f6374696d657117580c000000313331393436303438332e307118755805000000302e332e3171197d711a2858090000007374725f6d74696d65711b580d000000313331393436373033382e3235711c58090000007374725f6174696d65711d580c000000313332303432323637302e39711e58090000007374725f6374696d65711f580c000000313331393436303438332e307120755805000000302e332e3071217d71222858090000007374725f6d74696d657123580d000000313331393439323330312e3532712458090000007374725f6174696d657125580d000000313331393534393339302e3839712658090000007374725f6374696d657127580c000000313331393439313330362e307128755805000000302e332e3471297d712a2858090000007374725f6d74696d65712b580d000000313331393634313038352e3038712c58090000007374725f6174696d65712d580c000000313331393634353330362e32712e58090000007374725f6374696d65712f580c000000313331393633383634382e30713075752e"><vh>@file leoSessions.py</vh></v>
<v t="ekr.20080708094444.1"><vh>@file leoShadow.py</vh></v>
<v t="ekr.20031218072017.3446"><vh>@file leoTangle.py</vh></v>
<v t="ekr.20031218072017.3603"><vh>@file leoUndo.py</vh></v>
<v t="ekr.20131109170017.16504"><vh>@file leoVim.py</vh></v>
</v>
<v t="ekr.20150514035207.1"><vh>Command classes</vh>
<v t="ekr.20150514035943.1"><vh>@file ../commands/baseCommands.py</vh></v>
<v t="ekr.20150514035236.1"><vh>@file ../commands/abbrevCommands.py</vh></v>
<v t="ekr.20150514035559.1"><vh>@file ../commands/bufferCommands.py</vh></v>
<v t="ekr.20150514040100.1"><vh>@file ../commands/controlCommands.py</vh></v>
<v t="ekr.20160316095222.1"><vh>@file ../commands/convertCommands.py</vh></v>
<v t="ekr.20150514040118.1"><vh>@file ../commands/debugCommands.py</vh></v>
<v t="ekr.20150514035813.1"><vh>@file ../commands/editCommands.py</vh></v>
<v t="ekr.20150514041209.1"><vh>@file ../commands/editFileCommands.py</vh></v>
<v t="ekr.20150624112334.1"><vh>@file ../commands/gotoCommands.py</vh></v>
<v t="ekr.20150514040138.1"><vh>@file ../commands/helpCommands.py</vh></v>
<v t="ekr.20150514040140.1"><vh>@file ../commands/keyCommands.py</vh></v>
<v t="ekr.20150514040142.1"><vh>@file ../commands/killBufferCommands.py</vh></v>
<v t="ekr.20150514040144.1"><vh>@file ../commands/macroCommands.py</vh></v>
<v t="ekr.20150514040146.1"><vh>@file ../commands/rectangleCommands.py</vh></v>
<v t="ekr.20150514040234.1"><vh>@file ../commands/registerCommands.py</vh></v>
<v t="ekr.20150514040239.1"><vh>@file ../commands/spellCommands.py</vh></v>
</v>
<v t="ekr.20031218072017.3625"><vh>Gui base classes</vh>
<v t="ekr.20050721093241"><vh>&lt;&lt; about gui classes and gui plugins &gt;&gt;</vh></v>
<v t="ekr.20031218072017.3630"><vh>@file leoCompare.py</vh></v>
<v t="ekr.20060123151617"><vh>@file leoFind.py</vh></v>
<v t="ekr.20031218072017.3655"><vh>@file leoFrame.py</vh></v>
<v t="ekr.20031218072017.3719"><vh>@file leoGui.py</vh></v>
<v t="ekr.20061031131434"><vh>@file leoKeys.py</vh></v>
<v t="ekr.20031218072017.3749"><vh>@file leoMenu.py</vh></v>
</v>
<v t="ekr.20110605121601.17862"><vh>Qt gui</vh>
<v t="ekr.20120419093256.10048"><vh>@file ../plugins/free_layout.py</vh></v>
<v t="ekr.20110605121601.17954"><vh>@file ../plugins/nested_splitter.py</vh></v>
<v t="ekr.20140919181357.24956"><vh>@file ../plugins/qt_big_text.py</vh></v>
<v t="ekr.20110605121601.17996"><vh>@file ../plugins/qt_commands.py</vh></v>
<v t="ekr.20140907103315.18766"><vh>@file ../plugins/qt_events.py</vh></v>
<v t="ekr.20140907123524.18774"><vh>@file ../plugins/qt_frame.py</vh></v>
<v t="ekr.20140907085654.18699"><vh>@file ../plugins/qt_gui.py</vh></v>
<v t="ekr.20140907103315.18777"><vh>@file ../plugins/qt_idle_time.py</vh></v>
<v t="ekr.20140907123524.18777"><vh>@file ../plugins/qt_quickheadlines.py</vh></v>
<v t="ekr.20140831085423.18598"><vh>@file ../plugins/qt_text.py</vh></v>
<v t="ekr.20140907131341.18707"><vh>@file ../plugins/qt_tree.py</vh></v>
<v t="ekr.20110605121601.18002"><vh>@file ../plugins/qtGui.py</vh></v>
<v t="ekr.20110605121601.18695"><vh>QDesigner files</vh>
<v t="ekr.20110605121601.18698"><vh>@auto ../plugins/qt_main.py</vh></v>
<v t="ekr.20110605121601.18703"><vh>@auto ../plugins/qt_main.ui</vh></v>
<v t="ekr.20110605121601.18704"><vh>@auto ../plugins/qt_quicksearch.py</vh></v>
<v t="ekr.20110605121601.18709"><vh>@auto ../plugins/qt_quicksearch.ui</vh></v>
</v>
</v>
<v t="ekr.20160226082945.1"><vh>PyInstaller</vh>
<v t="ekr.20160124165611.1"><vh>@file ../../launchLeo-unified.spec</vh></v>
</v>
<v t="ekr.20090802181029.5989"><vh>Startup &amp; external files</vh>
<v t="ekr.20150304125314.4"><vh>@clean ../../leo_to_html.xsl</vh>
<v t="ekr.20150304130753.5"><vh>&lt;&lt;style&gt;&gt;</vh></v>
<v t="ekr.20150304130753.6"><vh>&lt;&lt;scripts&gt;&gt;</vh></v>
</v>
<v t="ekr.20150326145530.1"><vh>@clean ../modes/forth.py</vh>
<v t="ekr.20150326145530.2"><vh>&lt;&lt; define mode rules &gt;&gt;</vh></v>
<v t="ekr.20150326145530.3"><vh>&lt;&lt; define mode data &gt;&gt;</vh></v>
<v t="ekr.20150326145530.4"><vh>&lt;&lt; define extendForth class &gt;&gt;</vh>
<v t="ekr.20150326145530.5"><vh>ctor</vh></v>
<v t="ekr.20150326145530.6"><vh>init &amp; helper</vh>
<v t="ekr.20150326145530.7"><vh>splitList</vh></v>
</v>
<v t="ekr.20150326145530.8"><vh>createBracketRules &amp; helper</vh></v>
<v t="ekr.20150326145530.9"><vh>createDefiningWordRules &amp; helper</vh></v>
<v t="ekr.20150326145530.10"><vh>createKeywords</vh></v>
<v t="ekr.20150326145530.11"><vh>createStringRule</vh></v>
<v t="ekr.20150326145530.12"><vh>extendRulesDict</vh></v>
</v>
</v>
<v t="ekr.20160123142722.1"><vh>@clean ../external/make_stub_files.cfg</vh></v>
<v t="ekr.20160317054700.1"><vh>@file ../external/make_stub_files.py</vh></v>
<v t="ekr.20160316091132.1"><vh>@file ../external/py2cs.py</vh></v>
<v t="ekr.20160316091152.1"><vh>@clean ../external/py2cs_theory.md</vh>
<v t="ekr.20160316091152.2"><vh>The problem</vh></v>
<v t="ekr.20160316091152.3"><vh>The design</vh></v>
<v t="ekr.20160316091152.4"><vh>Using TokenSync class</vh></v>
<v t="ekr.20160316091152.5"><vh>Summary</vh></v>
</v>
<v t="ekr.20110310091639.14254"><vh>@file ../external/codewise.py</vh></v>
<v t="ekr.20130805134749.12436"><vh>@file ../external/edb.py</vh></v>
<v t="ekr.20120519121124.9919"><vh>@file ../external/leosax.py</vh></v>
<v t="ville.20091010232339.6117"><vh>@file ../external/lproto.py</vh></v>
<v t="ekr.20031218072017.2605"><vh>@file runLeo.py</vh></v>
<v t="ekr.20150304130753.4"><vh>leo-viewer/leo_to_html.xsl</vh></v>
</v>
<v t="ekr.20080730161153.8"><vh>Testing</vh>
<v t="ekr.20100221142603.5638"><vh>@file ../../pylint-leo.py</vh></v>
<v t="ekr.20080730161153.2"><vh>@file leoBridgeTest.py</vh></v>
<v t="ekr.20080730161153.5"><vh>@file leoDynamicTest.py</vh></v>
<v t="ekr.20051104075904" descendentVnodeUnknownAttributes="7d710058010000003071017d71025808000000616e6e6f746174657103285808000000616e6e6f7461746571047d710574710673732e"><vh>@file leoTest.py</vh></v>
</v>
<v t="ekr.20090802181029.5988"><vh>Version</vh>
<v t="ekr.20090717092906.12765"><vh>@file leoVersion.py</vh></v>
</v>
</v>
</vnodes>
<tnodes>
<t tx="EKR.20040430162943"></t>
<t tx="ekr.20031218072017.2406" _bklnk="7d71002858010000007871014b0058010000007971024b00580400000074797065710358080000006e6f646552656374710458050000006c696e6b7371055d7106752e"># This file contains almost all of Leo's sources.
# See the "About this file" node for important notes.
@language python
</t>
<t tx="ekr.20031218072017.2604"></t>
<t tx="ekr.20031218072017.3625">&lt;&lt; about gui classes and gui plugins &gt;&gt;
</t>
<t tx="ekr.20050721093241">@nobeautify
@nocolor

The following are notes for anyone who is interested in writing
alternate gui's for Leo.

Rule 1: Leo's core is (or should be) free of gui-specific code.

Core code calls 'gui wrapper methods' defined by gui-specific classes.
The base classes for these gui-specific classes are in the node
Code--&gt;Gui Base classes.

Rule 2: Gui-specific code should be localized.

The @file nodes contained in the node 'Code--&gt;Gui Tkinter classes' in
leoPy.leo contain all of Leo's Tkinter-specific code. Gui plugins
would typically put all similar code in a single file.

Rule 3: Gui-specific code can call gui methods directly.

There are no restrictions about the code in the gui-specific classes.

Rule 4: Gui-specific classes must implement the 'gui wrapper methods'
specified in the gui base classes.

This is the way that gui-specific classes provide gui-specific
services to Leo's core.

The alternative would be to implement all gui-specific commands
directly in the gui-specific code.  But this would be much more work
than needed.  For example, only a few gui-specific wrappers are needed
to implement all commands that deal with body text.  Implementing each
of these commands 'from scratch' would duplicate a lot of code
unnecessarily.

Using the gui wrapper methods is a bit messy for two reasons:

1. It requires defining enough wrappers (both in the base gui classes
   and subclasses) so that all gui-specific services needed by Leo's
   core are available.  Adding a wrapper to a gui base class involves
   adding it to all gui-specific subclasses.  It's easy to forget to
   add a wrapper.  The gui base class defines all wrappers as a
   function that just calls oops().  This prints a warning that the
   wrapper should be defined in a subclass.

2. The original wrappers assumed Tkinter-like indices.  Wrappers that
   were defined later assume Python indices (see Rule 5 below).  The
   newer style wrappers that use Python indices have 'Python' in their
   name.  Having two sets of wrappers is one of the ugliest features
   of the present code.  I find it hard to remember which wrappers
   exist and what exactly they do :-)

Rule 5: Leo's core should use Python indices, not gui-specific
indices.

Leo's core mostly follows this rule: there may be a few exceptions.

A Python index is an int that runs from 0 (beginning of text) to
len(s) (end of text s).  That is, there are exactly len(s) + 1 valid
indices.  In contrast, Tkinter indices run from "1.0" to "x.y" where
text s has x lines and where the length of the last line is y-1.

Two (recently written) functions in leoGlobals.py support conversions
from Python indices to the row/column indices used by Tkinter.

- g.convertPythonIndexToRowCol converts a Python index to a row/column
  index used by Tkinter.

- g.convertRowColToPythonIndex does the reverse.

Important: the first Tkinter index is '1.0', not '0.0', but the row
returned by g.convertPythonIndexToRowCol is zero based, so the code
that actually creates Tkinter indices from row/col must add 1 to the
row.  Similar remarks apply when going in the reverse direction.
</t>
<t tx="ekr.20051031040240"></t>
<t tx="ekr.20080412053100.5">@language rest
</t>
<t tx="ekr.20080730161153.8"></t>
<t tx="ekr.20090802181029.5988"></t>
<t tx="ekr.20090802181029.5989"></t>
<t tx="ekr.20100120072650.6088"></t>
<t tx="ekr.20110605121601.17862"># These files are true plugins, but it is more convenient to put them here.
</t>
<t tx="ekr.20110605121601.18695">@nobeautify
</t>
<t tx="ekr.20131111060930.18010"># Standard bindings...
# run-selected-unit-tests-externally = Alt-4 
# run-marked-unit-tests-externally = Alt-5
# run-marked-unit-tests-locally = Alt-6
run-selected-unit-tests-locally = Alt-4
do-nothing = Alt-5
run-marked-unit-tests-locally = Alt-6
</t>
<t tx="ekr.20131121084830.16362">@language python

# Toggle the settings.
g.app.debug_app = not g.app.debug_app
g.app.debug_widgets = not g.app.debug_widgets
# Report the new settings.
print('g.app.debug_app: %s' % g.app.debug_app)
print('g.app.debug_widgets: %s' % g.app.debug_widgets)
</t>
<t tx="ekr.20140102162014.16438">unl: Code--&gt;Core classes--&gt;@file leoConfig.py--&gt;class LocalConfigManager--&gt;c.config.Getters--&gt;c.config.Getters--&gt;c.config.getData
unl: Code--&gt;Core classes--&gt;@file leoConfig.py--&gt;class GlobalConfigManager--&gt;gcm.Getters...--&gt;gcm.getData &amp; getOutlineData
unl: Code--&gt;Core classes--&gt;@file leoConfig.py--&gt;&lt;&lt; class ParserBaseClass &gt;&gt;--&gt;kind handlers (ParserBaseClass)--&gt;doData
@language rest
The parser for @data nodes, doData, no longer strips *anything*.</t>
<t tx="ekr.20140103105930.16446"></t>
<t tx="ekr.20140808103117.18035"></t>
<t tx="ekr.20140808103117.18038">gnx: ekr.20110605121601.18698
</t>
<t tx="ekr.20140808103117.18040">gnx: ekr.20110605121601.18703
</t>
<t tx="ekr.20140808103117.18042">gnx: ekr.20110605121601.18704
</t>
<t tx="ekr.20140808103117.18044">gnx: ekr.20110605121601.18709
</t>
<t tx="ekr.20140831085423.18630">This outline contains all of Leo's core source code.

Leo's code uses the following conventions throughout:

c:  a commander.
ch: a character.
d:  a dialog or a dict.
f:  an open file.
fn: a file name.
g:  the leoGlobals module.
i, j, k: indices into a string.
p:  a Position.
s:  a string.
t:  a text widget.
u:  an undoer.
w:  a gui widget.
v:  a Vnode
z:  a local temp.

In more limited contexts, the following conventions apply:

si:     a g.ShortcutInfo object.
ks:     a g.KeyStroke object
stroke: a KeyStroke object.

btw:    leoFrame.BaseTextWrapper
stw:    leoFrame.StringTextWrapper

bqtw:   qt_text.BaseQTextWrapper
lqtb:   qt_text.LeoQTextBrowser
qhlw:   qt_text.QHeadlineWrapper
qmbw:   qt_text.QMinibufferWrapper
qlew:   qt_text.QLineEditWrapper
qsciw:  qt_text.QScintiallaWrapper
qtew:   qt_text.QTextEditWrapper</t>
<t tx="ekr.20140831085423.18631">The following 'official' ivars will always exist:

c.frame                 The frame containing the log,body,tree, etc.
c.frame.body            The body pane.
c.frame.body.widget     The gui widget for the body pane.
c.frame.body.wrapper    The high level interface for the body widget.
c.frame.iconBar         The icon bar.
c.frame.log             The log pane.
c.frame.log.widget      The gui widget for the log pane.
c.frame.log.wrapper     The high-level inteface for the log pane.
c.frame.tree            The tree pane.

The following were official ivars that no longer exist:

c.frame.body.bodyCtrl   Use c.frame.body.wrapper instead.
c.frame.log.logCtrl     Use c.frame.log.wrapper instead.
</t>
<t tx="ekr.20140831085423.18639">Here is what you *must know* to understand Leo's core:

1. A **widget** is an actual Qt widget.

Leo's core seldom accesses widgets directly.  Instead...

2. A **wrapper class** defines a standard api that hides the details
   of the underlying gui **text** widgets.

Leo's core uses this api almost exclusively. That is, Leo's core code treats wrappers *as if* they were only text widgets there are!

There is, however, a back door for (hopefully rare!) special cases. All wrapper classes define an official ``widget`` ivar, so core or plugin code can gain access to the real Qt widget using wrapper.widget. Searching for wrapper.widget will find all gui-dependent snippets of code in Leo's core.
</t>
<t tx="ekr.20140902032918.18591">@
@language rest
@wrap

Leo uses a model/view/controller architecture.

- Controller: The Commands class and its helpers in leoCommands.py and leoEditCommands.py.

- Model: The VNode and Position classes in leoNodes.py.

- View: The gui-independent base classes are in the node "Gui Base Classes". The Qt-Specific subclasses are in the node "Qt gui".

**Important**: The general organization of these classes have changed hardly at all in Leo's 20+ year history.  The reason is that what each class does is fairly obvious.  How the gets the job done may have changed drastically, but *that's an internal implementation detail of the class itself*.  This is the crucial design principle that allows Leo's code to remain stable.  *Classes do not know or meddle in the internal details of other classes*.  As a result, nobody, including EKR, needs to remember internal details.
@c
</t>
<t tx="ekr.20140902155015.18674"></t>
<t tx="ekr.20140905060158.18560">'''
A script to replace body.x with body.wrapper.x for all x in the WrapperAPI.

It is *not undoable* to save massive amounts of memory.
Please run on an already-saved .leo file, and take all other reasonable precautions.

If replace is False, it will just report the changes to be made.
'''
import leo.core.leoFrame as leoFrame
replace = False
aList = sorted([z for z in dir(leoFrame.WrapperAPI) if not z.startswith('__')])
nodes = 0
for p in c.all_unique_positions():
    s = p.b
    nodes += 1
    found = False
    for target in aList:
        i = 0
        pattern = 'body.' + target
        while True:
            i = s.find(pattern,i)
            if i == -1:
                break
            if g.match_word(s,i,pattern):
                if not found:
                    print('In node: %s' % p.h)
                    found = True
                i1,i2 = g.getLine(s,i)
                if replace:
                    j = i + len('body.')
                    s = s[:j] + 'wrapper.' + s[j:]
                    print(s[i1:i2+len('wrapper.')].rstrip())
                    i += len('wrapper.') + len(pattern)
                else:
                    print(s[i1:i2].rstrip())
                    i += len(pattern)
            else:
                i += len(pattern)
    if found and replace:
        p.b = s
print('searched %s nodes' % nodes)
</t>
<t tx="ekr.20140905060158.18561">a = c.frame.body.getInsertPoint()

aBody.getInsertPoint()

if 1:
    c.frame.body.setInsertPoint(0)</t>
<t tx="ekr.20140916101314.19538">The default language if no @language or @comment is in effect.

Valid values are (case is ignored):

actionscript,c,csharp,css,cweb,elisp,html,java,latex,
pascal,perl,perlpod,php,plain,plsql,python,rapidq,rebol,shell,tcltk.</t>
<t tx="ekr.20140918124632.19450"># This script prints the list of known Qt names. Qt seems to ignore case.
from leo.core.leoQt import QtGui
aList = sorted([g.u(z) for z in QtGui.QColor().colorNames()])
print('\n'.join(aList))
</t>
<t tx="ekr.20140919093158.17876"></t>
<t tx="ekr.20140920064112.17946"></t>
<t tx="ekr.20140923085942.17943"></t>
<t tx="ekr.20150304125314.4">@tabwidth -2
@killbeautify
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;

&lt;xsl:output method="html" version="1.0" encoding="UTF-8" indent="yes"/&gt;

&lt;!-- The default setting. Not needed unless there is a strip-space element. --&gt;
  &lt;!-- &lt;xsl:preserve-space elements='leo_file nodes t'/&gt; --&gt;

&lt;xsl:template match ='leo_file'&gt;
&lt;html&gt;
  &lt;head&gt;
    &lt;!--
    &lt;link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/8.9.1/styles/default.min.css"&gt;
    &lt;script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/8.9.1/highlight.min.js"&gt;&lt;/script&gt;
    --&gt;
    &lt;&lt;style&gt;&gt;
    &lt;&lt;scripts&gt;&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;xsl:apply-templates select='tnodes'/&gt;
    &lt;div class="outlinepane"&gt;
      &lt;!-- &lt;h4&gt;Outline Pane&lt;/h4&gt; --&gt;
      &lt;xsl:apply-templates select='vnodes'/&gt;
    &lt;/div&gt;
    &lt;div class="bodypane"&gt;
      &lt;!-- &lt;h4&gt;Body Pane&lt;/h4&gt; --&gt;
      &lt;pre class="body-text"&gt;&lt;code&gt;&lt;/code&gt;&lt;/pre&gt;
    &lt;/div&gt;
  &lt;/body&gt;
&lt;/html&gt;
&lt;/xsl:template&gt;

&lt;xsl:template match = 'tnodes'&gt;
&lt;div class="tnodes"&gt;
  &lt;xsl:for-each select = 't'&gt;
    &lt;div class="tnode"&gt;
      &lt;xsl:attribute name="id"&gt;&lt;xsl:value-of select='@tx'/&gt;&lt;/xsl:attribute&gt;
      &lt;xsl:value-of select='.'/&gt;
    &lt;/div&gt;
  &lt;/xsl:for-each&gt;
&lt;/div&gt;
&lt;/xsl:template&gt;

&lt;xsl:template match = 'vnodes'&gt;
  &lt;xsl:for-each select = 'v'&gt;
    &lt;xsl:apply-templates select ='.'/&gt;
  &lt;/xsl:for-each&gt;
&lt;/xsl:template&gt;

&lt;xsl:template match='v'&gt;
  &lt;div class="node"&gt;
    &lt;xsl:attribute name="id"&gt;&lt;xsl:value-of select='@t'/&gt;&lt;/xsl:attribute&gt;
    &lt;xsl:choose&gt;
      &lt;xsl:when test ='./v' &gt;
        &lt;xsl:attribute name="has-children"&gt;1&lt;/xsl:attribute&gt;
        &lt;h1&gt;+ &lt;xsl:value-of select='vh'/&gt;&lt;/h1&gt;
        &lt;xsl:apply-templates select = 'v'/&gt;
      &lt;/xsl:when&gt;
      &lt;xsl:when test ='vh' &gt;
        &lt;h1&gt;- &lt;xsl:value-of select='vh'/&gt;&lt;/h1&gt;
      &lt;/xsl:when&gt;
      &lt;!--
      &lt;xsl:otherwise&gt;
        &lt;h1&gt;- &lt;xsl:value-of select='vh'/&gt;&lt;/h1&gt;
      &lt;/xsl:otherwise&gt;
      --&gt;
    &lt;/xsl:choose&gt;
  &lt;/div&gt;
&lt;/xsl:template&gt;

&lt;/xsl:stylesheet&gt;
</t>
<t tx="ekr.20150304130753.4">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;

&lt;xsl:output method="html" version="1.0" encoding="UTF-8" indent="yes"/&gt;

&lt;!-- The default setting. Not needed unless there is a strip-space element. --&gt;
  &lt;!-- &lt;xsl:preserve-space elements='leo_file nodes t'/&gt; --&gt;

&lt;xsl:template match ='leo_file'&gt;
&lt;html&gt;
  &lt;head&gt;
    &lt;&lt;style&gt;&gt;
    &lt;&lt;scripts&gt;&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;xsl:apply-templates select='tnodes'/&gt;
    &lt;div class="outlinepane"&gt;
      &lt;xsl:apply-templates select='vnodes'/&gt;
    &lt;/div&gt;
    &lt;div class="bodypane"&gt;
      &lt;h1&gt;Body Pane&lt;/h1&gt;
      &lt;pre class="body-text"&gt;body&lt;/pre&gt;
    &lt;/div&gt;
  &lt;/body&gt;
&lt;/html&gt;
&lt;/xsl:template&gt;

&lt;xsl:template match = 'tnodes'&gt;
&lt;div class="tnodes"&gt;
  &lt;xsl:for-each select = 't'&gt;
    &lt;div class="tnode"&gt;
      &lt;xsl:attribute name="id"&gt;&lt;xsl:value-of select='@tx'/&gt;&lt;/xsl:attribute&gt;
      &lt;xsl:value-of select='.'/&gt;
    &lt;/div&gt;
  &lt;/xsl:for-each&gt;
&lt;/div&gt;
&lt;/xsl:template&gt;

&lt;xsl:template match = 'vnodes'&gt;
  &lt;xsl:for-each select = 'v'&gt;
    &lt;xsl:apply-templates select ='.'/&gt;
  &lt;/xsl:for-each&gt;
&lt;/xsl:template&gt;

&lt;xsl:template match='v'&gt;
  &lt;div class="node"&gt;
    &lt;xsl:attribute name="id"&gt;&lt;xsl:value-of select='@t'/&gt;&lt;/xsl:attribute&gt;
    &lt;h1&gt;&lt;xsl:value-of select='vh'/&gt;&lt;/h1&gt;
    &lt;xsl:if test ='./v' &gt;
      &lt;xsl:apply-templates select = 'v'/&gt;
    &lt;/xsl:if&gt;
  &lt;/div&gt;
&lt;/xsl:template&gt;

&lt;/xsl:stylesheet&gt;
</t>
<t tx="ekr.20150304130753.5">&lt;style&gt;
    /* pre { background:#FFE7C6; } */
    /* Must use h1 for nodes: see below. */
    h1 {
      font-size: 12pt;
      font-style: normal;
      font-weight: normal;
    }
    div.outlinepane {
      position: absolute;
      background: #ffffec; /* Leo yellow */
      top: 10px;
      height: 300px;
      width: 700px;
      overflow: scroll;
      line-height: 0.8;

    }
    div.bodypane {
      position: absolute;
      top: 310px;
      height: 300px;
      width: 700px;
      overflow: scroll;
    }
    div.tnode {
        visibility: hidden;
        height: 0;
    }
    div.node {
        position: relative;
        left: 20px;
    }
    div.node[has-children] &gt; h1 {
        &lt;!-- works --&gt;
        &lt;!-- background: red; --&gt;
    }
&lt;/style&gt;
</t>
<t tx="ekr.20150304130753.6">@language javascript

&lt;script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"&gt;&lt;/script&gt;
&lt;script&gt;

  $(document).ready(function(){
    if (true) {
        // Toggle all but top-level nodes.
        // This requires an indication
        $(".node").toggle()
        $(".outlinepane").children(".node").toggle()
    } else {
        // Toggle all second-level nodes.
        // Safer, until we can see which nodes have children.
        $(".outlinepane").children(".node").children(".node").toggle()
    }
    $("h1").click(function(){
      $(this).parent().children("div.node").toggle();
      // The parent div's id is v.x.
      // Find the tnode div whose id is t.x.
      console.clear();
      parent_id=$(this).parent().attr("id");
      if (parent_id) {
        target=$(this).parent().attr("id").substring(2);
          console.log("clicked:"+$(this).text())
          // console.log("parent:"+$(this).parent())
          // console.log("target:"+target)
        $(".tnode").each(function(){
          console.log($(this).attr("id"))
          target2=$(this).attr("id").substring(2);
          if (target === target2) {
            console.log("found:"+target2)
            // $("pre.body-text").text($(this).text());
            $("code").text($(this).text());
          };
        }); // end .each.
      };
    });
  });
@language html
&lt;/script&gt;
</t>
<t tx="ekr.20150326145530.1"># Hand-written Leo colorizer control file for forth mode.
# This file is in the public domain.
@killbeautify
import leo.core.leoGlobals as g
&lt;&lt; define mode rules &gt;&gt;
&lt;&lt; define mode data &gt;&gt;
&lt;&lt; define extendForth class &gt;&gt;
e = extendForth()

def pre_init_mode(c):
    # g.trace('modes/forth.py',c)
    e.c = c
    e.init()
    e.createKeywords()
    e.createBracketRules()
    e.createDefiningWordRules()
</t>
<t tx="ekr.20150326145530.10">def createKeywords(self):
    '''Create the mode keyword table and
    entries in the rulesDict for the forth_keyword_rule'''
    # global forth_main_keywords_dict
    # global forth_keyword_rule
    table = (
        (self.keywords, 'keyword1'),
      # (self.definingwords,    'keyword2'), # Done in createDefiningWordRules.
        (self.boldwords, 'keyword3'),
        (self.bolditalicwords, 'keyword4'),
        (self.italicwords, 'keyword5'),
        (self.stringwords, 'string'),
    )
    d = forth_main_keywords_dict
    for keywordList, kind in table:
        for z in keywordList:
            # Create the entry in the keyword table.
            if kind == 'string':
                func = self.createStringRule(d, z)
            else:
                func = forth_keyword_rule
            # Always make the entry.
            d[z] = kind
            self.extendRulesDict(ch=z[0], func=func)
</t>
<t tx="ekr.20150326145530.11">def createStringRule(self, d, pair):
    '''Create an entry in d for a string keyword.'''
    aList = pair.split(' ')
    if len(aList) != 2:
        g.trace('can not happen: expecting pair of forth strings:', pair)
        return
    begin, end = aList

    def forth_string_word_rule(colorer, s, i):
        return colorer.match_span(s, i, kind="literal1", begin=begin.strip(), end=end.strip(),
            at_line_start=False, at_whitespace_end=False, at_word_start=True, # Require word.
            delegate="", exclude_match=False,
            no_escape=False, no_line_break=False, no_word_break=False) # Don't require ending word.

    return forth_string_word_rule
</t>
<t tx="ekr.20150326145530.12">def extendRulesDict(self, ch, func):
    global rulesDict
    # Extend the rulesDict entry for the first character of z.
    aList = rulesDict.get(ch, [])
    if func not in aList:
        aList.append(func)
        rulesDict[ch] = aList
    # g.trace(z,kind)
</t>
<t tx="ekr.20150326145530.2"># Rules for forth_main ruleset.

def forth_block_comment_rule(colorer, s, i):
    return colorer.match_span(s, i, kind="comment2", begin="(", end=")",
        at_line_start=False, at_whitespace_end=False, at_word_start=True, # Require word.
        delegate="", exclude_match=False,
        no_escape=False, no_line_break=False, no_word_break=False)

def forth_comment_rule(colorer, s, i):
    return colorer.match_eol_span(s, i, kind="comment1", seq="\\",
        at_line_start=False, at_whitespace_end=False, at_word_start=True, # Require word
        delegate="", exclude_match=False)

def forth_keyword_rule(colorer, s, i):
    return colorer.match_keywords(s, i)

def forth_string_rule(colorer, s, i):
    return colorer.match_span(s, i, kind="literal1", begin="\"", end="\"",
        at_line_start=False, at_whitespace_end=False, at_word_start=True, # Require word
        delegate="", exclude_match=False,
        no_escape=False, no_line_break=False, no_word_break=False)
# ==========================

if 0:

    def forth_rule0(colorer, s, i):
        return colorer.match_eol_span(s, i, kind="comment1", seq="#",
            at_line_start=False, at_whitespace_end=False, at_word_start=False,
            delegate="", exclude_match=False)

    def forth_rule1(colorer, s, i):
        return colorer.match_span(s, i, kind="literal2", begin="\"\"\"", end="\"\"\"",
            at_line_start=False, at_whitespace_end=False, at_word_start=False,
            delegate="", exclude_match=False,
            no_escape=False, no_line_break=False, no_word_break=False)

    def forth_rule2(colorer, s, i):
        return colorer.match_span(s, i, kind="literal2", begin="'''", end="'''",
            at_line_start=False, at_whitespace_end=False, at_word_start=False,
            delegate="", exclude_match=False,
            no_escape=False, no_line_break=False, no_word_break=False)

    def forth_rule3(colorer, s, i):
        return colorer.match_span(s, i, kind="literal1", begin="\"", end="\"",
            at_line_start=False, at_whitespace_end=False, at_word_start=False,
            delegate="", exclude_match=False,
            no_escape=False, no_line_break=False, no_word_break=False)

    def forth_rule4(colorer, s, i):
        return colorer.match_span(s, i, kind="literal1", begin="'", end="'",
            at_line_start=False, at_whitespace_end=False, at_word_start=False,
            delegate="", exclude_match=False,
            no_escape=False, no_line_break=False, no_word_break=False)

    def forth_rule5(colorer, s, i):
        return colorer.match_seq(s, i, kind="operator", seq="=",
            at_line_start=False, at_whitespace_end=False, at_word_start=False, delegate="")

    def forth_rule6(colorer, s, i):
        return colorer.match_seq(s, i, kind="operator", seq="!",
            at_line_start=False, at_whitespace_end=False, at_word_start=False, delegate="")

    def forth_rule7(colorer, s, i):
        return colorer.match_seq(s, i, kind="operator", seq="&gt;=",
            at_line_start=False, at_whitespace_end=False, at_word_start=False, delegate="")

    def forth_rule8(colorer, s, i):
        return colorer.match_seq(s, i, kind="operator", seq="&lt;=",
            at_line_start=False, at_whitespace_end=False, at_word_start=False, delegate="")

    def forth_rule9(colorer, s, i):
        return colorer.match_seq(s, i, kind="operator", seq="+",
            at_line_start=False, at_whitespace_end=False, at_word_start=False, delegate="")

    def forth_rule10(colorer, s, i):
        return colorer.match_seq(s, i, kind="operator", seq="-",
            at_line_start=False, at_whitespace_end=False, at_word_start=False, delegate="")

    def forth_rule11(colorer, s, i):
        return colorer.match_seq(s, i, kind="operator", seq="/",
            at_line_start=False, at_whitespace_end=False, at_word_start=False, delegate="")

    def forth_rule12(colorer, s, i):
        return colorer.match_seq(s, i, kind="operator", seq="*",
            at_line_start=False, at_whitespace_end=False, at_word_start=False, delegate="")

    def forth_rule13(colorer, s, i):
        return colorer.match_seq(s, i, kind="operator", seq="&gt;",
            at_line_start=False, at_whitespace_end=False, at_word_start=False, delegate="")

    def forth_rule14(colorer, s, i):
        return colorer.match_seq(s, i, kind="operator", seq="&lt;",
            at_line_start=False, at_whitespace_end=False, at_word_start=False, delegate="")

    def forth_rule15(colorer, s, i):
        return colorer.match_seq(s, i, kind="operator", seq="%",
            at_line_start=False, at_whitespace_end=False, at_word_start=False, delegate="")

    def forth_rule16(colorer, s, i):
        return colorer.match_seq(s, i, kind="operator", seq="&amp;",
            at_line_start=False, at_whitespace_end=False, at_word_start=False, delegate="")

    def forth_rule17(colorer, s, i):
        return colorer.match_seq(s, i, kind="operator", seq="|",
            at_line_start=False, at_whitespace_end=False, at_word_start=False, delegate="")

    def forth_rule18(colorer, s, i):
        return colorer.match_seq(s, i, kind="operator", seq="^",
            at_line_start=False, at_whitespace_end=False, at_word_start=False, delegate="")

    def forth_rule19(colorer, s, i):
        return colorer.match_seq(s, i, kind="operator", seq="~",
            at_line_start=False, at_whitespace_end=False, at_word_start=False, delegate="")

    def forth_rule20(colorer, s, i):
        return colorer.match_mark_previous(s, i, kind="function", pattern="(",
            at_line_start=False, at_whitespace_end=False, at_word_start=False, exclude_match=True)

    def forth_rule21(colorer, s, i):
        return colorer.match_keywords(s, i)
</t>
<t tx="ekr.20150326145530.3"># Properties for forth mode.
properties = {
	# "indentNextLines": "\\s*[^#]{3,}:\\s*(#.*)?",
    "lineComment": "\\",
}
# Attributes dict for forth_main ruleset.
forth_main_attributes_dict = {
    "default": "null",
    "digit_re": "",
	# "escape": "\\",
    "highlight_digits": "false",
    "ignore_case": "false",
    "no_word_sep": "",
}
# Dictionary of attributes dictionaries for forth mode.
attributesDictDict = {
    "forth_main": forth_main_attributes_dict,
}
# Keywords dict for forth_main ruleset.
forth_main_keywords_dict = {} # Created by extendForth class.
# Dictionary of keywords dictionaries for forth mode.
keywordsDictDict = {
    "forth_main": forth_main_keywords_dict,
}
# Rules dict for forth_main ruleset.
# This is extended by extendForth.
rulesDict = {
    '(': [forth_block_comment_rule],
    '\\': [forth_comment_rule],
    '"': [forth_string_rule],
}
# x.rulesDictDict for forth mode.
rulesDictDict = {
    "forth_main": rulesDict,
}
# Import dict for forth mode.
importDict = {}
</t>
<t tx="ekr.20150326145530.4">class extendForth:
    '''A helper class to extend the mode tables from @data forth-x settings.'''
    @others
</t>
<t tx="ekr.20150326145530.5">def __init__(self):
    self.c = None # set by pre_init_mode function.
    # g.trace('modes/forth.py:extendForth')
    # Default forth keywords: extended by @data forth-words
    # Forth words to be rendered in boldface: extended by @data forth-bold-words
    self.boldwords = []
    # Forth bold-italics words: extended by @data forth-bold-italic-words
    # Note: on some boxen, bold italics may show in plain bold.
    self.bolditalicwords = []
    # Forth words that define brackets: extended by @data forth-delimiter-pairs
    self.brackets = [] # Helper: a list of tuples.
    self.brackets1 = []
    self.brackets2 = []
    # Words which define other words: extended by forth-defwords
    self.definingwords = []
    # Forth words to be rendered in italics: extended by forth-italic-words
    self.italicwords = []
    # Default keywords: extended by @data forth-keywords
    self.keywords = []
        # "variable", "constant", "code", "end-code",
        # "dup", "2dup", "swap", "2swap", "drop", "2drop",
        # "r&gt;", "&gt;r", "2r&gt;", "2&gt;r",
        # "if", "else", "then",
        # "begin", "again", "until", "while", "repeat",
        # "v-for", "v-next", "exit",
        # "meta", "host", "target", "picasm", "macro",
        # "needs", "include",
        # "'", "[']",
        # # ":", # Now a defining word.
        # ";",
        # "@", "!", ",", "1+", "+", "-",
        # "&lt;", "&lt;=", "=", "&gt;=", "&gt;",
        # "invert", "and", "or",
    # Forth words which start strings: extended by @data forth-string-word-pairs
    self.stringwords = []
    self.stringwords1 = []
    self.stringwords2 = []
    self.verbose = False # True: tell when extending forth words.
    # g.trace('rulesDict...\n',g.dictToString(rulesDict),tag='rulesDict...')
</t>
<t tx="ekr.20150326145530.6">def init(self):
    '''Set our ivars from settings.'''
    c = self.c
    assert(c)
    table = (
        (self.definingwords, "forth-defwords"),
        (self.brackets, "forth-delimiter-pairs"),
        (self.keywords, "forth-words"),
        (self.stringwords, "forth-string-word-pairs"),
        (self.boldwords, "forth-bold-words"),
        (self.bolditalicwords, "forth-bold-italic-words"),
        (self.italicwords, "forth-italic-words"),
    )
    # Add entries from @data nodes (if they exist) to the corresponding lists.
    for(ivarList, setting) in table:
        extras = []
        aList = c.config.getData(setting)
        if aList:
            for s in aList:
                s = s.strip()
                if s and s[0] != '\\':
                    # g.trace(setting,s)
                    extras.append(s)
            if extras:
                if self.verbose:
                    if not g.app.unitTesting and not g.app.batchMode:
                        g.pr("Found extra forth: %s" % " ".join(extras))
                ivarList.extend(extras)
    # Create brackets1/2 and stringwords1/2 lists.
    table2 = (
        ("brackets", "@data forth-delimiter-pairs"),
        ("stringwords", "@data forth-string-word-pairs"),
    )
    for(ivar, setting) in table2:
        self.splitList(ivar, setting)
    # g.trace('keywords',self.keywords)
</t>
<t tx="ekr.20150326145530.7">def splitList(self, ivar, setting):
    '''Process lines containing pairs of entries
    in a list whose *name* is ivar.
    Put the results in ivars whose names are ivar1 and ivar2.'''
    result1 = []; result2 = []
    aList = getattr(self, ivar)
    # Look for pairs.  Comments have already been removed.
    for s in aList:
        pair = s.split(' ')
        if len(pair) == 2 and pair[0].strip() and pair[1].strip():
            result1.append(pair[0].strip())
            result2.append(pair[1].strip())
        else:
            g.es_print('%s: ignoring line: %s' % (setting, s))
    # Set the ivars.
    name1 = '%s1' % ivar
    name2 = '%s2' % ivar
    setattr(self, name1, result1)
    setattr(self, name2, result2)
    if 0:
        g.trace(name1, getattr(self, name1))
        g.trace(name2, getattr(self, name2))
</t>
<t tx="ekr.20150326145530.8">def createBracketRules(self):
    for z in self.brackets1:
        func = self.createBracketRule(z)
        self.extendRulesDict(ch=z[0], func=func)

def createBracketRule(self, begin):
    i = self.brackets1.index(begin)
    end = self.brackets2[i]

    def forth_bracket_rule(colorer, s, i):
        return colorer.match_span(s, i, kind="bracketRange", begin=begin, end=end,
            at_line_start=False, at_whitespace_end=False, at_word_start=True, # Require word.
            delegate="", exclude_match=False,
            no_escape=False, no_line_break=False, no_word_break=True) # Require word.

    return forth_bracket_rule
</t>
<t tx="ekr.20150326145530.9">def createDefiningWordRules(self):
    for z in self.definingwords:
        func = self.createDefiningWordRule(z)
        self.extendRulesDict(ch=z[0], func=func)

def createDefiningWordRule(self, word):

    def forth_defining_word_rule(colorer, s, i):
        pattern = ''
        return colorer.match_word_and_regexp(s, i,
            kind1="keyword2", # defining word
            word=word,
            kind2="keyword3", # bold
            pattern=r'(\s)*(\S)+',
            at_line_start=False, at_whitespace_end=False, at_word_start=False,
            exclude_match=False)

    return forth_defining_word_rule
</t>
<t tx="ekr.20150413091056.1">'''Warn if leoProjects.txt or leoToDo.txt contain any clones.'''

clones,nodes,seen = 0,0,set()
table = (
  '@file ../doc/leoProjects.txt',
  '@file ../doc/leoToDo.txt',
)

def check_clone(c,p0,root):
    '''Warn if p appears in any @&lt;file&gt; node outside of root's tree.'''
    global nodes,seen
    v = p0.v
    for p in c.all_positions():
        nodes += 1
        if p.v == v:
            # Check *all* ancestors, not just the nearest one.
            for parent in p.self_and_parents():
                nodes += 1
                if parent.isAnyAtFileNode() and parent.v != root.v:
                    if parent.v not in seen:
                        seen.add(parent.v)
                        g.es_print('%s and %s contain clone: %s' % (
                            root.h,parent.h,p0.h))

for h in table:
    root = g.findNodeAnywhere(c,h)
    if root:
        for p in root.self_and_subtree():
            nodes += 1
            if p.isCloned():
                clones += 1
                check_clone(c,p,root)
    else:
        g.es_print('not found',h,color='red')
print('done: %s nodes, %s clones' % (nodes,clones))

@tabwidth -4
@language python
</t>
<t tx="ekr.20150425145248.1">run-pylint
clone-to-at-spot
beautify-tree
cfa-code
sort-lines
# show-data
# test-ptb
check-clones
expand-log-pane
contract-log-pane</t>
<t tx="ekr.20150502050609.1">@language python
'''Save a copy of this file to the Backup directory.'''
theDir,base = g.os_path_split(c.fileName())
path = g.os_path_join('~/Dropbox/','backups','leoPy',base)
c.backup(path)
</t>
<t tx="ekr.20150507170849.1">g.cls()

print('===== Start =====')

class CreateDecorators:
    '''
    A class to create decorators from tables in getPublicCommands.
    
    Note: the node "Found: getPublicCommands" must exist.
    '''
    def __init__(self,c,make_changes):
        self.c = c
        self.fixups = self.create_fixups()
        self.n = 0
        self.n_fail = 0
        self.make_changes=make_changes
        self.suppress = [
            'c.frame.body and c.frame.body.addEditor',
            'cls','cloneFindParents','cycleTabFocus',
            'k and k.keyboardQuit',
            'menuShortcutPlaceHolder','removeBlankLines',
            'saveBuffersKillLeo',
        ]
    @others

CreateDecorators(c,make_changes=False).run()
</t>
<t tx="ekr.20150507174711.1">def find_next_clone(self,p):
    v = p.v
    p = p.copy()
    p.moveToThreadNext()
    wrapped = False
    while 1:
        # g.trace(p.v,p.h)
        if p and p.v == v:
            break
        elif p:
            p.moveToThreadNext()
        elif wrapped:
            break
        else:
            wrapped = True
            p = c.rootPosition()
    return p
</t>
<t tx="ekr.20150507175246.1">def munge_lines(self,root,publicCommands):
    '''Return munged lines of '''
    # print('')
    # g.trace(root.h)
    s = publicCommands.b
    i,j = s.find('{'),s.find('}')
    s = s[i+1:j]
    # print(s)
    lines = sorted([z.strip() for z in g.splitLines(s) if z.strip()])
    lines = [z for z in lines if not z.startswith('#')]
    lines = [z[:z.find('#')] if z.find('#') &gt; -1 else z for z in lines]
    lines = [z.rstrip().rstrip(',') for z in lines]
    lines = [z[1:] for z in lines]
    lines = [z.replace("':",' ') for z in lines]
    # print('\n'.join(lines))
    self.n += len(lines)
    return lines
</t>
<t tx="ekr.20150508062944.1">def run(self):
    '''Top-level code.'''
    self.n = 0
    found = g.findNodeAnywhere(c,'Found: getPublicCommands')
    assert found
    for child in found.children():
        publicCommands = self.find_next_clone(child)
        root = self.find_class(publicCommands)
        if root:
            lines = self.munge_lines(root,publicCommands)
            d = self.create_d(lines,publicCommands)
            self.create_decorators(d,root)
    print('\n%s commands %s failed' % (self.n,self.n_fail))
</t>
<t tx="ekr.20150508063412.1">def create_decorators(self,d,root):
    '''Create decorators for all items in d in root's tree.'''
    # print('***** %s' % root.h)
    if root.h in self.fixups:
        roots = []
        aList = self.fixups.get(root.h)
        for root2_h in aList:
            root2 = g.findNodeAnywhere(self.c,root2_h)
            if root2:
                # g.trace(root.h,'=====&gt;',root2.h)
                roots.append(root2)
            else:
                g.trace('===== not found',root2_h)
    else:
        roots = [root]
    for f_name in sorted(d.keys()):
        found = False
        for root in roots:
            c_name = d.get(f_name)
            found = self.create_decorator(c_name,f_name,root)
            if found: break
        if not found and f_name not in self.suppress:
            print('===== not found: %30s %s' % (root.h,f_name))
            self.n_fail += 1
</t>
<t tx="ekr.20150508063538.1">def create_d(self,lines,publicCommands):
    '''Create a dict. keys are method names; values are command names.'''
    trace = False
    if trace:
        print('')
        g.trace(publicCommands.h)
    d = {}
    for s in lines:
        aList = s.split()
        if len(aList) &gt; 2:
            aList = [aList[0],' '.join(aList[1:])]
        c_name,f_name = aList[0].strip(),aList[1].strip()
        if ' ' not in f_name:
            f_name = f_name.split('.')[-1]
        # if '(' in f_name:
            # f_name = f_name[:f_name.find('(')]
        if trace: g.trace('%45s %s' % (c_name,f_name))
        d [f_name] = c_name
    return d
</t>
<t tx="ekr.20150508063926.1">def find_class(self,p):
    '''Return the position of the class enclosing p.'''
    for p2 in p.parents():
        if p2.h.lower().find('class') &gt; -1 and p2.b.find('class') &gt; -1:
            return p2
    else:
        g.trace('*** no class for p.h')
        return None
</t>
<t tx="ekr.20150508071622.1">def create_decorator(self,c_name,f_name,root):
    '''
    Search root for a definition of f_name.
    If found, insert @cmd(f_name) before the definition.
    '''
    # g.trace('%45s %s' % (c_name,f_name))
    trace = False
    found = False
    decorator = "@cmd('%s')\n" % (c_name)
    for p in root.self_and_subtree():
        changed,result = False,[]
        for s in g.splitLines(p.b):
            if g.match_word(s,0,'def ' + f_name):
                if found:
                    if f_name not in self.suppress:
                        g.trace('duplicate def',f_name)
                else:
                    changed,found = True,True
                    result.append(decorator)
                    # print('%s%s' % (decorator,s))
            result.append(s)
        # if changed and self.make_changes:
            # new_body = ''.join(result)
            # # use git as our undo :-)
            # p.b = new_body
    return found
</t>
<t tx="ekr.20150508074623.1">def create_fixups(self):
    '''
    Return a fixup dict.
    Keys are headlines for classes.
    Values are new headlines of nodes containing the actual class.
    '''
    return {
        'ChapterCommandsClass': ['class ChapterController'],
        'EditCommandsClass': [
            'EditCommandsClass',
            'class Commands',
            'class LeoQtFrame',
            'class LeoBody',
        ],
        'class SearchCommandsClass': ['class LeoFind (LeoFind.py)'],
        'KeyHandlerCommandsClass (add docstrings)': [
            'class KeyHandlerClass',
            'class AutoCompleterClass',
        ]
    }
</t>
<t tx="ekr.20150509183433.1">g.cls()

# Changed files:
# leoApp.py
# leoAtFile.py
# leoCommands.py
# leoFileCommands.py
# leoFrame.py
# leoUndo.py
# qt_frame.py

make_changes = True
    # True, actually make the change

class CreateDecorators:
    '''
    A class to create decorators from tables in getPublicCommands.
    
    Note: the node "Found: getPublicCommands" must exist.
    '''
    def __init__(self):
        self.n = 0
        self.n_fail = 0
        self.s = self.define_s()
    @others

CreateDecorators().run()
</t>
<t tx="ekr.20150509183433.2">def create_d(self,lines):
    '''Create a dict. keys are method names; values are command names.'''
    trace = False
    d = {}
    for s in lines:
        aList = s.split()
        if len(aList) &gt; 2:
            aList = [aList[0],' '.join(aList[1:])]
        c_name,f_name = aList[0].strip(),aList[1].strip()
        if ' ' not in f_name:
            f_name = f_name.split('.')[-1]
        # if '(' in f_name:
            # f_name = f_name[:f_name.find('(')]
        if trace: g.trace('%45s %s' % (c_name,f_name))
        d [f_name] = c_name
    return d
</t>
<t tx="ekr.20150509183433.3">def create_decorator(self,c_name,f_name,root):
    '''
    Search root for a definition of f_name.
    If found, insert @cmd(f_name) before the definition.
    '''
    trace = True
    found = False
    decorator = "@cmd('%s')\n" % (c_name)
    for p in root.self_and_subtree():
        changed,result = False,[]
        for s in g.splitLines(p.b):
            if g.match_word(s,0,'def ' + f_name):
                if found:
                    if f_name not in self.suppress:
                        g.trace('duplicate def',f_name)
                else:
                    changed,found = True,True
                    result.append(decorator)
                    # print('%s%s' % (decorator,s))
            result.append(s)
        if changed and make_changes:
            new_body = ''.join(result)
            print('%40s %s' % (p.h[:40],decorator.rstrip()))
            # use git as our undo :-)
            # p.b = new_body
    return found
</t>
<t tx="ekr.20150509183433.4">def create_decorators(self,d):
    '''Create decorators for all items in d in root's tree.'''
    table = (
        'class Commands', # c.
        'class LeoQtFrame', # f.
        'class LeoFrame', # f.
        'class LeoApp', # g.app.
        '@file leoAtFile.py', # c.atFileCommands
        '@file leoFileCommands.py', # c.fileCommands
        'class Undoer', # c.undoer
    )
    roots = []
    for h in table:
        root = g.findNodeAnywhere(c,h)
        assert root,h
        roots.append(root)
    for f_name in sorted(d.keys()):
        found = False
        for root in roots:
            c_name = d.get(f_name)
            found = self.create_decorator(c_name,f_name,root)
            if found: break
        if not found and f_name not in self.suppress:
            print('===== not found: %s' % (f_name))
            self.n_fail += 1
</t>
<t tx="ekr.20150509183433.8">def munge_lines(self,s):
    '''Return munged lines of s. '''
    lines = sorted([z.strip() for z in g.splitLines(s) if z.strip()])
    lines = [z for z in lines if not z.startswith('#')]
    lines = [z[:z.find('#')] if z.find('#') &gt; -1 else z for z in lines]
    lines = [z.rstrip().rstrip(',') for z in lines]
    lines = [z[1:] for z in lines]
    lines = [z.replace("':",' ') for z in lines]
    self.n += len(lines)
    return lines
</t>
<t tx="ekr.20150509183433.9">def run(self):
    '''Top-level code.'''
    lines = self.munge_lines(self.s)
    d = self.create_d(lines)
    self.create_decorators(d)
    print('%s commands %s failed' % (self.n,self.n_fail))
</t>
<t tx="ekr.20150509183832.1"># 'check-all-python-code':      c.checkAllPythonCode,
# 'check-python-code':          c.checkPythonCode,
# 'extract-python-method':        c.extractPythonMethod,
# 'extract-section':              c.extractSection,
# 'import-at-file':               c.importAtFile,
# 'import-at-root':               c.importAtRoot,
# 'import-cweb-files':            c.importCWEBFiles,
# 'import-derived-file':          c.importDerivedFile,
# 'import-flattened-outline':     c.importFlattenedOutline,
# 'import-noweb-files':           c.importNowebFiles,
# 'mark-changed-roots':           c.markChangedRoots,
# 'mark-clones':                c.markClones,
# 'open-compare-window':        c.openCompareWindow,
# 'open-online-tutorial':       c.leoTutorial,
# 'reformat-body':              c.reformatBody, # 2013/10/02.
def define_s(self):
    return '''
'abort-edit-headline':          f.abortEditLabelCommand,
'about-leo':                    c.about,
'add-comments':                 c.addComments,     
'beautify':                     c.beautifyPythonCode,
'beautify-all':                 c.beautifyAllPythonCode,
'beautify-c':                   c.beautifyCCode,
'beautify-tree':                c.beautifyPythonTree,
'cascade-windows':              f.cascade,
'check-derived-file':           c.atFileCommands.checkDerivedFile,
'check-leo-file':               c.fileCommands.checkLeoFile,
'check-outline':                c.fullCheckOutline,
'clean-recent-files':           c.cleanRecentFiles,
'clear-recent-files':           c.clearRecentFiles,
'clone-node':                   c.clone,
'clone-node-to-last-node':      c.cloneToLastNode,
'close-window':                 c.close,
'contract-all':                 c.contractAllHeadlines,
'contract-all-other-nodes':     c.contractAllOtherNodes,
'contract-node':                c.contractNode,
'contract-or-go-left':          c.contractNodeOrGoToParent,
'contract-parent':              c.contractParent,
'convert-all-blanks':           c.convertAllBlanks,
'convert-all-tabs':             c.convertAllTabs,
'convert-blanks':               c.convertBlanks,
'convert-tabs':                 c.convertTabs,
'copy-node':                    c.copyOutline,
'copy-text':                    f.copyText,
'cut-node':                     c.cutOutline,
'cut-text':                     f.cutText,
'de-hoist':                     c.dehoist,
'delete-comments':              c.deleteComments,
'delete-node':                  c.deleteOutline,
'demote':                       c.demote,
'dump-outline':                 c.dumpOutline,
'edit-headline':                c.editHeadline,
'end-edit-headline':            f.endEditLabelCommand,
'equal-sized-panes':            f.equalSizedPanes,
'execute-script':               c.executeScript,
'exit-leo':                     g.app.onQuit,
'expand-all':                   c.expandAllHeadlines,
'expand-all-subheads':          c.expandAllSubheads,
'expand-ancestors-only':        c.expandOnlyAncestorsOfNode,
'expand-and-go-right':          c.expandNodeAndGoToFirstChild,
'expand-next-level':            c.expandNextLevel,
'expand-node':                  c.expandNode,
'expand-or-go-right':           c.expandNodeOrGoToFirstChild,
'expand-prev-level':            c.expandPrevLevel,
'expand-to-level-1':            c.expandLevel1,
'expand-to-level-2':            c.expandLevel2,
'expand-to-level-3':            c.expandLevel3,
'expand-to-level-4':            c.expandLevel4,
'expand-to-level-5':            c.expandLevel5,
'expand-to-level-6':            c.expandLevel6,
'expand-to-level-7':            c.expandLevel7,
'expand-to-level-8':            c.expandLevel8,
'expand-to-level-9':            c.expandLevel9,
'export-headlines':             c.exportHeadlines,
'extract':                      c.extract,
'extract-names':                c.extractSectionNames,
'find-next-clone':              c.findNextClone,
'flatten-outline':              c.flattenOutline,
'flatten-outline-to-node':      c.flattenOutlineToNode,
'go-back':                      c.goPrevVisitedNode,
'go-forward':                   c.goNextVisitedNode,
'goto-first-node':              c.goToFirstNode,
'goto-first-sibling':           c.goToFirstSibling,
'goto-first-visible-node':      c.goToFirstVisibleNode,
'goto-last-node':               c.goToLastNode,
'goto-last-sibling':            c.goToLastSibling,
'goto-last-visible-node':       c.goToLastVisibleNode,
'goto-next-changed':            c.goToNextDirtyHeadline,
'goto-next-clone':              c.goToNextClone,
'goto-next-history-node':       c.goToNextHistory,
'goto-next-marked':             c.goToNextMarkedHeadline,
'goto-next-node':               c.selectThreadNext,
'goto-next-sibling':            c.goToNextSibling,
'goto-next-visible':            c.selectVisNext,
'goto-parent':                  c.goToParent,
'goto-prev-history-node':       c.goToPrevHistory,
'goto-prev-node':               c.selectThreadBack,
'goto-prev-sibling':            c.goToPrevSibling,
'goto-prev-visible':            c.selectVisBack,
'hide-invisibles':              c.hideInvisibles,
'hoist':                        c.hoist,
'import-file':                  c.importAnyFile,
'indent-region':                c.indentBody,
'insert-body-time':             c.insertBodyTime,
'insert-child':                 c.insertChild,
'insert-node':                  c.insertHeadline,
'insert-node-before':           c.insertHeadlineBefore,
'mark':                         c.markHeadline,
'mark-changed-items':           c.markChangedHeadlines,
'mark-subheads':                c.markSubheads,
'match-brackets':               c.findMatchingBracket,
'minimize-all':                 f.minimizeAll,
'move-outline-down':            c.moveOutlineDown,
'move-outline-left':            c.moveOutlineLeft,
'move-outline-right':           c.moveOutlineRight,
'move-outline-up':              c.moveOutlineUp,
'new':                          c.new,
'open-cheat-sheet-leo':         c.openCheatSheet,
'open-leoDocs-leo':             c.leoDocumentation,
'open-leoPlugins-leo':          c.openLeoPlugins,
'open-leoSettings-leo':         c.openLeoSettings,
'open-local-settings':          c.selectAtSettingsNode,
'open-myLeoSettings-leo':       c.openMyLeoSettings,
'open-offline-tutorial':        f.leoHelp,
'open-online-home':             c.leoHome,
'open-online-toc':              c.openLeoTOC,
'open-online-tutorials':        c.openLeoTutorials,
'open-online-videos':           c.openLeoVideos,
'open-outline':                 c.open,
'open-python-window':           c.openPythonWindow,
'open-quickstart-leo':          c.leoQuickStart,
'open-scripts-leo':             c.openLeoScripts,
'open-users-guide':             c.openLeoUsersGuide,
'open-with':                    c.openWith,
'outline-to-cweb':              c.outlineToCWEB,
'outline-to-noweb':             c.outlineToNoweb,
'paste-node':                   c.pasteOutline,
'paste-retaining-clones':       c.pasteOutlineRetainingClones,
'paste-text':                   f.pasteText,
'pretty-print-all-python-code': c.prettyPrintAllPythonCode,
'pretty-print-python-code':     c.prettyPrintPythonCode,
'promote':                      c.promote,
'read-at-auto-nodes':           c.readAtAutoNodes,
'read-at-file-nodes':           c.readAtFileNodes,
'read-at-shadow-nodes':         c.readAtShadowNodes,
'read-file-into-node':          c.readFileIntoNode,
'read-outline-only':            c.readOutlineOnly,
'redo':                         c.undoer.redo,
'reformat-paragraph':           c.reformatParagraph,
'refresh-from-disk':            c.refreshFromDisk,
'remove-sentinels':             c.removeSentinels,
'resize-to-screen':             f.resizeToScreen,
'revert':                       c.revert,
'save-all':                     c.saveAll,
'save-file':                    c.save,
'save-file-as':                 c.saveAs,
'save-file-as-unzipped':        c.saveAsUnzipped,
'save-file-as-zipped':          c.saveAsZipped,
'save-file-to':                 c.saveTo,
'set-colors':                   c.colorPanel,
'set-font':                     c.fontPanel,
'settings':                     c.preferences,
'show-invisibles':              c.showInvisibles,
'sort-children':                c.sortChildren,
'sort-recent-files':            c.sortRecentFiles,
'sort-siblings':                c.sortSiblings,
'tangle':                       c.tangle,
'tangle-all':                   c.tangleAll,
'tangle-marked':                c.tangleMarked,
'toggle-active-pane':           f.toggleActivePane,
'toggle-angle-brackets':        c.toggleAngleBrackets,
'toggle-invisibles':            c.toggleShowInvisibles,
'toggle-sparse-move':           c.toggleSparseMove,
'toggle-split-direction':       f.toggleSplitDirection,
'undo':                         c.undoer.undo,
'unformat-paragraph':           c.unformatParagraph,
'unindent-region':              c.dedentBody,
'unmark-all':                   c.unmarkAll,
'untangle':                     c.untangle,
'untangle-all':                 c.untangleAll,
'untangle-marked':              c.untangleMarked,
'weave':                        c.weave,
'write-at-auto-nodes':          c.atFileCommands.writeAtAutoNodes,
'write-at-file-nodes':          c.fileCommands.writeAtFileNodes,
'write-at-shadow-nodes':        c.fileCommands.writeAtShadowNodes,
'write-dirty-at-auto-nodes':    c.atFileCommands.writeDirtyAtAutoNodes,
'write-dirty-at-file-nodes':    c.fileCommands.writeDirtyAtFileNodes,
'write-dirty-at-shadow-nodes':  c.fileCommands.writeDirtyAtShadowNodes,
'write-file-from-node':         c.writeFileFromNode,
'write-missing-at-file-nodes':  c.fileCommands.writeMissingAtFileNodes,
'write-outline-only':           c.fileCommands.writeOutlineOnly,
'''
</t>
<t tx="ekr.20150514035207.1"></t>
<t tx="ekr.20150531102337.1"></t>
<t tx="ekr.20150604130353.1">gnx: ekr.20150604130223.363
</t>
<t tx="ekr.20150604130353.2"></t>
<t tx="ekr.20150617060607.1"></t>
<t tx="ekr.20150703061709.1">@language python

'''myLeoSettings.py: save the outline and run the pylint command'''

# print('@button run-pylint: %s' % c.shortFileName())
if c.isChanged():
    c.save()
c.k.simulateCommand('pylint')
</t>
<t tx="ekr.20160122104332.1"></t>
<t tx="ekr.20160123142722.1"># An example configuration file for make_stub_files.py.
# By default, this is ~/stubs/make_stub_files.cfg.
# Can be changed using the --config=path command-line option.

[Global]

files:

    # Files to be used *only* if no files are given on the command line.
    # glob.glob wildcards are supported.

    # c:/leo.repo/leo-editor/leo/core/leoAst.py
    # c:/leo.repo/leo-editor/leo/core/*.py
    # c:/leo.repo/leo-editor/plugins/*.py

output_directory: ~/stubs

prefix_lines:
    # Lines to be inserted at the start of each stub file.
    from typing import TypeVar, Iterable, Tuple
    T = TypeVar('T', int, float, complex)

[Def Name Patterns]

[General Patterns]
</t>
<t tx="ekr.20160226082945.1"></t>
<t tx="ekr.20160315161104.1">&lt;&lt; imports &gt;&gt;
g.cls()
if c.isChanged(): c.save()
@others
path = g.os_path_finalize_join(g.app.loadDir,
    '..', 'test', 'report_test.py') # 'leoAst.py')
assert g.os_path_exists(path), path
fn = g.shortFileName(path)
source = open(path, 'r').read()
node = ast.parse(source, filename=fn, mode='exec')
s = HTMLReportTraverser(debug=True).main(fn, node)
out_fn = g.os_path_finalize_join(g.app.loadDir, '..', 'test', 
    'HTMLReportTraverser_test.html')
open(out_fn, 'w').write(s)
# print('%s %s' % (len(s), out_fn))
os.startfile(out_fn)
# os.system('ed ' + out_fn)</t>
<t tx="ekr.20160315163533.1">import leo.core.leoAst as leoAst
import ast
import os
import xml.sax.saxutils as saxutils
import textwrap
# Needed only for the script version
AstFullTraverser = leoAst.AstFullTraverser</t>
<t tx="ekr.20160316065700.1">'''Test file for HTMLReportTraverser class.'''
&lt;&lt; includes &gt;&gt;
@others

</t>
<t tx="ekr.20160316065936.1">import ast
import xml.sax.saxutils as saxutils
import leo.core.leoGlobals as g</t>
<t tx="ekr.20160316070146.1">class TestClass(object):
    '''A class exercising important cases in the HTMLReportTraverser code.'''
    @others</t>
<t tx="ekr.20160316070255.1">def if_tests(self, a, b):
    '''Test if statements, especially 'elif' vs. 'else if'.'''
    if a and b:
        rt.gen("&lt;div class='%s' %s&gt;" % (full_class_name, extra))
    elif a:
        rt.gen("&lt;div class='%s'&gt;" % (full_class_name))
    else:
        assert not extra
        rt.gen("&lt;div&gt;")
    if a:
        print('a')
    else: # elif is not correct here.
        if b:
            print(b)
        print(c)</t>
<t tx="ekr.20160316070709.1">def comprehension_tests(self):
    '''Test comprehensions'''
    for i, aList in enumerate(self.line_tokens):
        print('hi')
    return [z for z in aList if self.token_kind(z) == 'string']
</t>
<t tx="ekr.20160316081359.1">def try_tests(self):
    '''Test try/except/finally'''
    try:
        for z in node.optional_vars:
            vars_list.append(self.visit(z))
    except TypeError:
        vars_list.append(self.visit(node.optional_vars))
    finally:
        print('ooops')
        raise
    </t>
<t tx="ekr.20160316091152.1">@language rest
@wrap

This is the theory of operation document for py2cs.py. The most interesting aspect of this script is the TokenSync class. This class provides a reliable way of associating tokenizer tokens with ast nodes.

@others



</t>
<t tx="ekr.20160316091152.2">
### The problem

The initial version of py2cs.py (the script) used only tokens. This solved all token-related problems, but made parsing difficult. Alas, it is [difficult](http://stackoverflow.com/questions/16748029/how-to-get-source-corresponding-to-a-python-ast-node) to associate tokens with ast nodes.

The script needs the following token-related data:

- The **ignored lines** (comment lines and blank lines) that precede any statement.

- The **trailing comment** strings that might follow any line.

- Optionally, the **line breaks** occurring within lines. At present, this script does not preserve such breaks, and it's probably not worth doing. Indeed, automatically breaking long lines seems more useful, especially considering that coffeescript lines may be substantially shorter than the corresponding python lines.

- The **exact spelling** of all strings.

The [ast_utils module](
https://bitbucket.org/plas/thonny/src/3b71fda7ac0b66d5c475f7a668ffbdc7ae48c2b5/thonny/ast_utils.py?at=master) purports to solve this problem with convoluted adjustments to the col_offset field. This approach is subject to subtle Python bugs, and subtle differences between Python 2 and Python 3. There is a better way...
</t>
<t tx="ekr.20160316091152.3">
### Design

The main idea is to use *only* the ast.lineno fields and the tokenizer module to recreate token data. The design assumes only that both the ast.lineno field and Python's tokenizer module are solid. This is a much more reasonable assumption than assuming that the col_offset field always tells the truth. In short, this design *ignores* the ast.col_offset field.

At startup, the TokenSync ctor assigns all the incoming tokens to various lists.  These lists are indexed by lineno:

    ts.line_tokens[i]: all the tokens on line i
    ts.string_tokens[i]: all string tokens on line i
    st.ignored_lines: the blank or comment line on line i
    
It is very easy to create these lists. The code does not depend on any arcane details.

#### Recovering the exact spelling of stings.

ts.synch_string returns the *next* string on the line. Here it is, stripped of defensive code:

    def sync_string(self, node):
        '''Return the spelling of the string at the given node.'''
        tokens = self.string_tokens[node.lineno-1]
        token = tokens.pop(0)
        self.string_tokens[node.lineno-1] = tokens
        return self.token_val(token)
       
Stripped of defensive code, the do_Str visitor is just:

    def do_Str(self, node):
        '''A string constant, including docstrings.'''
        return self.sync_string(node)
        
#### Recovering otherwise ignored nodes

**ts.leading_lines(node)** returns a list of otherwise ignored lines that
precede the node's line that have not already been returned.
**ts.leading_string(node)** is a convenience method that returns ''.join(ts.leading_lines(node)). The visitors of the CoffeeScriptTraverser class show how to use these methods.
</t>
<t tx="ekr.20160316091152.4">
### Using the TokenSync class

The present code is driven by ast trees, but each visitor of the CoffeeScriptTraverser class takes care to preserve **otherwise-ignored tokens**. These are tokens that would otherwise be ignored: namely blank lines and comments, both entire-line comments and trailing comments.

The visitor for each statement intersperses otherwise ignored tokens using calls to the TokenSync class.  The simplest cases are like this:

    def do_Break(self, node):
        head = self.leading_string(node)
        tail = self.trailing_comment(node)
        return head + self.indent('break') + tail

The leading_string and trailing_comment methods simply redirect to the corresponding methods in the TokenSync class.  Saves a bit of typing. Compound statements are a bit more bother, but not overly so. For example:

    def do_If(self, node):
   
        result = self.leading_lines(node)
        tail = self.trailing_comment(node)
        s = 'if %s:%s' % (self.visit(node.test), tail)
        result.append(self.indent(s))
        for z in node.body:
            self.level += 1
            result.append(self.visit(z))
            self.level -= 1
        if node.orelse:
            tail = self.tail_after_body(node.body, node.orelse, result)
            result.append(self.indent('else:' + tail))
            for z in node.orelse:
                self.level += 1
                result.append(self.visit(z))
                self.level -= 1
        return ''.join(result)

The line:

        tail = self.tail_after_body(node.body, node.orelse, result)

is a hack needed to compensate for the lack of an actual ast.Else node.
</t>
<t tx="ekr.20160316091152.5">
### Summary

The TokenSync class is, a new, elegant, unexpected and happy development. It is a relatively easy-to-use helper that allows parser-based code to preserve data that is not easily accessible in parse trees.

The TokenSync class avoids [problems with the col_offset field](
http://stackoverflow.com/questions/16748029/how-to-get-source-corresponding-to-a-python-ast-node) in ast nodes. The TokenSync class depends only on the ast.lineno field and the tokenize module. We can expect it to be rock solid.

Edward K. Ream  
February 20 to 25, 2016
</t>
<t tx="ekr.20160316091923.1">@cmd('python-to-coffeescript')
def python2coffeescript(self, event):
    '''
    Converts python text to coffeescript text. The conversion is not
    perfect, but it eliminates a lot of tedious text manipulation.
    '''
    @others
    c = self.c
    Python_To_Coffeescript_Adapter(c).main()
    c.bodyWantsFocus()
</t>
<t tx="ekr.20160316092837.1">class Python_To_Coffeescript_Adapter:
    '''An interface class between Leo and leo/external/py2cs.py.'''
    @others
</t>
<t tx="ekr.20160316093019.1">def main(self):
    '''Main line for Python_To_CoffeeScript class.'''
    if self.py2cs:
        self.run()
    else:
        g.es_print('can not load py2cs.py')
       
    
</t>
<t tx="ekr.20160316094011.7">def finalize(self, fn):
    '''Finalize and regularize a filename.'''
    return g.os_path_normpath(g.os_path_abspath(g.os_path_expanduser(fn)))
</t>
<t tx="ekr.20160316094011.8">def to_coffeescript(self, p):
    '''Convert the @&lt;file&gt; node at p to a .coffee file.'''
    assert p.isAnyAtFileNode()
    c = self.c
    fn = p.anyAtFileNodeName()
    if not fn.endswith('.py'):
        g.es_print('not a python file', fn)
        return
    abs_fn = g.fullPath(c, p)
    if not g.os_path_exists(abs_fn):
        g.es_print('not found', abs_fn)
        return
    if g.os_path_exists(self.output_directory):
        base_fn = g.os_path_basename(fn)
        out_fn = g.os_path_finalize_join(self.output_directory, base_fn)
    else:
        g.es_print('not found', self.output_directory)
        return
    out_fn = out_fn[:-3] + '.coffee'
    out_fn = g.os_path_normpath(out_fn)
    s = open(abs_fn).read()
    # s = self.strip_sentinels(s)
    if 0:
        for z in g.splitLines(s)[:20]:
            print(z.rstrip())
    x = self.py2cs.MakeCoffeeScriptController()
    # copy ivars and run.
    x.enable_unit_tests = False
    x.files = [abs_fn,]
    x.output_directory = self.output_directory
    x.overwrite = self.overwrite
    x.make_coffeescript_file(abs_fn, s=s)
</t>
<t tx="ekr.20160316094011.9">def run(self):
    '''Create .coffee files for all @&lt;file&gt; nodes in p's tree.'''
    p = c.p
    if p.isAnyAtFileNode():
        self.to_coffeescript(p)
        return
    # First, look down tree.
    after, p2 = p.nodeAfterTree(), p.firstChild()
    found = False
    while p2 and p != after:
        if p2.isAnyAtFileNode():
            self.to_coffeescript(p2)
            p2.moveToNext()
            found = True
        else:
            p2.moveToThreadNext()
    if not found:
        # Look up the tree.
        for p2 in p.parents():
            if p2.isAnyAtFileNode():
                self.to_coffeescript(p2)
                return
    g.es_print('no files found in tree:', p.h)
</t>
<t tx="ekr.20160316100329.1">from leo.commands.baseCommands import BaseEditCommandsClass as BaseEditCommandsClass
# h = '@file ../commands/convertCommands.py'
# p = g.findNodeAnywhere(c, h)
old_p = c.p
try:
    # c.selectPosition(p)
    # assert p, h
    @others
    class Dummy:
        def __init__(self, c):
            self.c = c
    python2coffeescript(self=Dummy(c), event=None)
finally:
    pass
    # c.selectPosition(old_p)
</t>
<t tx="ekr.20160316100524.1">def cmd(name):
    '''Command decorator for the ConvertCommandsClass class.'''
    return g.new_cmd_decorator(name, ['c', 'convertCommands',])
</t>
<t tx="ekr.20160316112717.1">def __init__(self,c):
    '''Ctor for Python_To_Coffeescript_Adapter class.'''
    self.c = c
    self.files = []
    self.output_directory = self.finalize(
        c.config.getString('py2cs-output-directory'))
    # self.output_fn = None
    self.overwrite = c.config.getBool('py2cs-overwrite', default=False)
    # Connect to the external module.
    self.py2cs = py2cs = g.importExtension(
        'py2cs',
        pluginName=None,
        verbose=False,
        required=False,
    )
</t>
<t tx="ekr.20160316141812.1">def strip_sentinels(self, s):
    '''
    Strip s of all sentinel lines.
    This may be dubious because it destroys outline structure.
    '''
    delims = ['#', None, None]
    return ''.join([z for z in g.splitLines(s) if not g.is_sentinel(z, delims)])
</t>
<t tx="ekr.20160406074930.4">gnx: ekr.20160301134754.6
unl: qt_quicksearch declarations
gnx: ekr.20160301134754.7
unl: class Ui_LeoQuickSearchWidget
gnx: ekr.20160301134754.8
unl: class Ui_LeoQuickSearchWidget--&gt;setupUi
gnx: ekr.20160301134754.9
unl: class Ui_LeoQuickSearchWidget--&gt;retranslateUi
</t>
<t tx="ekr.20160406074930.5"></t>
<t tx="ekr.20160415140808.2"></t>
<t tx="ekr.20160416133006.1">gnx: ekr.20160412193816.1
unl: qt_main declarations
gnx: ekr.20160412193816.2
unl: class Ui_MainWindow
gnx: ekr.20160412193816.3
unl: class Ui_MainWindow--&gt;setupUi (Ui_MainWindow)
gnx: ekr.20160412193816.4
unl: class Ui_MainWindow--&gt;retranslateUi
</t>
</tnodes>
</leo_file>
