Skip to content

Commit f267598

Browse files
committed
introduce a dedicated outline type for file/source location references, since they are so different
1 parent bb76ec4 commit f267598

File tree

6 files changed

+117
-25
lines changed

6 files changed

+117
-25
lines changed

Sources/SwiftinitPages/Sections/ProseSection.swift

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,25 @@ extension ProseSection:HTML.OutputStreamableMarkdown
4242

4343
switch self.outlines[reference]
4444
{
45-
case .text(let text):
46-
return text
45+
case .file(line: let line, let id):
46+
switch attribute
47+
{
48+
case .href:
49+
return self.context.link(source: id, line: line)?.target
50+
51+
case .src:
52+
guard
53+
let vertex:Unidoc.FileVertex = self.context[file: id]
54+
else
55+
{
56+
return nil
57+
}
58+
59+
return self.context.link(media: vertex)
60+
61+
default:
62+
return nil
63+
}
4764

4865
case .link(https: let url, safe: let safe):
4966
switch attribute
@@ -63,6 +80,7 @@ extension ProseSection:HTML.OutputStreamableMarkdown
6380
}
6481

6582
case .path(_, let scalars):
83+
// We would never have a use for the display text when loading an attribute.
6684
guard
6785
let target:Unidoc.Scalar = scalars.last
6886
else
@@ -75,9 +93,10 @@ extension ProseSection:HTML.OutputStreamableMarkdown
7593
case .href:
7694
return self.context[vertex: target]?.url
7795

96+
// This needs to be here for backwards compatibility with older symbol graphs.
7897
case .src:
7998
guard
80-
case (.file(let vertex), nil)? = self.context[vertex: target]
99+
let vertex:Unidoc.FileVertex = self.context[file: target]
81100
else
82101
{
83102
return nil
@@ -88,6 +107,9 @@ extension ProseSection:HTML.OutputStreamableMarkdown
88107
default:
89108
return nil
90109
}
110+
111+
case .text(let text):
112+
return text
91113
}
92114
}
93115

@@ -101,13 +123,18 @@ extension ProseSection:HTML.OutputStreamableMarkdown
101123

102124
switch self.outlines[reference]
103125
{
126+
case .file(line: let line, let id):
127+
html ?= self.context.link(source: id, line: line)
128+
104129
case .link(https: let url, safe: let safe):
105130
html[.a] { $0.href = "https://\(url)"; $0.rel = safe ? nil : .nofollow } = url
106131

107132
case .text(let text):
108133
html[.code] = text
109134

110135
case .path(let stem, let scalars):
136+
// We never started using path outlines for inline file elements, so we don’t need
137+
// any backwards compatibility adaptors here.
111138
if let scalar:Unidoc.Scalar = scalars.first,
112139
SymbolGraph.Plane.article.contains(scalar.citizen)
113140
{
@@ -134,8 +161,26 @@ extension ProseSection:TextOutputStreamableMarkdown
134161
}
135162
switch self.outlines[reference]
136163
{
137-
case .link:
138-
// No reason this should ever appear here.
164+
case .file(line: let line, let id):
165+
guard
166+
let file:Unidoc.FileVertex = self.context[file: id]
167+
else
168+
{
169+
break
170+
}
171+
172+
utf8 += file.symbol.last.utf8
173+
174+
if let line:Int = line
175+
{
176+
utf8 += ":\(line + 1)".utf8
177+
}
178+
179+
case .link(https: let url, safe: true):
180+
utf8 += "https://\(url)".utf8
181+
182+
case .link(https: _, safe: false):
183+
// We are probably better off not printing the URL at all.
139184
return
140185

141186
case .text(let text):

Sources/SymbolGraphLinker/Resolution/SSGC.Outliner.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,9 @@ extension SSGC.Outliner
164164

165165
if let resource:SSGC.Resource = self.locate(resource: name.string)
166166
{
167-
return self.cache.add(outline: .vertex(resource.id, text: name.string))
167+
// Historical note: we used to encode this as a vertex outline.
168+
return self.cache.add(
169+
outline: .location(.init(position: .zero, file: resource.id)))
168170
}
169171

170172
self.resolver.diagnostics[name.source] = SSGC.ResourceError.fileNotFound(name.string)

Sources/SymbolGraphs/Articles/SymbolGraph.Outline.swift

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ extension SymbolGraph
99
{
1010
case vertex(Int32, text:String)
1111
case vector(Int32, self:Int32, text:String)
12+
case location(SourceLocation<Int32>)
1213
case unresolved(Unresolved)
1314
}
1415
}
@@ -65,6 +66,9 @@ extension SymbolGraph.Outline:BSONDocumentEncodable
6566
bson[.vector_self] = heir
6667
bson[.text] = text
6768

69+
case .location(let self):
70+
bson[.location] = self
71+
6872
case .unresolved(let self):
6973
bson[.location] = self.location
7074

@@ -120,10 +124,16 @@ extension SymbolGraph.Outline:BSONDocumentDecodable
120124
type = .web
121125
link = text
122126
}
123-
else
127+
else if
128+
let text:String = try bson[.unresolved_unidocV3]?.decode()
124129
{
125130
type = .unidocV3
126-
link = try bson[.unresolved_unidocV3].decode()
131+
link = text
132+
}
133+
else
134+
{
135+
self = .location(try bson[.location].decode())
136+
return
127137
}
128138

129139
self = .unresolved(.init(

Sources/SymbolGraphs/SymbolGraphABI.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@ import SemanticVersions
33
@frozen public
44
enum SymbolGraphABI
55
{
6-
@inlinable public static var version:PatchVersion { .v(0, 8, 20) }
6+
@inlinable public static var version:PatchVersion { .v(0, 8, 21) }
77
}

Sources/UnidocLinker/Resolver/Unidoc.Resolver.swift

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -90,23 +90,36 @@ extension Unidoc.Resolver
9090

9191
switch outline
9292
{
93-
case .vertex(let id, text: let text):
94-
if let _:Int = id / .decl,
95-
let id:Unidoc.Scalar = self.current.scalars.decls[id]
93+
case .location(let location):
94+
let line:Int? = location.position == .zero ? nil : location.position.line
95+
return .file(line: line, self.current.id + location.file)
96+
97+
case .vertex(let id, text: let text):
98+
if case SymbolGraph.Plane.decl? = .of(id),
99+
let id:Unidoc.Scalar = self.current.scalars.decls[id]
96100
{
97101
return .path(text, self.context.expand(id, to: words(in: text)))
98102
}
99-
else if let namespace:Int = id / .module,
100-
let id:Unidoc.Scalar = self.current.scalars.modules[namespace]
103+
else if
104+
let namespace:Int = id / .module,
105+
let id:Unidoc.Scalar = self.current.scalars.modules[namespace]
101106
{
102107
return .path(text, [id])
103108
}
109+
else if
110+
case SymbolGraph.Plane.file? = .of(id)
111+
{
112+
// Prior to v0.8.21 of the symbol graph compiler, we encoded references to
113+
// files as vertex outlines. Eventually, the renderer will begin expecting
114+
// file references to appear as file outlines, so we need to prepare for that.
115+
return .file(line: nil, self.current.id + id)
116+
}
104117
else
105118
{
106119
return .path(text, [self.current.id + id])
107120
}
108121

109-
case .vector(let feature, self: let heir, text: let text):
122+
case .vector(let feature, self: let heir, text: let text):
110123
// Only references to declarations can generate vectors. So we can assume
111124
// both components are declaration scalars.
112125
if let feature:Unidoc.Scalar = self.current.scalars.decls[feature],
@@ -115,7 +128,7 @@ extension Unidoc.Resolver
115128
return .path(text, self.context.expand((heir, feature), to: words(in: text)))
116129
}
117130

118-
case .unresolved(let unresolved):
131+
case .unresolved(let unresolved):
119132
let resolution:CodelinkResolver<Unidoc.Scalar>.Overload.Target?
120133
let codelink:Codelink
121134

@@ -217,32 +230,38 @@ extension Unidoc.Resolver
217230
{
218231
switch outline
219232
{
220-
case .vertex(let scalar, text: _):
221-
if let _:Int = scalar / .decl,
222-
let scalar:Unidoc.Scalar = self.current.scalars.decls[scalar]
233+
case .location:
234+
// This doesn’t make sense in the context of a topic.
235+
break
236+
237+
case .vertex(let scalar, text: _):
238+
if let _:Int = scalar / .decl,
239+
let scalar:Unidoc.Scalar = self.current.scalars.decls[scalar]
223240
{
224241
return .scalar(scalar)
225242
}
226-
else if let namespace:Int = scalar / .module,
227-
let scalar:Unidoc.Scalar = self.current.scalars.modules[namespace]
243+
else if
244+
let namespace:Int = scalar / .module,
245+
let scalar:Unidoc.Scalar = self.current.scalars.modules[namespace]
228246
{
229247
return .scalar(scalar)
230248
}
231249
else
232250
{
251+
// TODO: there should be more sanity checks here.
233252
// The rest of the planes don’t cross packages... yet...
234253
return .scalar(self.current.id + scalar)
235254
}
236255

237-
case .vector(let feature, self: _, text: _):
256+
case .vector(let feature, self: _, text: _):
238257
// Only references to declarations can generate vectors. So we can assume
239258
// both components are declaration scalars.
240259
if let feature:Unidoc.Scalar = self.current.scalars.decls[feature]
241260
{
242261
return .scalar(feature)
243262
}
244263

245-
case .unresolved(let unresolved):
264+
case .unresolved(let unresolved):
246265
switch self.resolve(unresolved)
247266
{
248267
case nil:

Sources/UnidocRecords/Volumes/Passages/Unidoc.Outline.swift

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ extension Unidoc
66
@frozen public
77
enum Outline:Equatable, Sendable
88
{
9+
case file(line:Int?, Unidoc.Scalar)
910
/// An external web link. The string does not contain the URL scheme.
1011
case link(https:String, safe:Bool)
1112
case path(String, [Unidoc.Scalar])
@@ -17,6 +18,7 @@ extension Unidoc.Outline
1718
@frozen public
1819
enum CodingKey:String, Sendable
1920
{
21+
case file_line = "L"
2022
case link_safe = "H"
2123
case link_url = "U"
2224
case display = "T"
@@ -34,6 +36,10 @@ extension Unidoc.Outline:BSONDocumentEncodable
3436
bson[.link_safe] = safe ? true : nil
3537
bson[.link_url] = url
3638

39+
case .file(line: let number, let file):
40+
bson[.file_line] = number
41+
bson[.scalars] = [file]
42+
3743
case .path(let string, let scalars):
3844
bson[.scalars] = scalars
3945
bson[.display] = string
@@ -56,10 +62,20 @@ extension Unidoc.Outline:BSONDocumentDecodable
5662
case nil: self = .text(display)
5763
}
5864
}
65+
else if
66+
let url:String = try bson[.link_url]?.decode()
67+
{
68+
self = .link(https: url, safe: try bson[.link_safe]?.decode() ?? false)
69+
}
5970
else
6071
{
61-
self = .link(https: try bson[.link_url].decode(),
62-
safe: try bson[.link_safe]?.decode() ?? false)
72+
let id:Unidoc.Scalar = try bson[.scalars].decode
73+
{
74+
try $0.shape.expect(length: 1)
75+
return try $0[0].decode()
76+
}
77+
78+
self = .file(line: try bson[.file_line]?.decode(), id)
6379
}
6480
}
6581
}

0 commit comments

Comments
 (0)