Skip to content

Commit

Permalink
feat: Add option for including explicit interface implementations (#9471
Browse files Browse the repository at this point in the history
)
  • Loading branch information
yufeih authored Nov 25, 2023

Unverified

This commit is not signed, but one or more authors requires that any commit attributed to them is signed.
1 parent f04be7b commit 0a3bd2c
Showing 7 changed files with 68 additions and 9 deletions.
8 changes: 8 additions & 0 deletions docs/reference/docfx-json-reference.md
Original file line number Diff line number Diff line change
@@ -379,6 +379,14 @@ Specifies how enum members are sorted:
- `alphabetic` (default): Sort enum members in alphabetic order.
- `declaringOrder`: Sort enum members in the order as they are declared in the source code.

### `includePrivateMembers`

Specifies whether private or internal APIs are included in the generated docs. The default value is `false`.

### `includeExplicitInterfaceImplementations`

Specifies whether explicit interface implementations are included in the generated docs. The default value is `false`.

## `pdf`

Configuration options that are applied for `docfx pdf` command:
1 change: 1 addition & 0 deletions src/Docfx.Dotnet/DotnetApiCatalog.cs
Original file line number Diff line number Diff line change
@@ -161,6 +161,7 @@ private static ExtractMetadataConfig ConvertConfig(MetadataJsonItemConfig config
ShouldSkipMarkup = configModel?.ShouldSkipMarkup ?? false,
FilterConfigFile = configModel?.Filter is null ? null : Path.GetFullPath(Path.Combine(EnvironmentContext.BaseDirectory, configModel.Filter)),
IncludePrivateMembers = configModel?.IncludePrivateMembers ?? false,
IncludeExplicitInterfaceImplementations = configModel?.IncludeExplicitInterfaceImplementations ?? false,
GlobalNamespaceId = configModel?.GlobalNamespaceId,
MSBuildProperties = configModel?.Properties,
OutputFormat = configModel?.OutputFormat ?? default,
2 changes: 2 additions & 0 deletions src/Docfx.Dotnet/ManagedReference/ExtractMetadataConfig.cs
Original file line number Diff line number Diff line change
@@ -19,6 +19,8 @@ internal class ExtractMetadataConfig

public bool IncludePrivateMembers { get; init; }

public bool IncludeExplicitInterfaceImplementations { get; init; }

public string GlobalNamespaceId { get; init; }

public string CodeSourceBasePath { get; init; }
8 changes: 8 additions & 0 deletions src/Docfx.Dotnet/MetadataJsonConfig.cs
Original file line number Diff line number Diff line change
@@ -141,6 +141,14 @@ internal class MetadataJsonItemConfig
[JsonPropertyName("includePrivateMembers")]
public bool IncludePrivateMembers { get; set; }

/// <summary>
/// Include explicit interface implementations.
/// The default is false.
/// </summary>
[JsonProperty("includeExplicitInterfaceImplementations")]
[JsonPropertyName("includeExplicitInterfaceImplementations")]
public bool IncludeExplicitInterfaceImplementations { get; set; }

/// <summary>
/// Specify the name to use for the global namespace.
/// </summary>
23 changes: 16 additions & 7 deletions src/Docfx.Dotnet/SymbolFilter.cs
Original file line number Diff line number Diff line change
@@ -92,15 +92,19 @@ private bool IsSymbolAccessible(ISymbol symbol)
if (symbol.IsImplicitlyDeclared && symbol.Kind is not SymbolKind.Namespace)
return false;

if (_config.IncludeExplicitInterfaceImplementations &&
SymbolHelper.TryGetExplicitInterfaceImplementations(symbol, out var eiis) &&
IsEiiAndIncludesContainingSymbols(eiis))
{
return true;
}

if (_config.IncludePrivateMembers)
{
return symbol.Kind switch
{
SymbolKind.Method => IncludesContainingSymbols(((IMethodSymbol)symbol).ExplicitInterfaceImplementations),
SymbolKind.Property => IncludesContainingSymbols(((IPropertySymbol)symbol).ExplicitInterfaceImplementations),
SymbolKind.Event => IncludesContainingSymbols(((IEventSymbol)symbol).ExplicitInterfaceImplementations),
_ => true,
};
if (SymbolHelper.TryGetExplicitInterfaceImplementations(symbol, out eiis))
return IncludesContainingSymbols(eiis);

return true;
}

if (GetDisplayAccessibility(symbol) is null)
@@ -112,5 +116,10 @@ bool IncludesContainingSymbols(IEnumerable<ISymbol> symbols)
{
return !symbols.Any() || symbols.All(s => IncludeApi(s.ContainingSymbol));
}

bool IsEiiAndIncludesContainingSymbols(IEnumerable<ISymbol> symbols)
{
return symbols.Any() && symbols.All(s => IncludeApi(s.ContainingSymbol));
}
}
}
17 changes: 15 additions & 2 deletions src/Docfx.Dotnet/SymbolHelper.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Shared.Extensions;
using System.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis;

#nullable enable

@@ -173,4 +173,17 @@ static bool IsInheritable(ISymbol memberSymbol)
return true;
}
}

public static bool TryGetExplicitInterfaceImplementations(ISymbol symbol, [MaybeNullWhen(false)] out IEnumerable<ISymbol> eiis)
{
eiis = symbol.Kind switch
{
SymbolKind.Method => ((IMethodSymbol)symbol).ExplicitInterfaceImplementations,
SymbolKind.Property => ((IPropertySymbol)symbol).ExplicitInterfaceImplementations,
SymbolKind.Event => ((IEventSymbol)symbol).ExplicitInterfaceImplementations,
_ => null,
};

return eiis != null;
}
}
18 changes: 18 additions & 0 deletions test/Docfx.Dotnet.Tests/GenerateMetadataFromCSUnitTest.cs
Original file line number Diff line number Diff line change
@@ -3691,4 +3691,22 @@ public class Foo : Bar {}
var foo = output.Items[0].Items[0];
Assert.Equal("public class Foo : Bar", foo.Syntax.Content[SyntaxLanguage.CSharp]);
}

[Fact]
public void TestIncludeExplicitInterfaceImplementations()
{
var code =
"""
namespace Test
{
public class Foo : IFoo { void IFoo.Bar(); }
public interface IFoo { void Bar(); }
}
""";

var output = Verify(code, new() { IncludeExplicitInterfaceImplementations = true });
var foo = output.Items[0].Items[0];
Assert.Equal("public class Foo : IFoo", foo.Syntax.Content[SyntaxLanguage.CSharp]);
Assert.Equal("void IFoo.Bar()", foo.Items[0].Syntax.Content[SyntaxLanguage.CSharp]);
}
}

0 comments on commit 0a3bd2c

Please sign in to comment.