@motionscript/code 0.1.0-alpha.0 → 0.1.0-alpha.10
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 +96 -0
- package/NOTICE +49 -0
- package/README.md +148 -50
- package/dist/browser/THIRD_PARTY_LICENSES.txt +431 -0
- package/dist/browser/index.js +10 -7
- package/dist/browser/index.js.map +3 -3
- package/dist/browser/manifest.json +85 -3
- package/dist/code-range.d.ts +7 -1
- package/dist/code-range.d.ts.map +1 -1
- package/dist/code-range.js +9 -1
- 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 +2 -2
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +4 -4
- 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/layout.d.ts +1 -1
- package/dist/layout.d.ts.map +1 -1
- package/dist/layout.js.map +1 -1
- package/dist/measure-cache.d.ts +2 -2
- package/dist/measure-cache.d.ts.map +1 -1
- package/dist/measure-cache.js +0 -0
- package/dist/measure-cache.js.map +1 -1
- package/dist/node.d.ts +275 -281
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +634 -700
- package/dist/node.js.map +1 -1
- package/dist/nodes.d.ts +6 -9
- package/dist/nodes.d.ts.map +1 -1
- package/dist/nodes.js +7 -9
- package/dist/nodes.js.map +1 -1
- package/dist/render.d.ts +10 -3
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +60 -1
- 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 -4
- package/src/code-range.ts +10 -1
- package/src/diff.ts +137 -2
- package/src/engine.ts +5 -5
- package/src/highlight.ts +54 -5
- package/src/layout.ts +1 -1
- package/src/measure-cache.ts +0 -0
- package/src/node.ts +767 -747
- package/src/nodes.ts +8 -10
- package/src/render.ts +71 -3
- package/src/style.ts +11 -7
- package/src/tokens.ts +5 -1
- package/src/transitions.ts +21 -1
package/src/nodes.ts
CHANGED
|
@@ -4,15 +4,13 @@ import { Code } from "./node";
|
|
|
4
4
|
* Every node type this package publishes.
|
|
5
5
|
*
|
|
6
6
|
* A host registers these by handing the array to an engine —
|
|
7
|
-
* `new Engine(platform, { nodes: NODES })` — which reads each
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* for and two engines in one process can differ about it.
|
|
7
|
+
* `new Engine(platform, { nodes: NODES })` — which reads each component's key.
|
|
8
|
+
* Defining a component only *declares* that key: nothing is registered by
|
|
9
|
+
* importing a module, so a registry holds exactly what its owner asked for.
|
|
11
10
|
*
|
|
12
|
-
* Listing
|
|
13
|
-
* `sideEffects: false`,
|
|
14
|
-
*
|
|
15
|
-
* `import "./node"` for its side effect is exactly what gets shaken out, where an
|
|
16
|
-
* array of bindings is a data dependency that cannot be.
|
|
11
|
+
* Listing the component **values** is also what survives bundling: this package
|
|
12
|
+
* declares `sideEffects: false`, and a document names a node type by string, so
|
|
13
|
+
* an import kept only for its side effect is exactly what gets shaken out.
|
|
17
14
|
*/
|
|
18
|
-
|
|
15
|
+
// Typed by name, so the declaration names each component rather than spelling out its type through core.
|
|
16
|
+
export const NODES: (typeof Code)[] = [Code];
|
package/src/render.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { RenderContext2D, Graphics2D, NormalizedColor, parseColor, lerpNumber } from "@motionscript/
|
|
1
|
+
import { RenderContext2D, Graphics2D, NormalizedColor, parseColor, lerpNumber } from "@motionscript/sdk";
|
|
2
2
|
import type { CodeLayout, CodeMetrics } from "./layout";
|
|
3
3
|
import type { IdLine } from "./tokens";
|
|
4
4
|
import { windowProgress, moveProgress } from "./transitions";
|
|
5
|
-
import type { TokenState, StructuralTransition } from "./transitions";
|
|
5
|
+
import type { TokenState, StructuralTransition, RowBand, RowsTransition } from "./transitions";
|
|
6
6
|
import type { TokenAdvanceCache } from "./measure-cache";
|
|
7
7
|
|
|
8
8
|
/** Colour of a token the grammar had no opinion about. */
|
|
@@ -63,8 +63,16 @@ export interface DrawCodeState {
|
|
|
63
63
|
tokenStates: Map<number, TokenState>;
|
|
64
64
|
/** Whether a highlight (persistent dim, or a highlight()/resetHighlight() cross-fade) is currently engaged. */
|
|
65
65
|
highlightActive: boolean;
|
|
66
|
+
/** The settled row bands, and the change between two sets of them in flight, if any. */
|
|
67
|
+
rowBands?: RowBand[];
|
|
68
|
+
rowsEdit?: RowsTransition | null;
|
|
69
|
+
/** The node's laid-out width, which a band spans when it is wider than the block. */
|
|
70
|
+
boxW?: number;
|
|
66
71
|
}
|
|
67
72
|
|
|
73
|
+
/** What a frame is drawn onto: somewhere to draw, and the face's measurements. A render context is one. */
|
|
74
|
+
export type CodeCanvas = Pick<RenderContext2D, "draw" | "measureText">;
|
|
75
|
+
|
|
68
76
|
/**
|
|
69
77
|
* Draw one frame of a code listing: the settled structure, or — mid
|
|
70
78
|
* structural edit — the two structures it is interpolating between (what
|
|
@@ -72,7 +80,7 @@ export interface DrawCodeState {
|
|
|
72
80
|
* fades/slides in where it lands). Pure over `state`; touches nothing but
|
|
73
81
|
* `draw`.
|
|
74
82
|
*/
|
|
75
|
-
export function drawCode(draw:
|
|
83
|
+
export function drawCode(draw: CodeCanvas, state: DrawCodeState): void {
|
|
76
84
|
const { tokenLines, metrics, advanceCache, from, to, edit, tokenStates: dim } = state;
|
|
77
85
|
|
|
78
86
|
const progress = edit ? edit.progress : 1;
|
|
@@ -87,6 +95,8 @@ export function drawCode(draw: RenderContext2D, state: DrawCodeState): void {
|
|
|
87
95
|
// code text begins.
|
|
88
96
|
const numberRight = -blockW / 2 + metrics.padding.left + gutter - gutterGap;
|
|
89
97
|
|
|
98
|
+
drawRowBands(draw, state, Math.max(blockW, state.boxW ?? 0), pMove);
|
|
99
|
+
|
|
90
100
|
const drawNumber = (label: string, y: number, opacity: number): void => {
|
|
91
101
|
if (opacity <= 0) return;
|
|
92
102
|
const labelW = advanceCache.advance(draw, label, metrics.fontSize, metrics.fontFamily, 0);
|
|
@@ -201,6 +211,64 @@ export function drawCode(draw: RenderContext2D, state: DrawCodeState): void {
|
|
|
201
211
|
if (metrics.showLineNumbers) drawLineNumbers(state, drawNumber, pMove, pIn, pOut);
|
|
202
212
|
}
|
|
203
213
|
|
|
214
|
+
/** A band's top and bottom edge this frame, or `null` when neither of its rows is in the listing any more. */
|
|
215
|
+
function bandEdges(band: RowBand, state: DrawCodeState, pMove: number): { top: number; bottom: number } | null {
|
|
216
|
+
const { from, to, metrics } = state;
|
|
217
|
+
const half = (metrics.fontSize * metrics.lineHeight) / 2;
|
|
218
|
+
const centre = (id: number): number | null => {
|
|
219
|
+
const toIndex = to.lineIndex.get(id);
|
|
220
|
+
const fromIndex = from.lineIndex.get(id);
|
|
221
|
+
if (toIndex !== undefined && fromIndex !== undefined) return lerpNumber(from.lineY[fromIndex], to.lineY[toIndex], pMove);
|
|
222
|
+
if (toIndex !== undefined) return to.lineY[toIndex];
|
|
223
|
+
return fromIndex === undefined ? null : from.lineY[fromIndex];
|
|
224
|
+
};
|
|
225
|
+
const first = centre(band.startId);
|
|
226
|
+
const last = centre(band.endId);
|
|
227
|
+
if (first === null && last === null) return null;
|
|
228
|
+
return { top: (first ?? last as number) + half, bottom: (last ?? first as number) - half };
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Draw the row bands, under the text and across the whole block. A band rides
|
|
233
|
+
* its rows through a structural edit; between two sets, bands paired by position
|
|
234
|
+
* slide and recolour while an unpaired one fades.
|
|
235
|
+
*/
|
|
236
|
+
function drawRowBands(draw: CodeCanvas, state: DrawCodeState, width: number, pMove: number): void {
|
|
237
|
+
const { rowBands = [], rowsEdit = null } = state;
|
|
238
|
+
const paint = (top: number, bottom: number, color: NormalizedColor, radius: number, opacity: number): void => {
|
|
239
|
+
if (opacity <= 0 || top <= bottom) return;
|
|
240
|
+
draw.draw(new Graphics2D()
|
|
241
|
+
.rect({ x: 0, y: (top + bottom) / 2, width, height: top - bottom, cornerRadius: radius })
|
|
242
|
+
.fill([{ type: "solid", color, opacity }]));
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
if (!rowsEdit) {
|
|
246
|
+
for (const band of rowBands) {
|
|
247
|
+
const edges = bandEdges(band, state, pMove);
|
|
248
|
+
if (edges) paint(edges.top, edges.bottom, band.color, band.radius, 1);
|
|
249
|
+
}
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const p = rowsEdit.progress;
|
|
254
|
+
for (let i = 0; i < Math.max(rowsEdit.from.length, rowsEdit.to.length); i++) {
|
|
255
|
+
const was = rowsEdit.from[i];
|
|
256
|
+
const next = rowsEdit.to[i];
|
|
257
|
+
const a = was ? bandEdges(was, state, pMove) : null;
|
|
258
|
+
const b = next ? bandEdges(next, state, pMove) : null;
|
|
259
|
+
if (a && b) {
|
|
260
|
+
paint(
|
|
261
|
+
lerpNumber(a.top, b.top, p), lerpNumber(a.bottom, b.bottom, p),
|
|
262
|
+
lerpColor(was.color, next.color, p), lerpNumber(was.radius, next.radius, p), 1,
|
|
263
|
+
);
|
|
264
|
+
} else if (a) {
|
|
265
|
+
paint(a.top, a.bottom, was.color, was.radius, 1 - p);
|
|
266
|
+
} else if (b) {
|
|
267
|
+
paint(b.top, b.bottom, next.color, next.radius, p);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
204
272
|
/**
|
|
205
273
|
* Draw the gutter — one number per *slot*, not one per row.
|
|
206
274
|
*
|
package/src/style.ts
CHANGED
|
@@ -22,7 +22,7 @@ export interface CodeStyleRule {
|
|
|
22
22
|
* `@lezer/highlight`'s {@link tagHighlighter} resolves precedence.
|
|
23
23
|
*/
|
|
24
24
|
export interface CodeHighlightStyle {
|
|
25
|
-
/** Stable name, used
|
|
25
|
+
/** Stable name, used to register the style and to refer to it from a `theme` prop. */
|
|
26
26
|
name: string;
|
|
27
27
|
/** Color applied to text with no matching tag. */
|
|
28
28
|
defaultColor: string;
|
|
@@ -231,21 +231,25 @@ export type CodeThemeName = keyof typeof BUILTIN_THEMES;
|
|
|
231
231
|
*/
|
|
232
232
|
export type CodeTheme = CodeThemeName | (string & {}) | CodeHighlightStyle;
|
|
233
233
|
|
|
234
|
-
// Cache one compiled Highlighter per style
|
|
234
|
+
// Cache one compiled Highlighter per style *object*. Compiling builds a tag-rule
|
|
235
235
|
// matcher; the parsed result is pure data so it's safe to share across Code nodes.
|
|
236
|
-
|
|
236
|
+
// Keyed by the object rather than its `name`: two styles may share a name (an
|
|
237
|
+
// inline theme, or one re-registered under the same name with new colours), and
|
|
238
|
+
// the second must not colour with the first one's rules. A WeakMap, so a style
|
|
239
|
+
// nothing refers to any more takes its matcher with it.
|
|
240
|
+
const compiledByStyle = new WeakMap<CodeHighlightStyle, Highlighter>();
|
|
237
241
|
|
|
238
242
|
/**
|
|
239
243
|
* Compile a {@link CodeHighlightStyle} into a `@lezer/highlight` {@link Highlighter}
|
|
240
|
-
* whose "class" output is the literal color string for each tag. Cached
|
|
241
|
-
*
|
|
244
|
+
* whose "class" output is the literal color string for each tag. Cached per style
|
|
245
|
+
* object so repeated lookups (every tokenize) don't rebuild the matcher.
|
|
242
246
|
*/
|
|
243
247
|
export function compileStyle(style: CodeHighlightStyle): Highlighter {
|
|
244
|
-
const cached =
|
|
248
|
+
const cached = compiledByStyle.get(style);
|
|
245
249
|
if (cached) return cached;
|
|
246
250
|
const highlighter = tagHighlighter(
|
|
247
251
|
style.rules.map(rule => ({ tag: rule.tag, class: rule.color })),
|
|
248
252
|
);
|
|
249
|
-
|
|
253
|
+
compiledByStyle.set(style, highlighter);
|
|
250
254
|
return highlighter;
|
|
251
255
|
}
|
package/src/tokens.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { canHighlight, highlightToTokens } from "./highlight";
|
|
1
|
+
import { canHighlight, highlightToTokens, normalizeSource, resolveTheme } from "./highlight";
|
|
2
2
|
import { CodeTheme } from "./style";
|
|
3
3
|
|
|
4
4
|
// IdToken is our internal wrapper around a highlighter token that gives every
|
|
@@ -36,7 +36,11 @@ export function tokenizeCodeToIdLines(code: string, language: string, theme: Cod
|
|
|
36
36
|
// Until the language parser has loaded (it streams in via the asset loader),
|
|
37
37
|
// fall back to flat, uncolored lines instead of throwing on an unloaded
|
|
38
38
|
// grammar. The node re-tokenizes once the load completes.
|
|
39
|
+
code = normalizeSource(code);
|
|
39
40
|
if (!canHighlight(language, theme)) {
|
|
41
|
+
// Resolved for its warning: an unknown theme is reported whether or not
|
|
42
|
+
// the grammar has landed yet.
|
|
43
|
+
resolveTheme(theme);
|
|
40
44
|
return code.split('\n').map(line => makeIdLine([makeIdToken(line, '#d1d5db')]));
|
|
41
45
|
}
|
|
42
46
|
return highlightToTokens(code, language, theme).map(line =>
|
package/src/transitions.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { NormalizedColor } from "@motionscript/sdk";
|
|
1
2
|
import type { CodeLayout } from "./layout";
|
|
2
3
|
import { IdLine } from "./tokens";
|
|
3
4
|
|
|
@@ -77,7 +78,26 @@ export interface StructuralTransition {
|
|
|
77
78
|
toLayout: CodeLayout | null;
|
|
78
79
|
}
|
|
79
80
|
|
|
80
|
-
|
|
81
|
+
/** A filled band behind whole rows: the first and last line it covers, by line id so it follows them through an edit, and its paint. */
|
|
82
|
+
export interface RowBand {
|
|
83
|
+
startId: number;
|
|
84
|
+
endId: number;
|
|
85
|
+
color: NormalizedColor;
|
|
86
|
+
radius: number;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* One set of row bands becoming another. Bands at the same position in both
|
|
91
|
+
* lists travel and recolour; one with no counterpart fades.
|
|
92
|
+
*/
|
|
93
|
+
export interface RowsTransition {
|
|
94
|
+
kind: "rows";
|
|
95
|
+
from: RowBand[];
|
|
96
|
+
to: RowBand[];
|
|
97
|
+
progress: number;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export type CodeTransition = DimTransition | StructuralTransition | RowsTransition;
|
|
81
101
|
|
|
82
102
|
/**
|
|
83
103
|
* Decides *when* (in normalized transition time) each part of a structural
|