@bablr/agast-helpers 0.6.1 → 0.7.1

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/lib/path.js CHANGED
@@ -1,8 +1,9 @@
1
1
  import { WeakStackFrame } from '@bablr/weak-stack';
2
2
  import * as btree from '@bablr/agast-helpers/btree';
3
+ import * as sumtree from '@bablr/agast-helpers/sumtree';
3
4
  import {
4
5
  ReferenceTag,
5
- ArrayInitializerTag,
6
+ InitializerTag,
6
7
  EmbeddedNode,
7
8
  DoctypeTag,
8
9
  OpenNodeTag,
@@ -12,7 +13,7 @@ import {
12
13
  ShiftTag,
13
14
  } from './symbols.js';
14
15
  import {
15
- buildArrayInitializerTag,
16
+ buildInitializerTag,
16
17
  buildEmbeddedNode,
17
18
  buildGapTag,
18
19
  buildReferenceTag,
@@ -20,10 +21,11 @@ import {
20
21
  } from './builders.js';
21
22
 
22
23
  export const getOpenTag = (node) => {
23
- let tag = btree.getAt(0, node.children);
24
+ if (!node.children.length) return null;
25
+ let tag = sumtree.getAt(0, node.children);
24
26
  if (tag.type === NullTag || tag.type === GapTag) return null;
25
27
  if (tag.type === DoctypeTag) {
26
- tag = btree.getAt(1, node.children);
28
+ tag = sumtree.getAt(1, node.children);
27
29
  }
28
30
  if (tag && tag.type !== OpenNodeTag) throw new Error();
29
31
  return tag;
@@ -31,13 +33,13 @@ export const getOpenTag = (node) => {
31
33
 
32
34
  export const getCloseTag = (node) => {
33
35
  const { children } = node;
34
- const tag = btree.getAt(-1, children);
36
+ const tag = sumtree.getAt(-1, children);
35
37
  if (tag.type !== CloseNodeTag) return null;
36
38
  return tag;
37
39
  };
38
40
 
39
41
  export const isNullNode = (node) => {
40
- return node && node.type === null && btree.getAt(0, node.children).type === NullTag;
42
+ return node && node.type === null && sumtree.getAt(0, node.children).type === NullTag;
41
43
  };
42
44
 
43
45
  export const isFragmentNode = (node) => {
@@ -45,7 +47,117 @@ export const isFragmentNode = (node) => {
45
47
  };
46
48
 
47
49
  export const isGapNode = (node) => {
48
- return node && node.type === null && btree.getAt(0, node.children).type === GapTag;
50
+ return node && node.type === null && sumtree.getAt(0, node.children).type === GapTag;
51
+ };
52
+
53
+ export const getChildPropertyIndex = (agAstNode, childrenIndex) => {
54
+ let stack = sumtree.findPath(childrenIndex, agAstNode.children);
55
+ let { node, index: leafIdx } = stack.value;
56
+ let leaf = sumtree.getAt(leafIdx, node);
57
+
58
+ if (leaf.type !== ReferenceTag) return null;
59
+
60
+ let { name, isArray } = leaf.value;
61
+ let count = -1;
62
+
63
+ if (!isArray) return null;
64
+
65
+ for (let i = leafIdx; i >= 0; i--) {
66
+ let value = sumtree.getAt(i, node);
67
+
68
+ if (value.type === ReferenceTag && value.value.name === name) {
69
+ count++;
70
+ }
71
+ }
72
+ stack = stack.pop();
73
+
74
+ if (!stack.size) return count - 1;
75
+
76
+ ({ node, index: leafIdx } = stack.value);
77
+
78
+ do {
79
+ for (let i = leafIdx - 1; i >= 0; i--) {
80
+ let childNode = sumtree.getValues(node)[i];
81
+ let { references } = sumtree.getSums(childNode);
82
+
83
+ if (hasOwn(references, name)) {
84
+ count += references[name];
85
+ }
86
+ }
87
+ stack = stack.pop();
88
+ if (stack.size) {
89
+ ({ node, index: leafIdx } = stack.value);
90
+ }
91
+ } while (stack.size);
92
+
93
+ // the initializer doesn't matter to us
94
+ // also we're going from fenceposts to gaps
95
+ return count - 1;
96
+ };
97
+
98
+ export const getPropertyChildrenIndex = (agAstNode, reference) => {
99
+ let { index, name } = reference.value;
100
+ if (isArray) {
101
+ if (index == null) {
102
+ index = btree.getSize(agAstNode.properties[name]) - 1;
103
+ }
104
+ }
105
+
106
+ let firstRefIndex = __getPropertyChildrenIndex(agAstNode, name, 0);
107
+ let nextTag = sumtree.getAt(firstRefIndex + 1, agAstNode.children);
108
+
109
+ return __getPropertyChildrenIndex(
110
+ agAstNode,
111
+ name,
112
+ index + (nextTag.type === InitializerTag ? 1 : 0),
113
+ );
114
+ };
115
+
116
+ export const getInitializerChildrenIndex = (agAstNode, reference) => {
117
+ return __getPropertyChildrenIndex(agAstNode, reference.value.name, 0);
118
+ };
119
+
120
+ const __getPropertyChildrenIndex = (agAstNode, name, index) => {
121
+ let nameCount = 0;
122
+ let node = agAstNode.children;
123
+ let idx = -1;
124
+
125
+ // drill into subtrees, passing over subtrees with too few references of the desired name
126
+ outer: while (node) {
127
+ let isLeaf = sumtree.isLeafNode(node);
128
+ let sums = sumtree.getSums(node);
129
+ let valueNameCount = sums.references[name];
130
+
131
+ if (nameCount + valueNameCount < index) {
132
+ return null;
133
+ }
134
+
135
+ for (const value of sumtree.getValues(node)) {
136
+ if (isLeaf) {
137
+ idx++;
138
+ let tag = value;
139
+ if (tag.type === ReferenceTag && tag.value.name === name) {
140
+ nameCount += 1;
141
+ if (nameCount > index) {
142
+ return idx;
143
+ }
144
+ }
145
+ } else {
146
+ let valueSums = sumtree.getSums(value);
147
+ if (nameCount + valueSums.references[name] > index) {
148
+ node = value;
149
+ continue outer;
150
+ } else {
151
+ nameCount += valueSums.references[name] ?? 0;
152
+ idx += sumtree.getSize(value);
153
+ }
154
+ }
155
+ }
156
+
157
+ return null;
158
+ }
159
+
160
+ return null;
49
161
  };
50
162
 
51
163
  const { hasOwn } = Object;
@@ -70,7 +182,7 @@ export const getProperties = (ref, properties) => {
70
182
  }
71
183
  }
72
184
 
73
- if (isArray) {
185
+ if (isArray || Array.isArray(properties[name])) {
74
186
  return btree.getAt(index ?? -1, properties[name]);
75
187
  } else {
76
188
  return properties[name];
@@ -91,9 +203,12 @@ export const getPropertiesSimple = (ref, properties) => {
91
203
  }
92
204
  };
93
205
 
206
+ export const isDefined = (obj, key) => hasOwn(obj, key) && obj[key] !== undefined;
207
+ export const isUndefined = (obj, key) => !hasOwn(obj, key) || obj[key] === undefined;
208
+
94
209
  export const get = (ref, node) => {
95
- const { flags } = ref.value;
96
210
  const result = getProperties(ref, node.properties);
211
+ const { flags } = result.reference.value;
97
212
 
98
213
  return flags.expression ? btree.getAt(-1, result.node) : result?.node;
99
214
  };
@@ -106,7 +221,10 @@ export const getShifted = (shiftIndex, ref, node) => {
106
221
  };
107
222
 
108
223
  export const add = (node, reference, value, shift = null) => {
109
- if (node == null || reference == null || value == null) throw new Error();
224
+ if (node == null || reference == null) throw new Error('invalid arguments to add');
225
+ if (value === node) {
226
+ throw new Error('cannot add a node to itself');
227
+ }
110
228
 
111
229
  const { properties } = node;
112
230
  const { name, isArray, flags } = reference.value;
@@ -115,22 +233,26 @@ export const add = (node, reference, value, shift = null) => {
115
233
  throw new Error('Only fragments can have . properties');
116
234
  }
117
235
 
236
+ if (Array.isArray(value) && !isArray) throw new Error();
237
+ if (Array.isArray(value) && value.length) throw new Error();
238
+
118
239
  if (name == null) throw new Error();
119
240
 
120
- const lastChild = btree.getAt(-1, node.children);
241
+ const lastChild = sumtree.getAt(-1, node.children);
121
242
 
122
243
  if (lastChild.type === ReferenceTag) {
123
244
  if (!referencesAreEqual(lastChild, reference)) throw new Error();
124
245
  } else if (lastChild.type !== ShiftTag) {
125
- node.children = btree.push(node.children, shift == null ? reference : buildShiftTag(shift));
246
+ if ([ShiftTag, ReferenceTag].includes(sumtree.getAt(-1, node.children).type)) throw new Error();
247
+ node.children = sumtree.push(node.children, shift > 0 ? buildShiftTag(shift) : reference);
126
248
  }
127
249
 
128
250
  if (name === '#' || name === '@') {
129
- node.children = btree.push(node.children, buildEmbeddedNode(value));
251
+ node.children = sumtree.push(node.children, buildEmbeddedNode(value));
130
252
  } else {
131
253
  if (isArray) {
132
254
  let isInitializer = Array.isArray(value);
133
- let exists = !isInitializer && hasOwn(properties, name);
255
+ let exists = !isInitializer && isDefined(properties, name);
134
256
 
135
257
  let existed = exists;
136
258
  if (!existed) {
@@ -138,19 +260,19 @@ export const add = (node, reference, value, shift = null) => {
138
260
  throw new Error('Array value only allowed for initialization');
139
261
 
140
262
  properties[name] = [];
141
- node.children = btree.push(node.children, buildArrayInitializerTag(value));
263
+ node.children = sumtree.push(node.children, buildInitializerTag(true));
142
264
  exists = !isInitializer;
143
265
  }
144
266
 
145
267
  if (exists) {
146
268
  if (!existed) {
147
- if (btree.getAt(-1, node.children).type === ReferenceTag) throw new Error();
148
- node.children = btree.push(node.children, reference);
269
+ if (sumtree.getAt(-1, node.children).type === ReferenceTag) throw new Error();
270
+ node.children = sumtree.push(node.children, reference);
149
271
  }
150
272
 
151
273
  let newBinding;
152
274
  if (flags.expression) {
153
- let shiftedNodes = shift != null ? btree.getAt(-1, properties[name])?.node : [];
275
+ let shiftedNodes = shift > 0 ? btree.getAt(-1, properties[name])?.node : [];
154
276
 
155
277
  newBinding = {
156
278
  reference,
@@ -161,40 +283,67 @@ export const add = (node, reference, value, shift = null) => {
161
283
  }
162
284
 
163
285
  properties[name] =
164
- shift != null
286
+ shift > 0
165
287
  ? btree.replaceAt(-1, properties[name], newBinding)
166
288
  : btree.push(properties[name], newBinding);
167
289
 
168
- node.children = btree.push(node.children, buildGapTag(value));
290
+ node.children = sumtree.push(node.children, buildGapTag(value));
169
291
  }
170
292
  } else {
171
- if (hasOwn(properties, name)) {
172
- throw new Error();
173
- }
174
-
175
- if (flags.expression) {
176
- let shiftedNodes = shift ? properties[name]?.node : [];
177
- properties[name] = { reference, node: btree.push(shiftedNodes, value) };
293
+ if (value === undefined) {
294
+ if (isUndefined(properties, name)) {
295
+ let newBinding;
296
+ if (flags.expression) {
297
+ let shiftedNodes = shift > 0 ? btree.getAt(-1, properties[name])?.node : [];
298
+
299
+ newBinding = {
300
+ reference,
301
+ node: btree.push(shiftedNodes, undefined),
302
+ };
303
+ } else {
304
+ newBinding = { reference, node: undefined };
305
+ }
306
+ properties[name] = newBinding;
307
+ node.children = sumtree.push(node.children, buildInitializerTag());
308
+ }
178
309
  } else {
179
- properties[name] = { reference, node: value };
310
+ if (flags.expression) {
311
+ if (shift == null) {
312
+ throw new Error();
313
+ }
314
+
315
+ let shiftedNodes = shift ? properties[name]?.node : [];
316
+ properties[name] = { reference, node: btree.push(shiftedNodes, value) };
317
+ } else {
318
+ if (shift != null) throw new Error();
319
+ if (hasOwn(properties, name) && properties[name].node !== undefined) {
320
+ throw new Error();
321
+ }
322
+ properties[name] = { reference, node: value };
323
+ }
324
+ node.children = sumtree.push(node.children, buildGapTag(value));
180
325
  }
181
- node.children = btree.push(node.children, buildGapTag(value));
182
326
  }
183
327
  }
184
328
  };
185
329
 
186
- export function* allTagPathsFor(range) {
330
+ export function* allTagPathsFor(range, options = {}) {
187
331
  if (range == null) return;
188
332
 
333
+ const { unshift = false } = options;
189
334
  let startPath = range[0];
190
335
  let endPath = range[1];
191
336
  let path = startPath;
192
337
 
193
338
  while (path) {
194
- if (path.inner) {
339
+ if (path.inner && path.previousSibling.tag.type === ReferenceTag) {
195
340
  path = new TagPath(path.innerPath, 0);
196
341
  }
197
342
 
343
+ if (path.path.depth < startPath.path.depth) {
344
+ return;
345
+ }
346
+
198
347
  yield path;
199
348
 
200
349
  if (
@@ -205,24 +354,38 @@ export function* allTagPathsFor(range) {
205
354
  return;
206
355
  }
207
356
 
208
- path = path.next;
357
+ let gapPath = path.path.parent && TagPath.from(path.path.parent, path.path.referenceIndex + 1);
358
+
359
+ if (
360
+ endPath &&
361
+ path.tag.type === CloseNodeTag &&
362
+ gapPath &&
363
+ gapPath.childrenIndex === endPath.childrenIndex &&
364
+ gapPath.path.node === endPath.path.node
365
+ ) {
366
+ return;
367
+ }
368
+ path = unshift ? path.nextUnshifted : path.next;
209
369
  }
210
370
  }
211
371
 
212
- export function* allTagsFor(range) {
213
- for (const path of allTagPathsFor(range)) {
372
+ export function* allTagsFor(range, options = {}) {
373
+ for (const path of allTagPathsFor(range, options)) {
214
374
  yield path.tag;
215
375
  }
216
376
  }
217
377
 
378
+ export const buildFullRange = (node) => {
379
+ const sum = sumtree.getSize(node.children);
380
+ return sum ? [0, sum - 1] : null;
381
+ };
382
+
218
383
  export function* ownTagPathsFor(range) {
219
384
  if (!isArray(range)) throw new Error();
220
385
 
221
386
  const startPath = range[0];
222
387
  const endPath = range[1];
223
388
 
224
- let path = startPath;
225
-
226
389
  if (startPath.outer !== endPath.outer) throw new Error();
227
390
 
228
391
  const { children } = startPath.outer;
@@ -232,46 +395,6 @@ export function* ownTagPathsFor(range) {
232
395
  }
233
396
  }
234
397
 
235
- export class PathResolver {
236
- constructor() {
237
- this.childrenIndex = -1;
238
- this.counters = {};
239
- this.reference = null;
240
- }
241
-
242
- advance(tag) {
243
- this.childrenIndex++;
244
-
245
- const { counters } = this;
246
- if (tag.type === ReferenceTag) {
247
- const { isArray, name, flags } = tag.value;
248
-
249
- let resolvedReference = tag;
250
-
251
- this.reference = tag;
252
-
253
- if (isArray) {
254
- if (hasOwn(counters, name)) {
255
- const counter = ++counters[name];
256
-
257
- resolvedReference = buildReferenceTag(name, isArray, flags, counter);
258
- }
259
- } else if (name !== '@' && name !== '#') {
260
- if (hasOwn(counters, name)) throw new Error();
261
-
262
- counters[name] = true;
263
- }
264
-
265
- return resolvedReference;
266
- } else if (tag.type === ArrayInitializerTag) {
267
- counters[this.reference.value.name] = -1;
268
- return this.reference.value.name;
269
- }
270
- }
271
- }
272
-
273
- Object.freeze(PathResolver.prototype);
274
-
275
398
  const findRight = (arr, predicate) => {
276
399
  for (let i = arr.length - 1; i >= 0; i--) {
277
400
  const value = arr[i];
@@ -318,94 +441,6 @@ const skipToDepth = (depth, frame) => {
318
441
  return parent;
319
442
  };
320
443
 
321
- const buildBindings = (node) => {
322
- const { children, properties } = node;
323
- const referenceIndexes = new Array(children.length);
324
- const childrenIndexes = Object.fromEntries(Object.keys(properties).map((key) => [key, null]));
325
-
326
- const resolver = new PathResolver();
327
-
328
- for (const tag of btree.traverse(children)) {
329
- resolver.advance(tag);
330
- const i = resolver.childrenIndex;
331
-
332
- if (tag.type === ReferenceTag) {
333
- const { name, isArray, index } = tag.value;
334
-
335
- if (!name) throw new Error();
336
- // if (name === '.') throw new Error();
337
-
338
- const counter = isArray
339
- ? hasOwn(resolver.counters, name)
340
- ? resolver.counters[name]
341
- : null
342
- : null;
343
-
344
- if (index != null && index !== counter) throw new Error();
345
-
346
- referenceIndexes[i] = counter;
347
-
348
- if (isArray) {
349
- if (childrenIndexes[name] === null || !hasOwn(childrenIndexes, name)) {
350
- childrenIndexes[name] = [];
351
- } else if (counter >= 0) {
352
- childrenIndexes[name][counter] = i;
353
- } else {
354
- throw new Error();
355
- }
356
- } else {
357
- if (name !== '#' && name !== '@') {
358
- childrenIndexes[name] = i;
359
- }
360
- }
361
- } else {
362
- referenceIndexes[i] = null;
363
- }
364
- }
365
-
366
- return { referenceIndexes, childrenIndexes };
367
- };
368
-
369
- const nodeStates = new WeakMap();
370
-
371
- // TODO remove this; it is a very bad API to have to support!!
372
- export const updatePath = (path, tag) => {
373
- const { node, childrenIndexes, referenceIndexes } = path;
374
- const i = btree.getSum(node.children) - 1;
375
-
376
- if (tag.type === ReferenceTag) {
377
- const { name, isArray, index: literalArrayIndex } = tag.value;
378
-
379
- const arrayIndex = isArray
380
- ? hasOwn(node.properties, name)
381
- ? btree.getSum(node.properties[name])
382
- : -1
383
- : null;
384
-
385
- if (literalArrayIndex != null && literalArrayIndex !== arrayIndex) throw new Error();
386
-
387
- referenceIndexes[i] = arrayIndex;
388
-
389
- if (isArray) {
390
- if (!hasOwn(childrenIndexes, name) || childrenIndexes[name] === null) {
391
- childrenIndexes[name] = [];
392
- } else {
393
- if (arrayIndex >= 0) {
394
- childrenIndexes[name][arrayIndex] = i;
395
- }
396
- }
397
- } else {
398
- if (name !== '#' && name !== '@') {
399
- childrenIndexes[name] = i;
400
- }
401
- }
402
- } else if (tag.type === ArrayInitializerTag) {
403
- referenceIndexes[i] = -1;
404
- } else {
405
- referenceIndexes[i] = null;
406
- }
407
- };
408
-
409
444
  export const Path = class AgastPath extends WeakStackFrame {
410
445
  static from(node) {
411
446
  return this.create(node);
@@ -425,11 +460,10 @@ export const Path = class AgastPath extends WeakStackFrame {
425
460
 
426
461
  if (
427
462
  referenceIndex != null &&
428
- ![ReferenceTag, ShiftTag].includes(btree.getAt(referenceIndex, parent.node.children).type)
429
- )
463
+ ![ReferenceTag, ShiftTag].includes(sumtree.getAt(referenceIndex, parent.node.children).type)
464
+ ) {
430
465
  throw new Error();
431
-
432
- nodeStates.set(node, buildBindings(node));
466
+ }
433
467
 
434
468
  if (parent && (!this.reference || ![ReferenceTag, ShiftTag].includes(this.reference.type))) {
435
469
  throw new Error();
@@ -440,16 +474,24 @@ export const Path = class AgastPath extends WeakStackFrame {
440
474
  buildSkips(this);
441
475
  }
442
476
 
443
- get referenceIndexes() {
444
- return nodeStates.get(this.node).referenceIndexes;
477
+ get openTagPath() {
478
+ return TagPath.from(this, 0);
479
+ }
480
+
481
+ get openTag() {
482
+ return this.openTagPath.tag;
483
+ }
484
+
485
+ get closeTagPath() {
486
+ return TagPath.from(this, -1);
445
487
  }
446
488
 
447
- get childrenIndexes() {
448
- return nodeStates.get(this.node).childrenIndexes;
489
+ get closeTag() {
490
+ return this.closeTagPath.tag;
449
491
  }
450
492
 
451
493
  get reference() {
452
- return this.outer && btree.getAt(this.referenceIndex, this.outer.children);
494
+ return this.outer && sumtree.getAt(this.referenceIndex, this.outer.children);
453
495
  }
454
496
 
455
497
  get referencePath() {
@@ -457,7 +499,7 @@ export const Path = class AgastPath extends WeakStackFrame {
457
499
  }
458
500
 
459
501
  get gap() {
460
- return this.outer && btree.getAt(this.referenceIndex + 1, this.outer.children);
502
+ return this.outer && sumtree.getAt(this.referenceIndex + 1, this.outer.children);
461
503
  }
462
504
 
463
505
  get gapPath() {
@@ -473,9 +515,7 @@ export const Path = class AgastPath extends WeakStackFrame {
473
515
 
474
516
  let shiftOffset = (shiftIndex ?? 0) * 2;
475
517
 
476
- return (
477
- node && this.push(node, getPropertiesSimple(reference, this.childrenIndexes) + shiftOffset)
478
- );
518
+ return node && this.push(node, getPropertyChildrenIndex(this.node, reference) + shiftOffset);
479
519
  }
480
520
 
481
521
  at(depth) {
@@ -499,12 +539,16 @@ export class TagPath {
499
539
  }
500
540
 
501
541
  static from(path, childrenIndex) {
502
- let size = btree.getSum(path.node.children);
542
+ let size = sumtree.getSize(path.node.children);
503
543
  let index = childrenIndex < 0 ? size + childrenIndex : childrenIndex;
504
544
 
505
545
  return index >= 0 && index < size ? new TagPath(path, index) : null;
506
546
  }
507
547
 
548
+ static fromNode(node, childrenIndex) {
549
+ return TagPath.from(Path.from(node), childrenIndex);
550
+ }
551
+
508
552
  get tag() {
509
553
  return this.child;
510
554
  }
@@ -514,16 +558,24 @@ export class TagPath {
514
558
  }
515
559
 
516
560
  get child() {
517
- return btree.getAt(this.childrenIndex, this.path.node.children);
561
+ return sumtree.getAt(this.childrenIndex, this.path.node.children);
562
+ }
563
+
564
+ siblingPathAt(index) {
565
+ return TagPath.from(this.path, index);
566
+ }
567
+
568
+ siblingAt(index) {
569
+ return this.siblingPathAt(index)?.tag;
518
570
  }
519
571
 
520
572
  get nextSibling() {
521
573
  const { path, childrenIndex } = this;
522
574
 
523
575
  const child =
524
- childrenIndex + 1 >= btree.getSum(path.node.children)
576
+ childrenIndex + 1 >= sumtree.getSize(path.node.children)
525
577
  ? null
526
- : btree.getAt(childrenIndex + 1, path.node.children);
578
+ : sumtree.getAt(childrenIndex + 1, path.node.children);
527
579
 
528
580
  return child && new TagPath(path, childrenIndex + 1);
529
581
  }
@@ -534,8 +586,8 @@ export class TagPath {
534
586
  let leaving = false;
535
587
 
536
588
  for (;;) {
537
- let prevTag = btree.getAt(childrenIndex - 1, path.node.children);
538
- let tag = btree.getAt(childrenIndex, path.node.children);
589
+ let prevTag = sumtree.getAt(childrenIndex - 1, path.node.children);
590
+ let tag = sumtree.getAt(childrenIndex, path.node.children);
539
591
  let isInitialTag = path.node === this.path.node && childrenIndex === this.childrenIndex;
540
592
  let wasLeaving = leaving;
541
593
  leaving = false;
@@ -562,18 +614,20 @@ export class TagPath {
562
614
  if (tag.type === GapTag && !wasLeaving && !isGapNode(path.node)) {
563
615
  let refIndex = childrenIndex - 1;
564
616
  let refTag;
565
- let prevTag = btree.getAt(childrenIndex - 1, path.node.children);
566
- let nextTag = btree.getAt(childrenIndex + 1, path.node.children);
617
+ let prevTag = sumtree.getAt(childrenIndex - 1, path.node.children);
618
+ let nextTag = sumtree.getAt(childrenIndex + 1, path.node.children);
567
619
 
568
620
  if (
569
621
  path.parent &&
570
- btree.getAt(path.referenceIndex, path.outer.children)?.type === ShiftTag &&
571
- childrenIndex === 2
622
+ sumtree.getAt(path.referenceIndex, path.outer.children)?.type === ShiftTag
572
623
  ) {
573
- childrenIndex = path.referenceIndex + 1;
574
- path = path.parent;
575
- leaving = true;
576
- continue;
624
+ let third = sumtree.getAt(2, path.node.children);
625
+ if (childrenIndex === 2 || (childrenIndex === 4 && third.type === InitializerTag)) {
626
+ childrenIndex = path.referenceIndex + 1;
627
+ path = path.parent;
628
+ leaving = true;
629
+ continue;
630
+ }
577
631
  }
578
632
 
579
633
  if (prevTag.type === ReferenceTag) {
@@ -587,7 +641,7 @@ export class TagPath {
587
641
  const { name, isArray, flags } = refTag.value;
588
642
  let resolvedReference = refTag;
589
643
  if (isArray) {
590
- let index = path.referenceIndexes[refIndex];
644
+ let index = getChildPropertyIndex(path.node, refIndex);
591
645
  resolvedReference =
592
646
  index === -1 ? null : buildReferenceTag(name, index != null, flags, index);
593
647
  }
@@ -602,12 +656,12 @@ export class TagPath {
602
656
  } else {
603
657
  if (
604
658
  !['#', '@'].includes(refTag.value.name) &&
605
- (!refTag.value.isArray || path.referenceIndexes[refIndex] != null)
659
+ (!refTag.value.isArray || getChildPropertyIndex(path.node, refIndex) != null)
606
660
  ) {
607
661
  const { name, isArray, flags } = refTag.value;
608
662
  let resolvedReference = refTag;
609
663
  if (isArray) {
610
- let index = path.referenceIndexes[refIndex];
664
+ let index = getChildPropertyIndex(path.node, refIndex);
611
665
  resolvedReference =
612
666
  index === -1 ? null : buildReferenceTag(name, index != null, flags, index);
613
667
  }
@@ -625,22 +679,22 @@ export class TagPath {
625
679
  }
626
680
  } else if (prevTag.type === ShiftTag) {
627
681
  let refIndex = childrenIndex - prevTag.value.index * 2 - 1;
628
- let refTag = btree.getAt(refIndex, path.node.children);
682
+ let refTag = sumtree.getAt(refIndex, path.node.children);
629
683
 
630
684
  const { name, isArray, flags } = refTag.value;
631
685
  let resolvedReference = refTag;
632
686
  if (isArray) {
633
- let index = path.referenceIndexes[refIndex];
687
+ let index = getChildPropertyIndex(path.node, refIndex);
634
688
  resolvedReference =
635
689
  index === -1 ? null : buildReferenceTag(name, index != null, flags, index);
636
690
  }
637
691
 
638
692
  if (resolvedReference) {
639
- path = path.get(resolvedReference);
693
+ path = path.get(resolvedReference, prevTag.value.index);
640
694
  // this was introducing errors
641
695
  // caused us to return to a point before we left
642
696
  path.referenceIndex = childrenIndex;
643
- childrenIndex = 3;
697
+ childrenIndex = sumtree.getAt(2, path.node.children).type === InitializerTag ? 5 : 3;
644
698
  continue;
645
699
  }
646
700
  } else {
@@ -651,12 +705,12 @@ export class TagPath {
651
705
  // shift
652
706
  if (tag.type === ShiftTag) {
653
707
  let refIndex = childrenIndex - tag.value.index * 2;
654
- let refTag = btree.getAt(refIndex, path.node.children);
708
+ let refTag = sumtree.getAt(refIndex, path.node.children);
655
709
 
656
710
  const { name, isArray, flags } = refTag.value;
657
711
  let resolvedReference = null;
658
712
  if (isArray) {
659
- let index = path.referenceIndexes[refIndex];
713
+ let index = getChildPropertyIndex(path.node, refIndex);
660
714
  resolvedReference =
661
715
  index === -1 ? null : buildReferenceTag(name, index != null, flags, index);
662
716
  } else {
@@ -665,18 +719,16 @@ export class TagPath {
665
719
 
666
720
  if (resolvedReference) {
667
721
  path = path.get(resolvedReference, tag.value.index);
722
+
723
+ if (!path) return null;
724
+
668
725
  childrenIndex = 0;
669
726
  continue;
670
727
  }
671
-
672
- // go backwards through any other shifts until we're done
673
- // path = path.parent;
674
- // childrenIndex = 0;
675
- // continue;
676
728
  }
677
729
 
678
730
  // over
679
- if (path.node && childrenIndex + 1 < btree.getSum(path.node.children)) {
731
+ if (path.node && childrenIndex + 1 < sumtree.getSize(path.node.children)) {
680
732
  childrenIndex++;
681
733
  continue;
682
734
  }
@@ -684,9 +736,9 @@ export class TagPath {
684
736
  // out
685
737
  if (path.referenceIndex != null) {
686
738
  do {
687
- if (btree.getAt(path.referenceIndex + 2, path.outer.children)?.type === ShiftTag) {
739
+ if (sumtree.getAt(path.referenceIndex + 2, path.outer.children)?.type === ShiftTag) {
688
740
  childrenIndex =
689
- btree.getSum(path.outer.children) > path.referenceIndex + 2
741
+ sumtree.getSize(path.outer.children) > path.referenceIndex + 2
690
742
  ? path.referenceIndex + 2
691
743
  : null;
692
744
  } else {
@@ -711,7 +763,7 @@ export class TagPath {
711
763
  let leaving = false;
712
764
 
713
765
  for (;;) {
714
- let tag = btree.getAt(childrenIndex, path.node.children);
766
+ let tag = sumtree.getAt(childrenIndex, path.node.children);
715
767
  let isInitialTag = path.node === this.path.node && childrenIndex === this.childrenIndex;
716
768
  let wasLeaving = leaving;
717
769
  leaving = false;
@@ -739,7 +791,7 @@ export class TagPath {
739
791
  if (tag.type === GapTag && !wasLeaving && !isGapNode(path.node)) {
740
792
  let refIndex = childrenIndex - 1;
741
793
  let refTag;
742
- let prevTag = btree.getAt(childrenIndex - 1, path.node.children);
794
+ let prevTag = sumtree.getAt(childrenIndex - 1, path.node.children);
743
795
 
744
796
  if (prevTag.type === ShiftTag) {
745
797
  // continue
@@ -748,12 +800,12 @@ export class TagPath {
748
800
 
749
801
  if (
750
802
  !['#', '@'].includes(refTag.value.name) &&
751
- (!refTag.value.isArray || path.referenceIndexes[refIndex] != null)
803
+ (!refTag.value.isArray || getChildPropertyIndex(path.node, refIndex) != null)
752
804
  ) {
753
805
  const { name, isArray, flags } = refTag.value;
754
806
  let resolvedReference = refTag;
755
807
  if (isArray) {
756
- let index = path.referenceIndexes[refIndex];
808
+ let index = getChildPropertyIndex(path.node, refIndex);
757
809
  resolvedReference =
758
810
  index === -1 ? null : buildReferenceTag(name, index != null, flags, index);
759
811
  }
@@ -774,7 +826,7 @@ export class TagPath {
774
826
  }
775
827
 
776
828
  // over
777
- if (path.node && childrenIndex + 1 < btree.getSum(path.node.children)) {
829
+ if (path.node && childrenIndex + 1 < sumtree.getSize(path.node.children)) {
778
830
  childrenIndex++;
779
831
  continue;
780
832
  }
@@ -814,7 +866,7 @@ export class TagPath {
814
866
  let leaving = false;
815
867
 
816
868
  for (;;) {
817
- let tag = btree.getAt(childrenIndex, path.node.children);
869
+ let tag = sumtree.getAt(childrenIndex, path.node.children);
818
870
  let isInitialTag = path.node === this.path.node && childrenIndex === this.childrenIndex;
819
871
  let wasLeaving = leaving;
820
872
  leaving = false;
@@ -834,7 +886,7 @@ export class TagPath {
834
886
  // in
835
887
  if (tag.type === EmbeddedNode && !wasLeaving) {
836
888
  path = path.push(tag.value, childrenIndex - 1);
837
- childrenIndex = btree.getSum(tag.value.children) - 1;
889
+ childrenIndex = sumtree.getSize(tag.value.children) - 1;
838
890
  continue;
839
891
  }
840
892
 
@@ -842,7 +894,7 @@ export class TagPath {
842
894
  if (tag.type === GapTag && !wasLeaving && !isGapNode(path.node)) {
843
895
  let refIndex = childrenIndex - 1;
844
896
  let refTag;
845
- let prevTag = btree.getAt(childrenIndex - 1, path.node.children);
897
+ let prevTag = sumtree.getAt(childrenIndex - 1, path.node.children);
846
898
 
847
899
  if (prevTag.type === ShiftTag) {
848
900
  // continue
@@ -851,12 +903,12 @@ export class TagPath {
851
903
 
852
904
  if (
853
905
  !['#', '@'].includes(refTag.value.name) &&
854
- (!refTag.value.isArray || path.referenceIndexes[refIndex] != null)
906
+ (!refTag.value.isArray || getChildPropertyIndex(path.node, refIndex) != null)
855
907
  ) {
856
908
  const { name, isArray, flags } = refTag.value;
857
909
  let resolvedReference = refTag;
858
910
  if (isArray) {
859
- let index = path.referenceIndexes[refIndex];
911
+ let index = getChildPropertyIndex(path.node, refIndex);
860
912
  resolvedReference =
861
913
  index === -1 ? null : buildReferenceTag(name, index != null, flags, index);
862
914
  }
@@ -877,7 +929,7 @@ export class TagPath {
877
929
  }
878
930
 
879
931
  // over
880
- if (path.node && childrenIndex + 1 < btree.getSum(path.node.children)) {
932
+ if (path.node && childrenIndex + 1 < sumtree.getSize(path.node.children)) {
881
933
  childrenIndex--;
882
934
  continue;
883
935
  }
@@ -904,27 +956,33 @@ export class TagPath {
904
956
  }
905
957
 
906
958
  get innerPath() {
907
- let { tag, previousSibling: ref } = this;
959
+ let { tag, previousSibling: refPath } = this;
908
960
 
909
- if (tag.type !== GapTag || isGapNode(this.node) || ref.tag.type === ShiftTag) {
961
+ if (tag.type !== GapTag || isGapNode(this.node)) {
910
962
  return null;
911
963
  }
912
964
 
913
- if (ref.tag.type !== ReferenceTag) throw new Error();
965
+ let shiftPath = null;
966
+ if (refPath.tag.type === ShiftTag) {
967
+ shiftPath = refPath;
968
+ refPath = TagPath.from(this.path, this.childrenIndex - refPath.tag.value.index * 2 - 1);
969
+ }
970
+
971
+ if (refPath.tag.type !== ReferenceTag) throw new Error();
914
972
 
915
- let resolvedRef = ref.tag;
973
+ let resolvedRef = refPath.tag;
916
974
 
917
- if (ref.tag.value.isArray) {
918
- const { name, flags, isArray } = ref.tag.value;
975
+ if (refPath.tag.value.isArray) {
976
+ const { name, flags, isArray } = refPath.tag.value;
919
977
  resolvedRef = buildReferenceTag(
920
978
  name,
921
979
  isArray,
922
980
  flags,
923
- ref.path.referenceIndexes[ref.childrenIndex],
981
+ getChildPropertyIndex(refPath.path.node, refPath.childrenIndex),
924
982
  );
925
983
  }
926
984
 
927
- return this.path.get(resolvedRef);
985
+ return this.path.get(resolvedRef, shiftPath?.tag.value.index);
928
986
  }
929
987
 
930
988
  equalTo(tagPath) {