Skip to content

Commit 78d5ff2

Browse files
Fix mojibake in csharp.ts and factor [JsonInclude] helper
Commit b484198 introduced 590 UTF-8 double-encoded byte sequences in scripts/codegen/csharp.ts (box-drawing characters in section banners and em-dashes in comments). This commit restores the file to a clean UTF-8 encoding and reapplies the intended logic change (removing the unnecessary !propInternal && !propExperimental guards from the reqMod expressions in the four sites that had it). While here, factor the duplicated propInternal + [JsonInclude] + visibility pattern into a small pushCSharpInternalAttribute helper used by all six property-emission sites in this file, and delete the dead propExperimental declarations that were left over from the original revert hack. Generated output (SessionEvents.cs, Rpc.cs) is byte-identical to before this commit; the change is purely TypeScript-side cleanup. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 02d4f58 commit 78d5ff2

1 file changed

Lines changed: 37 additions & 39 deletions

File tree

scripts/codegen/csharp.ts

Lines changed: 37 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/*---------------------------------------------------------------------------------------------
1+
/*---------------------------------------------------------------------------------------------
22
* Copyright (c) Microsoft Corporation. All rights reserved.
33
*--------------------------------------------------------------------------------------------*/
44

@@ -54,9 +54,9 @@ import {
5454

5555
const execFileAsync = promisify(execFile);
5656

57-
// ── C# type rename overrides ────────────────────────────────────────────────
57+
// ── C# type rename overrides ────────────────────────────────────────────────
5858
// Map generated class names to shorter public-facing names.
59-
// Applied to base classes AND their derived variants (e.g., FooBar → Bar, FooBazShell → BarShell).
59+
// Applied to base classes AND their derived variants (e.g., FooBar Bar, FooBazShell BarShell).
6060
const TYPE_RENAMES: Record<string, string> = {
6161
PermissionRequestedDataPermissionRequest: "PermissionRequest",
6262
};
@@ -72,7 +72,7 @@ function applyTypeRename(className: string): string {
7272
return className;
7373
}
7474

75-
// ── C# utilities ────────────────────────────────────────────────────────────
75+
// ── C# utilities ────────────────────────────────────────────────────────────
7676

7777
function escapeXml(text: string): string {
7878
return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
@@ -177,7 +177,7 @@ function pushRpcMethodXmlDocs(
177177
const CANCELLATION_TOKEN_DESCRIPTION =
178178
'The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.';
179179

180-
/** Like xmlDocComment but skips XML escaping — use only for codegen-controlled strings that already contain valid XML tags. */
180+
/** Like xmlDocComment but skips XML escaping use only for codegen-controlled strings that already contain valid XML tags. */
181181
function rawXmlDocSummary(text: string, indent: string): string[] {
182182
const line = ensureTrailingPunctuation(text.trim());
183183
return [`${indent}/// <summary>${line}</summary>`];
@@ -297,7 +297,7 @@ function failUnmappable(context: string, schema: JSONSchema7): never {
297297
const summary = JSON.stringify(schema, (key, value) => (key === "description" ? undefined : value)).slice(0, 200);
298298
throw new Error(
299299
`C# codegen: cannot map schema to an idiomatic C# type (${context}). ` +
300-
`On the runtime side, either tighten the Zod schema to a typed shape, or — if it is genuinely free-form JSON — ` +
300+
`On the runtime side, either tighten the Zod schema to a typed shape, or if it is genuinely free-form JSON ` +
301301
`mark it \`.asOpaqueJson()\` so the schema emits \`x-opaque-json: true\` and the codegen maps it to JsonElement. ` +
302302
`Offending schema (truncated): ${summary}`,
303303
);
@@ -311,7 +311,7 @@ async function formatCSharpFile(filePath: string): Promise<void> {
311311
try {
312312
const projectFile = path.join(REPO_ROOT, "dotnet/src/GitHub.Copilot.SDK.csproj");
313313
await execFileAsync("dotnet", ["format", projectFile, "--include", filePath]);
314-
console.log(` ✓ Formatted with dotnet format`);
314+
console.log(` Formatted with dotnet format`);
315315
} catch {
316316
// dotnet format not available, skip
317317
}
@@ -346,7 +346,7 @@ function schemaTypeToCSharp(schema: JSONSchema7, required: boolean, knownTypes:
346346
const refName = schema.$ref.split("/").pop()!;
347347
return knownTypes.get(refName) || refName;
348348
}
349-
// Titled union schemas (anyOf with a title) — use the title if it's a known generated type
349+
// Titled union schemas (anyOf with a title) use the title if it's a known generated type
350350
if (schema.title && schema.anyOf && knownTypes.has(schema.title)) {
351351
return required ? schema.title : `${schema.title}?`;
352352
}
@@ -518,9 +518,23 @@ function pushObsoleteAttributes(lines: string[], indent = ""): void {
518518
lines.push(...obsoleteAttributes(indent));
519519
}
520520

521-
// ══════════════════════════════════════════════════════════════════════════════
521+
/**
522+
* Emit the `[JsonInclude]` attribute for an internally-marked property and
523+
* return the C# access modifier to use for the property declaration.
524+
*
525+
* `[JsonInclude]` is required because System.Text.Json only auto-(de)serialises
526+
* public members by default; without it, the `internal` setter would silently
527+
* be skipped.
528+
*/
529+
function pushCSharpInternalAttribute(lines: string[], schema: JSONSchema7, indent = " "): "public" | "internal" {
530+
const propInternal = isSchemaInternal(schema);
531+
if (propInternal) lines.push(`${indent}[JsonInclude]`);
532+
return propInternal ? "internal" : "public";
533+
}
534+
535+
// ══════════════════════════════════════════════════════════════════════════════
522536
// SESSION EVENTS
523-
// ══════════════════════════════════════════════════════════════════════════════
537+
// ══════════════════════════════════════════════════════════════════════════════
524538

525539
interface EventVariant {
526540
typeName: string;
@@ -779,12 +793,9 @@ function generateFlattenedBooleanDiscriminatedClass(
779793
if (isSchemaExperimental(info.schema)) pushExperimentalAttribute(lines, " ");
780794
if (isMillisecondsDurationProperty(propName, info.schema)) lines.push(` [JsonConverter(typeof(MillisecondsTimeSpanConverter))]`);
781795
if (!isReq) lines.push(` [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]`);
782-
const propInternal = isSchemaInternal(info.schema);
783-
const propExperimental = isSchemaExperimental(info.schema);
784-
if (propInternal) lines.push(` [JsonInclude]`);
796+
const propVisibility = pushCSharpInternalAttribute(lines, info.schema);
785797
lines.push(` [JsonPropertyName("${propName}")]`);
786798
const reqMod = isReq && !csharpType.endsWith("?") ? "required " : "";
787-
const propVisibility = propInternal ? "internal" : "public";
788799
lines.push(` ${propVisibility} ${reqMod}${csharpType} ${csharpName} { get; set; }`);
789800
}
790801

@@ -887,12 +898,9 @@ function generateDerivedClass(
887898
if (isSchemaExperimental(prop)) pushExperimentalAttribute(lines, " ");
888899
if (isMillisecondsDurationProperty(propName, prop)) lines.push(` [JsonConverter(typeof(MillisecondsTimeSpanConverter))]`);
889900
if (!isReq) lines.push(` [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]`);
890-
const propInternal = isSchemaInternal(prop);
891-
const propExperimental = isSchemaExperimental(prop);
892-
if (propInternal) lines.push(` [JsonInclude]`);
901+
const propVisibility = pushCSharpInternalAttribute(lines, prop);
893902
lines.push(` [JsonPropertyName("${propName}")]`);
894903
const reqMod = isReq && !csharpType.endsWith("?") ? "required " : "";
895-
const propVisibility = propInternal ? "internal" : "public";
896904
lines.push(` ${propVisibility} ${reqMod}${csharpType} ${csharpName} { get; set; }`, "");
897905
}
898906
}
@@ -1117,12 +1125,9 @@ function generateNestedClass(
11171125
if (isSchemaExperimental(prop)) pushExperimentalAttribute(lines, " ");
11181126
if (isMillisecondsDurationProperty(propName, prop)) lines.push(` [JsonConverter(typeof(MillisecondsTimeSpanConverter))]`);
11191127
if (!isReq) lines.push(` [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]`);
1120-
const propInternal = isSchemaInternal(prop);
1121-
const propExperimental = isSchemaExperimental(prop);
1122-
if (propInternal) lines.push(` [JsonInclude]`);
1128+
const propVisibility = pushCSharpInternalAttribute(lines, prop);
11231129
lines.push(` [JsonPropertyName("${propName}")]`);
11241130
const reqMod = isReq && !csharpType.endsWith("?") ? "required " : "";
1125-
const propVisibility = propInternal ? "internal" : "public";
11261131
lines.push(` ${propVisibility} ${reqMod}${csharpType} ${csharpName} { get; set; }`, "");
11271132
}
11281133
if (lines[lines.length - 1] === "") lines.pop();
@@ -1268,12 +1273,9 @@ function generateDataClass(variant: EventVariant, knownTypes: Map<string, string
12681273
if (isSchemaExperimental(prop)) pushExperimentalAttribute(lines, " ");
12691274
if (isMillisecondsDurationProperty(propName, prop)) lines.push(` [JsonConverter(typeof(MillisecondsTimeSpanConverter))]`);
12701275
if (!isReq) lines.push(` [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]`);
1271-
const propInternal = isSchemaInternal(prop);
1272-
const propExperimental = isSchemaExperimental(prop);
1273-
if (propInternal) lines.push(` [JsonInclude]`);
1276+
const propVisibility = pushCSharpInternalAttribute(lines, prop);
12741277
lines.push(` [JsonPropertyName("${propName}")]`);
12751278
const reqMod = isReq && !csharpType.endsWith("?") ? "required " : "";
1276-
const propVisibility = propInternal ? "internal" : "public";
12771279
lines.push(` ${propVisibility} ${reqMod}${csharpType} ${csharpName} { get; set; }`, "");
12781280
}
12791281
if (lines[lines.length - 1] === "") lines.pop();
@@ -1305,10 +1307,8 @@ function emitSessionEventEnvelopeProperty(
13051307
if (isSchemaExperimental(property.schema)) pushExperimentalAttribute(lines, " ");
13061308
if (isMillisecondsDurationProperty(property.name, property.schema)) lines.push(` [JsonConverter(typeof(MillisecondsTimeSpanConverter))]`);
13071309
if (!property.required) lines.push(` [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]`);
1308-
const propInternal = isSchemaInternal(property.schema);
1309-
if (propInternal) lines.push(` [JsonInclude]`);
1310+
const propVisibility = pushCSharpInternalAttribute(lines, property.schema);
13101311
lines.push(` [JsonPropertyName("${property.name}")]`);
1311-
const propVisibility = propInternal ? "internal" : "public";
13121312
lines.push(` ${propVisibility} ${csharpType} ${csharpName} { get; set; }`, "");
13131313

13141314
return lines;
@@ -1411,13 +1411,13 @@ export async function generateSessionEvents(schemaPath?: string): Promise<void>
14111411
const processed = postProcessSchema(schema);
14121412
const code = generateSessionEventsCode(processed);
14131413
const outPath = await writeGeneratedFile("dotnet/src/Generated/SessionEvents.cs", code);
1414-
console.log(` ✓ ${outPath}`);
1414+
console.log(` ${outPath}`);
14151415
await formatCSharpFile(outPath);
14161416
}
14171417

1418-
// ══════════════════════════════════════════════════════════════════════════════
1418+
// ══════════════════════════════════════════════════════════════════════════════
14191419
// RPC TYPES
1420-
// ══════════════════════════════════════════════════════════════════════════════
1420+
// ══════════════════════════════════════════════════════════════════════════════
14211421

14221422
let emittedRpcClassSchemas = new Map<string, string>();
14231423
let emittedRpcEnumResultTypes = new Set<string>();
@@ -1524,7 +1524,7 @@ function resolveRpcType(schema: JSONSchema7, isRequired: boolean, parentClassNam
15241524

15251525
return resolveRpcType(refSchema, isRequired, parentClassName, propName, classes);
15261526
}
1527-
// Handle anyOf: [T, null/{not:{}}] → T? (nullable typed property)
1527+
// Handle anyOf: [T, null/{not:{}}] T? (nullable typed property)
15281528
const nullableInner = getNullableInner(schema);
15291529
if (nullableInner) {
15301530
return resolveRpcType(nullableInner, false, parentClassName, propName, classes);
@@ -1661,8 +1661,7 @@ function emitRpcClass(
16611661
if (isSchemaDeprecated(prop)) pushObsoleteAttributes(lines, " ");
16621662
if (isSchemaExperimental(prop)) pushExperimentalAttribute(lines, " ");
16631663
if (isMillisecondsDurationProperty(propName, prop)) lines.push(` [JsonConverter(typeof(MillisecondsTimeSpanConverter))]`);
1664-
const propInternal = isSchemaInternal(prop);
1665-
if (propInternal) lines.push(` [JsonInclude]`);
1664+
const propVisibility = pushCSharpInternalAttribute(lines, prop);
16661665
lines.push(` [JsonPropertyName("${propName}")]`);
16671666

16681667
let defaultVal = "";
@@ -1680,7 +1679,6 @@ function emitRpcClass(
16801679
defaultVal = " = null!;";
16811680
}
16821681
}
1683-
const propVisibility = propInternal ? "internal" : "public";
16841682
lines.push(` ${propVisibility} ${csharpType} ${csharpName} ${propAccessors}${defaultVal}`);
16851683
if (i < props.length - 1) lines.push("");
16861684
}
@@ -2403,13 +2401,13 @@ export async function generateRpc(schemaPath?: string, sessionEventsSchema?: JSO
24032401
}
24042402
const code = generateRpcCode(schema, externalJsonSerializableRefs, externalValueTypes);
24052403
const outPath = await writeGeneratedFile("dotnet/src/Generated/Rpc.cs", code);
2406-
console.log(` ✓ ${outPath}`);
2404+
console.log(` ${outPath}`);
24072405
await formatCSharpFile(outPath);
24082406
}
24092407

2410-
// ══════════════════════════════════════════════════════════════════════════════
2408+
// ══════════════════════════════════════════════════════════════════════════════
24112409
// MAIN
2412-
// ══════════════════════════════════════════════════════════════════════════════
2410+
// ══════════════════════════════════════════════════════════════════════════════
24132411

24142412
async function generate(sessionSchemaPath?: string, apiSchemaPath?: string): Promise<void> {
24152413
await generateSessionEvents(sessionSchemaPath);

0 commit comments

Comments
 (0)