@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 +5 -5
- package/src/module-generator.ts +213 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ts-for-gir/generator-typescript",
|
|
3
|
-
"version": "4.0.0-beta.
|
|
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.
|
|
41
|
-
"@ts-for-gir/generator-base": "^4.0.0-beta.
|
|
42
|
-
"@ts-for-gir/lib": "^4.0.0-beta.
|
|
43
|
-
"@ts-for-gir/templates": "^4.0.0-beta.
|
|
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
|
}
|
package/src/module-generator.ts
CHANGED
|
@@ -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
|
-
|
|
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) {
|