diff --git a/CHANGELOG.md b/CHANGELOG.md index e783ce0b56..2224a66040 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - PREVIEW: Moved preview configuration files to the .kiota directory. [#4310](https://github.com/microsoft/kiota/issues/4310) - PREVIEW: Moved the copy descriptions to dedicated folders. [#4310](https://github.com/microsoft/kiota/issues/4310) - PREVIEW: Renamed the config to workspace file. [#4310](https://github.com/microsoft/kiota/issues/4310) +- Changed Csharp code generation to put braces on new lines (where it makes sense). [#4347](https://github.com/microsoft/kiota/issues/4347) ## [1.12.0] - 2024-03-06 diff --git a/src/Kiota.Builder/Writers/CSharp/CodeClassDeclarationWriter.cs b/src/Kiota.Builder/Writers/CSharp/CodeClassDeclarationWriter.cs index fbd487b882..cca625a398 100644 --- a/src/Kiota.Builder/Writers/CSharp/CodeClassDeclarationWriter.cs +++ b/src/Kiota.Builder/Writers/CSharp/CodeClassDeclarationWriter.cs @@ -37,6 +37,7 @@ public override void WriteCodeElement(ClassDeclaration codeElement, LanguageWrit var derivation = derivedTypes.Length != 0 ? ": " + derivedTypes.Aggregate(static (x, y) => $"{x}, {y}") + " " : string.Empty; conventions.WriteLongDescription(parentClass, writer); conventions.WriteDeprecationAttribute(parentClass, writer); - writer.StartBlock($"public class {codeElement.Name.ToFirstCharacterUpperCase()} {derivation}{{"); + writer.WriteLine($"public class {codeElement.Name.ToFirstCharacterUpperCase()} {derivation}"); + writer.StartBlock(); } } diff --git a/src/Kiota.Builder/Writers/CSharp/CodeEnumWriter.cs b/src/Kiota.Builder/Writers/CSharp/CodeEnumWriter.cs index 92d58d64fa..207468f20f 100644 --- a/src/Kiota.Builder/Writers/CSharp/CodeEnumWriter.cs +++ b/src/Kiota.Builder/Writers/CSharp/CodeEnumWriter.cs @@ -33,7 +33,8 @@ public override void WriteCodeElement(CodeEnum codeElement, LanguageWriter write if (codeElement.Flags) writer.WriteLine("[Flags]"); conventions.WriteDeprecationAttribute(codeElement, writer); - writer.StartBlock($"public enum {codeElement.Name.ToFirstCharacterUpperCase()} {{"); + writer.WriteLine($"public enum {codeElement.Name.ToFirstCharacterUpperCase()}"); + writer.StartBlock(); var idx = 0; foreach (var option in codeElement.Options) { diff --git a/src/Kiota.Builder/Writers/CSharp/CodeIndexerWriter.cs b/src/Kiota.Builder/Writers/CSharp/CodeIndexerWriter.cs index 89b3b292d6..d214ea34c4 100644 --- a/src/Kiota.Builder/Writers/CSharp/CodeIndexerWriter.cs +++ b/src/Kiota.Builder/Writers/CSharp/CodeIndexerWriter.cs @@ -16,10 +16,14 @@ public override void WriteCodeElement(CodeIndexer codeElement, LanguageWriter wr conventions.WriteShortDescription(codeElement.IndexParameter, writer, $"", ""); conventions.WriteAdditionalDescriptionItem($"A {conventions.GetTypeStringForDocumentation(codeElement.ReturnType, codeElement)}", writer); conventions.WriteDeprecationAttribute(codeElement, writer); - writer.StartBlock($"public {returnType} this[{conventions.GetTypeString(codeElement.IndexParameter.Type, codeElement)} position] {{ get {{"); + writer.WriteLine($"public {returnType} this[{conventions.GetTypeString(codeElement.IndexParameter.Type, codeElement)} position]"); + writer.StartBlock(); + writer.WriteLine("get"); + writer.StartBlock(); if (parentClass.GetPropertyOfKind(CodePropertyKind.PathParameters) is CodeProperty pathParametersProp) conventions.AddParametersAssignment(writer, pathParametersProp.Type, pathParametersProp.Name.ToFirstCharacterUpperCase(), string.Empty, (codeElement.IndexParameter.Type, codeElement.IndexParameter.SerializationName, "position")); conventions.AddRequestBuilderBody(parentClass, returnType, writer, conventions.TempDictionaryVarName, "return "); - writer.CloseBlock("} }"); + writer.CloseBlock(); + writer.CloseBlock(); } } diff --git a/src/Kiota.Builder/Writers/CSharp/CodeMethodWriter.cs b/src/Kiota.Builder/Writers/CSharp/CodeMethodWriter.cs index 08a2e53183..852d8b7a12 100644 --- a/src/Kiota.Builder/Writers/CSharp/CodeMethodWriter.cs +++ b/src/Kiota.Builder/Writers/CSharp/CodeMethodWriter.cs @@ -111,7 +111,8 @@ private void WriteRawUrlBuilderBody(CodeClass parentClass, CodeMethod codeElemen private static readonly CodePropertyTypeComparer CodePropertyTypeBackwardComparer = new(true); private void WriteFactoryMethodBodyForInheritedModel(CodeMethod codeElement, CodeClass parentClass, LanguageWriter writer) { - writer.StartBlock($"return {DiscriminatorMappingVarName} switch {{"); + writer.WriteLine($"return {DiscriminatorMappingVarName} switch"); + writer.StartBlock(); foreach (var mappedType in parentClass.DiscriminatorInformation.DiscriminatorMappings) { writer.WriteLine($"\"{mappedType.Key}\" => new {conventions.GetTypeString(mappedType.Value.AllTypes.First(), codeElement)}(),"); @@ -132,19 +133,15 @@ private void WriteFactoryMethodBodyForUnionModel(CodeMethod codeElement, CodeCla if (propertyType.TypeDefinition is CodeClass && !propertyType.IsCollection) { var mappedType = parentClass.DiscriminatorInformation.DiscriminatorMappings.FirstOrDefault(x => x.Value.Name.Equals(propertyType.Name, StringComparison.OrdinalIgnoreCase)); - writer.WriteLine($"{(includeElse ? "else " : string.Empty)}if(\"{mappedType.Key}\".Equals({DiscriminatorMappingVarName}, StringComparison.OrdinalIgnoreCase)) {{"); - writer.IncreaseIndent(); - writer.WriteLine($"{ResultVarName}.{property.Name.ToFirstCharacterUpperCase()} = new {conventions.GetTypeString(propertyType, codeElement)}();"); - writer.CloseBlock(); + writer.WriteLine($"{(includeElse ? "else " : string.Empty)}if(\"{mappedType.Key}\".Equals({DiscriminatorMappingVarName}, StringComparison.OrdinalIgnoreCase))"); + writer.WriteBlock(lines: $"{ResultVarName}.{property.Name.ToFirstCharacterUpperCase()} = new {conventions.GetTypeString(propertyType, codeElement)}();"); } else if (propertyType.TypeDefinition is CodeClass && propertyType.IsCollection || propertyType.TypeDefinition is null || propertyType.TypeDefinition is CodeEnum) { var typeName = conventions.GetTypeString(propertyType, codeElement, true, false); var valueVarName = $"{property.Name.ToFirstCharacterLowerCase()}Value"; - writer.WriteLine($"{(includeElse ? "else " : string.Empty)}if({parseNodeParameter.Name.ToFirstCharacterLowerCase()}.{GetDeserializationMethodName(propertyType, codeElement)} is {typeName} {valueVarName}) {{"); - writer.IncreaseIndent(); - writer.WriteLine($"{ResultVarName}.{property.Name.ToFirstCharacterUpperCase()} = {valueVarName};"); - writer.CloseBlock(); + writer.WriteLine($"{(includeElse ? "else " : string.Empty)}if({parseNodeParameter.Name.ToFirstCharacterLowerCase()}.{GetDeserializationMethodName(propertyType, codeElement)} is {typeName} {valueVarName})"); + writer.WriteBlock(lines: $"{ResultVarName}.{property.Name.ToFirstCharacterUpperCase()} = {valueVarName};"); } if (!includeElse) includeElse = true; @@ -164,9 +161,8 @@ private void WriteFactoryMethodBodyForIntersectionModel(CodeMethod codeElement, { var typeName = conventions.GetTypeString(propertyType, codeElement, true, false); var valueVarName = $"{property.Name.ToFirstCharacterLowerCase()}Value"; - writer.StartBlock($"{(includeElse ? "else " : string.Empty)}if({parseNodeParameter.Name.ToFirstCharacterLowerCase()}.{GetDeserializationMethodName(propertyType, codeElement)} is {typeName} {valueVarName}) {{"); - writer.WriteLine($"{ResultVarName}.{property.Name.ToFirstCharacterUpperCase()} = {valueVarName};"); - writer.CloseBlock(); + writer.WriteLine($"{(includeElse ? "else " : string.Empty)}if({parseNodeParameter.Name.ToFirstCharacterLowerCase()}.{GetDeserializationMethodName(propertyType, codeElement)} is {typeName} {valueVarName})"); + writer.WriteBlock(lines: $"{ResultVarName}.{property.Name.ToFirstCharacterUpperCase()} = {valueVarName};"); } if (!includeElse) includeElse = true; @@ -223,9 +219,8 @@ private static void WriteApiConstructorBody(CodeClass parentClass, CodeMethod me WriteSerializationRegistration(method.DeserializerModules, writer, "RegisterDefaultDeserializer"); if (!string.IsNullOrEmpty(method.BaseUrl)) { - writer.StartBlock($"if (string.IsNullOrEmpty({requestAdapterPropertyName}.BaseUrl)) {{"); - writer.WriteLine($"{requestAdapterPropertyName}.BaseUrl = \"{method.BaseUrl}\";"); - writer.CloseBlock(); + writer.WriteLine($"if (string.IsNullOrEmpty({requestAdapterPropertyName}.BaseUrl))"); + writer.WriteBlock(lines: $"{requestAdapterPropertyName}.BaseUrl = \"{method.BaseUrl}\";"); if (pathParametersProperty != null) writer.WriteLine($"{pathParametersProperty.Name.ToFirstCharacterUpperCase()}.TryAdd(\"baseurl\", {requestAdapterPropertyName}.BaseUrl);"); } @@ -293,9 +288,8 @@ private void WriteDeserializerBodyForUnionModel(CodeMethod method, CodeClass par .ThenBy(static x => x.Name) .Select(static x => x.Name.ToFirstCharacterUpperCase())) { - writer.StartBlock($"{(includeElse ? "else " : string.Empty)}if({otherPropName} != null) {{"); - writer.WriteLine($"return {otherPropName}.{method.Name.ToFirstCharacterUpperCase()}();"); - writer.CloseBlock(); + writer.WriteLine($"{(includeElse ? "else " : string.Empty)}if({otherPropName} != null)"); + writer.WriteBlock(lines: $"return {otherPropName}.{method.Name.ToFirstCharacterUpperCase()}();"); if (!includeElse) includeElse = true; } @@ -315,7 +309,8 @@ private void WriteDeserializerBodyForIntersectionModel(CodeClass parentClass, La var propertiesNamesAsConditions = propertiesNames .Select(static x => $"{x} != null") .Aggregate(static (x, y) => $"{x} || {y}"); - writer.StartBlock($"if({propertiesNamesAsConditions}) {{"); + writer.WriteLine($"if({propertiesNamesAsConditions})"); + writer.StartBlock(); var propertiesNamesAsArgument = propertiesNames .Aggregate(static (x, y) => $"{x}, {y}"); writer.WriteLine($"return ParseNodeHelper.MergeDeserializersForIntersectionWrapper({propertiesNamesAsArgument});"); @@ -326,7 +321,8 @@ private void WriteDeserializerBodyForIntersectionModel(CodeClass parentClass, La private void WriteDeserializerBodyForInheritedModel(bool shouldHide, CodeMethod codeElement, CodeClass parentClass, LanguageWriter writer) { var parentSerializationInfo = shouldHide ? $"(base.{codeElement.Name.ToFirstCharacterUpperCase()}())" : string.Empty; - writer.StartBlock($"return {DefaultDeserializerValue}{parentSerializationInfo} {{"); + writer.WriteLine($"return {DefaultDeserializerValue}{parentSerializationInfo}"); + writer.StartBlock(); foreach (var otherProp in parentClass .GetPropertiesOfKind(CodePropertyKind.Custom) .Where(static x => !x.ExistsInBaseType) @@ -381,8 +377,8 @@ protected void WriteRequestExecutorBody(CodeMethod codeElement, RequestParams re if (codeElement.ErrorMappings.Any()) { errorMappingVarName = "errorMapping"; - writer.WriteLine($"var {errorMappingVarName} = new Dictionary> {{"); - writer.IncreaseIndent(); + writer.WriteLine($"var {errorMappingVarName} = new Dictionary>"); + writer.StartBlock(); foreach (var errorMapping in codeElement.ErrorMappings.Where(errorMapping => errorMapping.Value.AllTypes.FirstOrDefault()?.TypeDefinition is CodeClass)) { writer.WriteLine($"{{\"{errorMapping.Key.ToUpperInvariant()}\", {conventions.GetTypeString(errorMapping.Value, codeElement, false)}.CreateFromDiscriminatorValue}},"); @@ -474,10 +470,8 @@ private void WriteSerializerBodyForUnionModel(CodeMethod method, CodeClass paren .OrderBy(static x => x, CodePropertyTypeForwardComparer) .ThenBy(static x => x.Name)) { - writer.WriteLine($"{(includeElse ? "else " : string.Empty)}if({otherProp.Name.ToFirstCharacterUpperCase()} != null) {{"); - writer.IncreaseIndent(); - writer.WriteLine($"writer.{GetSerializationMethodName(otherProp.Type, method)}(null, {otherProp.Name.ToFirstCharacterUpperCase()});"); - writer.CloseBlock(); + writer.WriteLine($"{(includeElse ? "else " : string.Empty)}if({otherProp.Name.ToFirstCharacterUpperCase()} != null)"); + writer.WriteBlock(lines: $"writer.{GetSerializationMethodName(otherProp.Type, method)}(null, {otherProp.Name.ToFirstCharacterUpperCase()});"); if (!includeElse) includeElse = true; } @@ -492,10 +486,8 @@ private void WriteSerializerBodyForIntersectionModel(CodeMethod method, CodeClas .OrderBy(static x => x, CodePropertyTypeBackwardComparer) .ThenBy(static x => x.Name)) { - writer.WriteLine($"{(includeElse ? "else " : string.Empty)}if({otherProp.Name.ToFirstCharacterUpperCase()} != null) {{"); - writer.IncreaseIndent(); - writer.WriteLine($"writer.{GetSerializationMethodName(otherProp.Type, method)}(null, {otherProp.Name.ToFirstCharacterUpperCase()});"); - writer.CloseBlock(); + writer.WriteLine($"{(includeElse ? "else " : string.Empty)}if({otherProp.Name.ToFirstCharacterUpperCase()} != null)"); + writer.WriteBlock(lines: $"writer.{GetSerializationMethodName(otherProp.Type, method)}(null, {otherProp.Name.ToFirstCharacterUpperCase()});"); if (!includeElse) includeElse = true; } @@ -630,11 +622,13 @@ private void WriteMethodPrototype(CodeMethod code, CodeClass parentClass, Langua conventions.GetParameterSignature(p, code)) .ToList()); CSharpConventionService.WriteNullableOpening(writer); - writer.WriteLine($"{conventions.GetAccessModifier(code.Access)} {staticModifier}{hideModifier}{completeReturnTypeWithNullable}{methodName}({nullableParameters}){baseSuffix} {{"); + writer.WriteLine($"{conventions.GetAccessModifier(code.Access)} {staticModifier}{hideModifier}{completeReturnTypeWithNullable}{methodName}({nullableParameters}){baseSuffix}"); + writer.WriteLine("{"); CSharpConventionService.WriteNullableMiddle(writer); } - writer.WriteLine($"{conventions.GetAccessModifier(code.Access)} {staticModifier}{hideModifier}{completeReturnType}{methodName}({parameters}){baseSuffix} {{"); + writer.WriteLine($"{conventions.GetAccessModifier(code.Access)} {staticModifier}{hideModifier}{completeReturnType}{methodName}({parameters}){baseSuffix}"); + writer.WriteLine("{"); if (includeNullableReferenceType) CSharpConventionService.WriteNullableClosing(writer); diff --git a/src/Kiota.Builder/Writers/CSharp/CodePropertyWriter.cs b/src/Kiota.Builder/Writers/CSharp/CodePropertyWriter.cs index e4b9efffe7..d50c0bc1ff 100644 --- a/src/Kiota.Builder/Writers/CSharp/CodePropertyWriter.cs +++ b/src/Kiota.Builder/Writers/CSharp/CodePropertyWriter.cs @@ -41,11 +41,11 @@ private void WritePropertyInternal(CodeProperty codeElement, LanguageWriter writ switch (codeElement.Kind) { case CodePropertyKind.RequestBuilder: - writer.WriteLine($"{conventions.GetAccessModifier(codeElement.Access)} {propertyType} {codeElement.Name.ToFirstCharacterUpperCase()} {{ get =>"); - writer.IncreaseIndent(); + writer.WriteLine($"{conventions.GetAccessModifier(codeElement.Access)} {propertyType} {codeElement.Name.ToFirstCharacterUpperCase()}"); + writer.StartBlock(); + writer.Write("get => "); conventions.AddRequestBuilderBody(parentClass, propertyType, writer); - writer.DecreaseIndent(); - writer.WriteLine("}"); + writer.CloseBlock(); break; case CodePropertyKind.AdditionalData when backingStoreProperty != null: case CodePropertyKind.Custom when backingStoreProperty != null: diff --git a/tests/Kiota.Builder.Tests/Writers/CSharp/CodeMethodWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/CSharp/CodeMethodWriterTests.cs index 820e7d2a5d..f6ebf05944 100644 --- a/tests/Kiota.Builder.Tests/Writers/CSharp/CodeMethodWriterTests.cs +++ b/tests/Kiota.Builder.Tests/Writers/CSharp/CodeMethodWriterTests.cs @@ -560,7 +560,7 @@ public void WritesModelFactoryBodyForUnionModels() writer.Write(factoryMethod); var result = tw.ToString(); Assert.Contains("var mappingValue = parseNode.GetChildNode(\"@odata.type\")?.GetStringValue()", result); - Assert.DoesNotContain("return mappingValue switch {", result); + Assert.DoesNotContain("return mappingValue switch", result); Assert.Contains("var result = new UnionTypeWrapper()", result); Assert.Contains("if(\"#kiota.complexType1\".Equals(mappingValue, StringComparison.OrdinalIgnoreCase))", result); Assert.Contains("ComplexType1Value = new ComplexType1()", result); @@ -600,7 +600,7 @@ public void WritesModelFactoryBodyForIntersectionModels() writer.Write(factoryMethod); var result = tw.ToString(); Assert.DoesNotContain("var mappingValue = parseNode.GetChildNode(\"@odata.type\")?.GetStringValue()", result); - Assert.DoesNotContain("return mappingValue switch {", result); + Assert.DoesNotContain("return mappingValue switch", result); Assert.Contains("var result = new IntersectionTypeWrapper()", result); Assert.DoesNotContain("if(\"#kiota.complexType1\".Equals(mappingValue, StringComparison.OrdinalIgnoreCase))", result); Assert.Contains("if(parseNode.GetStringValue() is string stringValueValue)", result); @@ -710,7 +710,7 @@ public void WritesModelFactoryBodyAndDisambiguateAmbiguousImportedTypes() var result = tw.ToString(); Assert.Contains("var mappingValue = parseNode.GetChildNode(\"@odata.type\")?.GetStringValue()", result); - Assert.Contains("return mappingValue switch {", result); + Assert.Contains("return mappingValue switch", result); Assert.Contains("\"namespaceLevelOne.ConflictingModel\" => new namespaceLevelOne.ConflictingModel(),", result); //Assert the disambiguation happens due to the enum imported Assert.Contains("_ => new ConflictingModelBaseClass()", result); AssertExtensions.CurlyBracesAreClosed(result); @@ -765,7 +765,7 @@ public void WritesModelFactoryBodyForInheritedModels() writer.Write(factoryMethod); var result = tw.ToString(); Assert.Contains("var mappingValue = parseNode.GetChildNode(\"@odata.type\")?.GetStringValue()", result); - Assert.Contains("return mappingValue switch {", result); + Assert.Contains("return mappingValue switch", result); Assert.Contains("\"ns.childmodel\" => new ChildModel()", result); Assert.Contains("_ => new ParentModel()", result); AssertExtensions.CurlyBracesAreClosed(result); @@ -859,7 +859,7 @@ public void DoesntWriteFactorySwitchOnEmptyPropertyName() var result = tw.ToString(); Assert.DoesNotContain("var mappingValue = parseNode.GetChildNode(\"@odata.type\")?.GetStringValue()", result); Assert.DoesNotContain("var mappingValue = mappingValueNode?.GetStringValue()", result); - Assert.DoesNotContain("return mappingValue switch {", result); + Assert.DoesNotContain("return mappingValue switch", result); Assert.DoesNotContain("\"ns.childmodel\" => new ChildModel()", result); Assert.Contains("return new ParentModel()", result); AssertExtensions.CurlyBracesAreClosed(result); @@ -900,7 +900,7 @@ public void DoesntWriteFactorySwitchOnEmptyMappings() var result = tw.ToString(); Assert.DoesNotContain("var mappingValue = parseNode.GetChildNode(\"@odata.type\")?.GetStringValue()", result); Assert.DoesNotContain("var mappingValue = mappingValueNode?.GetStringValue()", result); - Assert.DoesNotContain("return mappingValue switch {", result); + Assert.DoesNotContain("return mappingValue switch", result); Assert.DoesNotContain("\"ns.childmodel\" => new ChildModel()", result); Assert.Contains("return new ParentModel()", result); AssertExtensions.CurlyBracesAreClosed(result);