Skip to content

Commit 63235c4

Browse files
stephentoubCopilot
andcommitted
Align rebased ref generator changes
Keep the rebased $ref generator follow-up aligned with the latest C# typing changes and clean up the Python/TypeScript generator adjustments. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 8a6a5bc commit 63235c4

2 files changed

Lines changed: 52 additions & 84 deletions

File tree

scripts/codegen/csharp.ts

Lines changed: 47 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
getSessionEventsSchemaPath,
1919
writeGeneratedFile,
2020
collectDefinitions,
21+
postProcessSchema,
2122
resolveRef,
2223
refTypeName,
2324
isRpcMethod,
@@ -512,18 +513,25 @@ function resolveSessionPropertyType(
512513
): string {
513514
// Handle $ref by resolving against schema definitions
514515
if (propSchema.$ref) {
515-
const typeName = refTypeName(propSchema.$ref);
516-
const className = typeToClassName(typeName);
517-
if (!nestedClasses.has(className)) {
518-
const refSchema = resolveRef(propSchema.$ref, sessionDefinitions);
519-
if (refSchema) {
520-
if (refSchema.enum && Array.isArray(refSchema.enum)) {
521-
return getOrCreateEnum(className, "", refSchema.enum as string[], enumOutput);
522-
}
516+
const className = typeToClassName(refTypeName(propSchema.$ref));
517+
const refSchema = resolveRef(propSchema.$ref, sessionDefinitions);
518+
if (!refSchema) {
519+
return isRequired ? className : `${className}?`;
520+
}
521+
522+
if (refSchema.enum && Array.isArray(refSchema.enum)) {
523+
const enumName = getOrCreateEnum(className, "", refSchema.enum as string[], enumOutput, refSchema.description);
524+
return isRequired ? enumName : `${enumName}?`;
525+
}
526+
527+
if (refSchema.type === "object" && refSchema.properties) {
528+
if (!nestedClasses.has(className)) {
523529
nestedClasses.set(className, generateNestedClass(className, refSchema, knownTypes, nestedClasses, enumOutput));
524530
}
531+
return isRequired ? className : `${className}?`;
525532
}
526-
return isRequired ? className : `${className}?`;
533+
534+
return resolveSessionPropertyType(refSchema, parentClassName, propName, isRequired, knownTypes, nestedClasses, enumOutput);
527535
}
528536
if (propSchema.anyOf) {
529537
const hasNull = propSchema.anyOf.some((s) => typeof s === "object" && (s as JSONSchema7).type === "null");
@@ -556,40 +564,15 @@ function resolveSessionPropertyType(
556564
}
557565
if (propSchema.type === "array" && propSchema.items) {
558566
const items = propSchema.items as JSONSchema7;
559-
// Handle $ref in array items
560-
if (items.$ref) {
561-
const typeName = refTypeName(items.$ref);
562-
const className = typeToClassName(typeName);
563-
if (!nestedClasses.has(className)) {
564-
const refSchema = resolveRef(items.$ref, sessionDefinitions);
565-
if (refSchema) {
566-
nestedClasses.set(className, generateNestedClass(className, refSchema, knownTypes, nestedClasses, enumOutput));
567-
}
568-
}
569-
return isRequired ? `${className}[]` : `${className}[]?`;
570-
}
571-
// Array of discriminated union (anyOf with shared discriminator)
572-
if (items.anyOf && Array.isArray(items.anyOf)) {
573-
const variants = items.anyOf.filter((v): v is JSONSchema7 => typeof v === "object");
574-
const discriminatorInfo = findDiscriminator(variants);
575-
if (discriminatorInfo) {
576-
const baseClassName = (items.title as string) ?? `${parentClassName}${propName}Item`;
577-
const renamedBase = applyTypeRename(baseClassName);
578-
const polymorphicCode = generatePolymorphicClasses(baseClassName, discriminatorInfo.property, variants, knownTypes, nestedClasses, enumOutput, items.description);
579-
nestedClasses.set(renamedBase, polymorphicCode);
580-
return isRequired ? `${renamedBase}[]` : `${renamedBase}[]?`;
581-
}
582-
}
583-
if (items.type === "object" && items.properties) {
584-
const itemClassName = (items.title as string) ?? `${parentClassName}${propName}Item`;
585-
nestedClasses.set(itemClassName, generateNestedClass(itemClassName, items, knownTypes, nestedClasses, enumOutput));
586-
return isRequired ? `${itemClassName}[]` : `${itemClassName}[]?`;
587-
}
588-
if (items.enum && Array.isArray(items.enum)) {
589-
const enumName = getOrCreateEnum(parentClassName, `${propName}Item`, items.enum as string[], enumOutput, items.description, items.title as string | undefined);
590-
return isRequired ? `${enumName}[]` : `${enumName}[]?`;
591-
}
592-
const itemType = schemaTypeToCSharp(items, true, knownTypes);
567+
const itemType = resolveSessionPropertyType(
568+
items,
569+
parentClassName,
570+
`${propName}Item`,
571+
true,
572+
knownTypes,
573+
nestedClasses,
574+
enumOutput
575+
);
593576
return isRequired ? `${itemType}[]` : `${itemType}[]?`;
594577
}
595578
return schemaTypeToCSharp(propSchema, isRequired, knownTypes);
@@ -725,7 +708,8 @@ export async function generateSessionEvents(schemaPath?: string): Promise<void>
725708
console.log("C#: generating session-events...");
726709
const resolvedPath = schemaPath ?? (await getSessionEventsSchemaPath());
727710
const schema = cloneSchemaForCodegen(JSON.parse(await fs.readFile(resolvedPath, "utf-8")) as JSONSchema7);
728-
const code = generateSessionEventsCode(schema);
711+
const processed = postProcessSchema(schema);
712+
const code = generateSessionEventsCode(processed);
729713
const outPath = await writeGeneratedFile("dotnet/src/Generated/SessionEvents.cs", code);
730714
console.log(` ✓ ${outPath}`);
731715
await formatCSharpFile(outPath);
@@ -774,13 +758,24 @@ function stableStringify(value: unknown): string {
774758
function resolveRpcType(schema: JSONSchema7, isRequired: boolean, parentClassName: string, propName: string, classes: string[]): string {
775759
// Handle $ref by resolving against schema definitions and generating the referenced class
776760
if (schema.$ref) {
777-
const typeName = refTypeName(schema.$ref);
761+
const typeName = typeToClassName(refTypeName(schema.$ref));
778762
const refSchema = resolveRef(schema.$ref, rpcDefinitions);
779-
if (refSchema && !emittedRpcClasses.has(typeName)) {
763+
if (!refSchema) {
764+
return isRequired ? typeName : `${typeName}?`;
765+
}
766+
767+
if (refSchema.enum && Array.isArray(refSchema.enum)) {
768+
const enumName = getOrCreateEnum(typeName, "", refSchema.enum as string[], rpcEnumOutput, refSchema.description);
769+
return isRequired ? enumName : `${enumName}?`;
770+
}
771+
772+
if (refSchema.type === "object" && refSchema.properties) {
780773
const cls = emitRpcClass(typeName, refSchema, "public", classes);
781774
if (cls) classes.push(cls);
775+
return isRequired ? typeName : `${typeName}?`;
782776
}
783-
return isRequired ? typeName : `${typeName}?`;
777+
778+
return resolveRpcType(refSchema, isRequired, parentClassName, propName, classes);
784779
}
785780
// Handle anyOf: [T, null] → T? (nullable typed property)
786781
if (schema.anyOf) {
@@ -809,43 +804,17 @@ function resolveRpcType(schema: JSONSchema7, isRequired: boolean, parentClassNam
809804
}
810805
if (schema.type === "array" && schema.items) {
811806
const items = schema.items as JSONSchema7;
812-
// Handle $ref in array items
813-
if (items.$ref) {
814-
const typeName = refTypeName(items.$ref);
815-
const refSchema = resolveRef(items.$ref, rpcDefinitions);
816-
if (refSchema && !emittedRpcClasses.has(typeName)) {
817-
const cls = emitRpcClass(typeName, refSchema, "public", classes);
818-
if (cls) classes.push(cls);
819-
}
820-
return isRequired ? `List<${typeName}>` : `List<${typeName}>?`;
821-
}
822807
if (items.type === "object" && items.properties) {
823808
const itemClass = (items.title as string) ?? singularPascal(propName);
824809
classes.push(emitRpcClass(itemClass, items, "public", classes));
825810
return isRequired ? `IList<${itemClass}>` : `IList<${itemClass}>?`;
826811
}
827-
if (items.enum && Array.isArray(items.enum)) {
828-
const itemEnum = getOrCreateEnum(
829-
parentClassName,
830-
`${propName}Item`,
831-
items.enum as string[],
832-
rpcEnumOutput,
833-
items.description,
834-
items.title as string | undefined,
835-
);
836-
return isRequired ? `IList<${itemEnum}>` : `IList<${itemEnum}>?`;
837-
}
838-
const itemType = schemaTypeToCSharp(items, true, rpcKnownTypes);
812+
const itemType = resolveRpcType(items, true, parentClassName, `${propName}Item`, classes);
839813
return isRequired ? `IList<${itemType}>` : `IList<${itemType}>?`;
840814
}
841815
if (schema.type === "object" && schema.additionalProperties && typeof schema.additionalProperties === "object") {
842816
const vs = schema.additionalProperties as JSONSchema7;
843-
if (vs.type === "object" && vs.properties) {
844-
const valClass = (vs.title as string) ?? `${parentClassName}${propName}Value`;
845-
classes.push(emitRpcClass(valClass, vs, "public", classes));
846-
return isRequired ? `IDictionary<string, ${valClass}>` : `IDictionary<string, ${valClass}>?`;
847-
}
848-
const valueType = schemaTypeToCSharp(vs, true, rpcKnownTypes);
817+
const valueType = resolveRpcType(vs, true, parentClassName, `${propName}Value`, classes);
849818
return isRequired ? `IDictionary<string, ${valueType}>` : `IDictionary<string, ${valueType}>?`;
850819
}
851820
return schemaTypeToCSharp(schema, isRequired, rpcKnownTypes);
@@ -1045,15 +1014,9 @@ function emitServerInstanceMethod(
10451014
if (typeof pSchema !== "object") continue;
10461015
const isReq = requiredSet.has(pName);
10471016
const jsonSchema = pSchema as JSONSchema7;
1048-
let csType: string;
1049-
// If the property has an enum, resolve to the generated enum type by title
1050-
if (jsonSchema.enum && Array.isArray(jsonSchema.enum) && requestClassName) {
1051-
const enumTitle = (jsonSchema.title as string) ?? `${requestClassName}${toPascalCase(pName)}`;
1052-
const match = generatedEnums.get(enumTitle);
1053-
csType = match ? (isReq ? match.enumName : `${match.enumName}?`) : schemaTypeToCSharp(jsonSchema, isReq, rpcKnownTypes);
1054-
} else {
1055-
csType = schemaTypeToCSharp(jsonSchema, isReq, rpcKnownTypes);
1056-
}
1017+
const csType = requestClassName
1018+
? resolveRpcType(jsonSchema, isReq, requestClassName, toPascalCase(pName), classes)
1019+
: schemaTypeToCSharp(jsonSchema, isReq, rpcKnownTypes);
10571020
sigParams.push(`${csType} ${pName}${isReq ? "" : " = null"}`);
10581021
bodyAssignments.push(`${toPascalCase(pName)} = ${pName}`);
10591022
}

scripts/codegen/typescript.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,12 @@ import type { MessageConnection } from "vscode-jsonrpc/node.js";
213213

214214
// Strip the placeholder root type and keep only the definition-generated types
215215
const strippedTs = compiled
216+
.replace(
217+
/\/\*\*\n \* This (?:interface|type) was referenced by `_RpcSchemaRoot`'s JSON-Schema\n \* via the `definition` "[^"]+"\.\n \*\/\n/g,
218+
"\n"
219+
)
216220
.replace(/export interface _RpcSchemaRoot\s*\{[^}]*\}\s*/g, "")
221+
.replace(/export type _RpcSchemaRoot = [^;]+;\s*/g, "")
217222
.trim();
218223

219224
if (strippedTs) {

0 commit comments

Comments
 (0)