Skip to content

Commit 23ed8cb

Browse files
committed
towards file watching in salix
1 parent 217b3b6 commit 23ed8cb

File tree

16 files changed

+695
-58
lines changed

16 files changed

+695
-58
lines changed

src/main/rascal/salix/App.rsc

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,10 +139,8 @@ App[&T] webApp(SalixApp[&T] app, loc static, map[str,str] headers = ()) {
139139

140140
if (get(/^\/salix\/<rest:.*?>\.<ext:[^.]*>$/) := req) {
141141
loc l = |project://salix-core/src/main/rascal/salix/<rest>.<ext>|;
142-
println("l = <l>");
143142
if (!exists(l)) {
144143
l = |target://salix-core/salix/<rest>.<ext>|;
145-
println("l = <l>");
146144
if (!exists(l)) {
147145
l = |lib://salix-core/salix/<rest>.<ext>|;
148146
if (!exists(l)) {

src/main/rascal/salix/Core.rsc

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import List;
1818
import String;
1919
import IO;
2020
import lang::json::IO; // todo: typed JSON messages
21-
21+
import Type;
2222

2323

2424
@doc{This is the basic Message data type that clients
@@ -266,6 +266,11 @@ data Sub // Subscriptions
266266
Sub timeEvery(Msg(int) int2msg, int interval)
267267
= subscription("timeEvery", encode(int2msg), args = ("interval": interval));
268268
269+
270+
271+
Sub observeFile(Msg(list[FSChange]) recs2msg, str key)
272+
= subscription("observeFile", encode(recs2msg), args=("key": key));
273+
269274
alias Subs[&T] = list[Sub](&T);
270275
271276
list[Sub] noSubs(&T _) = [];
@@ -305,6 +310,8 @@ Cmd random(Msg(int) f, int from, int to)
305310
Cmd setFocus(Msg() f, str id)
306311
= command("setFocus", encode(f), args = ("id": id));
307312
313+
Cmd pickFile(Msg(FSHandle) f, str key)
314+
= command("pickFile", encode(f), args=("key": key));
308315
309316
/*
310317
* Event decoders
@@ -323,7 +330,8 @@ alias Parser = Msg(str,Handle,map[str,value]);
323330
@doc{Convert request parameters to a Msg value. Active mappers at `path`
324331
transform the message according to f.}
325332
Msg params2msg(map[str, value] params, Parser parse)
326-
= parse(params["type"], toHandle(params), params);
333+
= parse(params["type"], toHandle(params), params)
334+
when bprintln(params);
327335
328336
@doc{Parse request parameters into a Handle.}
329337
Handle toHandle(map[str, value] params)
@@ -350,6 +358,35 @@ Msg parseMsg("real", Handle h, map[str,value] p)
350358
Msg parseMsg("values", Handle h, map[str,value] p)
351359
= applyMaps(h, decode(h, #Msg(value,value))(p["value1"], p["value2"]));
352360
361+
// https://developer.chrome.com/blog/file-system-observer
362+
alias FSHandle = tuple[str kind, str name];
363+
alias FSChange = tuple[
364+
FSHandle root,
365+
FSHandle changedHandle,
366+
list[str] relativePathComponents,
367+
str \type,
368+
list[str] relativePathMovedFrom // optional
369+
];
370+
371+
Msg parseMsg("fshandle", Handle h, map[str, value] p)
372+
= applyMaps(h, decode(h, #(Msg(FSHandle)))(<typeCast(#str, p["kind"]), typeCast(#str, p["name"])>));
373+
374+
Msg parseMsg("fschange", Handle h, map[str, value] p) {
375+
// todo: parse relativePathComponents and relativePathMovedFrom
376+
changes = for ("object"(root=node r, \type=str t, changedHandle=node c) <- typeCast(#list[node], p["records"])) {
377+
FSHandle root = <typeCast(#str, r.kind), typeCast(#str, r.name)>;
378+
FSHandle changedHandle = <typeCast(#str, c.kind), typeCast(#str, c.name)>;
379+
append <root, changedHandle, [], t, []>;
380+
}
381+
return applyMaps(h, decode(h, #(Msg(list[FSChange])))(changes));
382+
}
383+
384+
385+
// = applyMaps(h, decode(h, #Msg(FSChanges)))([<
386+
// > | map[str,value] rec <- p["records"] ]);
387+
388+
389+
353390
alias XY = tuple[int x, int y];
354391
alias MouseXY = tuple[XY client, XY movement, XY offset, XY page, XY screen];
355392

src/main/rascal/salix/Diagram.rsc

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
module salix::Diagram
2+
3+
import salix::HTML;
4+
import salix::SVG;
5+
import salix::Node;
6+
import salix::Core;
7+
8+
9+
/*
10+
<script src="https://cdn.jsdelivr.net/npm/[email protected]/plain-draggable.min.js"></script>
11+
https://cdn.jsdelivr.net/npm/[email protected]/leader-line.min.js
12+
13+
Plan:
14+
15+
- when drag dropped: send event signaling coordinates update
16+
- svg/container simply contains html elements representing nodes
17+
- and invisible elements indicating edges
18+
(can we use onload? to invoke leaderline)
19+
- these are drawn by leader-line
20+
21+
edits of properties are simply salix.
22+
node add/remove etc. trigger
23+
*/
24+
25+
alias BB = tuple[int x, int y, int left, int right, int top, int bottom, int width, int height];
26+
27+
28+
Attr onDragEnd(Msg(BB) f) = event("click", dragBB(f));
29+
30+
Hnd dragBB(Msg(BB) bb2msg) = handler("dragBB", encode(bb2msg));
31+
32+
33+
34+
alias NF = void(str, void());
35+
alias EF = void(str, str, str);
36+
37+
void myDiagram() {
38+
diagram("my diagram", "100px", "100px", (NF nf, EF ef) {
39+
nf("node1", () {
40+
p("hello");
41+
});
42+
ef("node1", "node1", "an edge");
43+
});
44+
}
45+
46+
alias Diagrammer = tuple[void(void(NF,EF)) diagram];
47+
48+
Diagrammer diagrammer(str name, str w, str h) {
49+
map[str, void()] myNodes = ();
50+
51+
/*
52+
53+
order by:
54+
- pre-existing
55+
- then new ones
56+
57+
pass new ones into JS for edge drawing
58+
59+
*/
60+
61+
void nf(str name, void() block) {
62+
myNodes[name] = () {
63+
foreignObject(id(name), style(("border": "solid")),
64+
attr("onload", "new PlainDraggable(evt.target);"), block);
65+
};
66+
curNodes += {name};
67+
if (name notin oldNodes) {
68+
newNodes += {name};
69+
}
70+
}
71+
72+
void ef(str from, str to, str label) {
73+
line(attr("from", from), attr("to", to), attr("label", label));
74+
curEdges += {<from, to>};
75+
if (<from, to> notin oldEdges) {
76+
newEdges += {<from, to>};
77+
}
78+
}
79+
80+
void diagram(void(NF, EF) block) {
81+
svg(id(name), width(w), height(h), () {
82+
block(nf, ef);
83+
});
84+
85+
delNodes = oldNodes - curNodes;
86+
delEdges = oldEdges - curEdges;
87+
for (str x <- oldNodes & curNodes) {
88+
;
89+
}
90+
}
91+
92+
return <diagram>;
93+
}
94+
95+
void diagram(str name, str w, str h, void(NF, EF) block) {
96+
97+
map[str, void()] myNodes = ();
98+
99+
void nf(str name, void() block) {
100+
myNodes[name] = () {
101+
foreignObject(id(name), style(("border": "solid")),
102+
attr("onload", "new PlainDraggable(evt.target);"), block);
103+
};
104+
}
105+
106+
void ef(str from, str to, str label) {
107+
line(attr("from", from), attr("to", to), attr("label", label));
108+
}
109+
110+
svg(id(name), width(w), height(h), () {
111+
block(nf, ef);
112+
});
113+
114+
div(class("salix-alien"), id("<name>-alien")
115+
, attr("onclick", "$salix.registerAlien(\'<name>\', $chartpatch_<name>);"), () {
116+
script(src("https://cdn.jsdelivr.net/npm/[email protected]/plain-draggable.min.js"));
117+
script(src("https://cdn.jsdelivr.net/npm/[email protected]/leader-line.min.js"));
118+
});
119+
120+
}

src/main/rascal/salix/Diff.rsc

Lines changed: 99 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
module salix::Diff
1010

1111
import salix::Node;
12+
import salix::util::LCS;
1213
import List;
1314
import util::Math;
1415
import IO;
@@ -27,7 +28,7 @@ data Patch
2728
;
2829

2930
data 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

3839
data 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+
5777
Patch 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+
96156
Patch 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

src/main/rascal/salix/HTML.rsc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,8 @@ void menu(value vals...) = build(vals, "menu");
142142
* Attributes
143143
*/
144144

145-
Attr style(tuple[str, str] styles...) = attr("style", intercalate("; ", ["<k>: <v>" | <k, v> <- styles ]));
146-
Attr style(map[str,str] styles) = attr("style", intercalate("; ", ["<k>: <styles[k]>" | k <- styles ]));
145+
Attr style(tuple[str, value] styles...) = attr("style", intercalate("; ", ["<k>: <v>" | <k, v> <- styles ]));
146+
Attr style(map[str,value] styles) = attr("style", intercalate("; ", ["<k>: <styles[k]>" | k <- styles ]));
147147
148148
Attr crossorigin(str val) = attr("crossorigin", val);
149149
Attr integrity(str val) = attr("integrity", val);

0 commit comments

Comments
 (0)