Should Leo support a standard template tool?

46 views
Skip to first unread message

Edward K. Ream

unread,
May 25, 2012, 9:12:48 AM5/25/12
to leo-editor
About a year ago at the Ashland sprint Kent (iirc) made the following
suggestion: solid, simple implementation of one of the standard
template engines providing intuitive template nodes, variable
definitions, and rendering options.

This is moving up the to-do list and yesterday's docstring hack
brought this item to mind.

My question is, how would this work in Leo? Presumably, this can be
done by creating template-oriented commands.

Your comments and suggestions, please, Amigos.

Edward

P.S. Templates must have exactly *zero* impact on the code that reads
and writes either .leo files or external files. I shall reject any
scheme for integrating templates into Leo's read/write code. Don't
even think about trying to change my mind about this. Experience with
earlier versions of Leo convinced me that avoiding noweb escape
sequences was essential. I have never regretted that choice.

EKR

Ville Vainio

unread,
May 25, 2012, 10:21:50 AM5/25/12
to Edward K. Ream, leo-editor
Take a look at jinjarender for an example how it could behave.
Basically it's @nosent like one directional rendering. Valuespace
plugin is used as input source

Sent from my Windows Phone
From: Edward K. Ream
Sent: 5/25/2012 4:12 PM
To: leo-editor
Subject: Should Leo support a standard template tool?
--
You received this message because you are subscribed to the Google
Groups "leo-editor" group.
To post to this group, send email to leo-e...@googlegroups.com.
To unsubscribe from this group, send email to
leo-editor+...@googlegroups.com.
For more options, visit this group at
http://groups.google.com/group/leo-editor?hl=en.

Kent Tenney

unread,
May 25, 2012, 10:30:07 AM5/25/12
to leo-e...@googlegroups.com
I've been using jinja2 lately, because it's favored by Salt.
http://saltstack.org

There is so much wisdom behind the Salt team that I look to them
for best practice recommendations.

I've been wanting to slip in a plug for Salt, there you go. Of course there
are many very good template systems out there.

My naive take on templates support would be something like:

@template setup.py
name = '{{data.name}}'
...

project foo
data = {'name':'FooProject'}
c.render('@setup.py', data)

A node would declare a template, the template name would be available
to send data for rendering. Maybe the rendered result would be in a vr
pane, maybe a child of the node declaring the data.

I suppose different rendering engines could be accommodated via
@jinja2 setup.py
...

@mako .bashrc
...

Kent Tenney

unread,
May 25, 2012, 2:36:24 PM5/25/12
to leo-e...@googlegroups.com
On Fri, May 25, 2012 at 8:12 AM, Edward K. Ream <edre...@gmail.com> wrote:
> About a year ago at the Ashland sprint Kent (iirc) made the following
> suggestion: solid, simple implementation of one of the standard
> template engines providing intuitive template nodes, variable
> definitions, and rendering options.
>
> This is moving up the to-do list and yesterday's docstring hack
> brought this item to mind.
>
> My question is, how would this work in Leo? Presumably, this can be
> done by creating template-oriented commands.
>
> Your comments and suggestions, please, Amigos.
>
> Edward
>
> P.S. Templates must have exactly *zero* impact on the code that reads
> and writes either .leo files or external files.

I think I suggested just this ... AWKWARD ...


 I shall reject any
> scheme for integrating templates into Leo's read/write code.

Meaning the @xxxx xxxx
machinery is maxed out?

Don't
> even think about trying to change my mind about this.  Experience with
> earlier versions of Leo convinced me that avoiding noweb escape
> sequences was essential.  I have never regretted that choice.
>
> EKR
>

Edward K. Ream

unread,
May 25, 2012, 3:44:24 PM5/25/12
to leo-e...@googlegroups.com
On Fri, May 25, 2012 at 1:36 PM, Kent Tenney <kte...@gmail.com> wrote:

> I think I suggested just this ... AWKWARD ...

Don't panic. A templating script (or plugin) can do just about
anything as long as it doesn't monkey-patch leoFileCommands.py or
leoAtFile.py. But that's never going to be necessary.

Indeed, supporting templates is almost too easy, as I have just
verified. The problem isn't supporting templates, the problem is
choosing between all the various ways that could work.

Ville's jinjarender plugin is one way: it writes to files and uses the
valuespace plugin to get the Python dict that is passed to
Template.render.

But there are so many other ways. I just wrote a script that gets
key/value pairs from the children of an @jinja-data node and writes
the output to an @jinja-output node.

The way you suggest, namely passing an explicit dict of key/value
pairs is also perfectly feasible.

> Meaning the @x path machinery is maxed out?

Not at all. Let the wild rumpus start.

Edward

P.S. Here is a first draft of @button jinja-render. Simply
copy/paste into an outline. The code is dead easy, so it will be easy
to change it to make it do exactly what you want.

=====

<?xml version="1.0" encoding="utf-8"?>
<!-- Created by Leo (http://webpages.charter.net/edreamleo/front.html) -->
<?xml-stylesheet ekr_test?>
<leo_file xmlns:leo="http://www.leo-editor.org/2011/leo" >
<leo_header file_format="2"/>
<vnodes>
<v t="ekr.20120525134212.10696"><vh>@button jinja-render</vh>
<v t="ekr.20120525134212.10699"><vh>&lt;&lt; imports &gt;&gt;</vh></v>
<v t="ekr.20120525134212.10709"><vh>ctor</vh></v>
<v t="ekr.20120525134212.10714"><vh>error</vh></v>
<v t="ekr.20120525134212.10713"><vh>get_data</vh></v>
<v t="ekr.20120525134212.10720"><vh>put</vh></v>
<v t="ekr.20120525134212.10710"><vh>run</vh></v>
</v>
</vnodes>
<tnodes>
<t tx="ekr.20120525134212.10696">''' Render @jinja nodes. Requires
jinja2 module.

- Writes selected tree to the @jinja-output node in the selected tree.
- Takes arguments from the @jinja-data node.
'''

&lt;&lt; imports &gt;&gt;

class JinjaController:
@others

if Template:
JinjaController(c,p).run()
</t>
<t tx="ekr.20120525134212.10699">import leo.core.leoGlobals as g

try:
from jinja2 import Template
except ImportError:
Template = None
</t>
<t tx="ekr.20120525134212.10709">def __init__ (self,c,p):

self.c = c
self.p = p.copy()</t>
<t tx="ekr.20120525134212.10710">def run(self):

c,p = self.c,self.p
s = g.getScript(c,p,useSelectedText=False,useSentinels=False)
tmpl = Template(s)
d = self.get_data()
s = tmpl.render(d)
self.put(s)
</t>
<t tx="ekr.20120525134212.10713">def get_data (self):

c = self.c
p = g.findNodeAnywhere(c,'@jinja-data')
d = {}
if p:
for p in p.children():
key = p.h.strip()
val = p.b.strip() or None
d [key] = val
else:
self.error('no @jinja-data node')
return d
</t>
<t tx="ekr.20120525134212.10714">def error (self,s):
g.es_print(s,color='red')</t>
<t tx="ekr.20120525134212.10720">def put (self,s):

c,p = self.c,self.p
p2 = g.findNodeAnywhere(c,'@jinja-out')
if not p2:
p2 = p.insertAfter()
p2.h = '@jinja-out'
c.redraw()

p2.b = '@language rest\n\n' + s
c.selectPosition(p2)
</t>
</tnodes>
</leo_file>

==============

EKR

tfer

unread,
May 25, 2012, 3:44:30 PM5/25/12
to leo-e...@googlegroups.com
One of the schemes I'm contemplating would involve using non-printing plane 15 and 16 unicode codepoints as embeddable smart "tab-stops".  Such would interact with saving and reading, but probably all able to be taken care of by hooks rather than hacking the read/write code.

Such templates would consume all the tab-stops if filled out, but if the templates are to be completed in the future, after a save, some state info would have to be saved.  Note that any left over tab-stops would probably choke a language's interpreter/compiler, but the non-tabstop uncompleted template leftovers would not make sense either.

Several possibilities:
     1. Save the file as is, hook a "on-save" callback to write the tabstop triggered functionality to the pickleshare database.
     2. Offer to convert the templates to a form that incorporates the triggered functionality into normal characters.
     3. Offer to purge the unused templates.
     4. Convert the templates into a dumber, normal text based form.

I did some experiments a while ago that extended the codewise database stuff to store templates, it looks real good as a place to handle templates.

Tom

Edward K. Ream

unread,
May 25, 2012, 4:27:16 PM5/25/12
to leo-e...@googlegroups.com
On Fri, May 25, 2012 at 2:44 PM, tfer <tfeth...@aol.com> wrote:
> One of the schemes I'm contemplating would involve using non-printing plane
> 15 and 16 unicode codepoints as embeddable smart "tab-stops".  Such would
> interact with saving and reading, but probably all able to be taken care of
> by hooks rather than hacking the read/write code.

I should clarify my earlier statement about not interacting with the
read/write code. It's perfectly possible that we might want to tweak
such code. But it's not likely in this case because the problems are
so straightforward.

What I wanted to rule out were any kind of escape sequences that would
affect how Leo parses files, especially external files. It turns out
that such escape sequences are virtually impossible to support in a
fault-tolerant way. More generally I will tolerate *no* contemplated
change to Leo's sentinels or parsing code. Leo's read code simply is
not going to treat any characters or patterns in a special way.

> Such templates would consume all the tab-stops if filled out, but if the
> templates are to be completed in the future, after a save, some state info
> would have to be saved.  Note that any left over tab-stops would probably
> choke a language's interpreter/compiler, but the non-tabstop uncompleted
> template leftovers would not make sense either.

You are free to use uA's any way you want. And your scripts can do
anything they want, subject only to the limitations I've repeated
above.

The most general way would be for your scripts to do their own reading
and writing, independent of the code in leoFileCommands.py and
leoAtFile.py. Your scripts are free, of course, to use any utilities
in those files, or in leoGlobals.py, or anywhere else for that matter.

In short, script writers should find it straightforward to implement
templates. It's hard to believe that Leo's read/write code would have
to change to support such scripts.

Edward

Edward K. Ream

unread,
May 26, 2012, 6:03:05 AM5/26/12
to leo-e...@googlegroups.com
On Fri, May 25, 2012 at 2:44 PM, Edward K. Ream <edre...@gmail.com> wrote:

> supporting templates is almost too easy.

One can imagine a whole bunch of buttons, or better, @command nodes,
each of which creates a unique file. Each @command node would specify

A. The "source" outline.
B. The template data and (optionally) the name of the file to be written.

The format of either A or B is completely up to the writer of the
@command node, but here is the simplest thing that could possibly
work:

A. [Finding the source outline] Use g.findNodeAnywhere(c,headline)

B. [Getting the template data] Put the data in a dict.

C. [Executing the common code] Create a node, "@common jinja code" containing::

def run(fn,h,d):
'''Write the template in node with headline h
using data in dict d to the file whose name is fn.
'''
from jinja2 import Template
p = g.findNodeAnywhere(c,h)
if p:
s = g.getScript(c,p,useSelectedText=False,useSentinels=False)
s = Template(s).render(d)
print(s) # fn not used.
else:
print('not found',h)

Actually, as you can see, the output is printed instead of being sent to fn.

Each @command node would have the form::

exec(g.findTestScript(c,'@common jinja code'))
d = {whatever}
run(fileName,headline,d)

As an actual example::

exec(g.findTestScript(c,'@common jinja code'))
d = {'name':'Fred','rank':'colonel','serial_number':'3.14159'}
run('fn','jinja test',d)

If the node "jinja test' contains::

Hello again, {{ name }}.
Your rank is {{rank}} and your tag number is {{serial_number}}.

The output from the trace in run will be::

Hello again, Fred.
Your rank is colonel and your tag number is 3.14159.

That's it. This is all tested code.

Edward

Edward K. Ream

unread,
May 27, 2012, 11:15:05 AM5/27/12
to leo-editor


On May 26, 5:03 am, "Edward K. Ream" <edream...@gmail.com> wrote:

> One can imagine a whole bunch of buttons, or better, @command nodes,
> each of which creates a unique file.  Each @command node would specify
>
> A.  The "source" outline.
> B.  The template data and (optionally) the name of the file to be written.

As of rev 5361, script.leo contains the code I have been discussion in
the node::

#Scripts-->@thin leoScripts.txt-->Important-->
Prototype of jinja @command notes-->write jinja (prototype of
@command node)

This will conclude the current round of work with prototyping, except
for mention in Leo's docs.

Edward

Ville M. Vainio

unread,
May 25, 2012, 5:08:14 PM5/25/12
to leo-e...@googlegroups.com
Also look at jinja_test.leo in contrib branch; it renders a template
to variable "mytest" (with @= mytest, and @cl jinja, declared in
"prelude" node), and assigns the value of variable to node @r mytest.

You can "execute" the outline by alt-x vs-update.

It probably helps to read the docstring of valuespace plugin to
understand how the outline behaves.
Reply all
Reply to author
Forward
0 new messages