diff --git a/samples/csharp/docfx.json b/samples/csharp/docfx.json index 084aada28ad..828d0228138 100644 --- a/samples/csharp/docfx.json +++ b/samples/csharp/docfx.json @@ -1,22 +1,39 @@ { "metadata": [ + { + "src": [ + "src/*.csproj" + ], + "outputFormat": "mref", + "memberLayout": "separatePages", + "output": "obj/api" + }, + { + "src": [ + "src/*.csproj" + ], + "outputFormat": "apiPage", + "memberLayout": "separatePages", + "output": "obj/apipage" + }, { "src": [ "src/*.csproj" ], - "dest": "api" - } + "outputFormat": "markdown", + "memberLayout": "separatePages", + "output": "obj/md" + }, ], "build": { "content": [ - { - "files": [ - "api/**/*.yml", - "toc.yml" - ] - } + { "files": [ "*.{md,yml}" ]}, + { "files": [ "**/*.yml" ], "src": "obj/api", "dest": "api" }, + { "files": [ "**/*.yml" ], "src": "obj/apipage", "dest": "apipage" }, + { "files": [ "**/*.{md,yml}" ], "src": "obj/md", "dest": "md" }, ], - "dest": "_site", + "output": "_site", + "template": ["default", "modern"], "exportViewModel": true } -} \ No newline at end of file +} diff --git a/samples/csharp/index.md b/samples/csharp/index.md new file mode 100644 index 00000000000..97b90331fa4 --- /dev/null +++ b/samples/csharp/index.md @@ -0,0 +1,3 @@ +--- +_layout: landing +--- diff --git a/samples/csharp/src/CSharp13.cs b/samples/csharp/src/CSharp13.cs new file mode 100644 index 00000000000..c2130e60950 --- /dev/null +++ b/samples/csharp/src/CSharp13.cs @@ -0,0 +1,12 @@ +namespace CSharp13; + +// https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-13#allows-ref-struct +public class AllowRefStruct + where T : allows ref struct +{ + // Use T as a ref struct: + public void Method(scoped T p) + { + // The parameter p must follow ref safety rules + } +} diff --git a/samples/csharp/src/CSharp14.cs b/samples/csharp/src/CSharp14.cs new file mode 100644 index 00000000000..db7a32a9fb1 --- /dev/null +++ b/samples/csharp/src/CSharp14.cs @@ -0,0 +1,78 @@ +namespace CSharp14; + +// https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-14#the-field-keyword +public class FieldBackedPropertySample +{ + public int FieldBackedProperty + { + get; + set => field = value; + } +} + +public class SampleClass +{ + public required string Value { get; init; } +} + +public static class SampleClassExtensions +{ + // Extension block + extension(SampleClass sample) + { + // Extension property: + public string ExtensionProperty => sample.Value; + + // Extension method: + public void ExtensionMethod() + { + } + } + + // Extension block for static type + extension(SampleClass) + { + // static extension method: + public static void StaticExtensionMethod() + { + } + + // static extension property: + public static int StaticExtensionProperty => default; + + // static user defined operator: + public static SampleClass operator +(SampleClass value1, SampleClass value2) + => new() { Value = value1.Value + value2.Value }; + } +} + +public class UserDefinedCompoundAssignmentOperatorsSample +{ + public int? Value { get; set; } + + public override string? ToString() => Value?.ToString(); + + public void operator ++() { Value += 1; } + + public void operator --() { Value -= 1; } + + public void operator +=(UserDefinedCompoundAssignmentOperatorsSample c) { Value += c?.Value; } + + public void operator -=(UserDefinedCompoundAssignmentOperatorsSample c) { Value -= c?.Value; } + + public void operator *=(UserDefinedCompoundAssignmentOperatorsSample c) { Value *= c?.Value; } + + public void operator /=(UserDefinedCompoundAssignmentOperatorsSample c) { Value /= c?.Value; } + + public void operator %=(UserDefinedCompoundAssignmentOperatorsSample c) { Value %= c?.Value; } + + public void operator &=(UserDefinedCompoundAssignmentOperatorsSample c) { Value &= c?.Value; } + + public void operator |=(UserDefinedCompoundAssignmentOperatorsSample c) { Value |= c?.Value; } + + public void operator ^=(UserDefinedCompoundAssignmentOperatorsSample c) { Value ^= c?.Value; } + + public void operator <<=(int shift) { Value <<= shift; } + + public void operator >>=(int shift) { Value >>= shift; } +} diff --git a/samples/csharp/toc.yml b/samples/csharp/toc.yml new file mode 100644 index 00000000000..d909a754ad0 --- /dev/null +++ b/samples/csharp/toc.yml @@ -0,0 +1,8 @@ +- name: API Documentation + items: + - name: .NET API + href: obj/api/ + - name: .NET API (apipage) + href: obj/apipage/ + - name: .NET API (markdown) + href: obj/md/ diff --git a/src/Docfx.Dotnet/DotnetApiCatalog.Toc.cs b/src/Docfx.Dotnet/DotnetApiCatalog.Toc.cs index a4707e98494..c90dbdca325 100644 --- a/src/Docfx.Dotnet/DotnetApiCatalog.Toc.cs +++ b/src/Docfx.Dotnet/DotnetApiCatalog.Toc.cs @@ -26,6 +26,7 @@ enum TocNodeType Method, Event, Operator, + Extension, } class TocNode @@ -135,6 +136,12 @@ IEnumerable CreateNamedTypeToc(INamedTypeSymbol type) { var idExists = true; var id = VisitorHelper.PathFriendlyId(VisitorHelper.GetId(symbol)); + + // TODO: Handle C# 14 Extension Members. It'll be shown on `static class` and target type of extension. + // Currently Extension symbol is skipped. + if (type.TypeKind == TypeKind.Extension) + yield break; + if (!tocNodes.TryGetValue(id, out var node)) { idExists = false; diff --git a/src/Docfx.Dotnet/ManagedReference/Models/MemberType.cs b/src/Docfx.Dotnet/ManagedReference/Models/MemberType.cs index f43c346b052..3219fc40bd1 100644 --- a/src/Docfx.Dotnet/ManagedReference/Models/MemberType.cs +++ b/src/Docfx.Dotnet/ManagedReference/Models/MemberType.cs @@ -22,5 +22,6 @@ public enum MemberType Operator, Container, AttachedEvent, - AttachedProperty + AttachedProperty, + Extension, } diff --git a/src/Docfx.Dotnet/ManagedReference/Resolvers/ResolveExtensionMember.cs b/src/Docfx.Dotnet/ManagedReference/Resolvers/ResolveExtensionMember.cs new file mode 100644 index 00000000000..78527319c1e --- /dev/null +++ b/src/Docfx.Dotnet/ManagedReference/Resolvers/ResolveExtensionMember.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Docfx.DataContracts.ManagedReference; + +#nullable enable + +namespace Docfx.Dotnet; + +internal class ResolveExtensionMember : IResolverPipeline +{ + public void Run(MetadataModel yaml, ResolverContext context) + { + // Remove extension from root members. + yaml.Members.RemoveAll(x => x.Type == MemberType.Extension); + + // Remove extension from TocYamlViewModel items. + ProcessItem(yaml.TocYamlViewModel); + } + + private static void ProcessItem(MetadataItem metadataItem) + { + if (metadataItem.IsInvalid || metadataItem.Items == null) + return; + + metadataItem.Items.RemoveAll(x => x.Type == MemberType.Extension); + + foreach (var item in metadataItem.Items) + ProcessItem(item); + } +} diff --git a/src/Docfx.Dotnet/ManagedReference/Resolvers/YamlMetadataResolver.cs b/src/Docfx.Dotnet/ManagedReference/Resolvers/YamlMetadataResolver.cs index 57eda24d1df..d5b5c03a755 100644 --- a/src/Docfx.Dotnet/ManagedReference/Resolvers/YamlMetadataResolver.cs +++ b/src/Docfx.Dotnet/ManagedReference/Resolvers/YamlMetadataResolver.cs @@ -16,6 +16,7 @@ internal static class YamlMetadataResolver new NormalizeSyntax(), new BuildMembers(), new SetDerivedClass(), + new ResolveExtensionMember(), new BuildToc() ]; diff --git a/src/Docfx.Dotnet/ManagedReference/Visitors/SymbolVisitorAdapter.cs b/src/Docfx.Dotnet/ManagedReference/Visitors/SymbolVisitorAdapter.cs index d32cea12e2f..05c8435a241 100644 --- a/src/Docfx.Dotnet/ManagedReference/Visitors/SymbolVisitorAdapter.cs +++ b/src/Docfx.Dotnet/ManagedReference/Visitors/SymbolVisitorAdapter.cs @@ -197,7 +197,15 @@ s is not INamedTypeSymbol } } - AddReference(symbol); + if (symbol.IsExtension) + { + // Currently extension symbol is skipped and reference is not added. + // TODO: Handle C# 14 Extension Member definition. + } + else + { + AddReference(symbol); + } item.Attributes = GetAttributeInfo(symbol.GetAttributes()); diff --git a/src/Docfx.Dotnet/ManagedReference/Visitors/VisitorHelper.cs b/src/Docfx.Dotnet/ManagedReference/Visitors/VisitorHelper.cs index 04cc202c6d7..de4e1c59e77 100644 --- a/src/Docfx.Dotnet/ManagedReference/Visitors/VisitorHelper.cs +++ b/src/Docfx.Dotnet/ManagedReference/Visitors/VisitorHelper.cs @@ -192,6 +192,8 @@ public static MemberType GetMemberTypeFromTypeKind(TypeKind typeKind) return MemberType.Struct; case TypeKind.Delegate: return MemberType.Delegate; + case TypeKind.Extension: + return MemberType.Extension; default: return MemberType.Default; } diff --git a/src/Docfx.Dotnet/YamlViewModelExtensions.cs b/src/Docfx.Dotnet/YamlViewModelExtensions.cs index 5c910db7eb9..0d904ecd99a 100644 --- a/src/Docfx.Dotnet/YamlViewModelExtensions.cs +++ b/src/Docfx.Dotnet/YamlViewModelExtensions.cs @@ -12,7 +12,13 @@ internal static class YamlViewModelExtensions { public static bool IsPageLevel(this MemberType type) { - return type == MemberType.Namespace || type == MemberType.Class || type == MemberType.Enum || type == MemberType.Delegate || type == MemberType.Interface || type == MemberType.Struct; + return type is MemberType.Namespace + or MemberType.Class + or MemberType.Enum + or MemberType.Delegate + or MemberType.Interface + or MemberType.Struct + or MemberType.Extension; } /// @@ -22,7 +28,11 @@ public static bool IsPageLevel(this MemberType type) /// public static bool AllowMultipleItems(this MemberType type) { - return type == MemberType.Class || type == MemberType.Enum || type == MemberType.Delegate || type == MemberType.Interface || type == MemberType.Struct; + return type is MemberType.Class + or MemberType.Enum + or MemberType.Delegate + or MemberType.Interface + or MemberType.Struct; } public static MetadataItem ShrinkToSimpleToc(this MetadataItem item)