<?xml version="1.0" encoding="utf-8"?>
<!-- Created by Leo: https://leo-editor.github.io/leo-editor/leo_toc.html -->
<leo_file xmlns:leo="https://leo-editor.github.io/leo-editor/namespaces/leo-python-editor/1.1" >
<leo_header file_format="2"/>
<globals/>
<preferences/>
<find_panel_settings/>
<vnodes>
<v t="ekr.20070615131954"><vh>Startup</vh>
<v t="ekr.20150302053516.3"><vh>@settings</vh>
<v t="mhw.20181210131101.1"><vh>@bool put-expansion-bits-in-leo-files = False</vh></v>
</v>
<v t="ekr.20070427110751"><vh>@chapters</vh></v>
<v t="ekr.20141116105945.2"><vh>@persistence</vh>
<v t="ekr.20141116105945.3"><vh>@data:@auto create-leobat.bat</vh>
<v t="ekr.20141116105945.4"><vh>@gnxs</vh></v>
</v>
<v t="ekr.20161206070406.1"><vh>@data:@auto-md ../doc/c2py.md</vh>
<v t="ekr.20161206090903.1"><vh>@gnxs</vh></v>
</v>
<v t="maphew.20190209160547.1"><vh>@data:@auto build-leo.bat</vh>
<v t="lkj.20190712002811.1"><vh>@gnxs</vh></v>
</v>
</v>
<v t="ekr.20111017085134.16069"><vh>@ignore @button nodes</vh>
<v t="ekr.20111017085134.16072"><vh>@button check-dirty</vh></v>
<v t="ekr.20150508132450.1"><vh>@button create decorators</vh>
<v t="ekr.20150508132450.2"><vh>create_d</vh></v>
<v t="ekr.20150508132450.3"><vh>create_decorator</vh></v>
<v t="ekr.20150508132450.4"><vh>create_decorators</vh></v>
<v t="ekr.20150508132450.5"><vh>create_fixups</vh></v>
<v t="ekr.20150508132450.6"><vh>find_class</vh></v>
<v t="ekr.20150508132450.7"><vh>find_next_clone</vh></v>
<v t="ekr.20150508132450.8"><vh>munge_lines</vh></v>
<v t="ekr.20150508132450.9"><vh>run</vh></v>
</v>
<v t="ekr.20111017085134.16070"><vh>@button jython</vh></v>
<v t="ekr.20111017085134.16074"><vh>@button Lines Down</vh>
<v t="ekr.20111017085134.16075"><vh>moveLinesDown</vh></v>
</v>
<v t="ekr.20111017085134.16076"><vh>@button Lines Up</vh>
<v t="ekr.20111017085134.16077"><vh>moveLinesUp</vh></v>
</v>
<v t="ekr.20111017085134.16078"><vh>@button lispCall @key=alt+6</vh>
<v t="ekr.20111017085134.16079"><vh>ctor</vh></v>
<v t="ekr.20111017085134.16080"><vh>run &amp; helpers</vh>
<v t="ekr.20111017085134.16081"><vh>findCalls</vh></v>
<v t="ekr.20111017085134.16082"><vh>findDefs</vh></v>
<v t="ekr.20111017085134.16083"><vh>stripComments</vh></v>
<v t="ekr.20111017085134.16084"><vh>stripLispIds</vh></v>
<v t="ekr.20111017085134.16085"><vh>stripStrings</vh></v>
</v>
<v t="ekr.20111017085134.16086"><vh>class token</vh></v>
<v t="ekr.20111017085134.16087"><vh>choose</vh></v>
<v t="ekr.20111017085134.16088"><vh>dumpList</vh></v>
<v t="ekr.20111017085134.16089"><vh>Parsing...</vh>
<v t="ekr.20111017085134.16090"><vh>parse</vh></v>
<v t="ekr.20111017085134.16091"><vh>scan &amp; helpers</vh>
<v t="ekr.20111017085134.16092"><vh>skipComment</vh></v>
<v t="ekr.20111017085134.16093"><vh>skipId</vh></v>
<v t="ekr.20111017085134.16094"><vh>skipString</vh></v>
</v>
<v t="ekr.20111017085134.16095"><vh>scanForest</vh></v>
</v>
<v t="ekr.20111017085134.16096"><vh>Code generators...</vh>
<v t="ekr.20111017085134.16097"><vh>gen</vh></v>
<v t="ekr.20111017085134.16098"><vh>gen_token</vh></v>
<v t="ekr.20111017085134.16099"><vh>gen_block &amp; helper</vh>
<v t="ekr.20111017085134.16100"><vh>gen_block_id</vh></v>
</v>
<v t="ekr.20111017085134.16101"><vh>gen_call &amp; helper</vh>
<v t="ekr.20111017085134.16102"><vh>gen_arg</vh></v>
</v>
<v t="ekr.20111017085134.16103"><vh>gen_def</vh></v>
<v t="ekr.20111017085134.16104"><vh>gen_if &amp; helpers</vh>
<v t="ekr.20111017085134.16105"><vh>gen_then</vh></v>
</v>
<v t="ekr.20111017085134.16106"><vh>gen_expr</vh></v>
<v t="ekr.20111017085134.16107"><vh>gen_let &amp; helper</vh>
<v t="ekr.20111017085134.16108"><vh>gen_let_bindings</vh></v>
</v>
<v t="ekr.20111017085134.16109"><vh>put...</vh>
<v t="ekr.20111017085134.16110"><vh>put</vh></v>
<v t="ekr.20111017085134.16111"><vh>put_token</vh></v>
<v t="ekr.20111017085134.16112"><vh>put_code &amp; put_code_line</vh></v>
<v t="ekr.20111017085134.16113"><vh>put_code_token</vh></v>
</v>
</v>
</v>
<v t="ekr.20160123185308.1"><vh>@button make_stub_files script</vh>
<v t="ekr.20160123185308.2"><vh>class MakeStubFile</vh>
<v t="ekr.20160123185308.3"><vh>msf.ctors &amp; helpers</vh>
<v t="ekr.20160123185308.4"><vh>msf.make_types_dict</vh></v>
<v t="ekr.20160123185308.5"><vh>msf.scan_types_data</vh></v>
</v>
<v t="ekr.20160123185308.6"><vh>msf.make_stub_file</vh></v>
<v t="ekr.20160123185308.7"><vh>msf.run</vh></v>
</v>
<v t="ekr.20160123185308.8"><vh>class AstFormatter</vh>
<v t="ekr.20160123185308.9"><vh> f.Entries</vh>
<v t="ekr.20160123185308.10"><vh>f.format</vh></v>
<v t="ekr.20160123185308.11"><vh>f.visit</vh></v>
</v>
<v t="ekr.20160123185308.12"><vh>f.Contexts</vh>
<v t="ekr.20160123185308.13"><vh>f.ClassDef</vh></v>
<v t="ekr.20160123185308.14"><vh>f.FunctionDef</vh></v>
<v t="ekr.20160123185308.15"><vh>f.Interactive</vh></v>
<v t="ekr.20160123185308.16"><vh>f.Module</vh></v>
<v t="ekr.20160123185308.17"><vh>f.Lambda</vh></v>
</v>
<v t="ekr.20160123185308.18"><vh>f.Expressions</vh>
<v t="ekr.20160123185308.19"><vh>f.Expr</vh></v>
<v t="ekr.20160123185308.20"><vh>f.Expression</vh></v>
<v t="ekr.20160123185308.21"><vh>f.GeneratorExp</vh></v>
<v t="ekr.20160123185308.22"><vh>f.ctx nodes</vh></v>
</v>
<v t="ekr.20160123185308.23"><vh>f.Operands</vh>
<v t="ekr.20160123185308.24"><vh>f.arguments</vh></v>
<v t="ekr.20160123185308.25"><vh>f.arg (Python3 only)</vh></v>
<v t="ekr.20160123185308.26"><vh>f.Attribute</vh></v>
<v t="ekr.20160123185308.27"><vh>f.Bytes</vh></v>
<v t="ekr.20160123185308.28"><vh>f.Call &amp; f.keyword</vh>
<v t="ekr.20160123185308.29"><vh>f.keyword</vh></v>
</v>
<v t="ekr.20160123185308.30"><vh>f.comprehension</vh></v>
<v t="ekr.20160123185308.31"><vh>f.Dict</vh></v>
<v t="ekr.20160123185308.32"><vh>f.Ellipsis</vh></v>
<v t="ekr.20160123185308.33"><vh>f.ExtSlice</vh></v>
<v t="ekr.20160123185308.34"><vh>f.Index</vh></v>
<v t="ekr.20160123185308.35"><vh>f.List</vh></v>
<v t="ekr.20160123185308.36"><vh>f.ListComp</vh></v>
<v t="ekr.20160123185308.37"><vh>f.Name</vh></v>
<v t="ekr.20160123185308.38"><vh>f.Num</vh></v>
<v t="ekr.20160123185308.39"><vh>f.Repr</vh></v>
<v t="ekr.20160123185308.40"><vh>f.Slice</vh></v>
<v t="ekr.20160123185308.41"><vh>f.Str</vh></v>
<v t="ekr.20160123185308.42"><vh>f.Subscript</vh></v>
<v t="ekr.20160123185308.43"><vh>f.Tuple</vh></v>
</v>
<v t="ekr.20160123185308.44"><vh>f.Operators</vh>
<v t="ekr.20160123185308.45"><vh>f.BinOp</vh></v>
<v t="ekr.20160123185308.46"><vh>f.BoolOp</vh></v>
<v t="ekr.20160123185308.47"><vh>f.Compare</vh></v>
<v t="ekr.20160123185308.48"><vh>f.UnaryOp</vh></v>
<v t="ekr.20160123185308.49"><vh>f.ifExp (ternary operator)</vh></v>
</v>
<v t="ekr.20160123185308.50"><vh>f.Statements</vh>
<v t="ekr.20160123185308.51"><vh>f.Assert</vh></v>
<v t="ekr.20160123185308.52"><vh>f.Assign</vh></v>
<v t="ekr.20160123185308.53"><vh>f.AugAssign</vh></v>
<v t="ekr.20160123185308.54"><vh>f.Break</vh></v>
<v t="ekr.20160123185308.55"><vh>f.Continue</vh></v>
<v t="ekr.20160123185308.56"><vh>f.Delete</vh></v>
<v t="ekr.20160123185308.57"><vh>f.ExceptHandler</vh></v>
<v t="ekr.20160123185308.58"><vh>f.Exec</vh></v>
<v t="ekr.20160123185308.59"><vh>f.For</vh></v>
<v t="ekr.20160123185308.60"><vh>f.Global</vh></v>
<v t="ekr.20160123185308.61"><vh>f.If</vh></v>
<v t="ekr.20160123185308.62"><vh>f.Import &amp; helper</vh>
<v t="ekr.20160123185308.63"><vh>f.get_import_names</vh></v>
</v>
<v t="ekr.20160123185308.64"><vh>f.ImportFrom</vh></v>
<v t="ekr.20160123185308.65"><vh>f.Pass</vh></v>
<v t="ekr.20160123185308.66"><vh>f.Print</vh></v>
<v t="ekr.20160123185308.67"><vh>f.Raise</vh></v>
<v t="ekr.20160123185308.68"><vh>f.Return</vh></v>
<v t="ekr.20160123185308.69"><vh>f.Suite</vh></v>
<v t="ekr.20160123185308.70"><vh>f.TryExcept</vh></v>
<v t="ekr.20160123185308.71"><vh>f.TryFinally</vh></v>
<v t="ekr.20160123185308.72"><vh>f.While</vh></v>
<v t="ekr.20160123185308.73"><vh>f.With</vh></v>
<v t="ekr.20160123185308.74"><vh>f.Yield</vh></v>
</v>
<v t="ekr.20160123185308.75"><vh>f.Utils</vh>
<v t="ekr.20160123185308.76"><vh>f.kind</vh></v>
<v t="ekr.20160123185308.77"><vh>f.indent</vh></v>
<v t="ekr.20160123185308.78"><vh>f.op_name</vh></v>
</v>
</v>
<v t="ekr.20160123185308.79"><vh>class StubFormatter (AstFormatter)</vh>
<v t="ekr.20160123185308.80"><vh>sf.Constants &amp; Name</vh></v>
</v>
<v t="ekr.20160123185308.81"><vh>class StubTraverser (ast.NodeVisitor)</vh>
<v t="ekr.20160123185308.82"><vh>st.indent &amp; out</vh></v>
<v t="ekr.20160123185308.83"><vh>st.run</vh></v>
<v t="ekr.20160123185308.84"><vh>st.visit (not used)</vh></v>
<v t="ekr.20160123185308.85"><vh>st.Visitors</vh>
<v t="ekr.20160123185308.86"><vh>st.ClassDef</vh></v>
<v t="ekr.20160123185308.87"><vh>st.FunctionDef &amp; helpers</vh>
<v t="ekr.20160123185308.88"><vh>format_arguments &amp; helper</vh>
<v t="ekr.20160123185308.89"><vh>munge_arg</vh></v>
</v>
<v t="ekr.20160123185308.90"><vh>format_returns</vh></v>
</v>
<v t="ekr.20160123185308.91"><vh>st.Return</vh></v>
</v>
</v>
</v>
<v t="ekr.20111017085134.16114"><vh>@button outlineToClipboard</vh></v>
<v t="ekr.20111017085134.16071"><vh>@button pydoc</vh></v>
<v t="ekr.20150525161132.1"><vh>@button tokens2tree</vh>
<v t="ekr.20150525161132.2"><vh>&lt;&lt; imports &gt;&gt;</vh></v>
</v>
<v t="ekr.20170301022829.1"><vh>@ignore buttons new in Leo 5.5</vh>
<v t="ekr.20170301014329.1"><vh>@button cycling syntax coloring</vh></v>
<v t="ekr.20170211083757.1"><vh>@button Demo @key=Ctrl-9</vh>
<v t="ekr.20170211083757.2"><vh>&lt;&lt; imports &gt;&gt;</vh></v>
<v t="ekr.20170211083757.3"><vh>&lt;&lt; class myDemo &gt;&gt;</vh>
<v t="ekr.20170211083757.4"><vh>setup</vh></v>
<v t="ekr.20170211083757.5"><vh>setup_script</vh></v>
<v t="ekr.20170211083757.6"><vh>teardown</vh></v>
<v t="ekr.20170211083757.7"><vh>teardown_script</vh></v>
</v>
<v t="ekr.20170211083757.8"><vh>demo-script</vh>
<v t="ekr.20170211083757.9"><vh>First</vh></v>
<v t="ekr.20170211083757.10"><vh>Full featured outliner</vh></v>
<v t="ekr.20170211083757.11"><vh>Leo's panes</vh></v>
<v t="ekr.20170211083757.12"><vh>Leo creates scripts from outlines</vh></v>
<v t="ekr.20170211083757.13"><vh>Clones and cff</vh></v>
<v t="ekr.20170211083757.14"><vh>Last</vh></v>
</v>
</v>
<v t="ekr.20161006092435.1"><vh>@button import-tab-files</vh>
<v t="ekr.20161006092435.2"><vh>class TabImporter</vh>
<v t="ekr.20161006092435.3"><vh>check</vh></v>
<v t="ekr.20161006092435.4"><vh>dump_stack</vh></v>
<v t="ekr.20161006092435.5"><vh>lws</vh></v>
<v t="ekr.20161006092435.6"><vh>scan</vh></v>
<v t="ekr.20161006092435.7"><vh>scan_helper</vh></v>
<v t="ekr.20161006092435.8"><vh>undent</vh></v>
</v>
</v>
<v t="ekr.20170301023612.1"><vh>@button insert headlines as comments</vh></v>
<v t="ekr.20170212105552.1"><vh>@button IntroSlides @key=Ctrl-9</vh>
<v t="ekr.20170212105552.2"><vh>&lt;&lt; imports &gt;&gt;</vh></v>
<v t="ekr.20170212105552.3"><vh>&lt;&lt; class IntroSlides &gt;&gt;</vh>
<v t="ekr.20170212105552.4"><vh>setup</vh></v>
<v t="ekr.20170212105552.5"><vh>setup_script</vh></v>
<v t="ekr.20170212105552.6"><vh>teardown</vh></v>
<v t="ekr.20170212105552.7"><vh>teardown_script</vh></v>
</v>
<v t="ekr.20170212105552.8"><vh>&lt;&lt; main &gt;&gt;</vh></v>
<v t="ekr.20170212105552.9"><vh>Leo's Main Window</vh></v>
<v t="ekr.20170212105552.10"><vh>intro-slides-script</vh>
<v t="ekr.20170212105552.11"><vh>Slide 1: Leo's main window</vh></v>
</v>
</v>
<v t="ekr.20160923132656.1"><vh>@button introspect</vh></v>
<v t="ekr.20161031130627.1"><vh>@button pep8 @key=Ctrl-5</vh>
<v t="ekr.20161031130627.2"><vh>class Pep8</vh>
<v t="ekr.20161031130627.3"><vh>change_all &amp; helpers</vh>
<v t="ekr.20161031130627.4"><vh>change_body</vh></v>
<v t="ekr.20161031130627.5"><vh>change_headline</vh></v>
</v>
<v t="ekr.20161031130627.6"><vh>get_name</vh></v>
<v t="ekr.20161031130627.7"><vh>run</vh></v>
<v t="ekr.20161031130627.8"><vh>to_pep8</vh></v>
</v>
<v t="ekr.20161031130627.9"><vh>clear</vh></v>
</v>
<v t="ekr.20170225023738.1"><vh>@button test-http</vh></v>
<v t="ekr.20170120110015.1"><vh>@ignore importer scripts</vh>
<v t="ekr.20161120175101.1"><vh>@button make-md-heads</vh>
<v t="ekr.20161120175101.2"><vh>markup</vh></v>
</v>
<v t="ekr.20161123085419.1"><vh>@button make-table</vh></v>
<v t="ekr.20161124034654.1"><vh>@button make-importer</vh>
<v t="ekr.20161124034654.2"><vh>&lt;&lt; define run &amp; helpers &gt;&gt;</vh>
<v t="ekr.20161124034654.3"><vh>copy_tree</vh></v>
<v t="ekr.20161124034654.4"><vh>make_substitutions</vh></v>
<v t="ekr.20161124034654.5"><vh>run</vh></v>
<v t="ekr.20161124034654.6"><vh>substitue</vh></v>
</v>
<v t="ekr.20161124034654.7"><vh>@@file importers/{{name}}.py</vh>
<v t="ekr.20161124034654.8"><vh>class {{cap_name}}_Importer</vh>
<v t="ekr.20161124034654.9"><vh>{{name}}.Overrides</vh>
<v t="ekr.20161124034654.10"><vh>{{name}}.clean_headline</vh></v>
<v t="ekr.20161124034654.11"><vh>{{name}}.clean_nodes</vh></v>
</v>
</v>
<v t="ekr.20161124034654.12"><vh>class class {{cap_name}}_ScanState</vh>
<v t="ekr.20161124034654.13"><vh>{{name}}_state.level</vh></v>
<v t="ekr.20161124034654.14"><vh>{{name}}_state.update</vh></v>
</v>
</v>
</v>
<v t="ekr.20161204063803.1"><vh>@button make-md-toc</vh>
<v t="ekr.20161204063803.2"><vh>run</vh></v>
<v t="ekr.20161204063803.3"><vh>mak_link</vh></v>
<v t="ekr.20161204063803.4"><vh>make_toc</vh></v>
</v>
</v>
</v>
<v t="ekr.20181019042717.1"><vh>@ignore at-button nodes in LeoSettings.leo</vh>
<v t="ekr.20181019042907.1"><vh>@button check-bindings</vh>
<v t="ekr.20181019042907.2"><vh>defineSetNames</vh></v>
<v t="ekr.20181019042907.3"><vh>doSet</vh></v>
<v t="ekr.20181019042907.4"><vh>doShortcuts</vh></v>
<v t="ekr.20181019042907.5"><vh>checkSets</vh></v>
<v t="ekr.20181019042907.6"><vh>main</vh></v>
</v>
<v t="ekr.20181019042834.1"><vh>@button check-menus-cmds</vh></v>
<v t="ekr.20181019042808.1"><vh>@button check-settings</vh>
<v t="ekr.20181019042808.2"><vh>ctor</vh></v>
<v t="ekr.20181019042808.3"><vh>check &amp; helpers</vh>
<v t="ekr.20181019042808.4"><vh>filter_get_x</vh></v>
<v t="ekr.20181019042808.5"><vh>filter_user_config</vh></v>
</v>
<v t="ekr.20181019042808.6"><vh>error</vh></v>
<v t="ekr.20181019042808.7"><vh>get_commanders</vh></v>
<v t="ekr.20181019042808.8"><vh>get_configs &amp; helpers</vh>
<v t="ekr.20181019042808.9"><vh>get_configs_from_outline &amp; helper</vh>
<v t="ekr.20181019042808.10"><vh>scan_for_configs</vh></v>
</v>
</v>
<v t="ekr.20181019042808.11"><vh>get_settings &amp; helper</vh>
<v t="ekr.20181019042808.12"><vh>is_setting</vh></v>
<v t="ekr.20181019042808.13"><vh>parse_setting</vh></v>
</v>
<v t="ekr.20181019042808.14"><vh>munge</vh></v>
<v t="ekr.20181019042808.15"><vh>run</vh></v>
</v>
</v>
</v>
</v>
<v t="ekr.20061008055616" descendentVnodeUnknownAttributes="7d7100285809000000302e312e31302e313671017d710258120000007273745f687474705f61747472696275746571035d71042858460000003c6120636c6173733d22746172676574222069643d22687474702d6e6f64652d6d61726b65722d333922206e616d653d22687474702d6e6f64652d6d61726b65722d3339223e710558040000003c2f613e71065d71072858340000003c64697620636c6173733d2273656374696f6e222069643d2272756e6e696e672d6c656f2d696e2d62617463682d6d6f6465223e710858060000003c2f6469763e71095d710a28583f0000003c64697620636c6173733d22646f63756d656e74222069643d22636861707465722d372d736372697074696e672d6c656f2d776974682d707974686f6e223e710b58060000003c2f6469763e710c5d710d2858060000003c626f64793e710e58070000003c2f626f64793e710f5d71102858430000003c68746d6c20786d6c6e733d22687474703a2f2f7777772e77332e6f72672f313939392f7868746d6c2220786d6c3a6c616e673d22656e22206c616e673d22656e223e711158070000003c2f68746d6c3e71124e6565656558070000003c2f6469763e0a7113583e0000003c64697620636c6173733d2273656374696f6e222069643d2266756e6374696f6e732d646566696e65642d696e2d6c656f676c6f62616c732d7079223e0a7114587e0000003c68313e3c6120636c6173733d22746f632d6261636b7265662220687265663d22236964323022206e616d653d2266756e6374696f6e732d646566696e65642d696e2d6c656f676c6f62616c732d7079223e46756e6374696f6e7320646566696e656420696e206c656f476c6f62616c732e70793c2f613e3c2f68313e0a711565735809000000302e312e31302e323671167d7117580b0000005f5f626f6f6b6d61726b7371187d7119580700000069735f64757065711a4930300a7373752e"><vh>Scripts</vh>
<v t="ekr.20181019042544.1"><vh>script: regularize settings names</vh></v>
<v t="ekr.20051110111150" descendentVnodeUnknownAttributes="7d7100285807000000302e31302e313671017d710258120000007273745f687474705f61747472696275746571035d71042858460000003c6120636c6173733d22746172676574222069643d22687474702d6e6f64652d6d61726b65722d333922206e616d653d22687474702d6e6f64652d6d61726b65722d3339223e710558040000003c2f613e71065d71072858340000003c64697620636c6173733d2273656374696f6e222069643d2272756e6e696e672d6c656f2d696e2d62617463682d6d6f6465223e710858060000003c2f6469763e71095d710a28583f0000003c64697620636c6173733d22646f63756d656e74222069643d22636861707465722d372d736372697074696e672d6c656f2d776974682d707974686f6e223e710b58060000003c2f6469763e710c5d710d2858060000003c626f64793e710e58070000003c2f626f64793e710f5d71102858430000003c68746d6c20786d6c6e733d22687474703a2f2f7777772e77332e6f72672f313939392f7868746d6c2220786d6c3a6c616e673d22656e22206c616e673d22656e223e711158070000003c2f68746d6c3e71124e6565656558070000003c2f6469763e0a7113583e0000003c64697620636c6173733d2273656374696f6e222069643d2266756e6374696f6e732d646566696e65642d696e2d6c656f676c6f62616c732d7079223e0a7114587e0000003c68313e3c6120636c6173733d22746f632d6261636b7265662220687265663d22236964323022206e616d653d2266756e6374696f6e732d646566696e65642d696e2d6c656f676c6f62616c732d7079223e46756e6374696f6e7320646566696e656420696e206c656f476c6f62616c732e70793c2f613e3c2f68313e0a711565735807000000302e31302e323671167d7117580b0000005f5f626f6f6b6d61726b7371187d7119580700000069735f64757065711a4930300a7373752e"><vh>@file leoScripts.txt</vh></v>
</v>
<v t="ekr.20230925012840.1"><vh>--- Recent</vh>
<v t="ekr.20211020082938.1"><vh>script: display slides</vh>
<v t="ekr.20211020082938.2"><vh>get_files</vh></v>
<v t="ekr.20211020082938.3"><vh>class Slides</vh>
<v t="ekr.20211020082938.4"><vh>Slides.delete</vh></v>
<v t="ekr.20211020082938.5"><vh>Slides.keyPressEvent</vh></v>
<v t="ekr.20211020082938.6"><vh>Slides.move_up/down/left/right</vh></v>
<v t="ekr.20211020082938.7"><vh>Slides.move_to</vh></v>
<v t="ekr.20211020082938.8"><vh>Slides.next_slide</vh></v>
<v t="ekr.20211020082938.9"><vh>Slides.prev_slide</vh></v>
<v t="ekr.20211020082938.10"><vh>Slides.quit</vh></v>
<v t="ekr.20211020082938.11"><vh>Slides.run &amp; helper</vh>
<v t="ekr.20211020082938.12"><vh>Slides.make_widgets</vh></v>
</v>
<v t="ekr.20211020082938.13"><vh>Slides.show_help</vh></v>
<v t="ekr.20211020082938.14"><vh>Slides.show_slide</vh></v>
<v t="ekr.20211020082938.15"><vh>Slides.sort</vh></v>
<v t="ekr.20211020082938.16"><vh>Slides.timerEvent</vh></v>
<v t="ekr.20211020082938.17"><vh>Slides.toggle_full_screen</vh></v>
<v t="ekr.20211020082938.18"><vh>Slides.zoom_in &amp; zoom_out</vh></v>
</v>
</v>
<v t="ekr.20220319162442.1"><vh>script: diff-two-revs-wo-comments</vh>
<v t="ekr.20220319162442.2"><vh>function: make_diff_outlines_ignoring_comments</vh></v>
<v t="ekr.20220319162442.3"><vh>function: strip_comments</vh></v>
</v>
<v t="ekr.20221105063835.1"><vh>script: insert-macros</vh>
<v t="ekr.20221105063835.2"><vh>add_macros</vh></v>
<v t="ekr.20221105063835.3"><vh>macro-test</vh>
<v t="ekr.20221105063835.4"><vh>multiple defs</vh></v>
<v t="ekr.20221105063835.5"><vh>spam</vh></v>
<v t="ekr.20221105063835.6"><vh>eggs</vh></v>
</v>
</v>
<v t="ekr.20230421070847.1"><vh>script: replace kwargs in mode files</vh></v>
<v t="ekr.20230925012359.1"><vh>script: list all rhythms in a 4/4 measure</vh>
<v t="ekr.20230925012359.2"><vh>compute_quarters</vh></v>
<v t="ekr.20230925012359.3"><vh>convert_pattern and convert_patterns</vh></v>
<v t="ekr.20230925012359.4"><vh>find_patterns</vh></v>
<v t="ekr.20230925012359.5"><vh>is_square</vh></v>
</v>
<v t="ekr.20231008041100.1"><vh>script: to-github</vh>
<v t="ekr.20231008041100.2"><vh>put_all_nodes</vh></v>
<v t="ekr.20231008041100.3"><vh>python_i.delete_comments_and_strings</vh></v>
</v>
<v t="ekr.20231217050920.1"><vh>Tom Passin's Current Directory Buttons</vh>
<v t="ekr.20231217050920.2"><vh>@button Show Current Dir</vh></v>
<v t="ekr.20231217050920.3"><vh>@button Cmd Window Here</vh></v>
<v t="ekr.20231217050920.4"><vh>@button Node Dir to Clip</vh></v>
</v>
</v>
<v t="ekr.20060824111500.108"><vh>@button jEdit2Py</vh>
<v t="ekr.20060824111500.113"><vh>top-level</vh>
<v t="ekr.20160319080102.1"><vh>cleanSaxInputString</vh></v>
<v t="ekr.20060824111500.115"><vh>parse_jEdit_file</vh></v>
<v t="ekr.20060824111500.114"><vh>convert</vh></v>
<v t="ekr.20060824111500.116"><vh>munge</vh></v>
<v t="ekr.20160319081141.1"><vh>main</vh></v>
</v>
<v t="ekr.20060824111500.117"><vh>class Mode</vh>
<v t="ekr.20060824111500.118"><vh> Mode.__init__</vh></v>
<v t="ekr.20060824111500.119"><vh> Mode.__str__ &amp; __repr__</vh></v>
<v t="ekr.20060824111500.153"><vh>Mode.error</vh></v>
<v t="ekr.20060824111500.154"><vh>Mode: Getters</vh></v>
<v t="ekr.20220927115543.1"><vh>Mode: Handlers</vh>
<v t="ekr.20060824111500.150"><vh>Mode.doAttribute</vh></v>
<v t="ekr.20060824111500.151"><vh>Mode.doContent</vh></v>
<v t="ekr.20060824111500.152"><vh>Mode.endElement</vh></v>
<v t="ekr.20060824111500.155"><vh>Mode.inRule</vh></v>
<v t="ekr.20060824111500.156"><vh>Mode.startElement</vh></v>
</v>
<v t="ekr.20060824111500.120"><vh>Mode: Output...</vh>
<v t="ekr.20060824111500.121"><vh>Mode.escapeString &amp; quoteString</vh></v>
<v t="ekr.20060827162343"><vh>Mode.fullDelegate</vh></v>
<v t="ekr.20060824111500.122"><vh>Mode.put</vh></v>
<v t="ekr.20060824111500.123"><vh>Mode.putAttributes</vh></v>
<v t="ekr.20060824111500.124"><vh>Mode.putDict &amp; putDictOfLists</vh></v>
<v t="ekr.20060824111500.125"><vh>Mode.putImportDict</vh></v>
<v t="ekr.20060824111500.126"><vh>Mode.putKeywordsData</vh></v>
<v t="ekr.20060824111500.128"><vh>Mode.putModeProperties</vh></v>
<v t="ekr.20060824111500.129"><vh>Mode.putRule &amp; rule creators</vh>
<v t="ekr.20060824111500.130"><vh>Mode.putBadRule</vh></v>
<v t="ekr.20060824111500.131"><vh>Mode.putEolSpan</vh></v>
<v t="ekr.20060824111500.132"><vh>Mode.putEolSpanRegexp</vh></v>
<v t="ekr.20060824111500.133"><vh>Mode.putImport</vh></v>
<v t="ekr.20060824111500.134"><vh>Mode.putKeywords</vh></v>
<v t="ekr.20060824111500.135"><vh>Mode.putMarkFollowing</vh></v>
<v t="ekr.20060824111500.136"><vh>Mode.putMarkPrevious</vh></v>
<v t="ekr.20060824111500.137"><vh>Mode.putSeq</vh></v>
<v t="ekr.20060824111500.138"><vh>Mode.putSeqRegexp</vh></v>
<v t="ekr.20060824111500.139"><vh>Mode.putSpan</vh></v>
<v t="ekr.20060824111500.140"><vh>Mode.putSpanRegexp</vh></v>
<v t="ekr.20060824111500.141"><vh>Mode.putTerminate</vh></v>
</v>
<v t="ekr.20060824111500.143"><vh>Mode.write</vh></v>
<v t="ekr.20060824111500.142"><vh>Mode.putRules</vh></v>
</v>
<v t="ekr.20060824111500.144"><vh>Mode: Printing...</vh>
<v t="ekr.20060824111500.145"><vh>Mode.printModeAttributes, printRulesetAttributes &amp; printAttributesHelper</vh></v>
<v t="ekr.20060824111500.146"><vh>Mode.printProperty</vh></v>
<v t="ekr.20060824111500.147"><vh>Mode.printRule</vh></v>
<v t="ekr.20060824111500.148"><vh>Mode.printRuleset</vh></v>
</v>
</v>
<v t="ekr.20060824111500.159"><vh>class Rule</vh>
<v t="ekr.20060824111500.161"><vh>rule.getters</vh></v>
</v>
<v t="ekr.20060824111500.157"><vh>class Ruleset</vh></v>
<v t="ekr.20060824111500.162"><vh>class ContentHandler(xml.sax.saxutils.XMLGenerator)</vh>
<v t="ekr.20060824111500.163"><vh> __init__ &amp; helpers</vh></v>
<v t="ekr.20060824111500.164"><vh>helpers</vh>
<v t="ekr.20060824111500.165"><vh>attrsToList</vh></v>
<v t="ekr.20060824111500.166"><vh>attrsToString</vh></v>
<v t="ekr.20060824111500.168"><vh>error</vh></v>
<v t="ekr.20060824111500.169"><vh>printStartElement</vh></v>
</v>
<v t="ekr.20060824111500.171"><vh>sax over-rides</vh>
<v t="ekr.20060824111500.172"><vh> Do nothing...</vh></v>
<v t="ekr.20060824111500.176"><vh>characters</vh></v>
<v t="ekr.20060824111500.177"><vh>elements &amp; helpers</vh>
<v t="ekr.20060824111500.179"><vh>doStartElement</vh></v>
<v t="ekr.20060824111500.180"><vh>doEndElement</vh></v>
</v>
</v>
<v t="ekr.20060824111500.181"><vh>getMode</vh></v>
</v>
</v>
<v t="ekr.20241117081230.1"><vh>script: scan-modes</vh>
<v t="ekr.20241117081901.1"><vh>scan</vh></v>
<v t="ekr.20241117082739.1"><vh>scan_def</vh></v>
<v t="ekr.20241117125039.1"><vh>add_matcher</vh></v>
<v t="ekr.20241117145426.1"><vh>delete_cruft</vh></v>
<v t="ekr.20241117131431.1"><vh>delete_strings</vh></v>
<v t="ekr.20241117125038.1"><vh>filter_line</vh></v>
<v t="ekr.20241117125039.2"><vh>show_line</vh></v>
</v>
</vnodes>
<tnodes>
<t tx="ekr.20060824111500.108">"""
Convert jEdit description file (an .xml file) to an equivalent .py file.
"""
import string
import textwrap
import xml.sax
import xml.sax.saxutils

# Globals...
files_list = ['openscad.xml']
theDir = g.os_path_abspath(g.os_path_join(g.app.loadDir, '..', 'modes'))
files = [g.os_path_abspath(g.os_path_join(theDir, s)) for s in files_list]

@others

main()

@language python
@tabwidth -4
@pagewidth 80
</t>
<t tx="ekr.20060824111500.113"></t>
<t tx="ekr.20060824111500.114">def convert(inputFileName, outputFileName):

    junk, fn = g.os_path_split(inputFileName)
    language, junk = g.os_path_splitext(fn)
    mode = parse_jEdit_file(inputFileName, language)
    if not mode:
        g.trace('FAIL: no mode', language, inputFileName)
        return
    with open(outputFileName, 'w') as f:
        try:
            mode.write(f, language)
            g.es_print('wrote', outputFileName)
        except IOError:
            g.es_print('can not create', outputFileName)
</t>
<t tx="ekr.20060824111500.115">def parse_jEdit_file(inputFileName, language):

    if not inputFileName:
        return None
    if not inputFileName.endswith('.xml'):
        inputFileName = inputFileName + '.xml'
    path = g.os_path_join(g.app.loadDir, '../', 'modes', inputFileName)
    path = g.os_path_normpath(path)
    if not g.os_path_exists(path):
        g.es_print('not found:', path)
        return
    try:
        f = open(path, 'r')
    except IOError:
        g.trace(f"can not open {path!r}")
        return None
    try:
        try:
            mode = None
            parser = xml.sax.make_parser()
                # Do not include external general entities.
                # The actual feature name is
                # "http://xml.org/sax/features/external-general-entities"
            parser.setFeature(xml.sax.handler.feature_external_ges, 0)
            handler = ContentHandler(inputFileName)
            parser.setContentHandler(handler)
            parser.parse(f)
                # Works with Python 3.
                # Fails with Python 2 when the file contains non-ascii characters.
            mode = handler.getMode()
        except:
            g.es(f"unexpected exception parsing {inputFileName}", color='red')
            g.es_exception()
    finally:
        f.close()
        return mode
</t>
<t tx="ekr.20060824111500.116">def munge(s):

    '''Munge a mode name so that it is a valid python id.'''

    valid = string.ascii_letters + string.digits + '_'
    return ''.join([ch.lower() if ch in valid else '_' for ch in s])
</t>
<t tx="ekr.20060824111500.117">class Mode:
    """A class representing one jEdit language-description mode."""

    boolAttrs = [  # Boolean attributes.
        'at_line_start', 'at_whitespace_end', 'at_word_start',
        'exclude_match', 'highlight_digits', 'ignore_case',
        'no_escape', 'no_line_break', 'no_word_break',
    ]
    ruleElements = [  # Elements that start a rule.
        'eol_span', 'eol_span_regexp',
        'import', 'keywords',
        'mark_following', 'mark_previous',
        'seq', 'seq_regexp',
        'span', 'span_regexp',
        'terminate',
    ]

    @others
</t>
<t tx="ekr.20060824111500.118">def __init__ (self, contentHandler, fileName):
    
    self.contentHandler = contentHandler
    self.fileName = g.shortFileName(fileName)  # The file from which the mode was imported.
    modeName, junk = g.os_path_splitext(self.fileName)
    self.fileModeName = modeName
    self.modeName = munge(modeName).lower()
    self.outputFile = None  # The open output file to which Python statements get written.

    # Mode semantics.
    self.attributes = {}
    self.handlerCount = 0
    self.importedRules = []  # A bunch describing the imported ruleset.
    self.inProps = False
    self.inRules = False
    self.keywords = None
    self.modeProperties = []
    self.presentProperty = None  # A bunch to be assigned to modeProperties or rulesetProperties.
    self.rule = None
    self.rulesets = []
    self.rules = []  # The rules of the present rules element.
    self.rulesetProperties = []
    self.rulesetAttributes = {}  # The attributes of the present rules element.
</t>
<t tx="ekr.20060824111500.119">def __str__ (self):
    return f"&lt;Mode for {self.fileName}&gt;"

__repr__ = __str__
</t>
<t tx="ekr.20060824111500.120"># These methods generate the output file.
</t>
<t tx="ekr.20060824111500.121">def escapeString (self, s):
    '''Return string s enclosed in double quotes.'''
    if not s:
        return '""'
    if isinstance(s, (list, tuple)):
        s = ''.join(s)
    s = g.toUnicode(s)
    # Order is important: escape backspaces first.
    # Don't use an f-string here!
    return '"%s"' % s.replace('\\', '\\\\').replace('"', '\\"').replace('\t', '\\t')

quoteString = escapeString
</t>
<t tx="ekr.20060824111500.122">def put (self, s):
    self.outputFile.write(s)
</t>
<t tx="ekr.20060824111500.123">def putAttributes (self):

    dd = {}
    data = (
        ('default', 'null'),
        ('digit_re', ''),
        ('escape', ''),
        ('highlight_digits', True),
        ('ignore_case', True),
        ('no_word_sep', None), # could be false or ''.
    )
    for ruleset in self.rulesets:
        d = {}
        prefix = f"{self.modeName}_{ruleset.name}"
        self.put(f"# Attributes dict for {prefix} ruleset.\n")
        for key, default in data:
            val = ruleset.attributes.get(key, default)
            if default == True:
                val = 'true' if val else 'false'
            elif default == None:
                if val and val.lower() == 'false':
                    val = ''
                else:
                    val = val or ''
            d [key] = val
        self.putDict(f"{prefix}_attributes_dict", d)
        dd [ prefix ] = f"{prefix}_attributes_dict"

    self.put(f"# Dictionary of attributes dictionaries for {self.modeName} mode.\n")
    self.putDict('attributesDictDict', dd, escape=False)
</t>
<t tx="ekr.20060824111500.124">def putDict (self, name, theDict, escape=True):

    esc = self.escapeString
    esc2 = self.escapeString if escape else lambda a: a
    keys = list(set((theDict.keys())))
    keys = sorted([g.toUnicode(z) for z in keys if z])
    s = ''.join([
        f"    {esc(key)}: {esc2(theDict.get(key))},\n" for key in keys
    ])
    if s:
        s = '\n' + s
    self.put(f"{name} = {{{s}}}\n\n")

def putDictOfLists (self, name, theDict, strings=False):

    esc = self.escapeString
    keys = list(set(theDict.keys()))
    keys = sorted([g.toUnicode(z) for z in keys if z])
    theList = []
    for key in keys:
        if strings:
            # Not completely general, but it works for the import dict, and that's good enough.
            s = ', '.join([f'"{z}"' for z in theDict.get(key)])
        else:
             s = ', '.join([f"{z}" for z in theDict.get(key)])
        theList.append(f"    {esc(key)}: [{s}],\n")
    s = ''.join(theList)
    if s:
        s = '\n' + s
    self.put(f"{name} = {{{s}}}\n\n")
</t>
<t tx="ekr.20060824111500.125">def putImportDict (self):

    d = {}
    for ruleset in self.rulesets:
        prefix = f"{self.modeName}_{ruleset.name}"
        for rule in ruleset.rules:
            if rule.name == 'import':
                delegate = rule.getStrAttrib('delegate').lower()
                if delegate:
                    delegate_name = delegate if '::' in delegate else f"{prefix}::{delegate}"
                    theList = d.get(prefix, [])
                    if delegate_name not in theList:
                        theList.append(delegate_name)
                        d [prefix] = theList

    self.put(f"# Import dict for {self.modeName} mode.\n")
    self.putDictOfLists('importDict', d, strings=True)
</t>
<t tx="ekr.20060824111500.126">def putKeywordsData (self):

    dd = {}
    for ruleset in self.rulesets:
        prefix = f"{self.modeName}_{ruleset.name}"
        prefix = g.toUnicode(prefix)
        ignore_case = ruleset.attributes.get('ignore_case', 'false')
        if not isinstance(ignore_case, bool):
            ignore_case = ignore_case.lower() == 'false'
        self.put(f"# Keywords dict for {prefix} ruleset.\n")
        for rule in ruleset.rules:
            if rule.name == 'keywords':
                # d = rule.keywordsDict
                d = {
                    g.toUnicode(key): g.toUnicode(val)
                        for key, val in rule.keywordsDict.items()
                            if key.strip()
                }
                for key in sorted(set(d.keys())):
                    if ' ' in key or '\t' in key:
                        del d [key]
                        g.es_print(f"Ignoring keyword containing whitespace: {key!r}")
                    elif ignore_case: # New in 4.4.1 final.
                        # Downcase all keys.
                        val = d.get(key)
                        key2 = key.lower()
                        if key2 != key:
                            if key in d: del d[key]
                        d[key2] = val
                break
        else:
            d = {}
        self.putDict('%s_keywords_dict' % (prefix), d)
        dd [ prefix ] = '%s_keywords_dict' % (prefix)

    self.put(f"# Dictionary of keywords dictionaries for {self.modeName} mode.\n")
    self.putDict('keywordsDictDict', dd, escape=False)
</t>
<t tx="ekr.20060824111500.128">def putModeProperties (self, language):

    d = {}
    self.put(f"# Properties for {language} mode.\n")
    for prop in self.modeProperties:
        d2 = prop.attributes
        name = d2.get('name')
        d [name] = d2.get('value')
    self.putDict('properties', d)
</t>
<t tx="ekr.20060824111500.129">def putRule (self, rule):
    """Call the rule creator for the given rule."""
    d = {
        'eol_span':         self.putEolSpan,
        'eol_span_regexp':  self.putEolSpanRegexp,
        'import':           self.putImport,
        'keywords':         self.putKeywords,
        'mark_following':   self.putMarkFollowing,
        'mark_previous':    self.putMarkPrevious,
        'seq':              self.putSeq,
        'seq_regexp':       self.putSeqRegexp,
        'span':             self.putSpan,
        'span_regexp':      self.putSpanRegexp,
        'terminate':        self.putTerminate,
    }
    f = d.get(rule.name, self.putBadRule)
    val = f (rule)
    self.handlerCount += 1
    return val
</t>
<t tx="ekr.20060824111500.130">def putBadRule (self, rule):

    self.put(f"\n\n# *****no output creator for {rule.name}*****")
</t>
<t tx="ekr.20060824111500.131">def putEolSpan (self, rule):

    quote = self.quoteString
    seq = rule.getSeq(rule.name)

    s = '''\n\
def %s_rule%d(colorer, s, i):
    return colorer.match_eol_span(s, i, kind=%s, seq=%s,
        at_line_start=%s, at_whitespace_end=%s, at_word_start=%s,
        delegate=%s, exclude_match=%s)''' % (
        self.fileModeName,
        self.handlerCount,
        quote(rule.getStrAttrib('type').lower()),
        quote(seq),
        rule.getBoolAttrib('at_line_start'),
        rule.getBoolAttrib('at_whitespace_end'),
        rule.getBoolAttrib('at_word_end'),
        quote(self.fullDelegate(rule.getStrAttrib('delegate'))),
        rule.getBoolAttrib('exclude_match'),
    )
    self.put(textwrap.dedent(s))
    return seq[0]
</t>
<t tx="ekr.20060824111500.132">def putEolSpanRegexp (self, rule):

    quote = self.quoteString
    seq = rule.getSeq(rule.name)
    hash_char = rule.getStrAttrib('hash_char') or seq[0]

    s = '''\n\
def %s_rule%d(colorer, s, i):
    return colorer.match_eol_span_regexp(s, i, kind=%s, regexp=%s,
        at_line_start=%s, at_whitespace_end=%s, at_word_start=%s,
        delegate=%s, exclude_match=%s)''' % (
        self.fileModeName,
        self.handlerCount,
        quote(rule.getStrAttrib('type').lower()),
        quote(seq),
        rule.getBoolAttrib('at_line_start'),
        rule.getBoolAttrib('at_whitespace_end'),
        rule.getBoolAttrib('at_word_end'),
        quote(self.fullDelegate(rule.getStrAttrib('delegate'))),
        rule.getBoolAttrib('exclude_match'),
    )
    self.put(textwrap.dedent(s))
    return hash_char
</t>
<t tx="ekr.20060824111500.133"># Do nothing here: putImportDict creates x.importDict.

def putImport (self, rule):

    # Decrement the count to indicate that this method did not generate a rule.
    self.handlerCount -= 1
    return ''
</t>
<t tx="ekr.20060824111500.134">def putKeywords (self, rule):

    s = '''\n\
def %s_rule%d(colorer, s, i):
    return colorer.match_keywords(s, i)''' % (
    self.fileModeName,
    self.handlerCount)
    self.put(textwrap.dedent(s))
    return 'keywords'
</t>
<t tx="ekr.20060824111500.135">def putMarkFollowing (self, rule):

    quote = self.quoteString
    seq = rule.getSeq(rule.name)

    s = '''\n\
def %s_rule%d(colorer, s, i):
    return colorer.match_mark_following(s, i, kind=%s, pattern=%s,
        at_line_start=%s, at_whitespace_end=%s, at_word_start=%s, exclude_match=%s)''' % (
        self.fileModeName,
        self.handlerCount,
        quote(rule.getStrAttrib('type').lower()),
        quote(seq),
        rule.getBoolAttrib('at_line_start'),
        rule.getBoolAttrib('at_whitespace_end'),
        rule.getBoolAttrib('at_word_end'),
        rule.getBoolAttrib('exclude_match'),
    )
    self.put(textwrap.dedent(s))
    return seq[0]
</t>
<t tx="ekr.20060824111500.136">def putMarkPrevious (self, rule):

    quote = self.quoteString
    seq = rule.getSeq(rule.name)

    s = '''\n\
def %s_rule%d(colorer, s, i):
    return colorer.match_mark_previous(s, i, kind=%s, pattern=%s,
        at_line_start=%s, at_whitespace_end=%s, at_word_start=%s, exclude_match=%s)''' % (
        self.fileModeName,
        self.handlerCount,
        quote(rule.getStrAttrib('type').lower()),
        quote(seq),
        rule.getBoolAttrib('at_line_start'),
        rule.getBoolAttrib('at_whitespace_end'),
        rule.getBoolAttrib('at_word_end'),
        rule.getBoolAttrib('exclude_match'),
    )
    self.put(textwrap.dedent(s))
    return seq[0]
</t>
<t tx="ekr.20060824111500.137">def putSeq (self, rule):

    quote = self.quoteString
    seq = rule.getSeq(rule.name)

    s = '''\n\
def %s_rule%d(colorer, s, i):
    return colorer.match_seq(s, i, kind=%s, seq=%s,
        at_line_start=%s, at_whitespace_end=%s, at_word_start=%s, delegate=%s)''' % (
        self.fileModeName,
        self.handlerCount,
        quote(rule.getStrAttrib('type').lower()),
        quote(seq),
        rule.getBoolAttrib('at_line_start'),
        rule.getBoolAttrib('at_whitespace_end'),
        rule.getBoolAttrib('at_word_end'),
        quote(self.fullDelegate(rule.getStrAttrib('delegate'))),
    )
    self.put(textwrap.dedent(s))
    return seq[0]
</t>
<t tx="ekr.20060824111500.138">def putSeqRegexp (self, rule):

    quote = self.quoteString
    seq = rule.getSeq(rule.name)
    hash_char = rule.getStrAttrib('hash_char') or seq[0]

    s = '''\n\
def %s_rule%d(colorer, s, i):
    return colorer.match_seq_regexp(s, i, kind=%s, regexp=%s,
        at_line_start=%s, at_whitespace_end=%s, at_word_start=%s, delegate=%s)''' % (
        self.fileModeName,
        self.handlerCount,
        quote(rule.getStrAttrib('type').lower()),
        quote(seq),
        # quote(rule.getStrAttrib('hash_char')),
        rule.getBoolAttrib('at_line_start'),
        rule.getBoolAttrib('at_whitespace_end'),
        rule.getBoolAttrib('at_word_end'),
        quote(self.fullDelegate(rule.getStrAttrib('delegate'))),
    )
    self.put(textwrap.dedent(s))
    return hash_char
</t>
<t tx="ekr.20060824111500.139">def putSpan (self, rule):

    quote = self.quoteString
    begin, end = rule.getSpan()

    s = '''\n\
def %s_rule%d(colorer, s, i):
    return colorer.match_span(s, i, kind=%s, begin=%s, end=%s,
        at_line_start=%s, at_whitespace_end=%s, at_word_start=%s,
        delegate=%s, exclude_match=%s,
        no_escape=%s, no_line_break=%s, no_word_break=%s)''' % (
        self.fileModeName,
        self.handlerCount,
        quote(rule.getStrAttrib('type').lower()),
        quote(begin), quote(end),
        rule.getBoolAttrib('at_line_start'),
        rule.getBoolAttrib('at_whitespace_end'),
        rule.getBoolAttrib('at_word_end'),
        quote(self.fullDelegate(rule.getStrAttrib('delegate'))),
        rule.getBoolAttrib('exclude_match'),
        rule.getBoolAttrib('no_escape'),
        rule.getBoolAttrib('no_line_break'),
        rule.getBoolAttrib('no_word_break'),
    )
    self.put(textwrap.dedent(s))
    return begin[0]
</t>
<t tx="ekr.20060824111500.140">def putSpanRegexp (self, rule):

    quote = self.quoteString
    begin, end = rule.getSpan()
    hash_char = rule.getStrAttrib('hash_char') or begin[0]

    s = '''\n\
def %s_rule%d(colorer, s, i):
    return colorer.match_span_regexp(s, i, kind=%s, begin=%s, end=%s,
        at_line_start=%s, at_whitespace_end=%s, at_word_start=%s,
        delegate=%s, exclude_match=%s,
        no_escape=%s, no_line_break=%s, no_word_break=%s)''' % (
        self.fileModeName,
        self.handlerCount,
        quote(rule.getStrAttrib('type').lower()),
        quote(begin), quote(end),
        rule.getBoolAttrib('at_line_start'),
        rule.getBoolAttrib('at_whitespace_end'),
        rule.getBoolAttrib('at_word_end'),
        quote(self.fullDelegate(rule.getStrAttrib('delegate'))),
        rule.getBoolAttrib('exclude_match'),
        rule.getBoolAttrib('no_escape'),
        rule.getBoolAttrib('no_line_break'),
        rule.getBoolAttrib('no_word_break'),
    )
    self.put(textwrap.dedent(s))
    return hash_char
</t>
<t tx="ekr.20060824111500.141">def putTerminate (self, rule):

    quote = self.quoteString

    n = rule.getIntAttrib('at_char')
    if n == None:
        return
    s = '''\n\
def %s_rule%d(colorer, s, i):
    return colorer.match_terminate(s, i, kind=%s, at_char=%d)''' % (
        self.fileModeName,
        self.handlerCount,
        quote(rule.getStrAttrib('type').lower()),
        n,
    )
    self.put(textwrap.dedent(s))
</t>
<t tx="ekr.20060824111500.142">def putRules (self):

    '''Create all rule matchers, a rules dict for each ruleset and x.rulesDictDict.'''

    d = {} ; d2Count = 0
    for ruleset in self.rulesets:
        d2 = {}
        prefix = f"{self.modeName}_{ruleset.name}"
        self.put(f"# Rules for {prefix} ruleset.\n")
        for rule in ruleset.rules:
            ch = self.putRule(rule)
            self.put('\n')
            if ch == 'keywords':
                for ch in self.keywordChars:
                    theList = d2.get(ch, [])
                    theList.append(f"{self.fileModeName}_rule{self.handlerCount-1}")
                    d2 [ch] = theList
            elif ch:
                theList = d2.get(ch, [])
                theList.append(f"{self.fileModeName}_rule{self.handlerCount-1}")
                d2 [ch] = theList
        # Create the rules dict for the ruleset.
        self.put(f"\n# Rules dict for {prefix} ruleset.\n")
        d2Count += 1
        name = f"rulesDict{d2Count}"
        self.putDictOfLists(name, d2)
        d [prefix] = name
    # Create rulesDictDict.
    self.put(f"# x.rulesDictDict for {self.modeName} mode.\n")    
    self.putDict('rulesDictDict', d, escape=False)
</t>
<t tx="ekr.20060824111500.143">def write (self, theFile, language):

    # Compute all the letters that can occur in a keyword.
    self.keywordChars = [ch for ch in string.ascii_letters + string.digits + '@']
    for ruleset in self.rulesets:
        for rule in ruleset.rules:
            d = rule.keywordsDict
            for key in list(d.keys()):
                key = g.toUnicode(key)
                for ch in key:
                    if ch not in self.keywordChars and ch not in (' ', '\t', '\n'):
                        self.keywordChars.append(ch)
        
    self.keywordChars = ''.join(list(set(self.keywordChars)))
    self.outputFile = theFile
    self.put('# Leo colorizer control file for %s mode.\n' % language)
    self.put('# This file is in the public domain.\n\n')
    self.putModeProperties(language)
    self.putAttributes()
    self.putKeywordsData()
    self.putRules()
    self.putImportDict()
</t>
<t tx="ekr.20060824111500.144"># Not used.
</t>
<t tx="ekr.20060824111500.145">def printModeAttributes (self):
    self.printAttributesHelper('mode attributes', self.attributes)

def printRulesetAttributes (self, ruleset, tag=None):
    if not tag:
        tag = 'main ruleset'
    self.printAttributesHelper(tag, ruleset.attributes)

def printAttributesHelper (self, kind, attrs):
    print('%-15s' % (kind), 'attrs:', attrs)
</t>
<t tx="ekr.20060824111500.146">def printProperty (self, theProperty):
    # A property is a bunch.
    d = theProperty.attributes
    if d:
        self.printAttributesHelper('property', d)
</t>
<t tx="ekr.20060824111500.147">def printRule (self, rule):

    # A rule is a g.Bunch.
    if rule.name == 'keywords':
        print('%-15s' % ('rule:keywords'))
        d = rule.keywordsDict
        d2 = {}
        for key in d:
            val = d.get(key)
            d2 [val] = d2.get(val, 0) + 1
        keys = list(d2.keys())
        keys.sort()
        for key in keys:
            print(f"{key}: {d2.get(key)}")
        print()
    else:
        d = rule.attributes
        d2 = rule.contents
        if d or d2:
            print('%-15s' % ('rule:'+rule.name))
            if d:
                print('attrs:', d)
            if d2:
                print('contents:', d2)
            print()
</t>
<t tx="ekr.20060824111500.148">def printRuleset (self, ruleset, tag):

    self.printRulesetAttributes(ruleset, tag)

    for rule in self.rulesets[0].rules:
        self.printRule(rule)
</t>
<t tx="ekr.20060824111500.150">def doAttribute (self, name, val):

    name = str(name.lower())
    if name in self.boolAttrs:
        val = True if val.lower() == 'true' else False
    else:
        val = str(val) # Do NOT lower this value!
    if self.rule:
        d = self.rule.attributes
        d [name] = val
    elif self.presentProperty:
        d = self.presentProperty.get('attributes')
        d [name] = val
    elif self.inRules:
        self.rulesetAttributes[name] = val
    else:
        self.attributes[name] = val
</t>
<t tx="ekr.20060824111500.151">def doContent (self, elementName, content):

    if not content:
        return
    name = str(elementName.lower())
    if self.inRule('keywords'):
        d = self.rule.keywordsDict
        d [ content ] = name
    elif self.rule:
        d = self.rule.contents
        s = d.get(name, '')
        d [name] = s + g.toUnicode(content)
        self.contents = d
</t>
<t tx="ekr.20060824111500.152">def endElement (self, elementName):

    name = elementName.lower()

    if name == 'props':
        self.inProps = True
    if name == 'rules':
        self.inRules = False
        ruleset = Ruleset(self.rulesetAttributes, self.keywords, self.rulesetProperties, self.rules)
        self.rulesets.append(ruleset)
    if name == 'property':
        bunch = self.presentProperty
        if bunch:
            if self.inRules:
                self.rulesetProperties.append(bunch)
            else:
                self.modeProperties.append(bunch)
        else:
            self.error(f"end {name} not matched by start {name}")
        self.presentProperty = None
    if name in self.ruleElements:
        if self.inRule(name):
            self.rules.append(self.rule)
            self.rule = None
        else:
            self.error(f"end {name} not matched by start {name}")
</t>
<t tx="ekr.20060824111500.153">def error (self, message):
    self.contentHandler.error(message)
</t>
<t tx="ekr.20060824111500.154">def getAttributes (self):
    return self.attributes

def getAttributesForRuleset (self, ruleset):
    bunch = ruleset
    return bunch.attributes

def getFileName (self):
    return self.fileName

def getKeywords (self, n, ruleset):
    bunch = ruleset
    keywords = bunch.keywords
    if keywords:
        return keywords.get(f"keyword{n}", [])
    return []

def getLanguage (self):
    path, name = g.os_path_split(self.fileName)
    language, ext = g.os_path_splitext(name)
    return language

def getPropertiesForMode (self):
    return self.props

def getPropertiesForRuleset (self, name=''):
    bunch = self.getRuleset(name)
    if bunch:
        return bunch.properties
    else:
        return []

def getRuleset(self, name=''):
    if not name:
        return self.rulesets[0] # Return the main ruleset.
    for ruleset in self.rulesets:
        if ruleset.name.lower()==name.lower():
            return ruleset
    else: return None

def getRulesets(self):
    return self.rulesets

def getRulesForRuleset (self, name=''):
    bunch = self.getRuleset(name)
    if bunch:
        return bunch.rules
    else:
        return []
</t>
<t tx="ekr.20060824111500.155">def inRule (self, elementName):
    return self.rule and self.rule.name == elementName
</t>
<t tx="ekr.20060824111500.156">def startElement (self, elementName):

    name = elementName.lower()

    if name == 'props':
        self.inProps = True
    if name == 'rules':
        self.inRules = True
        self.attributes=[]
        self.keywords=[]
        self.rulesetProperties=[]
        self.rules=[]
    if name == 'property':
        if self.inProps:
            self.presentProperty = g.bunch(name=name, attributes={})
        else:
            self.error('property not in props element')
    if name in self.ruleElements:
        if self.inRules:
            self.rule = Rule(name=name)
            if name == 'keywords':
                self.keywords = self.rule
        else:
            self.error(f"{name} not in rules element")
</t>
<t tx="ekr.20060824111500.157">class Ruleset:

    def __init__ (self, attributes, keywords, properties, rules):
    
        self.name = munge(attributes.get('set', 'main'))
        self.attributes = attributes.copy()  # A dict.
        self.properties = properties[:]  # A list.
        self.keywords = keywords  # A bunch.
        self.rules = rules[:]  # A list.
        self.defaultColor = self.attributes.get('default')

    def __str__ (self):
        return f"&lt;ruleset {self.name}&gt;"

    __repr__ = __str__
</t>
<t tx="ekr.20060824111500.159">class Rule:
    """A class representing one xml rule."""
    
    def __init__ (self, name):
        self.attributes = {}
        self.contents = {}
        self.keywordsDict = {}
        self.name = name

    def __str__ (self):
        return (
            f"&lt;Rule {self.name}\n"
            f"   attr: {g.dictToString(self.attributes)}\n"
            f"conents: {g.dictToString(self.contents)}"
        )

    __repr__ = __str__

    @others
</t>
<t tx="ekr.20060824111500.161">def getBoolAttrib(self, name):
    d = self.attributes
    val = d.get(name)
    return 'True' if val else 'False'

def getIntAttrib(self, name):
    d = self.attributes
    val = d.get(name)
    if val is not None:
        try:
            val = int(val)
        except ValueError:
            g.trace(f"bad int argument: {name} = {val!r}")
            val = None
    return val

def getSpan (self):
    d = self.contents
    begin = d.get('begin', '')
    end   = d.get('end', '')
    return begin, end

def getStrAttrib(self, name):
    d = self.attributes
    val = d.get(name, '')
    return str(val)

def getSeq(self, kind):
    d = self.contents
    return d.get(kind, '')
</t>
<t tx="ekr.20060824111500.162">class ContentHandler (xml.sax.saxutils.XMLGenerator):

    '''A sax content handler class that handles jEdit language-description files.

    Creates mode that can be retrieved using the getMode method.'''

    @others
</t>
<t tx="ekr.20060824111500.163">def __init__ (self, inputFileName):

    # Init the base class.
    xml.sax.saxutils.XMLGenerator.__init__(self)
    self.inputFileName = inputFileName

    # Options...
    self.ignoreWs = True  # True: don't print contents with only ws.
    self.newLineAfterStartElement = [
        'keywords', 'mode', 'props', 'property', 'rules', 'span', 'eol_span',
    ]

    # Semantics: most of these should be mode ivars.
    self.elementStack = []
    self.errors = 0
    self.mode = None # The present mode, or None if outside all modes.
    self.modes = [] # All modes defined here or by imports.
</t>
<t tx="ekr.20060824111500.164"></t>
<t tx="ekr.20060824111500.165">def attrsToList (self, attrs):
    """
    Convert attrs to a list of g.Bunches.

    attrs: an Attributes item passed to startElement.
    """
    return [
        g.Bunch(name=name, val=attrs.getValue(name))
            for name in attrs.getNames()
    ]
</t>
<t tx="ekr.20060824111500.166">def attrsToString (self, attrs, sep='\n'):

    '''Convert the attributes to a string.

    attrs: an Attributes item passed to startElement.

    sep: the separator charater between attributes.'''

    result = [
        f'{bunch.name}="{bunch.val}"' for bunch in self.attrsToList(attrs)
    ]
    return sep.join(result)
</t>
<t tx="ekr.20060824111500.168">def error (self, message):
    print(f"\n\nXML error: {message}\n")
    self.errors += 1
</t>
<t tx="ekr.20060824111500.169">def printStartElement(self, name, attrs):

    name_s = g.toEncodedString(name, "ascii").strip()
    if attrs.getLength() &gt; 0:
        attrs_s = self.attrsToString(attrs, sep=' ')
        print(f"&lt;{name_s} {attrs_s}")
    else:
        print(f"&lt;{name_s}&gt;")
        
    if name.lower() in self.newLineAfterStartElement:
        print('')
</t>
<t tx="ekr.20060824111500.171"></t>
<t tx="ekr.20060824111500.172"># Expected calls...
def endDocument(self):
    pass
    
def startDocument(self):
    pass

# Trace unexpected calls...
def ignorableWhitespace(self):
    g.trace()

def processingInstruction (self, target, data):
    g.trace()

def skippedEntity(self, name):
    g.trace(name)

def startElementNS(self, name, qname, attrs):
    g.trace(name)

def endElementNS(self, name, qname):
    g.trace(name)
</t>
<t tx="ekr.20060824111500.176">def characters(self, content):
    content = content.replace('\r', '')
    if content.strip():
        content = content.strip()
    content = g.toEncodedString(content, "ascii")
    elementName = self.elementStack[-1].lower() if self.elementStack else '&lt;no element name&gt;'
    if self.mode:
        self.mode.doContent(elementName, content)
    else:
        self.error('characters outside of mode')
</t>
<t tx="ekr.20060824111500.177">def endElement(self, name):
    self.doEndElement(name)
    name2 = self.elementStack.pop()
    assert name == name2

def startElement(self, name, attrs):
    self.elementStack.append(name)
    self.doStartElement(name, attrs)
</t>
<t tx="ekr.20060824111500.179">def doStartElement (self, elementName, attrs):

    elementName = elementName.lower()
    if elementName == 'mode':
        if self.mode:
            self.error('Multiple modes')
        else:
            self.mode = Mode(self, self.inputFileName)
    elif self.mode:
        self.mode.startElement(elementName)
        for bunch in self.attrsToList(attrs):
            self.mode.doAttribute(bunch.name, bunch.val)
    else:
        self.error(f"Start element appears outside of Mode:{elementName}")
        for bunch in self.attrsToList(attrs):
            self.error(f"Attribute appears outside of Mode:{bunch.name}")
</t>
<t tx="ekr.20060824111500.180">def doEndElement (self, elementName):
    if self.mode:
        self.mode.endElement(elementName)
    else:
        self.error(f"End element appears outside of Mode:{elementName}")
</t>
<t tx="ekr.20060824111500.181">def getMode (self):
    return None if self.errors else self.mode
</t>
<t tx="ekr.20060827162343">def fullDelegate (self, delegate):
    if not delegate:
        return ''
    delegate = delegate.lower()
    if '::' in delegate:
        return delegate
    return f"{self.fileModeName.lower()}::{delegate}"
</t>
<t tx="ekr.20061008055616"></t>
<t tx="ekr.20070427110751"></t>
<t tx="ekr.20070615131954"></t>
<t tx="ekr.20111017085134.16069"></t>
<t tx="ekr.20111017085134.16070"># **** Change these as needed ***
import os, sys

tempFileName = g.os_path_abspath(
    g.os_path_join(g.app.testDir,'jythonScript.py'))

if sys.platform == 'win32':
    jython = r'C:\Jython-21\jython.bat'
    command = '%s "%s"' % (jython,tempFileName) 
else:
    jython = r'cd ~/jython-2.1 ; ./jython'
    command = '%s "%s" &amp;' % (jython,tempFileName)

script = g.getScript(c,p).strip()
if not script:
    s = "no script in %s" % p.h
    g.es(s) ; print s
else:
    f = file(tempFileName,'w')
    f.write(script + '\n')
    f.close()
    print command
    os.system(command)
</t>
<t tx="ekr.20111017085134.16071">import os, sys, threading

python = sys.executable
pythonDir,junk = g.os_path_split(python)
pydoc = g.os_path_join(pythonDir,'Lib','pydoc.py')
command = '%s %s -g' % (python,pydoc)

# Execute the command in a separate thread.
def go():
    os.system(command)
    
threading.Thread(target=go).start()
</t>
<t tx="ekr.20111017085134.16072"># This works, but is too slow to be really useful.

count = 0
for p in c.all_unique_positions():
    if p.isDirty():
        c.checkPythonNode (p,suppressErrors=False)
        count += 1
        
g.es('%s nodes checked' % count)
</t>
<t tx="ekr.20111017085134.16074"># Move selected text of the body down one line.

@others

moveLinesDown(c,p)
</t>
<t tx="ekr.20111017085134.16075">def moveLinesDown(c,p):
    
    undoType = 'Move Lines Down'
    head,lines,tail,oldSel,oldYview = c.getBodyLines()
    if not tail or not lines: return
    tail = g.splitLines(tail)[1:]
    lines = '\n'.join(lines) + '\n'
    lines = g.splitLines(lines)
    # Move the lines down.
    lines.insert(0,tail[0])
    tail = tail[1:]
    if not tail: lines.insert(1,'\n')
    # Convert back to strings.
    tail = ''.join(tail)
    lines = ''.join(lines)
    # Adjust selection.
    sel1,sel2 = oldSel
    row,col = sel1.split('.') ; sel1 = '%d.%s' % (int(row)+1,col)
    row,col = sel2.split('.') ; sel2 = '%d.%s' % (int(row)+1,col)
    newSel = sel1,sel2
    c.updateBodyPane(head,lines,tail,undoType,newSel,oldYview)
</t>
<t tx="ekr.20111017085134.16076"># Move selected text of the body up one line.

@others

moveLinesUp(c,p)
</t>
<t tx="ekr.20111017085134.16077">def moveLinesUp(c,p):
    
    undoType = 'Move Lines Up'
    head,lines,tail,oldSel,oldYview = c.getBodyLines()
    if not head or not lines: return
    # A slight adjustment: lines doesn't end in '\n'.
    head = g.splitLines(head)
    lines = '\n'.join(lines) + '\n'
    lines = g.splitLines(lines)
    # Move the lines up.
    lines.append(head[-1])
    head = head[:-1]
    # Convert back to strings.
    head = ''.join(head)
    lines = ''.join(lines)[:-1] # Undo the adjustment above.
    # Adjust selection.
    sel1,sel2 = oldSel
    row,col = sel1.split('.') ; sel1 = '%d.%s' % (int(row)-1,col)
    row,col = sel2.split('.') ; sel2 = '%d.%s' % (int(row)-1,col)
    newSel = sel1,sel2
    c.updateBodyPane(head,lines,tail,undoType,newSel,oldYview)
</t>
<t tx="ekr.20111017085134.16078">'''Analyze external calls in lisp files.'''

import glob
class lispAnalyzer:
    @others

base = '/media/disk/XEmacs/XEmacs-21.4.13/lisp/*.el'
paths = glob.glob(base)
# g.es(g.listToString(paths))
paths = [paths[0]]

x = lispAnalyzer(c,paths)
x.run()
</t>
<t tx="ekr.20111017085134.16079">def __init__ (self,c,paths):

    self.c = c
    self.paths = paths

    # Debugging.
    self.debug = True
    self.trace = False

    # Semantic info.
    self.def_keywords = ('defun','defvar',)
    self.indent_keywords = ('if','prog','prog1','progn','set',)
    self.expr_keywords = ('and','not','or',)

    self.indent = 0 # Indentation of production output.

    # Dispatch dictionary.
    self.dispatchDict = {}
</t>
<t tx="ekr.20111017085134.16080">def run(self):

    fn = self.paths[0]
    f = open(fn)
    s = f.read()
    g.es_print(fn)
    # g.trace(s)
    s = self.stripStrings(s)
    s = self.stripComments(s)
    defs = self.findDefs(s)
    calls = self.findCalls(s,defs)
    calls = self.stripLispIds(calls)
    defs.sort()
    calls.sort()
    g.trace('defs...\n',g.listToString(defs))
    g.trace('calls...\n',g.listToString(calls))
</t>
<t tx="ekr.20111017085134.16081">def findCalls (self,s,defs):

    calls = []
    i = 0
    while i &lt; len(s):
        progress = i
        ch = s[i]
        if ch.isalnum() or ch == u'_':
            j = self.skipId(s,i)
            theId = s[i:j].strip()
            if theId not in defs and theId not in calls:
                calls.append(theId)
            i = j
        else: i += 1
        assert progress &lt; i

    return calls       
</t>
<t tx="ekr.20111017085134.16082">def findDefs (self,s):

    defs = [] ; tag = '(defun'
    i = 0
    while True:
        progress = i
        j = s.find(tag,i)
        if j == -1:
            return defs
        i = j + len(tag)
        j = g.skip_ws(s,i)
        if j &gt; i:
            k = self.skipId(s,j)
            if k &gt; j:
                defs.append(s[j:k])
            i = k
        assert progress &lt; i 
</t>
<t tx="ekr.20111017085134.16083">def stripComments(self,s):

    result = []
    i = 0
    while i &lt; len(s):
        progress = i
        ch = s[i]
        if ch == ';':
            i = self.skipComment(s,i)
        else:
            result.append(ch)
            i += 1
        assert progress &lt; i

    return ''.join(result)
</t>
<t tx="ekr.20111017085134.16084">def stripLispIds (self,aList):

    lispIds = [
        't','f',
        'apply',
        'boundp',
        'car','cons','cdr','consp',
        'eq','equal','eval',
        'lamda','lambda',
        'macro',
        'nil','not','null','numberp',
        'or',
        'unless',
        'vector','vectorp',
        # special forms
        'and',
        'catch',
        'cond',
        'condition-case',
        'defconst',
        'defmacro',
        'defun',
        'defvar',
        'function',
        'if',
        'interactive',
        'let',
        'let*',
        'or',
        'prog1',
        'prog2',
        'progn',
        'quote',
        'save-current-buffer',
        'save-excursion',
        'save-restriction',
        'save-window-excursion',
        'setq',
        'setq-default',
        'track-mouse',
        'unwind-protect',
        'while',
        'with-output-to-temp-buffer',
    ]

    return [z for z in aList if z not in lispIds]
</t>
<t tx="ekr.20111017085134.16085">def stripStrings(self,s):

    result = []
    i = 0
    while i &lt; len(s):
        progress = i
        ch = s[i]
        if ch == '"':
            i = self.skipString(s,i)
        elif ch == "'":
            i = self.skipId(s,i+1)
        else:
            result.append(ch)
            i += 1
        assert progress &lt; i

    return ''.join(result)
</t>
<t tx="ekr.20111017085134.16086">class token:

    '''Representing one elisp syntactic entity,
    with a list of preceding comments.'''

    def __init__ (self,comments,kind,val):
        self.comments = comments[:]
        self.kind = kind
        self.val = val
            # For blocks, a list of tokens.
            # For all other tokens, the spelling of the token.

    def __repr__ (self):
        return '&lt;token kind: %s, val: %s&gt;' % (self.kind,self.val)

    def __str__ (self):
        if self.kind == 'block:':
            return 'block: [snip]'
        elif self.kind == 'string:':
            return '%s%s' % (self.kind,self.val[:20])
        else:
            return '%s%s' % (self.kind,self.val)
</t>
<t tx="ekr.20111017085134.16087">def choose(self,cond,a,b): # warning: evaluates all arguments

    if cond: return a
    else: return b
</t>
<t tx="ekr.20111017085134.16088">def dumpList(self,aList):

    if type(aList) == type([]):
        result = self.dumpListHelper(aList,indent=0)
        return '\n'.join(result)
    else:
        return repr(aList)

def dumpListHelper(self,aList,indent):

    result = []
    leading = ' ' * (4 * indent)

    for z in aList:
        if z is None:
            result.append('%s%s' % (leading,'None'))
        elif z == []:
            result.append('%s%s' % (leading,'[]'))
        elif type(z) == type([]):
            result.append('%s%s' % (leading,'['))
            result.extend(self.dumpListHelper(z,indent+1))
            result.append('%s%s' % (leading,']'))
        elif isinstance(z,self.token):
            if z.kind=='block:':
                result.append('%s%s' % (leading,'block:'))
                result.extend(self.dumpListHelper(z.val,indent+1))
                # result.append('%s%s' % (leading,'block:]'))
            else:
                result.append('%s%s' % (leading,str(z)))
        else:
            result.append('%s%s' % (leading,str(z)))

    return result

listToString = dumpList
</t>
<t tx="ekr.20111017085134.16089"></t>
<t tx="ekr.20111017085134.16090">def parse(self,s):

    # Generate the nodes, including directive and section references.
    return self.scanForest(s)
</t>
<t tx="ekr.20111017085134.16091">def scan(self,s,i):

    '''Scan an elisp expression.'''

    start = i ; end = len(s) ; result = []
    comments = [] ; token = self.token
    # A hack. ignore initial @language lisp
    tag = '@language lisp'
    if i == 0 and s[i:i+len(tag)]==tag:
        i += len(tag)
    while i &lt; end:
        progress = i
        ch = s[i]
        if ch == ';':
            j = self.skipComment(s,i)
            start = i = j
        elif ch == '"':
            j = self.skipString(s,i)
            start = i = j
        elif ch.isalnum() or ch == u'_':
            j = self.skipId(s,i)
            result.append(token(comments,'id:',s[i:j]))
            start = i = j
        elif ch =='(':
            i += 1
            j,aList = self.scan(s,i)
            result.append(token(comments,'block:',aList))
            start = i = j
        elif ch == ')':
            i += 1
            return i,result
        else:
            # if ch == "'": ch = 'quote'
            # if ch not in (' ','\t','\n','\r'):
                # result.append(token(comments,'op:',ch))
            i += 1
        assert progress &lt; i,'i: %d, ch: %s' % (i,repr(s[i]))

    if start &lt; end:
        tail = s[start:end].strip()
        if tail:
            result.append(token(comments,'tail:',tail))
            comment = []
    if comments:
        result.append(token(comments,'trailing-comment:',''))

    return i,result
</t>
<t tx="ekr.20111017085134.16092">def skipComment (self,s,i):

    '''Skip a comment.'''

    while i &lt; len(s):
        if s[i] == '\n':
            break
        i += 1
    return i
</t>
<t tx="ekr.20111017085134.16093">def skipId (self,s,i):

    while i &lt; len(s):
        ch = s[i]
        if ch.isalnum() or ch in ('_','-'):
            i += 1
        else:
            break
    return i
</t>
<t tx="ekr.20111017085134.16094">def skipString(self,s,i):

    """Skip a string literal."""

    assert(s[i] == '"')
    i += 1
    while i &lt; len(s):
        ch = s[i]
        if ch == '\\' : i += 2
        elif ch == '"':
            i += 1 ; break
        else: i += 1

    return i
</t>
<t tx="ekr.20111017085134.16095">def scanForest (self,s):

    i = 0 ; result = []

    while i &lt; len(s):
        progress = i
        i,aList = self.scan(s,i)
        aList and result.extend(aList)
        assert i &gt; progress

    return result


</t>
<t tx="ekr.20111017085134.16096">@ From Richard Deibenkorn:

1. Attempt what is not certain. Certainty may or may not come later. It may then
be a valuable delusion.

2. The pretty, initial position which falls short of completeness is not to be
valued--except as stimulus for further moves.

3. Do search.  But in order to find other than what is looked for.

4. Use and respond to the initial fresh qualities but consider them absolutely
expendable.
</t>
<t tx="ekr.20111017085134.16097">def gen(self,tokens,indent,init=False):

    result = []

    if init: result.append('='*40)

    for token in tokens:
        aList = self.gen_token(token,indent)
        result.extend(aList)

    if init: result.append('-'*40)

    return result

</t>
<t tx="ekr.20111017085134.16098">def gen_token(self,token,indent):

    result = []

    if self.debug:
        if token.kind == 'block:':
            aList = self.gen_block(token,indent)
            result.extend(aList)
        else:
            self.put_token(token,indent,result)
    else:
        if token.kind == 'block:':
            self.gen_block(token,indent)
        else:
            self.put_code_token(token)

    return result
</t>
<t tx="ekr.20111017085134.16099">def gen_block (self,token,indent):

    if not (token and token.val):
        return []

    blockList = token.val
    token2 = blockList[0]
    result = []

    if token2.kind.startswith('id'):
        aList = self.gen_block_id(token2.val,blockList,indent)
    else:
        if self.debug:
            self.put('block...',[],indent,result)
        aList = self.gen(token.val,indent+1)

    result.extend(aList)
    return result
</t>
<t tx="ekr.20111017085134.16100">def gen_block_id (self,theId,tokens,indent):

    result = []

    # Eventually there will be a lookup of the dispatch dict here.
    if theId == 'let':
        aList = self.gen_let(tokens,indent)
    elif theId == 'if':
        aList = self.gen_if(tokens,indent)
    elif theId in self.def_keywords:
        aList = self.gen_def(theId,tokens,indent)
    elif theId in self.indent_keywords:
        self.put('%s...' % (theId),[],indent,result)
        aList = self.gen(tokens[1:],indent+1)
    elif theId in self.expr_keywords:
        aList = self.gen_expr(theId,tokens[1:],indent+1)
    else:
        aList = self.gen_call(theId,tokens[1:],indent)

    result.extend(aList)
    return result
</t>
<t tx="ekr.20111017085134.16101">def gen_call (self,funcId,tokens,indent):

    result = []

    if self.debug:
        self.put('call: %s' % (funcId),[],indent,result)
        for token in tokens:
            aList = self.gen_arg(token,indent+1)
            result.extend(aList)
    else:
        self.put_code_line('%s(' % (funcId))
        for token in tokens:
            self.gen_arg(token,indent+1)
        self.put_code(')')

    return result

</t>
<t tx="ekr.20111017085134.16102">def gen_arg(self,token,indent):

    result = []

    if self.debug:
        if token.kind == 'block:':
            self.put('arg block:...',[],indent,result)
            aList = self.gen_block(token,indent)
            result.extend(aList)
        else:
            self.put_token(token,indent,result)
    else:
        if token.kind == 'block:':
            aList = self.gen_block(token,indent)
            self.put_code(''.join(aList))
        else:
            self.put_code_token(token)

    return result
</t>
<t tx="ekr.20111017085134.16103">def gen_def(self,theId,tokens,indent):

    result = []

    if not tokens or len(tokens) &lt; 3:
        result.append('*** bad def tokens')
        return result

    defToken,idToken = tokens[0:2]
    if idToken.kind != 'id:':
        result.append('*** bad def id')
        return result

    if self.debug:
        self.put(theId,idToken.val,indent,result)
        aList = self.gen(tokens[2:],indent+1)
        result.extend(aList)
    else:
        self.put_code('def %s (' % idToken.val)
        self.gen_token(tokens[2],indent)
        self.put_code('): # end def\n')
        self.indent += 1
        self.gen(tokens[3:],indent+1)
        self.indent -= 1

    return result
</t>
<t tx="ekr.20111017085134.16104">@ if condition then-form else-forms.

If the evaluated condition is non-nil, then-form is evaluated and the result
returned. Otherwise, the else-forms are evaluated in textual order, and the
value of the last one is returned. If condition has the value nil, and no
else-forms are given, if returns nil.
@c

def gen_if (self,tokens,indent):

    # tokens[0]: id:if
    # tokens[1] cond

    for i in xrange(len(tokens)):
        g.trace('tokens[%d]: %s' % (i,self.dumpList(tokens[i])))

    # g.trace(self.dumpList(tokens))

    result = []

    if self.debug:
        self.put('if...',[],indent,result)
        aList = self.gen(tokens[1:],indent+1)
        result.extend(aList)
    else:
        self.put_code('if ')
        self.gen(tokens[1:],indent+1)
        self.put_code(': # end if\n')

    return result
</t>
<t tx="ekr.20111017085134.16105">def gen_then (self,token):

    pass
</t>
<t tx="ekr.20111017085134.16106">def gen_expr (self,theId,aList,indent):

    binops = ('and','or',)
    result = []

    self.put(theId,[],indent,result)
    aList = self.gen(aList,indent+1)

    result.extend(aList)
    return result
</t>
<t tx="ekr.20111017085134.16107">@
(let ((variable value)
      (variable value)
      ...)
  body...)
@c

def gen_let (self,tokens,indent):

    if not tokens: return []
    if len(tokens) != 3:
        g.trace('unusual let')
        result = self.gen(tokens,indent+1)
        return result

    if 0:
        for i in xrange(len(tokens)):
            g.trace('token',i,tokens[i])

    letToken,bindingToken,bodyToken = tokens
    result = []
    self.put('let...',[],indent,result)
    self.put('let-bindings...',[],indent+1,result)
    aList = self.gen_let_bindings(bindingToken,indent+2)
    result.extend(aList)
    self.put('let-block...',[],indent+1,result)
    aList = self.gen_block(bodyToken,indent+2)
    result.extend(aList)
    return result
</t>
<t tx="ekr.20111017085134.16108">def gen_let_bindings (self,token,indent):

    result = []

    if token.kind != 'block:':
        g.trace('unexpected let')
        return result

    for z in token.val:
        if z.kind == 'block:': # one (id,val) pair
            if z.val and len(z.val) == 2:
                token1 = z.val[0]
                token2 = z.val[1]
                self.put('let-id',token1,indent,result)
                self.put('let-val...',[],indent,result)
                if token2.kind == 'block:':
                    aList = self.gen_block(token2,indent+1)
                    result.extend(aList)
                else:
                    #g.trace('no let list')
                    self.put_token(token2,indent+1,result)
            else:
                g.trace('unexpected let 2')

    return result
</t>
<t tx="ekr.20111017085134.16109"></t>
<t tx="ekr.20111017085134.16110">def put (self,kind,val,indent,result):

    '''Append one or more lines of output to result.'''

    leading = '%2d: %s' % (indent,' ' * indent)

    if kind == 'string:':
        val = self.choose(len(val)&gt;20,val[:20]+'..."',val)

    if val:
        s = '%s%s %s' % (leading,str(kind),str(val))
    else:
        s = '%s%s' % (leading,str(kind))

    result.append(s)
</t>
<t tx="ekr.20111017085134.16111">def put_token (self,token,indent,result):

    for z in token.comments:
        self.put('comment:',z,indent,result)

    self.put(token.kind,token.val,indent,result)
</t>
<t tx="ekr.20111017085134.16112">def put_code_line (self,s):

    s2 = '%s%s' % (' '*self.indent,s)
    print s2,

def put_code (self,s):

    print s,
</t>
<t tx="ekr.20111017085134.16113">def put_code_token (self,token):

    if token.kind == 'block:':
        self.put_code('&lt;block&gt;')
    else:
        self.put_code(token.val)

</t>
<t tx="ekr.20111017085134.16114">result = []
firstLevel = p.level()

for p in p.self_and_subtree_iter():
    head = p.moreHead(firstLevel)
    head = g.toEncodedString(head,'ascii',reportErrors=True)
    result.append(head)
    body = p.moreBody() # Inserts escapes for '+' and '-' characters.
    if body:
        body = g.toEncodedString(body,'ascii',reportErrors=True)
        result.append(body)

result = '\n'.join(result) + '\n'
# print result
w = c.frame.body.bodyCtrl
w.clipboard_clear()
w.clipboard_append(result)
</t>
<t tx="ekr.20141116105945.2"></t>
<t tx="ekr.20141116105945.3">gnx: maphew.20130809155103.2862
</t>
<t tx="ekr.20141116105945.4"></t>
<t tx="ekr.20150302053516.3"></t>
<t tx="ekr.20150508132450.1">g.cls()

class CreateDecorators:
    '''
    A class to create decorators from tables in getPublicCommands.
    This class uses a node called "Found: getPublicCommands".
    
    This @button node must be executed in LeoPy.leo.
    '''
    def __init__(self,c):
        self.c = c
        self.fixups = self.create_fixups()
        self.n = 0
        self.n_fail = 0
    @others

CreateDecorators(c).run()
</t>
<t tx="ekr.20150508132450.2">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.20150508132450.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.
    '''
    # g.trace('%45s %s' % (c_name,f_name))
    found = False
    for p in root.self_and_subtree():
        result = []
        for s in g.splitLines(p.b):
            if g.match_word(s,0,'def ' + f_name):
                if found:
                    g.trace('duplicate def',f_name)
                else:
                    found = True
                    decorator = '@cmd(%s)\n' % (c_name)
                    result.append(decorator)
                    # print('%s%s' % (decorator,s))
            result.append(s)
    return found
</t>
<t tx="ekr.20150508132450.4">def create_decorators(self,d,root):
    '''Create decorators for all items in d in root's tree.'''
    print('\n%s\n' % 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:
            g.trace('not found',f_name)
            self.n_fail += 1
</t>
<t tx="ekr.20150508132450.5">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.20150508132450.6">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.20150508132450.7">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.20150508132450.8">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.20150508132450.9">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.20150525161132.1">r'''The script to test AddTokensToTree class.'''
g.cls()
if c.isChanged():
    c.save()
&lt;&lt; imports &gt;&gt;
project = False
if project:
    aList = leoBeautify.ProjectUtils().project_files('leo')
    aList = ['@file %s' % (g.shortFileName(z).rstrip()) for z in aList]
    aList = aList
    settings_d = {}
else:
    aList = [
        'unit test: leoBeautify.py',
        # '@file leoFileCommands.py',
        # '@file leoBeautify.py',
    ]
    settings_d = {
        'ast_tokens_d': False,
        'stats': True,
        'input_string': True,
        'input_lines': False,
        'input_tokens': False,
        'token_list': False,
        'code_list': False,
        'output_string': True,
    }
assert aList,'no input in %s' % (p.h)
t1 = time.clock()
for h in aList:
    p2 = g.findNodeAnywhere(c,h)
    if p2:
        try:
            leoBeautify.test_LeoTidy(c,h,p2,settings_d)
        except Exception:
            g.es_exception()
if project:
    print('done: %4.2f sec.' % (time.clock()-t1))
</t>
<t tx="ekr.20150525161132.2"># import leo.external.PythonTidy as PythonTidy
import leo.core.leoAst as leoAst
import leo.core.leoBeautify as leoBeautify
import imp
import time
imp.reload(leoAst)
imp.reload(leoBeautify)
# imp.reload(PythonTidy)
</t>
<t tx="ekr.20160123185308.1">'''
Make a stub file in the ~/home directory for every @&lt;file&gt; node in the
selected tree.
'''
import ast
import os
import textwrap
import leo.core.leoGlobals as g
@others
MakeStubFile(c).run(c.p)
</t>
<t tx="ekr.20160123185308.10">def format(self, node):
    '''Format the node (or list of nodes) and its descendants.'''
    self.level = 0
    val = self.visit(node)
    return val and val.strip() or ''
</t>
<t tx="ekr.20160123185308.11">def visit(self, node):
    '''Return the formatted version of an Ast node, or list of Ast nodes.'''
    if isinstance(node, (list, tuple)):
        return ','.join([self.visit(z) for z in node])
    elif node is None:
        return 'None'
    else:
        assert isinstance(node, ast.AST), node.__class__.__name__
        method_name = 'do_' + node.__class__.__name__
        method = getattr(self, method_name)
        s = method(node)
        # pylint: disable=unidiomatic-typecheck
        assert type(s) == type('abc'), type(s)
        return s
</t>
<t tx="ekr.20160123185308.12"></t>
<t tx="ekr.20160123185308.13"># ClassDef(identifier name, expr* bases, stmt* body, expr* decorator_list)

def do_ClassDef(self, node):
    result = []
    name = node.name # Only a plain string is valid.
    bases = [self.visit(z) for z in node.bases] if node.bases else []
    if bases:
        result.append(self.indent('class %s(%s):\n' % (name, ','.join(bases))))
    else:
        result.append(self.indent('class %s:\n' % name))
    for z in node.body:
        self.level += 1
        result.append(self.visit(z))
        self.level -= 1
    return ''.join(result)
</t>
<t tx="ekr.20160123185308.14"># FunctionDef(identifier name, arguments args, stmt* body, expr* decorator_list)

def do_FunctionDef(self, node):
    '''Format a FunctionDef node.'''
    result = []
    if node.decorator_list:
        for z in node.decorator_list:
            result.append('@%s\n' % self.visit(z))
    name = node.name # Only a plain string is valid.
    args = self.visit(node.args) if node.args else ''
    result.append(self.indent('def %s(%s):\n' % (name, args)))
    for z in node.body:
        self.level += 1
        result.append(self.visit(z))
        self.level -= 1
    return ''.join(result)
</t>
<t tx="ekr.20160123185308.15">def do_Interactive(self, node):
    for z in node.body:
        self.visit(z)
</t>
<t tx="ekr.20160123185308.16">def do_Module(self, node):
    assert 'body' in node._fields
    result = ''.join([self.visit(z) for z in node.body])
    return result # 'module:\n%s' % (result)
</t>
<t tx="ekr.20160123185308.17">def do_Lambda(self, node):
    return self.indent('lambda %s: %s' % (
        self.visit(node.args),
        self.visit(node.body)))
</t>
<t tx="ekr.20160123185308.18"></t>
<t tx="ekr.20160123185308.19">def do_Expr(self, node):
    '''An outer expression: must be indented.'''
    return self.indent('%s\n' % self.visit(node.value))
</t>
<t tx="ekr.20160123185308.2">class MakeStubFile:
    '''A class to make Python stub (.pyi) files.'''
    @others
</t>
<t tx="ekr.20160123185308.20">def do_Expression(self, node):
    '''An inner expression: do not indent.'''
    return '%s\n' % self.visit(node.body)
</t>
<t tx="ekr.20160123185308.21">def do_GeneratorExp(self, node):
    elt = self.visit(node.elt) or ''
    gens = [self.visit(z) for z in node.generators]
    gens = [z if z else '&lt;**None**&gt;' for z in gens] ### Kludge: probable bug.
    return '&lt;gen %s for %s&gt;' % (elt, ','.join(gens))
</t>
<t tx="ekr.20160123185308.22">def do_AugLoad(self, node):
    return 'AugLoad'

def do_Del(self, node):
    return 'Del'

def do_Load(self, node):
    return 'Load'

def do_Param(self, node):
    return 'Param'

def do_Store(self, node):
    return 'Store'
</t>
<t tx="ekr.20160123185308.23"></t>
<t tx="ekr.20160123185308.24"># arguments = (expr* args, identifier? vararg, identifier? kwarg, expr* defaults)

def do_arguments(self, node):
    '''Format the arguments node.'''
    kind = self.kind(node)
    assert kind == 'arguments', kind
    args = [self.visit(z) for z in node.args]
    defaults = [self.visit(z) for z in node.defaults]
    # Assign default values to the last args.
    args2 = []
    n_plain = len(args) - len(defaults)
    for i in range(len(args)):
        if i &lt; n_plain:
            args2.append(args[i])
        else:
            args2.append('%s=%s' % (args[i], defaults[i - n_plain]))
    # Now add the vararg and kwarg args.
    name = getattr(node, 'vararg', None)
    if name: args2.append('*' + name)
    name = getattr(node, 'kwarg', None)
    if name: args2.append('**' + name)
    return ','.join(args2)
</t>
<t tx="ekr.20160123185308.25"># Python 3:
# arg = (identifier arg, expr? annotation)

def do_arg(self, node):
    if node.annotation:
        return self.visit(node.annotation)
    else:
        return ''
</t>
<t tx="ekr.20160123185308.26"># Attribute(expr value, identifier attr, expr_context ctx)

def do_Attribute(self, node):
    return '%s.%s' % (
        self.visit(node.value),
        node.attr) # Don't visit node.attr: it is always a string.
</t>
<t tx="ekr.20160123185308.27">def do_Bytes(self, node): # Python 3.x only.
    return str(node.s)
</t>
<t tx="ekr.20160123185308.28"># Call(expr func, expr* args, keyword* keywords, expr? starargs, expr? kwargs)

def do_Call(self, node):
    # g.trace(node,Utils().dump_ast(node))
    func = self.visit(node.func)
    args = [self.visit(z) for z in node.args]
    for z in node.keywords:
        # Calls f.do_keyword.
        args.append(self.visit(z))
    if getattr(node, 'starargs', None):
        args.append('*%s' % (self.visit(node.starargs)))
    if getattr(node, 'kwargs', None):
        args.append('**%s' % (self.visit(node.kwargs)))
    args = [z for z in args if z] # Kludge: Defensive coding.
    return '%s(%s)' % (func, ','.join(args))
</t>
<t tx="ekr.20160123185308.29"># keyword = (identifier arg, expr value)

def do_keyword(self, node):
    # node.arg is a string.
    value = self.visit(node.value)
    # This is a keyword *arg*, not a Python keyword!
    return '%s=%s' % (node.arg, value)
</t>
<t tx="ekr.20160123185308.3">def __init__(self, c):
    self.c = c
    self.d = self.scan_types_data(c) or self.make_types_dict(c)
        # Keys are strings, values are Type objects.
</t>
<t tx="ekr.20160123185308.30">def do_comprehension(self, node):
    result = []
    name = self.visit(node.target) # A name.
    it = self.visit(node.iter) # An attribute.
    result.append('%s in %s' % (name, it))
    ifs = [self.visit(z) for z in node.ifs]
    if ifs:
        result.append(' if %s' % (''.join(ifs)))
    return ''.join(result)
</t>
<t tx="ekr.20160123185308.31">def do_Dict(self, node):
    result = []
    keys = [self.visit(z) for z in node.keys]
    values = [self.visit(z) for z in node.values]
    if len(keys) == len(values):
        result.append('{\n' if keys else '{')
        items = []
        for i in range(len(keys)):
            items.append('  %s:%s' % (keys[i], values[i]))
        result.append(',\n'.join(items))
        result.append('\n}' if keys else '}')
    else:
        print('Error: f.Dict: len(keys) != len(values)\nkeys: %s\nvals: %s' % (
            repr(keys), repr(values)))
    return ''.join(result)
</t>
<t tx="ekr.20160123185308.32">def do_Ellipsis(self, node):
    return '...'
</t>
<t tx="ekr.20160123185308.33">def do_ExtSlice(self, node):
    return ':'.join([self.visit(z) for z in node.dims])
</t>
<t tx="ekr.20160123185308.34">def do_Index(self, node):
    return self.visit(node.value)
</t>
<t tx="ekr.20160123185308.35">def do_List(self, node):
    # Not used: list context.
    # self.visit(node.ctx)
    elts = [self.visit(z) for z in node.elts]
    elst = [z for z in elts if z] # Defensive.
    return '[%s]' % ','.join(elts)
</t>
<t tx="ekr.20160123185308.36">def do_ListComp(self, node):
    elt = self.visit(node.elt)
    gens = [self.visit(z) for z in node.generators]
    gens = [z if z else '&lt;**None**&gt;' for z in gens] ### Kludge: probable bug.
    return '%s for %s' % (elt, ''.join(gens))
</t>
<t tx="ekr.20160123185308.37">def do_Name(self, node):
    return node.id
</t>
<t tx="ekr.20160123185308.38">def do_Num(self, node):
    return repr(node.n)
</t>
<t tx="ekr.20160123185308.39"># Python 2.x only

def do_Repr(self, node):
    return 'repr(%s)' % self.visit(node.value)
</t>
<t tx="ekr.20160123185308.4">def make_types_dict(self, c):
    '''Return a dict whose keys are names and values are type specs.'''
    return {
        'aList': 'Sequence',
        'aList2': 'Sequence',
        'c': 'Commander',
        'i': 'int',
        'j': 'int',
        'k': 'int',
        'node': 'ast.Ast',
        'p': 'Position',
        's': 'str',
        's2': 'str',
        'v': 'VNode',
    }
</t>
<t tx="ekr.20160123185308.40">def do_Slice(self, node):
    lower, upper, step = '', '', ''
    if getattr(node, 'lower', None) is not None:
        lower = self.visit(node.lower)
    if getattr(node, 'upper', None) is not None:
        upper = self.visit(node.upper)
    if getattr(node, 'step', None) is not None:
        step = self.visit(node.step)
    if step:
        return '%s:%s:%s' % (lower, upper, step)
    else:
        return '%s:%s' % (lower, upper)
</t>
<t tx="ekr.20160123185308.41">def do_Str(self, node):
    '''This represents a string constant.'''
    return repr(node.s)
</t>
<t tx="ekr.20160123185308.42"># Subscript(expr value, slice slice, expr_context ctx)

def do_Subscript(self, node):
    value = self.visit(node.value)
    the_slice = self.visit(node.slice)
    return '%s[%s]' % (value, the_slice)
</t>
<t tx="ekr.20160123185308.43">def do_Tuple(self, node):
    elts = [self.visit(z) for z in node.elts]
    return '(%s)' % ','.join(elts)
</t>
<t tx="ekr.20160123185308.44"></t>
<t tx="ekr.20160123185308.45">def do_BinOp(self, node):
    return '%s%s%s' % (
        self.visit(node.left),
        self.op_name(node.op),
        self.visit(node.right))
</t>
<t tx="ekr.20160123185308.46">def do_BoolOp(self, node):
    op_name = self.op_name(node.op)
    values = [self.visit(z) for z in node.values]
    return op_name.join(values)
</t>
<t tx="ekr.20160123185308.47">def do_Compare(self, node):
    result = []
    lt = self.visit(node.left)
    # ops   = [self.visit(z) for z in node.ops]
    ops = [self.op_name(z) for z in node.ops]
    comps = [self.visit(z) for z in node.comparators]
    result.append(lt)
    if len(ops) == len(comps):
        for i in range(len(ops)):
            result.append('%s%s' % (ops[i], comps[i]))
    else:
        g.trace('ops', repr(ops), 'comparators', repr(comps))
    return ''.join(result)
</t>
<t tx="ekr.20160123185308.48">def do_UnaryOp(self, node):
    return '%s%s' % (
        self.op_name(node.op),
        self.visit(node.operand))
</t>
<t tx="ekr.20160123185308.49">def do_IfExp(self, node):
    return '%s if %s else %s ' % (
        self.visit(node.body),
        self.visit(node.test),
        self.visit(node.orelse))
</t>
<t tx="ekr.20160123185308.5">def scan_types_data(self, c):
    '''Create self.d from @data stub-types nodes.'''
    aList = c.config.getData(
        'stub-types',
        strip_comments=True,
        strip_data=True)
    d = {}
    for s in aList:
        name, value = s.split(None,1)
        d[name.strip()] = value.strip()
    if False:
        for key in sorted(d.keys()):
            g.trace(key, d.get(key))
    return d
</t>
<t tx="ekr.20160123185308.50"></t>
<t tx="ekr.20160123185308.51">def do_Assert(self, node):
    test = self.visit(node.test)
    if getattr(node, 'msg', None):
        message = self.visit(node.msg)
        return self.indent('assert %s, %s' % (test, message))
    else:
        return self.indent('assert %s' % test)
</t>
<t tx="ekr.20160123185308.52">def do_Assign(self, node):
    return self.indent('%s=%s\n' % (
        '='.join([self.visit(z) for z in node.targets]),
        self.visit(node.value)))
</t>
<t tx="ekr.20160123185308.53">def do_AugAssign(self, node):
    return self.indent('%s%s=%s\n' % (
        self.visit(node.target),
        self.op_name(node.op), # Bug fix: 2013/03/08.
        self.visit(node.value)))
</t>
<t tx="ekr.20160123185308.54">def do_Break(self, node):
    return self.indent('break\n')
</t>
<t tx="ekr.20160123185308.55">def do_Continue(self, node):
    return self.indent('continue\n')
</t>
<t tx="ekr.20160123185308.56">def do_Delete(self, node):
    targets = [self.visit(z) for z in node.targets]
    return self.indent('del %s\n' % ','.join(targets))
</t>
<t tx="ekr.20160123185308.57">def do_ExceptHandler(self, node):
    result = []
    result.append(self.indent('except'))
    if getattr(node, 'type', None):
        result.append(' %s' % self.visit(node.type))
    if getattr(node, 'name', None):
        if isinstance(node.name, ast.AST):
            result.append(' as %s' % self.visit(node.name))
        else:
            result.append(' as %s' % node.name) # Python 3.x.
    result.append(':\n')
    for z in node.body:
        self.level += 1
        result.append(self.visit(z))
        self.level -= 1
    return ''.join(result)
</t>
<t tx="ekr.20160123185308.58"># Python 2.x only

def do_Exec(self, node):
    body = self.visit(node.body)
    args = [] # Globals before locals.
    if getattr(node, 'globals', None):
        args.append(self.visit(node.globals))
    if getattr(node, 'locals', None):
        args.append(self.visit(node.locals))
    if args:
        return self.indent('exec %s in %s\n' % (
            body, ','.join(args)))
    else:
        return self.indent('exec %s\n' % (body))
</t>
<t tx="ekr.20160123185308.59">def do_For(self, node):
    result = []
    result.append(self.indent('for %s in %s:\n' % (
        self.visit(node.target),
        self.visit(node.iter))))
    for z in node.body:
        self.level += 1
        result.append(self.visit(z))
        self.level -= 1
    if node.orelse:
        result.append(self.indent('else:\n'))
        for z in node.orelse:
            self.level += 1
            result.append(self.visit(z))
            self.level -= 1
    return ''.join(result)
</t>
<t tx="ekr.20160123185308.6">def make_stub_file(self, p):
    '''Make a stub file in ~/stubs for the @&lt;file&gt; node at p.'''
    import ast
    import leo.core.leoAst as leoAst
    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.os_path_finalize_join(g.app.loadDir, fn)
    abs_fn = g.fullPath(c, p)
    if not g.os_path_exists(abs_fn):
        g.es_print('not found', abs_fn)
        return
    stubs = g.os_path_finalize(g.os_path_expanduser('~/stubs'))
    if g.os_path_exists(stubs):
        base_fn = g.os_path_basename(fn)
        out_fn = g.os_path_finalize_join(stubs,base_fn)
    else:
        g.es_print('not found', stubs)
        return
        # out_fn = g.os_path_finalize_join(g.app.loadDir, fn)
    out_fn = out_fn[:-3] + '.pyi'
    s = open(abs_fn).read()
    node = ast.parse(s,filename=fn,mode='exec')
    leoAst.StubTraverser(self.c, self.d, out_fn).run(node)
</t>
<t tx="ekr.20160123185308.60">def do_Global(self, node):
    return self.indent('global %s\n' % (
        ','.join(node.names)))
</t>
<t tx="ekr.20160123185308.61">def do_If(self, node):
    result = []
    result.append(self.indent('if %s:\n' % (
        self.visit(node.test))))
    for z in node.body:
        self.level += 1
        result.append(self.visit(z))
        self.level -= 1
    if node.orelse:
        result.append(self.indent('else:\n'))
        for z in node.orelse:
            self.level += 1
            result.append(self.visit(z))
            self.level -= 1
    return ''.join(result)
</t>
<t tx="ekr.20160123185308.62">def do_Import(self, node):
    names = []
    for fn, asname in self.get_import_names(node):
        if asname:
            names.append('%s as %s' % (fn, asname))
        else:
            names.append(fn)
    return self.indent('import %s\n' % (
        ','.join(names)))
</t>
<t tx="ekr.20160123185308.63">def get_import_names(self, node):
    '''Return a list of the the full file names in the import statement.'''
    result = []
    for ast2 in node.names:
        if self.kind(ast2) == 'alias':
            data = ast2.name, ast2.asname
            result.append(data)
        else:
            g.trace('unsupported kind in Import.names list', self.kind(ast2))
    return result
</t>
<t tx="ekr.20160123185308.64">def do_ImportFrom(self, node):
    names = []
    for fn, asname in self.get_import_names(node):
        if asname:
            names.append('%s as %s' % (fn, asname))
        else:
            names.append(fn)
    return self.indent('from %s import %s\n' % (
        node.module,
        ','.join(names)))
</t>
<t tx="ekr.20160123185308.65">def do_Pass(self, node):
    return self.indent('pass\n')
</t>
<t tx="ekr.20160123185308.66"># Python 2.x only

def do_Print(self, node):
    vals = []
    for z in node.values:
        vals.append(self.visit(z))
    if getattr(node, 'dest', None):
        vals.append('dest=%s' % self.visit(node.dest))
    if getattr(node, 'nl', None):
        # vals.append('nl=%s' % self.visit(node.nl))
        vals.append('nl=%s' % node.nl)
    return self.indent('print(%s)\n' % (
        ','.join(vals)))
</t>
<t tx="ekr.20160123185308.67">def do_Raise(self, node):
    args = []
    for attr in ('type', 'inst', 'tback'):
        if getattr(node, attr, None) is not None:
            args.append(self.visit(getattr(node, attr)))
    if args:
        return self.indent('raise %s\n' % (
            ','.join(args)))
    else:
        return self.indent('raise\n')
</t>
<t tx="ekr.20160123185308.68">def do_Return(self, node):
    if node.value:
        return self.indent('return %s\n' % (
            self.visit(node.value)))
    else:
        return self.indent('return\n')
</t>
<t tx="ekr.20160123185308.69"># def do_Suite(self,node):
    # for z in node.body:
        # s = self.visit(z)
</t>
<t tx="ekr.20160123185308.7">def run(self, p):
    '''Make stub files for all files in p's tree.'''
    if p.isAnyAtFileNode():
        self.make_stub_file(p)
        return
    # First, look down tree.
    after, p2 = p.nodeAfterTree(), p.firstChild()
    found = False
    while p2 and p != after:
        if p2.isAnyAtFileNode():
            self.make_stub_file(p2)
            p2.moveToNext()
            found = True
        else:
            p2.moveToThreadNext()
    if not found:
        # Look up the tree.
        for p2 in p.parents():
            if p2.isAnyAtFileNode():
                self.make_stub_file(p2)
                break
        else:
            g.es('no files found in tree:', p.h)
</t>
<t tx="ekr.20160123185308.70">def do_TryExcept(self, node):
    result = []
    result.append(self.indent('try:\n'))
    for z in node.body:
        self.level += 1
        result.append(self.visit(z))
        self.level -= 1
    if node.handlers:
        for z in node.handlers:
            result.append(self.visit(z))
    if node.orelse:
        result.append('else:\n')
        for z in node.orelse:
            self.level += 1
            result.append(self.visit(z))
            self.level -= 1
    return ''.join(result)
</t>
<t tx="ekr.20160123185308.71">def do_TryFinally(self, node):
    result = []
    result.append(self.indent('try:\n'))
    for z in node.body:
        self.level += 1
        result.append(self.visit(z))
        self.level -= 1
    result.append(self.indent('finally:\n'))
    for z in node.finalbody:
        self.level += 1
        result.append(self.visit(z))
        self.level -= 1
    return ''.join(result)
</t>
<t tx="ekr.20160123185308.72">def do_While(self, node):
    result = []
    result.append(self.indent('while %s:\n' % (
        self.visit(node.test))))
    for z in node.body:
        self.level += 1
        result.append(self.visit(z))
        self.level -= 1
    if node.orelse:
        result.append('else:\n')
        for z in node.orelse:
            self.level += 1
            result.append(self.visit(z))
            self.level -= 1
    return ''.join(result)
</t>
<t tx="ekr.20160123185308.73">def do_With(self, node):
    result = []
    result.append(self.indent('with '))
    if hasattr(node, 'context_expression'):
        result.append(self.visit(node.context_expresssion))
    vars_list = []
    if hasattr(node, 'optional_vars'):
        try:
            for z in node.optional_vars:
                vars_list.append(self.visit(z))
        except TypeError: # Not iterable.
            vars_list.append(self.visit(node.optional_vars))
    result.append(','.join(vars_list))
    result.append(':\n')
    for z in node.body:
        self.level += 1
        result.append(self.visit(z))
        self.level -= 1
    result.append('\n')
    return ''.join(result)
</t>
<t tx="ekr.20160123185308.74">def do_Yield(self, node):
    if getattr(node, 'value', None):
        return self.indent('yield %s\n' % (
            self.visit(node.value)))
    else:
        return self.indent('yield\n')
</t>
<t tx="ekr.20160123185308.75"></t>
<t tx="ekr.20160123185308.76">def kind(self, node):
    '''Return the name of node's class.'''
    return node.__class__.__name__
</t>
<t tx="ekr.20160123185308.77">def indent(self, s):
    return '%s%s' % (' ' * 4 * self.level, s)
</t>
<t tx="ekr.20160123185308.78">@nobeautify

def op_name (self,node,strict=True):
    '''Return the print name of an operator node.'''
    d = {
        # Binary operators. 
        'Add':       '+',
        'BitAnd':    '&amp;',
        'BitOr':     '|',
        'BitXor':    '^',
        'Div':       '/',
        'FloorDiv':  '//',
        'LShift':    '&lt;&lt;',
        'Mod':       '%',
        'Mult':      '*',
        'Pow':       '**',
        'RShift':    '&gt;&gt;',
        'Sub':       '-',
        # Boolean operators.
        'And':   ' and ',
        'Or':    ' or ',
        # Comparison operators
        'Eq':    '==',
        'Gt':    '&gt;',
        'GtE':   '&gt;=',
        'In':    ' in ',
        'Is':    ' is ',
        'IsNot': ' is not ',
        'Lt':    '&lt;',
        'LtE':   '&lt;=',
        'NotEq': '!=',
        'NotIn': ' not in ',
        # Context operators.
        'AugLoad':  '&lt;AugLoad&gt;',
        'AugStore': '&lt;AugStore&gt;',
        'Del':      '&lt;Del&gt;',
        'Load':     '&lt;Load&gt;',
        'Param':    '&lt;Param&gt;',
        'Store':    '&lt;Store&gt;',
        # Unary operators.
        'Invert':   '~',
        'Not':      ' not ',
        'UAdd':     '+',
        'USub':     '-',
    }
    name = d.get(self.kind(node),'&lt;%s&gt;' % node.__class__.__name__)
    if strict: assert name,self.kind(node)
    return name
</t>
<t tx="ekr.20160123185308.79">class StubFormatter (AstFormatter):
    @others
</t>
<t tx="ekr.20160123185308.8">class AstFormatter:
    '''
    A class to recreate source code from an AST.
    
    This does not have to be perfect, but it should be close.
    
    Also supports optional annotations such as line numbers, file names, etc.
    '''
    # No ctor.
    # pylint: disable=consider-using-enumerate
    @others
</t>
<t tx="ekr.20160123185308.80"># Return generic markers allow better pattern matches.

def do_BoolOp(self, node): # Python 2.x only.
    return 'bool'

def do_Bytes(self, node): # Python 3.x only.
    return 'bytes' # return str(node.s)

def do_Name(self, node):
    return 'bool' if node.id in ('True', 'False') else node.id

def do_Num(self, node):
    return 'number' # return repr(node.n)

def do_Str(self, node):
    '''This represents a string constant.'''
    return 'str' # return repr(node.s)
</t>
<t tx="ekr.20160123185308.81">class StubTraverser (ast.NodeVisitor):
    
    def __init__(self, c, d, output_fn):
        '''Ctor for StubTraverser class.'''
        self.c = c
        self.d = d
        self.format = StubFormatter().format
        self.in_function = False
        self.level = 0
        self.output_file = None
        self.output_fn = output_fn
        self.returns = set()

    @others
</t>
<t tx="ekr.20160123185308.82">def indent(self, s):
    '''Return s, properly indented.'''
    return '%s%s' % (' ' * 4 * self.level, s)

def out(self, s):
    '''Output the string to the console or the file.'''
    if self.output_file:
        self.output_file.write(self.indent(s)+'\n')
    else:
        print(self.indent(s))
</t>
<t tx="ekr.20160123185308.83">def run(self, node):
    '''StubTraverser.run: write the stubs in node's tree to self.output_fn.'''
    c = self.c
    dir_ = g.os_path_dirname(self.output_fn)
    if g.os_path_exists(dir_):
        self.output_file = open(self.output_fn, 'w')
        aList = c.config.getData('stub-prefix')
        if aList:
            for z in aList:
                self.out(z.strip())
        self.visit(node)
        self.output_file.close()
        self.output_file = None
        g.es_print('wrote', self.output_fn)
    else:
        g.es_print('not found:', dir_)

</t>
<t tx="ekr.20160123185308.84"># This is needed only when subclassing from the leoAst.AstFullTraverser class.

# def visit(self, node):
    # '''Visit a *single* ast node.  Visitors are responsible for visiting children!'''
    # assert isinstance(node, ast.AST), node.__class__.__name__
    # method = getattr(self, 'do_' + node.__class__.__name__)
    # method(node)
</t>
<t tx="ekr.20160123185308.85"></t>
<t tx="ekr.20160123185308.86"># ClassDef(identifier name, expr* bases, stmt* body, expr* decorator_list)

def visit_ClassDef(self, node):

    # Format...
    if not node.name.startswith('_'):
        if node.bases:
            s = '(%s)' % ','.join([self.format(z) for z in node.bases])
        else:
            s = ''
        self.out('class %s%s:' % (node.name, s))
    # Visit...
    self.level += 1
    old_in_function = self.in_function
    self.in_function = False
    for z in node.body:
        self.visit(z)
    self.level -= 1
    self.in_function = old_in_function
</t>
<t tx="ekr.20160123185308.87"># FunctionDef(identifier name, arguments args, stmt* body, expr* decorator_list)

def visit_FunctionDef(self, node):
    
    # Do nothing if we are already in a function.
    # We do not generate stubs for inner defs.
    if self.in_function or node.name.startswith('_'):
        return
    # First, visit the function body.
    self.returns = set()
    self.in_function = True
    self.level += 1
    for z in node.body:
        self.visit(z)
    self.level -= 1
    self.in_function = False
    # Format *after* traversing
    self.out('def %s(%s) -&gt; %s: ...' % (
        node.name,
        self.format_arguments(node.args),
        self.format_returns(node)))
</t>
<t tx="ekr.20160123185308.88"># arguments = (expr* args, identifier? vararg, identifier? kwarg, expr* defaults)

def format_arguments(self, node):
    '''
    Format the arguments node.
    Similar to AstFormat.do_arguments, but it is not a visitor!
    '''
    assert isinstance(node,ast.arguments), node
    args = [self.format(z) for z in node.args]
    defaults = [self.format(z) for z in node.defaults]
    # Assign default values to the last args.
    result = []
    n_plain = len(args) - len(defaults)
    # pylint: disable=consider-using-enumerate
    for i in range(len(args)):
        s = self.munge_arg(args[i])
        if i &lt; n_plain:
            result.append(s)
        else:
            result.append('%s=%s' % (s, defaults[i - n_plain]))
    # Now add the vararg and kwarg args.
    name = getattr(node, 'vararg', None)
    if name: result.append('*' + name)
    name = getattr(node, 'kwarg', None)
    if name: result.append('**' + name)
    return ', '.join(result)
</t>
<t tx="ekr.20160123185308.89">def munge_arg(self, s):
    '''Add an annotation for s if possible.'''
    a = self.d.get(s)
    return '%s: %s' % (s, a) if a else s
</t>
<t tx="ekr.20160123185308.9"></t>
<t tx="ekr.20160123185308.90">def format_returns(self, node):
    '''Calculate the return type.'''
    def split(s):
        return '\n     ' + self.indent(s) if len(s) &gt; 30 else s
        
    r = list(self.returns)
    r = [self.format(z) for z in r]
    # if r: g.trace(r)
    if len(r) == 0:
        return 'None'
    if len(r) == 1:
        return split(r[0])
    elif 'None' in r:
        r.remove('None')
        return split('Optional[%s]' % ', '.join(r))
    else:
        # return 'Any'
        s = ', '.join(r)
        if len(s) &gt; 30:
            return ', '.join(['\n    ' + self.indent(z) for z in r])
        else:
            return split(', '.join(r))
</t>
<t tx="ekr.20160123185308.91">def visit_Return(self, node):

    self.returns.add(node.value)
</t>
<t tx="ekr.20160319080102.1">def cleanSaxInputString(s):
    '''Clean control characters from s.
    s may be a bytes or a (unicode) string.'''
    # Note: form-feed ('\f') is 12 decimal.
    badchars = [chr(ch) for ch in range(32)]
    badchars.remove('\t')
    badchars.remove('\r')
    badchars.remove('\n')
    flatten = ''.join(badchars)
    pad = ' ' * len(flatten)
    flatten = bytes(flatten, 'utf-8')
    pad = bytes(pad, 'utf-8')
    transtable = bytes.maketrans(flatten, pad)
    return s.translate(transtable)
</t>
<t tx="ekr.20160319081141.1">def main():
    for path1 in files:
        if path1.endswith('.xml'):
            path2 = path1[:-4] + '.py'
        else:
            path2 = path1 + '.py'
        try:
            convert(path1, path2)
        except Exception:
            print('Exception creating', path2)
            g.es_exception()
    g.es_print('done')
</t>
<t tx="ekr.20160923132656.1">@language python
"""Introspect"""

# By Terry Brown.  Requires Python 2.x.

# https://groups.google.com/forum/#!msg/leo-editor/Qu2HccpC_wc/_ee11jIvAQAJ

import types

sub_mode = 'instance'
# 'instance' or 'class' - controls which, instance or class names,
# are put it a subnode.  'instance class' sub-nodes both.
# '' appends classes after names, not useful.

def classname(thing):
    if hasattr(thing, '__class__'):
        return thing.__class__.__name__
    else:
        return thing.__name__

if not hasattr(c.p.v, '_introspection_target'):
    txt = g.app.gui.runAskOkCancelStringDialog(
        c, "Introspect what", "Introspect what")
    if txt is not None:
        o = eval(txt)
        c.p.v._introspection_target = o
        c.p.h = "%s %s" % (txt, classname(o))

# c.p.deletePositionsInList([i.copy() for i in p.children()])

obj = c.p.v._introspection_target
g.es(classname(obj))

def show_obj(c, obj):

    inames = sorted(dir(obj))
    
    things = {}
    instances = []
    for iname in inames:
        
        if iname.startswith('__'):
            continue
        
        o = getattr(obj, iname)
        cname = classname(o)
        instances.append((iname, o))
        things.setdefault(cname, []).append(instances[-1])

    if 'instance' in sub_mode:
        tnd = c.p.v.insertAsNthChild(0)
        tnd.h = "&lt;by name&gt;"
    else:
        tnd = c.p.v

    instances.sort()
    for iname, o in instances:
        
        if classname(o) == 'position':
            # apparently this collapses the space-time continuum?
            continue
        
        nd = tnd.insertAsLastChild()
        
        if not seen_already(tnd, nd, iname, o):
            nd.h = "%s %s" % (iname, format_type(nd, o))
            nd._introspection_target = o

    if 'class' in sub_mode:
        ttnd = c.p.v.insertAsNthChild(0)
        ttnd.h = "&lt;by class&gt;"
    else:
        ttnd = c.p.v

    for cname in sorted(things):
    
        if len(things[cname]) == 1:
            tnd = ttnd
        else:
            tnd = ttnd.insertAsLastChild()
            tnd.h = "&lt;%s&gt;"%cname
    
        for iname, o in sorted(things[cname]):
            
            if cname == 'position':
                # apparently this collapses the space-time continuum?
                continue
            
            nd = tnd.insertAsLastChild()
            if not seen_already(tnd, nd, iname, o):
                show_child(nd, iname, o)
                nd._introspection_target = o
         
def seen_already(tnd, nd, iname, o):
        
    up = tnd.parents
    while up:
        if (hasattr(up[0], '_introspection_target') and
            up[0]._introspection_target is o):
            break
        up = up[0].parents
    else:
        return False
        
    nd.h = "[%s %s]" % (classname(o), iname)
    pos = c.vnode2position(up[0])
    nd.b = pos.get_UNL(with_file=True, with_proto=True)
    
    return True
            
def show_child(nd, iname, o):
                
    nd._introspection_target = o
    nd.h = "%s %s" % (format_type(nd, o), iname)
    
docable = (
    types.ClassType, types.MethodType, types.UnboundMethodType, 
    types.BuiltinFunctionType, types.BuiltinMethodType,
)
    
def format_type(nd, o):
    
    if isinstance(o, docable):
        if hasattr(o, '__doc__'):
            nd.b = o.__doc__
    
    if isinstance(o, (str, unicode)):
        nd.b = o
        return "%s '%s'" % (classname(o), o[:20])
    elif isinstance(o, bool):
        return "%s %s" % (classname(o), 'T' if o else 'F')
    elif isinstance(o, (int, float)):
        return "%s %s" % (classname(o), o)
    elif isinstance(o, (tuple, list, dict)):
        return "%s %s" % (classname(o), len(o))
    else:
        return classname(o)
    
def show_list(c, list_):
    
    if len(list_) &gt; 100:
        nd = c.p.v.insertAsLastChild()
        nd.h = "&lt;%s of %d items truncated&gt;" % len(list_.__class__.__name__, list_)
        
    if len(list_) == 0:
        nd = c.p.v.insertAsLastChild()
        nd.h = "&lt;%s of 0 items&gt;" % list_.__class__.__name__
        
    for n, i in enumerate(list_[:100]):
        nd = c.p.v.insertAsLastChild()
        show_child(nd, '', i)
        nd.h = "%d: %s" % (n, nd.h)
        nd._introspection_target = i

def show_dict(c, dict_):
    
    if len(dict_) &gt; 100:
        nd = c.p.v.insertAsLastChild()
        nd.h = "&lt;dict of %d items truncated&gt;" % len(dict_)
        
    if len(dict_) == 0:
        nd = c.p.v.insertAsLastChild()
        nd.h = "&lt;dict of 0 items&gt;"
        
    keys = dict_.keys()
    keys.sort()
        
    for k in keys[:100]:
        nd = c.p.v.insertAsLastChild()
        i = dict_[k]
        show_child(nd, '', i)
        nd.h = "%s: %s" % (k, nd.h)
        nd._introspection_target = i

dispatch = {
    list: show_list,
    tuple: show_list,
    dict: show_dict,
}

func = dispatch.get(type(obj), show_obj)

func(c, obj)
   
c.p.expand()
c.redraw()
</t>
<t tx="ekr.20161006092435.1">'''Import tab-indented files.'''
g.cls()
if c.isChanged():
    c.save()
separate = True # True: put all lines in separate nodes.
path = r'c:/test/tab_file_test.txt'
if 1:
    # Use the TabImporter class in leoImport.py.
    import leo.core.leoImport as leoImport
    importer = leoImport.TabImporter(c, separate=separate)
    importer.import_files([path])
else:
    # Use a custom class.
    @others
    s = open(path).read()
    last = c.lastTopLevel()
    root = last.insertAfter()
    root.h = path
    TabImporter(c, root=root, separate=separate).scan(s)
    g.es_print('Imported %s' % path)
    c.selectPosition(root)
    root.expand()
    c.redraw()
</t>
<t tx="ekr.20161006092435.2">class TabImporter:
    
    def __init__(self, c, root, separate):
        '''Ctor for the TabImporter class.'''
        self.c = c
        self.stack = []
        self.root = root
        self.separate = separate

    @others
</t>
<t tx="ekr.20161006092435.3">def check(self, lines):
    '''Return False and warn if lines contains mixed leading tabs/blanks.'''
    blanks, tabs = 0, 0
    for s in lines:
        lws = self.lws(s)
        if '\t' in lws: tabs += 1
        if ' ' in lws: blanks += 1
    if tabs and blanks:
        g.es_print('intermixed leading blanks and tabs.')
        return False
    else:
        return True
</t>
<t tx="ekr.20161006092435.4">def dump_stack(self):
    '''Dump the stack, containing (level, p) tuples.'''
    g.trace('==========')
    for i, data in enumerate(self.stack):
        level, p = data
        print('%2s %s %r' % (i, level, p.h))
</t>
<t tx="ekr.20161006092435.5">def lws(self, s):
    '''Return the length of the leading whitespace of s.'''
    for i, ch in enumerate(s):
        if ch not in ' \t':
            return s[:i]
    return s
    
    
</t>
<t tx="ekr.20161006092435.6">def scan(self, s1):
    trace = False and not g.unitTesting
    c, root, separate = self.c, self.root, self.separate
    if not s1.strip() or not root:
        return
    if trace: g.trace('importing to %s' % root.h)
    lines = g.splitLines(s1)
    if self.check(lines):
        self.stack = []
        for s in lines:
            if s.strip() or not separate:
                self.scan_helper(s)
</t>
<t tx="ekr.20161006092435.7">def scan_helper(self, s):
    '''Update the stack as necessary and return (level, parent, stack).'''
    trace = False and not g.unitTesting
    root, separate, stack = self.root, self.separate, self.stack
    if stack:
        level, parent = stack[-1]
    else:
        level, parent = 0, None
    lws = len(self.lws(s))
    if trace:
        g.trace('----- level: %s lws: %s %s' % (level, lws, s.rstrip()))
    h = s.strip()
    if lws == level:
        if separate or not parent:
            # Replace the top of the stack with a new entry.
            if stack:
                stack.pop()
            grand_parent = stack[-1][1] if stack else root
            parent = grand_parent.insertAsLastChild() # lws == level
            parent.h = h
            stack.append((level, parent),)
        elif not parent.h:
            parent.h = h
    elif lws &gt; level:
        # Create a new parent.
        level = lws
        parent = parent.insertAsLastChild()
        parent.h = h
        stack.append((level, parent),)
    else:
        # Find the previous parent.
        if trace: self.dump_stack()
        while stack:
            level2, parent2 = stack.pop()
            if level2 == lws:
                grand_parent = stack[-1][1] if stack else root
                parent = grand_parent.insertAsLastChild() # lws &lt; level
                parent.h = h
                level = lws
                stack.append((level, parent),)
                break
        else:
            level = 0
            parent = root.insertAsLastChild()
            parent.h = h
            stack = [(0, parent),]
    if trace:
        g.trace('DONE: lws: %s level: %s parent: %s' % (lws, level, parent.h))
        self.dump_stack()
    assert parent and parent == stack[-1][1]
        # An important invariant.
    assert level == stack[-1][0], (level, stack[-1][0])
    if not separate:
        parent.b = parent.b + self.undent(level, s)
    return level
</t>
<t tx="ekr.20161006092435.8">def undent(self, level, s):
    '''Unindent all lines of p.b by level.'''
    # g.trace(level, s.rstrip())
    if level &lt;= 0:
        return s
    if s.strip():
        lines = g.splitLines(s)
        ch = lines[0][0]
        assert ch in ' \t', repr(ch)
        # Check that all lines start with the proper lws.
        lws = ch * level
        for s in lines:
            if not s.startswith(lws):
                g.trace('bad indentation: %r' % s)
                return s
        return ''.join([z[len(lws):] for z in lines])
    else:
        return ''
</t>
<t tx="ekr.20161031130627.1">'''
Undoably converts the word at the cursor to pep8 style throughout a given tree.
Also sets the find text to the new word.
'''
# aTestExample notFoundExample.
import re
# clear()
table = (
    'BLS.new_scan &amp; helpers',
    'BLS.Code generation',
)
@others
Pep8(table, change=True).run()
</t>
<t tx="ekr.20161031130627.2">class Pep8:
    '''
    Convert the word under the cursor to pep8 style in all subtrees in
    table.
    '''
    
    def __init__ (self, table, change=False):
        '''Ctor for Pep8 class.'''
        self.change = change
        self.table = table
        
    @others
</t>
<t tx="ekr.20161031130627.3">def change_all(self, name, new_name, root):
    '''Undoably change name to new_name throughout root's tree.'''
    u = c.undoer
    bunch = u.beforeChangeTree(root)
    found = False
    self.pattern = re.compile(r'\b%s\b' % name)
    for p in root.self_and_subtree():
        found = self.change_headline(name, new_name, p) or found
        found = self.change_body(name, new_name, p) or found
    if found:
        u.afterChangeTree(root, 'pep8', bunch)
    return found
</t>
<t tx="ekr.20161031130627.4">def change_body(self, name, new_name, p):
    indices = []
    for m in self.pattern.finditer(p.b):
        indices.append(str(m.start()))
    if indices:
        n = len(indices)
        g.es_print('%s change%s: %s' % (n, g.plural(n), p.h))
        s = p.b
        for i in reversed(indices):
            i = int(i)
            s = s[:i] + new_name + s[i+len(name):]
        if self.change:
            p.b = s
            p.setDirty()
        else:
            g.es_print(s)
    return bool(indices)
</t>
<t tx="ekr.20161031130627.5">def change_headline(self, name, new_name, p):
    m = self.pattern.search(p.h)
    if m:
        i = m.start()
        s = p.h
        s = s[:i] + new_name + s[i+len(name):]
        if self.change:
            p.h = s
            p.setDirty()
            g.es_print('changed headline', s)
        else:
            g.es_print('headline', s)
    return bool(m)
</t>
<t tx="ekr.20161031130627.6">def get_name(self):
    i, j = c.editCommands.extendToWord(event=None, select=False)
    w = c.frame.body.wrapper
    s = w.getAllText()
    name = s[i:j]
    return name
</t>
<t tx="ekr.20161031130627.7">def run(self):
    # self.clear()
    name = self.get_name()
    new_name = self.to_pep8(name)
    if len(name) &lt; 2:
        g.es_print('name too short:', name)
    elif new_name == name:
        g.es_print('already pep8:', name)
    else:
        g.es_print('%s -&gt; %s' % (name, new_name))
        c.findCommands.ftm.setFindText(new_name)
            # Preload the replacement text.
        found = False
        for target in table:
            root = g.findNodeAnywhere(c, target)
            assert root, target
            found = self.change_all(name, new_name, root) or found
        if found:
            c.redraw()
        else:
            g.es_print('not found:', name)
</t>
<t tx="ekr.20161031130627.8">def to_pep8(self, s):
    
    if len(s) &gt; 1 and s[0].islower() and s.lower() != s:
        result = []
        for ch in s:
            result.append(ch)
            if ch.isupper():
                result.pop()
                result.append('_%s' % (ch.lower()))
        return ''.join(result)
    else:
        return name
</t>
<t tx="ekr.20161031130627.9">def clear():
    g.cls()
    c.k.simulateCommand('clear-log')
</t>
<t tx="ekr.20161120175101.1">'''
Create markdown headers throughout the nearest .md outline.

That is, prepend p.b[0] with markdown section markup, if appriate.
'''
g.cls()
import re
@others

def predicate(p):
    return p.isAnyAtFileNode() and p.h.strip().endswith('.md')

for root in c.all_unique_roots(predicate):
    print(root.h)
    for p in root.self_and_subtree():
        markup(p, root)
c.redraw()

@language python
@tabwidth -4
</t>
<t tx="ekr.20161120175101.2">pattern = re.compile(r'^(#+\s+)(.*)$')

def markup(p, root):
    '''prepend p.b[0] with markdown section markup, if appriate.'''
    root_level = root.level()
    lines = g.splitLines(p.b)
    if len(lines) &lt; 2: return
    line0, line1 = lines[0], lines[1]
    if (
        not p.h.startswith('@md-ignore') and
        not line0.isspace() and # A real first line.
        not line0.startswith('@') and # Not a directive
        line1.isspace() # the next line is blank
    ):
        # Remove existing markup.
        m = pattern.match(line0)
        if m:  line0 = m.group(2) + '\n'
        # Add the correct markup.
        hashes = '#'*(p.level()-root_level+1)
        lines[0] = '%s %s' % (hashes, line0)
    # Ensure a blank line, so as not to interfere with later headers.
    s = ''.join(lines).rstrip()+'\n\n'
    if p.b != s:
        g.es_print('changed: %s' % (p.h))
        p.setDirty()
        c.setChanged(True)
        p.b = s
</t>
<t tx="ekr.20161123085419.1">'''
Create a table of expected headlines in a unit test.

To use this script, select the root of the tree containing the expected
results. After running this, copy the table from the console.
'''
g.cls()
# Proper escapes are tricky.
table = [
    '"%s",' % p.h.replace('\\', '\\\\').replace('"', '\\"')
        for p in c.p.subtree()
]
print("table = (\n    %s\n)" % '\n    '.join(table))
</t>
<t tx="ekr.20161124034654.1">g.cls()
# define constants that describe the new language.
name = 'otl'
    # The name of the file, and the prefix for classes.
language = 'plain'
    # The name of the language, case doesn't matter.
extensions = ['.org',]
    # A list of file extensions supported by this importer.
strict = False
    # True if leading whitespace is particularly significant.
state_ivar = 'self.not_used'
    # 'self.indent' for python, coffeescript.
    # 'self.curlies' for many other languages
    # '(self, curlies, self.parens)' for more complex comparisons
&lt;&lt; define run &amp; helpers &gt;&gt;
run(extensions, language, name, state_ivar)
</t>
<t tx="ekr.20161124034654.10">### Define an override if desired...

if 0: # The base class
    def clean_headline(self, s):
        '''Return a cleaned up headline s.'''
        return s.strip()
        
if 0: # A more complex example, for the C language.
    def clean_headline(self, s):
        '''Return a cleaned up headline s.'''
        import re
        type1 = r'(static|extern)*'
        type2 = r'(void|int|float|double|char)*'
        class_pattern = r'\s*(%s)\s*class\s+(\w+)' % (type1)
        pattern = r'\s*(%s)\s*(%s)\s*(\w+)' % (type1, type2)
        m = re.match(class_pattern, s)
        if m:
            prefix1 = '%s ' % (m.group(1)) if m.group(1) else ''
            return '%sclass %s' % (prefix1, m.group(2))
        m = re.match(pattern, s)
        if m:
            prefix1 = '%s ' % (m.group(1)) if m.group(1) else ''
            prefix2 = '%s ' % (m.group(2)) if m.group(2) else ''
            h = m.group(3) or '&lt;no c function name&gt;'
            return '%s%s%s' % (prefix1, prefix2, h)
        else:
            return s
</t>
<t tx="ekr.20161124034654.11">def clean_nodes(self, parent):
    '''
    Clean all nodes in parent's tree.
    Subclasses override this as desired.
    See perl_i.clean_nodes for an examplle.
    '''
    pass
</t>
<t tx="ekr.20161124034654.12">class {{cap_name}}_ScanState:
    '''A class representing the state of the {{name}} line-oriented scan.'''
    
    def __init__(self, d=None):
        '''{{cap_name}}_ScanState.__init__'''
        if d:
            prev = d.get('prev')
            self.context = prev.context
            ### Adjust these by hand.
            self.curlies = prev.curlies
        else:
            self.context = ''
            ### Adjust these by hand.
            self.curlies = 0

    def __repr__(self):
        '''{{cap_name}}_ScanState.__repr__'''
        ### Adjust these by hand.
        return "{{cap_name}}_ScanState context: %r curlies: %s" % (
            self.context, self.curlies)

    __str__ = __repr__

    @others
</t>
<t tx="ekr.20161124034654.13">def level(self):
    '''{{cap_name}}_ScanState.level.'''
    return {{state_ivar}}

</t>
<t tx="ekr.20161124034654.14">def update(self, data):
    '''
    {{cap_name}}_ScanState.update

    Update the state using the 6-tuple returned by v2_scan_line.
    Return i = data[1]
    '''
    context, i, delta_c, delta_p, delta_s, bs_nl = data
    # All ScanState classes must have a context ivar.
    self.context = context
    self.curlies += delta_c  
    ### Update {{cap_name}}_ScanState ivars
    # self.bs_nl = bs_nl
    # self.parens += delta_p
    # self.squares += delta_s
    return i
</t>
<t tx="ekr.20161124034654.2">@others
</t>
<t tx="ekr.20161124034654.3">def copy_tree(source, root, h):
    '''Copy the source tree to the node after p, with headline h.'''
    p2 = root.insertAfter()
    source.copyTreeFromSelfTo(p2)
    p2.h = h
    return p2
 
</t>
<t tx="ekr.20161124034654.4">def make_substitutions(destination, patterns):
    '''Make all substitutions in the destination tree.'''
    for p in destination.self_and_subtree():
        h = substitute(p.h, patterns)
        if p.h != h:
            # g.trace('CHANGED:', p.h, '==&gt;', h)
            p.h = h
        b = substitute(p.b, patterns)
        if p.b != b:
            # g.trace('CHANGED:', p.b, '==&gt;', b)
            p.b = b
</t>
<t tx="ekr.20161124034654.5">def run(extensions, language, name, state_ivar):
    '''The driver for this script.'''
    patterns = {
        'cap_name': name.capitalize(),
        'extensions': '[%s]' % ', '.join(["'%s'" % (z) for z in extensions]),
        'language': language.lower(),
        'name': name.lower(),
        'strict': 'True' if strict else 'False',
        'state_ivar': state_ivar,
    }
    h = '@button make-importer'
    root = g.findNodeAnywhere(c, h)
    assert root, h
    h = '@@file importers/{{name}}.py'
    source = g.findNodeInTree(c, root, h)
    assert source, h
    destination = copy_tree(source, root, h)
    make_substitutions(destination, patterns)
    c.contractAllHeadlines()
    c.redraw()
</t>
<t tx="ekr.20161124034654.6">def substitute(s, patterns):
    '''Make all substitutions in s.'''
    for pattern in patterns:
        find = '{{%s}}' % pattern
        replace = patterns.get(pattern)
        i = 0
        while i &lt; len(s):
            progress = i
            j = s.find(find, i)
            if j == -1: break
            s = s[:j] + replace + s[j+len(find):]
            i = j+len(replace)
            assert progress &lt; i
    return s
</t>
<t tx="ekr.20161124034654.7">'''The @auto importer for the {{name}} language.'''
import leo.plugins.importers.linescanner as linescanner
Importer = linescanner.Importer
@others
importer_dict = {
    'class': {{cap_name}}_Importer,
    'extensions': {{extensions}},
}
@language python
@tabwidth -4


</t>
<t tx="ekr.20161124034654.8">class {{cap_name}}_Importer(Importer):
    '''The importer for the {{name}} lanuage.'''

    def __init__(self, importCommands, atAuto):
        '''{{cap_name}}_Importer.__init__'''
        # Init the base class.
        Importer.__init__(self,
            importCommands,
            atAuto = atAuto,
            language = '{{language}}',
            state_class = {{cap_name}}_ScanState,
            strict = {{strict}},
        )
        
    @others
</t>
<t tx="ekr.20161124034654.9"># These can be overridden in subclasses.
</t>
<t tx="ekr.20161204063803.1">'''Make a markdown table of contents from an @auto-md node.'''
g.cls()
if c.isChanged(): c.save()

class Controller:
    '''Controller class for @button md-toc.'''
    @others
    
if 0:
    h = '@auto-md importers.md'
    p = g.findTopLevelNode(c, h)
    assert p, h
else:
    p = c.p
Controller().run(p)
@language python
@tabwidth -4
</t>
<t tx="ekr.20161204063803.2">def run(self, p):
    '''The driver for @button md-toc.'''
    if p.h.startswith('@auto-md '): #  or p.h.endswith('.md'):
        last = c.lastTopLevel()
        self.root = last.insertAfter()
        self.root.h = 'Table of contents'
        self.make_toc(p)
    else:
        print('must be an @auto-md node or an x.md node: %s' % p.h)
</t>
<t tx="ekr.20161204063803.3">def make_link(self, s):
    '''Return the markdown link for s.'''
    result = []
    for ch in s.lower():
        if ch in ' -':
            result.append('-')
        elif ch.isalnum():
            result.append(ch)
        else:
            pass
    return ''.join(result)
</t>
<t tx="ekr.20161204063803.4">def make_toc(self, p):
    '''Create the toc in self.root.b.'''
    result, stack = [], []
    prefix = p.h.lstrip('@auto-md').strip()
    for p in p.subtree():
        level = p.level() - self.root.level()
        assert level &gt; 0
        if len(stack) &lt; level:
            stack.append(1)
        else:
            stack = stack[:level]
        n = stack[-1]
        stack[-1] = n+1
        indent = ' '*4*(level-1)
        line = '%s%s. [%s](%s#%s)\n' % (
            indent, n, p.h, prefix, self.make_link(p.h))
        result.append(line)
    if 0:
        g.trace(p.h)
        g.printList(result)
    else:
        self.root.b = ''.join(result)
    c.redraw()
</t>
<t tx="ekr.20161206070406.1">gnx: ekr.20161206070323.1
</t>
<t tx="ekr.20161206090903.1">gnx: ekr.20161206070323.2
unl: &lt;&lt; what c2py does &gt;&gt;
gnx: ekr.20161206070323.3
unl: &lt;&lt; theory of operation &gt;&gt;
gnx: ekr.20161206070323.4
unl: &lt;&lt; specify user types &gt;&gt;
gnx: ekr.20161206070323.5
unl: &lt;&lt; define testData &gt;&gt;
gnx: ekr.20161206070323.6
unl: speedTest
gnx: ekr.20161206070323.7
unl: leo1to2
gnx: ekr.20161206070323.8
unl: leo1to2--&gt;leo1to2
gnx: ekr.20161206070323.9
unl: leo1to2--&gt;convertLeo1to2
gnx: ekr.20161206070323.10
unl: leo1to2--&gt;convertStringLeo1to2
gnx: ekr.20161206070323.11
unl: leo1to2--&gt;convertCodeList1to2
gnx: ekr.20161206070323.12
unl: c2py entry points
gnx: ekr.20161206070323.13
unl: c2py entry points--&gt;convertCurrentTree
gnx: ekr.20161206070323.14
unl: c2py entry points--&gt;convertLeoTree
gnx: ekr.20161206070323.15
unl: c2py entry points--&gt;convertCFileToPython
gnx: ekr.20161206070323.16
unl: convertCStringToPython &amp; helpers (top level)
gnx: ekr.20161206070323.17
unl: convertCStringToPython &amp; helpers (top level)--&gt;convertCodeList (main pattern function)
gnx: ekr.20161206070323.18
unl: convertCStringToPython &amp; helpers (top level)--&gt;convertDocList
gnx: ekr.20161206070323.19
unl: convertCStringToPython &amp; helpers (top level)--&gt;skipDocPart
gnx: ekr.20161206070323.20
unl: convertCStringToPython &amp; helpers (top level)--&gt;skipCodePart
gnx: ekr.20161206070323.21
unl: Scanning &amp; Replacing...
gnx: ekr.20161206070323.22
unl: Scanning &amp; Replacing...--&gt;convertLeadingBlanks
gnx: ekr.20161206070323.23
unl: Scanning &amp; Replacing...--&gt;mungeAllFunctions
gnx: ekr.20161206070323.24
unl: Scanning &amp; Replacing...--&gt;mungeAllFunctions--&gt;handlePossibleFunctionHeader
gnx: ekr.20161206070323.25
unl: Scanning &amp; Replacing...--&gt;mungeAllFunctions--&gt;massageFunctionArgs
gnx: ekr.20161206070323.26
unl: Scanning &amp; Replacing...--&gt;mungeAllFunctions--&gt;massageFunctionHead (sets gClassName)
gnx: ekr.20161206070323.27
unl: Scanning &amp; Replacing...--&gt;mungeAllFunctions--&gt;massageFunctionBody
gnx: ekr.20161206070323.28
unl: Scanning &amp; Replacing...--&gt;mungeAllFunctions--&gt;massageFunctionBody--&gt;massageIvars
gnx: ekr.20161206070323.29
unl: Scanning &amp; Replacing...--&gt;mungeAllFunctions--&gt;massageFunctionBody--&gt;removeCasts
gnx: ekr.20161206070323.30
unl: Scanning &amp; Replacing...--&gt;mungeAllFunctions--&gt;massageFunctionBody--&gt;removeTypeNames
gnx: ekr.20161206070323.31
unl: Scanning &amp; Replacing...--&gt;handleAllKeywords
gnx: ekr.20161206070323.32
unl: Scanning &amp; Replacing...--&gt;handleAllKeywords--&gt;handleKeyword
gnx: ekr.20161206070323.33
unl: Scanning &amp; Replacing...--&gt;isX...
gnx: ekr.20161206070323.34
unl: Scanning &amp; Replacing...--&gt;isX...--&gt;isWs and isWOrNl
gnx: ekr.20161206070323.35
unl: Scanning &amp; Replacing...--&gt;isX...--&gt;isSectionDef
gnx: ekr.20161206070323.36
unl: Scanning &amp; Replacing...--&gt;isX...--&gt;isStringOrComment
gnx: ekr.20161206070323.37
unl: Scanning &amp; Replacing...--&gt;find... &amp; match...
gnx: ekr.20161206070323.38
unl: Scanning &amp; Replacing...--&gt;find... &amp; match...--&gt;findInCode
gnx: ekr.20161206070323.39
unl: Scanning &amp; Replacing...--&gt;find... &amp; match...--&gt;findInList
gnx: ekr.20161206070323.40
unl: Scanning &amp; Replacing...--&gt;find... &amp; match...--&gt;match
gnx: ekr.20161206070323.41
unl: Scanning &amp; Replacing...--&gt;find... &amp; match...--&gt;matchWord
gnx: ekr.20161206070323.42
unl: Scanning &amp; Replacing...--&gt;remove...
gnx: ekr.20161206070323.43
unl: Scanning &amp; Replacing...--&gt;remove...--&gt;removeAllCComments
gnx: ekr.20161206070323.44
unl: Scanning &amp; Replacing...--&gt;remove...--&gt;removeAllCSentinels
gnx: ekr.20161206070323.45
unl: Scanning &amp; Replacing...--&gt;remove...--&gt;removeAllPythonComments
gnx: ekr.20161206070323.46
unl: Scanning &amp; Replacing...--&gt;remove...--&gt;removeAllPythonSentinels
gnx: ekr.20161206070323.47
unl: Scanning &amp; Replacing...--&gt;remove...--&gt;removeAtRoot
gnx: ekr.20161206070323.48
unl: Scanning &amp; Replacing...--&gt;remove...--&gt;removeBlankLines
gnx: ekr.20161206070323.49
unl: Scanning &amp; Replacing...--&gt;remove...--&gt;removeExcessWs
gnx: ekr.20161206070323.50
unl: Scanning &amp; Replacing...--&gt;remove...--&gt;removeExcessWs--&gt;removeExessWsFromLine
gnx: ekr.20161206070323.51
unl: Scanning &amp; Replacing...--&gt;remove...--&gt;removeLeadingAtCode
gnx: ekr.20161206070323.52
unl: Scanning &amp; Replacing...--&gt;remove...--&gt;removeMatchingBrackets
gnx: ekr.20161206070323.53
unl: Scanning &amp; Replacing...--&gt;remove...--&gt;removeSemicolonsAtEndOfLines
gnx: ekr.20161206070323.54
unl: Scanning &amp; Replacing...--&gt;remove...--&gt;removeTrailingWs
gnx: ekr.20161206070323.55
unl: Scanning &amp; Replacing...--&gt;replace... &amp; safeReplace
gnx: ekr.20161206070323.56
unl: Scanning &amp; Replacing...--&gt;replace... &amp; safeReplace--&gt;replace
gnx: ekr.20161206070323.57
unl: Scanning &amp; Replacing...--&gt;replace... &amp; safeReplace--&gt;replaceComments
gnx: ekr.20161206070323.58
unl: Scanning &amp; Replacing...--&gt;replace... &amp; safeReplace--&gt;replaceSectionDefs
gnx: ekr.20161206070323.59
unl: Scanning &amp; Replacing...--&gt;replace... &amp; safeReplace--&gt;safeReplace
gnx: ekr.20161206070323.60
unl: Scanning &amp; Replacing...--&gt;skip... &amp; prev...
gnx: ekr.20161206070323.61
unl: Scanning &amp; Replacing...--&gt;skip... &amp; prev...--&gt;prevNonWsChar and prevNonWsOrNlChar
gnx: ekr.20161206070323.62
unl: Scanning &amp; Replacing...--&gt;skip... &amp; prev...--&gt;skipCBlockComment
gnx: ekr.20161206070323.63
unl: Scanning &amp; Replacing...--&gt;skip... &amp; prev...--&gt;skipPastLine
gnx: ekr.20161206070323.64
unl: Scanning &amp; Replacing...--&gt;skip... &amp; prev...--&gt;skipPastWord
gnx: ekr.20161206070323.65
unl: Scanning &amp; Replacing...--&gt;skip... &amp; prev...--&gt;skipString
gnx: ekr.20161206070323.66
unl: Scanning &amp; Replacing...--&gt;skip... &amp; prev...--&gt;skipStringOrComment
gnx: ekr.20161206070323.67
unl: Scanning &amp; Replacing...--&gt;skip... &amp; prev...--&gt;skipToMatchingBracket
gnx: ekr.20161206070323.68
unl: Scanning &amp; Replacing...--&gt;skip... &amp; prev...--&gt;skipWs and skipWsAndNl
gnx: ekr.20161206070323.69
unl: Scanning &amp; Replacing...--&gt;stringToList &amp; listToString
gnx: ekr.20161206070323.70
unl: Scanning &amp; Replacing...--&gt;stringToList &amp; listToString--&gt;stringToList
gnx: ekr.20161206070323.71
unl: Scanning &amp; Replacing...--&gt;stringToList &amp; listToString--&gt;listToString
</t>
<t tx="ekr.20170120110015.1"></t>
<t tx="ekr.20170211083757.1">if c.isChanged(): c.save()
&lt;&lt; imports &gt;&gt;
&lt;&lt; class MyDemo &gt;&gt;
# Don't use @others here.
# The *same* command/key binding calls both demo-start and demo.next.
try:
    if getattr(g.app, 'demo', None):
        g.app.demo.next()
    else:
        g.cls()
        c.frame.log.clearTab('Log')
        g.es_print('Starting MyDemo')
        c.k.demoNextKey = c.k.strokeFromSetting('Ctrl-9')
            # Tell k.masterKeyHandler to process Ctrl-9 immediately.
            # Binding demo-next in a setting does *not* work.
        demo = MyDemo(c)
        p = g.findNodeAnywhere(c, '@button Demo @key=Ctrl-9')
        script_tree = g.findNodeInTree(c, p, 'demo-script')
        demo.start(script_tree, auto_run=True)
except Exception:
    g.app.demo = None
    raise
</t>
<t tx="ekr.20170211083757.10"># Create, move, promote, demote, hoist.
demo.retain(Title('Leo is a full featured outliner.'))
demo.wait(1.0)
###
demo.insert_node('a new node', keys=True, speed=10.0)
###
c.moveOutlineRight()
###
# demo.end() # Test of early exit.
###
demo.insert_node('another headline')
###
demo.insert_node('yet another node')
###
p = g.findNodeInTree(c, demo.root, 'a new node')
assert p, 'a new node'
c.selectPosition(p)
demo.wait(0.25)
###
c.demote()
demo.wait(1.0)
###
demo.delete_retained_widgets()
</t>
<t tx="ekr.20170211083757.11"># The body pane shows the text of the selected outline node.
</t>
<t tx="ekr.20170211083757.12"># Section refs, @others, @file
</t>
<t tx="ekr.20170211083757.13"></t>
<t tx="ekr.20170211083757.14"># Callout('Thanks for watching')
###
# demo.next()
</t>
<t tx="ekr.20170211083757.2">from leo.core.leoQt import QtGui
import leo.plugins.demo as demo_module
import imp
imp.reload(demo_module)
</t>
<t tx="ekr.20170211083757.3">class MyDemo (demo_module.Demo):
    
    @others
</t>
<t tx="ekr.20170211083757.4">def setup(self, p=None):
    c = self.c
    self.end_on_exception = True # Good for debugging.
    self.delta = 10
    demo.set_text_delta(10)
    self.geometry1 = self.get_top_geometry()
    p = g.findNodeAnywhere(c, 'Demo Area')
    self.root = p.copy() if p else None
    if p:
        p.expand()
        c.selectPosition(p)
    # c.frame.equalSizedPanes()
    c.redraw()
    self.set_youtube_position()
</t>
<t tx="ekr.20170211083757.5">def setup_script(self):
    self.delete_widgets()
</t>
<t tx="ekr.20170211083757.6">def teardown(self):
    c = self.c
    self.delete_all_widgets()
    # self.set_top_geometry(self.geometry1)
    if hasattr(self, 'delta') and self.delta &gt; 0:
        self.set_text_delta(-self.delta)
    if self.root and c.positionExists(self.root, trace=False):
        self.root.deleteAllChildren()
    p = c.lastTopLevel()
    p.expand()
    c.selectPosition(p)
    c.redraw()
</t>
<t tx="ekr.20170211083757.7">def teardown_script(self):
    if self.auto_run:
        # Default wait.
        self.wait(0.5)
</t>
<t tx="ekr.20170211083757.8">@language python
</t>
<t tx="ekr.20170211083757.9">Callout('Hello. This tutorial introduces Leo')
# Callout("First, I'll increase the text size for easier viewing")
# demo.next(wait=1.0)
</t>
<t tx="ekr.20170212105552.1">@language python
'''Create intro slides for screen shots.'''
# The *same* command/key binding calls both demo-start and demo.next.
if c.isChanged():
    c.save()
&lt;&lt; imports &gt;&gt;
#
# Do NOT use @others here.
#
&lt;&lt; class IntroSlides &gt;&gt;
&lt;&lt; main &gt;&gt;
if getattr(g.app, 'demo', None):
    g.app.demo.next()
else:
    demo = IntroSlides(c)
    main(c, demo,
        auto_run=False,
        hoist_node = "Leo's Main Window",
        script_name='intro-slides-script')
</t>
<t tx="ekr.20170212105552.10">@language python
</t>
<t tx="ekr.20170212105552.11">Callout("This is Leo's main window")

###
demo.next()
</t>
<t tx="ekr.20170212105552.2">from leo.core.leoQt import QtGui
import leo.plugins.demo as demo_module
# import imp
# imp.reload(demo_module)
</t>
<t tx="ekr.20170212105552.3">class IntroSlides (demo_module.Demo):
    
    @others
</t>
<t tx="ekr.20170212105552.4">def setup(self, p=None):
    c = self.c
    self.end_on_exception = True # Good for debugging.
    self.delta = 0
    demo.set_text_delta(self.delta)
    # self.set_youtube_position()
    if hasattr(self, 'hoist_node'):
        c.selectPosition(self.hoist_node)
        c.hoist()
    c.redraw()
</t>
<t tx="ekr.20170212105552.5">def setup_script(self):
    self.delete_widgets()
</t>
<t tx="ekr.20170212105552.6">def teardown(self):
    c = self.c
    self.delete_all_widgets()
    if self.delta &gt; 0:
        self.set_text_delta(-self.delta)
    if self.hoist_node:
        c.selectPosition(self.hoist_node)
        c.dehoist()
    c.redraw()
</t>
<t tx="ekr.20170212105552.7">def teardown_script(self):
    if self.auto_run:
        # Default wait.
        self.wait(0.5)
</t>
<t tx="ekr.20170212105552.8">def main(c, demo, script_name, auto_run=False, hoist_node=None):
    g.cls()
    k = c.k
    class_name = demo.__class__.__name__
    c.frame.log.clearTab('Log')
    g.es_print('Starting', class_name)
    k.demoNextKey = k.strokeFromSetting('Ctrl-9')
        # Tell k.masterKeyHandler to process Ctrl-9 immediately.
        # Binding demo-next in a setting does *not* work.
    h = '@button %s @key=Ctrl-9' % class_name
    p = g.findNodeAnywhere(c, h)
    assert p, h
    script_tree = g.findNodeInTree(c, p, script_name)
    assert script_tree, repr(script_name)
    demo.hoist_node = hoist_node and g.findNodeInTree(c, p, hoist_node)
    demo.start(script_tree, auto_run=auto_run)
</t>
<t tx="ekr.20170212105552.9"></t>
<t tx="ekr.20170225023738.1">'''
Web development involves endless reloading of pages.  It gets old really fast.

This @button allows me to fiddle with the .css and javascript for mod_http
itself without having to reload Leo. Oh so useful. I can just change the
settings in myLeoSettings and hit Ctrl-p to re-execute the button. 

This trick doesn't work when changing mod_http.py itself, but most of
the work involved the css and javascript.

This pattern could be useful to other web developers.
'''

# g.cls()
if c.isChanged():
    c.save()
# *Always* use the most current settings.
g.app.pluginsController.loadOnePlugin(
    'leo.plugins.mod_http',
    tag='open0',
    verbose=False)
g.app.loadManager.readGlobalSettingsFiles()
g.handleUrl('http://127.0.0.1:8130/leoPlugins.leo')
</t>
<t tx="ekr.20170301014329.1">'''Cycle syntax coloring when there are multiple @langauge directives in a node.'''
# Original by Terry Brown, Revised by EKR.
while not p.isRoot():
    if p.b.strip().startswith("@language "):
        lines = g.splitLines(p.b)
        words = lines[0].split()
        # Careful: don't treat comments as languages.
        if len(words) &gt; 2 and words[2][0].isalpha():
            # Cycle the languages on line 1.
            line0 = '%s %s %s\n' % (words[0], ' '.join(words[2:]), words[1])
            p.b = line0 + ''.join(lines[1:])
            c.selectVisBack()
            c.selectVisNext()
            break
    p.moveToParent()
else:
    g.es("No ambiguous @language directive found")
</t>
<t tx="ekr.20170301022829.1"># Please don't ignore these :-)
</t>
<t tx="ekr.20170301023612.1">'''
    insert / update headlines as comments in @nosent R code
    https://gist.github.com/tbnorth/eb913fcab82f6a4b37734b5156543308
'''
# By Terry Brown
headlines = []
for nd in p.self_and_subtree():
    if nd.h and nd.h[0] == '@' or nd.b and nd.b[0] == '@':
        continue
    headlines.append(nd.h)
    lines = nd.b.split('\n')
    if lines and lines[0].startswith('### '):
        del lines[0]
    if lines and lines[0].strip():
        lines[0: 0] = [""]
    lines[0: 0] = [
        "### %s %s" % (nd.h, "#" * (80 - len(nd.h) - 5)),
    ]
    if '.coffee' in p.h:
        lines[0: 0] = [""]
    if lines[-1].strip():
        lines.append("")
    if lines[-2].strip():
        lines.append("")
    b = '\n'.join(lines)
    if nd.b != b:
        nd.b = b
g.es('\n'.join(headlines))
c.redraw()
</t>
<t tx="ekr.20181019042544.1"># Replace underscore by - in settings names.
# See https://github.com/leo-editor/leo-editor/issues/993
g.cls()
import re
kinds = (
    'Bool', 'Color', 'Data',
    'Directory', 'Encoding', # Not found.
    'Float', 'Int',
    'Path', 'Ratio', # Not found.
    'String',
)
for kind in kinds:
    print('===== %s =====\n' % kind)
    pat = re.compile(r"get%s\s*\([\'\"]([\w_]+)[\'\"][,)]" % kind)
    for p in c.all_positions():
        s = p.b
        i, found = 0, False
        while True:
            m = pat.search(s[i:])
            if not m:
                break
            start, end = m.start(1), m.end(1)
            all = s[i+m.start(0):i+m.end(0)]
            word = s[i+start:i+end]
            if '_' in word:
                found = True
                print('')
                print('-----', p.h)
                print(all)
                s = s[:i+start] + word.replace('_','-') + s[i+end:]
                print(s[i+m.start(0):i+m.end(0)])
            i += end
        if found:
            assert p.b != s, p.h
            # print(s)
            p.b = s
            p.v.setDirty()
c.setChanged()
c.redraw()     
</t>
<t tx="ekr.20181019042717.1"></t>
<t tx="ekr.20181019042808.1">'''Check the consistency of all settings.'''
# https://github.com/leo-editor/leo-editor/issues/993

class Controller:
    @others

Controller(c).run()
</t>
<t tx="ekr.20181019042808.10">def scan_for_configs(self, p, d):
    '''
    Scan the body text of p, finding all calls to config.getX.
    
    This code does not know about `if 0`, but does know about comments.
    '''
    kinds = (
        'getBool', 'getColor', 'getInt', 'getFloat',
        # '@font', # special case.
        'getPath', 'getRatio', 'getString',
    )
    i, s = 0, p.b
    while i &lt; len(s):
        progress = i
        ch = s[i]
        if (
            ch == '@' and
            (g.match(s, i, '@ ') or g.match(s, i, '@\n')) and
            (i == 0 or s[i - 1] == '\n')
        ):
            # Skip the @doc part.
            i = s.find('\n@c', i)
            if i == -1: break
        elif ch == '#':
            i = g.skip_to_end_of_line(s, i)
        elif ch in ('"', "'"):
            i = g.skip_python_string(s, i, verbose=False)
        elif ch == '_' or ch.isalpha():
            j = g.skip_id(s, i)
            kind = s[i: j]
            if kind in kinds:
                # We have found a call to getBool, etc.
                i = g.skip_ws(s, j)
                if g.match(s, i, '('):
                    i = g.skip_ws(s, i + 1)
                    if g.match(s, i, '"') or g.match(s, i, "'"):
                        j = g.skip_string(s, i)
                        name = s[i + 1: j - 1]
                        aList = d.get(kind, [])
                        if name not in aList:
                            aList.append(name)
                        d[kind] = aList
                else:
                    j = i
            i = j
        else:
            i += 1
        assert progress &lt; i
    return d
</t>
<t tx="ekr.20181019042808.11">def get_settings(self):
    '''Return a dict containing a representation
    of all settings in leoSettings.leo.
    '''
    trace = False
    c, d = self.settings, {}
    print('scanning: %s' % c.shortFileName())
    settings_node = g.findNodeAnywhere(c, '@settings')
    if not settings_node:
        return self.error('no @settings node')
    for p in settings_node.subtree():
        if self.is_setting(p):
            kind, name = self.parse_setting(p)
            if name:
                # name = self.munge(name)
                aList = d.get(kind, [])
                if name not in aList:
                    aList.append(name)
                d[kind] = aList
            else:
                self.error('no name for %s' % (kind))
    if trace:
        keys = list(d.keys())
        for key in sorted(keys):
            print(key)
            aList = d.get(key)
            for name in sorted(aList):
                print('  ' + name)
    return d
</t>
<t tx="ekr.20181019042808.12">def is_setting(self, p):
    # For now, these are enough
    table = (
        '@bool', '@color', '@int', '@float',
        # '@font', # special case.
        '@ratio', '@path', '@string',
    )
    for s in table:
        if g.match_word(p.h, 0, s):
            return True
    return False
</t>
<t tx="ekr.20181019042808.13">def parse_setting(self, p):
    s = p.h
    assert s[0] == '@'
    i = g.skip_id(s, 1)
    kind = s[: i]
    assert kind
    i = g.skip_ws(s, i)
    j = g.skip_id(s, i, chars='-')
    name = s[i: j]
    return kind, name
</t>
<t tx="ekr.20181019042808.14">def munge(self, s):
    '''Return the canonicalized name for settings and arguments to c.config.getX.'''
    return g.toUnicode(s.replace('-', '').replace('_', '').lower())
</t>
<t tx="ekr.20181019042808.15">def run(self):
    g.cls()
    self.get_commanders()
    configs = self.get_configs()
    settings = self.get_settings()
    if self.errors == 0:
        self.check(configs, settings)
    g.trace('done')
</t>
<t tx="ekr.20181019042808.2">def __init__(self, c):
    self.c = c
    self.errors = 0
    # Commanders...
    self.core = None # leoPy.leo.
    self.plugins = None # leoPlugins.leo.
    self.settings = None # leoSettings.leo.
</t>
<t tx="ekr.20181019042808.3">def check(self, configs_d, settings_d):
    munge = self.munge
    table = ('Bool', 'Int', 'Float', 'Ratio', 'Path', 'String',) # 'Color', 'Font',
    #
    # Print missing user settings...
    for kind in table:
        config_key = 'get%s' % kind
        settings_key = '@%s' % kind.lower()
        configs = configs_d.get(config_key, [])
        settings = settings_d.get(settings_key, [])
        m_configs = [munge(z) for z in configs]
        m_settings = [munge(z) for z in settings]
        missing = set([z for z in m_configs if not z in m_settings])
        aList = [z for z in missing if self.filter_user_config(z)]
        if aList:
            print('\nmissing %s %s settings...\n' % (len(aList), settings_key))
            for z in sorted(aList):
                aList2 = [z2 for z2 in configs if munge(z2) == munge(z)]
                g.printObj(aList2)
    #
    # Print missing calls to c.config.getX...
    for kind in table:
        config_key = 'get%s' % kind
        settings_key = '@%s' % kind.lower()
        configs = configs_d.get(config_key, [])
        settings = settings_d.get(settings_key, [])
        m_configs = [munge(z) for z in configs]
        m_settings = [munge(z) for z in settings]
        missing = set([z for z in m_settings if not z in m_configs])
        aList = [z for z in missing if self.filter_get_x(z)]
        if aList:
            print('\nmissing %s config.%s calls...\n' % (len(aList), config_key))
            for z in sorted(aList):
                aList2 = [z2 for z2 in settings if munge(z2) == munge(z)]
                g.printObj(aList2)
</t>
<t tx="ekr.20181019042808.4">def filter_get_x(self, setting):
    '''
    Return False if we can safely ignore a missing call to config.get(setting).
    
    Everything here is a hack. Some are bigger than others.
    '''
    munge = self.munge
    #
    # These *ivars* are set by the GlobalConfigManager class.
    # There *should* be settings for all of these, despite missing config.get calls.
    table = (
        # encodingIvarsDict...
            "default_at_auto_file_encoding",
            "default_derived_file_encoding",
            "new_leo_file_encoding",
        # defaultsDict...
            "color_directives_in_plain_text",
            "output_doc_chunks",
            "page_width",
            "tab_width",
            "tangle_outputs_header",
            "target_language",
            "underline_undefined_section_names",
        # ivarsDict
            "at_root_bodies_start_in_doc_mode",
            "create_nonexistent_directories",
            "output_initial_comment",
            "output_newline",
            "page_width",
            "read_only",
            "redirect_execute_script_output_to_log_pane",
            "relative_path_base_directory",
            "remove_sentinels_extension",
            "save_clears_undo_buffer",
            "stylesheet",
            "tab_width",
            "target_language",
            "trailing_body_newlines",
            "use_plugins",
            "undo_granularity",
            "write_strips_blank_lines",
    )
    table = [munge(z) for z in table]
    if setting in table:
        return False
    #
    # unitTest.leo tests test-darwin-setting and test-win32-setting
    if setting in ('testdarwinsetting', 'testwin32setting'):
        return False
    #
    # Stylesheets use these settings.
    for pattern in (
        'bg', 'border', 'color', 'fg', 'font',
        'leogreen', 'leoyello',
        'margin', 'padding', 'relief',
        'solarized', 'split-bar', 'text-foreground', 'tree-image',
    ):
        if setting.find(munge(pattern)) &gt; -1:
            return False
    #
    # These plugins/use settings in non-standard ways.
    if setting.startswith(
        ('bookmarks', 'datenodes', 'http', 'opml',
        'rst3', 'todo', 'vim', 'zodb'),
    ):
        return False
    #
    # Find settings are defined in non-standard ways.
    for pattern in (
        'batch', 'change-text', 'find-text', 'ignore-case',
        'mark-changes', 'mark-finds', 'node-only', 'pattern-match',
        'reverse', 'search-body', 'search-headline', 'suboutline-only',
        'whole-word', 'wrap',
    ):
        if setting == munge(pattern):
            return False
    #
    # Issue a warning.
    return True
</t>
<t tx="ekr.20181019042808.5">def filter_user_config(self, setting):
    '''
    Return False if we can safely ignore a setting that does not exist in leoSettings.leo.
    
    Everything here is a hack. Some are bigger than others.
    '''
    munge = self.munge
    #
    # unitTest.leo tests test-darwin-setting and test-win32-setting
    if setting in ('testdarwinsetting', 'testwin32setting'):
        return False
    #
    # The calls to config.get* are commented out in the code,
    # but get_configs isn't smart enough to know that.
    for ignore in (
        'auto-set-ignore-case',
        'find-def-creates-clones',
        'qt-rich-text-zoom-in',
        'theme-name',
        'pytest-path', # In a (disabled) @button node
        'leo-to-html-%s', # Loads multiple settings from an .ini file.
    ):
        if setting == munge(ignore):
            return False
    #
    # Stylesheets use these settings.
    # It would be a major project to discover what settings
    # are actually used in the present stylesheet.
    for pattern in (
        'bg', 'border', 'color', 'fg', 'font',
        'leogreen', 'leoyello',
        'margin', 'padding', 'relief',
        'solarized', 'split-bar', 'text-foreground', 'tree-image',
    ):
        if setting.find(munge(pattern)) &gt; -1:
            return False
    #
    # These plugins use settings in non-standard ways.
    if setting.startswith(
        ('activepath', 'bookmarks', 'datenodes', 'http', 'opml',
        'rst3', 'todo', 'vim', 'zodb'),
    ):
        return False
    #
    # Find settings are defined in non-standard ways.
    for pattern in (
        'batch', 'change-text', 'find-text', 'ignore-case',
        'mark-changes', 'mark-finds', 'node-only', 'pattern-match',
        'reverse', 'search-body', 'search-headline', 'suboutline-only',
        'whole-word', 'wrap',
    ):
        if setting == munge(pattern):
            return False
    #
    # Tangle/untagle settings are deprecated and imo should not exist.
    for pattern in (
        'output-doc-flag', 'tangle-batch-flag',
        'untangle-batch-flag', 'use-header-flag',
    ):
        if setting == munge(pattern):
            return False
    #
    # Issue a warning.
    return True
</t>
<t tx="ekr.20181019042808.6">def error(self, s):
    print(s)
    self.errors += 1
</t>
<t tx="ekr.20181019042808.7">def get_commanders(self):
    '''Open files as needed and set the commander ivars.'''

    def open_commander(fn):
        c = g.openWithFileName(fn, old_c=self.c, gui=g.app.nullGui)
        if not c:
            self.error('not found: %s' % fn)
        return c

    join, loadDir = g.os_path_join, g.app.loadDir
    self.core = open_commander(join(loadDir, '..', 'core', 'leoPy.leo'))
        # Opening LeoPyRef.leo would be slower.
    self.plugins = open_commander(join(loadDir, '..', 'plugins', 'leoPlugins.leo'))
    self.settings = open_commander(join(loadDir, '..', 'config', 'leoSettings.leo'))
</t>
<t tx="ekr.20181019042808.8">def get_configs(self):
    '''
    Return a dict containing a representation of all calls to x.config.getX.
    '''
    d = {}
    for c in (self.core, self.plugins):
        print('scanning: %s' % c.shortFileName())
        self.get_configs_from_outline(c, d)
    return d
</t>
<t tx="ekr.20181019042808.9">def get_configs_from_outline(self, c, d):
    '''
    Scan the outline for all calls to x.config.getX and add items to d.
    '''
    for p in c.all_positions():
        self.scan_for_configs(p, d)
    return d
</t>
<t tx="ekr.20181019042834.1">'''Check that all commands mentioned in the @menus tree actually exist.'''

def checkMenu (p):
    for p2 in p.subtree_iter():
        if p2.h.startswith('@item'):
            checkItem(p.h,p2)

def checkItem (menuName,p):
    h = p.h[len('@item'):].replace('&amp;','').replace('*','').strip()
    if h != '-' and h not in c.commandsDict:
        print ('command not found: %s: %s' % (menuName,p.h))

menus = g.findNodeAnywhere(c,'@menus')
assert menus, 'no @menus tree'
for p in menus.subtree_iter():
    if p.h.startswith('@menu'):
        checkMenu(p.copy())

print ('done')
</t>
<t tx="ekr.20181019042907.1">'''Check for commands that exist in some but not all key- binding sets.'''
g.cls()
import leo.core.leoConfig as leoConfig
# import leo.core.leoTest as leoTest
parser = leoConfig.SettingsTreeParser(c)
# Add these as required to handle commands defined by plugins.
optionalCommandPrefixes = ['group-operations']
optionalCommandNames = [
    # These are the command names as defined by plugins.
    # LeoSlideShows.leo defines buttons whose commands do not end in '-command'.
    'next-slide-command','next-slide-show-command',
    'prev-slide-command','prev-slide-show-command',
    # The ipython plugin.
    'start-ipython','get-ipython-results','push-to-ipython',
    # The viewrendered plugin.
    'vr-toggle',
]
setNames = []
setsDict = {} # keys are set names, values are dicts of command names.
shortcutsDict = {}
@others
main()
</t>
<t tx="ekr.20181019042907.2">def defineSetNames():
    global setNames
    if 1:
        setNames = []
        for p in c.allNodes_iter():
            h = p.headString()
            if h.startswith('@keys'):
                h = h[5:].strip()
                if h not in setNames:
                    setNames.append(h)
        g.es('Found these sets...')
        for setName in setNames:
            g.es_print('  %s' % str(setName))
    else:
        setNames = [
            'Default Emacs shortcuts',
            'Legacy Leo bindings',
            'Legacy Leo shortcuts with important Emacs bindings',
            'No bindings',
            'EKR bindings: a mix',
            'EKR bindings: Mode-oriented',
        ]

    setNames.sort()
</t>
<t tx="ekr.20181019042907.3">def doSet(p,name):

    global shortcutsDict

    shortcutsDict = {}

    for p in p.subtree_iter():
        if p.headString().startswith('@shortcuts'):
            doShortcuts(p,name)
</t>
<t tx="ekr.20181019042907.4">def doShortcuts(p,setName):

    global parser,setsDict,shortcutsDict

    d = setsDict.get(setName,{})
    s = p.bodyString()
    lines = g.splitLines(s)
    for line in lines:
        line = line.strip()
        if line and not line.startswith('#'):
            commandName,si = parser.parseShortcutLine('test',line)
            if not si:
                g.es_print('In %s:\nmissing "=" in shortcut line:%s' % (
                    p.headString(),repr(line)),color='red')
            else:
                # Having multiple shortcuts for a command if fine,
                # But having multiple commands for a shortcut is not.
                shortcut = si.stroke
                pane = si.pane
                if shortcut not in (None,'None','none'):
                    aList = shortcutsDict.get(shortcut,[])
                    if aList:
                        for commandName2,pane2 in aList:
                            if pane == pane2:
                                g.es_print('duplicate shortcut %s in set %s: previous command: %s' % (
                                    shortcut,setName,commandName2),color='red')
                        else:
                            aList.append((commandName,pane),)
                    else:
                        shortcutsDict [shortcut] = [(commandName,pane),]

                data = d.get(commandName)
                if data:
                    shortcut2,pane2 = data
                    if shortcut == shortcut2 and pane == pane2:
                        g.es_print('duplicate %s in set %s' % (
                            commandName,setName),color='red')
                else:
                    data = shortcut,pane
                    d[commandName] = data

    setsDict[setName] = d
</t>
<t tx="ekr.20181019042907.5">def checkSets():

    global setNames, optionalCommandPrefixes, optionalCommandNames
    # Compute the union of all the command names.
    allNames = {}
    for setName in setNames:
        d = setsDict.get(setName)
        if d:
            for key in d.keys():
                allNames[key] = key
        else:
            g.es_print('No setsDict for %s' % (repr(setName)),color='red')
    keys = sorted(allNames.keys())
    # Warn about missing names.
    for setName in ('No bindings',): # setNames:
        d = setsDict.get(setName)
        if d:
            for key in keys:
                if key not in ('none','None',None) and key not in d.keys():
                    # Don't warn about missing 'enter-xxx-mode' commands.
                    if (
                        not (key.startswith('enter-') and key.endswith('-mode')) and
                        not (key.startswith('press-') and key.endswith('-button'))
                    ):
                        g.es_print('%s is missing %-35s = None' % (setName,repr(key)))
        else:
            g.es_print("'@keys No bindings' not found",color='blue')
    # Warn about undefined commands.
    for key in keys:
        if not c.commandsDict.get(key):
            ok = False
            # full-command and quick command are weird special cases.
            if key not in ('None',None,'full-command','quick-command'):
                # Don't warn about missing 'enter-xxx-mode' commands.
                if key.startswith('enter-') and key.endswith('-mode'):
                    ok = True
                elif key.startswith('press-') and key.endswith('-button'):
                    ok = True
                for prefix in optionalCommandPrefixes:
                    if key.startswith(prefix):
                        ok = True
                for optionalCommand in optionalCommandNames:
                    if key == optionalCommand:
                        ok = True
                if not ok:
                    g.es_print('Undefined command name: %s' % (key))
</t>
<t tx="ekr.20181019042907.6">def main ():

    global setNames
    defineSetNames()

    g.es_print('-' * 40)
    seen = {}
    for p in c.allNodes_iter():
        h = p.headString()
        if h.startswith('@keys'):
            h = h[5:].strip()
            if not seen.get(h):
                seen[h] = True
                doSet(p,h)
    checkSets()
    g.es('Check Bindings done')
</t>
<t tx="ekr.20211020082938.1">"""
Display slides from a folder and its subfolders.

Prompts for a folder containing images, then displays the images.

Plain keys control the display of slides:
    
      space: show the next slide.
  backspace: show the previous slide.
     escape: end the slideshow
          =: zoom in
          -: zoom out
arrows keys: pan the slide
          d: prompt to move the slide to the trash
          h: show the help message
          m: move the file.

Default settings:

- background_color: black
- 60 seconds between slides.
- Start in full-screen mode.
- Display in random order.
- Allowed file extensions: ['.jpeg', '.jpg', '.png']

"""
import os
import pathlib
import random
import textwrap
from leo.core.leoQt import isQt5, QtCore, QtGui, QtWidgets

# Defaults
background_color = "black"
delay = 60  # seconds
extensions = ['.jpeg', '.jpg', '.png']  # Allowed file extensions.
full_screen = True
sort_kind = 'random'  # 'date', 'name', 'none', 'random', or 'size'
height = 900  # Window height (pixels) when not in full screen mode.
width = 1500  # Window width (pixels) when not un full screen mode.

@others

path = QtWidgets.QFileDialog().getExistingDirectory()
if path:
    files_list = get_files(path)
    if files_list:
        n = len(files_list)
        print(f"Found {n} picture{g.plural(n)} in {path}")
        w = Slides()
        w.run()
        print('done')
    else:
        print(f"No slides found in {path!r}")
</t>
<t tx="ekr.20211020082938.10">def quit(self):
    self.timer.stop()
    self.destroy()
</t>
<t tx="ekr.20211020082938.11">def run(self):
    global background_color, delay, full_screen, sort_kind
    w = self
    # Init ivars
    self.delay = delay
    self.full_screen = not full_screen  # So toggling below works.
    # Init the widget.
    w.make_widgets()
    # Center the widget
    qtRectangle = w.frameGeometry()
    centerPoint = QtWidgets.QDesktopWidget().availableGeometry().center()
    qtRectangle.moveCenter(centerPoint)
    w.move(qtRectangle.topLeft())
    # Show the widget.
    w.showNormal()
    if full_screen:
        w.toggle_full_screen()
    # Show the next slide.
    self.sort(sort_kind)
    w.next_slide()  # show_slide resets the timer.
</t>
<t tx="ekr.20211020082938.12">def make_widgets(self):
    
    global width, height
    w = self

    # Init the window's attributes.
    w.setStyleSheet(f"background: {background_color}")
    w.setGeometry(0, 0, width, height)  # The non-full-screen sizes.
    
    # Create the picture area.
    w.picture = QtWidgets.QLabel('picture', self)
    
    # Create the scroll area.
    w.scroll_area = area =QtWidgets.QScrollArea()
    area.setWidget(self.picture)
    AlignmentFlag = QtCore.Qt if isQt5 else QtCore.Qt.AlignmentFlag
    area.setAlignment(AlignmentFlag.AlignHCenter | AlignmentFlag.AlignVCenter)
    
    # Disable scrollbars.
    ScrollBarPolicy = QtCore.Qt if isQt5 else QtCore.Qt.ScrollBarPolicy
    area.setHorizontalScrollBarPolicy(ScrollBarPolicy.ScrollBarAlwaysOff)
    area.setVerticalScrollBarPolicy(ScrollBarPolicy.ScrollBarAlwaysOff)
    
    # Init the layout.
    layout = QtWidgets.QVBoxLayout()
    layout.addWidget(self.scroll_area)
    w.setLayout(layout)
</t>
<t tx="ekr.20211020082938.13">def show_help(self):
    """Show the help message."""
    print(textwrap.dedent('''\
                    d delete slide
                    f toggle full screen
                    h show help
         n or &lt;space&gt; show next slide
     p or &lt;backspace&gt; show previous slide
           q or &lt;esc&gt; end slideshow
                    + zoom in
                    - zoom out
             up arrow scroll up
           down arrow scroll down
           left arrow scroll left
          right arrow scroll right
    '''))
</t>
<t tx="ekr.20211020082938.14">def show_slide(self):
    # Reset the timer.
    self.timer.stop()
    self.timer.start(self.delay * 1000.0, self)
    # Get the file name.
    file_name = files_list[self.slide_number]
    # Change the title.
    self.setWindowTitle(file_name)
    # Display the picture.
    pixmap = QtGui.QPixmap(file_name)
    try:
        TransformationMode = QtCore.Qt if isQt5 else QtCore.Qt.TransformationMode
        image = pixmap.scaledToHeight(
            self.height() * self.scale,
            TransformationMode.SmoothTransformation,
        )
        self.picture.setPixmap(image)
        self.picture.adjustSize()
    except Exception:
        self.next_slide()
</t>
<t tx="ekr.20211020082938.15">def sort(self, sort_kind):
    """sort files_list based on sort_kind."""
    global files_list
    if sort_kind == 'date':
        print('Sorting by date...')
        files_list.sort(key = os.path.getmtime)
    elif sort_kind == 'name':
        print('Sorting by name...')
        files_list.sort()
    elif sort_kind in (None, 'none'):
        pass
    elif sort_kind == 'random':
        print('Randomizing...')
        random.shuffle(files_list)
    elif sort_kind == 'size':
        print('Sorting by size...')
        files_list.sort(key = os.path.getsize)
    else:
        g.trace(f"unknown sort kind: {sort_kind!r}")
</t>
<t tx="ekr.20211020082938.16">def timerEvent(self, e=None):
    self.next_slide()  # show_slide resets the timer.
</t>
<t tx="ekr.20211020082938.17">def toggle_full_screen(self):
    w = self
    if w.full_screen:
        w.full_screen = False
        w.picture.adjustSize()
        w.showNormal()
    else:
        w.full_screen = True
        WindowState = QtCore.Qt if isQt5 else QtCore.Qt.WindowState
        w.setWindowState(WindowState.WindowFullScreen)
        w.picture.setGeometry(0, 0, w.width(), w.height())
        w.picture.adjustSize()
</t>
<t tx="ekr.20211020082938.18">def zoom_in(self):
    self.scale = self.scale * 1.05
    self.show_slide()
    
def zoom_out(self):
    self.scale = self.scale * (1.0 / 1.05)
    self.show_slide()
</t>
<t tx="ekr.20211020082938.2">def get_files(path):
    """Return all files in path, including all subdirectories."""
    global extensions
    return [
        str(z) for z in pathlib.Path(path).rglob('*')
            if z.is_file()
            and os.path.splitext(str(z))[1].lower() in extensions
    ]
</t>
<t tx="ekr.20211020082938.3">class Slides(QtWidgets.QWidget):

    scale = 1.0
    slide_number = -1
    timer = QtCore.QBasicTimer()
    
    @others
</t>
<t tx="ekr.20211020082938.4">send_to_trash_warning_given = False

def delete(self):
    """Issue a prompt and delete the file if the user agrees."""
    try:
        from send2trash import send2trash
    except Exception:
        if not self.send_to_trash_warning_given:
            self.send_to_trash_warning_given = True
            print("Deleting files requires send2trash")
            print("pip install Send2Trash")
        return
    file_name = files_list[self.slide_number]
    result = g.app.gui.runAskYesNoDialog(c,
        title = "Delete File?",
        message = f"Delete file {g.shortFileName(file_name)}?"
    )
    if result == 'yes':
        # Move the file to the trash.
        send2trash(file_name)
        del files_list[self.slide_number]
        print(f"Deleted {file_name}")
        self.slide_number = max(0, self.slide_number - 1)
        self.next_slide()
</t>
<t tx="ekr.20211020082938.5">def keyPressEvent (self, event):
    
    i = event.key()
    s = event.text()
    mods = event.modifiers()
    if s == 'd':
        self.delete()
    elif s == 'f':
        self.toggle_full_screen()
    elif s == 'h':
        self.show_help()
    elif s == 'm':
        self.move_to()
    elif s == 'n' or i == 32:  # ' '
        self.next_slide()
    elif s == 'p' or s == '\b':
        self.prev_slide()
    elif s == 'q' or s == '\x1b':  # ESC.
        self.quit()
    elif s in '=+':
        self.zoom_in()
    elif s == '-':
        self.zoom_out()
    elif i == 16777235:
        self.move_up()
    elif i == 16777237:
        self.move_down()
    elif i == 16777234:
        self.move_left()
    elif i == 16777236:
        self.move_right()
    else:
        g.trace(repr(s), i, repr(mods))
    
</t>
<t tx="ekr.20211020082938.6">def move_down(self):
    self.scroll_area.scrollContentsBy(0, -200)

def move_left(self):
    self.scroll_area.scrollContentsBy(-200, 0)

def move_right(self):
    self.scroll_area.scrollContentsBy(200, 0)

def move_up(self):
    self.scroll_area.scrollContentsBy(0, 200)
</t>
<t tx="ekr.20211020082938.7">def move_to(self):
    """Issue a prompt and move the file if the user agrees."""
    file_name = files_list[self.slide_number]
    path = QtWidgets.QFileDialog().getExistingDirectory()
    if path:
        new_path = os.path.join(path, os.path.basename(file_name))
        if os.path.exists(new_path):
            # result = g.app.guirunAskYesNoDialog
            print("File exists:", new_path)
        else:
            pathlib.Path(file_name).rename(new_path)
            del files_list[self.slide_number]
            self.slide_number = max(0, self.slide_number - 1)
            self.next_slide()
</t>
<t tx="ekr.20211020082938.8">def next_slide(self):
    if self.slide_number + 1 &lt; len(files_list):
        self.slide_number += 1  # Don't wrap.
    self.scale = 1.0
    self.show_slide()
</t>
<t tx="ekr.20211020082938.9">def prev_slide(self):
    if self.slide_number &gt; 0: # Don't wrap.
        self.slide_number -= 1
    self.scale = 1.0
    self.show_slide()
</t>
<t tx="ekr.20220319162442.1">g.cls()

# Monkey-patched git-diff-pr command.
import leo.commands.editFileCommands as efc

rev1 = '7fe2cc486153'  # Preveious commit of devel'
rev2 = 'c4d109012028'  # Last commit of ekr-clean-comments
x = efc.GitDiffController(c)

@others

# Monkey-patch, with x bound.
x.make_diff_outlines = make_diff_outlines_ignoring_comments
x.diff_two_revs(rev1, rev2)
</t>
<t tx="ekr.20220319162442.2">def make_diff_outlines_ignoring_comments(c1, c2, fn, rev1='', rev2=''):
    """Create an outline-oriented diff from the *hidden* outlines c1 and c2."""
    self = x
    added, deleted, changed = self.compute_dicts(c1, c2)
    table = (
        (added, 'Added'),
        (deleted, 'Deleted'),
        (changed, 'Changed'))
    for d, kind in table:
        if kind.lower() == 'changed':
            for key in d:
                v1, v2 = d.get(key)
                v1.b = strip_comments(v1.b)
                v2.b = strip_comments(v2.b)
        self.create_compare_node(c1, c2, d, kind, rev1, rev2)
</t>
<t tx="ekr.20220319162442.3">def strip_comments(aString):
    """
    Strip everything that looks like a comment from aString.
    It's fine, for now, to ignore strings and docstrings.
    """
    result = []
    lines = g.splitLines(aString)
    for s in lines:
        if s.strip().startswith('#@'):
            # Retain everything that looks like a sentinel.
            result.append(s)
        else:
            # Strip the comment, ignoring the end of the line.
            i = s.find('#')
            if i == -1:
                result.append(s)
            else:
                tail = s[:i]
                if tail.strip():
                    result.append(tail.rstrip() + '\n')
    return ''.join(result)
</t>
<t tx="ekr.20220927115543.1"></t>
<t tx="ekr.20221105063835.1">"""
script: Insert text-based macros in python code.

- Add traces at start of each def.
- Add traces at end of each def.
- Add traces before each return.
"""
# This is prototype code. I have no plans to improve it.
g.cls()
import re
from typing import List
@others

target = g.findNodeInChildren(c, p, 'macro-test')
for p in target.self_and_subtree():
    add_macros(p)
print('Done!')
</t>
<t tx="ekr.20221105063835.2">def_pat = re.compile(r'^def\s+(\w+)\(.*\).*:')
ret_pat = re.compile(r'^\s*return\b(.*)')

def add_macros(p):
    if not p.b.strip():
        return
    result: List[str] = []  # List of lines, with added traces.
    indents: List[int] = []  # Indentation of def statements.
    names: List[str] = []  # Names of defs.
    returns: List[bool] = []  # True if def has any minimally indented returns.
    indent, last_indent = 0, 0
    for i, line in enumerate(g.splitLines(p.b.rstrip() + '\n')):
        s = line.lstrip()
        indent = len(line) - len(s)
        indent_s = indent * ' '
        if not s:
            result.append(line)
            continue  # Blank lines don't affect indentation.
        # End any previous def at this level.
        while indents and indent &lt; last_indent:
            last_indent = indents.pop()
            last_name = names.pop()
            last_return = returns.pop()
            if not last_return:
                indent_s = last_indent * ' '
                result.append(f"{indent_s}g.trace('end {last_name}')\n")
        def_m = def_pat.match(s)
        if def_m:
            # End any previous entry at this level.
            if indents and indents[-1] == indent:
                indents.pop()
                names.pop()
                returns.pop()
            # Enter this def.
            indents.append(indent)
            name = def_m.group(1)
            names.append(name)
            returns.append(False)
            last_indent = indent
            indent_s = (indent + 4) * ' '
            result.append(line)
            result.append(f"{indent_s}g.trace('enter: {name}')\n")
            continue
        ret_m = ret_pat.match(s)
        if ret_m:
            if indents:
                if indents[-1] + 4 == indent:
                    returns[-1] = True
                    # g.trace(f"Suppress end trace: {line!r}")
                val = ret_m.group(1).strip()
                result.append(f"{indent_s}g.trace('return {val}')\n")
            else:
                result.append(f"### Line: {i} Return outside of def. {p.h}\n")
        result.append(line)
    if indents:
        indent_s = (indents[-1] + 4) * ' '
        name = names[-1]
        if not returns[-1]:
            result.append(f"{indent_s}g.trace('end {name}')\n")
    g.printObj(result, tag=f"{p.h}:")
</t>
<t tx="ekr.20221105063835.3"></t>
<t tx="ekr.20221105063835.4">def test_1():
    return 'a'
    
def test_2():
    return 'b'
    
def test_3():
    if 1:
        return 1
    else:
        return 2
</t>
<t tx="ekr.20221105063835.5">def spam():
    print("spam")
</t>
<t tx="ekr.20221105063835.6">def eggs(a=None):
    print('eggs')
</t>
<t tx="ekr.20230421070847.1">"""Replace default kwargs in all leo/modes files"""

g.cls()
import glob
import os
import re

bool_kwargs = (
    'at_line_start', 'at_whitespace_end', 'at_word_start',
    'exclude_match',
    'no_escape', 'no_line_break', 'no_word_break',
)

patterns = []
for bool_arg in bool_kwargs:
    # Remove kwargs inited to False.
    pattern1 = re.compile(fr"{bool_arg}=False,")
    patterns.append((pattern1, ''))
    pattern2 = re.compile(fr"{bool_arg}=False\)")
    patterns.append((pattern2, ')'))

delegate_patterns = (
    # Remove kwargs inited to "".
    (re.compile(r'delegate="",'), ''),
    (re.compile(r'delegate=""\)'), ')'),
)
cleanup_patterns = [
    # Remove blank lines between trailing comma and ')'.
    (re.compile(r',[ ]*(\n[ ]*)*\)', re.MULTILINE), ')'),
]
for bool_arg in bool_kwargs:
    # Remove blank lines between trailing comma and any bool kwarg.
    pattern3 = re.compile(fr",[ ]*(\n[ ]*)*{bool_arg}")
    cleanup_patterns.append((pattern3, fr",\n{' '*10}{bool_arg}"))   

for aTuple in delegate_patterns:
    patterns.append(aTuple)
for aTuple in cleanup_patterns:
    patterns.append(aTuple)
    
# Exclude these files: @file nodes define them.
exclude = (
    'batch.py', 'forth.py', 'html.py',
    'javascript.py', 'julia.py', 'python.py',
)
modes = os.path.normpath(os.path.join(g.app.loadDir, '..', 'modes'))
paths = glob.glob(f"{modes}{os.sep}*.py")
paths = [z for z in paths if not any(z2 in z for z2 in exclude)]
# g.printObj([g.shortFileName(z) for z in paths])

paths = paths[:1]  ###
for path in paths:  ###
    with open(path, 'r') as f:
        contents = f.read()
    for pattern, repl in patterns:
        contents =  re.sub(pattern, repl, contents)
    lines = [z.rstrip() + '\n' for z in g.splitLines(contents)]
    contents = ''.join(lines)
    lines = g.splitLines(contents)
    if 0:
        g.printObj(lines, tag=path)
    if 0:
        g.printObj(list(f"{i+660:3} {z}" for i, z in enumerate(lines[660:])))
    if 0:
        contents = contents.replace('  ', ' ')
        lines = [f"{i:3} {z}" for i, z in enumerate(g.splitLines(contents))
            if ('colorer.match' in z
                or ',\n\n' in z
                or any(z2 in z for z2 in bool_kwargs)
            )]
        g.printObj(lines, tag=path)
    with open(path, 'w') as f:
        f.write(contents)
g.es_print('Done!')
</t>
<t tx="ekr.20230925012359.1">g.cls()

@others  # Define helpers
        
total, total_square, total_jazz = 0, 0, 0

for group in range(5):

    n8 = group * 2
    n4 = compute_quarters(n8)
    print('')
    print(f"===== Group {group + 1} =====: {n8} eighth notes, {n4} quarter notes...")
    print('')

    results = find_patterns(n8)
    converted = convert_patterns(results)
    assert len(converted) == len(results)
    
    square_patterns = [z for z in results if is_square(z)]
    jazz_patterns = [z for z in results if not is_square(z)]
    
    total += len(results)
    total_square += len(square_patterns)
    total_jazz += len(jazz_patterns)

    print(
        f"{len(results)} total rhythm{g.plural(len(results))}. "
        f"{len(square_patterns)} square, {len(jazz_patterns)} jazz")

    square_converted = [convert_pattern(z) for z in square_patterns]
    jazz_converted = [convert_pattern(z) for z in jazz_patterns]

    print('\nSquare:\n')
    for i, z in enumerate(square_converted):
        print(f"{i + 1:2} {z!s}")
    
    if jazz_patterns:
        print('\nJazz:\n')
        for i, z in enumerate(jazz_converted):
            print(f"{i + 1:2} {z!s}")
            
print(f"\nGrand totals: {total} patterns, {total_square} square, {total_jazz} jazz")
</t>
<t tx="ekr.20230925012359.2">def compute_quarters(n8) -&gt; int:
    """
    Return the quarter notes in the measure containing the given number of
    eighth notes.
    """
    assert (n8 % 2) == 0, (n8, (n8 % 2))
    return int(4 - n8/2)
</t>
<t tx="ekr.20230925012359.3">def convert_pattern(pattern:str) -&gt; str:
    """Convert a string of 1s and 0s to a string of dots and dashes"""
    return pattern.replace('1', '.').replace('0', ' _')

def convert_patterns(patterns: list[int]) -&gt; list[str]:
    return [convert_pattern(pattern) for pattern in patterns]
</t>
<t tx="ekr.20230925012359.4">def find_patterns(n8: int) -&gt; list[str]:
    """
    Given a desired number of eighth notes in a 4-beat bar, return the
    padded string representation of all the base-2 numbers (in descending
    order) representing rhythms contain the given number of eighth notes.
    
    As always in this program, 1s represent an eighth note, 0s represent
    quarter notes.
    """
    # Special case.
    if n8 == 0:
        return ['0000']

    # Compute a string with n8 leading 1s and n_zeros 
    n_zeros = compute_quarters(n8)
    n_s = '1' * n8 + '0' * n_zeros
    width = n8 + n_zeros
    
    # Convert the string to a base-2 number.
    n = int(n_s, 2)
    
    # Count down, accepting only numbers with the desired number of 1s.
    results = []
    while n &gt; 0:
        s = bin(n)[2:]
        if s.count('1') == n8:
            pad = '0' * (width - len(s))
            results.append(pad + s)
        n -= 1
    return results
</t>
<t tx="ekr.20230925012359.5">def is_square(s) -&gt; bool:
    """
    Return True if s represents a square rhythm.
    """
    # Remove square patterns (0 or 11) from the left.
    # s represents a jazz pattern if the remainder ever contains 01.
    digits = list(s)
    while digits:
        if digits[0] == '0':
            digits = digits[1:]
            continue
        tail = ''.join(digits[0:2])
        # if tail in ('1', '01'):
        if tail == '10':
            return False
        digits = digits[2:]
    return True
</t>
<t tx="ekr.20230925012840.1"></t>
<t tx="ekr.20231008041100.1">"""
Convert a tree of nodes to a GitHub-formatted string.

This script assumes only the top-level node contains @others.
"""

# Note: There is an @others in the docstring! That's why we must use guide lines!

g.cls()
import re

if 1:  # Convert *this* script: @button to-github.
    source_h = '@button to-github'
    target_h = 'Flat to-github'
else:  # Convert the original script.
    source_h = '@button create-rst'
    target_h = 'Flat create-rst'

# Find/create nodes.
source_p = g.findNodeAnywhere(c, source_h)
assert source_p
target_p = g.findTopLevelNode(c, target_h)
if not target_p:
    target_p = c.lastTopLevel().insertAfter()
    target_p.h = target_h
    
@others  # Define functions.
    
put_all_nodes(source_p, target_p)
c.redraw()
print('Done')

@language python
@tabwidth -4
</t>
<t tx="ekr.20231008041100.2">def put_all_nodes(source_p, target_p):
    """
    Put all nodes, expanding @others only in the top-level node.
    """
    # Leo note: guide lines contain no comments, strings or docstrings.
    #           Using guide lines prevents false matches of @others!
    lines = g.splitLines(source_p.b)
    guide_lines = delete_comments_and_strings(lines)
    assert len(lines) == len(guide_lines)
    target_lines = [
        '```python\n\n',
        f"# {source_p.h}\n\n",
    ]
    for i, line in enumerate(lines):
        guide_line = guide_lines[i]
        target_lines.append(line if line.strip() else '\n')
        if '@others' in guide_line:
            target_lines.append('\n')
            for child_p in source_p.subtree():
                if child_p.h.strip().startswith('&lt;&lt;'):
                    target_lines.append(f"# {child_p.h}\n")
                target_lines.extend(g.splitLines(child_p.b + '\n'))
    # Finish.
    target_lines.append('\n```\n')
    target_p.b = ''.join(target_lines)
</t>
<t tx="ekr.20231008041100.3"># Copied from leo.plugins.importers.python.
string_pat1 = re.compile(r'([fFrR]*)("""|")')
string_pat2 = re.compile(r"([fFrR]*)('''|')")

def delete_comments_and_strings(lines: list[str]) -&gt; list[str]:
    """
    Python_i.delete_comments_and_strings.

    This method handles f-strings properly.
    """

    def skip_string(delim: str, i: int, line: str) -&gt; tuple[str, int]:
        """
        Skip the remainder of a string.

        String ends:      return ('', i)
        String continues: return (delim, len(line))
        """
        if delim not in line:
            return delim, len(line)
        delim_pat = re.compile(delim)
        while i &lt; len(line):
            ch = line[i]
            if ch == '\\':
                i += 2
                continue
            if delim_pat.match(line, i):
                return '', i + len(delim)
            i += 1
        return delim, i

    delim: str = ''  # The open string delim.
    result: list[str] = []
    for line_i, line in enumerate(lines):
        i, result_line = 0, []
        while i &lt; len(line):
            if delim:
                delim, i = skip_string(delim, i, line)
                continue
            ch = line[i]
            if ch in '#\n':
                break
            m = string_pat1.match(line, i) or string_pat2.match(line, i)
            if m:
                # Start skipping the string.
                prefix, delim = m.group(1), m.group(2)
                i += len(prefix)
                i += len(delim)
                if i &lt; len(line):
                    delim, i = skip_string(delim, i, line)
            else:
                result_line.append(ch)
                i += 1

        # End the line and append it to the result.
        if line.endswith('\n'):
            result_line.append('\n')
        result.append(''.join(result_line))
    assert len(result) == len(lines)  # A crucial invariant.
    return result
</t>
<t tx="ekr.20231217050920.1">@language rest
@wrap

Here's an example of how they can be used.

You have a node and you want to have some images that you can display with, say, VR3.

You want to put them into an images subdir, but you have to navigate there first.  How can you do that?

The show-current-dir button opens the system's file manager/Windows Explorer window opens on that directory.
Now you can copy those image files to the images subdirectory.

@language python
</t>
<t tx="ekr.20231217050920.2">@language python
"""Open directory for a node, respecting any @path directives and "~"."""

from pathlib import Path
from subprocess import run

pth = Path(c.fullPath(p))
if pth.exists():
    if pth.is_file():
        direc = pth.parent
    else:
        direc = pth
else:
    direc = Path(g.scanAllAtPathDirectives(c, p))

if direc:
    normdir = str(direc.resolve())
    term = 'explorer.exe' if g.isWindows else 'xdg-open'
    run([term, direc])

else:
    g.es(f"Path {direc} doesn't exist", color='red')
</t>
<t tx="ekr.20231217050920.3">@language python
"""Open command window at current directory. Expands "~"."""

from pathlib import Path
from subprocess import run

pth = Path(c.fullPath(p))
if pth.exists():
    if pth.is_file():
        direc = pth.parent
    else:
        direc = pth
else:
    direc = Path(g.scanAllAtPathDirectives(c, p))

if direc:
    normdir = str(direc.resolve())
    if g.isWindows:
        cmd = f'start cmd.exe /K "cd {direc}"'
        run(cmd, shell=True)
    else:
        cmd = 'x-terminal-emulator'
        run(cmd, cwd=direc)
else:
    g.es(f"Path {direc} doesn't exist", color='red')</t>
<t tx="ekr.20231217050920.4">@language python
"""Copy the effective path of the current node to the clipboard.

This command honors @path directives.
"""
from pathlib import Path

pth = Path(c.fullPath(p))
if pth.exists():
    if pth.is_file():
        direc = pth.parent
    else:
        direc = pth
else:
    direc = Path(g.scanAllAtPathDirectives(c, p))

if direc:
    normdir = str(direc.resolve())
    g.app.gui.replaceClipboardWith(normdir)
else:
    g.es(f"Path {direc} doesn't exist")

</t>
<t tx="ekr.20241117081230.1">"""
This script has been mostly obsoleted by TestSyntax.slow_test_all_mode_files.

It summarizes the rules in mode files.
"""

g.cls()
g.cls()

import glob
import os
import re

join = g.os_path_finalize_join
finalize = g.os_path_finalize

@others

all = True

languages = (
    '/actionscript.py',
    # '/javascript.py',
    # '/md.py',
    # '/python.py',
    # '/html.py',
    # '/xml.py',
)
mode_path = join(g.app.loadDir, '..', 'modes')
paths = glob.glob(f"{mode_path}{os.sep}*.py")
paths = [finalize(z) for z in paths if not z.startswith('__')]

summary = set()
for path in paths:
    if all or path.endswith(languages):
        scan(path, summary)
# Print the cumulative results.
lines = sorted(list(summary))
for i, s in enumerate(lines):
    print(f"{i:2} {s.rstrip()}")
</t>
<t tx="ekr.20241117081901.1">def scan(path: str, summary: set) -&gt; None:
    matchers: dict[str, set[str]] = {}
    fn = os.path.basename(path)
    with open(path, 'r') as f:
        contents = f.read()
    if 0:
        print('')
        g.trace(f"contents: {len(contents):&gt;6} {fn} ")
        print('')
    lines = g.splitLines(contents)
    for i, line in enumerate(lines):
        if line.startswith('def '):
            scan_def(i, fn, lines, matchers)
            
    if 0:  # Print per-file results.
        for fn, result_set in matchers.items():
            g.printObj(list(sorted(list(result_set))), tag=fn)
    if 1:  # Add the result_set to the global summary.
        for fn, result_set in matchers.items():
            lines = list(sorted(list(result_set)))
            # g.printObj(lines, tag=fn)
            for line in lines:
                line = delete_cruft(line)
                if line:
                    # print(line)
                    summary.add(line)
</t>
<t tx="ekr.20241117082739.1"># matcher_pat = re.compile(r'return colorer\.(match_.*?)\s*\(')

def scan_def(
    i: int,
    fn: str,
    lines: list[str],
    matchers: dict[str, set[str]],
) -&gt; None:
    """
    Print the definition starting at lines[i].
    
    Add all calls to matchers to the matchers_dict
    """
    show_line(i, lines[i])
    if 0:  # Brief
        i += 1
        add_matcher(fn, i, i+1, lines, matchers)
    else:
        i += 1
        i1 = i
        while i &lt; len(lines) and not lines[i].startswith('def '):
            show_line(i, lines[i])
            i += 1
        add_matcher(fn, i1, i, lines, matchers)
</t>
<t tx="ekr.20241117125038.1">def filter_line(line: str) -&gt; bool:
    """
    Return True if s is neither empty, nor a comment nor a dictionary
    entry.
    """
    s = line.strip()
    # This is mostly a hack, but it gets the job done.
    return s and not s.startswith((
        '#', '"', "'", '}',
        'importDict', 'rulesDict'
    ))
</t>
<t tx="ekr.20241117125039.1">def add_matcher(fn: str, i: int, j: int,
    lines: list[str],
    matchers: dict[str, set[str]],
) -&gt; None:
    """Add matchers to the matchers dict"""
    filtered_lines = [z for z in lines[i: j] if filter_line(z)]
    result_lines = [
        z.replace('\n', ' ').replace('  ', ' ').lstrip()
            for z in filtered_lines
    ]
    result_lines = [delete_strings(z) for z in result_lines]
    result_lines = [z.rstrip() for z in result_lines if z.strip()]
    a_set = matchers.get(fn, set())
    result = ''.join(result_lines)
    a_set.add(result)
    matchers [fn] = a_set

</t>
<t tx="ekr.20241117125039.2">trace = False

def show_line(i: int, line: str) -&gt; None:
    if trace and filter_line(line):
        print(f"{i:4}: {line.rstrip()}")</t>
<t tx="ekr.20241117131431.1">def delete_strings(s: str) -&gt; str:
    """
    Replace all string kwargs by ""
    """
    i, result = 0, s
    has_r = '=r"' in s
    trace = False and has_r
    if trace:
        g.trace('===== Entry', s)
    while True:
        progress = i
        if trace:
            g.trace('Loop:', i, len(result), result)
            g.trace('Loop: TAIL:', result[i:])   ###
        for target in ('="', '=r"'):
            i2 = result.find(target, i)
            if i2 &gt; -1:
                i = i2
                j = result.find('"', i2 + len(target))
                break
        else:
            if trace:
                g.trace('Fail i', i, target, 'result:', result)
                print('')
            return result
        if j == -1:
            if has_r:
                g.trace('Fail j', i, target, 'result', result)
                print('')
            return result
        if result[j + 1] == '"':
            j += 1
        if trace:
            g.trace(f" FOUND: {i}:{j} target: {target:4} result: {result}")
            g.trace('DELETE:', result[i + len(target):j])
        result = result[:i + len(target)] + result[j:]
        result = result.replace('r""', '""')
        i += len(target) + 1
        assert i &gt; progress
    if trace:
        g.trace('----- Return', result)
        print('')
    return result
        </t>
<t tx="ekr.20241117145426.1">def delete_cruft(s: str) -&gt; str:
    """
    Return an empty string for lines that don't look like a real match.
    Otherwise, return cruft from the end.
    """
    if not s.startswith('return colorer.match_'):
        return ''
    if s.count('colorer.match') != 1:
        return ''
    if s.count('(') != 1 or s.count(')') != 1:
        return ''
    i = s.find('(')
    j = s.find(')')
    return '' if j &lt;= i else s[:j+1]</t>
<t tx="lkj.20190712002811.1"></t>
<t tx="maphew.20190209160547.1">gnx: maphew.20130809155103.2863
</t>
<t tx="mhw.20181210131101.1">True (recommended):
    Write "E" attribute bits in &lt;v&gt; elements.
    Leo outlines will record the expansion state of all nodes.
    
False: (Good for files like unitTest.leo)
    Suppress "E" attribute bits in &lt;v&gt; elements.
    Only the ancestors of the presently selected node will
    be expanded when Leo opens an outline.</t>
</tnodes>
</leo_file>
