@gi-tcg/gts-transpiler 0.4.4 → 0.4.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/dist/index.js CHANGED
@@ -1293,7 +1293,7 @@ function applyReplacements(state, code, mappings) {
1293
1293
  ? ${payload.attrName} /* have duplicate, disable this */
1294
1294
  : never
1295
1295
  );
1296
- let ${payload.lhs}!: { ${Meta}: ${payload.metaType} } & Omit<${payload.defType}, ${omittedKeys}>;
1296
+ let ${payload.lhs}!: ${payload.hintOnly ? `{}` : `{ ${Meta}: ${payload.metaType} }`} & Omit<${payload.defType}, ${omittedKeys}>;
1297
1297
  `;
1298
1298
  } else if (payload.type === "createBindingTyping") {
1299
1299
  const typingIdLhs = `${payload.typingId}_lhs`;
@@ -1328,6 +1328,7 @@ function applyReplacements(state, code, mappings) {
1328
1328
  //#endregion
1329
1329
  //#region src/transform/volar/walker.ts
1330
1330
  const EMPTY = { type: "EmptyStatement" };
1331
+ const ATTR_HINT_ATTR_NAME = JSON.stringify("~attrNameHint");
1331
1332
  const enterVMFromRoot = (state) => {
1332
1333
  let defTypeId = {
1333
1334
  type: "Identifier",
@@ -1407,7 +1408,8 @@ const enterAttr = (state, attrName) => {
1407
1408
  defType: defTypeId.name,
1408
1409
  metaType: metaTypeId.name,
1409
1410
  lhs: lhsId.name,
1410
- attrName
1411
+ attrName,
1412
+ hintOnly: attrName === ATTR_HINT_ATTR_NAME
1411
1413
  }));
1412
1414
  return { lhsId };
1413
1415
  };
@@ -1439,6 +1441,19 @@ const exitAttr = (state, returningId) => {
1439
1441
  returnType: returningId.name
1440
1442
  }));
1441
1443
  };
1444
+ const insertHintStatement = (state, whiteSpaceStart, whiteSpaceEnd) => {
1445
+ const { lhsId } = enterAttr(state, ATTR_HINT_ATTR_NAME);
1446
+ state.typingPendingStatements.push({
1447
+ type: "GTSAttributeNameHintStatement",
1448
+ object: lhsId,
1449
+ whiteSpaceStart,
1450
+ whiteSpaceEnd
1451
+ });
1452
+ exitAttr(state, {
1453
+ type: "Identifier",
1454
+ name: `__gts_attrRet_hint_${state.idCounter++}`
1455
+ });
1456
+ };
1442
1457
  const gtsToTypingsWalker = {
1443
1458
  Program(node, { state, visit }) {
1444
1459
  const body = [];
@@ -1638,7 +1653,17 @@ const gtsToTypingsWalker = {
1638
1653
  return EMPTY;
1639
1654
  },
1640
1655
  GTSNamedAttributeBlock(node, { state, visit }) {
1641
- for (const attr of node.attributes) visit(attr);
1656
+ const attributeListEnd = node.directAction?.range?.[0] ?? node.range?.[1] ?? -1;
1657
+ const attributeListStart = node.attributes[0]?.range?.[0] ?? attributeListEnd;
1658
+ if (node.range && attributeListStart > node.range[0] + 1) insertHintStatement(state, node.range[0] + 1, attributeListStart - 1);
1659
+ for (let i = 0; i < node.attributes.length; i++) {
1660
+ const attribute = node.attributes[i];
1661
+ visit(attribute);
1662
+ let nextTokenStart;
1663
+ if (i < node.attributes.length - 1) nextTokenStart = node.attributes[i + 1].range?.[0] ?? -1;
1664
+ else nextTokenStart = attributeListEnd;
1665
+ if (attribute.range && nextTokenStart > attribute.range[1]) insertHintStatement(state, attribute.range[1], nextTokenStart - 1);
1666
+ }
1642
1667
  if (node.directAction) {
1643
1668
  const stubStatement = {
1644
1669
  type: "ExpressionStatement",
@@ -1695,6 +1720,7 @@ const gtsToTypingsWalker = {
1695
1720
  }
1696
1721
  }]
1697
1722
  });
1723
+ exitAttr(state, returnValue);
1698
1724
  }
1699
1725
  return EMPTY;
1700
1726
  },
@@ -1735,6 +1761,7 @@ function getPrintOptions(source, state) {
1735
1761
  if (state.attributeNameNodes.has(node)) return false;
1736
1762
  return state.sourceNodes.has(node);
1737
1763
  },
1764
+ printCommentsOnUntouchedNodes: true,
1738
1765
  getLeadingComments: (node) => node.leadingComments,
1739
1766
  getTrailingComments: (node) => node.trailingComments,
1740
1767
  getMappingData: () => DEFAULT_VOLAR_MAPPING_DATA,
@@ -1804,6 +1831,12 @@ function getPrintOptions(source, state) {
1804
1831
  end: state.contentStartOffset + 1
1805
1832
  }, context.generatedOffset, context.generatedOffset + 1, DEFAULT_VOLAR_MAPPING_DATA);
1806
1833
  }
1834
+ },
1835
+ GTSAttributeNameHintStatement(node, context) {
1836
+ context.writeNode(node.object);
1837
+ context.write(".");
1838
+ context.writeSource(node.whiteSpaceStart, node.whiteSpaceEnd);
1839
+ context.write(";");
1807
1840
  }
1808
1841
  },
1809
1842
  experimentalGetLeftParenSourceRange: (node) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gi-tcg/gts-transpiler",
3
- "version": "0.4.4",
3
+ "version": "0.4.5",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/piovium/gts.git"
@@ -22,7 +22,7 @@
22
22
  "@sveltejs/acorn-typescript": "^1.0.8",
23
23
  "acorn": "^8.15.0",
24
24
  "dedent": "^1.7.2",
25
- "espolar": "^0.6.0",
25
+ "espolar": "^0.6.1",
26
26
  "esrap": "2.2.1",
27
27
  "magic-string": "^0.30.21",
28
28
  "path-browserify-esm": "^1.0.6",
@@ -10,6 +10,7 @@ import {
10
10
  type AST as EspolarAST,
11
11
  defaultPrinters,
12
12
  type SourceRange,
13
+ type PrinterContext,
13
14
  } from "espolar";
14
15
  import type { CodeInformation } from "@volar/language-core";
15
16
  import {
@@ -19,7 +20,7 @@ import {
19
20
  LITERAL_FROM_ID_MAPPING_DATA,
20
21
  VERIFICATION_ONLY_MAPPING_DATA,
21
22
  } from "./mappings.ts";
22
- import type { TypingTranspileState } from "./walker.ts";
23
+ import type { TypingTranspileState, GTSAttributeNameHintStatement } from "./walker.ts";
23
24
 
24
25
  export function getPrintOptions(
25
26
  source: string,
@@ -36,6 +37,7 @@ export function getPrintOptions(
36
37
  }
37
38
  return state.sourceNodes.has(node as Node);
38
39
  },
40
+ printCommentsOnUntouchedNodes: true,
39
41
  getLeadingComments: (node) => (node as Node).leadingComments,
40
42
  getTrailingComments: (node) => (node as Node).trailingComments,
41
43
  getMappingData: () => DEFAULT_VOLAR_MAPPING_DATA,
@@ -186,6 +188,14 @@ export function getPrintOptions(
186
188
  );
187
189
  }
188
190
  },
191
+ // @ts-expect-error This is a custom node type that don't have typing.
192
+ // @see `GTSAttributeNameHintStatement`
193
+ GTSAttributeNameHintStatement(node: GTSAttributeNameHintStatement, context: PrinterContext<CodeInformation>) {
194
+ context.writeNode(node.object as EspolarAST.Node);
195
+ context.write(".");
196
+ context.writeSource(node.whiteSpaceStart, node.whiteSpaceEnd);
197
+ context.write(";");
198
+ }
189
199
  },
190
200
  // Enable triggering signature completion
191
201
  experimentalGetLeftParenSourceRange: (node) => {
@@ -38,6 +38,7 @@ type ReplacementPayload =
38
38
  metaType: string;
39
39
  lhs: string;
40
40
  attrName: string;
41
+ hintOnly: boolean;
41
42
  }
42
43
  | {
43
44
  type: "createBindingTyping";
@@ -179,7 +180,7 @@ export function applyReplacements(
179
180
  ? ${payload.attrName} /* have duplicate, disable this */
180
181
  : never
181
182
  );
182
- let ${payload.lhs}!: { ${Meta}: ${payload.metaType} } & Omit<${payload.defType}, ${omittedKeys}>;
183
+ let ${payload.lhs}!: ${payload.hintOnly ? `{}` : `{ ${Meta}: ${payload.metaType} }`} & Omit<${payload.defType}, ${omittedKeys}>;
183
184
  `;
184
185
  } else if (payload.type === "createBindingTyping") {
185
186
  const typingIdLhs = `${payload.typingId}_lhs`;
@@ -92,7 +92,25 @@ export interface TypingTranspileState extends TranspileState {
92
92
  contentStartOffset: number;
93
93
  }
94
94
 
95
+ /**
96
+ * Map whitespaces inside named attribute blocks to include a "NameHintStatement", which printed
97
+ * as following:
98
+ * ```ts
99
+ * __gts_attr_obj. ;
100
+ * // ^~~~~~ these whitespaces are mapped from source
101
+ * ```
102
+ * so that when user press Ctrl+Space inside whitespace characters, they can get hint of available
103
+ * attribute names inside this block.
104
+ */
105
+ export interface GTSAttributeNameHintStatement {
106
+ type: "GTSAttributeNameHintStatement";
107
+ object: Identifier;
108
+ whiteSpaceStart: number;
109
+ whiteSpaceEnd: number;
110
+ }
111
+
95
112
  const EMPTY: EmptyStatement = { type: "EmptyStatement" };
113
+ const ATTR_HINT_ATTR_NAME = JSON.stringify("~attrNameHint");
96
114
 
97
115
  const enterVMFromRoot = (state: TypingTranspileState) => {
98
116
  let defTypeId: Identifier = {
@@ -174,7 +192,7 @@ const enterAttr = (
174
192
  const defTypeId = state.vmDefTypeIdStack.at(-1);
175
193
  const metaTypeId = state.metaTypeIdStack.at(-1);
176
194
  if (!defTypeId || !metaTypeId) {
177
- // TODO error handling?
195
+ // FIXME error handling?
178
196
  return { lhsId: { type: "Identifier", name: "__gts_invalid_attr_obj" } };
179
197
  }
180
198
  state.attrsOfCurrentVm.at(-1)!.push(attrName);
@@ -189,6 +207,7 @@ const enterAttr = (
189
207
  metaType: metaTypeId.name,
190
208
  lhs: lhsId.name,
191
209
  attrName,
210
+ hintOnly: attrName === ATTR_HINT_ATTR_NAME,
192
211
  }),
193
212
  );
194
213
  return { lhsId: lhsId };
@@ -238,6 +257,25 @@ const exitAttr = (state: TypingTranspileState, returningId: Identifier) => {
238
257
  );
239
258
  };
240
259
 
260
+ const insertHintStatement = (
261
+ state: TypingTranspileState,
262
+ whiteSpaceStart: number,
263
+ whiteSpaceEnd: number,
264
+ ) => {
265
+ const { lhsId } = enterAttr(state, ATTR_HINT_ATTR_NAME);
266
+ state.typingPendingStatements.push({
267
+ type: "GTSAttributeNameHintStatement",
268
+ object: lhsId,
269
+ whiteSpaceStart,
270
+ whiteSpaceEnd,
271
+ } satisfies GTSAttributeNameHintStatement as any);
272
+ const returnValue: Identifier = {
273
+ type: "Identifier",
274
+ name: `__gts_attrRet_hint_${state.idCounter++}`,
275
+ };
276
+ exitAttr(state, returnValue);
277
+ };
278
+
241
279
  export const gtsToTypingsWalker: Visitors<Node, TypingTranspileState> = {
242
280
  Program(node, { state, visit }) {
243
281
  const body: Program["body"] = [];
@@ -471,8 +509,35 @@ export const gtsToTypingsWalker: Visitors<Node, TypingTranspileState> = {
471
509
  return EMPTY;
472
510
  },
473
511
  GTSNamedAttributeBlock(node, { state, visit }) {
474
- for (const attr of node.attributes) {
475
- visit(attr);
512
+ // Insert hint statement around each attribute name:
513
+ // ```
514
+ // define foo {
515
+ // // (1) hidden hint
516
+ // bar 1; // (2a) hidden hint
517
+ // baz 2; // (2b) hidden hint
518
+ // }
519
+ // ```
520
+ const attributeListEnd =
521
+ node.directAction?.range?.[0] ?? node.range?.[1] ?? -1;
522
+ const attributeListStart =
523
+ node.attributes[0]?.range?.[0] ?? attributeListEnd;
524
+ // (1)
525
+ if (node.range && attributeListStart > node.range[0] + 1) {
526
+ insertHintStatement(state, node.range[0] + 1, attributeListStart - 1);
527
+ }
528
+ for (let i = 0; i < node.attributes.length; i++) {
529
+ const attribute = node.attributes[i];
530
+ visit(attribute);
531
+ let nextTokenStart: number;
532
+ if (i < node.attributes.length - 1) {
533
+ nextTokenStart = node.attributes[i + 1].range?.[0] ?? -1;
534
+ } else {
535
+ nextTokenStart = attributeListEnd;
536
+ }
537
+ // (2)
538
+ if (attribute.range && nextTokenStart > attribute.range[1]) {
539
+ insertHintStatement(state, attribute.range[1], nextTokenStart - 1);
540
+ }
476
541
  }
477
542
  if (node.directAction) {
478
543
  const stubStatement: ExpressionStatement = {
@@ -529,6 +594,7 @@ export const gtsToTypingsWalker: Visitors<Node, TypingTranspileState> = {
529
594
  },
530
595
  ],
531
596
  });
597
+ exitAttr(state, returnValue);
532
598
  }
533
599
  return EMPTY;
534
600
  },