From a481dcfb179eb973884c12ec55c33223d5b195d4 Mon Sep 17 00:00:00 2001 From: JoshLove-msft <54595583+JoshLove-msft@users.noreply.github.com> Date: Thu, 3 Nov 2022 18:04:20 -0700 Subject: [PATCH 1/6] Hide EditorBrowsableNever members by default --- .../APIView/APIView/CodeFileRenderer.cs | 11 ++- src/dotnet/APIView/APIView/CodeLine.cs | 9 ++- .../APIView/Languages/CodeFileBuilder.cs | 68 ++++++++++++++++--- .../APIView/Model/CodeFileTokenKind.cs | 4 +- .../APIView/APIView/Model/NavigationItem.cs | 2 + .../APIView/APIViewWeb/Client/css/site.scss | 9 +++ .../APIView/APIViewWeb/Client/src/review.ts | 19 +++++- .../APIViewWeb/Pages/Assemblies/Review.cshtml | 6 ++ .../Pages/Assemblies/_CodeLine.cshtml | 12 +++- .../APIViewWeb/Pages/Shared/Navigation.cshtml | 2 +- 10 files changed, 122 insertions(+), 20 deletions(-) diff --git a/src/dotnet/APIView/APIView/CodeFileRenderer.cs b/src/dotnet/APIView/APIView/CodeFileRenderer.cs index 14b2084ece4..1ba9cd5226b 100644 --- a/src/dotnet/APIView/APIView/CodeFileRenderer.cs +++ b/src/dotnet/APIView/APIView/CodeFileRenderer.cs @@ -34,6 +34,7 @@ private void Render(List list, IEnumerable node, bool s var stringBuilder = new StringBuilder(); string currentId = null; bool isDocumentationRange = false; + bool isHiddenApiRange = false; bool isDeprecatedToken = false; bool isSkipDiffRange = false; Stack nodesInProcess = new Stack(); @@ -56,7 +57,7 @@ private void Render(List list, IEnumerable node, bool s { case CodeFileTokenKind.Newline: int ? sectionKey = (nodesInProcess.Count > 0 && section == null) ? sections.Count: null; - CodeLine codeLine = new CodeLine(stringBuilder.ToString(), currentId, String.Empty, ++lineNumber, sectionKey, isDocumentation: isDocumentationRange); + CodeLine codeLine = new CodeLine(stringBuilder.ToString(), currentId, String.Empty, ++lineNumber, sectionKey, isDocumentation: isDocumentationRange, isHiddenApi: isHiddenApiRange); if (leafSectionPlaceHolderNumber != 0) { lineNumber += leafSectionPlaceHolderNumber - 1; @@ -104,6 +105,14 @@ private void Render(List list, IEnumerable node, bool s isDocumentationRange = false; break; + case CodeFileTokenKind.HiddenApiRangeStart: + isHiddenApiRange = true; + break; + + case CodeFileTokenKind.HiddenApiRangeEnd: + isHiddenApiRange = false; + break; + case CodeFileTokenKind.DeprecatedRangeStart: isDeprecatedToken = true; break; diff --git a/src/dotnet/APIView/APIView/CodeLine.cs b/src/dotnet/APIView/APIView/CodeLine.cs index 94d3226ec09..b70739a26a6 100644 --- a/src/dotnet/APIView/APIView/CodeLine.cs +++ b/src/dotnet/APIView/APIView/CodeLine.cs @@ -13,8 +13,9 @@ namespace ApiView public int Indent { get; } public bool IsDocumentation { get; } public TreeNode NodeRef { get; } + public bool IsHiddenApi { get; } - public CodeLine(string html, string id, string lineClass, int? lineNumber = null, int? sectionKey = null, int indent = 0, bool isDocumentation = false, TreeNode nodeRef = null) + public CodeLine(string html, string id, string lineClass, int? lineNumber = null, int? sectionKey = null, int indent = 0, bool isDocumentation = false, TreeNode nodeRef = null, bool isHiddenApi = false) { this.DisplayString = html; this.ElementId = id; @@ -24,9 +25,10 @@ public CodeLine(string html, string id, string lineClass, int? lineNumber = null this.Indent = indent; this.IsDocumentation = isDocumentation; this.NodeRef = nodeRef; + this.IsHiddenApi = isHiddenApi; } - public CodeLine(CodeLine codeLine, string html = null, string id = null, string lineClass = null, int? lineNumber = null, int? sectionKey = null, int indent = 0, bool isDocumentation = false, TreeNode nodeRef = null) + public CodeLine(CodeLine codeLine, string html = null, string id = null, string lineClass = null, int? lineNumber = null, int? sectionKey = null, int indent = 0, bool isDocumentation = false, TreeNode nodeRef = null, bool isHiddenApi = false) { this.DisplayString = html ?? codeLine.DisplayString; this.ElementId = id ?? codeLine.ElementId; @@ -36,6 +38,7 @@ public CodeLine(CodeLine codeLine, string html = null, string id = null, string this.Indent = (indent != 0)? indent : codeLine.Indent; this.IsDocumentation = (isDocumentation != false)? isDocumentation : codeLine.IsDocumentation; this.NodeRef = nodeRef ?? codeLine.NodeRef; + this.IsHiddenApi = isHiddenApi; } public override string ToString() @@ -45,7 +48,7 @@ public override string ToString() public bool Equals(CodeLine other) { - return DisplayString == other.DisplayString && ElementId == other.ElementId; + return DisplayString == other.DisplayString && ElementId == other.ElementId && other.IsHiddenApi == IsHiddenApi; } } } diff --git a/src/dotnet/APIView/APIView/Languages/CodeFileBuilder.cs b/src/dotnet/APIView/APIView/Languages/CodeFileBuilder.cs index 55234a7a6d3..a5d4f9e3cc6 100644 --- a/src/dotnet/APIView/APIView/Languages/CodeFileBuilder.cs +++ b/src/dotnet/APIView/APIView/Languages/CodeFileBuilder.cs @@ -9,6 +9,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.ComponentModel; using System.IO; using System.Linq; @@ -49,7 +50,7 @@ public class CodeFileBuilder public ICodeFileBuilderSymbolOrderProvider SymbolOrderProvider { get; set; } = new CodeFileBuilderSymbolOrderProvider(); - public const string CurrentVersion = "22"; + public const string CurrentVersion = "23"; private IEnumerable EnumerateNamespaces(IAssemblySymbol assemblySymbol) { @@ -88,12 +89,12 @@ public CodeFile Build(IAssemblySymbol assemblySymbol, bool runAnalysis, List navigationItems) + private void BuildNamespace(CodeFileTokensBuilder builder, INamespaceSymbol namespaceSymbol, List navigationItems) { + bool isHidden = HasOnlyHiddenTypes(namespaceSymbol); + + if (isHidden) + { + builder.Append(null, CodeFileTokenKind.HiddenApiRangeStart); + } builder.Keyword(SyntaxKind.NamespaceKeyword); builder.Space(); BuildNamespaceName(builder, namespaceSymbol); @@ -157,7 +164,7 @@ private void Build(CodeFileTokensBuilder builder, INamespaceSymbol namespaceSymb List namespaceItems = new List(); foreach (var namedTypeSymbol in SymbolOrderProvider.OrderTypes(namespaceSymbol.GetTypeMembers())) { - BuildType(builder, namedTypeSymbol, namespaceItems); + BuildType(builder, namedTypeSymbol, namespaceItems, isHidden); } CloseBrace(builder); @@ -167,9 +174,14 @@ private void Build(CodeFileTokensBuilder builder, INamespaceSymbol namespaceSymb NavigationId = namespaceSymbol.GetId(), Text = namespaceSymbol.ToDisplayString(), ChildItems = namespaceItems.ToArray(), - Tags = { { "TypeKind", "namespace" } } + Tags = { { "TypeKind", "namespace" } }, + IsHiddenApi = isHidden }; navigationItems.Add(namespaceItem); + if (isHidden) + { + builder.Append(null, CodeFileTokenKind.HiddenApiRangeEnd); + } } private void BuildNamespaceName(CodeFileTokensBuilder builder, INamespaceSymbol namespaceSymbol) @@ -184,24 +196,36 @@ private void BuildNamespaceName(CodeFileTokensBuilder builder, INamespaceSymbol private bool HasAnyPublicTypes(INamespaceSymbol subNamespaceSymbol) { - return subNamespaceSymbol.GetTypeMembers().Any(t => IsAccessible(t)); + return subNamespaceSymbol.GetTypeMembers().Any(IsAccessible); } - private void BuildType(CodeFileTokensBuilder builder, INamedTypeSymbol namedType, List navigationBuilder) + private bool HasOnlyHiddenTypes(INamespaceSymbol namespaceSymbol) + { + return namespaceSymbol.GetTypeMembers().All(t=> IsHiddenFromIntellisense(t) || !IsAccessible(t)); + } + + private void BuildType(CodeFileTokensBuilder builder, INamedTypeSymbol namedType, List navigationBuilder, bool inHiddenScope) { if (!IsAccessible(namedType)) { return; } + bool isHidden = IsHiddenFromIntellisense(namedType); var navigationItem = new NavigationItem() { NavigationId = namedType.GetId(), Text = namedType.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat), + IsHiddenApi = isHidden }; navigationBuilder.Add(navigationItem); navigationItem.Tags.Add("TypeKind", namedType.TypeKind.ToString().ToLowerInvariant()); + if (isHidden && !inHiddenScope) + { + builder.Append(null, CodeFileTokenKind.HiddenApiRangeStart); + } + BuildDocumentation(builder, namedType); BuildAttributes(builder, namedType.GetAttributes()); @@ -255,7 +279,7 @@ private void BuildType(CodeFileTokensBuilder builder, INamedTypeSymbol namedType foreach (var namedTypeSymbol in SymbolOrderProvider.OrderTypes(namedType.GetTypeMembers())) { - BuildType(builder, namedTypeSymbol, navigationBuilder); + BuildType(builder, namedTypeSymbol, navigationBuilder, inHiddenScope || isHidden); } foreach (var member in SymbolOrderProvider.OrderMembers(namedType.GetMembers())) @@ -272,10 +296,16 @@ private void BuildType(CodeFileTokensBuilder builder, INamedTypeSymbol namedType continue; } } - BuildMember(builder, member); + + BuildMember(builder, member, inHiddenScope); } CloseBrace(builder); + + if (isHidden && !inHiddenScope) + { + builder.Append(null, CodeFileTokenKind.HiddenApiRangeEnd); + } } private void BuildDocumentation(CodeFileTokensBuilder builder, ISymbol symbol) @@ -363,8 +393,15 @@ private static void CloseBrace(CodeFileTokensBuilder builder) builder.NewLine(); } - private void BuildMember(CodeFileTokensBuilder builder, ISymbol member) + private void BuildMember(CodeFileTokensBuilder builder, ISymbol member, bool inHiddenScope) { + bool isHidden = IsHiddenFromIntellisense(member); + + if (isHidden && !inHiddenScope) + { + builder.Append(null, CodeFileTokenKind.HiddenApiRangeStart); + } + BuildDocumentation(builder, member); BuildAttributes(builder, member.GetAttributes()); @@ -381,6 +418,10 @@ private void BuildMember(CodeFileTokensBuilder builder, ISymbol member) } builder.NewLine(); + if (isHidden && !inHiddenScope) + { + builder.Append(null, CodeFileTokenKind.HiddenApiRangeEnd); + } } private void BuildAttributes(CodeFileTokensBuilder builder, ImmutableArray attributes) @@ -453,12 +494,17 @@ private bool IsSkippedAttribute(INamedTypeSymbol attributeAttributeClass) case "IteratorStateMachineAttribute": case "DefaultMemberAttribute": case "AsyncIteratorStateMachineAttribute": + case "EditorBrowsableAttribute": return true; default: return false; } } + private bool IsHiddenFromIntellisense(ISymbol member) => + member.GetAttributes().Any(d => d.AttributeClass?.Name == "EditorBrowsableAttribute" + && (EditorBrowsableState) d.ConstructorArguments[0].Value == EditorBrowsableState.Never); + private void BuildTypedConstant(CodeFileTokensBuilder builder, TypedConstant typedConstant) { if (typedConstant.IsNull) diff --git a/src/dotnet/APIView/APIView/Model/CodeFileTokenKind.cs b/src/dotnet/APIView/APIView/Model/CodeFileTokenKind.cs index d64808f459d..992b973365e 100644 --- a/src/dotnet/APIView/APIView/Model/CodeFileTokenKind.cs +++ b/src/dotnet/APIView/APIView/Model/CodeFileTokenKind.cs @@ -33,6 +33,8 @@ public enum CodeFileTokenKind TableCellEnd = 26, LeafSectionPlaceholder = 27, ExternalLinkStart = 28, - ExternalLinkEnd = 29 + ExternalLinkEnd = 29, + HiddenApiRangeStart = 30, + HiddenApiRangeEnd = 31 } } diff --git a/src/dotnet/APIView/APIView/Model/NavigationItem.cs b/src/dotnet/APIView/APIView/Model/NavigationItem.cs index 1c0be3a6986..8129f76a1cf 100644 --- a/src/dotnet/APIView/APIView/Model/NavigationItem.cs +++ b/src/dotnet/APIView/APIView/Model/NavigationItem.cs @@ -13,6 +13,8 @@ public class NavigationItem public Dictionary Tags { get; set; } = new Dictionary(0); + public bool IsHiddenApi { get; set; } + public override string ToString() => Text; } } diff --git a/src/dotnet/APIView/APIViewWeb/Client/css/site.scss b/src/dotnet/APIView/APIViewWeb/Client/css/site.scss index 9d3e592b7d1..eb72c050da7 100644 --- a/src/dotnet/APIView/APIViewWeb/Client/css/site.scss +++ b/src/dotnet/APIView/APIViewWeb/Client/css/site.scss @@ -757,6 +757,15 @@ code { text-decoration: line-through; } +.hidden-api .code-inner { + color: var(--text-color-muted) !important; + font-style: italic; + span, a { + color: var(--text-color-muted) !important; + font-style: italic; + } +} + .file-code-icon { filter: var(--icon-color-filter); } diff --git a/src/dotnet/APIView/APIViewWeb/Client/src/review.ts b/src/dotnet/APIView/APIViewWeb/Client/src/review.ts index 1ef2d30341f..010486ace1f 100644 --- a/src/dotnet/APIView/APIViewWeb/Client/src/review.ts +++ b/src/dotnet/APIView/APIViewWeb/Client/src/review.ts @@ -9,15 +9,22 @@ $(() => { const SHOW_DIFFONLY_CHECKBOX = ".show-diffonly-checkbox"; const SHOW_DIFFONLY_HREF = ".show-diffonly"; const TOGGLE_DOCUMENTATION = ".line-toggle-documentation-button"; + const SEL_HIDDEN_CLASS = ".hidden-api-toggleable"; + const SHOW_HIDDEN_CHECK_COMPONENT = "#show-hidden-api-component"; + const SHOW_HIDDEN_CHECKBOX = "#show-hidden-api-checkbox"; + const SHOW_HIDDEN_HREF = ".show-hidden-api"; - hideCheckboxIfNoDocs(); + hideCheckboxesIfNotApplicable(); /* FUNCTIONS --------------------------------------------------------------------------------------------------------------------------------------------------------*/ - function hideCheckboxIfNoDocs() { + function hideCheckboxesIfNotApplicable() { if ($(SEL_DOC_CLASS).length == 0) { $(SHOW_DOC_CHECK_COMPONENT).hide(); } + if ($(SEL_HIDDEN_CLASS).length == 0) { + $(SHOW_HIDDEN_CHECK_COMPONENT).hide(); + } } /* Split left and right review panes using split.js */ @@ -337,6 +344,14 @@ $(() => { $(SHOW_DOC_CHECKBOX)[0].click(); }); + $(SHOW_HIDDEN_CHECKBOX).on("click", e => { + $(SEL_HIDDEN_CLASS).toggleClass("d-none"); + }); + + $(SHOW_HIDDEN_HREF).on("click", e => { + $(SHOW_HIDDEN_CHECKBOX)[0].click(); + }); + $(SHOW_DIFFONLY_CHECKBOX).on("click", e => { $(SHOW_DIFFONLY_HREF)[0].click(); }); diff --git a/src/dotnet/APIView/APIViewWeb/Pages/Assemblies/Review.cshtml b/src/dotnet/APIView/APIViewWeb/Pages/Assemblies/Review.cshtml index 075a76f88ac..545f32ce86e 100644 --- a/src/dotnet/APIView/APIViewWeb/Pages/Assemblies/Review.cshtml +++ b/src/dotnet/APIView/APIViewWeb/Pages/Assemblies/Review.cshtml @@ -196,6 +196,12 @@ + + +