@ts-for-gir/generator-typescript 4.0.0-beta.34 → 4.0.0-beta.36

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ts-for-gir/generator-typescript",
3
- "version": "4.0.0-beta.34",
3
+ "version": "4.0.0-beta.36",
4
4
  "description": "TypeScript type definition generator for ts-for-gir",
5
5
  "main": "src/index.ts",
6
6
  "module": "src/index.ts",
@@ -37,10 +37,10 @@
37
37
  "typescript": "^5.9.2"
38
38
  },
39
39
  "dependencies": {
40
- "@gi.ts/parser": "^4.0.0-beta.34",
41
- "@ts-for-gir/generator-base": "^4.0.0-beta.34",
42
- "@ts-for-gir/lib": "^4.0.0-beta.34",
43
- "@ts-for-gir/templates": "^4.0.0-beta.34",
40
+ "@gi.ts/parser": "^4.0.0-beta.36",
41
+ "@ts-for-gir/generator-base": "^4.0.0-beta.36",
42
+ "@ts-for-gir/lib": "^4.0.0-beta.36",
43
+ "@ts-for-gir/templates": "^4.0.0-beta.36",
44
44
  "ejs": "^3.1.10",
45
45
  "xml2js": "^0.6.2"
46
46
  }
@@ -17,6 +17,7 @@ import {
17
17
  type GirModule,
18
18
  generateIndent,
19
19
  generateMemberName,
20
+ hasVfuncSignatureConflicts,
20
21
  IntrospectedAlias,
21
22
  type IntrospectedBaseClass,
22
23
  IntrospectedCallback,
@@ -229,8 +230,9 @@ export class ModuleGenerator extends FormatGenerator<string[]> {
229
230
  const isGObject = node.someParent((p) => p.namespace.namespace === "GObject" && p.name === "Object");
230
231
  const functions = filterFunctionConflict(node.namespace, node, node.members, []);
231
232
  const hasStaticFunctions = functions.some((f) => f instanceof IntrospectedStaticClassFunction);
233
+ const hasVirtualMethods = node.members.some((m) => m instanceof IntrospectedVirtualClassFunction);
232
234
 
233
- const hasNamespace = isGObject || hasStaticFunctions || node.callbacks.length > 0;
235
+ const hasNamespace = isGObject || hasStaticFunctions || node.callbacks.length > 0 || hasVirtualMethods;
234
236
 
235
237
  return [
236
238
  ...this.generateClassNamespaces(node),
@@ -1176,6 +1178,97 @@ export class ModuleGenerator extends FormatGenerator<string[]> {
1176
1178
  return def;
1177
1179
  }
1178
1180
 
1181
+ /**
1182
+ * Generate virtual methods with overloads for interfaces that have conflicting signatures.
1183
+ * This is used when an interface can't inherit from Interface namespace due to signature conflicts.
1184
+ * @param girInterface The interface to generate virtual methods for
1185
+ * @param indentCount Indentation level
1186
+ */
1187
+ generateVirtualMethodOverloads(girInterface: IntrospectedInterface, indentCount = 1): string[] {
1188
+ const def: string[] = [];
1189
+ const indent = generateIndent(indentCount);
1190
+
1191
+ // Get all virtual methods from this interface
1192
+ const virtualMethods = girInterface.members.filter(
1193
+ (m) => m instanceof IntrospectedVirtualClassFunction,
1194
+ ) as IntrospectedVirtualClassFunction[];
1195
+
1196
+ if (virtualMethods.length === 0) {
1197
+ return def;
1198
+ }
1199
+
1200
+ def.push("");
1201
+ def.push(`${indent}// Virtual methods - generated with overloads due to conflicts`);
1202
+ def.push("");
1203
+
1204
+ // Group virtual methods by name to handle overloads
1205
+ const methodsByName = new Map<string, IntrospectedVirtualClassFunction[]>();
1206
+ for (const vmethod of virtualMethods) {
1207
+ const methods = methodsByName.get(vmethod.name) || [];
1208
+ methods.push(vmethod);
1209
+ methodsByName.set(vmethod.name, methods);
1210
+ }
1211
+
1212
+ // For each method name, generate overloads
1213
+ for (const [methodName, methods] of methodsByName) {
1214
+ // Find parent methods with the same name
1215
+ const parentMethods: IntrospectedVirtualClassFunction[] = [];
1216
+
1217
+ girInterface.someParent((parent) => {
1218
+ const parentVirtualMethods = parent.members.filter(
1219
+ (m) => m instanceof IntrospectedVirtualClassFunction && m.name === methodName,
1220
+ ) as IntrospectedVirtualClassFunction[];
1221
+ parentMethods.push(...parentVirtualMethods);
1222
+ return false; // Continue searching all parents
1223
+ });
1224
+
1225
+ // Generate overloads for all signatures
1226
+ const allMethods = [...methods, ...parentMethods];
1227
+ const uniqueSignatures = new Map<string, IntrospectedVirtualClassFunction>();
1228
+
1229
+ // Deduplicate by signature
1230
+ for (const method of allMethods) {
1231
+ const signature = this.generateMethodSignature(method);
1232
+ if (!uniqueSignatures.has(signature)) {
1233
+ uniqueSignatures.set(signature, method);
1234
+ }
1235
+ }
1236
+
1237
+ // Generate all unique overloads
1238
+ for (const method of uniqueSignatures.values()) {
1239
+ const methodDef = method.asString(this);
1240
+ // Add @ignore tag to hide from documentation
1241
+ if (methodDef.length > 0 && !methodDef[0].includes("@ignore")) {
1242
+ const docLines: string[] = [];
1243
+ if (method.doc) {
1244
+ docLines.push(...this.addGirDocComment(method.doc, [], indentCount));
1245
+ }
1246
+ // Add @ignore tag
1247
+ if (docLines.length > 0) {
1248
+ // Insert @ignore before the closing */
1249
+ const lastLine = docLines[docLines.length - 1];
1250
+ docLines[docLines.length - 1] = lastLine.replace(" */", ` * @ignore\n${indent} */`);
1251
+ } else {
1252
+ docLines.push(`${indent}/** @ignore */`);
1253
+ }
1254
+ def.push(...docLines);
1255
+ }
1256
+ def.push(...methodDef);
1257
+ }
1258
+ }
1259
+
1260
+ return def;
1261
+ }
1262
+
1263
+ /**
1264
+ * Generate a signature string for a virtual method (used for deduplication)
1265
+ */
1266
+ private generateMethodSignature(method: IntrospectedVirtualClassFunction): string {
1267
+ const params = method.parameters.map((p) => `${p.name}:${p.type.print(this.namespace, this.config)}`).join(",");
1268
+ const returnType = method.return().print(this.namespace, this.config);
1269
+ return `${method.name}(${params}):${returnType}`;
1270
+ }
1271
+
1179
1272
  generateClassSignalInterfaces(girClass: IntrospectedClass, indentCount = 0) {
1180
1273
  const def: string[] = [];
1181
1274
  const _tsSignals = girClass.signals;
@@ -1402,6 +1495,11 @@ export class ModuleGenerator extends FormatGenerator<string[]> {
1402
1495
  bodyDef.push(...this.generateClassSignalInterfaces(girClass, indentCount + 1));
1403
1496
  }
1404
1497
 
1498
+ if (girClass instanceof IntrospectedInterface) {
1499
+ // Virtual interface for implementation
1500
+ bodyDef.push(...this.generateVirtualInterface(girClass, indentCount + 1));
1501
+ }
1502
+
1405
1503
  bodyDef.push(...this.generateClassCallbacks(girClass));
1406
1504
 
1407
1505
  // Properties interface for construction
@@ -1448,6 +1546,35 @@ export class ModuleGenerator extends FormatGenerator<string[]> {
1448
1546
  ? resolution.implements().map((i) => i.node.getType().print(this.namespace, this.config))
1449
1547
  : []),
1450
1548
  ];
1549
+
1550
+ // For interfaces: check if we should inherit from Interface namespace or generate method overloads
1551
+ let shouldGenerateVirtualMethodOverloads = false;
1552
+ if (girClass instanceof IntrospectedInterface) {
1553
+ // Check if this interface has virtual methods
1554
+ const hasVirtualMethods = girClass.members.some((m) => m instanceof IntrospectedVirtualClassFunction);
1555
+
1556
+ if (hasVirtualMethods) {
1557
+ // Check if there are conflicts with parent virtual methods
1558
+ const hasConflicts = hasVfuncSignatureConflicts(this.namespace, girClass);
1559
+
1560
+ if (hasConflicts) {
1561
+ // Don't inherit from Interface namespace if there are conflicts
1562
+ // We'll generate method overloads instead
1563
+ shouldGenerateVirtualMethodOverloads = true;
1564
+ } else {
1565
+ // No conflicts, inherit from Interface namespace as usual
1566
+ // Extract only the generic type names (e.g., "A", "B") from the generic definitions
1567
+ const typeNames = girClass.generics
1568
+ .map((g) => g.type.identifier) // Use g.type.identifier to get the generic name
1569
+ .filter((name) => name && name.length > 0);
1570
+
1571
+ const genericTypeNames = typeNames.length > 0 ? `<${typeNames.join(", ")}>` : "";
1572
+
1573
+ implementationNames.push(`${girClass.name}.Interface${genericTypeNames}`);
1574
+ }
1575
+ }
1576
+ }
1577
+
1451
1578
  const ext = implementationNames.length ? ` extends ${implementationNames.join(", ")}` : "";
1452
1579
  const interfaceHead = `${girClass.name}${genericParameters}${ext}`;
1453
1580
  def.push(this.generateExport("interface", interfaceHead, "{"));
@@ -1464,8 +1591,16 @@ export class ModuleGenerator extends FormatGenerator<string[]> {
1464
1591
  // Methods
1465
1592
  def.push(...this.generateClassMethods(girClass));
1466
1593
 
1467
- // Virtual methods
1468
- def.push(...this.generateClassVirtualMethods(girClass));
1594
+ // Virtual methods - generate for classes/records always, for interfaces only when there are conflicts
1595
+ if (!(girClass instanceof IntrospectedInterface) || shouldGenerateVirtualMethodOverloads) {
1596
+ if (shouldGenerateVirtualMethodOverloads && girClass instanceof IntrospectedInterface) {
1597
+ // Generate virtual methods with overloads for conflicting signatures
1598
+ def.push(...this.generateVirtualMethodOverloads(girClass));
1599
+ } else {
1600
+ // Generate normal virtual methods
1601
+ def.push(...this.generateClassVirtualMethods(girClass));
1602
+ }
1603
+ }
1469
1604
  // END BODY
1470
1605
 
1471
1606
  // END INTERFACE
@@ -1475,6 +1610,81 @@ export class ModuleGenerator extends FormatGenerator<string[]> {
1475
1610
  return def;
1476
1611
  }
1477
1612
 
1613
+ /**
1614
+ * Generates a virtual-methods-only interface for proper GObject interface implementation.
1615
+ * This interface contains only the virtual methods (vfunc_*) that need to be implemented
1616
+ * when creating a class that implements a GObject interface.
1617
+ */
1618
+ generateVirtualInterface(girClass: IntrospectedInterface, indentCount = 1): string[] {
1619
+ const def: string[] = [];
1620
+ if (!girClass) return def;
1621
+
1622
+ const indent = generateIndent(indentCount);
1623
+
1624
+ // Get only virtual methods from this interface
1625
+ const virtualMethods = girClass.members.filter(
1626
+ (m) => m instanceof IntrospectedVirtualClassFunction,
1627
+ ) as IntrospectedVirtualClassFunction[];
1628
+
1629
+ // Don't generate an Interface if there are no virtual methods
1630
+ if (virtualMethods.length === 0) {
1631
+ return def;
1632
+ }
1633
+
1634
+ // Build inheritance chain for virtual interface
1635
+ const resolution = girClass.resolveParents();
1636
+ const parentInterfaces: string[] = [];
1637
+
1638
+ // Inherit from parent interface's Interface if it exists
1639
+ const parentResolution = resolution.extends();
1640
+ if (parentResolution && parentResolution.node instanceof IntrospectedInterface) {
1641
+ const parentInterface = parentResolution.node as IntrospectedInterface;
1642
+ const parentTypeIdentifier = parentResolution.identifier
1643
+ .resolveIdentifier(this.namespace, this.config)
1644
+ ?.print(this.namespace, this.config);
1645
+
1646
+ // Check if parent has virtual methods to avoid empty inheritance
1647
+ const parentHasVirtualMethods = parentInterface.members.some(
1648
+ (m) => m instanceof IntrospectedVirtualClassFunction,
1649
+ );
1650
+
1651
+ if (parentTypeIdentifier && parentHasVirtualMethods) {
1652
+ parentInterfaces.push(`${parentTypeIdentifier}.Interface`);
1653
+ }
1654
+ }
1655
+
1656
+ // Apply inheritance or fallback to base interface
1657
+ let extendsClause = "";
1658
+ if (parentInterfaces.length > 0) {
1659
+ extendsClause = ` extends ${parentInterfaces.join(", ")}`;
1660
+ }
1661
+ // No default inheritance for virtual interfaces to avoid non-existent types
1662
+
1663
+ // Generate the Interface interface with generic parameters
1664
+ const genericParameters = this.generateGenericParameters(girClass.generics);
1665
+ def.push(`${indent}/**`);
1666
+ def.push(`${indent} * Interface for implementing ${girClass.name}.`);
1667
+ def.push(`${indent} * Contains only the virtual methods that need to be implemented.`);
1668
+ def.push(`${indent} */`);
1669
+ def.push(`${indent}interface Interface${genericParameters}${extendsClause} {`);
1670
+
1671
+ // Generate virtual methods
1672
+ if (virtualMethods.length > 0) {
1673
+ def.push(
1674
+ ...this.generateFunctions(
1675
+ filterFunctionConflict(girClass.namespace, girClass, virtualMethods, []),
1676
+ indentCount + 1,
1677
+ "Virtual methods",
1678
+ ),
1679
+ );
1680
+ }
1681
+
1682
+ def.push(`${indent}}`);
1683
+ def.push("");
1684
+
1685
+ return def;
1686
+ }
1687
+
1478
1688
  protected extends(node: IntrospectedBaseClass) {
1479
1689
  const { namespace: ns, options } = this;
1480
1690
  if (node.superType) {