@motionscript/code 0.1.0-alpha.3 → 0.1.0-alpha.5
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/CHANGELOG.md +45 -0
- package/LICENSE +201 -201
- package/NOTICE +48 -0
- package/README.md +161 -108
- package/dist/browser/THIRD_PARTY_LICENSES.txt +431 -0
- package/dist/browser/index.js +7 -6
- package/dist/browser/index.js.map +3 -3
- package/dist/browser/manifest.json +84 -2
- package/dist/code-range.d.ts +6 -0
- package/dist/code-range.d.ts.map +1 -1
- package/dist/code-range.js +8 -0
- package/dist/code-range.js.map +1 -1
- package/dist/diff.d.ts +14 -0
- package/dist/diff.d.ts.map +1 -1
- package/dist/diff.js +134 -2
- package/dist/diff.js.map +1 -1
- package/dist/engine.d.ts +1 -1
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +1 -1
- package/dist/engine.js.map +1 -1
- package/dist/highlight.d.ts +10 -2
- package/dist/highlight.d.ts.map +1 -1
- package/dist/highlight.js +40 -6
- package/dist/highlight.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/layout.js.map +1 -1
- package/dist/node.d.ts +42 -7
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +174 -47
- package/dist/node.js.map +1 -1
- package/dist/nodes.js.map +1 -1
- package/dist/render.d.ts +6 -1
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +59 -0
- package/dist/render.js.map +1 -1
- package/dist/style.d.ts +3 -3
- package/dist/style.d.ts.map +1 -1
- package/dist/style.js +10 -6
- package/dist/style.js.map +1 -1
- package/dist/tokens.d.ts.map +1 -1
- package/dist/tokens.js +5 -1
- package/dist/tokens.js.map +1 -1
- package/dist/transitions.d.ts +19 -1
- package/dist/transitions.d.ts.map +1 -1
- package/dist/transitions.js.map +1 -1
- package/package.json +7 -5
- package/registry.json +6 -6
- package/src/code-range.ts +165 -156
- package/src/diff.ts +351 -216
- package/src/engine.ts +21 -21
- package/src/highlight.ts +340 -291
- package/src/index.ts +3 -3
- package/src/layout.ts +157 -157
- package/src/node.ts +1038 -904
- package/src/nodes.ts +18 -18
- package/src/render.ts +312 -247
- package/src/style.ts +255 -251
- package/src/tokens.ts +79 -75
- package/src/transitions.ts +204 -184
package/dist/node.js
CHANGED
|
@@ -4,14 +4,21 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
4
4
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
};
|
|
7
|
-
import { node, Clip, Node2D, property, insetsProperty, lerpNumber, command, driveCommand, } from "@motionscript/sdk";
|
|
8
|
-
import { rangeToCharOffsets, charOffsetsToRange, TokenAdvanceCache, tokenizeCodeToIdLines, defaultCodeDiff, layoutCode, metricsSignature, makeAnim, defaultCodePhases, resolveTokenStates, moveProgress, canHighlight, ensureHighlighter, DefaultHighlightStyle, drawCode, } from "./engine.js";
|
|
7
|
+
import { node, Clip, Node2D, property, insetsProperty, lerpNumber, colorOps, command, driveCommand, } from "@motionscript/sdk";
|
|
8
|
+
import { rangeToCharOffsets, charOffsetsToRange, TokenAdvanceCache, tokenizeCodeToIdLines, defaultCodeDiff, layoutCode, metricsSignature, makeAnim, defaultCodePhases, resolveTokenStates, moveProgress, canHighlight, ensureHighlighter, normalizeSource, DefaultHighlightStyle, drawCode, } from "./engine.js";
|
|
9
|
+
/** A row band's paint and corner radius when `highlightRows` is given neither. */
|
|
10
|
+
const ROW_BAND_COLOR = "rgba(255, 255, 255, 0.12)";
|
|
11
|
+
const ROW_BAND_RADIUS = 6;
|
|
9
12
|
let Code = class Code extends Node2D {
|
|
10
13
|
/** The settled structure. During an edit, the structure the edit lands on. */
|
|
11
14
|
tokenLines = [];
|
|
12
|
-
tokenized = false;
|
|
13
15
|
/** The source `tokenLines` spells, so a frame compares it with `code` without joining every token. */
|
|
14
16
|
_linesSource = "";
|
|
17
|
+
/** What `tokenLines` were coloured and split with, so a frame notices a `language` or `theme` that has moved since. */
|
|
18
|
+
_linesBasis = { language: "", theme: "", highlighted: false };
|
|
19
|
+
/** The last `code` seen and its normalised form, so a frame that finds it unchanged does not scan it. */
|
|
20
|
+
_rawCode = null;
|
|
21
|
+
_normalCode = "";
|
|
15
22
|
transitions = [];
|
|
16
23
|
// Caches expensive scope.measureText() calls; cleared when the font
|
|
17
24
|
// signature (fontSize|fontFamily) changes. See TokenAdvanceCache.
|
|
@@ -27,6 +34,8 @@ let Code = class Code extends Node2D {
|
|
|
27
34
|
// tokens whose id is NOT in the highlight set. null means "not highlighting".
|
|
28
35
|
highlightDimOpacity = null;
|
|
29
36
|
highlightedIds = new Set();
|
|
37
|
+
// The bands highlightRows() left behind whole rows; empty means none.
|
|
38
|
+
rowBands = [];
|
|
30
39
|
/**
|
|
31
40
|
* Which token became which across an edit, and when each part of that
|
|
32
41
|
* edit happens. Construction-time-only (see {@link CodeStrategies}) —
|
|
@@ -36,10 +45,10 @@ let Code = class Code extends Node2D {
|
|
|
36
45
|
phaseStrategy;
|
|
37
46
|
constructor(props) {
|
|
38
47
|
super(props);
|
|
39
|
-
this.diffStrategy = props
|
|
40
|
-
this.phaseStrategy = props
|
|
41
|
-
this.applyProp("width", props
|
|
42
|
-
this.applyProp("height", props
|
|
48
|
+
this.diffStrategy = props?.diffStrategy ?? defaultCodeDiff;
|
|
49
|
+
this.phaseStrategy = props?.phaseStrategy ?? defaultCodePhases;
|
|
50
|
+
this.applyProp("width", props?.width ?? "hug");
|
|
51
|
+
this.applyProp("height", props?.height ?? "hug");
|
|
43
52
|
this.applyProp("_driven", false);
|
|
44
53
|
// Best-effort, for a highlighter constructed outside the asset pipeline
|
|
45
54
|
// entirely (a bare `new Code(...)` in a test or a headless measure). On
|
|
@@ -48,19 +57,7 @@ let Code = class Code extends Node2D {
|
|
|
48
57
|
// there the tokens are right the first time rather than upgrading later.
|
|
49
58
|
// (Theme is a synchronous color style; only the language parser loads.)
|
|
50
59
|
ensureHighlighter(undefined, [this.language]).catch(() => { });
|
|
51
|
-
this.
|
|
52
|
-
}
|
|
53
|
-
set(props) {
|
|
54
|
-
super.set(props);
|
|
55
|
-
if (props.code !== undefined || props.language !== undefined || props.theme !== undefined) {
|
|
56
|
-
this.tokenized = false;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
tokenize() {
|
|
60
|
-
this.setLines(this._structureFor(this.code), this.code);
|
|
61
|
-
// Only consider ourselves tokenized once we actually highlighted; while
|
|
62
|
-
// the language is still loading we keep retrying on each render.
|
|
63
|
-
this.tokenized = canHighlight(this.language, this.theme);
|
|
60
|
+
this._sync();
|
|
64
61
|
}
|
|
65
62
|
/**
|
|
66
63
|
* Swap in a new settled structure, invalidating the layout cache.
|
|
@@ -69,11 +66,12 @@ let Code = class Code extends Node2D {
|
|
|
69
66
|
* counter rather than a hash of the content — a write that skipped the bump
|
|
70
67
|
* would keep serving the previous frame's geometry for the new listing.
|
|
71
68
|
*/
|
|
72
|
-
setLines(lines, source) {
|
|
69
|
+
setLines(lines, source, basis = this._basis()) {
|
|
73
70
|
if (this.tokenLines === lines)
|
|
74
71
|
return;
|
|
75
72
|
this.tokenLines = lines;
|
|
76
73
|
this._linesSource = source;
|
|
74
|
+
this._linesBasis = basis;
|
|
77
75
|
this.structureVersion++;
|
|
78
76
|
this.layoutCache = null;
|
|
79
77
|
this.layoutCacheKey = null;
|
|
@@ -116,6 +114,15 @@ let Code = class Code extends Node2D {
|
|
|
116
114
|
}
|
|
117
115
|
return null;
|
|
118
116
|
}
|
|
117
|
+
/** The row-band change in flight, if any; the latest wins. */
|
|
118
|
+
activeRows() {
|
|
119
|
+
for (let i = this.transitions.length - 1; i >= 0; i--) {
|
|
120
|
+
const tr = this.transitions[i];
|
|
121
|
+
if (tr.kind === "rows")
|
|
122
|
+
return tr;
|
|
123
|
+
}
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
119
126
|
settledLayout(m, scope) {
|
|
120
127
|
const key = `${this.structureVersion}|${metricsSignature(m)}|${this.advanceCache.signature(m.fontSize, m.fontFamily)}`;
|
|
121
128
|
if (this.layoutCache && this.layoutCacheKey === key)
|
|
@@ -188,14 +195,9 @@ let Code = class Code extends Node2D {
|
|
|
188
195
|
// column-by-column rather than merely look different.
|
|
189
196
|
ctx.pushTextStyle(null);
|
|
190
197
|
try {
|
|
198
|
+
// Also what upgrades a frame drawn as plain text to full highlighting
|
|
199
|
+
// the moment the asset loader resolves the grammar.
|
|
191
200
|
this._sync();
|
|
192
|
-
// Keep retrying until the language+theme have actually loaded, so a frame
|
|
193
|
-
// that rendered as plain text upgrades to full highlighting the moment the
|
|
194
|
-
// asset loader resolves. Never mid-edit: re-tokenizing mints fresh ids,
|
|
195
|
-
// and an in-flight transition is keyed by the ones it captured.
|
|
196
|
-
if (!this.tokenized && !this.activeEdit() && canHighlight(this.language, this.theme)) {
|
|
197
|
-
this.tokenize();
|
|
198
|
-
}
|
|
199
201
|
super.renderContent(ctx);
|
|
200
202
|
}
|
|
201
203
|
finally {
|
|
@@ -425,6 +427,25 @@ let Code = class Code extends Node2D {
|
|
|
425
427
|
this.highlightedIds = done ? new Set() : previousIds;
|
|
426
428
|
});
|
|
427
429
|
}
|
|
430
|
+
/**
|
|
431
|
+
* Lay a band of `color` behind whole rows: every line a range touches, across
|
|
432
|
+
* the block. Persistent, and independent of {@link highlight}'s dimming. A
|
|
433
|
+
* second call slides and resizes the bands onto their new rows, pairing them
|
|
434
|
+
* in the order given; `resetRowHighlight()` fades them out. A band keeps to
|
|
435
|
+
* its lines when an edit moves them.
|
|
436
|
+
*/
|
|
437
|
+
highlightRows(args) {
|
|
438
|
+
this._sync();
|
|
439
|
+
const { target, color, radius } = args.data;
|
|
440
|
+
const paint = colorOps.resolve(color ?? ROW_BAND_COLOR);
|
|
441
|
+
const bands = this.rowBandsIn(normalizeCodeRanges(target), paint, Math.max(0, radius ?? ROW_BAND_RADIUS));
|
|
442
|
+
return this.runRows(this.rowBands, bands, args.duration, args.easing);
|
|
443
|
+
}
|
|
444
|
+
/** Fade every row band out and clear them. */
|
|
445
|
+
resetRowHighlight(args) {
|
|
446
|
+
this._sync();
|
|
447
|
+
return this.runRows(this.rowBands, [], args.duration, args.easing);
|
|
448
|
+
}
|
|
428
449
|
/**
|
|
429
450
|
* Animate the listing to a new source.
|
|
430
451
|
*
|
|
@@ -440,10 +461,15 @@ let Code = class Code extends Node2D {
|
|
|
440
461
|
* of two unrelated listings rather than as an edit being made.
|
|
441
462
|
*/
|
|
442
463
|
editTo(next, duration, easing) {
|
|
464
|
+
if (!this.mounted)
|
|
465
|
+
return driveCommand(duration, () => { });
|
|
466
|
+
next = normalizeSource(next);
|
|
443
467
|
const from = this.tokenLines;
|
|
444
468
|
const fromCode = this.joinedSource();
|
|
445
469
|
if (next === fromCode)
|
|
446
470
|
return driveCommand(duration, () => { });
|
|
471
|
+
const fromBasis = this._linesBasis;
|
|
472
|
+
const basis = this._basis();
|
|
447
473
|
const edit = this.diffStrategy(from, tokenizeCodeToIdLines(next, this.language, this.theme));
|
|
448
474
|
const transition = {
|
|
449
475
|
kind: "structural",
|
|
@@ -459,12 +485,10 @@ let Code = class Code extends Node2D {
|
|
|
459
485
|
fromLayout: null,
|
|
460
486
|
toLayout: null,
|
|
461
487
|
};
|
|
462
|
-
// A tokenize triggered by the grammar landing mid-edit would mint fresh
|
|
488
|
+
// A re-tokenize triggered by the grammar landing mid-edit would mint fresh
|
|
463
489
|
// ids underneath this transition, and the transition is keyed by the ones
|
|
464
|
-
// it captured — so
|
|
465
|
-
//
|
|
466
|
-
// the edit settles.
|
|
467
|
-
this.tokenized = canHighlight(this.language, this.theme);
|
|
490
|
+
// it captured — so `_sync` leaves the structure alone while an edit is in
|
|
491
|
+
// flight, and compares the basis recorded here once it settles.
|
|
468
492
|
return driveCommand(duration, (t) => {
|
|
469
493
|
this._drive();
|
|
470
494
|
const running = t > 0 && t < 1;
|
|
@@ -478,11 +502,11 @@ let Code = class Code extends Node2D {
|
|
|
478
502
|
// starts, the listing is what preceded it; at or past the end, the
|
|
479
503
|
// result. A `finally` could only ever have run forwards.
|
|
480
504
|
if (t <= 0) {
|
|
481
|
-
this.setLines(from, fromCode);
|
|
505
|
+
this.setLines(from, fromCode, fromBasis);
|
|
482
506
|
this._writeProp("code", fromCode);
|
|
483
507
|
}
|
|
484
508
|
else {
|
|
485
|
-
this.setLines(edit.lines, next);
|
|
509
|
+
this.setLines(edit.lines, next, basis);
|
|
486
510
|
this._writeProp("code", next);
|
|
487
511
|
}
|
|
488
512
|
});
|
|
@@ -497,6 +521,8 @@ let Code = class Code extends Node2D {
|
|
|
497
521
|
* way*, and `progress` is assigned from `t` rather than accumulated.
|
|
498
522
|
*/
|
|
499
523
|
runDim(tokens, duration, easing, settle) {
|
|
524
|
+
if (!this.mounted)
|
|
525
|
+
return driveCommand(duration, () => { });
|
|
500
526
|
const transition = { kind: "dim", tokens, progress: 0 };
|
|
501
527
|
return driveCommand(duration, (t) => {
|
|
502
528
|
this._drive();
|
|
@@ -510,6 +536,23 @@ let Code = class Code extends Node2D {
|
|
|
510
536
|
settle?.(t >= 1);
|
|
511
537
|
});
|
|
512
538
|
}
|
|
539
|
+
/** The same membership rule as {@link runDim}: on the stack strictly inside the run, settled at either end. */
|
|
540
|
+
runRows(from, to, duration, easing) {
|
|
541
|
+
if (!this.mounted || (from.length === 0 && to.length === 0))
|
|
542
|
+
return driveCommand(duration, () => { });
|
|
543
|
+
const transition = { kind: "rows", from, to, progress: 0 };
|
|
544
|
+
return driveCommand(duration, (t) => {
|
|
545
|
+
this._drive();
|
|
546
|
+
const running = t > 0 && t < 1;
|
|
547
|
+
const index = this.transitions.indexOf(transition);
|
|
548
|
+
if (running && index < 0)
|
|
549
|
+
this.transitions.push(transition);
|
|
550
|
+
else if (!running && index >= 0)
|
|
551
|
+
this.transitions.splice(index, 1);
|
|
552
|
+
transition.progress = easing ? easing(t) : t;
|
|
553
|
+
this.rowBands = t >= 1 ? to : from;
|
|
554
|
+
});
|
|
555
|
+
}
|
|
513
556
|
/**
|
|
514
557
|
* Route a tweened `code` through the diff engine.
|
|
515
558
|
*
|
|
@@ -520,9 +563,11 @@ let Code = class Code extends Node2D {
|
|
|
520
563
|
* the rest of the step's props tween alongside it untouched.
|
|
521
564
|
*/
|
|
522
565
|
_prepareStep(to, duration, easing) {
|
|
566
|
+
if (!this.mounted)
|
|
567
|
+
return super._prepareStep(to, duration, easing);
|
|
523
568
|
this._sync();
|
|
524
|
-
const next = to.code;
|
|
525
|
-
if (
|
|
569
|
+
const next = typeof to.code === "string" ? normalizeSource(to.code) : undefined;
|
|
570
|
+
if (next === undefined || next === this.joinedSource()) {
|
|
526
571
|
return super._prepareStep(to, duration, easing);
|
|
527
572
|
}
|
|
528
573
|
const rest = { ...to };
|
|
@@ -625,6 +670,19 @@ let Code = class Code extends Node2D {
|
|
|
625
670
|
return result;
|
|
626
671
|
}
|
|
627
672
|
// ── Drawing ─────────────────────────────────────────────────────────────
|
|
673
|
+
/** One band per range, over every line it touches; a range past the end of the listing gives none. */
|
|
674
|
+
rowBandsIn(codeRanges, color, radius) {
|
|
675
|
+
const count = this.tokenLines.length;
|
|
676
|
+
const bands = [];
|
|
677
|
+
for (const range of codeRanges) {
|
|
678
|
+
const first = Math.max(1, Math.round(range.startLine ?? 1));
|
|
679
|
+
const last = Math.min(count, range.endLine == null || range.endLine === Infinity ? count : Math.round(range.endLine));
|
|
680
|
+
if (first > last)
|
|
681
|
+
continue;
|
|
682
|
+
bands.push({ startId: this.tokenLines[first - 1].id, endId: this.tokenLines[last - 1].id, color, radius });
|
|
683
|
+
}
|
|
684
|
+
return bands;
|
|
685
|
+
}
|
|
628
686
|
/**
|
|
629
687
|
* Thin delegator: everything about *how* a frame is painted (settled or
|
|
630
688
|
* mid structural-edit, entry/exit motion, colour cross-fade) lives in
|
|
@@ -641,6 +699,9 @@ let Code = class Code extends Node2D {
|
|
|
641
699
|
...this.frameLayout(draw),
|
|
642
700
|
tokenStates: resolveTokenStates(this.tokenLines, this.transitions, this.highlightDimOpacity, this.highlightedIds),
|
|
643
701
|
highlightActive: this.highlightDimOpacity !== null || this.transitions.some(tr => tr.kind === "dim"),
|
|
702
|
+
rowBands: this.rowBands,
|
|
703
|
+
rowsEdit: this.activeRows(),
|
|
704
|
+
boxW: this.layoutBounds.width,
|
|
644
705
|
});
|
|
645
706
|
}
|
|
646
707
|
/** Each source the props have named, tokenized once, so a listing derived again keeps the ids a highlight selected. */
|
|
@@ -650,28 +711,59 @@ let Code = class Code extends Node2D {
|
|
|
650
711
|
this._sync();
|
|
651
712
|
this._writeProp("_driven", true);
|
|
652
713
|
}
|
|
714
|
+
/**
|
|
715
|
+
* Bring the settled structure up to the props it is derived from.
|
|
716
|
+
*
|
|
717
|
+
* Pulled from every reader rather than pushed from a prop write, because
|
|
718
|
+
* `code`, `language` and `theme` can each be tweened or bound, and neither
|
|
719
|
+
* has a write to hang a subscription on. Never mid-edit: re-tokenizing mints
|
|
720
|
+
* fresh ids, and an in-flight transition is keyed by the ones it captured.
|
|
721
|
+
*/
|
|
653
722
|
_sync() {
|
|
723
|
+
const code = this._normalised();
|
|
654
724
|
if (!this._driven) {
|
|
655
725
|
this.transitions.length = 0;
|
|
656
726
|
this.highlightDimOpacity = null;
|
|
657
727
|
if (this.highlightedIds.size > 0)
|
|
658
728
|
this.highlightedIds = new Set();
|
|
659
|
-
|
|
729
|
+
if (this.rowBands.length > 0)
|
|
730
|
+
this.rowBands = [];
|
|
731
|
+
this.setLines(this._structureFor(code), code);
|
|
732
|
+
}
|
|
733
|
+
else if (!this.activeEdit()) {
|
|
734
|
+
const basis = this._basis();
|
|
735
|
+
const was = this._linesBasis;
|
|
736
|
+
if (this._linesSource === code && was.language === basis.language && was.theme === basis.theme && was.highlighted === basis.highlighted)
|
|
737
|
+
return;
|
|
738
|
+
const lines = this._structureFor(code);
|
|
739
|
+
// The same text split the same way, only coloured differently: the
|
|
740
|
+
// tokens are the ones a highlight or a transition already names.
|
|
741
|
+
if (this._linesSource === code && was.language === basis.language && was.highlighted === basis.highlighted) {
|
|
742
|
+
adoptIds(this.tokenLines, lines);
|
|
743
|
+
}
|
|
744
|
+
this.setLines(lines, code, basis);
|
|
660
745
|
}
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
746
|
+
}
|
|
747
|
+
_normalised() {
|
|
748
|
+
const code = this.code;
|
|
749
|
+
if (code !== this._rawCode) {
|
|
750
|
+
this._rawCode = code;
|
|
751
|
+
this._normalCode = normalizeSource(code);
|
|
664
752
|
}
|
|
753
|
+
return this._normalCode;
|
|
754
|
+
}
|
|
755
|
+
_basis() {
|
|
756
|
+
return { language: this.language, theme: this.theme, highlighted: canHighlight(this.language, this.theme) };
|
|
665
757
|
}
|
|
666
758
|
_structureFor(code) {
|
|
667
|
-
const highlighted =
|
|
759
|
+
const { language, theme, highlighted } = this._basis();
|
|
668
760
|
let entry = this._derived.get(code);
|
|
669
|
-
if (!entry || entry.language !==
|
|
761
|
+
if (!entry || entry.language !== language || entry.theme !== theme || entry.highlighted !== highlighted) {
|
|
670
762
|
entry = {
|
|
671
|
-
language
|
|
672
|
-
theme
|
|
763
|
+
language,
|
|
764
|
+
theme,
|
|
673
765
|
highlighted,
|
|
674
|
-
lines: tokenizeCodeToIdLines(code,
|
|
766
|
+
lines: tokenizeCodeToIdLines(code, language, theme),
|
|
675
767
|
};
|
|
676
768
|
this._derived.delete(code);
|
|
677
769
|
this._derived.set(code, entry);
|
|
@@ -745,6 +837,18 @@ __decorate([
|
|
|
745
837
|
__decorate([
|
|
746
838
|
command()
|
|
747
839
|
], Code.prototype, "resetHighlight", null);
|
|
840
|
+
__decorate([
|
|
841
|
+
command({
|
|
842
|
+
args: [
|
|
843
|
+
{ key: "target", kind: "selector" },
|
|
844
|
+
{ key: "color", kind: "color", default: ROW_BAND_COLOR },
|
|
845
|
+
{ key: "radius", kind: "number", default: ROW_BAND_RADIUS, min: 0, step: 1 },
|
|
846
|
+
],
|
|
847
|
+
})
|
|
848
|
+
], Code.prototype, "highlightRows", null);
|
|
849
|
+
__decorate([
|
|
850
|
+
command()
|
|
851
|
+
], Code.prototype, "resetRowHighlight", null);
|
|
748
852
|
Code = __decorate([
|
|
749
853
|
node({
|
|
750
854
|
key: "code",
|
|
@@ -758,6 +862,25 @@ Code = __decorate([
|
|
|
758
862
|
})
|
|
759
863
|
], Code);
|
|
760
864
|
export { Code };
|
|
865
|
+
/** Carry `from`'s line and token ids onto `to` when the two spell the same tokens. */
|
|
866
|
+
function adoptIds(from, to) {
|
|
867
|
+
if (from.length !== to.length)
|
|
868
|
+
return;
|
|
869
|
+
for (let i = 0; i < from.length; i++) {
|
|
870
|
+
const a = from[i].tokens;
|
|
871
|
+
const b = to[i].tokens;
|
|
872
|
+
if (a.length !== b.length)
|
|
873
|
+
return;
|
|
874
|
+
for (let j = 0; j < a.length; j++)
|
|
875
|
+
if (a[j].content !== b[j].content)
|
|
876
|
+
return;
|
|
877
|
+
}
|
|
878
|
+
for (let i = 0; i < from.length; i++) {
|
|
879
|
+
to[i].id = from[i].id;
|
|
880
|
+
for (let j = 0; j < from[i].tokens.length; j++)
|
|
881
|
+
to[i].tokens[j].id = from[i].tokens[j].id;
|
|
882
|
+
}
|
|
883
|
+
}
|
|
761
884
|
/** Sources a node keeps tokenized, oldest dropped first; a bound `code` can name any number of them. */
|
|
762
885
|
const DERIVED_LISTINGS = 16;
|
|
763
886
|
/**
|
|
@@ -765,13 +888,17 @@ const DERIVED_LISTINGS = 16;
|
|
|
765
888
|
* `CodeRange` (a plain object, not iterable) becomes a one-element array; a
|
|
766
889
|
* `CodeRangeChain`/plain array of ranges spreads as-is. `CodeRange` and
|
|
767
890
|
* `CodeRangeChain` are distinguished by iterability, not `instanceof` — a
|
|
768
|
-
* bare array of `CodeRange`s (no chain) works the same way.
|
|
891
|
+
* bare array of `CodeRange`s (no chain) works the same way. The `{ list }` shape
|
|
892
|
+
* a chain used to serialise as is read as the chain it came from.
|
|
769
893
|
*/
|
|
770
894
|
function normalizeCodeRanges(codeRange) {
|
|
771
895
|
const maybeIterable = codeRange;
|
|
772
896
|
if (typeof maybeIterable[Symbol.iterator] === "function") {
|
|
773
897
|
return [...codeRange];
|
|
774
898
|
}
|
|
899
|
+
const stored = codeRange.list;
|
|
900
|
+
if (Array.isArray(stored))
|
|
901
|
+
return [...stored];
|
|
775
902
|
return [codeRange];
|
|
776
903
|
}
|
|
777
904
|
const grammarLoaders = new Map();
|