Skip to content

Commit d671536

Browse files
authored
add a section anchor to the first extension group originating from each contributing module, and modify the sidebar renderer to qualify cross-module links with the origin module (#374)
1 parent f2939d4 commit d671536

17 files changed

+238
-202
lines changed

Package.resolved

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Sources/UnidocUI/Cones/Unidoc.Cone.Halo.Partisanship.swift

Lines changed: 0 additions & 10 deletions
This file was deleted.

Sources/UnidocUI/Cones/Unidoc.Cone.Halo.swift

Lines changed: 93 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ extension Unidoc.Cone
3030
var modules:[Unidoc.Scalar]
3131

3232
private
33-
var extensions:[Unidoc.ExtensionGroup]
33+
var extensions:[[Unidoc.ExtensionGroup]]
3434

3535
private(set)
3636
var peerConstraints:[GenericConstraint<Unidoc.Scalar>]
@@ -103,7 +103,7 @@ extension Unidoc.Cone.Halo
103103
container:Unidoc.Group? = nil,
104104
generics:Generics = .init([])) throws
105105
{
106-
var extensions:[(Unidoc.ExtensionGroup, Partisanship, Generality)] = []
106+
var extensions:[Unidoc.ExtendingModule: [Unidoc.ExtensionGroup]] = [:]
107107
for group:Unidoc.AnyGroup in groups
108108
{
109109
switch group
@@ -116,18 +116,20 @@ extension Unidoc.Cone.Halo
116116
continue
117117
}
118118

119-
let partisanship:Partisanship = self.context[secondary: group.id.edition]
120-
.map
121-
{
122-
.third($0.symbol.package)
123-
} ?? .first
119+
let module:Unidoc.ExtendingModule
124120

125-
let genericness:Generality = group.constraints.isEmpty ?
126-
.unconstrained : generics.count(substituting: group.constraints) > 0 ?
127-
.constrained :
128-
.concretized
121+
if let volume:Unidoc.VolumeMetadata = self.context[secondary: group.id.edition]
122+
{
123+
module = .init(partisanship: .third(volume.symbol.package),
124+
index: group.culture.id.citizen)
125+
}
126+
else
127+
{
128+
module = .init(partisanship: .first,
129+
index: group.culture.id.citizen)
130+
}
129131

130-
extensions.append((group, partisanship, genericness))
132+
extensions[module, default: []].append(group)
131133

132134
case .intrinsic(let group):
133135
if case group.id? = container
@@ -188,30 +190,40 @@ extension Unidoc.Cone.Halo
188190
}
189191
}
190192

191-
extensions.sort
192-
{
193-
// Sort libraries by partisanship, first-party first, then third-party
194-
// by package identifier.
195-
// Then, break ties by extension culture. Module numbers are
196-
// lexicographically ordered according to the package’s internal dependency
197-
// graph, so the library with the lowest module number will always be the
198-
// current culture, if it is present.
199-
// Then, break ties by genericness. Generic extensions come first, concrete
200-
// extensions come last.
201-
// Finally, break ties by extension id. This is arbitrary, but we usually try
202-
// to assign id numbers such that the extensions with the fewest constraints
203-
// come first.
204-
($0.1, $0.0.culture.citizen, $0.2, $0.0.id) <
205-
($1.1, $1.0.culture.citizen, $1.2, $1.0.id)
206-
}
207-
208193
// Prevent the currently-shown page from appearing in the “See Also” section.
209194
let apex:Unidoc.Scalar = self.context.id
210195

211196
curated.insert(apex)
212197
self.curation.removeAll { $0 == apex }
213198

214-
self.extensions = extensions.map { $0.0.subtracting(curated) }
199+
// Sort libraries by partisanship, first-party first, then third-party
200+
// by package identifier.
201+
// Then, break ties by extension culture. Module numbers are
202+
// lexicographically ordered according to the package’s internal dependency
203+
// graph, so the library with the lowest module number will always be the
204+
// current culture, if it is present.
205+
// Then, break ties by genericness. Generic extensions come first, concrete
206+
// extensions come last.
207+
// Finally, break ties by extension id. This is arbitrary, but we usually try
208+
// to assign id numbers such that the extensions with the fewest constraints
209+
// come first.
210+
let extendingModules:[Unidoc.ExtendingModule] = extensions.keys.sorted()
211+
212+
self.extensions = extendingModules.map
213+
{
214+
var groups:[(Unidoc.ExtensionGroup, Generality)] = extensions.removeValue(
215+
forKey: $0)?.map
216+
{
217+
let genericness:Generality = $0.constraints.isEmpty ?
218+
.unconstrained : generics.count(substituting: $0.constraints) > 0 ?
219+
.constrained :
220+
.concretized
221+
return ($0, genericness)
222+
} ?? []
223+
224+
groups.sort { ($0.1, $0.0.id) < ($1.1, $1.0.id) }
225+
return groups.map { $0.0.subtracting(curated) }
226+
}
215227

216228
self.uncategorized.removeAll(where: curated.contains(_:))
217229
self.requirements.removeAll(where: curated.contains(_:))
@@ -366,15 +378,59 @@ extension Unidoc.Cone.Halo:HTML.OutputStreamable
366378
window: extensionsEmpty ? nil : 8 ... 12)
367379
}
368380

369-
for group:Unidoc.ExtensionGroup in self.extensions
381+
for extensions:[Unidoc.ExtensionGroup] in self.extensions
370382
{
371-
html[.section]
383+
var culture:Unidoc.LinkReference<Unidoc.CultureVertex>?
384+
var module:Symbol.Module?
385+
386+
for group:Unidoc.ExtensionGroup in extensions where !group.isEmpty
372387
{
373-
$0.class = "group segregated extension"
374-
} = Unidoc.ExtensionSection.init(group: group,
375-
decl: decl,
376-
bias: self.bias,
377-
with: self.context)
388+
culture = culture ?? self.context[culture: group.culture]
389+
390+
guard
391+
let culture:Unidoc.LinkReference<Unidoc.CultureVertex>
392+
else
393+
{
394+
continue
395+
}
396+
397+
// We only set the section landmark for the first extension group from
398+
// a given culture.
399+
let moduleLeader:Bool
400+
let moduleSymbol:Symbol.Module
401+
402+
if let module:Symbol.Module
403+
{
404+
moduleLeader = false
405+
moduleSymbol = module
406+
}
407+
else
408+
{
409+
moduleLeader = true
410+
moduleSymbol = culture.vertex.module.id
411+
module = moduleSymbol
412+
}
413+
414+
html[.section]
415+
{
416+
$0.class = "group segregated extension"
417+
$0.id = moduleLeader ? "sm:\(moduleSymbol)" : nil
418+
}
419+
content:
420+
{
421+
let header:Unidoc.ExtensionHeader = .init(extension: group,
422+
culture: culture,
423+
module: moduleSymbol,
424+
bias: self.bias,
425+
with: self.context)
426+
427+
$0[.header] = header
428+
$0 += Unidoc.ExtensionBody.init(extension: group,
429+
decl: decl,
430+
name: header.name,
431+
with: self.context)
432+
}
433+
}
378434
}
379435
}
380436
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import Symbols
2+
3+
extension Unidoc.ExtendingModule
4+
{
5+
enum Partisanship:Equatable, Hashable, Comparable
6+
{
7+
case first
8+
case third(Symbol.Package)
9+
}
10+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
extension Unidoc
2+
{
3+
struct ExtendingModule:Equatable, Hashable
4+
{
5+
let partisanship:Partisanship
6+
let index:Int32
7+
8+
init(partisanship:Partisanship, index:Int32)
9+
{
10+
self.partisanship = partisanship
11+
self.index = index
12+
}
13+
}
14+
}
15+
extension Unidoc.ExtendingModule:Comparable
16+
{
17+
static func < (a:Self, b:Self) -> Bool
18+
{
19+
(a.partisanship, a.index) < (b.partisanship, b.index)
20+
}
21+
}

Sources/UnidocUI/Endpoints/Docs/Unidoc.DocsEndpoint.ArticlePage.swift

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,23 @@ extension Unidoc.DocsEndpoint
1010
struct ArticlePage
1111
{
1212
let sidebar:Unidoc.Sidebar<Unidoc.DocsEndpoint>
13+
1314
let cone:Unidoc.Cone
1415
let apex:Unidoc.ArticleVertex
1516

1617
private
1718
let culture:Unidoc.LinkReference<Unidoc.CultureVertex>
1819

19-
init(sidebar:Unidoc.Sidebar<Unidoc.DocsEndpoint>,
20-
cone:Unidoc.Cone,
21-
apex:Unidoc.ArticleVertex) throws
20+
init(cone:Unidoc.Cone, apex:Unidoc.ArticleVertex, tree:Unidoc.TypeTree?) throws
2221
{
23-
self.sidebar = sidebar
24-
self.apex = apex
2522
self.cone = cone
23+
self.apex = apex
24+
2625
self.culture = try self.cone.context[culture: self.apex.culture]
26+
self.sidebar = .module(
27+
volume: self.cone.context.volume,
28+
origin: self.culture.vertex.module.id,
29+
tree: tree)
2730
}
2831
}
2932
}

Sources/UnidocUI/Endpoints/Docs/Unidoc.DocsEndpoint.DeclPage.swift

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,19 @@ extension Unidoc.DocsEndpoint
2525
private
2626
let stem:Unidoc.StemComponents
2727

28-
init(sidebar:Unidoc.Sidebar<Unidoc.DocsEndpoint>,
29-
cone:Unidoc.Cone,
30-
apex:Unidoc.DeclVertex) throws
28+
init(cone:Unidoc.Cone, apex:Unidoc.DeclVertex, tree:Unidoc.TypeTree?) throws
3129
{
32-
self.culture = try cone.context[culture: apex.culture]
3330
self.colony = try apex.colony.map { try cone.context[culture: $0] }
3431
self.stem = try .init(apex.stem)
3532

36-
self.sidebar = sidebar
3733
self.cone = cone
3834
self.apex = apex
35+
36+
self.culture = try self.cone.context[culture: self.apex.culture]
37+
self.sidebar = .module(
38+
volume: self.cone.context.volume,
39+
origin: self.culture.vertex.module.id,
40+
tree: tree)
3941
}
4042
}
4143
}

Sources/UnidocUI/Endpoints/Docs/Unidoc.DocsEndpoint.ModulePage.swift

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,15 @@ extension Unidoc.DocsEndpoint
1313
let cone:Unidoc.Cone
1414
let apex:Unidoc.CultureVertex
1515

16-
init(sidebar:Unidoc.Sidebar<Unidoc.DocsEndpoint>,
17-
cone:Unidoc.Cone,
18-
apex:Unidoc.CultureVertex)
16+
init(cone:Unidoc.Cone, apex:Unidoc.CultureVertex, tree:Unidoc.TypeTree?)
1917
{
20-
self.sidebar = sidebar
21-
self.apex = apex
2218
self.cone = cone
19+
self.apex = apex
20+
21+
self.sidebar = .module(
22+
volume: self.cone.context.volume,
23+
origin: self.apex.module.id,
24+
tree: tree)
2325
}
2426
}
2527
}

Sources/UnidocUI/Endpoints/Docs/Unidoc.DocsEndpoint.swift

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -48,31 +48,21 @@ extension Unidoc.DocsEndpoint:Unidoc.VertexEndpoint
4848
format:Unidoc.RenderFormat) throws -> HTTP.ServerResponse
4949
{
5050
let resource:HTTP.Resource
51-
5251
switch apex
5352
{
5453
case .article(let apex):
55-
let sidebar:Unidoc.Sidebar<Self> = .module(
56-
volume: context.volume,
57-
tree: tree)
5854
let cone:Unidoc.Cone = try .init(context, groups: groups, apex: apex)
59-
let page:ArticlePage = try .init(sidebar: sidebar, cone: cone, apex: apex)
55+
let page:ArticlePage = try .init(cone: cone, apex: apex, tree: tree)
6056
resource = page.resource(format: format)
6157

6258
case .culture(let apex):
63-
let sidebar:Unidoc.Sidebar<Self> = .module(
64-
volume: context.volume,
65-
tree: tree)
6659
let cone:Unidoc.Cone = try .init(context, groups: groups, apex: apex)
67-
let page:ModulePage = .init(sidebar: sidebar, cone: cone, apex: apex)
60+
let page:ModulePage = .init(cone: cone, apex: apex, tree: tree)
6861
resource = page.resource(format: format)
6962

7063
case .decl(let apex):
71-
let sidebar:Unidoc.Sidebar<Self> = .module(
72-
volume: context.volume,
73-
tree: tree)
7464
let cone:Unidoc.Cone = try .init(context, groups: groups, apex: apex)
75-
let page:DeclPage = try .init(sidebar: sidebar, cone: cone, apex: apex)
65+
let page:DeclPage = try .init(cone: cone, apex: apex, tree: tree)
7666
resource = page.resource(format: format)
7767

7868
case .file:

Sources/UnidocUI/Endpoints/Ptcl/Unidoc.PtclEndpoint.ConformersPage.swift

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,21 @@ extension Unidoc.PtclEndpoint
2828
private
2929
let stem:Unidoc.StemComponents
3030

31-
init(sidebar:Unidoc.Sidebar<Unidoc.DocsEndpoint>,
32-
vertex:Unidoc.DeclVertex,
33-
halo:Unidoc.ConformingTypes) throws
31+
init(vertex:Unidoc.DeclVertex,
32+
halo:Unidoc.ConformingTypes,
33+
tree:Unidoc.TypeTree?) throws
3434
{
35-
self.culture = try halo.context[culture: vertex.culture]
3635
self.colony = try vertex.colony.map { try halo.context[culture: $0] }
3736
self.stem = try .init(vertex.stem)
3837

39-
self.sidebar = sidebar
40-
self.vertex = vertex
4138
self.halo = halo
39+
self.vertex = vertex
40+
41+
self.culture = try self.halo.context[culture: self.vertex.culture]
42+
self.sidebar = .module(
43+
volume: self.halo.context.volume,
44+
origin: self.culture.vertex.module.id,
45+
tree: tree)
4246
}
4347
}
4448
}

0 commit comments

Comments
 (0)