Terry Brown
unread,Jul 17, 2012, 11:15:19 AM7/17/12Sign in to reply to author
Sign in to forward
You do not have permission to delete messages in this group
Either email addresses are anonymous for this group or you need the view member email addresses permission to view the original message
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()