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