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