@thi.ng/zipper 2.1.68 → 2.1.70
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1 -1
- package/README.md +1 -1
- package/api.js +0 -1
- package/package.json +11 -8
- package/zipper.js +275 -181
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
package/api.js
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thi.ng/zipper",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.70",
|
|
4
4
|
"description": "Functional tree editing, manipulation & navigation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "./index.js",
|
|
@@ -24,7 +24,9 @@
|
|
|
24
24
|
"author": "Karsten Schmidt (https://thi.ng)",
|
|
25
25
|
"license": "Apache-2.0",
|
|
26
26
|
"scripts": {
|
|
27
|
-
"build": "yarn
|
|
27
|
+
"build": "yarn build:esbuild && yarn build:decl",
|
|
28
|
+
"build:decl": "tsc --declaration --emitDeclarationOnly",
|
|
29
|
+
"build:esbuild": "esbuild --format=esm --platform=neutral --target=es2022 --tsconfig=tsconfig.json --outdir=. src/**/*.ts",
|
|
28
30
|
"clean": "rimraf --glob '*.js' '*.d.ts' '*.map' doc",
|
|
29
31
|
"doc": "typedoc --excludePrivate --excludeInternal --out doc src/index.ts",
|
|
30
32
|
"doc:ae": "mkdir -p .ae/doc .ae/temp && api-extractor run --local --verbose",
|
|
@@ -33,13 +35,14 @@
|
|
|
33
35
|
"test": "bun test"
|
|
34
36
|
},
|
|
35
37
|
"dependencies": {
|
|
36
|
-
"@thi.ng/api": "^8.9.
|
|
37
|
-
"@thi.ng/arrays": "^2.7.
|
|
38
|
-
"@thi.ng/checks": "^3.4.
|
|
39
|
-
"@thi.ng/errors": "^2.4.
|
|
38
|
+
"@thi.ng/api": "^8.9.13",
|
|
39
|
+
"@thi.ng/arrays": "^2.7.9",
|
|
40
|
+
"@thi.ng/checks": "^3.4.13",
|
|
41
|
+
"@thi.ng/errors": "^2.4.7"
|
|
40
42
|
},
|
|
41
43
|
"devDependencies": {
|
|
42
44
|
"@microsoft/api-extractor": "^7.38.3",
|
|
45
|
+
"esbuild": "^0.19.8",
|
|
43
46
|
"rimraf": "^5.0.5",
|
|
44
47
|
"tools": "^0.0.1",
|
|
45
48
|
"typedoc": "^0.25.4",
|
|
@@ -63,7 +66,7 @@
|
|
|
63
66
|
"access": "public"
|
|
64
67
|
},
|
|
65
68
|
"engines": {
|
|
66
|
-
"node": ">=
|
|
69
|
+
"node": ">=18"
|
|
67
70
|
},
|
|
68
71
|
"files": [
|
|
69
72
|
"./*.js",
|
|
@@ -92,5 +95,5 @@
|
|
|
92
95
|
],
|
|
93
96
|
"year": 2015
|
|
94
97
|
},
|
|
95
|
-
"gitHead": "
|
|
98
|
+
"gitHead": "25a42a81fac8603a1e440a7aa8bc343276211ff4\n"
|
|
96
99
|
}
|
package/zipper.js
CHANGED
|
@@ -2,193 +2,287 @@ import { peek } from "@thi.ng/arrays/peek";
|
|
|
2
2
|
import { isArray } from "@thi.ng/checks/is-array";
|
|
3
3
|
import { assert } from "@thi.ng/errors/assert";
|
|
4
4
|
const newPath = (l, r, path, nodes, changed = false) => ({
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
l,
|
|
6
|
+
r,
|
|
7
|
+
path,
|
|
8
|
+
nodes,
|
|
9
|
+
changed
|
|
10
10
|
});
|
|
11
|
-
const changedPath = (path) => path ? { ...path, changed: true } :
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
11
|
+
const changedPath = (path) => path ? { ...path, changed: true } : void 0;
|
|
12
|
+
class Location {
|
|
13
|
+
_node;
|
|
14
|
+
_ops;
|
|
15
|
+
_path;
|
|
16
|
+
constructor(node, ops, path) {
|
|
17
|
+
this._node = node;
|
|
18
|
+
this._ops = ops;
|
|
19
|
+
this._path = path;
|
|
20
|
+
}
|
|
21
|
+
get isBranch() {
|
|
22
|
+
return this._ops.branch(this._node);
|
|
23
|
+
}
|
|
24
|
+
get isFirst() {
|
|
25
|
+
return !this.lefts;
|
|
26
|
+
}
|
|
27
|
+
get isLast() {
|
|
28
|
+
return !this.rights;
|
|
29
|
+
}
|
|
30
|
+
get depth() {
|
|
31
|
+
let d = 0;
|
|
32
|
+
let path = this._path;
|
|
33
|
+
while (path) {
|
|
34
|
+
d++;
|
|
35
|
+
path = path.path;
|
|
20
36
|
}
|
|
21
|
-
|
|
22
|
-
|
|
37
|
+
return d;
|
|
38
|
+
}
|
|
39
|
+
get node() {
|
|
40
|
+
return this._node;
|
|
41
|
+
}
|
|
42
|
+
get children() {
|
|
43
|
+
return this._ops.children(this._node);
|
|
44
|
+
}
|
|
45
|
+
get path() {
|
|
46
|
+
return this._path ? this._path.nodes : void 0;
|
|
47
|
+
}
|
|
48
|
+
get lefts() {
|
|
49
|
+
return this._path ? this._path.l : void 0;
|
|
50
|
+
}
|
|
51
|
+
get rights() {
|
|
52
|
+
return this._path ? this._path.r : void 0;
|
|
53
|
+
}
|
|
54
|
+
get left() {
|
|
55
|
+
const path = this._path;
|
|
56
|
+
const lefts = path && path.l;
|
|
57
|
+
return lefts && lefts.length ? new Location(
|
|
58
|
+
peek(lefts),
|
|
59
|
+
this._ops,
|
|
60
|
+
newPath(
|
|
61
|
+
lefts.slice(0, lefts.length - 1),
|
|
62
|
+
[this._node].concat(path.r || []),
|
|
63
|
+
path.path,
|
|
64
|
+
path.nodes,
|
|
65
|
+
path.changed
|
|
66
|
+
)
|
|
67
|
+
) : void 0;
|
|
68
|
+
}
|
|
69
|
+
get right() {
|
|
70
|
+
const path = this._path;
|
|
71
|
+
const rights = path && path.r;
|
|
72
|
+
if (!rights)
|
|
73
|
+
return;
|
|
74
|
+
const r = rights.slice(1);
|
|
75
|
+
return new Location(
|
|
76
|
+
rights[0],
|
|
77
|
+
this._ops,
|
|
78
|
+
newPath(
|
|
79
|
+
(path.l || []).concat([this._node]),
|
|
80
|
+
r.length ? r : void 0,
|
|
81
|
+
path.path,
|
|
82
|
+
path.nodes,
|
|
83
|
+
path.changed
|
|
84
|
+
)
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
get leftmost() {
|
|
88
|
+
const path = this._path;
|
|
89
|
+
const lefts = path && path.l;
|
|
90
|
+
return lefts && lefts.length ? new Location(
|
|
91
|
+
lefts[0],
|
|
92
|
+
this._ops,
|
|
93
|
+
newPath(
|
|
94
|
+
void 0,
|
|
95
|
+
lefts.slice(1).concat([this._node], path.r || []),
|
|
96
|
+
path.path,
|
|
97
|
+
path.nodes,
|
|
98
|
+
path.changed
|
|
99
|
+
)
|
|
100
|
+
) : this;
|
|
101
|
+
}
|
|
102
|
+
get rightmost() {
|
|
103
|
+
const path = this._path;
|
|
104
|
+
const rights = path && path.r;
|
|
105
|
+
return rights ? new Location(
|
|
106
|
+
peek(rights),
|
|
107
|
+
this._ops,
|
|
108
|
+
newPath(
|
|
109
|
+
(path.l || []).concat(
|
|
110
|
+
[this._node],
|
|
111
|
+
rights.slice(0, rights.length - 1)
|
|
112
|
+
),
|
|
113
|
+
void 0,
|
|
114
|
+
path.path,
|
|
115
|
+
path.nodes,
|
|
116
|
+
path.changed
|
|
117
|
+
)
|
|
118
|
+
) : this;
|
|
119
|
+
}
|
|
120
|
+
get down() {
|
|
121
|
+
if (!this.isBranch)
|
|
122
|
+
return;
|
|
123
|
+
const children = this.children;
|
|
124
|
+
if (!children)
|
|
125
|
+
return;
|
|
126
|
+
const path = this._path;
|
|
127
|
+
const r = children.slice(1);
|
|
128
|
+
return new Location(
|
|
129
|
+
children[0],
|
|
130
|
+
this._ops,
|
|
131
|
+
newPath(
|
|
132
|
+
void 0,
|
|
133
|
+
r.length ? r : void 0,
|
|
134
|
+
path,
|
|
135
|
+
path ? path.nodes.concat([this._node]) : [this._node]
|
|
136
|
+
)
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
get up() {
|
|
140
|
+
let path = this._path;
|
|
141
|
+
const pnodes = path && path.nodes;
|
|
142
|
+
if (!pnodes)
|
|
143
|
+
return;
|
|
144
|
+
const pnode = peek(pnodes);
|
|
145
|
+
if (path.changed) {
|
|
146
|
+
return new Location(
|
|
147
|
+
this.newNode(
|
|
148
|
+
pnode,
|
|
149
|
+
(path.l || []).concat([this._node], path.r || [])
|
|
150
|
+
),
|
|
151
|
+
this._ops,
|
|
152
|
+
changedPath(path.path)
|
|
153
|
+
);
|
|
154
|
+
} else {
|
|
155
|
+
return new Location(pnode, this._ops, path.path);
|
|
23
156
|
}
|
|
24
|
-
|
|
25
|
-
|
|
157
|
+
}
|
|
158
|
+
get root() {
|
|
159
|
+
const parent = this.up;
|
|
160
|
+
return parent ? parent.root : this._node;
|
|
161
|
+
}
|
|
162
|
+
get prev() {
|
|
163
|
+
let node = this.left;
|
|
164
|
+
if (!node)
|
|
165
|
+
return this.up;
|
|
166
|
+
while (true) {
|
|
167
|
+
const child = node.isBranch ? node.down : void 0;
|
|
168
|
+
if (!child)
|
|
169
|
+
return node;
|
|
170
|
+
node = child.rightmost;
|
|
26
171
|
}
|
|
27
|
-
|
|
28
|
-
|
|
172
|
+
}
|
|
173
|
+
get next() {
|
|
174
|
+
if (this.isBranch)
|
|
175
|
+
return this.down;
|
|
176
|
+
let right = this.right;
|
|
177
|
+
if (right)
|
|
178
|
+
return right;
|
|
179
|
+
let loc = this;
|
|
180
|
+
while (true) {
|
|
181
|
+
const up = loc.up;
|
|
182
|
+
if (!up)
|
|
183
|
+
return;
|
|
184
|
+
right = up.right;
|
|
185
|
+
if (right)
|
|
186
|
+
return right;
|
|
187
|
+
loc = up;
|
|
29
188
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
if (path.changed) {
|
|
100
|
-
return new Location(this.newNode(pnode, (path.l || []).concat([this._node], path.r || [])), this._ops, changedPath(path.path));
|
|
101
|
-
}
|
|
102
|
-
else {
|
|
103
|
-
return new Location(pnode, this._ops, path.path);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
get root() {
|
|
107
|
-
const parent = this.up;
|
|
108
|
-
return parent ? parent.root : this._node;
|
|
109
|
-
}
|
|
110
|
-
get prev() {
|
|
111
|
-
let node = this.left;
|
|
112
|
-
if (!node)
|
|
113
|
-
return this.up;
|
|
114
|
-
while (true) {
|
|
115
|
-
const child = node.isBranch
|
|
116
|
-
? node.down
|
|
117
|
-
: undefined;
|
|
118
|
-
if (!child)
|
|
119
|
-
return node;
|
|
120
|
-
node = child.rightmost;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
get next() {
|
|
124
|
-
if (this.isBranch)
|
|
125
|
-
return this.down;
|
|
126
|
-
let right = this.right;
|
|
127
|
-
if (right)
|
|
128
|
-
return right;
|
|
129
|
-
let loc = this;
|
|
130
|
-
while (true) {
|
|
131
|
-
const up = loc.up;
|
|
132
|
-
if (!up)
|
|
133
|
-
return;
|
|
134
|
-
right = up.right;
|
|
135
|
-
if (right)
|
|
136
|
-
return right;
|
|
137
|
-
loc = up;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
replace(x) {
|
|
141
|
-
return new Location(x, this._ops, changedPath(this._path));
|
|
142
|
-
}
|
|
143
|
-
update(fn, ...xs) {
|
|
144
|
-
return this.replace(fn(this._node, ...xs));
|
|
145
|
-
}
|
|
146
|
-
insertLeft(x) {
|
|
147
|
-
this.ensureNotRoot();
|
|
148
|
-
const path = this._path;
|
|
149
|
-
return new Location(this._node, this._ops, newPath(path.l ? path.l.concat([x]) : [x], path.r, path.path, path.nodes, true));
|
|
150
|
-
}
|
|
151
|
-
insertRight(x) {
|
|
152
|
-
this.ensureNotRoot();
|
|
153
|
-
const path = this._path;
|
|
154
|
-
return new Location(this._node, this._ops, newPath(path.l, [x].concat(path.r || []), path.path, path.nodes, true));
|
|
155
|
-
}
|
|
156
|
-
insertChild(x) {
|
|
157
|
-
this.ensureBranch();
|
|
158
|
-
return this.replace(this.newNode(this._node, [x, ...this.children]));
|
|
159
|
-
}
|
|
160
|
-
appendChild(x) {
|
|
161
|
-
this.ensureBranch();
|
|
162
|
-
return this.replace(this.newNode(this._node, this.children.concat([x])));
|
|
163
|
-
}
|
|
164
|
-
remove() {
|
|
165
|
-
this.ensureNotRoot();
|
|
166
|
-
const path = this._path;
|
|
167
|
-
const lefts = path.l;
|
|
168
|
-
if (lefts ? lefts.length : 0) {
|
|
169
|
-
let loc = new Location(peek(lefts), this._ops, newPath(lefts.slice(0, lefts.length - 1), path.r, path.path, path.nodes, true));
|
|
170
|
-
while (true) {
|
|
171
|
-
const child = loc.isBranch ? loc.down : undefined;
|
|
172
|
-
if (!child)
|
|
173
|
-
return loc;
|
|
174
|
-
loc = child.rightmost;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
return new Location(this.newNode(peek(path.nodes), path.r || []), this._ops, changedPath(path.path));
|
|
178
|
-
}
|
|
179
|
-
newNode(node, children) {
|
|
180
|
-
return this._ops.factory(node, children);
|
|
181
|
-
}
|
|
182
|
-
ensureNotRoot() {
|
|
183
|
-
assert(!!this._path, "can't insert at root level");
|
|
184
|
-
}
|
|
185
|
-
ensureBranch() {
|
|
186
|
-
assert(this.isBranch, "can only insert in branches");
|
|
189
|
+
}
|
|
190
|
+
replace(x) {
|
|
191
|
+
return new Location(x, this._ops, changedPath(this._path));
|
|
192
|
+
}
|
|
193
|
+
update(fn, ...xs) {
|
|
194
|
+
return this.replace(fn(this._node, ...xs));
|
|
195
|
+
}
|
|
196
|
+
insertLeft(x) {
|
|
197
|
+
this.ensureNotRoot();
|
|
198
|
+
const path = this._path;
|
|
199
|
+
return new Location(
|
|
200
|
+
this._node,
|
|
201
|
+
this._ops,
|
|
202
|
+
newPath(
|
|
203
|
+
path.l ? path.l.concat([x]) : [x],
|
|
204
|
+
path.r,
|
|
205
|
+
path.path,
|
|
206
|
+
path.nodes,
|
|
207
|
+
true
|
|
208
|
+
)
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
insertRight(x) {
|
|
212
|
+
this.ensureNotRoot();
|
|
213
|
+
const path = this._path;
|
|
214
|
+
return new Location(
|
|
215
|
+
this._node,
|
|
216
|
+
this._ops,
|
|
217
|
+
newPath(
|
|
218
|
+
path.l,
|
|
219
|
+
[x].concat(path.r || []),
|
|
220
|
+
path.path,
|
|
221
|
+
path.nodes,
|
|
222
|
+
true
|
|
223
|
+
)
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
insertChild(x) {
|
|
227
|
+
this.ensureBranch();
|
|
228
|
+
return this.replace(this.newNode(this._node, [x, ...this.children]));
|
|
229
|
+
}
|
|
230
|
+
appendChild(x) {
|
|
231
|
+
this.ensureBranch();
|
|
232
|
+
return this.replace(
|
|
233
|
+
this.newNode(this._node, this.children.concat([x]))
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
remove() {
|
|
237
|
+
this.ensureNotRoot();
|
|
238
|
+
const path = this._path;
|
|
239
|
+
const lefts = path.l;
|
|
240
|
+
if (lefts ? lefts.length : 0) {
|
|
241
|
+
let loc = new Location(
|
|
242
|
+
peek(lefts),
|
|
243
|
+
this._ops,
|
|
244
|
+
newPath(
|
|
245
|
+
lefts.slice(0, lefts.length - 1),
|
|
246
|
+
path.r,
|
|
247
|
+
path.path,
|
|
248
|
+
path.nodes,
|
|
249
|
+
true
|
|
250
|
+
)
|
|
251
|
+
);
|
|
252
|
+
while (true) {
|
|
253
|
+
const child = loc.isBranch ? loc.down : void 0;
|
|
254
|
+
if (!child)
|
|
255
|
+
return loc;
|
|
256
|
+
loc = child.rightmost;
|
|
257
|
+
}
|
|
187
258
|
}
|
|
259
|
+
return new Location(
|
|
260
|
+
this.newNode(peek(path.nodes), path.r || []),
|
|
261
|
+
this._ops,
|
|
262
|
+
changedPath(path.path)
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
newNode(node, children) {
|
|
266
|
+
return this._ops.factory(node, children);
|
|
267
|
+
}
|
|
268
|
+
ensureNotRoot() {
|
|
269
|
+
assert(!!this._path, "can't insert at root level");
|
|
270
|
+
}
|
|
271
|
+
ensureBranch() {
|
|
272
|
+
assert(this.isBranch, "can only insert in branches");
|
|
273
|
+
}
|
|
188
274
|
}
|
|
189
|
-
|
|
190
|
-
|
|
275
|
+
const zipper = (ops, node) => new Location(node, ops);
|
|
276
|
+
const arrayZipper = (root) => zipper(
|
|
277
|
+
{
|
|
191
278
|
branch: isArray,
|
|
192
279
|
children: (x) => x,
|
|
193
|
-
factory: (_, xs) => xs
|
|
194
|
-
},
|
|
280
|
+
factory: (_, xs) => xs
|
|
281
|
+
},
|
|
282
|
+
root
|
|
283
|
+
);
|
|
284
|
+
export {
|
|
285
|
+
Location,
|
|
286
|
+
arrayZipper,
|
|
287
|
+
zipper
|
|
288
|
+
};
|