99module salix ::Diff
1010
1111import salix ::Node ;
12+ import salix ::util ::LCS ;
1213import List ;
1314import util ::Math ;
1415import IO ;
@@ -27,7 +28,7 @@ data Patch
2728 ;
2829
2930data EditType
30- = setText () | replace () | removeNode () | appendNode ()
31+ = setText () | replace () | removeNode () | appendNode () | insertNode ()
3132 | setAttr () | setProp () | setEvent () | setExtra ()
3233 | removeAttr () | removeProp () | removeEvent () | removeExtra ()
3334 ;
@@ -37,7 +38,7 @@ data Hnd = null();
3738
3839data Edit
3940 = edit (EditType \type , str contents ="" , Node html =none (), Hnd handler =null (),
40- str name ="" , str val ="" , value extra =());
41+ str name ="" , str val ="" , int pos =- 1 , value extra =());
4142
4243// nodes at this level are always assumed to be <html> nodes,
4344// however, we only diff their bodies. This is (unfortunately)
@@ -54,7 +55,68 @@ Patch diff(Node old, Node new) {
5455 return p ;
5556}
5657
58+ bool nodeEq (Node x , Node y ) {
59+ if (x .\type != y .\type ) {
60+ return false ;
61+ }
62+
63+ if (x .\type is element ) {
64+ if (x .tagName == y .tagName ) {
65+ return true ;
66+ }
67+ }
68+
69+ if (x .\type is txt ) {
70+ if (x .contents == y .contents ) {
71+ return true ;
72+ }
73+ }
74+ return false ;
75+ }
76+
5777Patch diff (Node old , Node new , int idx ) {
78+ //println("diffing: <old> against <new>");
79+
80+ // Patch p = patch(idx);
81+
82+ // if (!nodeEq(old, new)) {
83+ // p.edits=[edit(replace(), html=new)];
84+ // return p;
85+ // }
86+
87+ // // they're nodeEq now, so if one of them is element, the other is too
88+ // if (old.\type is element) {
89+ // p.edits = diffMap(old.attrs, new.attrs, setAttr(), removeAttr())
90+ // + diffMap(old.props, new.props, setProp(), removeProp())
91+ // + diffEventMap(old.events, new.events)
92+ // + diffExtra(old.extra, new.extra);
93+ // }
94+
95+ // list[Node] oldKids = old.kids;
96+ // list[Node] newKids = new.kids;
97+ // LCSMatrix mx = lcsMatrix(oldKids, newKids, nodeEq);
98+ // list[Diff[Node]] d = getDiff(mx, oldKids, newKids, size(oldKids), size(newKids), nodeEq);
99+
100+ // iprintln(d);
101+
102+ // for (int i <- [0..size(d)]) {
103+ // switch (d[i]) {
104+ // case same(Node n1, Node n2): {
105+ // Patch p2 = diff(n1, n2, i);
106+ // if (p2.patches != [] || p2.edits != []) {
107+ // p.patches += [p2];
108+ // }
109+ // }
110+ // case add(Node n, int pos):
111+ // p.edits += [edit(insertNode(), html=n, pos=pos)];
112+ // case remove(Node _, int pos):
113+ // p.edits += [edit(removeNode(), pos=pos)];
114+ // }
115+ // }
116+
117+ // return p;
118+
119+
58120 if (old .\type is empty ) {
59121 return patch (idx , edits = [edit (replace (), html =new )]);
60122 }
@@ -69,16 +131,7 @@ Patch diff(Node old, Node new, int idx) {
69131 }
70132 return patch (idx );
71133 }
72-
73- // if (old.\type is native, new.\type is native) {
74- // edits = diffMap(old.props, new.props, setProp(), removeProp())
75- // + diffMap(old.attrs, new.attrs, setAttr(), removeAttr())
76- // + diffEventMap(old.events, new.events);
77- // if (old.id != new.id) {
78- // edits += edit(setProp(), name="id", val=new.id);
79- // }
80- // return patch(idx, edits = edits);
81- // }
134+
82135
83136 if (old .\type is element , old .tagName != new .tagName ) {
84137 return patch (idx , edits = [edit (replace (), html =new )]);
@@ -93,14 +146,45 @@ Patch diff(Node old, Node new, int idx) {
93146 return diffKids (old .kids , new .kids , patch (idx , edits = edits ));
94147}
95148
149+ bool isAlien (Node nk ) = nk .\type is element
150+ && nk .tagName == "div"
151+ && "class" in nk .attrs
152+ && nk .attrs ["class" ] == "salix-alien" ;
153+
154+ str getId (Node n ) = n .attrs ["id" ];
155+
96156Patch diffKids (list [Node ] oldKids , list [Node ] newKids , Patch myPatch ) {
97157 oldLen = size (oldKids );
98158 newLen = size (newKids );
99159
100160 for (int i <- [0 ..min (oldLen , newLen )]) {
101- Patch p = diff (oldKids [i ], newKids [i ], i );
102- if (p .edits != [] || p .patches != []) {
103- myPatch .patches += [p ];
161+ Node ok = oldKids [i ];
162+ Node nk = newKids [i ];
163+
164+
165+ // aliens are not robust to diffing their internals
166+ // so if an existing alien moves or a new alien is created
167+ // we don't patch it but (re)build it from scratch;
168+ // this only happens if newKid is an alien
169+ // and oldKid is not. (the reverse case is not important:
170+ // in this case the the old alien would be "destroyed"
171+ // by patching it into the new (non-alien) node).
172+ // if both old and new are alien *and* have the same
173+ // the custom patch algorithm handles the things
174+ // (covered by the else branch below).
175+
176+ if (!isAlien (ok ), isAlien (nk )) {
177+ myPatch .patches += [patch (i , edits =[edit (replace (), html =nk )])];
178+ }
179+ // else if (isAlien(ok), isAlien(nk), getId(ok) != getId(nk)) {
180+ // myPatch.patches += [patch(i, edits=[edit(replace(), html=nk)])];
181+ // }
182+ else {
183+ Patch p = diff (oldKids [i ], newKids [i ], i );
184+
185+ if (p .edits != [] || p .patches != []) {
186+ myPatch .patches += [p ];
187+ }
104188 }
105189 }
106190
0 commit comments