bobe 0.0.25 → 0.0.27

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,45 @@ 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
+
160
199
  const _excluded = ["dentStack", "isFirstToken"];
161
200
  class Interpreter {
162
201
  constructor(tokenizer) {
@@ -209,6 +248,9 @@ class Interpreter {
209
248
  if (ctx.current.__logicType) {
210
249
  if (isLogicNode) {
211
250
  aoye.setPulling(ctx.current.effect);
251
+ if (ctx.current.__logicType & FakeType.ForItem) {
252
+ ctx.prevSibling = ctx.current.realBefore;
253
+ }
212
254
  }
213
255
  } else {
214
256
  if (ctx.current) {
@@ -259,8 +301,8 @@ class Interpreter {
259
301
  this.tokenizer.resume(snapshot);
260
302
  this.tokenizer.nextToken();
261
303
  this.tokenizer.nextToken();
262
- ctx.prevSibling = parent;
263
304
  ctx.current = forNode.children[++forNode.i];
305
+ ctx.prevSibling = ctx.current.realBefore;
264
306
  continue;
265
307
  }
266
308
  ctx.prevSibling = forNode.prevSibling;
@@ -364,21 +406,25 @@ class Interpreter {
364
406
  this.tokenizer.nextToken();
365
407
  const itemToken = this.tokenizer.nextToken();
366
408
  const isDestruct = itemToken.type === TokenType.InsertionExp;
367
- let itemExp = itemToken.value;
409
+ let itemExp = itemToken.value,
410
+ vars;
368
411
  if (isDestruct) {
369
412
  itemExp = '{' + itemExp + '}';
370
- const vars = itemExp.match(bobeShared.jsVarRegexp).join(',');
371
- itemExp = new Function('item', `let ${vars}; (${itemExp}=item); return {${vars}};`);
413
+ vars = itemExp.match(bobeShared.jsVarRegexp);
414
+ const varStr = vars.join(',');
415
+ itemExp = new Function(itemExp, `return {${varStr}};`);
372
416
  }
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
- }
417
+ let indexName,
418
+ keyExp,
419
+ char = this.tokenizer.peekChar();
420
+ if (char === ';') {
421
+ this.tokenizer.nextToken();
422
+ if (this.tokenizer.peekChar() !== '\n') keyExp = this.tokenizer.jsExp().value;
423
+ } else if (char === '\n') ; else {
424
+ indexName = this.tokenizer.nextToken().value;
425
+ if (this.tokenizer.peekChar() === ';') {
426
+ this.tokenizer.nextToken();
427
+ if (this.tokenizer.peekChar() !== '\n') keyExp = this.tokenizer.jsExp().value;
382
428
  }
383
429
  }
384
430
  const owner = this.ctx.stack.peekByType(NodeSort.TokenizerSwitcher)?.node;
@@ -391,12 +437,14 @@ class Interpreter {
391
437
  realBefore: prevSibling?.realAfter || prevSibling,
392
438
  realAfter: null,
393
439
  arr: null,
440
+ arrSignal: null,
394
441
  itemExp,
395
442
  indexName,
396
443
  getKey: null,
397
444
  children: [],
398
445
  effect: null,
399
446
  owner,
447
+ vars,
400
448
  i: 0
401
449
  };
402
450
  if (keyExp) {
@@ -407,6 +455,7 @@ class Interpreter {
407
455
  const cells = data[aoye.Keys.Meta].cells;
408
456
  const hasArrExpKey = Reflect.has(data[aoye.Keys.Raw], arrExp);
409
457
  const arrSignal = hasArrExpKey ? (data[arrExp], cells.get(arrExp)) : new aoye.Computed(this.getFn(data, arrExp));
458
+ forNode.arrSignal = arrSignal;
410
459
  forNode.realAfter = this.insertAfterAnchor('for-after');
411
460
  const _forNode$snapshot = forNode.snapshot;
412
461
  _forNode$snapshot.dentStack;
@@ -414,26 +463,23 @@ class Interpreter {
414
463
  const snapshotForUpdate = _objectWithoutProperties(_forNode$snapshot, _excluded);
415
464
  let isFirstRender = true;
416
465
  forNode.effect = new aoye.Effect(() => {
417
- let arr = forNode.arr = arrSignal.get();
466
+ let arr = arrSignal.get();
418
467
  arr[aoye.Keys.Iterator];
419
- arr = aoye.toRaw(arr);
468
+ const prevCtx = aoye.getPulling();
469
+ aoye.setPulling(null);
470
+ forNode.arr = arr = aoye.toRaw(arr);
420
471
  const children = forNode.children;
421
472
  if (isFirstRender) {
422
473
  const len = arr.length;
423
474
  for (let i = len; i--;) {
424
- const nextItem = children[i + 1];
425
475
  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
- }
476
+ item.realAfter = this.insertAfterAnchor('for-item-after');
477
+ item.realBefore = this.insertAfterAnchor('for-item-before');
431
478
  item.realParent = forNode.realParent;
432
479
  children[i] = item;
433
480
  }
434
481
  const firstInsert = children[0];
435
482
  if (firstInsert) {
436
- firstInsert.realBefore = forNode.realBefore;
437
483
  this.tokenizer.nextToken();
438
484
  this.tokenizer.nextToken();
439
485
  } else {
@@ -447,65 +493,210 @@ class Interpreter {
447
493
  if (!forNode.getKey) {
448
494
  if (newLen < oldLen) {
449
495
  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();
496
+ this.removeForItem(children, i);
454
497
  }
455
498
  }
456
499
  if (oldLen < newLen) {
457
500
  const lastAfter = children.at(-1)?.realAfter || forNode.realBefore;
458
501
  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;
502
+ this.insertForItem(forNode, i, data, newChildren, lastAfter, snapshotForUpdate);
479
503
  }
480
504
  }
481
505
  for (let i = minLen; i--;) {
482
506
  const child = children[i];
483
507
  newChildren[i] = child;
484
- if (typeof itemExp === 'string') {
485
- child.data[itemExp] = arr[i];
508
+ this.reuseForItem(child, arr[i], itemExp, i, indexName);
509
+ }
510
+ } else {
511
+ let s = 0,
512
+ e1 = oldLen - 1,
513
+ e2 = newLen - 1;
514
+ while (s <= e1 && s <= e2) {
515
+ const child = children[s];
516
+ const old = child.key;
517
+ const itemData = this.getItemData(forNode, s, data);
518
+ const key = forNode.getKey(itemData);
519
+ if (old === key) {
520
+ newChildren[s] = child;
521
+ this.reuseForItem(child, arr[s], itemExp, s, indexName);
522
+ s++;
486
523
  } else {
487
- Object.assign(child.data, itemExp(arr[i]));
524
+ break;
525
+ }
526
+ }
527
+ while (s <= e1 && s <= e2) {
528
+ const child = children[e1];
529
+ const old = child.key;
530
+ const itemData = this.getItemData(forNode, e2, data);
531
+ const key = forNode.getKey(itemData);
532
+ if (old === key) {
533
+ newChildren[e2] = child;
534
+ this.reuseForItem(child, arr[e2], itemExp, e2, indexName);
535
+ e1--;
536
+ e2--;
537
+ } else {
538
+ break;
539
+ }
540
+ }
541
+ if (s > e1) {
542
+ if (s <= e2) {
543
+ const lastAfter = children.at(-1)?.realAfter || forNode.realBefore;
544
+ for (let i = e2; i >= s; i--) {
545
+ this.insertForItem(forNode, i, data, newChildren, lastAfter, snapshotForUpdate);
546
+ }
547
+ }
548
+ } else if (s > e2) {
549
+ if (s <= e1) {
550
+ for (let i = e1; i >= s; i--) {
551
+ this.removeForItem(children, i);
552
+ }
553
+ }
554
+ } else {
555
+ let s1 = s,
556
+ s2 = s;
557
+ const mixLen = e2 - s2 + 1;
558
+ const key2new = new Map();
559
+ for (let i = s2; i <= e2; i++) {
560
+ const itemData = this.getItemData(forNode, i, data);
561
+ const key = forNode.getKey(itemData);
562
+ key2new.set(key, i);
563
+ }
564
+ let maxIncNewI = -1;
565
+ let hasMove = false;
566
+ const new2oldI = new Array(mixLen).fill(-1);
567
+ for (let i = s1; i <= e1; i++) {
568
+ const key = children[i].key;
569
+ const newI = key2new.get(key);
570
+ if (newI == null) {
571
+ this.removeForItem(children, i);
572
+ continue;
573
+ }
574
+ const child = children[i];
575
+ newChildren[newI] = child;
576
+ this.reuseForItem(child, arr[newI], itemExp, newI, indexName);
577
+ new2oldI[newI - s2] = i;
578
+ key2new.delete(key);
579
+ if (newI < maxIncNewI) {
580
+ hasMove = true;
581
+ } else {
582
+ maxIncNewI = newI;
583
+ }
584
+ }
585
+ if (!hasMove) {
586
+ key2new.forEach((i, key) => {
587
+ const before = i === 0 ? forNode.realBefore : newChildren[i - 1].realAfter;
588
+ this.insertForItem(forNode, i, data, newChildren, before, snapshotForUpdate);
589
+ });
590
+ } else {
591
+ const incI = macInc(new2oldI),
592
+ incLen = incI.length;
593
+ let p1, p2;
594
+ for (p1 = s2, p2 = 0; p1 <= e2; p1++) {
595
+ const oldI = new2oldI[p1];
596
+ if (oldI === -1) {
597
+ const before = p1 === 0 ? forNode.realBefore : newChildren[p1 - 1].realAfter;
598
+ this.insertForItem(forNode, p1, data, newChildren, before, snapshotForUpdate);
599
+ continue;
600
+ }
601
+ const staticIdx = incI[p2] + s2;
602
+ if (p1 === staticIdx) {
603
+ p2 <= incLen && p2++;
604
+ continue;
605
+ }
606
+ let before = p1 === 0 ? forNode.realBefore : newChildren[p1 - 1].realAfter;
607
+ const child = newChildren[p1];
608
+ const realBefore = child.realBefore,
609
+ realAfter = child.realAfter,
610
+ realParent = child.realParent;
611
+ let point = realBefore,
612
+ next;
613
+ do {
614
+ next = this.nextSib(point);
615
+ this.insertAfter(realParent, point, before);
616
+ before = point;
617
+ if (point === realAfter) break;
618
+ point = next;
619
+ } while (true);
620
+ }
488
621
  }
489
622
  }
490
- forNode.children = newChildren;
491
623
  }
624
+ forNode.children = newChildren;
492
625
  }
493
626
  isFirstRender = false;
627
+ aoye.setPulling(prevCtx);
628
+ return isDestroy => {
629
+ if (isDestroy) {
630
+ for (let i = 0; i < forNode.children.length; i++) {
631
+ const item = forNode.children[i];
632
+ item.effect.dispose();
633
+ }
634
+ }
635
+ };
494
636
  });
495
637
  return forNode.children[0] || forNode;
496
638
  }
639
+ insertForItem(forNode, i, parentData, newChildren, before, snapshotForUpdate) {
640
+ const item = this.createForItem(forNode, i, parentData);
641
+ newChildren[i] = item;
642
+ let realAfter = this.createAnchor('for-item-after');
643
+ this.handleInsert(forNode.realParent, realAfter, before);
644
+ let realBefore = this.createAnchor('for-item-before');
645
+ this.handleInsert(forNode.realParent, realBefore, before);
646
+ item.realBefore = realBefore;
647
+ item.realAfter = realAfter;
648
+ this.tokenizer = forNode.owner.tokenizer;
649
+ this.tokenizer.resume(snapshotForUpdate);
650
+ this.tokenizer.useDedentAsEof = false;
651
+ aoye.runWithPulling(() => {
652
+ this.program(forNode.realParent, forNode.owner, realBefore, item);
653
+ }, item.effect);
654
+ }
655
+ removeForItem(children, i) {
656
+ const child = children[i];
657
+ this.removeLogicNode(child);
658
+ this.remove(child.realBefore);
659
+ this.remove(child.realAfter);
660
+ child.effect.dispose();
661
+ }
662
+ reuseForItem(child, data, itemExp, i, indexName) {
663
+ if (typeof itemExp === 'string') {
664
+ child.data[itemExp] = data;
665
+ if (indexName) {
666
+ child.data[indexName] = i;
667
+ }
668
+ }
669
+ }
497
670
  forItemId = 0;
498
671
  createForItem(forNode, i, parentData) {
499
672
  let forItemNode;
673
+ let data;
500
674
  const scope = new aoye.Scope(() => {});
501
675
  scope.scope = null;
502
676
  aoye.runWithPulling(() => {
503
677
  scope.get();
504
678
  }, null);
679
+ data = this.getItemData(forNode, i, parentData);
680
+ forItemNode = {
681
+ id: this.forItemId++,
682
+ __logicType: FakeType.ForItem,
683
+ realParent: null,
684
+ realBefore: null,
685
+ realAfter: null,
686
+ forNode,
687
+ key: forNode.getKey?.(data),
688
+ effect: null,
689
+ data
690
+ };
691
+ forItemNode.effect = scope;
692
+ return forItemNode;
693
+ }
694
+ getItemData(forNode, i, parentData) {
505
695
  const arr = forNode.arr,
506
696
  itemExp = forNode.itemExp,
507
697
  indexName = forNode.indexName,
508
- getKey = forNode.getKey;
698
+ vars = forNode.vars,
699
+ arrSignal = forNode.arrSignal;
509
700
  let data;
510
701
  if (typeof itemExp === 'string') {
511
702
  data = aoye.deepSignal(indexName ? {
@@ -515,26 +706,18 @@ class Interpreter {
515
706
  [itemExp]: arr[i]
516
707
  }, aoye.getPulling());
517
708
  } else {
518
- const rawData = itemExp(arr[i]);
519
- if (indexName) {
520
- rawData[indexName] = i;
709
+ data = aoye.deepSignal(indexName ? {
710
+ [indexName]: i
711
+ } : {}, aoye.getPulling());
712
+ const computedData = new aoye.Computed(() => itemExp(arrSignal.get()[i]));
713
+ const cells = data[aoye.Keys.Meta].cells;
714
+ for (let i = 0; i < vars.length; i++) {
715
+ const name = vars[i];
716
+ cells.set(name, new aoye.Computed(() => computedData.get()[name]));
521
717
  }
522
- data = aoye.deepSignal(rawData, aoye.getPulling());
523
718
  }
524
719
  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;
720
+ return data;
538
721
  }
539
722
  getData() {
540
723
  const _this$ctx$stack$peekB = this.ctx.stack.peekByType(NodeSort.CtxProvider),
@@ -710,7 +893,7 @@ class Interpreter {
710
893
  }
711
894
  ifNode.isFirstRender = false;
712
895
  }, [signal]);
713
- ifNode.effect = ef.ins;
896
+ ifNode.effect = ef;
714
897
  return ifNode;
715
898
  }
716
899
  removeLogicNode(node) {
@@ -1033,36 +1216,27 @@ class Tokenizer {
1033
1216
  this.setToken(TokenType.Identifier, value || true);
1034
1217
  return this.token;
1035
1218
  }
1036
- forLoopSubExp() {
1219
+ jsExp() {
1037
1220
  this.token = null;
1038
1221
  let value = '';
1039
- let count = 0;
1040
1222
  while (1) {
1041
1223
  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;
1224
+ if (char === ';' || char === '\n') {
1225
+ this.setToken(TokenType.Identifier, value.trim());
1226
+ return this.token;
1060
1227
  } else {
1061
1228
  value += char;
1062
1229
  }
1063
1230
  this.i++;
1064
1231
  }
1065
1232
  }
1233
+ peekChar() {
1234
+ let i = this.i;
1235
+ while (this.code[i] === ' ' || this.code[i] === '\t') {
1236
+ i++;
1237
+ }
1238
+ return this.code[i];
1239
+ }
1066
1240
  assignment() {
1067
1241
  this.setToken(TokenType.Assign, '=');
1068
1242
  }