From 23189493e36f8e134ae4d3ebd76191f65d15772c Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Fri, 9 Feb 2024 15:37:01 -0700 Subject: [PATCH] Include all base classes Resolves #2486 --- CHANGELOG.md | 5 +-- .../output/themes/default/DefaultTheme.tsx | 18 ++--------- .../themes/default/templates/hierarchy.tsx | 10 ++---- src/lib/output/themes/lib.tsx | 31 +++++++++++++++++++ 4 files changed, 39 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dfb312213..f8f2d3469 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,15 +6,16 @@ - Added support for the `@class` tag. When added to a comment on a variable or function, TypeDoc will convert the member as a class, #2479. Note: This should only be used on symbols which actually represent a class, but are not declared as a class for some reason. - Added support for `@groupDescription` and `@categoryDescription` to provide a description of groups and categories, #2494. -- Exposed `Context.getNodeComment` for plugin use, #2498. +- API: Exposed `Context.getNodeComment` for plugin use, #2498. ## Bug Fixes - Fixed an issue where a namespace would not be created for merged function-namespaces which are declared as variables, #2478. - A class which implements itself will no longer cause a crash when rendering HTML, #2495. - Variable functions which have construct signatures will no longer be converted as functions, ignoring the construct signatures. +- The class hierarchy page will now include classes whose base class is not included in the documentation, #2486. - Fixed an issue where, if the index section was collapsed when loading the page, all content within it would be hidden until expanded, and a member visibility checkbox was changed. -- `Context.programs` will no longer contain duplicates, #2498. +- API: `Context.programs` will no longer contain duplicates, #2498. ## v0.25.7 (2024-01-08) diff --git a/src/lib/output/themes/default/DefaultTheme.tsx b/src/lib/output/themes/default/DefaultTheme.tsx index 301f8d6cf..192ae6a13 100644 --- a/src/lib/output/themes/default/DefaultTheme.tsx +++ b/src/lib/output/themes/default/DefaultTheme.tsx @@ -16,7 +16,7 @@ import type { PageEvent } from "../../events"; import type { MarkedPlugin } from "../../plugins"; import { DefaultThemeRenderContext } from "./DefaultThemeRenderContext"; import { JSX } from "../../../utils"; -import { classNames, getDisplayName, toStyleClass } from "../lib"; +import { classNames, getDisplayName, getHierarchyRoots, toStyleClass } from "../lib"; /** * Defines a mapping of a {@link Models.Kind} to a template file. @@ -155,7 +155,7 @@ export class DefaultTheme extends Theme { urls.push(new UrlMapping("index.html", project, this.indexTemplate)); } - if (includeHierarchyPage(project)) { + if (getHierarchyRoots(project).length) { urls.push(new UrlMapping("hierarchy.html", project, this.hierarchyTemplate)); } @@ -465,17 +465,3 @@ function shouldShowGroups(reflection: Reflection, opts: { includeCategories: boo } return reflection.comment?.hasModifier("@showGroups") === true; } - -function includeHierarchyPage(project: ProjectReflection) { - for (const id in project.reflections) { - const refl = project.reflections[id] as DeclarationReflection; - - if (refl.kindOf(ReflectionKind.ClassOrInterface)) { - // Keep this condition in sync with the one in hierarchy.tsx for determining roots - if (!(refl.implementedTypes || refl.extendedTypes) && (refl.implementedBy || refl.extendedBy)) { - return true; - } - } - } - return false; -} diff --git a/src/lib/output/themes/default/templates/hierarchy.tsx b/src/lib/output/themes/default/templates/hierarchy.tsx index f741aeab8..ee8fbcb1c 100644 --- a/src/lib/output/themes/default/templates/hierarchy.tsx +++ b/src/lib/output/themes/default/templates/hierarchy.tsx @@ -1,7 +1,8 @@ import type { DefaultThemeRenderContext } from "../DefaultThemeRenderContext"; import type { PageEvent } from "../../../events"; import { JSX } from "../../../../utils"; -import { ReflectionKind, type ProjectReflection, DeclarationReflection } from "../../../../models"; +import { getHierarchyRoots } from "../../lib"; +import type { DeclarationReflection, ProjectReflection } from "../../../../models"; function fullHierarchy( context: DefaultThemeRenderContext, @@ -34,15 +35,10 @@ function fullHierarchy( } export function hierarchyTemplate(context: DefaultThemeRenderContext, props: PageEvent) { - // Keep this condition in sync with the one in DefaultTheme.tsx - const roots = (props.project.getReflectionsByKind(ReflectionKind.ClassOrInterface) as DeclarationReflection[]) - .filter((refl) => !(refl.implementedTypes || refl.extendedTypes) && (refl.implementedBy || refl.extendedBy)) - .sort((a, b) => a.name.localeCompare(b.name)); - return ( <>

Class Hierarchy

- {roots.map((root) => ( + {getHierarchyRoots(props.project).map((root) => ( ))} diff --git a/src/lib/output/themes/lib.tsx b/src/lib/output/themes/lib.tsx index ec7dbb12a..4c1605521 100644 --- a/src/lib/output/themes/lib.tsx +++ b/src/lib/output/themes/lib.tsx @@ -157,3 +157,34 @@ export function renderName(refl: Reflection) { return wbr(refl.name); } + +export function getHierarchyRoots(project: ProjectReflection): DeclarationReflection[] { + const allClasses = project.getReflectionsByKind(ReflectionKind.ClassOrInterface) as DeclarationReflection[]; + + const roots = allClasses.filter((refl) => { + // If nobody extends this class, there's no possible hierarchy to display. + if (!refl.implementedBy && !refl.extendedBy) { + return false; + } + + // If we don't extend anything, then we are a root + if (!refl.implementedTypes && !refl.extendedTypes) { + return true; + } + + // We might still be a root, if our extended/implemented types are not included + // in the documentation. + const types = [...(refl.implementedTypes || []), ...(refl.extendedTypes || [])]; + + return types.every( + (type) => + !type.visit({ + reference(ref) { + return ref.reflection !== undefined; + }, + }), + ); + }); + + return roots.sort((a, b) => a.name.localeCompare(b.name)); +}