Tool for diffing Leo files

78 views
Skip to first unread message

Terry Brown

unread,
Jul 17, 2012, 11:15:19 AM7/17/12
to leo-e...@googlegroups.com
The script below is a tool for diffing two Leo files. The attached
screenshot illustrates the output for two different revisions of
LeoPyRef.leo.

``- nodename``
indicates a node which disappeared
``+ nodename``
a node which is new,
``!v nodename`` followed by ``!^ nodename``
a node with an unchanged heading but changed content, the first
linking to the old version, the second linking to the new version

If you have the bookmarks.py plugin active, you can double click nodes
to jump to the original(s).



from leo.core.leoNodes import vnode
if not hasattr(vnode, 'insertAsLastChild'):
# add insertAsLastChild method to vnodes
def ialc(self):
vnode(self.context)._linkAsNthChild(self, len(self.children))
return self.children[-1]
vnode.insertAsLastChild = ialc

from_filename = g.app.gui.runOpenFileDialog('From (old) file', [('Leo', '*.leo')])
to_filename = g.app.gui.runOpenFileDialog('To (new) file', [('Leo', '*.leo')])

# from_filename = "/mnt/shuttle/bkup/usr1/2012-07-13/home/tbrown/.leo/.todo.leo"
# to_filename = "/mnt/shuttle/bkup/usr1/2012-07-15/home/tbrown/.leo/.todo.leo"

from_c = g.openWithFileName(from_filename, c)
to_c = g.openWithFileName(to_filename, c)

vf = from_c.hiddenRootNode
vt = to_c.hiddenRootNode

assert from_c != c
assert to_c != c
assert from_c != to_c

nd = c.rootPosition().insertAfter()
nd.copy().back().moveAfter(nd)
nd.h = 'diff @bookmarks'

def text_match(a, b):
return (a.h == b.h,
a.h == b.h and a.b == b.b)
def gnx_match(a, b):
return (a.h == b.h and a.gnx == b.gnx,
a.h == b.h and a.b == b.b and a.gnx == b.gnx)

def diff_trees(vf, vt, path):

fonly = [] # nodes only in from tree
tonly = [] # nodes only in to tree
diffs = [] # nodes which occur in both but have different descendants

# count number of times each headline occurs as a child of
# each node being compared
count_f = {}
for cf in vf.children:
count_f[cf.h] = count_f.get(cf.h, 0) + 1
count_t = {}
for ct in vt.children:
count_t[ct.h] = count_t.get(ct.h, 0) + 1

for cf in vf.children:

for ct in vt.children:

if count_f[cf.h] == 1 and count_t[ct.h] == 1:
equal = text_match
else:
equal = gnx_match

head_eq, body_eq = equal(cf, ct)

if body_eq:
diffs.append(diff_trees(cf, ct, path+[vf.h]))

break
elif head_eq:
d = diff_trees(cf, ct, path+[vf.h])
if d:
d.h = '!v '+d.h
else:
d = vnode(nd.v.context)
d.h = '!v '+cf.h
d.b = "file://%s/#%s\n\n%s" % (
from_filename,
'-->'.join((path+[vf.h]+[cf.h])[1:]),
cf.b
)
diffs.append(d)
d = vnode(nd.v.context)
d.h = '!^ '+cf.h
d.b = "file://%s/#%s\n\n%s" % (
to_filename,
'-->'.join((path+[vt.h]+[ct.h])[1:]),
ct.b
)
diffs.append(d)
break
else:
fonly.append(cf)

for ct in vt.children:

for cf in vf.children:

if count_f[cf.h] == 1 and count_t[ct.h] == 1:
equal = text_match
else:
equal = gnx_match

head_eq, body_eq = equal(cf, ct)
if head_eq or body_eq:
# no need to recurse matches again
break

else:
tonly.append(ct)

if not any(diffs) and not fonly and not tonly:
return None

vd = vnode(nd.v.context)
vd.h = vf.h
for d in diffs:
if d:
vd.children.append(d)
for f in fonly:
n = vd.insertAsLastChild()
n.h = '- '+f.h
n.b = "file://%s/#%s" % (from_filename, '-->'.join((path+[vf.h]+[f.h])[1:]))
for t in tonly:
n = vd.insertAsLastChild()
n.h = '+ '+t.h
n.b = "file://%s/#%s" % (to_filename, '-->'.join((path+[vf.h]+[t.h])[1:]))

return vd

v = diff_trees(vf, vt, [])
if v:
nd.v.children.extend(v.children) # snip off <hidden root node>

c.bringToFront()
c.redraw()




screen.png

resi147

unread,
Aug 6, 2012, 9:21:45 AM8/6/12
to leo-e...@googlegroups.com, terry_...@yahoo.com
Hi Terry,

I tried this one and it seems to be very useful. What I would also like to have is a similar (or the same) script,
that does the same on two different nodes of a leo file. I admit, the changes should be minimal to be done by
myself, but as I'm not familiar with the internal leo api, I wanted to ask for how to best do this changes.

Probably somewhere here
"""
...

vf = from_c.hiddenRootNode
vt = to_c.hiddenRootNode
...
"""
just to feed in the 2 nodes, Any quick idea how this can be done the best way?

Cheers,
Karl.

Reply all
Reply to author
Forward
0 new messages