Skip to content

Commit abd581f

Browse files
yufeihp-kostov
authored andcommitted
feat: Add option for including explicit interface implementations (dotnet#9471)
1 parent 03cf14d commit abd581f

File tree

7 files changed

+68
-9
lines changed

7 files changed

+68
-9
lines changed

docs/reference/docfx-json-reference.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,14 @@ Specifies how enum members are sorted:
379379
- `alphabetic` (default): Sort enum members in alphabetic order.
380380
- `declaringOrder`: Sort enum members in the order as they are declared in the source code.
381381

382+
### `includePrivateMembers`
383+
384+
Specifies whether private or internal APIs are included in the generated docs. The default value is `false`.
385+
386+
### `includeExplicitInterfaceImplementations`
387+
388+
Specifies whether explicit interface implementations are included in the generated docs. The default value is `false`.
389+
382390
## `pdf`
383391

384392
Configuration options that are applied for `docfx pdf` command:

src/Docfx.Dotnet/DotnetApiCatalog.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ private static ExtractMetadataConfig ConvertConfig(MetadataJsonItemConfig config
161161
ShouldSkipMarkup = configModel?.ShouldSkipMarkup ?? false,
162162
FilterConfigFile = configModel?.Filter is null ? null : Path.GetFullPath(Path.Combine(EnvironmentContext.BaseDirectory, configModel.Filter)),
163163
IncludePrivateMembers = configModel?.IncludePrivateMembers ?? false,
164+
IncludeExplicitInterfaceImplementations = configModel?.IncludeExplicitInterfaceImplementations ?? false,
164165
GlobalNamespaceId = configModel?.GlobalNamespaceId,
165166
MSBuildProperties = configModel?.Properties,
166167
OutputFormat = configModel?.OutputFormat ?? default,

src/Docfx.Dotnet/ManagedReference/ExtractMetadataConfig.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ internal class ExtractMetadataConfig
1919

2020
public bool IncludePrivateMembers { get; init; }
2121

22+
public bool IncludeExplicitInterfaceImplementations { get; init; }
23+
2224
public string GlobalNamespaceId { get; init; }
2325

2426
public string CodeSourceBasePath { get; init; }

src/Docfx.Dotnet/MetadataJsonConfig.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,14 @@ internal class MetadataJsonItemConfig
141141
[JsonPropertyName("includePrivateMembers")]
142142
public bool IncludePrivateMembers { get; set; }
143143

144+
/// <summary>
145+
/// Include explicit interface implementations.
146+
/// The default is false.
147+
/// </summary>
148+
[JsonProperty("includeExplicitInterfaceImplementations")]
149+
[JsonPropertyName("includeExplicitInterfaceImplementations")]
150+
public bool IncludeExplicitInterfaceImplementations { get; set; }
151+
144152
/// <summary>
145153
/// Specify the name to use for the global namespace.
146154
/// </summary>

src/Docfx.Dotnet/SymbolFilter.cs

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -92,15 +92,19 @@ private bool IsSymbolAccessible(ISymbol symbol)
9292
if (symbol.IsImplicitlyDeclared && symbol.Kind is not SymbolKind.Namespace)
9393
return false;
9494

95+
if (_config.IncludeExplicitInterfaceImplementations &&
96+
SymbolHelper.TryGetExplicitInterfaceImplementations(symbol, out var eiis) &&
97+
IsEiiAndIncludesContainingSymbols(eiis))
98+
{
99+
return true;
100+
}
101+
95102
if (_config.IncludePrivateMembers)
96103
{
97-
return symbol.Kind switch
98-
{
99-
SymbolKind.Method => IncludesContainingSymbols(((IMethodSymbol)symbol).ExplicitInterfaceImplementations),
100-
SymbolKind.Property => IncludesContainingSymbols(((IPropertySymbol)symbol).ExplicitInterfaceImplementations),
101-
SymbolKind.Event => IncludesContainingSymbols(((IEventSymbol)symbol).ExplicitInterfaceImplementations),
102-
_ => true,
103-
};
104+
if (SymbolHelper.TryGetExplicitInterfaceImplementations(symbol, out eiis))
105+
return IncludesContainingSymbols(eiis);
106+
107+
return true;
104108
}
105109

106110
if (GetDisplayAccessibility(symbol) is null)
@@ -112,5 +116,10 @@ bool IncludesContainingSymbols(IEnumerable<ISymbol> symbols)
112116
{
113117
return !symbols.Any() || symbols.All(s => IncludeApi(s.ContainingSymbol));
114118
}
119+
120+
bool IsEiiAndIncludesContainingSymbols(IEnumerable<ISymbol> symbols)
121+
{
122+
return symbols.Any() && symbols.All(s => IncludeApi(s.ContainingSymbol));
123+
}
115124
}
116125
}

src/Docfx.Dotnet/SymbolHelper.cs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
using Microsoft.CodeAnalysis;
2-
using Microsoft.CodeAnalysis.Shared.Extensions;
1+
using System.Diagnostics.CodeAnalysis;
2+
using Microsoft.CodeAnalysis;
33

44
#nullable enable
55

@@ -173,4 +173,17 @@ static bool IsInheritable(ISymbol memberSymbol)
173173
return true;
174174
}
175175
}
176+
177+
public static bool TryGetExplicitInterfaceImplementations(ISymbol symbol, [MaybeNullWhen(false)] out IEnumerable<ISymbol> eiis)
178+
{
179+
eiis = symbol.Kind switch
180+
{
181+
SymbolKind.Method => ((IMethodSymbol)symbol).ExplicitInterfaceImplementations,
182+
SymbolKind.Property => ((IPropertySymbol)symbol).ExplicitInterfaceImplementations,
183+
SymbolKind.Event => ((IEventSymbol)symbol).ExplicitInterfaceImplementations,
184+
_ => null,
185+
};
186+
187+
return eiis != null;
188+
}
176189
}

test/Docfx.Dotnet.Tests/GenerateMetadataFromCSUnitTest.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3691,4 +3691,22 @@ public class Foo : Bar {}
36913691
var foo = output.Items[0].Items[0];
36923692
Assert.Equal("public class Foo : Bar", foo.Syntax.Content[SyntaxLanguage.CSharp]);
36933693
}
3694+
3695+
[Fact]
3696+
public void TestIncludeExplicitInterfaceImplementations()
3697+
{
3698+
var code =
3699+
"""
3700+
namespace Test
3701+
{
3702+
public class Foo : IFoo { void IFoo.Bar(); }
3703+
public interface IFoo { void Bar(); }
3704+
}
3705+
""";
3706+
3707+
var output = Verify(code, new() { IncludeExplicitInterfaceImplementations = true });
3708+
var foo = output.Items[0].Items[0];
3709+
Assert.Equal("public class Foo : IFoo", foo.Syntax.Content[SyntaxLanguage.CSharp]);
3710+
Assert.Equal("void IFoo.Bar()", foo.Items[0].Syntax.Content[SyntaxLanguage.CSharp]);
3711+
}
36943712
}

0 commit comments

Comments
 (0)