@alloy-js/core 0.20.0-dev.3 → 0.20.0-dev.6

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.
Files changed (137) hide show
  1. package/dist/src/binder.d.ts +62 -38
  2. package/dist/src/binder.d.ts.map +1 -1
  3. package/dist/src/binder.js +214 -173
  4. package/dist/src/components/Declaration.d.ts +2 -2
  5. package/dist/src/components/Declaration.d.ts.map +1 -1
  6. package/dist/src/components/Declaration.js +8 -2
  7. package/dist/src/components/MemberDeclaration.d.ts +2 -2
  8. package/dist/src/components/MemberDeclaration.d.ts.map +1 -1
  9. package/dist/src/components/MemberDeclaration.js +9 -5
  10. package/dist/src/components/MemberScope.d.ts +30 -13
  11. package/dist/src/components/MemberScope.d.ts.map +1 -1
  12. package/dist/src/components/MemberScope.js +37 -15
  13. package/dist/src/components/Output.d.ts.map +1 -1
  14. package/dist/src/components/Output.js +2 -5
  15. package/dist/src/components/ReferenceOrContent.d.ts +1 -1
  16. package/dist/src/components/ReferenceOrContent.d.ts.map +1 -1
  17. package/dist/src/components/Scope.d.ts +5 -5
  18. package/dist/src/components/Scope.d.ts.map +1 -1
  19. package/dist/src/components/Scope.js +9 -5
  20. package/dist/src/context/member-scope.d.ts +7 -8
  21. package/dist/src/context/member-scope.d.ts.map +1 -1
  22. package/dist/src/context/member-scope.js +5 -5
  23. package/dist/src/context/name-policy.d.ts.map +1 -1
  24. package/dist/src/context/name-policy.js +3 -0
  25. package/dist/src/context/scope.d.ts +1 -0
  26. package/dist/src/context/scope.d.ts.map +1 -1
  27. package/dist/src/context/scope.js +7 -0
  28. package/dist/src/inspect.browser.d.ts +5 -0
  29. package/dist/src/inspect.browser.d.ts.map +1 -0
  30. package/dist/src/inspect.browser.js +5 -0
  31. package/dist/src/inspect.d.ts +2 -0
  32. package/dist/src/inspect.d.ts.map +1 -0
  33. package/dist/src/inspect.js +1 -0
  34. package/dist/src/name-policy.d.ts +11 -0
  35. package/dist/src/name-policy.d.ts.map +1 -1
  36. package/dist/src/name-policy.js +3 -0
  37. package/dist/src/reactive-union-set.d.ts.map +1 -1
  38. package/dist/src/reactive-union-set.js +12 -8
  39. package/dist/src/refkey.d.ts +39 -3
  40. package/dist/src/refkey.d.ts.map +1 -1
  41. package/dist/src/refkey.js +52 -8
  42. package/dist/src/symbols/basic-scope.d.ts +14 -0
  43. package/dist/src/symbols/basic-scope.d.ts.map +1 -0
  44. package/dist/src/symbols/basic-scope.js +20 -0
  45. package/dist/src/symbols/basic-symbol.d.ts +19 -0
  46. package/dist/src/symbols/basic-symbol.d.ts.map +1 -0
  47. package/dist/src/symbols/basic-symbol.js +28 -0
  48. package/dist/src/symbols/index.d.ts +3 -1
  49. package/dist/src/symbols/index.d.ts.map +1 -1
  50. package/dist/src/symbols/index.js +3 -1
  51. package/dist/src/symbols/output-scope.d.ts +70 -41
  52. package/dist/src/symbols/output-scope.d.ts.map +1 -1
  53. package/dist/src/symbols/output-scope.js +98 -130
  54. package/dist/src/symbols/output-space.d.ts +25 -0
  55. package/dist/src/symbols/output-space.d.ts.map +1 -0
  56. package/dist/src/symbols/output-space.js +35 -0
  57. package/dist/src/symbols/output-symbol.d.ts +213 -37
  58. package/dist/src/symbols/output-symbol.d.ts.map +1 -1
  59. package/dist/src/symbols/output-symbol.js +323 -203
  60. package/dist/src/symbols/symbol-flow.d.ts +1 -1
  61. package/dist/src/symbols/symbol-flow.d.ts.map +1 -1
  62. package/dist/src/symbols/symbol-flow.js +22 -7
  63. package/dist/src/symbols/symbol-slot.d.ts +27 -9
  64. package/dist/src/symbols/symbol-slot.d.ts.map +1 -1
  65. package/dist/src/symbols/symbol-slot.js +20 -4
  66. package/dist/src/symbols/symbol-table.d.ts +19 -8
  67. package/dist/src/symbols/symbol-table.d.ts.map +1 -1
  68. package/dist/src/symbols/symbol-table.js +65 -16
  69. package/dist/src/tracer.d.ts +15 -3
  70. package/dist/src/tracer.d.ts.map +1 -1
  71. package/dist/src/tracer.js +39 -63
  72. package/dist/src/utils.d.ts +1 -1
  73. package/dist/src/utils.d.ts.map +1 -1
  74. package/dist/src/utils.js +4 -4
  75. package/dist/src/write-output.d.ts +1 -1
  76. package/dist/src/write-output.d.ts.map +1 -1
  77. package/dist/src/write-output.js +31 -37
  78. package/dist/test/components/declaration.test.js +9 -14
  79. package/dist/test/components/reference-or-content.test.js +2 -2
  80. package/dist/test/symbols/output-scope.test.js +33 -198
  81. package/dist/test/symbols/output-symbol.test.js +139 -385
  82. package/dist/test/symbols/resolution.test.js +431 -114
  83. package/dist/test/symbols/symbol-table.test.d.ts +2 -0
  84. package/dist/test/symbols/symbol-table.test.d.ts.map +1 -0
  85. package/dist/test/symbols/symbol-table.test.js +14 -0
  86. package/dist/test/symbols/utils.d.ts +10 -24
  87. package/dist/test/symbols/utils.d.ts.map +1 -1
  88. package/dist/test/symbols/utils.js +23 -45
  89. package/dist/tsconfig.tsbuildinfo +1 -1
  90. package/package.json +4 -2
  91. package/src/binder.ts +348 -273
  92. package/src/components/Declaration.tsx +13 -3
  93. package/src/components/MemberDeclaration.tsx +15 -8
  94. package/src/components/MemberScope.tsx +61 -20
  95. package/src/components/Output.tsx +0 -4
  96. package/src/components/Scope.tsx +16 -9
  97. package/src/context/member-scope.ts +10 -10
  98. package/src/context/name-policy.ts +3 -0
  99. package/src/context/scope.ts +9 -0
  100. package/src/inspect.browser.ts +6 -0
  101. package/src/inspect.ts +1 -0
  102. package/src/name-policy.ts +14 -0
  103. package/src/reactive-union-set.ts +14 -8
  104. package/src/refkey.ts +88 -14
  105. package/src/symbols/basic-scope.ts +23 -0
  106. package/src/symbols/basic-symbol.ts +32 -0
  107. package/src/symbols/index.ts +3 -1
  108. package/src/symbols/output-scope.ts +131 -170
  109. package/src/symbols/output-space.ts +49 -0
  110. package/src/symbols/output-symbol.ts +434 -258
  111. package/src/symbols/symbol-flow.ts +38 -9
  112. package/src/symbols/symbol-slot.tsx +46 -8
  113. package/src/symbols/symbol-table.ts +95 -21
  114. package/src/tracer.ts +53 -83
  115. package/src/utils.tsx +4 -4
  116. package/src/write-output.ts +33 -45
  117. package/temp/api.json +5551 -3066
  118. package/test/components/declaration.test.tsx +6 -19
  119. package/test/components/reference-or-content.test.tsx +2 -2
  120. package/test/symbols/output-scope.test.ts +33 -125
  121. package/test/symbols/output-symbol.test.ts +128 -348
  122. package/test/symbols/resolution.test.ts +530 -117
  123. package/test/symbols/symbol-table.test.ts +15 -0
  124. package/test/symbols/utils.ts +38 -74
  125. package/tsdoc.json +4 -0
  126. package/dist/src/slot.d.ts +0 -15
  127. package/dist/src/slot.d.ts.map +0 -1
  128. package/dist/src/slot.js +0 -50
  129. package/dist/src/symbols/flags.d.ts +0 -70
  130. package/dist/src/symbols/flags.d.ts.map +0 -1
  131. package/dist/src/symbols/flags.js +0 -72
  132. package/dist/test/components/slot.test.d.ts +0 -2
  133. package/dist/test/components/slot.test.d.ts.map +0 -1
  134. package/dist/test/components/slot.test.js +0 -134
  135. package/src/slot.ts +0 -89
  136. package/src/symbols/flags.ts +0 -82
  137. package/test/components/slot.test.tsx +0 -174
@@ -1,9 +1,9 @@
1
1
  import { isRef, Ref, shallowReactive } from "@vue/reactivity";
2
2
  import { Context, effect, getContext, onCleanup } from "../reactivity.js";
3
3
 
4
- import { MemberScopeContext } from "../context/member-scope.js";
4
+ import { MemberContext } from "../context/member-scope.js";
5
5
  import { ScopeContext } from "../context/scope.js";
6
- import { OutputSymbolFlags } from "./flags.js";
6
+ import { formatSymbolName, trace, TracePhase } from "../tracer.js";
7
7
  import { OutputSymbol } from "./output-symbol.js";
8
8
 
9
9
  export interface TakeSymbolCallback {
@@ -36,6 +36,15 @@ export function takeSymbols(cb?: (symbol: OutputSymbol) => void) {
36
36
  export function emitSymbol(
37
37
  symbol: OutputSymbol | Ref<OutputSymbol | undefined>,
38
38
  ) {
39
+ if (isRef(symbol)) {
40
+ trace(TracePhase.symbol.flow, () => `Emitting ref to symbol`);
41
+ } else {
42
+ trace(
43
+ TracePhase.symbol.flow,
44
+ () => `Emitting symbol ${formatSymbolName(symbol)}`,
45
+ );
46
+ }
47
+
39
48
  let symbolTaker: Context | undefined;
40
49
  let context = getContext()!.owner;
41
50
  while (context) {
@@ -46,10 +55,13 @@ export function emitSymbol(
46
55
 
47
56
  if (
48
57
  context.context &&
49
- (context.context[ScopeContext.id] ||
50
- context.context[MemberScopeContext.id])
58
+ (context.context[ScopeContext.id] || context.context[MemberContext.id])
51
59
  ) {
52
60
  // don't cross scope boundaries.
61
+ trace(
62
+ TracePhase.symbol.flow,
63
+ () => `Not emitting symbol across scope boundary`,
64
+ );
53
65
  break;
54
66
  }
55
67
 
@@ -57,6 +69,7 @@ export function emitSymbol(
57
69
  }
58
70
 
59
71
  if (!symbolTaker) {
72
+ trace(TracePhase.symbol.flow, () => `No symbol taker found, not emitting`);
60
73
  return;
61
74
  }
62
75
 
@@ -76,6 +89,11 @@ export function emitSymbol(
76
89
  }
77
90
  });
78
91
  } else {
92
+ trace(
93
+ TracePhase.symbol.flow,
94
+ () =>
95
+ `Emitting symbol ${formatSymbolName(symbol)} taken by ${symbolTaker.componentOwner?.name ?? "unknown component"}`,
96
+ );
79
97
  symbolTaker.takenSymbols!.add(symbol);
80
98
  onCleanup(() => {
81
99
  context!.takenSymbols!.delete(symbol);
@@ -85,16 +103,27 @@ export function emitSymbol(
85
103
 
86
104
  export function moveTakenMembersTo(baseSymbol: OutputSymbol) {
87
105
  const taken = takeSymbols();
88
-
89
106
  effect(() => {
90
107
  for (const symbol of taken) {
91
- if (symbol.flags & OutputSymbolFlags.Transient) {
92
- symbol.moveTo(baseSymbol);
108
+ if (symbol.isTransient) {
109
+ symbol.moveMembersTo(baseSymbol);
110
+ }
111
+
112
+ for (const refkey of symbol.refkeys) {
113
+ if (!baseSymbol.refkeys.includes(refkey)) {
114
+ baseSymbol.refkeys.push(refkey);
115
+ }
93
116
  }
94
117
  }
95
118
  });
96
119
  }
97
120
 
98
- export function instantiateTakenMembersTo(baseSymbol: OutputSymbol) {
99
- takeSymbols((symbol) => symbol.instantiateTo(baseSymbol));
121
+ export function instantiateTakenMembersTo(
122
+ baseSymbol: OutputSymbol,
123
+ toSpaceKey: string,
124
+ fromSpaceKey: string,
125
+ ) {
126
+ takeSymbols((symbol) => {
127
+ baseSymbol.type = symbol;
128
+ });
100
129
  }
@@ -1,10 +1,37 @@
1
1
  import { Ref, ShallowReactive, shallowRef } from "@vue/reactivity";
2
2
  import { effect, onCleanup } from "../reactivity.js";
3
- import type { Children } from "../runtime/component.js";
3
+ import type { Children, Component } from "../runtime/component.js";
4
4
  import { OutputSymbol } from "./output-symbol.js";
5
5
  import { takeSymbols } from "./symbol-flow.js";
6
6
 
7
- export function createSymbolSlot() {
7
+ export interface SymbolSlot extends Component<{}> {
8
+ /**
9
+ * A ref for the set of symbols taken by this slot. Undefined when the Slot component
10
+ * has not been rendered yet.
11
+ */
12
+ ref: Ref<ShallowReactive<Set<OutputSymbol>> | undefined>;
13
+ /**
14
+ * A ref for the first symbol taken by this slot. Undefined when the Slot component
15
+ * has not been rendered yet or has not taken any symbols.
16
+ */
17
+ firstSymbol: Ref<OutputSymbol | undefined>;
18
+
19
+ /**
20
+ * Copy any members from taken symbols to the given symbol.
21
+ */
22
+ copyMembersTo(baseSymbol: OutputSymbol): void;
23
+
24
+ /**
25
+ * Move any members from transient taken symbols to the given symbol.
26
+ */
27
+ moveMembersTo(baseSymbol: OutputSymbol): void;
28
+ }
29
+ /**
30
+ * Create a component which accepts emitted symbols. The returned component has
31
+ * a `ref` property which is a ref to a reactive set of all symbols emitted by
32
+ * children of the component.
33
+ */
34
+ export function createSymbolSlot(): SymbolSlot {
8
35
  const symbolSlotRef: Ref<ShallowReactive<Set<OutputSymbol>> | undefined> =
9
36
  shallowRef();
10
37
  function SymbolSlot(props: { children: Children }) {
@@ -20,29 +47,40 @@ export function createSymbolSlot() {
20
47
 
21
48
  SymbolSlot.ref = symbolSlotRef;
22
49
 
23
- SymbolSlot.instantiateInto = (baseSymbol: OutputSymbol) => {
50
+ Object.defineProperty(SymbolSlot, "firstSymbol", {
51
+ get() {
52
+ const ref = shallowRef();
53
+ effect(() => {
54
+ ref.value = symbolSlotRef.value?.values().next().value;
55
+ });
56
+ return ref;
57
+ },
58
+ });
59
+
60
+ SymbolSlot.copyMembersTo = (baseSymbol: OutputSymbol) => {
24
61
  effect(() => {
25
62
  if (!symbolSlotRef.value) {
26
63
  return;
27
64
  }
28
65
 
29
66
  for (const symbol of symbolSlotRef.value) {
30
- symbol.instantiateTo(baseSymbol);
67
+ symbol.copyMembersTo(baseSymbol);
31
68
  }
32
69
  });
33
70
  };
34
71
 
35
- SymbolSlot.copyInto = (baseSymbol: OutputSymbol) => {
72
+ SymbolSlot.moveMembersTo = (baseSymbol: OutputSymbol) => {
36
73
  effect(() => {
37
74
  if (!symbolSlotRef.value) {
38
75
  return;
39
76
  }
40
-
41
77
  for (const symbol of symbolSlotRef.value) {
42
- symbol.copyTo(baseSymbol);
78
+ if (symbol.isTransient) {
79
+ symbol.moveMembersTo(baseSymbol);
80
+ }
43
81
  }
44
82
  });
45
83
  };
46
84
 
47
- return SymbolSlot;
85
+ return SymbolSlot as any;
48
86
  }
@@ -1,34 +1,64 @@
1
- import type { NameConflictResolver } from "../binder.js";
1
+ import type { Binder, NameConflictResolver } from "../binder.js";
2
2
  import { ReactiveUnionSet } from "../reactive-union-set.js";
3
+ import { Refkey } from "../refkey.js";
3
4
  import { queueJob } from "../scheduler.js";
4
5
  import {
5
- formatScopeName,
6
6
  formatSymbolName,
7
+ formatSymbolTableName,
7
8
  trace,
8
9
  TracePhase,
9
10
  } from "../tracer.js";
10
- import type { OutputScope } from "./output-scope.js";
11
+ import { OutputSpace } from "./output-space.js";
11
12
  import type { OutputSymbol } from "./output-symbol.js";
12
13
 
13
- export class SymbolTable extends ReactiveUnionSet<OutputSymbol> {
14
- private _namesToDeconflict: Set<string> = new Set();
15
- private _nameConflictResolver?: NameConflictResolver;
16
- private _deconflictNames = () => {
17
- for (const name of this._namesToDeconflict) {
14
+ export abstract class SymbolTable extends ReactiveUnionSet<OutputSymbol> {
15
+ #namesToDeconflict: Set<string> = new Set();
16
+ #nameConflictResolver?: NameConflictResolver;
17
+ #deconflictNames = () => {
18
+ for (const name of this.#namesToDeconflict) {
18
19
  const conflictedSymbols = [...this].filter(
19
- (sym) => sym.originalName === name,
20
+ (sym) => sym.originalName === name && !sym.ignoreNameConflict,
20
21
  );
21
- if (this._nameConflictResolver) {
22
- this._nameConflictResolver(name, conflictedSymbols);
22
+ if (this.#nameConflictResolver) {
23
+ this.#nameConflictResolver(name, conflictedSymbols);
23
24
  } else {
24
25
  defaultConflictHandler(name, conflictedSymbols);
25
26
  }
26
- this._namesToDeconflict.delete(name);
27
+ this.#namesToDeconflict.delete(name);
27
28
  }
28
29
  };
29
- public scope;
30
+
31
+ #binder: Binder | undefined;
32
+
33
+ get binder() {
34
+ return this.#binder;
35
+ }
36
+
37
+ #key: string;
38
+
39
+ /**
40
+ * The key of this symbol table, e.g. "static" or "instance".
41
+ */
42
+ get key() {
43
+ return this.#key;
44
+ }
45
+
46
+ #symbolsByRefkey: ReadonlyMap<Refkey, OutputSymbol>;
47
+ /**
48
+ * The symbols defined within this scope, indexed by refkey.
49
+ */
50
+ get symbolsByRefkey() {
51
+ return this.#symbolsByRefkey;
52
+ }
53
+
54
+ #symbolNames: ReadonlyMap<string, OutputSymbol>;
55
+ get symbolNames() {
56
+ return this.#symbolNames;
57
+ }
58
+
30
59
  constructor(
31
- scope: OutputScope,
60
+ key: string,
61
+ binder?: Binder,
32
62
  options: {
33
63
  nameConflictResolver?: NameConflictResolver;
34
64
  } = {},
@@ -37,26 +67,70 @@ export class SymbolTable extends ReactiveUnionSet<OutputSymbol> {
37
67
  onAdd: (symbol) => {
38
68
  trace(
39
69
  TracePhase.symbol.addToScope,
40
- () => `${formatSymbolName(symbol)} -> ${formatScopeName(scope)}`,
70
+ () =>
71
+ `${formatSymbolName(symbol)} added to ${formatSymbolTableName(this)}`,
41
72
  );
42
73
 
43
- this._namesToDeconflict.add(symbol.name);
74
+ this.#namesToDeconflict.add(symbol.name);
44
75
 
45
- queueJob(this._deconflictNames);
76
+ queueJob(this.#deconflictNames);
46
77
 
47
78
  return symbol;
48
79
  },
49
- onDelete(symbol) {
80
+ onDelete: (symbol) => {
50
81
  trace(
51
82
  TracePhase.symbol.removeFromScope,
52
- () => `${formatSymbolName(symbol)} -> ${formatScopeName(scope)}`,
83
+ () =>
84
+ `${formatSymbolName(symbol)} removed from ${formatSymbolTableName(this)}`,
53
85
  );
54
86
  },
55
87
  });
56
88
 
57
- this.scope = scope;
89
+ this.#nameConflictResolver =
90
+ options.nameConflictResolver ?? binder?.nameConflictResolver;
91
+ this.#binder = binder;
92
+ this.#key = key;
93
+ this.#symbolsByRefkey = this.createIndex((s) => s.refkeys);
94
+ this.#symbolNames = this.createIndex((s) => {
95
+ return s.name;
96
+ });
97
+ }
98
+
99
+ moveTo(target: SymbolTable): void {
100
+ trace(
101
+ TracePhase.scope.moveSymbols,
102
+ () =>
103
+ `${formatSymbolTableName(this)} -> ${formatSymbolTableName(target)}`,
104
+ );
105
+
106
+ target.addSubset(this, {
107
+ onAdd: (symbol) => {
108
+ symbol.spaces = [target as OutputSpace];
109
+ return symbol;
110
+ },
111
+ });
112
+ }
113
+
114
+ copyTo(
115
+ target: SymbolTable,
116
+ options: {
117
+ createRefkeys?(sourceSymbol: OutputSymbol): Refkey[];
118
+ } = {},
119
+ ): void {
120
+ trace(
121
+ TracePhase.scope.copySymbols,
122
+ () =>
123
+ `${formatSymbolTableName(this)} copied to ${formatSymbolTableName(target)}`,
124
+ );
58
125
 
59
- this._nameConflictResolver = options.nameConflictResolver;
126
+ target.addSubset(this, {
127
+ onAdd: (symbol) => {
128
+ const copy = symbol.copy();
129
+ copy.spaces = [target as OutputSpace];
130
+ copy.refkeys = options.createRefkeys?.(symbol) ?? [];
131
+ return copy;
132
+ },
133
+ });
60
134
  }
61
135
  }
62
136
 
package/src/tracer.ts CHANGED
@@ -1,10 +1,14 @@
1
1
  import { effect, ReactiveEffectRunner } from "@vue/reactivity";
2
2
  import { untrack } from "./reactivity.js";
3
- import type { Refkey } from "./refkey.js";
3
+ import { isMemberRefkey, type Refkey } from "./refkey.js";
4
4
  import { scheduler } from "./scheduler.js";
5
- import { OutputScopeFlags, OutputSymbolFlags } from "./symbols/flags.js";
6
5
  import { type OutputScope } from "./symbols/output-scope.js";
6
+ import type {
7
+ OutputDeclarationSpace,
8
+ OutputMemberSpace,
9
+ } from "./symbols/output-space.js";
7
10
  import { type OutputSymbol } from "./symbols/output-symbol.js";
11
+ import { SymbolTable } from "./symbols/symbol-table.js";
8
12
 
9
13
  // enable tracing for specific phases using a comma separated list of
10
14
  // dotted identifiers, e.g. `scope.update,symbol.create`.
@@ -47,6 +51,11 @@ export const TracePhase = {
47
51
  subarea: "copySymbols",
48
52
  bg: { r: 0, g: 100, b: 100 },
49
53
  },
54
+ moveSymbols: {
55
+ area: "scope",
56
+ subarea: "moveSymbols",
57
+ bg: { r: 0, g: 100, b: 100 },
58
+ },
50
59
  },
51
60
  symbol: {
52
61
  update: {
@@ -274,24 +283,6 @@ function colorText(text: string, fmt?: TextFormat): string {
274
283
  return `${prefix}${text}${reset}`;
275
284
  }
276
285
 
277
- /**
278
- * Format flag values in a concise way, showing only the flags that are set
279
- * @param flags The numeric flags value to format
280
- * @param flagEnum The enum containing flag definitions
281
- * @returns An array of flag names that are set
282
- */
283
- function formatFlags<T extends Record<string, string | number>>(
284
- flags: number,
285
- flagEnum: T,
286
- ): string[] {
287
- return Object.entries(flagEnum)
288
- .filter(
289
- ([name, value]) =>
290
- typeof value === "number" && value !== 0 && (flags & value) === value,
291
- )
292
- .map(([name]) => name);
293
- }
294
-
295
286
  /**
296
287
  * Format a symbol name with its ID in a blue-green color
297
288
  * @param symbol The symbol to format
@@ -307,21 +298,6 @@ export function formatSymbolName(symbol: OutputSymbol): string {
307
298
  });
308
299
  }
309
300
 
310
- /**
311
- * Format the symbols in a member scope, showing their names and IDs
312
- * @param scope The member scope containing the symbols to format
313
- * @returns A formatted string representation of the member scope symbols
314
- */
315
- function formatMemberScopeSymbols(scope: OutputScope): string {
316
- if (!scope || scope.symbols.size === 0) {
317
- return "none";
318
- }
319
-
320
- return Array.from(scope.symbols)
321
- .map((symbol) => formatSymbolName(symbol))
322
- .join(", ");
323
- }
324
-
325
301
  export function formatSymbol(symbol: OutputSymbol): string {
326
302
  // Base display with name and ID
327
303
  let result = formatSymbolName(symbol);
@@ -333,35 +309,19 @@ export function formatSymbol(symbol: OutputSymbol): string {
333
309
  details.push(colorText(" !UNBOUND", { fg: { r: 255, g: 0, b: 0 } }));
334
310
  }
335
311
 
336
- // Show only enabled flags
337
- const flagsInfo = formatFlags(symbol.flags, OutputSymbolFlags);
338
-
339
- if (flagsInfo.length > 0) {
340
- details.push(` flags: ${flagsInfo.join(", ")}`);
341
- }
342
-
343
312
  // Show scope info with formatted name
344
313
  if (symbol.scope) {
345
314
  details.push(untrack(() => ` scope: ${formatScopeName(symbol.scope)}`));
346
315
  }
347
316
 
348
- // Show member scopes if present
349
- if (symbol.instanceMemberScope) {
350
- untrack(() => {
351
- const memberCount = symbol.instanceMemberScope!.symbols.size;
352
- details.push(
353
- ` instance members: ${memberCount} - ${formatMemberScopeSymbols(symbol.instanceMemberScope!)}`,
354
- );
355
- });
317
+ for (const space of symbol.memberSpaces) {
318
+ details.push(untrack(() => formatSpaceSymbols(space)));
356
319
  }
357
320
 
358
- if (symbol.staticMemberScope) {
359
- untrack(() => {
360
- const memberCount = symbol.staticMemberScope!.symbols.size;
361
- details.push(
362
- ` static members: ${memberCount} - ${formatMemberScopeSymbols(symbol.staticMemberScope!)}`,
363
- );
364
- });
321
+ if (symbol.ownerSymbol) {
322
+ details.push(
323
+ untrack(() => ` ownerSymbol: ${formatSymbolName(symbol.ownerSymbol!)}`),
324
+ );
365
325
  }
366
326
 
367
327
  // Show all refkeys with proper formatting
@@ -376,20 +336,37 @@ export function formatSymbol(symbol: OutputSymbol): string {
376
336
  return result;
377
337
  }
378
338
 
379
- export function formatScopeName(scope: OutputScope): string {
380
- let text = colorText(`${scope.name}[${scope.id}]`, {
339
+ export function formatSpaceSymbols(space: SymbolTable) {
340
+ return ` ${space.key} symbols (${space.size}): ${[...space].map((s) => s.name).join(", ")}`;
341
+ }
342
+
343
+ export function formatScopeName(scope: OutputScope | undefined): string {
344
+ if (!scope) {
345
+ return "no scope";
346
+ }
347
+
348
+ return colorText(`${scope.name}[${scope.id}]`, {
381
349
  fg: {
382
350
  r: 0,
383
351
  g: 150,
384
352
  b: 50,
385
353
  },
386
354
  });
355
+ }
387
356
 
388
- if (scope.owner) {
389
- text += untrack(() => ` of ${formatSymbolName(scope.owner!)}`);
390
- }
391
-
392
- return text;
357
+ export function formatSymbolTableName(table: SymbolTable): string {
358
+ // avoid instance of checks here in order to not create circular module imports.
359
+ const name =
360
+ "symbol" in table ?
361
+ formatSymbolName((table as OutputMemberSpace).symbol)
362
+ : formatScopeName((table as OutputDeclarationSpace).scope);
363
+ return colorText(`${name}:${table.key}`, {
364
+ fg: {
365
+ r: 0,
366
+ g: 125,
367
+ b: 25,
368
+ },
369
+ });
393
370
  }
394
371
 
395
372
  /**
@@ -397,7 +374,7 @@ export function formatScopeName(scope: OutputScope): string {
397
374
  * @param scope The scope to format
398
375
  * @returns A formatted string representation of the scope
399
376
  */
400
- export function formatScope(scope: OutputSymbol["scope"]): string {
377
+ export function formatScope(scope: OutputScope): string {
401
378
  if (!scope) {
402
379
  return "!Undefined scope!";
403
380
  }
@@ -416,34 +393,22 @@ export function formatScope(scope: OutputSymbol["scope"]): string {
416
393
  if (!scope.binder) {
417
394
  details.push(colorText(" !UNBOUND", { fg: { r: 255, g: 0, b: 0 } }));
418
395
  }
419
- // Show only enabled flags
420
- const flagsInfo = formatFlags(scope.flags, OutputScopeFlags);
421
-
422
- if (flagsInfo.length > 0) {
423
- details.push(` flags: ${flagsInfo.join(", ")}`);
424
- }
425
-
426
- // Show symbol count
427
- const symbolCount = scope.symbols.size;
428
- if (symbolCount > 0) {
429
- details.push(` symbols: ${symbolCount}`);
430
- }
431
396
 
432
397
  // Show parent scope if present
433
398
  if (scope.parent) {
434
399
  details.push(` parent: ${formatScopeName(scope.parent)}`);
435
400
  }
436
401
 
437
- // Show owner if present (for member scopes)
438
- if (scope.owner) {
439
- details.push(` owner: ${formatSymbolName(scope.owner)}`);
440
- }
441
-
442
402
  // Show child scopes if present
443
403
  if (scope.children && scope.children.size > 0) {
444
404
  details.push(` children: ${scope.children.size}`);
445
405
  }
446
406
 
407
+ // Show declaration spaces
408
+ for (const space of scope.spaces) {
409
+ details.push(` ${untrack(() => formatSpaceSymbols(space))}`);
410
+ }
411
+
447
412
  if (details.length > 0) {
448
413
  result += "\n" + details.join("\n");
449
414
  }
@@ -464,7 +429,12 @@ export function formatRefkeys(refkeys: Refkey[] | Refkey | undefined) {
464
429
  }
465
430
 
466
431
  function formatRefkey(refkey: Refkey): string {
467
- return colorText(`refkey[${refkey.key}]`, {
432
+ const text =
433
+ isMemberRefkey(refkey) ?
434
+ `memberRefkey[${formatRefkey(refkey.base)} -> ${formatRefkey(refkey.member)}]`
435
+ : `refkey[${refkey.key}]`;
436
+
437
+ return colorText(text, {
468
438
  fg: {
469
439
  r: 150,
470
440
  g: 0,
package/src/utils.tsx CHANGED
@@ -311,16 +311,16 @@ export interface OutputVisitor {
311
311
  * @param sourceDirectory - The root directory to traverse.
312
312
  * @param visitor - The visitor to call for each file and directory.
313
313
  */
314
- export function traverseOutput(
314
+ export async function traverseOutput(
315
315
  sourceDirectory: OutputDirectory,
316
316
  visitor: OutputVisitor,
317
317
  ) {
318
- visitor.visitDirectory(sourceDirectory);
318
+ await visitor.visitDirectory(sourceDirectory);
319
319
  for (const item of sourceDirectory.contents) {
320
320
  if (item.kind === "directory") {
321
- traverseOutput(item, visitor);
321
+ await traverseOutput(item, visitor);
322
322
  } else {
323
- visitor.visitFile(item);
323
+ await visitor.visitFile(item);
324
324
  }
325
325
  }
326
326
  }
@@ -10,54 +10,42 @@ export async function writeOutput(
10
10
  output: OutputDirectory,
11
11
  basePath: string = "",
12
12
  ) {
13
- const ops: Promise<void>[] = [];
14
-
15
- traverseOutput(output, {
16
- visitDirectory(directory) {
17
- ops.push(
18
- (async () => {
19
- const path = resolve(basePath, directory.path);
20
- if (await AlloyHost.exists(path)) {
21
- return;
22
- }
13
+ return await traverseOutput(output, {
14
+ async visitDirectory(directory) {
15
+ const path = resolve(basePath, directory.path);
16
+ if (await AlloyHost.exists(path)) {
17
+ return;
18
+ }
19
+ // eslint-disable-next-line no-console
20
+ console.log("create", relative(process.cwd(), path));
21
+ await AlloyHost.mkdir(path);
22
+ },
23
+ async visitFile(file) {
24
+ if ("contents" in file) {
25
+ const path = resolve(basePath, file.path);
26
+ if (await AlloyHost.exists(path)) {
27
+ // eslint-disable-next-line no-console
28
+ console.log("overwrite", relative(process.cwd(), path));
29
+ } else {
23
30
  // eslint-disable-next-line no-console
24
31
  console.log("create", relative(process.cwd(), path));
25
- await AlloyHost.mkdir(path);
26
- })(),
27
- );
28
- },
29
- visitFile(file) {
30
- ops.push(
31
- (async () => {
32
- if ("contents" in file) {
33
- const path = resolve(basePath, file.path);
34
- if (await AlloyHost.exists(path)) {
35
- // eslint-disable-next-line no-console
36
- console.log("overwrite", relative(process.cwd(), path));
37
- } else {
38
- // eslint-disable-next-line no-console
39
- console.log("create", relative(process.cwd(), path));
40
- }
32
+ }
41
33
 
42
- await AlloyHost.write(path, file.contents);
43
- } else {
44
- // copy file
45
- const source = resolve(basePath, file.sourcePath);
46
- const target = resolve(basePath, file.path);
47
- if (await AlloyHost.exists(target)) {
48
- // eslint-disable-next-line no-console
49
- console.log("copy over", relative(process.cwd(), target));
50
- } else {
51
- // eslint-disable-next-line no-console
52
- console.log("copy", relative(process.cwd(), target));
53
- }
54
- await AlloyHost.mkdir(dirname(target));
55
- await AlloyHost.write(target, AlloyHost.read(source).stream());
56
- }
57
- })(),
58
- );
34
+ await AlloyHost.write(path, file.contents);
35
+ } else {
36
+ // copy file
37
+ const source = resolve(basePath, file.sourcePath);
38
+ const target = resolve(basePath, file.path);
39
+ if (await AlloyHost.exists(target)) {
40
+ // eslint-disable-next-line no-console
41
+ console.log("copy over", relative(process.cwd(), target));
42
+ } else {
43
+ // eslint-disable-next-line no-console
44
+ console.log("copy", relative(process.cwd(), target));
45
+ }
46
+ await AlloyHost.mkdir(dirname(target));
47
+ await AlloyHost.write(target, AlloyHost.read(source).stream());
48
+ }
59
49
  },
60
50
  });
61
-
62
- return Promise.all(ops);
63
51
  }