@revideo/2d 0.5.0 → 0.5.2

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.
@@ -78,13 +78,11 @@ export class CodeSignalContext<TOwner>
78
78
  public constructor(
79
79
  initial: PossibleCodeScope,
80
80
  owner: TOwner,
81
- private readonly highlighter?: SignalValue<CodeHighlighter>,
82
- private readonly dialect?: SignalValue<string>,
81
+ private readonly highlighter?: SignalValue<CodeHighlighter | null>,
83
82
  ) {
84
83
  super(initial, deepLerp, owner);
85
84
  if (owner instanceof Code) {
86
85
  this.highlighter ??= owner.highlighter;
87
- this.dialect ??= owner.dialect;
88
86
  }
89
87
  Object.defineProperty(this.invokable, 'edit', {
90
88
  value: this.edit.bind(this),
@@ -113,15 +111,14 @@ export class CodeSignalContext<TOwner>
113
111
  ): ThreadGenerator {
114
112
  let tokenize = defaultTokenize;
115
113
  const highlighter = unwrap(this.highlighter);
116
- const dialect = unwrap(this.dialect);
117
- if (highlighter && dialect) {
114
+ if (highlighter) {
118
115
  yield (async () => {
119
116
  do {
120
117
  await DependencyContext.consumePromises();
121
118
  highlighter.initialize();
122
119
  } while (DependencyContext.hasPromises());
123
120
  })();
124
- tokenize = (input: string) => highlighter.tokenize(input, dialect);
121
+ tokenize = (input: string) => highlighter.tokenize(input);
125
122
  }
126
123
 
127
124
  this.progress(0);
@@ -1,26 +1,23 @@
1
1
  import {HighlightStyle} from '@codemirror/language';
2
2
  import {Parser, SyntaxNode, Tree} from '@lezer/common';
3
3
  import {highlightTree} from '@lezer/highlight';
4
- import {useLogger} from '@revideo/core';
5
4
  import {CodeHighlighter, HighlightResult} from './CodeHighlighter';
6
- import {defaultTokenize} from './CodeTokenizer';
5
+ import {DefaultHighlightStyle} from './DefaultHighlightStyle';
7
6
 
8
7
  interface LezerCache {
9
8
  tree: Tree;
10
9
  code: string;
11
- colorLookup: Map<number, string>;
10
+ colorLookup: Map<string, string>;
12
11
  }
13
12
 
14
13
  export class LezerHighlighter implements CodeHighlighter<LezerCache | null> {
15
- private static readonly parserMap = new Map<string, Parser>();
16
14
  private static classRegex = /\.(\S+).*color:([^;]+)/;
17
15
  private readonly classLookup = new Map<string, string>();
18
16
 
19
- public static registerParser(parser: Parser, dialect = ''): void {
20
- this.parserMap.set(dialect, parser);
21
- }
22
-
23
- public constructor(private readonly style: HighlightStyle) {
17
+ public constructor(
18
+ private readonly parser: Parser,
19
+ private readonly style: HighlightStyle = DefaultHighlightStyle,
20
+ ) {
24
21
  for (const rule of this.style.module?.getRules().split('\n') ?? []) {
25
22
  const match = rule.match(LezerHighlighter.classRegex);
26
23
  if (!match) {
@@ -37,17 +34,9 @@ export class LezerHighlighter implements CodeHighlighter<LezerCache | null> {
37
34
  return true;
38
35
  }
39
36
 
40
- public prepare(code: string, dialect: string): LezerCache | null {
41
- const parser = LezerHighlighter.parserMap.get(dialect);
42
- if (!parser) {
43
- if (dialect !== '') {
44
- useLogger().warn(`No parser found for dialect: ${dialect}`);
45
- }
46
- return null;
47
- }
48
-
49
- const colorLookup = new Map<number, string>();
50
- const tree = parser.parse(code);
37
+ public prepare(code: string): LezerCache | null {
38
+ const colorLookup = new Map<string, string>();
39
+ const tree = this.parser.parse(code);
51
40
  highlightTree(tree, this.style, (from, to, classes) => {
52
41
  const color = this.classLookup.get(classes);
53
42
  if (!color) {
@@ -97,16 +86,8 @@ export class LezerHighlighter implements CodeHighlighter<LezerCache | null> {
97
86
  };
98
87
  }
99
88
 
100
- public tokenize(code: string, dialect: string): string[] {
101
- const parser = LezerHighlighter.parserMap.get(dialect);
102
- if (!parser) {
103
- if (dialect !== '') {
104
- useLogger().warn(`No parser found for dialect: ${dialect}`);
105
- }
106
- return defaultTokenize(code);
107
- }
108
-
109
- const tree = parser.parse(code);
89
+ public tokenize(code: string): string[] {
90
+ const tree = this.parser.parse(code);
110
91
  const cursor = tree.cursor();
111
92
  const tokens: string[] = [];
112
93
  let current = 0;
@@ -126,14 +107,7 @@ export class LezerHighlighter implements CodeHighlighter<LezerCache | null> {
126
107
  return tokens;
127
108
  }
128
109
 
129
- private getNodeId(node: SyntaxNode): number {
130
- if (!node.parent) {
131
- return -1;
132
- }
133
-
134
- // NOTE: They don't want us to know about this property.
135
- // We need a way to persistently identify nodes and this seems to work.
136
- // Perhaps it could break if the tree is edited? But we don't do that. Yet.
137
- return (node as any).index;
110
+ private getNodeId(node: SyntaxNode): string {
111
+ return `${node.from}:${node.to}`;
138
112
  }
139
113
  }
@@ -61,9 +61,7 @@ export function extractRange(
61
61
  if (fromColumn === currentColumn) {
62
62
  index = newFragments.length + 1;
63
63
  newFragments.push(resolved.slice(0, i), '');
64
- }
65
-
66
- if (char === '\n') {
64
+ } else if (char === '\n') {
67
65
  index = newFragments.length + 1;
68
66
  newFragments.push(
69
67
  resolved.slice(0, i) + ' '.repeat(fromColumn - currentColumn),
@@ -80,7 +78,14 @@ export function extractRange(
80
78
  }
81
79
 
82
80
  if (char === '\n') {
83
- newFragments.push(resolved.slice(i));
81
+ if (currentColumn < toColumn) {
82
+ extracted += '\n';
83
+ if (i + 1 < resolved.length) {
84
+ newFragments.push(resolved.slice(i + 1));
85
+ }
86
+ } else {
87
+ newFragments.push(resolved.slice(i));
88
+ }
84
89
  found = true;
85
90
  break;
86
91
  }
@@ -1,8 +1,7 @@
1
1
  import {
2
2
  BBox,
3
3
  createSignal,
4
- ExperimentalError,
5
- lazy,
4
+ experimentalLog,
6
5
  map,
7
6
  SerializedVector2,
8
7
  Signal,
@@ -11,6 +10,8 @@ import {
11
10
  ThreadGenerator,
12
11
  TimingFunction,
13
12
  unwrap,
13
+ useLogger,
14
+ useScene,
14
15
  Vector2,
15
16
  } from '@revideo/core';
16
17
  import {
@@ -23,10 +24,8 @@ import {
23
24
  CodeSignal,
24
25
  codeSignal,
25
26
  CodeSignalContext,
26
- DefaultHighlightStyle,
27
27
  findAllCodeRanges,
28
28
  isPointInCodeSelection,
29
- LezerHighlighter,
30
29
  lines,
31
30
  parseCodeSelection,
32
31
  PossibleCodeScope,
@@ -35,7 +34,6 @@ import {
35
34
  } from '../code';
36
35
  import {computed, initial, nodeName, parser, signal} from '../decorators';
37
36
  import {DesiredLength} from '../partials';
38
- import {useScene2D} from '../scenes';
39
37
  import {Shape, ShapeProps} from './Shape';
40
38
 
41
39
  export interface DrawTokenHook {
@@ -75,10 +73,6 @@ export interface CodeProps extends ShapeProps {
75
73
  * {@inheritDoc Code.highlighter}
76
74
  */
77
75
  highlighter?: SignalValue<CodeHighlighter | null>;
78
- /**
79
- * {@inheritDoc Code.dialect}
80
- */
81
- dialect?: SignalValue<string>;
82
76
  /**
83
77
  * {@inheritDoc Code.code}
84
78
  */
@@ -143,59 +137,19 @@ export class Code extends Shape {
143
137
  *
144
138
  * @param initial - The initial code.
145
139
  * @param highlighter - Custom highlighter to use.
146
- * @param dialect - Custom dialect to use.
147
140
  */
148
141
  public static createSignal(
149
142
  initial: PossibleCodeScope,
150
143
  highlighter?: SignalValue<CodeHighlighter>,
151
- dialect?: SignalValue<string>,
152
144
  ): CodeSignal<void> {
153
145
  return new CodeSignalContext<void>(
154
146
  initial,
155
147
  undefined,
156
148
  highlighter,
157
- dialect,
158
149
  ).toSignal();
159
150
  }
160
151
 
161
- @lazy(() => new LezerHighlighter(DefaultHighlightStyle))
162
- public static readonly defaultHighlighter: LezerHighlighter;
163
-
164
- /**
165
- * The dialect to use for highlighting the code.
166
- *
167
- * @remarks
168
- * This value will be passed to the {@link code.CodeHighlighter}
169
- * defined by the {@link highlighter} property. Different highlighters may use
170
- * it differently.
171
- *
172
- * The default {@link code.LezerHighlighter} uses it to select
173
- * the language parser to use. The parser for the given dialect can be
174
- * registered as follows:
175
- * ```tsx
176
- * // Import the lezer parser:
177
- * import {parser} from '@lezer/javascript';
178
- *
179
- * // Register it in the highlighter:
180
- * LezerHighlighter.registerParser(parser, 'js');
181
- *
182
- * // Use the dialect in a code node:
183
- * <Code dialect="js" code="const a = 7;" />
184
- * ```
185
- * When no dialect is provided, the highlighter will use the default
186
- * parser:
187
- * ```tsx
188
- * // Register the default parser by omitting the dialect:
189
- * LezerHighlighter.registerParser(parser);
190
- *
191
- * // Code nodes with no dialect will now use the default parser:
192
- * <Code code="const a = 7;" />
193
- * ```
194
- */
195
- @initial('')
196
- @signal()
197
- public declare readonly dialect: SimpleSignal<string, this>;
198
-
152
+ public static defaultHighlighter: CodeHighlighter | null = null;
199
153
  /**
200
154
  * The code highlighter to use for this code node.
201
155
  *
@@ -204,7 +158,10 @@ export class Code extends Shape {
204
158
  */
205
159
  @initial(() => Code.defaultHighlighter)
206
160
  @signal()
207
- public declare readonly highlighter: SimpleSignal<CodeHighlighter, this>;
161
+ public declare readonly highlighter: SimpleSignal<
162
+ CodeHighlighter | null,
163
+ this
164
+ >;
208
165
 
209
166
  /**
210
167
  * The code to display.
@@ -246,6 +203,20 @@ export class Code extends Shape {
246
203
  @signal()
247
204
  public declare readonly drawHooks: SimpleSignal<DrawHooks, this>;
248
205
 
206
+ protected setDrawHooks(value: DrawHooks) {
207
+ if (
208
+ !useScene().experimentalFeatures &&
209
+ value !== this.drawHooks.context.getInitial()
210
+ ) {
211
+ useLogger().log({
212
+ ...experimentalLog(`Code uses experimental draw hooks.`),
213
+ inspect: this.key,
214
+ });
215
+ } else {
216
+ this.drawHooks.context.setter(value);
217
+ }
218
+ }
219
+
249
220
  /**
250
221
  * The currently selected code range.
251
222
  *
@@ -309,11 +280,10 @@ export class Code extends Shape {
309
280
  const code = this.code();
310
281
  const before = resolveScope(code, false);
311
282
  const after = resolveScope(code, true);
312
- const dialect = this.dialect();
313
283
 
314
284
  return {
315
- before: highlighter.prepare(before, dialect),
316
- after: highlighter.prepare(after, dialect),
285
+ before: highlighter.prepare(before),
286
+ after: highlighter.prepare(after),
317
287
  };
318
288
  }
319
289
 
@@ -328,9 +298,6 @@ export class Code extends Shape {
328
298
  fontFamily: 'monospace',
329
299
  ...props,
330
300
  });
331
- if (!useScene2D().experimentalFeatures) {
332
- throw new ExperimentalError('The Code node is an experimental feature');
333
- }
334
301
  }
335
302
 
336
303
  /**
@@ -343,7 +310,6 @@ export class Code extends Shape {
343
310
  initial,
344
311
  this,
345
312
  this.highlighter,
346
- this.dialect,
347
313
  ).toSignal();
348
314
  }
349
315
 
@@ -47,6 +47,9 @@ export interface CodeModification {
47
47
  to: Code;
48
48
  }
49
49
 
50
+ /**
51
+ * @deprecated Use {@link Code} instead.
52
+ */
50
53
  @nodeName('CodeBlock')
51
54
  export class CodeBlock extends Shape {
52
55
  private static initialized = createComputedAsync(