Skip to content

Commit 0385c01

Browse files
committed
pass tests, but still not complete
1 parent 55b4dce commit 0385c01

File tree

4 files changed

+200
-27
lines changed

4 files changed

+200
-27
lines changed

src/transforms/tree.js

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -169,20 +169,29 @@ function nodeData(field) {
169169
function normalizer(delimiter = "/") {
170170
return `${delimiter}` === "/"
171171
? (P) => P // paths are already slash-separated
172-
: (P) => P.map(slashEscape).map(replaceAll(delimiter, "/")); // TODO string.replaceAll when supported
172+
: (P) => P.map(slashDelimiter(delimiter));
173173
}
174174

175-
function slashEscape(string) {
176-
return string.replace(/\//g, "\\/");
175+
function slashDelimiter(delimiter) {
176+
const search = new RegExp(`(\\\\*)(${regexEscape(delimiter)}|/)`, "g");
177+
return (value) =>
178+
value == null
179+
? null
180+
: value.replace(
181+
search,
182+
(match, a, b) =>
183+
b === delimiter
184+
? a.length & 1
185+
? `${a.slice(1)}${delimiter}` // drop one backslash
186+
: `${a}/` // replace delimiter with slash
187+
: a.length & 1
188+
? `${a}\\\\/` // add two backslashes to escape backslash
189+
: `${a}\\/` // add one backslash to escape slash
190+
);
177191
}
178192

179193
function slashUnescape(string) {
180-
return string.replace(/\\\//g, "/");
181-
}
182-
183-
function replaceAll(search, replace) {
184-
search = new RegExp(regexEscape(search), "g");
185-
return (value) => (value == null ? null : `${value}`.replace(search, replace));
194+
return string.replace(/\\\//g, "/").replace(/\\\\/g, "\\"); // TODO count backslashes properly
186195
}
187196

188197
function regexEscape(string) {

test/output/treeDelimiter.svg

Lines changed: 80 additions & 0 deletions
Loading

test/output/treeDelimiter2.svg

Lines changed: 80 additions & 0 deletions
Loading

test/plots/tree-delimiter.ts

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,22 @@ import * as Plot from "@observablehq/plot";
33
export async function treeDelimiter() {
44
return Plot.plot({
55
axis: null,
6-
height: 120,
6+
height: 150,
77
margin: 10,
88
marginLeft: 40,
99
marginRight: 190,
1010
marks: [
1111
Plot.tree(
1212
[
13-
"foo;bar;https://example.com",
14-
"foo;bar;https://example.com/posts/1",
15-
"foo;baz;https://example.com/posts/2",
16-
"foo;bar\\;baz;https://example2.com", // “bar;baz” should be a single node
17-
"foo;bar/baz;https://example4.com", // "bar/baz" should be a single node, distinct from “bar;baz”
18-
"foo;bar\\/baz;https://example3.com" // “bar\/baz” should be a single node
13+
"foo;a;//example", // foo → a → //example
14+
"foo;a;//example/1", // foo → a → //example/1
15+
"foo;b;//example/2", // foo → b → //example/2
16+
"foo;c\\;c;//example2", // foo → c;c → //example2
17+
"foo;d\\\\;d;//example2", // foo → d\ → d → //example3
18+
"foo;d\\\\;\\d;//example2", // foo → d\ → \d → //example3
19+
"foo;e\\\\\\;e;//example2", // foo → e\;e → //example3
20+
"foo;f/f;//example4", // foo → f/f → //example4
21+
"foo;g\\/g;//example3" // foo → g\/g → //example3
1922
],
2023
{delimiter: ";"}
2124
)
@@ -26,21 +29,22 @@ export async function treeDelimiter() {
2629
export async function treeDelimiter2() {
2730
return Plot.plot({
2831
axis: null,
29-
height: 120,
32+
height: 150,
3033
margin: 10,
3134
marginLeft: 40,
3235
marginRight: 190,
3336
marks: [
34-
Plot.tree(
35-
[
36-
"foo/bar/https:\\/\\/example.com",
37-
"foo/bar/https:\\/\\/example.com\\/posts\\/1",
38-
"foo/baz/https:\\/\\/example.com\\/posts\\/2",
39-
"foo/bar;baz/https:\\/\\/example2.com", // “bar;baz” should be a single node
40-
"foo/bar\\/baz/https:\\/\\/example4.com", // "bar/baz" should be a single node, distinct from “bar;baz”
41-
"foo/bar\\\\\\/baz/https:\\/\\/example3.com" // “bar\/baz” should be a single node
42-
]
43-
)
37+
Plot.tree([
38+
"foo/a/\\/\\/example", // foo → a → //example
39+
"foo/a/\\/\\/example\\/1", // foo → a → //example/1
40+
"foo/b/\\/\\/example\\/2", // foo → b → //example/2
41+
"foo/c;c/\\/\\/example2", // foo → c;c → //example2
42+
"foo/d\\\\/d/\\/\\/example2", // foo → d\ → d → //example3
43+
"foo/d\\\\/\\d/\\/\\/example2", // foo → d\ → \d → //example3
44+
"foo/e\\\\;e/\\/\\/example2", // foo → e\;e → //example3
45+
"foo/f\\/f/\\/\\/example4", // foo → f/f → //example4
46+
"foo/g\\\\\\/g/\\/\\/example3" // foo → g\/g → //example3
47+
])
4448
]
4549
});
4650
}

0 commit comments

Comments
 (0)