bobe 0.0.26 → 0.0.28

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/dist/bobe.cjs.js CHANGED
@@ -157,6 +157,47 @@ class MultiTypeStack {
157
157
  }
158
158
  }
159
159
 
160
+ function macInc(arr) {
161
+ const len = arr.length;
162
+ let candyLast = [],
163
+ i = 0;
164
+ while (i < len) {
165
+ const it = arr[i];
166
+ if (it !== -1) {
167
+ candyLast = [i];
168
+ break;
169
+ }
170
+ i++;
171
+ }
172
+ if (i + 1 >= len) return candyLast;
173
+ const toPrev = new Int32Array(len);
174
+ while (i < len) {
175
+ const target = arr[i];
176
+ if (target === -1) continue;
177
+ let start = -1,
178
+ end = candyLast.length;
179
+ while (start + 1 < end) {
180
+ const mid = start + end >> 1;
181
+ if (arr[candyLast[mid]] < target) {
182
+ start = mid;
183
+ } else {
184
+ end = mid;
185
+ }
186
+ }
187
+ candyLast[end] = i;
188
+ toPrev[i] = candyLast[start];
189
+ i++;
190
+ }
191
+ let length = candyLast.length;
192
+ for (let j = length - 1; j > 0; j--) {
193
+ const prev = toPrev[candyLast[j]];
194
+ candyLast[j - 1] = prev;
195
+ }
196
+ return candyLast;
197
+ }
198
+
199
+ const KEY_INDEX = '__BOBE_KEY_INDEX';
200
+
160
201
  const _excluded = ["dentStack", "isFirstToken"];
161
202
  class Interpreter {
162
203
  constructor(tokenizer) {
@@ -209,6 +250,9 @@ class Interpreter {
209
250
  if (ctx.current.__logicType) {
210
251
  if (isLogicNode) {
211
252
  aoye.setPulling(ctx.current.effect);
253
+ if (ctx.current.__logicType & FakeType.ForItem) {
254
+ ctx.prevSibling = ctx.current.realBefore;
255
+ }
212
256
  }
213
257
  } else {
214
258
  if (ctx.current) {
@@ -259,8 +303,8 @@ class Interpreter {
259
303
  this.tokenizer.resume(snapshot);
260
304
  this.tokenizer.nextToken();
261
305
  this.tokenizer.nextToken();
262
- ctx.prevSibling = parent;
263
306
  ctx.current = forNode.children[++forNode.i];
307
+ ctx.prevSibling = ctx.current.realBefore;
264
308
  continue;
265
309
  }
266
310
  ctx.prevSibling = forNode.prevSibling;
@@ -364,21 +408,25 @@ class Interpreter {
364
408
  this.tokenizer.nextToken();
365
409
  const itemToken = this.tokenizer.nextToken();
366
410
  const isDestruct = itemToken.type === TokenType.InsertionExp;
367
- let itemExp = itemToken.value;
411
+ let itemExp = itemToken.value,
412
+ vars;
368
413
  if (isDestruct) {
369
414
  itemExp = '{' + itemExp + '}';
370
- const vars = itemExp.match(bobeShared.jsVarRegexp).join(',');
371
- itemExp = new Function('item', `let ${vars}; (${itemExp}=item); return {${vars}};`);
415
+ vars = itemExp.match(bobeShared.jsVarRegexp);
416
+ const varStr = vars.join(',');
417
+ itemExp = new Function(itemExp, `return {${varStr}};`);
372
418
  }
373
- let indexName, keyExp;
374
- while (this.tokenizer.code[this.tokenizer.i] !== '\n') {
375
- const next = this.tokenizer.nextToken();
376
- if (next.type !== TokenType.Semicolon) {
377
- if (!indexName) {
378
- indexName = next.value;
379
- } else {
380
- keyExp = next.value;
381
- }
419
+ let indexName,
420
+ keyExp,
421
+ char = this.tokenizer.peekChar();
422
+ if (char === ';') {
423
+ this.tokenizer.nextToken();
424
+ if (this.tokenizer.peekChar() !== '\n') keyExp = this.tokenizer.jsExp().value;
425
+ } else if (char === '\n') ; else {
426
+ indexName = this.tokenizer.nextToken().value;
427
+ if (this.tokenizer.peekChar() === ';') {
428
+ this.tokenizer.nextToken();
429
+ if (this.tokenizer.peekChar() !== '\n') keyExp = this.tokenizer.jsExp().value;
382
430
  }
383
431
  }
384
432
  const owner = this.ctx.stack.peekByType(NodeSort.TokenizerSwitcher)?.node;
@@ -391,12 +439,14 @@ class Interpreter {
391
439
  realBefore: prevSibling?.realAfter || prevSibling,
392
440
  realAfter: null,
393
441
  arr: null,
442
+ arrSignal: null,
394
443
  itemExp,
395
444
  indexName,
396
445
  getKey: null,
397
446
  children: [],
398
447
  effect: null,
399
448
  owner,
449
+ vars,
400
450
  i: 0
401
451
  };
402
452
  if (keyExp) {
@@ -407,6 +457,7 @@ class Interpreter {
407
457
  const cells = data[aoye.Keys.Meta].cells;
408
458
  const hasArrExpKey = Reflect.has(data[aoye.Keys.Raw], arrExp);
409
459
  const arrSignal = hasArrExpKey ? (data[arrExp], cells.get(arrExp)) : new aoye.Computed(this.getFn(data, arrExp));
460
+ forNode.arrSignal = arrSignal;
410
461
  forNode.realAfter = this.insertAfterAnchor('for-after');
411
462
  const _forNode$snapshot = forNode.snapshot;
412
463
  _forNode$snapshot.dentStack;
@@ -414,26 +465,23 @@ class Interpreter {
414
465
  const snapshotForUpdate = _objectWithoutProperties(_forNode$snapshot, _excluded);
415
466
  let isFirstRender = true;
416
467
  forNode.effect = new aoye.Effect(() => {
417
- let arr = forNode.arr = arrSignal.get();
468
+ let arr = arrSignal.get();
418
469
  arr[aoye.Keys.Iterator];
419
- arr = aoye.toRaw(arr);
470
+ const prevCtx = aoye.getPulling();
471
+ aoye.setPulling(null);
472
+ forNode.arr = arr = aoye.toRaw(arr);
420
473
  const children = forNode.children;
421
474
  if (isFirstRender) {
422
475
  const len = arr.length;
423
476
  for (let i = len; i--;) {
424
- const nextItem = children[i + 1];
425
477
  const item = this.createForItem(forNode, i, data);
426
- const anchor = this.insertAfterAnchor('for-item-after');
427
- item.realAfter = anchor;
428
- if (nextItem) {
429
- nextItem.realBefore = anchor;
430
- }
478
+ item.realAfter = this.insertAfterAnchor('for-item-after');
479
+ item.realBefore = this.insertAfterAnchor('for-item-before');
431
480
  item.realParent = forNode.realParent;
432
481
  children[i] = item;
433
482
  }
434
483
  const firstInsert = children[0];
435
484
  if (firstInsert) {
436
- firstInsert.realBefore = forNode.realBefore;
437
485
  this.tokenizer.nextToken();
438
486
  this.tokenizer.nextToken();
439
487
  } else {
@@ -447,65 +495,214 @@ class Interpreter {
447
495
  if (!forNode.getKey) {
448
496
  if (newLen < oldLen) {
449
497
  for (let i = oldLen - 1; i >= newLen; i--) {
450
- const child = children[i];
451
- this.removeLogicNode(child);
452
- this.remove(child.realAfter);
453
- child.effect.dispose();
498
+ this.removeForItem(children, i);
454
499
  }
455
500
  }
456
501
  if (oldLen < newLen) {
457
502
  const lastAfter = children.at(-1)?.realAfter || forNode.realBefore;
458
503
  for (let i = newLen - 1; i >= oldLen; i--) {
459
- const item = this.createForItem(forNode, i, data);
460
- newChildren[i] = item;
461
- const nextItem = newChildren[i + 1];
462
- const anchor = this.createAnchor('for-item-after');
463
- this.insertAfter(forNode.realParent, anchor, lastAfter);
464
- item.realAfter = anchor;
465
- if (nextItem) {
466
- nextItem.realBefore = anchor;
467
- }
468
- item.realParent = forNode.realParent;
469
- this.tokenizer = owner.tokenizer;
470
- this.tokenizer.resume(snapshotForUpdate);
471
- this.tokenizer.useDedentAsEof = false;
472
- aoye.runWithPulling(() => {
473
- this.program(forNode.realParent, forNode.owner, lastAfter, item);
474
- }, item.effect);
475
- }
476
- const firstInsert = newChildren[oldLen];
477
- if (firstInsert) {
478
- firstInsert.realBefore = lastAfter;
504
+ this.insertForItem(forNode, i, data, newChildren, lastAfter, snapshotForUpdate);
479
505
  }
480
506
  }
481
507
  for (let i = minLen; i--;) {
482
508
  const child = children[i];
483
509
  newChildren[i] = child;
484
- if (typeof itemExp === 'string') {
485
- child.data[itemExp] = arr[i];
510
+ this.reuseForItem(child, arr[i], itemExp, i, indexName);
511
+ }
512
+ } else {
513
+ let s = 0,
514
+ e1 = oldLen - 1,
515
+ e2 = newLen - 1;
516
+ while (s <= e1 && s <= e2) {
517
+ const child = children[s];
518
+ const old = child.key;
519
+ const itemData = this.getItemData(forNode, s, data);
520
+ const key = forNode.getKey(itemData);
521
+ if (old === key) {
522
+ newChildren[s] = child;
523
+ this.reuseForItem(child, arr[s], itemExp, s, indexName);
524
+ s++;
525
+ } else {
526
+ break;
527
+ }
528
+ }
529
+ while (s <= e1 && s <= e2) {
530
+ const child = children[e1];
531
+ const old = child.key;
532
+ const itemData = this.getItemData(forNode, e2, data);
533
+ const key = forNode.getKey(itemData);
534
+ if (old === key) {
535
+ newChildren[e2] = child;
536
+ this.reuseForItem(child, arr[e2], itemExp, e2, indexName);
537
+ e1--;
538
+ e2--;
486
539
  } else {
487
- Object.assign(child.data, itemExp(arr[i]));
540
+ break;
541
+ }
542
+ }
543
+ if (s > e1) {
544
+ if (s <= e2) {
545
+ const firstBefore = s > 0 ? children.at(-1)?.realAfter || forNode.realBefore : forNode.realBefore;
546
+ for (let i = e2; i >= s; i--) {
547
+ this.insertForItem(forNode, i, data, newChildren, firstBefore, snapshotForUpdate);
548
+ }
549
+ }
550
+ } else if (s > e2) {
551
+ if (s <= e1) {
552
+ for (let i = e1; i >= s; i--) {
553
+ this.removeForItem(children, i);
554
+ }
555
+ }
556
+ } else {
557
+ let s1 = s,
558
+ s2 = s;
559
+ const mixLen = e2 - s2 + 1;
560
+ const key2new = new Map();
561
+ for (let i = s2; i <= e2; i++) {
562
+ const itemData = this.getItemData(forNode, i, data);
563
+ const key = forNode.getKey(itemData);
564
+ key2new.set(key, i);
565
+ }
566
+ let maxIncNewI = -1;
567
+ let hasMove = false;
568
+ const new2oldI = new Array(mixLen).fill(-1);
569
+ for (let i = s1; i <= e1; i++) {
570
+ const key = children[i].key;
571
+ const newI = key2new.get(key);
572
+ if (newI == null) {
573
+ this.removeForItem(children, i);
574
+ continue;
575
+ }
576
+ const child = children[i];
577
+ newChildren[newI] = child;
578
+ this.reuseForItem(child, arr[newI], itemExp, newI, indexName);
579
+ new2oldI[newI - s2] = i;
580
+ key2new.delete(key);
581
+ if (newI < maxIncNewI) {
582
+ hasMove = true;
583
+ } else {
584
+ maxIncNewI = newI;
585
+ }
586
+ }
587
+ if (!hasMove) {
588
+ key2new.forEach((i, key) => {
589
+ const before = i === 0 ? forNode.realBefore : newChildren[i - 1].realAfter;
590
+ this.insertForItem(forNode, i, data, newChildren, before, snapshotForUpdate);
591
+ });
592
+ } else {
593
+ const incI = macInc(new2oldI),
594
+ incLen = incI.length;
595
+ let p1, p2;
596
+ for (p1 = s2, p2 = 0; p1 <= e2; p1++) {
597
+ const oldI = new2oldI[p1];
598
+ if (oldI === -1) {
599
+ const before = p1 === 0 ? forNode.realBefore : newChildren[p1 - 1].realAfter;
600
+ this.insertForItem(forNode, p1, data, newChildren, before, snapshotForUpdate);
601
+ continue;
602
+ }
603
+ const staticIdx = incI[p2] + s2;
604
+ if (p1 === staticIdx) {
605
+ p2 <= incLen && p2++;
606
+ continue;
607
+ }
608
+ let before = p1 === 0 ? forNode.realBefore : newChildren[p1 - 1].realAfter;
609
+ const child = newChildren[p1];
610
+ const realBefore = child.realBefore,
611
+ realAfter = child.realAfter,
612
+ realParent = child.realParent;
613
+ let point = realBefore,
614
+ next;
615
+ do {
616
+ next = this.nextSib(point);
617
+ this.insertAfter(realParent, point, before);
618
+ before = point;
619
+ if (point === realAfter) break;
620
+ point = next;
621
+ } while (true);
622
+ }
488
623
  }
489
624
  }
490
- forNode.children = newChildren;
491
625
  }
626
+ forNode.children = newChildren;
492
627
  }
493
628
  isFirstRender = false;
629
+ aoye.setPulling(prevCtx);
630
+ return isDestroy => {
631
+ if (isDestroy) {
632
+ for (let i = 0; i < forNode.children.length; i++) {
633
+ const item = forNode.children[i];
634
+ item.effect.dispose();
635
+ }
636
+ }
637
+ };
494
638
  });
495
639
  return forNode.children[0] || forNode;
496
640
  }
641
+ insertForItem(forNode, i, parentData, newChildren, before, snapshotForUpdate) {
642
+ const item = this.createForItem(forNode, i, parentData);
643
+ newChildren[i] = item;
644
+ let realAfter = this.createAnchor('for-item-after');
645
+ this.handleInsert(forNode.realParent, realAfter, before);
646
+ let realBefore = this.createAnchor('for-item-before');
647
+ this.handleInsert(forNode.realParent, realBefore, before);
648
+ item.realBefore = realBefore;
649
+ item.realAfter = realAfter;
650
+ this.tokenizer = forNode.owner.tokenizer;
651
+ this.tokenizer.resume(snapshotForUpdate);
652
+ this.tokenizer.useDedentAsEof = false;
653
+ aoye.runWithPulling(() => {
654
+ this.program(forNode.realParent, forNode.owner, realBefore, item);
655
+ }, item.effect);
656
+ }
657
+ removeForItem(children, i) {
658
+ const child = children[i];
659
+ this.removeLogicNode(child);
660
+ this.remove(child.realBefore);
661
+ this.remove(child.realAfter);
662
+ child.effect.dispose();
663
+ }
664
+ reuseForItem(child, data, itemExp, i, indexName) {
665
+ if (typeof itemExp === 'string') {
666
+ child.data[itemExp] = data;
667
+ if (indexName) {
668
+ child.data[indexName] = i;
669
+ }
670
+ } else {
671
+ indexName = indexName || KEY_INDEX;
672
+ child.data[indexName] = i;
673
+ }
674
+ }
497
675
  forItemId = 0;
498
676
  createForItem(forNode, i, parentData) {
499
677
  let forItemNode;
678
+ let data;
500
679
  const scope = new aoye.Scope(() => {});
501
680
  scope.scope = null;
502
681
  aoye.runWithPulling(() => {
503
682
  scope.get();
504
683
  }, null);
684
+ data = this.getItemData(forNode, i, parentData);
685
+ forItemNode = {
686
+ id: this.forItemId++,
687
+ __logicType: FakeType.ForItem,
688
+ realParent: null,
689
+ realBefore: null,
690
+ realAfter: null,
691
+ forNode,
692
+ key: forNode.getKey?.(data),
693
+ effect: null,
694
+ data
695
+ };
696
+ forItemNode.effect = scope;
697
+ return forItemNode;
698
+ }
699
+ getItemData(forNode, i, parentData) {
505
700
  const arr = forNode.arr,
506
701
  itemExp = forNode.itemExp,
507
- indexName = forNode.indexName,
702
+ vars = forNode.vars,
703
+ arrSignal = forNode.arrSignal,
508
704
  getKey = forNode.getKey;
705
+ let indexName = forNode.indexName;
509
706
  let data;
510
707
  if (typeof itemExp === 'string') {
511
708
  data = aoye.deepSignal(indexName ? {
@@ -515,26 +712,21 @@ class Interpreter {
515
712
  [itemExp]: arr[i]
516
713
  }, aoye.getPulling());
517
714
  } else {
518
- const rawData = itemExp(arr[i]);
519
- if (indexName) {
520
- rawData[indexName] = i;
521
- }
715
+ indexName = indexName ?? KEY_INDEX;
716
+ const rawData = {
717
+ [indexName]: i
718
+ };
522
719
  data = aoye.deepSignal(rawData, aoye.getPulling());
720
+ const computedData = new aoye.Computed(() => itemExp(arrSignal.get()[getKey ? data[indexName] : i]));
721
+ const cells = data[aoye.Keys.Meta].cells;
722
+ for (let i = 0; i < vars.length; i++) {
723
+ const name = vars[i];
724
+ rawData[name] = undefined;
725
+ cells.set(name, new aoye.Computed(() => computedData.get()[name]));
726
+ }
523
727
  }
524
728
  Object.setPrototypeOf(data, parentData);
525
- forItemNode = {
526
- id: this.forItemId++,
527
- __logicType: FakeType.ForItem,
528
- realParent: null,
529
- realBefore: null,
530
- realAfter: null,
531
- forNode,
532
- key: getKey?.(data),
533
- effect: null,
534
- data
535
- };
536
- forItemNode.effect = scope;
537
- return forItemNode;
729
+ return data;
538
730
  }
539
731
  getData() {
540
732
  const _this$ctx$stack$peekB = this.ctx.stack.peekByType(NodeSort.CtxProvider),
@@ -1033,36 +1225,27 @@ class Tokenizer {
1033
1225
  this.setToken(TokenType.Identifier, value || true);
1034
1226
  return this.token;
1035
1227
  }
1036
- forLoopSubExp() {
1228
+ jsExp() {
1037
1229
  this.token = null;
1038
1230
  let value = '';
1039
- let count = 0;
1040
1231
  while (1) {
1041
1232
  const char = this.code[this.i];
1042
- const isSemicolon = char === ';';
1043
- if (isSemicolon || char === '\n') {
1044
- value = value.trim();
1045
- if (!this.token) {
1046
- this.setToken(TokenType.Identifier, value);
1047
- } else {
1048
- this.waitingTokens.push({
1049
- type: TokenType.Identifier,
1050
- typeName: TokenType[TokenType.Identifier],
1051
- value
1052
- });
1053
- }
1054
- value = '';
1055
- count++;
1056
- if (count > 3) {
1057
- throw SyntaxError(`for 循环最多可包含三个表达式, 分别为 arr ; item index [; key]`);
1058
- }
1059
- if (!isSemicolon) return count === 3;
1233
+ if (char === ';' || char === '\n') {
1234
+ this.setToken(TokenType.Identifier, value.trim());
1235
+ return this.token;
1060
1236
  } else {
1061
1237
  value += char;
1062
1238
  }
1063
1239
  this.i++;
1064
1240
  }
1065
1241
  }
1242
+ peekChar() {
1243
+ let i = this.i;
1244
+ while (this.code[i] === ' ' || this.code[i] === '\t') {
1245
+ i++;
1246
+ }
1247
+ return this.code[i];
1248
+ }
1066
1249
  assignment() {
1067
1250
  this.setToken(TokenType.Assign, '=');
1068
1251
  }