Skip to content

Commit 7472d0b

Browse files
committed
Finish implementation of expandable class heirarchy
Resolves #2749 Resolves #2744
1 parent 5a6c9bf commit 7472d0b

File tree

14 files changed

+378
-624
lines changed

14 files changed

+378
-624
lines changed

src/lib/internationalization/locales/en.cts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -484,8 +484,6 @@ export = {
484484
theme_hierarchy: "Hierarchy",
485485
theme_hierarchy_summary: "Hierarchy Summary",
486486
theme_hierarchy_view_summary: "View Summary",
487-
theme_hierarchy_expand: "Expand",
488-
theme_hierarchy_collapse: "Collapse",
489487
theme_implemented_by: "Implemented by",
490488
theme_defined_in: "Defined in",
491489
theme_implementation_of: "Implementation of",
@@ -513,8 +511,12 @@ export = {
513511
theme_permalink: "Permalink",
514512

515513
// Used by the frontend JS
514+
// For the English translations only, these should also be added to
515+
// src/lib/output/themes/default/assets/typedoc/Application.ts
516516
theme_copy: "Copy",
517517
theme_copied: "Copied!",
518518
theme_normally_hidden:
519519
"This member is normally hidden due to your filter settings.",
520+
theme_hierarchy_expand: "Expand",
521+
theme_hierarchy_collapse: "Collapse",
520522
} as const;

src/lib/output/plugins/AssetsPlugin.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ export class AssetsPlugin extends RendererComponent {
3434
copy: this.application.i18n.theme_copy(),
3535
copied: this.application.i18n.theme_copied(),
3636
normally_hidden: this.application.i18n.theme_normally_hidden(),
37+
hierarchy_expand: this.application.i18n.theme_hierarchy_expand(),
38+
hierarchy_collapse:
39+
this.application.i18n.theme_hierarchy_collapse(),
3740
};
3841
}
3942

src/lib/output/plugins/HierarchyPlugin.ts

Lines changed: 69 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,34 @@
11
import * as Path from "path";
22
import { RendererComponent } from "../components.js";
3-
import { PageEvent, RendererEvent } from "../events.js";
4-
import { JSX, writeFile } from "../../utils/index.js";
3+
import { RendererEvent } from "../events.js";
4+
import { writeFile } from "../../utils/index.js";
55
import { DefaultTheme } from "../themes/default/DefaultTheme.js";
66
import { gzip } from "zlib";
77
import { promisify } from "util";
8-
import {
9-
DeclarationReflection,
10-
ReferenceType,
11-
ReflectionKind,
12-
} from "../../models/index.js";
8+
139
import type { Renderer } from "../index.js";
10+
import {
11+
getHierarchyRoots,
12+
getKindClass,
13+
getUniquePath,
14+
} from "../themes/lib.js";
15+
import type { DeclarationReflection } from "../../models/index.js";
1416

1517
const gzipP = promisify(gzip);
1618

17-
export interface HierarchyElement {
18-
html: string;
19-
text: string;
20-
path: string;
21-
parents?: ({ path: string } | { html: string; text: string })[];
22-
children?: ({ path: string } | { html: string; text: string })[];
19+
interface JsonHierarchyElement {
20+
name: string;
21+
kind: number;
22+
url: string;
23+
children?: number[];
24+
uniqueNameParents?: number[];
25+
class: string;
26+
}
27+
28+
interface JsonHierarchy {
29+
// ids of root instances
30+
roots: number[];
31+
reflections: Record<number, JsonHierarchyElement>;
2332
}
2433

2534
export class HierarchyPlugin extends RendererComponent {
@@ -39,60 +48,53 @@ export class HierarchyPlugin extends RendererComponent {
3948
}
4049

4150
private async buildHierarchy(event: RendererEvent) {
42-
const theme = this.owner.theme as DefaultTheme;
43-
44-
const context = theme.getRenderContext(new PageEvent(event.project));
45-
46-
const hierarchy = (
47-
event.project.getReflectionsByKind(
48-
ReflectionKind.ClassOrInterface,
49-
) as DeclarationReflection[]
50-
)
51-
.filter(
52-
(reflection) =>
53-
reflection.extendedTypes?.length ||
54-
reflection.extendedBy?.length,
55-
)
56-
.map((reflection) => ({
57-
html: JSX.renderElement(
58-
context.type(
59-
ReferenceType.createResolvedReference(
60-
reflection.name,
61-
reflection,
62-
reflection.project,
63-
),
64-
),
65-
),
66-
// Full name should be safe here, since this list only includes classes/interfaces.
67-
text: reflection.getFullName(),
68-
path: reflection.url!,
69-
parents: reflection.extendedTypes?.map((type) =>
70-
!(type instanceof ReferenceType) ||
71-
!(type.reflection instanceof DeclarationReflection)
72-
? {
73-
html: JSX.renderElement(context.type(type)),
74-
text: type.toString(),
75-
}
76-
: { path: type.reflection.url! },
77-
),
78-
children: reflection.extendedBy?.map((type) =>
79-
!(type instanceof ReferenceType) ||
80-
!(type.reflection instanceof DeclarationReflection)
81-
? {
82-
html: JSX.renderElement(context.type(type)),
83-
text: type.toString(),
84-
}
85-
: { path: type.reflection.url! },
86-
),
87-
}));
88-
89-
if (!hierarchy.length) return;
90-
91-
hierarchy.forEach((element) => {
92-
if (!element.parents?.length) delete element.parents;
93-
94-
if (!element.children?.length) delete element.children;
95-
});
51+
const project = event.project;
52+
53+
const hierarchy: JsonHierarchy = {
54+
roots: getHierarchyRoots(project).map((refl) => refl.id),
55+
reflections: {},
56+
};
57+
58+
const queue = [...hierarchy.roots];
59+
60+
while (queue.length) {
61+
const id = queue.pop()!;
62+
const refl = project.getReflectionById(id) as DeclarationReflection;
63+
if (id in hierarchy.reflections) continue;
64+
if (!refl.url) continue;
65+
66+
const jsonRecord: JsonHierarchyElement = {
67+
name: refl.name,
68+
kind: refl.kind,
69+
url: refl.url,
70+
class: getKindClass(refl),
71+
};
72+
73+
const path = getUniquePath(refl);
74+
if (path.length > 1) {
75+
jsonRecord.uniqueNameParents = path
76+
.slice(0, -1)
77+
.map((r) => r.id);
78+
queue.push(...jsonRecord.uniqueNameParents);
79+
}
80+
81+
const children = [
82+
...(refl.implementedBy || []),
83+
...(refl.extendedBy || []),
84+
];
85+
86+
for (const child of children) {
87+
if (child.reflection) {
88+
jsonRecord.children ||= [];
89+
jsonRecord.children.push(child.reflection.id);
90+
}
91+
}
92+
if (jsonRecord.children) {
93+
queue.push(...jsonRecord.children);
94+
}
95+
96+
hierarchy.reflections[id] = jsonRecord;
97+
}
9698

9799
const hierarchyJs = Path.join(
98100
event.outputDirectory,

src/lib/output/themes/default/assets/typedoc/Application.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,22 @@ declare global {
66
copy: string;
77
copied: string;
88
normally_hidden: string;
9+
hierarchy_expand: string;
10+
hierarchy_collapse: string;
911
};
1012
}
1113
}
1214

15+
// For debugging with a watch build
16+
window.translations ||= {
17+
copy: "Copy",
18+
copied: "Copied!",
19+
normally_hidden:
20+
"This member is normally hidden due to your filter settings.",
21+
hierarchy_expand: "Expand",
22+
hierarchy_collapse: "Collapse",
23+
};
24+
1325
/**
1426
* Component definition.
1527
*/

0 commit comments

Comments
 (0)