├── .gitignore
├── .gitmodules
├── LICENSE
├── Package.resolved
├── Package.swift
├── README.md
├── Sources
├── Biome
│ ├── Article
│ │ ├── Article.Excerpt.swift
│ │ ├── Article.Template.swift
│ │ ├── Article.swift
│ │ ├── Extension.Aside.swift
│ │ ├── Extension.Headline.swift
│ │ ├── Extension.Keyword.swift
│ │ ├── Extension.Metadata.swift
│ │ ├── Extension.Renderer.swift
│ │ ├── Extension.Sections.swift
│ │ ├── Extension.swift
│ │ └── SwiftSyntax.swift
│ ├── Ecosystem
│ │ ├── Cache.swift
│ │ ├── DocumentationNode.swift
│ │ ├── Ecosystem.Resolution.swift
│ │ ├── Ecosystem.swift
│ │ ├── Error.swift
│ │ ├── Order.swift
│ │ ├── RSS.swift
│ │ └── Response.swift
│ ├── Module
│ │ ├── Abstractor.swift
│ │ ├── Module.Scope.swift
│ │ └── Module.swift
│ ├── Package
│ │ ├── CulturalBuffer.swift
│ │ ├── History.swift
│ │ ├── Package.Pinned.swift
│ │ ├── Package.Pins.swift
│ │ ├── Package.Versions.swift
│ │ ├── Package.swift
│ │ └── Version.swift
│ ├── Packages
│ │ ├── Beliefs.swift
│ │ ├── Comments.swift
│ │ ├── Packages.Selection.swift
│ │ ├── Packages.swift
│ │ └── Renderer.swift
│ ├── Page
│ │ ├── HTML.swift
│ │ ├── Page html.swift
│ │ ├── Page.Topics.swift
│ │ └── Page.swift
│ ├── Route
│ │ ├── Root.swift
│ │ ├── Route.Key.swift
│ │ ├── Route.Stems.swift
│ │ └── Route.swift
│ ├── Swift
│ │ └── Demangle.swift
│ ├── Symbol.Facts
│ │ ├── Generic.Conditional.swift
│ │ ├── Symbol.Composite.swift
│ │ ├── Symbol.Diacritic.swift
│ │ ├── Symbol.Facts.swift
│ │ ├── Symbol.Role.swift
│ │ └── Symbol.Trait.swift
│ ├── Symbol.Link
│ │ ├── Symbol.Link.Query.swift
│ │ ├── Symbol.Link.Rule.swift
│ │ ├── Symbol.Link.swift
│ │ └── URI.swift
│ └── Symbol
│ │ ├── Symbol.Group.swift
│ │ ├── Symbol.Kind.swift
│ │ ├── Symbol.Scope.swift
│ │ ├── Symbol.Shape.swift
│ │ └── Symbol.swift
├── PackageCatalogs
│ ├── ModuleCatalog.swift
│ ├── PackageCatalog.swift
│ └── SnippetCatalog.swift
├── PackageLoader
│ └── Ecosystem.swift
├── PackageResolution
│ ├── PackageResolution.Pin.swift
│ └── PackageResolution.swift
├── PieCharts
│ └── Pie.swift
├── Preview
│ ├── Listener.swift
│ ├── Main.swift
│ └── Preview.swift
├── SymbolGraphConvert
│ └── Main.swift
├── SymbolGraphs
│ ├── Availability
│ │ ├── Availability.Domain.swift
│ │ ├── Availability.swift
│ │ ├── MaskedVersion.swift
│ │ ├── Platform.swift
│ │ ├── SwiftAvailability.swift
│ │ ├── UnversionedAvailability.swift
│ │ └── VersionedAvailability.swift
│ ├── Graphs
│ │ ├── RawSymbolGraph.swift
│ │ ├── SymbolGraph.Dependency.swift
│ │ ├── SymbolGraph.Edge.swift
│ │ ├── SymbolGraph.Hint.swift
│ │ ├── SymbolGraph.Relationship.swift
│ │ ├── SymbolGraph.SourceFeature.swift
│ │ ├── SymbolGraph.Subgraph.swift
│ │ ├── SymbolGraph.Symbol.swift
│ │ ├── SymbolGraph.Vertex.swift
│ │ └── SymbolGraph.swift
│ ├── IR
│ │ ├── Availability+IR.swift
│ │ ├── Generic+IR.swift
│ │ ├── Generic.Constraint+IR.swift
│ │ ├── Notebook+IR.swift
│ │ ├── SymbolGraph+IR.swift
│ │ └── SymbolGraph.Vertex+IR.swift
│ ├── Identifiers
│ │ ├── ModuleIdentifier.swift
│ │ ├── PackageIdentifier.swift
│ │ ├── Path.swift
│ │ ├── SymbolIdentifier+USR.swift
│ │ ├── SymbolIdentifier.swift
│ │ └── USR.swift
│ └── Source
│ │ ├── AccessLevel.swift
│ │ ├── Community.swift
│ │ ├── Declaration.swift
│ │ ├── Documentation.swift
│ │ ├── Generic.Constraint.swift
│ │ ├── Generic.swift
│ │ ├── Highlight.swift
│ │ └── Notebook.swift
├── URI
│ ├── URI.Rule.swift
│ └── URI.swift
└── Versions
│ ├── MaskedVersion.Rule.swift
│ ├── MaskedVersion.swift
│ └── PreciseVersion.swift
├── screenshots
├── Screenshot from 2022-02-21 09-40-14.png
├── Screenshot from 2022-02-21 09-42-38.png
├── autocomplete-search-demo.apng
├── autocomplete-search-demo.gif
├── cross-module-link-demo.apng
├── cross-module-link-demo.gif
├── cross-module-link-failure.apng
├── crosslink-failure.png
├── fuzziness-demo.apng
├── fuzziness-demo.gif
├── fuzziness-failure.apng
├── fuzziness-failure.gif
├── mobile-view.png
├── network-docc.png
├── network.png
├── screenshot.png
├── screenshot@v0.3.2.png
├── versioning-0.apng
├── versioning-0.gif
├── versioning-1.apng
├── versioning-1.gif
├── versioning-2.apng
├── versioning-2.gif
├── versioning-3.apng
├── versioning-3.gif
├── versioning-4.png
├── versioning-5.png
├── versioning-6.png
├── versioning-7.apng
├── versioning-7.gif
├── versioning-8.apng
├── versioning-8.gif
└── versioning-9.png
└── swift-balanced-trees
├── Benchmarks
├── Package.swift
└── Sources
│ └── ForestBenchmarks
│ └── Main.swift
├── Package.swift
├── Sources
└── Forest
│ ├── Forest.Tree.swift
│ ├── Forest.swift
│ ├── Insert.swift
│ ├── Remove.swift
│ └── Validate.swift
└── Tests
└── ForestTests
└── Main.swift
/.gitignore:
--------------------------------------------------------------------------------
1 | .build/
2 | .resources/
3 | /Packages
4 | .vscode
5 | .biome
6 | .checkouts
7 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "resources"]
2 | path = resources
3 | url = https://github.com/swift-biome/swift-biome-resources.git
4 |
--------------------------------------------------------------------------------
/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "pins" : [
3 | {
4 | "identity" : "swift-argument-parser",
5 | "kind" : "remoteSourceControl",
6 | "location" : "https://github.com/apple/swift-argument-parser.git",
7 | "state" : {
8 | "revision" : "df9ee6676cd5b3bf5b330ec7568a5644f547201b",
9 | "version" : "1.1.3"
10 | }
11 | },
12 | {
13 | "identity" : "swift-atomics",
14 | "kind" : "remoteSourceControl",
15 | "location" : "https://github.com/apple/swift-atomics.git",
16 | "state" : {
17 | "revision" : "919eb1d83e02121cdb434c7bfc1f0c66ef17febe",
18 | "version" : "1.0.2"
19 | }
20 | },
21 | {
22 | "identity" : "swift-backtrace",
23 | "kind" : "remoteSourceControl",
24 | "location" : "https://github.com/swift-server/swift-backtrace.git",
25 | "state" : {
26 | "revision" : "baf9597d126100550f00397fc9d16b1e5b0814a8",
27 | "version" : "1.3.2"
28 | }
29 | },
30 | {
31 | "identity" : "swift-cmark",
32 | "kind" : "remoteSourceControl",
33 | "location" : "https://github.com/apple/swift-cmark.git",
34 | "state" : {
35 | "branch" : "gfm",
36 | "revision" : "6ddaa3991789bd790a9a6004566f99d85bfcb675"
37 | }
38 | },
39 | {
40 | "identity" : "swift-dom",
41 | "kind" : "remoteSourceControl",
42 | "location" : "https://github.com/kelvin13/swift-dom",
43 | "state" : {
44 | "revision" : "e3f2d2d8f8255a0b8ec3728e97ed3bf6fd5ca006",
45 | "version" : "0.5.2"
46 | }
47 | },
48 | {
49 | "identity" : "swift-grammar",
50 | "kind" : "remoteSourceControl",
51 | "location" : "https://github.com/kelvin13/swift-grammar",
52 | "state" : {
53 | "revision" : "9951511e213741b18de71264f22be1392163428e",
54 | "version" : "0.2.0"
55 | }
56 | },
57 | {
58 | "identity" : "swift-hash",
59 | "kind" : "remoteSourceControl",
60 | "location" : "https://github.com/kelvin13/swift-hash.git",
61 | "state" : {
62 | "revision" : "a3cb6b6880d68ce10eec6f0cbd0713402e526583",
63 | "version" : "0.2.3"
64 | }
65 | },
66 | {
67 | "identity" : "swift-highlight",
68 | "kind" : "remoteSourceControl",
69 | "location" : "https://github.com/kelvin13/swift-highlight",
70 | "state" : {
71 | "revision" : "42f606398358cfff129907ea940694742c7ab03c",
72 | "version" : "0.1.4"
73 | }
74 | },
75 | {
76 | "identity" : "swift-json",
77 | "kind" : "remoteSourceControl",
78 | "location" : "https://github.com/kelvin13/swift-json",
79 | "state" : {
80 | "branch" : "master",
81 | "revision" : "3f62c133b96f39289c880f40ce3dce446b025eef"
82 | }
83 | },
84 | {
85 | "identity" : "swift-markdown",
86 | "kind" : "remoteSourceControl",
87 | "location" : "https://github.com/apple/swift-markdown.git",
88 | "state" : {
89 | "branch" : "swift-DEVELOPMENT-SNAPSHOT-2022-08-24-a",
90 | "revision" : "52563fc74a540b29854fde20e836b27394be2749"
91 | }
92 | },
93 | {
94 | "identity" : "swift-nio",
95 | "kind" : "remoteSourceControl",
96 | "location" : "https://github.com/apple/swift-nio.git",
97 | "state" : {
98 | "revision" : "b4e0a274f7f34210e97e2f2c50ab02a10b549250",
99 | "version" : "2.41.1"
100 | }
101 | },
102 | {
103 | "identity" : "swift-resource",
104 | "kind" : "remoteSourceControl",
105 | "location" : "https://github.com/kelvin13/swift-resource",
106 | "state" : {
107 | "revision" : "d1b94b500afd8af35e4396205ea05f2f20b4e990",
108 | "version" : "0.3.2"
109 | }
110 | },
111 | {
112 | "identity" : "swift-syntax",
113 | "kind" : "remoteSourceControl",
114 | "location" : "https://github.com/apple/swift-syntax.git",
115 | "state" : {
116 | "branch" : "swift-DEVELOPMENT-SNAPSHOT-2022-08-24-a",
117 | "revision" : "17b81be00bb918fa8d136eb80fab3d4e642202ad"
118 | }
119 | },
120 | {
121 | "identity" : "swift-system",
122 | "kind" : "remoteSourceControl",
123 | "location" : "https://github.com/apple/swift-system.git",
124 | "state" : {
125 | "revision" : "025bcb1165deab2e20d4eaba79967ce73013f496",
126 | "version" : "1.2.1"
127 | }
128 | },
129 | {
130 | "identity" : "swift-system-extras",
131 | "kind" : "remoteSourceControl",
132 | "location" : "https://github.com/kelvin13/swift-system-extras.git",
133 | "state" : {
134 | "revision" : "42f297a3191e668ed072878d5db3890db8eada50",
135 | "version" : "0.2.0"
136 | }
137 | }
138 | ],
139 | "version" : 2
140 | }
141 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | **Swift Biome has been superseded by [Swift Unidoc](https://github.com/tayloraswift/swift-unidoc)!**
2 |
3 |
4 | biome
0.3.2
5 |
6 |
7 | **`swift-biome`** is a versioned, multi-package Swift documentation compiler.
8 |
9 | Biome is meant to be the back-end component of a web service or a static site generator. Biome handles symbolgraph parsing, dependency resolution, cross-linking, version control, organization, presentation, HTML rendering, and URI routing.
10 |
11 | Biome powers the [swiftinit.org ecosystem documentation](https://swiftinit.org/reference/swift)!
12 |
13 | 
14 |
15 | ## Overview
16 |
17 | Biome is built atop many of the same components as DocC. Its primary input source is the symbolgraph format generated by [`lib/SymbolGraphGen`](https://github.com/apple/swift/tree/main/lib/SymbolGraphGen). It also reads `Package.resolved`, and `Package.catalog`, which is generated by the [`swift-package-catalog`](https://github.com/kelvin13/swift-package-catalog) plugin.
18 |
19 | Since v0.3.1, Biome compiles raw symbolgraphs ahead-of-time into the `ss` file format, which is a more performant, compact, and compression algorithm-friendly symbolgraph representation.
20 |
21 | Biome includes a tool, `swift-symbolgraphc`, which can be used to convert raw symbolgraphs into `ss` files.
22 |
23 | The [`swift-biome-resources`](https://github.com/swift-biome/swift-biome-resources) submodule holds precompiled `ss` files for recent versions of the standard library, and various sources and webpacks for its default frontend.
24 |
25 | The [`ecosystem`](https://github.com/swift-biome/ecosystem) repository is not tracked by this repository, but it contains historical symbolgraphs, `Package.resolved` files, and `Package.catalog` files for select ecosystem packages.
26 |
27 | The `swift-biome` package includes an executable [`swift-nio`](https://github.com/apple/swift-nio)-based target called `preview` which can be used to build and browse docs locally. **This server does not have security features, and is not intended to be used in production**.
28 |
29 | Consumers of `swift-biome` with more sophisticated use-cases are expected to implement their own web server interfacing with Biome via [`WebSemantics`](https://github.com/kelvin13/swift-resource).
30 |
31 | The frontend is written in Sass and Typescript.
32 |
33 | ## Building
34 |
35 | Building Biome requires the `DEVELOPMENT-SNAPSHOT-2022-08-09-a` toolchain. The toolchain requirement is exact due to the way that [`swift-syntax`](https://github.com/apple/swift-syntax.git) and [`swift-markdown`](https://github.com/apple/swift-markdown.git) link to the swift runtime.
36 |
37 | Currently, Biome can only be built on linux. Ubuntu and Amazon Linux 2 are officially supported. The only technical limitations preventing Biome from building on macOS are a handful of file system APIs used by the `PackageLoader` target, and we hope to port Biome to macOS soon.
38 |
39 | ## Constituents
40 |
41 | ### Subpackages
42 |
43 | 1. [**`swift-balanced-trees`**](swift-balanced-trees)
44 |
45 | Provides a red-black forest implementation, used by Biome’s in-memory database. (Biome uses a red-black forest, and not a collection of B-trees because it versions symbols individually.)
46 |
47 | ### Library products
48 |
49 | 1. [**`URI`**](Sources/URI)
50 |
51 | Implements a URI parser and basic URI operations.
52 |
53 | 1. [**`Versions`**](Sources/Versions)
54 |
55 | Implements semantic versions.
56 |
57 | 1. [**`SymbolGraphs`**](Sources/SymbolGraphs)
58 |
59 | Decodes raw symbolgraph fragments emitted by the swift compiler, performs module-local post-processing, and encodes them into symbolgraph files. Also provides type definitions for various source code constructs.
60 |
61 | 1. [**`Biome`**](Sources/Biome)
62 |
63 | Implements the documentation compiler, renderer, and in-memory database.
64 |
65 | 1. [**`PackageResolution`**](Sources/PackageResolution)
66 |
67 | Decodes the `Package.resolved` file format.
68 |
69 | 1. [**`PackageCatalogs`**](Sources/PackageCatalogs)
70 |
71 | Decodes the `Package.catalog` file format, and handles loading and discovery of symbolgraphs, DocC archives, and SPM snippets from the file system. Can also invoke `SymbolGraphs` to compile raw symbolgraph fragments on-the-fly.
72 |
73 | 1. [**`PackageLoader`**](Sources/PackageLoader)
74 |
75 | Invokes `PackageCatalogs` and `Biome`, and provides convenience APIs for adding symbolgraphs from the former to the latter.
76 |
77 | ### Executable products
78 |
79 | 1. [**`Preview`**](Sources/Preview) (`preview`)
80 |
81 | A basic, unsecured `swift-nio`-based server suitable for browsing Biome docs locally.
82 |
83 | 2. [**`SymbolGraphConvert`**](Sources/SymbolGraphConvert) (`swift-symbolgraphc`)
84 |
85 | A command-line interface for invoking `SymbolGraphs`. Supports multithreading.
86 |
87 | ### External dependencies
88 |
89 | 1. [**`swift-grammar`**](https://github.com/kelvin13/swift-grammar)
90 | 1. [**`swift-json`**](https://github.com/kelvin13/swift-json)
91 | 1. [**`swift-highlight`**](https://github.com/kelvin13/swift-highlight)
92 | 1. [**`swift-resource`**](https://github.com/kelvin13/swift-resource)
93 | 1. [**`swift-dom`**](https://github.com/kelvin13/swift-dom)
94 | 1. [`apple/`**`swift-markdown`**](https://github.com/apple/swift-markdown)
95 | 1. [`apple/`**`swift-syntax`**](https://github.com/apple/swift-syntax)
96 |
97 | The swiftinit.org deployment also depends on @Joannis ’s [`mongokitten`](https://github.com/orlandos-nl/MongoKitten), although `swift-biome` itself does not depend on it.
98 |
--------------------------------------------------------------------------------
/Sources/Biome/Article/Article.Excerpt.swift:
--------------------------------------------------------------------------------
1 | extension Article
2 | {
3 | struct Excerpt:Equatable
4 | {
5 | let title:String
6 | let headline:[UInt8]
7 | let snippet:String
8 |
9 | init(_ title:String)
10 | {
11 | self.init(title: title, headline: .init(title.utf8), snippet: "")
12 | }
13 | init(title:String, headline:[UInt8], snippet:String)
14 | {
15 | self.headline = headline
16 | self.snippet = snippet
17 | self.title = title
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Sources/Biome/Article/Article.Template.swift:
--------------------------------------------------------------------------------
1 | import DOM
2 | import HTML
3 |
4 | extension Article
5 | {
6 | struct Template:Equatable where Key:Equatable
7 | {
8 | let errors:[Error]
9 | let summary:DOM.Flattened
10 | let discussion:DOM.Flattened
11 |
12 | // don’t include ``errors``
13 | static
14 | func == (lhs:Self, rhs:Self) -> Bool
15 | {
16 | lhs.summary == rhs.summary && lhs.discussion == rhs.discussion
17 | }
18 |
19 | var isEmpty:Bool
20 | {
21 | self.summary.isEmpty && self.discussion.isEmpty
22 | }
23 |
24 | init()
25 | {
26 | self.errors = []
27 | self.summary = .init()
28 | self.discussion = .init()
29 | }
30 | init(errors:[Error],
31 | summary:DOM.Flattened,
32 | discussion:DOM.Flattened)
33 | {
34 | self.errors = errors
35 | self.summary = summary
36 | self.discussion = discussion
37 | }
38 |
39 | func map(_ transform:(Key) throws -> T)
40 | rethrows -> Template
41 | where T:Hashable
42 | {
43 | return .init(errors: self.errors,
44 | summary: try self.summary.map(transform),
45 | discussion: try self.discussion.map(transform))
46 | }
47 | func transform(
48 | _ transform:(Key, inout [Error]) throws -> DOM.Substitution)
49 | rethrows -> Template
50 | where T:Hashable, Segment:Sequence, Segment.Element == UInt8
51 | {
52 | var errors:[Error] = self.errors
53 | let summary:DOM.Flattened = try self.summary.transform
54 | {
55 | try transform($0, &errors)
56 | }
57 | let discussion:DOM.Flattened = try self.discussion.transform
58 | {
59 | try transform($0, &errors)
60 | }
61 | return .init(errors: errors, summary: summary, discussion: discussion)
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/Sources/Biome/Article/Article.swift:
--------------------------------------------------------------------------------
1 | import struct SymbolGraphs.Path
2 | import HTML
3 |
4 | @usableFromInline
5 | struct Article:Identifiable
6 | {
7 | /// A globally-unique index referencing an article.
8 | ///
9 | /// An article index encodes the module it belongs to, whichs makes it possible
10 | /// to query module membership based on the index alone.
11 | @frozen public
12 | struct Index:CulturalIndex, Sendable
13 | {
14 | public
15 | let module:Module.Index
16 | public
17 | let bits:UInt32
18 |
19 | @inlinable public
20 | var culture:Module.Index
21 | {
22 | self.module
23 | }
24 | @inlinable public
25 | init(_ module:Module.Index, bits:UInt32)
26 | {
27 | self.module = module
28 | self.bits = bits
29 | }
30 | }
31 |
32 | struct Heads
33 | {
34 | @History.Branch.Optional
35 | var excerpt:History.Branch.Head?
36 | @History.Branch.Optional
37 | var documentation:History.Branch.Head?
38 |
39 | init()
40 | {
41 | self._excerpt = .init()
42 | self._documentation = .init()
43 | }
44 | }
45 |
46 | @usableFromInline
47 | struct ID:Hashable, Sendable
48 | {
49 | let key:Route.Key
50 |
51 | init(_ key:Route.Key)
52 | {
53 | self.key = key
54 | }
55 | }
56 |
57 | @usableFromInline
58 | let id:ID
59 | let path:Path
60 | var name:String
61 | {
62 | self.path.last
63 | }
64 | var heads:Heads
65 |
66 | var route:Route.Key
67 | {
68 | self.id.key
69 | }
70 |
71 | init(id:ID, path:Path)
72 | {
73 | self.id = id
74 | self.path = path
75 | self.heads = .init()
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/Sources/Biome/Article/Extension.Headline.swift:
--------------------------------------------------------------------------------
1 | import Markdown
2 | import HTML
3 |
4 | extension Extension
5 | {
6 | enum Headline
7 | {
8 | case implicit
9 | case explicit(Heading)
10 |
11 | var rank:Int
12 | {
13 | switch self
14 | {
15 | case .implicit: return 1
16 | case .explicit: return 0
17 | }
18 | }
19 | var level:Int
20 | {
21 | switch self
22 | {
23 | case .implicit: return 1
24 | case .explicit(let heading): return heading.level
25 | }
26 | }
27 |
28 | var plainText:String
29 | {
30 | switch self
31 | {
32 | case .implicit:
33 | return ""
34 | case .explicit(let headline):
35 | return headline.plainText
36 | }
37 | }
38 | func rendered(as _:UTF8.Type = UTF8.self) -> UTF8
39 | where UTF8:RangeReplaceableCollection, UTF8.Element == UInt8
40 | {
41 | var output:UTF8 = .init()
42 | switch self
43 | {
44 | case .implicit:
45 | break
46 | case .explicit(let headline):
47 | for child:any InlineMarkup in headline.inlineChildren
48 | {
49 | HTML.Element.render(recurring: child).node.rendered(into: &output)
50 | }
51 | }
52 | return output
53 | }
54 | }
55 | }
56 |
57 | extension HTML.Element
58 | {
59 | // `RecurringInlineMarkup` is not a useful abstraction
60 | fileprivate static
61 | func render(recurring inline:any InlineMarkup) -> Self
62 | {
63 | switch inline
64 | {
65 | case is LineBreak:
66 | return .br
67 | case is SoftBreak:
68 | return .init(escaped: " ")
69 |
70 | case let span as CustomInline:
71 | return .init(span.text)
72 | case let text as Text:
73 | return .init(text.string)
74 | case let span as InlineHTML:
75 | return .init(escaped: span.rawHTML)
76 | case let span as InlineCode:
77 | return .code(span.code)
78 | case let span as Emphasis:
79 | return .em(span.inlineChildren.map(Self.render(recurring:)))
80 | case let span as Strikethrough:
81 | return .s(span.inlineChildren.map(Self.render(recurring:)))
82 | case let span as Strong:
83 | return .strong(span.inlineChildren.map(Self.render(recurring:)))
84 | case let span as Markdown.Image:
85 | return .span(span.inlineChildren.map(Self.render(recurring:)))
86 | case let span as Link:
87 | return .span(span.inlineChildren.map(Self.render(recurring:)))
88 | case let link as SymbolLink:
89 | return .code(link.destination ?? "")
90 | default:
91 | fatalError("unreachable")
92 | }
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/Sources/Biome/Article/Extension.Keyword.swift:
--------------------------------------------------------------------------------
1 | import Markdown
2 |
3 | extension ListItem
4 | {
5 | func recognize(where keyword:(Substring) throws -> Keyword?)
6 | rethrows -> (Keyword, [any BlockMarkup])?
7 | {
8 | var blocks:[any BlockMarkup] = .init(self.blockChildren)
9 |
10 | guard let paragraph:any BlockMarkup = blocks.first,
11 | let paragraph:Paragraph = paragraph as? Paragraph
12 | else
13 | {
14 | return nil
15 | }
16 |
17 | let magic:Keyword
18 | var inline:[any InlineMarkup] = .init(paragraph.inlineChildren)
19 | // find the first colon among the first two inline elements
20 | guard let first:any InlineMarkup = inline.first
21 | else
22 | {
23 | return nil
24 | }
25 | if let text:Text = first as? Text
26 | {
27 | // 'keyword: blah blah blah'
28 | let string:String = text.string
29 | guard let colon:String.Index = string.firstIndex(of: ":"),
30 | let keyword:Keyword = try keyword(string[..(_ string:S) where S:StringProtocol, S.SubSequence == Substring
103 | {
104 | if let aside:Aside = .init(string)
105 | {
106 | self = .aside(aside)
107 | return
108 | }
109 |
110 | let words:[Substring] = string.split(whereSeparator: \.isWhitespace)
111 |
112 | guard let keyword:String = words.first?.lowercased()
113 | else
114 | {
115 | return nil
116 | }
117 | if words.count == 2
118 | {
119 | if keyword == "parameter"
120 | {
121 | self = .parameter(String.init(words[1]))
122 | }
123 | else
124 | {
125 | return nil
126 | }
127 | }
128 | else if words.count == 1
129 | {
130 | switch keyword
131 | {
132 | case "parameters": self = .parameters
133 | case "returns": self = .returns
134 | default: self = .other(String.init(words[0]))
135 | }
136 | }
137 | else
138 | {
139 | return nil
140 | }
141 | }
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/Sources/Biome/Article/Extension.Metadata.swift:
--------------------------------------------------------------------------------
1 | import struct SymbolGraphs.Path
2 | import Markdown
3 |
4 | extension Extension
5 | {
6 | struct Metadata
7 | {
8 | var path:Path?
9 | var imports:Set
10 | var errors:[DirectiveArgumentText.ParseError]
11 |
12 | var noTitle:Bool
13 |
14 | init(directives:[BlockDirective])
15 | {
16 | self.path = nil
17 | self.errors = []
18 | self.imports = []
19 |
20 | self.noTitle = false
21 |
22 | let directives:[String: [BlockDirective]] = .init(grouping: directives, by: \.name)
23 | // @notitle
24 | if let anything:[BlockDirective] = directives["notitle"], !anything.isEmpty
25 | {
26 | self.noTitle = true
27 | }
28 | // @import(_:)
29 | if let matches:[BlockDirective] = directives["import"]
30 | {
31 | for invocation:BlockDirective in matches
32 | {
33 | guard let imported:Substring = invocation.argumentText.segments.first?.trimmedText
34 | else
35 | {
36 | continue
37 | }
38 | self.imports.insert(Module.ID.init(imported))
39 | }
40 | }
41 | // @path(_:)
42 | if let matches:[BlockDirective] = directives["path"],
43 | let match:BlockDirective = matches.last
44 | {
45 | self.path = .init(match.argumentText.segments
46 | .map(\.trimmedText)
47 | .joined()
48 | .split(separator: "/")
49 | .map(String.init(_:)))
50 | }
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/Sources/Biome/Article/SwiftSyntax.swift:
--------------------------------------------------------------------------------
1 | import enum SymbolGraphs.Highlight
2 | import SwiftSyntaxParser
3 | import SwiftSyntax
4 |
5 | extension Extension.Renderer
6 | {
7 | enum CodeBlockLanguage:String
8 | {
9 | case swift = "swift"
10 | case text = "text"
11 | }
12 |
13 | static
14 | func highlight(_ code:String) -> [(text:String, color:Highlight)]
15 | {
16 | do
17 | {
18 | return Self.highlight(tree: .init(try SyntaxParser.parse(source: code)))
19 | }
20 | catch let error
21 | {
22 | return
23 | [
24 | ("// highlighting error:", .comment),
25 | ("\n", .newlines),
26 | ("// \(error)", .comment),
27 | ]
28 | +
29 | code.split(separator: "\n", omittingEmptySubsequences: false).map
30 | {
31 | [
32 | ("\n", .newlines),
33 | (String.init($0), .comment),
34 | ]
35 | }.joined()
36 | }
37 | }
38 | private static
39 | func highlight(tree:Syntax) -> [(text:String, color:Highlight)]
40 | {
41 | var highlights:[(text:String, color:Highlight)] = []
42 | for token:TokenSyntax in tree.tokens(viewMode: .sourceAccurate)
43 | {
44 | for trivia:TriviaPiece in token.leadingTrivia
45 | {
46 | highlights.append(Self.highlight(trivia: trivia))
47 | }
48 | if !token.text.isEmpty
49 | {
50 | highlights.append(Self.highlight(token: token))
51 | }
52 | for trivia:TriviaPiece in token.trailingTrivia
53 | {
54 | highlights.append(Self.highlight(trivia: trivia))
55 | }
56 | }
57 | // strip trailing newlines
58 | while case .newlines? = highlights.last?.color
59 | {
60 | highlights.removeLast()
61 | }
62 | return highlights
63 | }
64 | private static
65 | func highlight(token:TokenSyntax) -> (text:String, color:Highlight)
66 | {
67 | let color:Highlight
68 | switch token.tokenClassification.kind
69 | {
70 | case .keyword:
71 | switch token.tokenKind
72 | {
73 | case .initKeyword,
74 | .deinitKeyword,
75 | .subscriptKeyword: color = .keywordIdentifier
76 | default: color = .keywordText
77 | }
78 | case .none: color = .text
79 |
80 | case .identifier: color = .identifier
81 | case .typeIdentifier: color = .type
82 | case .dollarIdentifier: color = .pseudo
83 | case .integerLiteral: color = .number
84 | case .floatingLiteral: color = .number
85 | case .stringLiteral: color = .string
86 | case .stringInterpolationAnchor: color = .interpolation
87 | case .poundDirectiveKeyword: color = .directive
88 | case .buildConfigId: color = .keywordDirective
89 | case .attribute: color = .attribute
90 | // only used by xcode
91 | case .objectLiteral: color = .text
92 | case .editorPlaceholder: color = .text
93 | case .lineComment, .blockComment: color = .comment
94 | case .docLineComment, .docBlockComment: color = .documentationComment
95 | }
96 | return (token.text, color)
97 | }
98 | private static
99 | func highlight(trivia:TriviaPiece) -> (text:String, color:Highlight)
100 | {
101 | switch trivia
102 | {
103 | case .unexpectedText(let text):
104 | return (text, .invalid)
105 | case .shebang(let text):
106 | return (text, .text)
107 | case .spaces(let count):
108 | return (.init(repeating: " ", count: count), .text)
109 | case .tabs(let count):
110 | return (.init(repeating: " ", count: count * 4), .text)
111 | case .verticalTabs(let count), .formfeeds(let count):
112 | return (.init(repeating: " ", count: count), .text)
113 | case .newlines(let count), .carriageReturns(let count), .carriageReturnLineFeeds(let count):
114 | return (.init(repeating: "\n", count: count), .newlines)
115 | case .lineComment(let string), .blockComment(let string):
116 | return (string, .comment)
117 | case .docLineComment(let string), .docBlockComment(let string):
118 | return (string, .documentationComment)
119 | }
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/Sources/Biome/Ecosystem/Cache.swift:
--------------------------------------------------------------------------------
1 | import JSON
2 | import Resources
3 | import URI
4 |
5 | struct Cache
6 | {
7 | let sitemap:Resource
8 | let search:Resource
9 | }
10 |
11 | extension Ecosystem
12 | {
13 | func generateSearchIndex(for package:Package.Index) -> Resource
14 | {
15 | let current:Package.Pinned = self[package].pinned()
16 | let modules:[JSON] = current.package.modules.all.map
17 | {
18 | var types:[JSON] = []
19 | for colony:Symbol.ColonialRange in $0.symbols
20 | {
21 | for offset:Int in colony.offsets
22 | {
23 | let index:Symbol.Index = .init($0.index, offset: offset)
24 |
25 | let symbol:Symbol = current.package[local: index]
26 | switch symbol.community
27 | {
28 | case .protocol, .typealias, .concretetype(_), .global(_):
29 | break
30 | case .associatedtype, .callable(_):
31 | continue
32 | }
33 | guard current.package.contains(index, at: current.version)
34 | else
35 | {
36 | continue
37 | }
38 |
39 | let declaration:Declaration = current.declaration(index)
40 | let uri:URI = self.uri(of: .init(natural: index), in: current)
41 | let keywords:[JSON] = declaration.signature.compactMap
42 | {
43 | switch $0.color
44 | {
45 | case .identifier, .keywordIdentifier, .argument:
46 | return JSON.string($0.text)
47 | default:
48 | return nil
49 | }
50 | }
51 | types.append(.object(
52 | [
53 | ("s", .array(keywords)),
54 | ("t", .string(symbol.description)),
55 | ("u", .string(uri.description)),
56 | ]))
57 | }
58 | }
59 | let module:String = .init($0.title)
60 | return .object([("module", .string(module)), ("symbols", .array(types))])
61 | }
62 | let json:JSON = .array(_move modules)
63 | let bytes:[UInt8] = .init((_move json).description.utf8)
64 | return .init(hashing: bytes, type: .utf8(encoded: .json))
65 | }
66 |
67 | func generateSiteMap(for package:Package.Index) -> Resource
68 | {
69 | let domain:String.UTF8View = "https://swiftinit.org".utf8
70 | let current:Package.Pinned = self[package].pinned()
71 | // only include natural symbols in a sitemap, since google is likely to
72 | // consider the synthesized ones non-canonical
73 | var sitemap:[UInt8] = []
74 | for module:Module in current.package.modules.all
75 | {
76 | let uri:URI = self.uri(of: module.index, in: current)
77 | sitemap += domain
78 | sitemap += uri.description.utf8
79 | sitemap.append(0x0a) // '\n'
80 | }
81 | for module:Module in current.package.modules.all
82 | {
83 | for article:Article.Index in module.articles.joined()
84 | {
85 | let uri:URI = self.uri(of: article, in: current)
86 | sitemap += domain
87 | sitemap += uri.description.utf8
88 | sitemap.append(0x0a) // '\n'
89 | }
90 | for colony:Symbol.ColonialRange in module.symbols
91 | {
92 | for offset:Int in colony.offsets
93 | {
94 | let index:Symbol.Index = .init(module.index, offset: offset)
95 | guard current.package.contains(index, at: current.version)
96 | else
97 | {
98 | continue
99 | }
100 |
101 | let uri:URI = self.uri(of: .init(natural: index),
102 | in: current)
103 | sitemap += domain
104 | sitemap += uri.description.utf8
105 | sitemap.append(0x0a) // '\n'
106 | }
107 | }
108 | }
109 | return .init(hashing: sitemap, type: .utf8(encoded: .plain))
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/Sources/Biome/Ecosystem/Error.swift:
--------------------------------------------------------------------------------
1 | import SymbolGraphs
2 |
3 | enum DependencyError:Error
4 | {
5 | case packageNotFound(Package.ID)
6 | case moduleNotFound(Module.ID, in:Package.ID)
7 |
8 | case packageCycle
9 | case moduleCycle(in:Package.ID)
10 | }
11 |
12 | extension Ecosystem
13 | {
14 | struct LinkResolutionError:Error
15 | {
16 | let link:String
17 | let error:Error
18 | }
19 | }
20 | extension Packages
21 | {
22 | enum SelectionError:Error
23 | {
24 | case none
25 | case many([Symbol.Composite])
26 | }
27 | }
28 | extension Symbol
29 | {
30 | public
31 | enum LookupError:Error, CustomStringConvertible
32 | {
33 | case unknownID(ID)
34 |
35 | public
36 | var description:String
37 | {
38 | switch self
39 | {
40 | case .unknownID(let id):
41 | return "could not find symbol with id '\(id)'"
42 | }
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/Sources/Biome/Ecosystem/Order.swift:
--------------------------------------------------------------------------------
1 | import SymbolGraphs
2 |
3 | extension Ecosystem
4 | {
5 | static
6 | func order(modules:[SymbolGraph], package:Package.ID) throws -> [SymbolGraph]
7 | {
8 | // collect intra-package dependencies
9 | var dependencies:[Module.ID: Set] = [:]
10 | for module:SymbolGraph in modules
11 | {
12 | for dependency:SymbolGraph.Dependency in module.dependencies
13 | where package == dependency.package && !dependency.modules.isEmpty
14 | {
15 | dependencies[module.id, default: []].formUnion(dependency.modules)
16 | }
17 | }
18 | var consumers:[Module.ID: [SymbolGraph]] = [:]
19 | for module:SymbolGraph in modules
20 | {
21 | guard let dependencies:Set = dependencies[module.id]
22 | else
23 | {
24 | continue
25 | }
26 | // need to sort dependency set to make topological sort deterministic
27 | for dependency:Module.ID in dependencies.sorted()
28 | {
29 | consumers[dependency, default: []].append(module)
30 | }
31 | }
32 |
33 | var graphs:[SymbolGraph] = []
34 | graphs.reserveCapacity(modules.count)
35 | // perform topological sort
36 | var sources:[SymbolGraph] = modules.compactMap
37 | {
38 | dependencies[$0.id, default: []].isEmpty ? $0 : nil
39 | }
40 | while let source:SymbolGraph = sources.popLast()
41 | {
42 | graphs.append(source)
43 |
44 | guard let next:[SymbolGraph] = consumers.removeValue(forKey: source.id)
45 | else
46 | {
47 | continue
48 | }
49 | for next:SymbolGraph in next
50 | {
51 | guard let index:Dictionary>.Index =
52 | dependencies.index(forKey: next.id)
53 | else
54 | {
55 | // already added module to sorted output
56 | continue
57 | }
58 | dependencies.values[index].remove(source.id)
59 | if dependencies.values[index].isEmpty
60 | {
61 | dependencies.remove(at: index)
62 | sources.append(next)
63 | }
64 | }
65 | }
66 | if dependencies.isEmpty, consumers.isEmpty
67 | {
68 | return graphs
69 | }
70 | else
71 | {
72 | throw DependencyError.moduleCycle(in: package)
73 | }
74 | }
75 | }
--------------------------------------------------------------------------------
/Sources/Biome/Ecosystem/RSS.swift:
--------------------------------------------------------------------------------
1 | import RSS
2 | import URI
3 |
4 | extension Ecosystem
5 | {
6 | public
7 | func generateRssFeed(for module:Module.Index, domain:String) -> [RSS.Element]
8 | {
9 | let pinned:Package.Pinned = self[module.package].pinned()
10 | return pinned.package[local: module].articles.joined().map
11 | {
12 | let excerpt:Article.Excerpt = pinned.excerpt($0)
13 | let uri:URI = self.uri(of: $0, in: pinned)
14 | return .item(
15 | .title(excerpt.title),
16 | .description(excerpt.snippet),
17 | .link("https://\(domain)\(uri.description)"))
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Sources/Biome/Module/Module.Scope.swift:
--------------------------------------------------------------------------------
1 | extension Module
2 | {
3 | // the endpoints of a graph edge can reference symbols in either this
4 | // package or one of its dependencies. since imports are module-wise, and
5 | // not package-wise, it’s possible for multiple index dictionaries to
6 | // return matches, as long as only one of them belongs to an depended-upon module.
7 | //
8 | // it’s also possible to prefer a dictionary result in a foreign package over
9 | // a dictionary result in the local package, if the foreign package contains
10 | // a module that shadows one of the modules in the local package (as long
11 | // as the target itself does not also depend upon the shadowed local module.)
12 | struct Scope
13 | {
14 | private
15 | var namespaces:[ID: Index]
16 | private(set)
17 | var filter:Set
18 | let culture:Index
19 |
20 | subscript(namespace:ID) -> Index?
21 | {
22 | _read
23 | {
24 | yield self.namespaces[namespace]
25 | }
26 | }
27 |
28 | private
29 | init(culture:Index, namespaces:[ID: Index])
30 | {
31 | self.culture = culture
32 | self.namespaces = namespaces
33 | self.filter = .init(namespaces.values)
34 | }
35 | init(culture:Index, id:ID)
36 | {
37 | self.init(culture: culture, namespaces: [id: culture])
38 | }
39 |
40 | mutating
41 | func insert(_ namespace:Index, id:ID)
42 | {
43 | self.namespaces[id] = namespace
44 | self.filter.insert(namespace)
45 | }
46 |
47 | func contains(_ namespace:ID) -> Bool
48 | {
49 | self.namespaces.keys.contains(namespace)
50 | }
51 | func contains(_ namespace:Index) -> Bool
52 | {
53 | self.filter.contains(namespace)
54 | }
55 |
56 | func dependencies() -> Set
57 | {
58 | var dependencies:Set = self.filter
59 | dependencies.remove(self.culture)
60 | return dependencies
61 | }
62 |
63 | func `import`(_ modules:Set, swift:Package.Index?) -> Self
64 | {
65 | .init(culture: self.culture, namespaces: self.namespaces.filter
66 | {
67 | if case $0.value.package? = swift
68 | {
69 | return true
70 | }
71 | else if $0.value == self.culture
72 | {
73 | return true
74 | }
75 | else
76 | {
77 | return modules.contains($0.key)
78 | }
79 | })
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/Sources/Biome/Module/Module.swift:
--------------------------------------------------------------------------------
1 | import SymbolGraphs
2 | import Notebook
3 | import URI
4 |
5 | public
6 | struct Module:Identifiable, Sendable
7 | {
8 | /// A globally-unique index referencing a module.
9 | @frozen public
10 | struct Index:CulturalIndex, Sendable
11 | {
12 | public
13 | let package:Package.Index
14 | public
15 | let bits:UInt16
16 |
17 | @inlinable public
18 | var culture:Package.Index
19 | {
20 | self.package
21 | }
22 | @inlinable public
23 | init(_ package:Package.Index, bits:UInt16)
24 | {
25 | self.package = package
26 | self.bits = bits
27 | }
28 | }
29 |
30 | enum Culture:Hashable, Sendable
31 | {
32 | case primary
33 | case accepted(Index)
34 | case international(Index)
35 | }
36 |
37 | struct Pin:Hashable, Sendable
38 | {
39 | var culture:Index
40 | var version:Version
41 | }
42 |
43 | struct Heads
44 | {
45 | @History>.Branch.Optional
46 | var dependencies:History>.Branch.Head?
47 | @History>.Branch.Optional
48 | var toplevel:History>.Branch.Head?
49 | @History>.Branch.Optional
50 | var guides:History>.Branch.Head?
51 | @History.Branch.Optional
52 | var documentation:History.Branch.Head?
53 |
54 | init()
55 | {
56 | self._dependencies = .init()
57 | self._toplevel = .init()
58 | self._guides = .init()
59 | self._documentation = .init()
60 | }
61 | }
62 |
63 | typealias Redirect = (uri:URI, version:Version)
64 |
65 | public
66 | let id:ModuleIdentifier
67 | let index:Index
68 |
69 | var symbols:[Symbol.ColonialRange]
70 | var articles:[Range]
71 |
72 | var heads:Heads
73 | var redirect:(module:Redirect?, articles:Redirect?)
74 |
75 | /// this module’s exact identifier string, e.g. '_Concurrency'
76 | var name:String
77 | {
78 | self.id.string
79 | }
80 | /// this module’s identifier string with leading underscores removed, e.g. 'Concurrency'
81 | var title:Substring
82 | {
83 | self.id.title
84 | }
85 | var path:Path
86 | {
87 | .init(last: self.id.string)
88 | }
89 |
90 | init(id:ID, index:Index)
91 | {
92 | self.id = id
93 | self.index = index
94 | self.symbols = []
95 | self.articles = []
96 |
97 | self.heads = .init()
98 | self.redirect = (nil, nil)
99 | }
100 |
101 | var fragments:[Notebook.Fragment]
102 | {
103 | [
104 | .init("import", color: .keywordText),
105 | .init(" ", color: .text),
106 | .init(self.name, color: .identifier),
107 | ]
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/Sources/Biome/Package/CulturalBuffer.swift:
--------------------------------------------------------------------------------
1 | public
2 | protocol CulturalIndex:Strideable, Hashable
3 | {
4 | associatedtype Culture:Hashable
5 | associatedtype Bits:UnsignedInteger
6 |
7 | var culture:Culture { get }
8 | var bits:Bits { get }
9 |
10 | init(_ culture:Culture, bits:Bits)
11 | }
12 | extension CulturalIndex
13 | {
14 | init(_ culture:Culture, offset:Int)
15 | {
16 | self.init(culture, bits: .init(offset))
17 | }
18 | var offset:Int
19 | {
20 | .init(self.bits)
21 | }
22 | // *really* weird shit happens if we don’t provide these implementations,
23 | // because by default, ``Strideable`` ignores the `culture` property...
24 | @inlinable public static
25 | func == (lhs:Self, rhs:Self) -> Bool
26 | {
27 | lhs.bits == rhs.bits && lhs.culture == rhs.culture
28 | }
29 | @inlinable public static
30 | func < (lhs:Self, rhs:Self) -> Bool
31 | {
32 | lhs.bits < rhs.bits
33 | }
34 | @inlinable public
35 | func hash(into hasher:inout Hasher)
36 | {
37 | self.culture.hash(into: &hasher)
38 | self.bits.hash(into: &hasher)
39 | }
40 |
41 | @inlinable public
42 | func advanced(by stride:Bits.Stride) -> Self
43 | {
44 | .init(self.culture, bits: self.bits.advanced(by: stride))
45 | }
46 | @inlinable public
47 | func distance(to other:Self) -> Bits.Stride
48 | {
49 | self.bits.distance(to: other.bits)
50 | }
51 | }
52 |
53 | struct CulturalBuffer where Index:CulturalIndex, Element:Identifiable
54 | {
55 | private
56 | var storage:[Element]
57 | private(set)
58 | var indices:[Element.ID: Index]
59 |
60 | var all:[Element]
61 | {
62 | _read
63 | {
64 | yield self.storage
65 | }
66 | }
67 |
68 | init()
69 | {
70 | self.storage = []
71 | self.indices = [:]
72 | }
73 |
74 | // in general we use ``count`` and not `endIndex` because cultural indices
75 | // are defined in terms of offsets ...
76 | var count:Int
77 | {
78 | self.storage.count
79 | }
80 |
81 | subscript(local index:Index) -> Element
82 | {
83 | _read
84 | {
85 | yield self.storage[index.offset]
86 | }
87 | _modify
88 | {
89 | yield &self.storage[index.offset]
90 | }
91 | }
92 |
93 | mutating
94 | func insert(_ id:Element.ID, culture:Index.Culture,
95 | _ create:(Element.ID, Index) throws -> Element) rethrows -> Index
96 | {
97 | if let index:Index = self.indices[id]
98 | {
99 | return index
100 | }
101 | else
102 | {
103 | // create records for elements if they do not yet exist
104 | let index:Index = .init(culture, offset: self.count)
105 | self.storage.append(try create(id, index))
106 | self.indices[id] = index
107 | return index
108 | }
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/Sources/Biome/Package/History.swift:
--------------------------------------------------------------------------------
1 | import Forest
2 |
3 | struct History where Value:Equatable
4 | {
5 | typealias Index = Forest.Index
6 |
7 | struct Keyframe
8 | {
9 | var versions:ClosedRange
10 | let value:Value
11 |
12 | init(_ value:Value, version:Version)
13 | {
14 | self.value = value
15 | self.versions = version ... version
16 | }
17 | }
18 |
19 | private
20 | var forest:Forest
21 |
22 | private(set)
23 | subscript(index:Index) -> Keyframe
24 | {
25 | _read
26 | {
27 | yield self.forest[index].value
28 | }
29 | _modify
30 | {
31 | yield &self.forest[index].value
32 | }
33 | }
34 |
35 | init()
36 | {
37 | self.forest = .init()
38 | }
39 |
40 | mutating
41 | func push(_ value:Value, version:Version, into tree:inout Branch.Head?)
42 | {
43 | guard let head:Index = tree?.index
44 | else
45 | {
46 | tree = self.forest.insert(root: .init(value, version: version))
47 | return
48 | }
49 | let previous:Keyframe = self[head]
50 | if previous.versions ~= version._predecessor,
51 | previous.value == value
52 | {
53 | self[head].versions = previous.versions.lowerBound ... version
54 | }
55 | else
56 | {
57 | self.forest.push(min: .init(value, version: version), into: &tree)
58 | }
59 | }
60 | }
61 | extension History
62 | {
63 | subscript(branch:Branch.Head?) -> Branch
64 | {
65 | .init(self.forest[branch])
66 | }
67 |
68 | struct Branch
69 | {
70 | typealias Head = Forest.Tree.Head
71 |
72 | @propertyWrapper
73 | struct Optional:Hashable, Sendable
74 | {
75 | private
76 | var bits:UInt32
77 |
78 | init()
79 | {
80 | self.bits = .max
81 | }
82 |
83 | var wrappedValue:Head?
84 | {
85 | get
86 | {
87 | self.bits != .max ? .init(.init(bits: self.bits)) : nil
88 | }
89 | set(value)
90 | {
91 | if let bits:UInt32 = value?.index.bits
92 | {
93 | precondition(bits != .max)
94 | self.bits = bits
95 | }
96 | else
97 | {
98 | self.bits = .max
99 | }
100 | }
101 | }
102 | }
103 |
104 | private
105 | let tree:Forest.Tree
106 |
107 | fileprivate
108 | init(_ tree:Forest.Tree)
109 | {
110 | self.tree = tree
111 | }
112 |
113 | func contains(_ version:Version) -> Bool
114 | {
115 | if case _? = self.find(version)
116 | {
117 | return true
118 | }
119 | else
120 | {
121 | return false
122 | }
123 | }
124 | func at(_ version:Version) -> Value?
125 | {
126 | self.find(version).map { self.tree.forest[$0].value.value }
127 | }
128 | func find(_ version:Version) -> Index?
129 | {
130 | self.tree.find
131 | {
132 | if $0.versions.upperBound < version
133 | {
134 | return false
135 | }
136 | else if $0.versions.lowerBound > version
137 | {
138 | return true
139 | }
140 | else
141 | {
142 | return nil
143 | }
144 | }
145 | }
146 | }
147 | }
--------------------------------------------------------------------------------
/Sources/Biome/Package/Package.Pins.swift:
--------------------------------------------------------------------------------
1 | extension Package
2 | {
3 | @usableFromInline
4 | struct Pins:Sendable
5 | {
6 | let local:(package:Index, version:Version)
7 | let dependencies:[Index: Version]
8 |
9 | subscript(index:Index) -> Version?
10 | {
11 | index == self.local.package ? self.local.version : self.dependencies[index]
12 | }
13 |
14 | init(local:(package:Index, version:Version), dependencies:[Index: Version])
15 | {
16 | self.local = local
17 | self.dependencies = dependencies
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Sources/Biome/Package/Package.Versions.swift:
--------------------------------------------------------------------------------
1 | import Versions
2 |
3 | extension Package
4 | {
5 | struct Node:Sendable
6 | {
7 | let version:PreciseVersion
8 | let dependencies:[Package.Index: Version]
9 | var consumers:[Package.Index: Set]
10 |
11 | init(version:PreciseVersion, dependencies:[Package.Index: Version],
12 | consumers:[Package.Index: Set] = [:])
13 | {
14 | self.version = version
15 | self.consumers = consumers
16 | self.dependencies = dependencies
17 | }
18 | }
19 |
20 | struct Versions:RandomAccessCollection, Sendable
21 | {
22 | var latest:Version
23 | {
24 | .init(offset: self.nodes.endIndex - 1)
25 | }
26 |
27 | private
28 | var nodes:[Node],
29 | mapping:[MaskedVersion?: Version]
30 | let package:Package.Index
31 |
32 | var startIndex:Version
33 | {
34 | .init(offset: self.nodes.startIndex)
35 | }
36 | var endIndex:Version
37 | {
38 | .init(offset: self.nodes.endIndex)
39 | }
40 | subscript(version:Version) -> Node
41 | {
42 | _read
43 | {
44 | yield self.nodes[version.offset]
45 | }
46 | _modify
47 | {
48 | yield &self.nodes[version.offset]
49 | }
50 | }
51 |
52 | func pins(at version:Version) -> Pins
53 | {
54 | .init(local: (self.package, version), dependencies: self[version].dependencies)
55 | }
56 | func pins(at masked:MaskedVersion?) -> Pins?
57 | {
58 | self.mapping[masked].map(self.pins(at:))
59 | }
60 |
61 | func snap(_ masked:MaskedVersion?) -> Version
62 | {
63 | self.mapping[masked] ?? self.latest
64 | }
65 |
66 | init(package:Package.Index)
67 | {
68 | self.package = package
69 | self.mapping = [:]
70 | self.nodes = []
71 | }
72 |
73 | mutating
74 | func push(_ precise:PreciseVersion, dependencies:[Package.Index: Version]) -> Pins
75 | {
76 | self.nodes.append(.init(version: precise, dependencies: dependencies))
77 | let version:Version = self.latest
78 | switch precise
79 | {
80 | case .semantic(let major, let minor, let patch, let edition):
81 | self.mapping[.edition(major, minor, patch, edition)] = version
82 | self.mapping[ .patch(major, minor, patch)] = version
83 | self.mapping[ .minor(major, minor)] = version
84 | self.mapping[ .major(major)] = version
85 | case .toolchain(year: let year, month: let month, day: let day, letter: let letter):
86 | self.mapping[.hourly(year: year, month: month, day: day, letter: letter)] = version
87 | self.mapping[.nightly(year: year, month: month, day: day)] = version
88 | }
89 | self.mapping[nil] = version
90 | return .init(local: (self.package, version), dependencies: dependencies)
91 | }
92 |
93 | func abbreviate(_ version:Version) -> MaskedVersion?
94 | {
95 | guard self.latest != version
96 | else
97 | {
98 | return nil
99 | }
100 | switch self[version].version
101 | {
102 | case .semantic(let major, let minor, let patch, let edition):
103 | if case version? = self.mapping[.major(major)]
104 | {
105 | return .major(major)
106 | }
107 | else if case version? = self.mapping[.minor(major, minor)]
108 | {
109 | return .minor(major, minor)
110 | }
111 | else if case version? = self.mapping[.patch(major, minor, patch)]
112 | {
113 | return .patch(major, minor, patch)
114 | }
115 | else
116 | {
117 | return .edition(major, minor, patch, edition)
118 | }
119 |
120 | case .toolchain(year: let year, month: let month, day: let day, letter: let letter):
121 | if case version? = self.mapping[.nightly(year: year, month: month, day: day)]
122 | {
123 | return .nightly(year: year, month: month, day: day)
124 | }
125 | else
126 | {
127 | return .hourly(year: year, month: month, day: day, letter: letter)
128 | }
129 | }
130 | }
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/Sources/Biome/Package/Version.swift:
--------------------------------------------------------------------------------
1 | @usableFromInline
2 | struct Version:Hashable, Strideable, Sendable
3 | {
4 | let offset:Int
5 |
6 | static
7 | let max:Self = .init(offset: .max)
8 |
9 | @usableFromInline static
10 | func < (lhs:Self, rhs:Self) -> Bool
11 | {
12 | lhs.offset < rhs.offset
13 | }
14 | @usableFromInline
15 | func advanced(by offset:Int) -> Self
16 | {
17 | .init(offset: self.offset.advanced(by: offset))
18 | }
19 | @usableFromInline
20 | func distance(to other:Self) -> Int
21 | {
22 | self.offset.distance(to: other.offset)
23 | }
24 |
25 | var _predecessor:Self
26 | {
27 | self.advanced(by: -1)
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Sources/Biome/Packages/Beliefs.swift:
--------------------------------------------------------------------------------
1 | import SymbolGraphs
2 |
3 | extension Sequence
4 | {
5 | func generateBeliefs(abstractors:[Abstractor], context:Packages) -> Beliefs
6 | {
7 | var beliefs:Beliefs = .init()
8 | for (graph, abstractor):(SymbolGraph, Abstractor) in zip(self, abstractors)
9 | {
10 | beliefs.insert(
11 | statements: graph.statements(abstractor: abstractor, context: context),
12 | symbols: abstractor.updates,
13 | context: context)
14 | }
15 | beliefs.integrate()
16 | return beliefs
17 | }
18 | }
19 | struct Beliefs
20 | {
21 | var facts:[Symbol.Index: Symbol.Facts]
22 | var opinions:[Symbol.Diacritic: Symbol.Traits]
23 |
24 | init()
25 | {
26 | self.facts = [:]
27 | self.opinions = [:]
28 | }
29 |
30 | fileprivate mutating
31 | func insert(statements:[Symbol.Statement],
32 | symbols:Abstractor.Updates,
33 | context:Packages)
34 | {
35 | var traits:[Symbol.Index: [Symbol.Trait]] = [:]
36 | var roles:[Symbol.Index: [Symbol.Role]] = [:]
37 | for (subject, predicate):Symbol.Statement in statements
38 | {
39 | switch (symbols.culture == subject.module, predicate)
40 | {
41 | case (false, .is(_)):
42 | fatalError("unimplemented")
43 | case (false, .has(let trait)):
44 | traits [subject, default: []].append(trait)
45 | case (true, .has(let trait)):
46 | traits [subject, default: []].append(trait)
47 | case (true, .is(let role)):
48 | roles [subject, default: []].append(role)
49 | }
50 | }
51 | for symbol:Symbol.Index? in symbols
52 | {
53 | guard let symbol:Symbol.Index
54 | else
55 | {
56 | continue
57 | }
58 | self.facts[symbol] = .init(
59 | traits: traits.removeValue(forKey: symbol) ?? [],
60 | roles: roles.removeValue(forKey: symbol) ?? [],
61 | as: context[symbol].community)
62 | }
63 | for (symbol, traits):(Symbol.Index, [Symbol.Trait]) in traits
64 | {
65 | let diacritic:Symbol.Diacritic = .init(host: symbol, culture: symbols.culture)
66 | self.opinions[diacritic] = .init(traits, as: context[symbol].community)
67 | }
68 | }
69 | fileprivate mutating
70 | func integrate()
71 | {
72 | self.opinions = self.opinions.filter
73 | {
74 | guard $0.key.host.module.package == $0.key.culture.package,
75 | let index:Dictionary.Index =
76 | self.facts.index(forKey: $0.key.host)
77 | else
78 | {
79 | return true
80 | }
81 | self.facts.values[index].predicates.updateAcceptedTraits($0.value,
82 | culture: $0.key.culture)
83 | return false
84 | }
85 | }
86 | }
87 | extension Beliefs
88 | {
89 | func generateTrees(context:Packages) -> Route.Trees
90 | {
91 | var natural:[Route.NaturalTree] = []
92 | var synthetic:[Route.SyntheticTree] = []
93 | for (symbol, facts):(Symbol.Index, Symbol.Facts) in self.facts
94 | {
95 | let host:Symbol = context[symbol]
96 |
97 | natural.append(.init(key: host.route, target: symbol))
98 |
99 | if let stem:Route.Stem = host.kind.path
100 | {
101 | for (culture, features):(Module.Index?, Set) in
102 | facts.predicates.featuresAssumingConcreteType()
103 | {
104 | synthetic.append(.init(namespace: host.namespace, stem: stem,
105 | diacritic: .init(host: symbol, culture: culture ?? symbol.module),
106 | features: features.map { ($0, context[$0].route.leaf) }))
107 | }
108 | }
109 | }
110 | for (diacritic, traits):(Symbol.Diacritic, Symbol.Traits) in self.opinions
111 | {
112 | // can have external traits that do not have to do with features
113 | if !traits.features.isEmpty
114 | {
115 | let host:Symbol = context[diacritic.host]
116 | if let stem:Route.Stem = host.kind.path
117 | {
118 | synthetic.append(.init(namespace: host.namespace, stem: stem,
119 | diacritic: diacritic,
120 | features: traits.features.map { ($0, context[$0].route.leaf) }))
121 | }
122 | }
123 | }
124 | return (natural, synthetic)
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/Sources/Biome/Packages/Comments.swift:
--------------------------------------------------------------------------------
1 | import SymbolGraphs
2 |
3 | extension Sequence
4 | {
5 | // "abc" module C
6 | // |
7 | // --------|-------------------
8 | // ↓
9 | // nil -----→ "abc" module B
10 | // |
11 | // --------------------|-------
12 | // ↓
13 | // "abc" module A
14 | func generateComments(abstractors:[Abstractor])
15 | -> [Symbol.Index: Documentation]
16 | {
17 | guard let culture:Package.Index = abstractors.first?.culture.package
18 | else
19 | {
20 | return [:]
21 | }
22 |
23 | var pruned:Int = 0
24 | var comments:[Symbol.Index: Documentation] = [:]
25 | for (graph, abstractor):(SymbolGraph, Abstractor) in zip(self, abstractors)
26 | {
27 | for (index, vertex):(Symbol.Index?, SymbolGraph.Vertex) in
28 | zip(abstractor.updates, graph.vertices)
29 | {
30 | guard let index:Symbol.Index,
31 | let documentation:Documentation =
32 | vertex.documentation?.flatMap({ abstractor[$0] })
33 | else
34 | {
35 | continue
36 | }
37 | if case .extends(let origin?, with: let comment) = documentation,
38 | origin.module != abstractor.culture,
39 | case .extends(_, with: comment)? = comments[origin]
40 | {
41 | // inherited a comment from a *different* module.
42 | // if it were from the same module, symbolgraphconvert
43 | // should have deleted it.
44 | comments[index] = .inherits(origin)
45 | pruned += 1
46 | }
47 | else
48 | {
49 | comments[index] = documentation
50 | }
51 | }
52 | }
53 | if pruned != 0
54 | {
55 | print("pruned \(pruned) duplicate comments")
56 | }
57 |
58 | var skipped:Int = 0,
59 | dropped:Int = 0
60 | comments = comments.compactMapValues
61 | {
62 | if case .inherits(var origin) = $0
63 | {
64 | // fast-forward until we either reach a package boundary,
65 | // or a local symbol that has documentation
66 | var visited:Set = []
67 | fastforwarding:
68 | while origin.module.package == culture
69 | {
70 | if case _? = visited.update(with: origin)
71 | {
72 | fatalError("detected cycle in doccomment inheritance graph")
73 | }
74 | switch comments[origin]
75 | {
76 | case nil:
77 | dropped += 1
78 | return nil
79 | case .extends(_, with: _)?:
80 | break fastforwarding
81 | case .inherits(let next)?:
82 | origin = next
83 | skipped += 1
84 | }
85 | }
86 | return .inherits(origin)
87 | }
88 | else
89 | {
90 | return $0
91 | }
92 | }
93 | if skipped != 0
94 | {
95 | print("shortened \(skipped) doccomment inheritance links")
96 | }
97 | if dropped != 0
98 | {
99 | print("pruned \(dropped) nil-terminating doccomment inheritance chains")
100 | }
101 | return comments
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/Sources/Biome/Packages/Packages.Selection.swift:
--------------------------------------------------------------------------------
1 | import Versions
2 |
3 | extension Packages
4 | {
5 | @usableFromInline
6 | enum Selection
7 | {
8 | case one(Symbol.Composite)
9 | case many([Symbol.Composite])
10 |
11 | init?(_ matches:[Symbol.Composite])
12 | {
13 | guard let first:Symbol.Composite = matches.first
14 | else
15 | {
16 | return nil
17 | }
18 | if matches.count < 2
19 | {
20 | self = .one(first)
21 | }
22 | else
23 | {
24 | self = .many(matches)
25 | }
26 | }
27 |
28 | func composite() throws -> Symbol.Composite
29 | {
30 | switch self
31 | {
32 | case .one(let composite):
33 | return composite
34 | case .many(let composites):
35 | throw SelectionError.many(composites)
36 | }
37 | }
38 | }
39 |
40 | func localize(destination:Package.Index,
41 | arrival:MaskedVersion? = nil,
42 | lens:Symbol.Link.Lens?) -> Package.Pins?
43 | {
44 | if let lens:Symbol.Link.Lens
45 | {
46 | return self[lens.culture]?.versions.pins(at: lens.version)
47 | }
48 | else
49 | {
50 | return self[destination].versions.pins(at: arrival)
51 | }
52 | }
53 | }
54 |
55 | extension Packages
56 | {
57 | func selectExtantWithRedirect(_ route:Route.Key, lens:Package.Pinned,
58 | by disambiguator:Symbol.Disambiguator)
59 | -> (selection:Selection, redirected:Bool)?
60 | {
61 | route.first
62 | {
63 | self.selectExtant($0, lenses: CollectionOfOne.init(lens))
64 | {
65 | self.filter($0, by: disambiguator)
66 | }
67 | }
68 | }
69 | func selectExtant(_ route:Route.Key, lenses:Lenses,
70 | where predicate:(Symbol.Composite) throws -> Bool)
71 | rethrows -> Selection?
72 | where Lenses:Sequence, Lenses.Element == Package.Pinned
73 | {
74 | var matches:[Symbol.Composite] = []
75 | for pinned:Package.Pinned in lenses
76 | {
77 | try pinned.package.groups[route]?.forEach
78 | {
79 | if try predicate($0), pinned.contains($0)
80 | {
81 | matches.append($0)
82 | }
83 | }
84 | }
85 | return .init(matches)
86 | }
87 |
88 | func selectHistoricalWithRedirect(_ route:Route.Key, lens:Package,
89 | by disambiguator:Symbol.Disambiguator)
90 | -> (selection:Selection, redirected:Bool)?
91 | {
92 | route.first
93 | {
94 | self.selectHistorical($0, lens: lens)
95 | {
96 | self.filter($0, by: disambiguator)
97 | }
98 | }
99 | }
100 | private
101 | func selectHistorical(_ route:Route.Key, lens:Package,
102 | where predicate:(Symbol.Composite) throws -> Bool)
103 | rethrows -> Selection?
104 | {
105 | var matches:[Symbol.Composite] = []
106 | try lens.groups[route]?.forEach
107 | {
108 | if try predicate($0)
109 | {
110 | matches.append($0)
111 | }
112 | }
113 | return .init(matches)
114 | }
115 |
116 | func filter(_ composite:Symbol.Composite, by disambiguator:Symbol.Disambiguator)
117 | -> Bool
118 | {
119 | let host:Symbol = self[composite.diacritic.host]
120 | let base:Symbol = self[composite.base]
121 | switch disambiguator.docC
122 | {
123 | case nil:
124 | break
125 | case .fnv(_)?:
126 | // TODO: implement this
127 | break
128 | case .community(base.community)?:
129 | break
130 | case .community(_)?:
131 | return false
132 | }
133 | switch (disambiguator.base, disambiguator.host)
134 | {
135 | case (base.id?, host.id?),
136 | (base.id?, nil),
137 | (nil, host.id?),
138 | (nil, nil):
139 | return true
140 | default:
141 | return false
142 | }
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/Sources/Biome/Page/Page html.swift:
--------------------------------------------------------------------------------
1 | import MIME
2 | import HTML
3 |
4 | extension Page
5 | {
6 | static
7 | let html:HTML.Root = .init(
8 | .head(
9 | .title(.init(anchor: .title)),
10 |
11 | .meta(attributes: [.charset("UTF-8")]),
12 | .meta(attributes: [.name("viewport"), .content("width=device-width, initial-scale=1")]),
13 |
14 | .script(.init(anchor: .constants)),
15 | .script(attributes: [.src("/search.js"), .defer]),
16 | .link(attributes: [.href("/biome.css"), .rel("stylesheet")]),
17 | .link(attributes: [.href("/favicon.png"), .rel("icon")]),
18 | .link(attributes: [.href("/favicon.ico"), .rel("icon"), .type(MIME.icon.rawValue)])),
19 | .body(
20 | .header(
21 | .nav(.init(anchor: .breadcrumbs), attributes: [.class("breadcrumbs")]),
22 | .div(
23 | .div(
24 | .form(.input(
25 | attributes:
26 | [
27 | .id("search-input"),
28 | .type("search"),
29 | .placeholder("search symbols"),
30 | .autocomplete("off"),
31 | ]),
32 | attributes:
33 | [
34 | .role("search"),
35 | .id("search"),
36 | ]),
37 | .input(attributes:
38 | [
39 | .id("version-menu-toggle"),
40 | .type("checkbox"),
41 | .autocomplete("off")
42 | ]),
43 | .label(.init(anchor: .pin), attributes:
44 | [
45 | .id("version"),
46 | .for("version-menu-toggle"),
47 | ]),
48 | attributes: [.class("toolbar")]),
49 | .ol(attributes: [.id("search-results")]),
50 | .div(.init(anchor: .versions), attributes: [.id("version-menu")]),
51 | attributes: [.class("toolbar-container")])),
52 | .main(
53 | .div(
54 | .div([
55 | .article(
56 | .section(
57 | .div(
58 | .span(.init(anchor: .kind), attributes: [.class("kind")]),
59 | .span(
60 | .init(anchor: .namespace),
61 | .init(anchor: .culture),
62 | .init(anchor: .base),
63 | attributes: [.class("nationality")]),
64 | attributes: [.class("eyebrows")]),
65 |
66 | .init(anchor: .headline),
67 | .init(anchor: .notices),
68 | .init(anchor: .summary),
69 | .init(anchor: .notes),
70 | .init(anchor: .availability),
71 | attributes: [.class("introduction")]),
72 | .init(anchor: .platforms),
73 | .init(anchor: .fragments),
74 | .init(anchor: .dependencies),
75 | .init(anchor: .consumers),
76 | .init(anchor: .discussion),
77 | attributes: [.class("upper-container-left")])],
78 | attributes: [.class("upper-container")]),
79 | attributes: [.class("upper")]),
80 | .div(.init(anchor: .topics), attributes: [.class("lower")]))),
81 | attributes: [.lang("en")])
82 | }
83 |
--------------------------------------------------------------------------------
/Sources/Biome/Route/Root.swift:
--------------------------------------------------------------------------------
1 | public
2 | enum Root:Hashable, Sendable
3 | {
4 | case master
5 | case article
6 | case sitemap
7 | case searchIndex
8 | }
9 |
--------------------------------------------------------------------------------
/Sources/Biome/Route/Route.Key.swift:
--------------------------------------------------------------------------------
1 | @available(*, deprecated, renamed: "Route.Stem")
2 | typealias Stem = Route.Stem
3 | @available(*, deprecated, renamed: "Route.Leaf")
4 | typealias Leaf = Route.Leaf
5 |
6 | extension Route
7 | {
8 | struct Leaf:Hashable
9 | {
10 | let bitPattern:UInt32
11 |
12 | var stem:Stem
13 | {
14 | .init(masking: self.bitPattern)
15 | }
16 | var outed:Self?
17 | {
18 | let outed:Self = .init(bitPattern: self.stem.bitPattern)
19 | return outed == self ? nil : outed
20 | }
21 | var orientation:Symbol.Link.Orientation
22 | {
23 | self.bitPattern & 1 == 0 ? .gay : .straight
24 | }
25 |
26 | init(_ stem:Stem, orientation:Symbol.Link.Orientation)
27 | {
28 | switch orientation
29 | {
30 | case .gay: self.init(bitPattern: stem.bitPattern)
31 | case .straight: self.init(bitPattern: stem.bitPattern | 1)
32 | }
33 | }
34 | private
35 | init(bitPattern:UInt32)
36 | {
37 | self.bitPattern = bitPattern
38 | }
39 | }
40 | // the lsb is reserved to encode orientation
41 | struct Stem:Hashable
42 | {
43 | private(set)
44 | var bitPattern:UInt32
45 |
46 | init()
47 | {
48 | self.bitPattern = 0
49 | }
50 | init(masking bits:UInt32)
51 | {
52 | self.bitPattern = bits & 0xffff_fffe
53 | }
54 |
55 | mutating
56 | func increment() -> Self
57 | {
58 | self.bitPattern += 2
59 | return self
60 | }
61 | }
62 | struct Key:Hashable, Sendable, CustomStringConvertible
63 | {
64 | let namespace:Module.Index
65 | let stem:Stem
66 | let leaf:Leaf
67 |
68 | var outed:Self?
69 | {
70 | self.leaf.outed.map { .init(self.namespace, self.stem, $0) }
71 | }
72 |
73 | var description:String
74 | {
75 | """
76 | \(self.namespace.package.bits):\
77 | \(self.namespace.bits).\
78 | \(self.stem.bitPattern >> 1).\
79 | \(self.leaf.bitPattern)
80 | """
81 | }
82 |
83 | init(_ namespace:Module.Index, _ stem:Stem, _ leaf:Stem, orientation:Symbol.Link.Orientation)
84 | {
85 | self.init(namespace, stem, .init(leaf, orientation: orientation))
86 | }
87 | init(_ namespace:Module.Index, _ stem:Stem, _ leaf:Leaf)
88 | {
89 | self.namespace = namespace
90 | self.stem = stem
91 | self.leaf = leaf
92 | }
93 |
94 | func first(where transform:(Self) throws -> T?) rethrows -> (T, redirected:Bool)?
95 | {
96 | if let result:T = try transform(self)
97 | {
98 | return (result, false)
99 | }
100 | else if let outed:Self = self.outed,
101 | let result:T = try transform(outed)
102 | {
103 | return (result, true)
104 | }
105 | else
106 | {
107 | return nil
108 | }
109 | }
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/Sources/Biome/Route/Route.Stems.swift:
--------------------------------------------------------------------------------
1 | extension Route
2 | {
3 | struct Stems
4 | {
5 | private
6 | var counter:Stem
7 | private
8 | var table:[String: Stem]
9 |
10 | var _count:Int
11 | {
12 | self.table.count
13 | }
14 | var _memoryFootprint:Int
15 | {
16 | let direct:Int = self.table.capacity *
17 | MemoryLayout.Element>.stride
18 | let indirect:Int = self.table.keys.reduce(0) { $0 + $1.utf8.count }
19 | return direct + indirect
20 | }
21 |
22 | init()
23 | {
24 | self.counter = .init()
25 | self.table = [:]
26 | }
27 |
28 | private static
29 | func subpath(_ component:S) -> String
30 | where S:StringProtocol
31 | {
32 | component.lowercased()
33 | }
34 | private static
35 | func subpath(_ components:S) -> String
36 | where S:Sequence, S.Element:StringProtocol
37 | {
38 | components.map { $0.lowercased() }.joined(separator: "\u{0}")
39 | }
40 |
41 | private
42 | subscript(subpath:String) -> Stem?
43 | {
44 | self.table[subpath]
45 | }
46 |
47 | subscript(leaf component:Component) -> Stem?
48 | where Component:StringProtocol
49 | {
50 | self.table[Self.subpath(component)]
51 | }
52 | private
53 | subscript(stem components:Path) -> Stem?
54 | where Path:Sequence, Path.Element:StringProtocol
55 | {
56 | self.table[Self.subpath(components)]
57 | }
58 |
59 |
60 | private
61 | subscript(leaf component:Symbol.Link.Component) -> Stem?
62 | {
63 | if let hyphen:String.Index = component.hyphen
64 | {
65 | return self[leaf: component.string[..(namespace:Module.Index, infix:Path) -> Key?
74 | where Path:BidirectionalCollection, Path.Element:StringProtocol
75 | {
76 | if let leaf:Path.Element = infix.last,
77 | let leaf:Stem = self[leaf: leaf],
78 | let stem:Stem = self[stem: infix.dropLast()]
79 | {
80 | return .init(namespace, stem, leaf, orientation: .straight)
81 | }
82 | else
83 | {
84 | return nil
85 | }
86 | }
87 | subscript(namespace:Module.Index, infix:[String], suffix:Symbol.Link) -> Key?
88 | {
89 | if let leaf:Symbol.Link.Component = suffix.last,
90 | let leaf:Stem = self[leaf: leaf],
91 | let stem:Stem = self[stem: suffix.prefix(prepending: infix)]
92 | {
93 | return .init(namespace, stem, leaf, orientation: suffix.orientation)
94 | }
95 | else
96 | {
97 | return nil
98 | }
99 | }
100 | subscript(namespace:Module.Index, suffix:Symbol.Link) -> Key?
101 | {
102 | self[namespace, [], suffix]
103 | }
104 |
105 | private mutating
106 | func register(_ string:String) -> Stem
107 | {
108 | if let stem:Stem = self.table[string]
109 | {
110 | return stem
111 | }
112 | else
113 | {
114 | self.table[string] = self.counter.increment()
115 | return self.counter
116 | }
117 | }
118 | mutating
119 | func register(components:S) -> Stem
120 | where S:Sequence, S.Element:StringProtocol
121 | {
122 | self.register(Self.subpath(components))
123 | }
124 | mutating
125 | func register(component:S) -> Stem
126 | where S:StringProtocol
127 | {
128 | self.register(Self.subpath(component))
129 | }
130 | }
131 | }
--------------------------------------------------------------------------------
/Sources/Biome/Route/Route.swift:
--------------------------------------------------------------------------------
1 | struct Route
2 | {
3 | let key:Key
4 | let target:Symbol.Composite
5 |
6 | init(key:Key, target:Symbol.Composite)
7 | {
8 | self.key = key
9 | self.target = target
10 | }
11 |
12 | typealias Trees = (natural:[NaturalTree], synthetic:[SyntheticTree])
13 |
14 | struct NaturalTree
15 | {
16 | let key:Key
17 | let target:Symbol.Index
18 |
19 | var route:Route
20 | {
21 | .init(key: key, target: .init(natural: self.target))
22 | }
23 | }
24 | struct SyntheticTree:RandomAccessCollection
25 | {
26 | let namespace:Module.Index
27 | let stem:Stem
28 |
29 | let diacritic:Symbol.Diacritic
30 | let features:[(base:Symbol.Index, leaf:Leaf)]
31 |
32 | var startIndex:Int
33 | {
34 | self.features.startIndex
35 | }
36 | var endIndex:Int
37 | {
38 | self.features.endIndex
39 | }
40 | subscript(index:Int) -> Route
41 | {
42 | let (base, leaf):(Symbol.Index, Leaf) = self.features[index]
43 | return .init(key: .init(self.namespace, self.stem, leaf),
44 | target: .init(base, self.diacritic))
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/Sources/Biome/Swift/Demangle.swift:
--------------------------------------------------------------------------------
1 | #if os(Linux)
2 | import Glibc
3 | #elseif os(macOS)
4 | import Darwin
5 | #endif
6 |
7 | enum Demangle
8 | {
9 | #if os(Linux) || os(macOS)
10 | private
11 | typealias Function = @convention(c)
12 | (
13 | _ name:UnsafePointer?,
14 | _ count:Int,
15 | _ output:UnsafeMutablePointer?,
16 | _ capacity:UnsafeMutablePointer?,
17 | _ flags:UInt32
18 | ) -> UnsafeMutablePointer?
19 |
20 | private static
21 | var function:Function =
22 | {
23 | guard let swift:UnsafeMutableRawPointer = dlopen(nil, RTLD_NOW)
24 | else
25 | {
26 | fatalError("could not load swift runtime")
27 | }
28 | guard let symbol:UnsafeMutableRawPointer = dlsym(swift, "swift_demangle")
29 | else
30 | {
31 | fatalError("could not load symbol 'swift_demangle'")
32 | }
33 | return unsafeBitCast(symbol, to: Function.self)
34 | }()
35 |
36 | // argument should include 's' prefix, but not the '$' prefix
37 | static
38 | subscript(mangled:String) -> String
39 | {
40 | // '$s'
41 | let prefixed:String = "$\(mangled)"
42 | guard let string:UnsafeMutablePointer = self.function(prefixed, prefixed.utf8.count, nil, nil, 0)
43 | else
44 | {
45 | print("warning: could not demangle symbol '\(mangled)'")
46 | return mangled
47 | }
48 | defer
49 | {
50 | string.deallocate()
51 | }
52 | return String.init(cString: string)
53 | }
54 | #else
55 | static
56 | subscript(mangled:String) -> String
57 | {
58 | print("warning: could not demangle symbol '\(mangled)' (demangling not supported on this platform)")
59 | return mangled
60 | }
61 | #endif
62 | }
63 |
--------------------------------------------------------------------------------
/Sources/Biome/Symbol.Facts/Generic.Conditional.swift:
--------------------------------------------------------------------------------
1 | import SymbolGraphs
2 |
3 | extension Generic.Conditional:Equatable where Target:Equatable {}
4 | extension Generic.Conditional:Hashable where Target:Hashable {}
5 | extension Generic.Conditional:Sendable where Target:Sendable {}
6 | extension Generic
7 | {
8 | struct Conditional
9 | {
10 | // not a ``Set``, because of
11 | // https://github.com/apple/swift/blob/main/docs/ABI/GenericSignature.md
12 | let conditions:[Constraint]
13 | let target:Target
14 |
15 | @available(*, deprecated, renamed: "target")
16 | var index:Target
17 | {
18 | self.target
19 | }
20 |
21 | init(_ target:Target, where constraints:[Constraint] = [])
22 | {
23 | self.target = target
24 | self.conditions = constraints
25 | }
26 |
27 | func map(_ transform:(Target) throws -> T) rethrows -> Conditional
28 | {
29 | .init(try transform(self.target),
30 | where: try self.conditions.map { try $0.map(transform) })
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Sources/Biome/Symbol.Facts/Symbol.Composite.swift:
--------------------------------------------------------------------------------
1 | extension Symbol
2 | {
3 | // 20 B size, 24 B stride
4 | @usableFromInline
5 | struct Composite:Hashable, Sendable
6 | {
7 | // there are up to three cultures that come into play here:
8 | // 1. host culture
9 | // 2. witness culture
10 | // 3. perpetrator culture
11 | let base:Index
12 | let diacritic:Diacritic
13 |
14 | var culture:Module.Index
15 | {
16 | self.diacritic.culture
17 | }
18 | var isNatural:Bool
19 | {
20 | self.base == self.diacritic.host
21 | }
22 | var host:Index?
23 | {
24 | self.isNatural ? nil : self.diacritic.host
25 | }
26 | var natural:Index?
27 | {
28 | self.isNatural ? self.base : nil
29 | }
30 |
31 | init(natural:Index)
32 | {
33 | self.base = natural
34 | self.diacritic = .init(natural: natural)
35 | }
36 | init(_ base:Index, _ diacritic:Diacritic)
37 | {
38 | self.base = base
39 | self.diacritic = diacritic
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Sources/Biome/Symbol.Facts/Symbol.Diacritic.swift:
--------------------------------------------------------------------------------
1 | extension Symbol
2 | {
3 | @usableFromInline
4 | struct Diacritic:Hashable, Sendable
5 | {
6 | let host:Index
7 | let culture:Module.Index
8 |
9 | init(host:Index, culture:Module.Index)
10 | {
11 | self.host = host
12 | self.culture = culture
13 | }
14 |
15 | init(natural:Index)
16 | {
17 | self.host = natural
18 | self.culture = natural.module
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Sources/Biome/Symbol.Facts/Symbol.Facts.swift:
--------------------------------------------------------------------------------
1 | import SymbolGraphs
2 |
3 | extension Symbol
4 | {
5 | typealias Statement = (subject:Index, predicate:Predicate)
6 |
7 | enum Predicate
8 | {
9 | case `is`(Role)
10 | case has(Trait)
11 | }
12 | struct Predicates:Equatable, Sendable
13 | {
14 | let roles:Roles?
15 | var primary:Traits
16 | var accepted:[Module.Index: Traits]
17 |
18 | init(roles:Roles?, primary:Traits = .init())
19 | {
20 | self.roles = roles
21 | self.primary = primary
22 | self.accepted = [:]
23 | }
24 |
25 | mutating
26 | func updateAcceptedTraits(_ traits:Traits, culture:Module.Index)
27 | {
28 | self.accepted[culture] = traits.subtracting(self.primary)
29 | }
30 |
31 | func featuresAssumingConcreteType() -> [(perpetrator:Module.Index?, features:Set)]
32 | {
33 | var features:[(perpetrator:Module.Index?, features:Set)] = []
34 | if !self.primary.features.isEmpty
35 | {
36 | features.append((nil, self.primary.features))
37 | }
38 | for (perpetrator, traits):(Module.Index, Traits) in self.accepted
39 | where !traits.features.isEmpty
40 | {
41 | features.append((perpetrator, traits.features))
42 | }
43 | return features
44 | }
45 | }
46 | struct Facts
47 | {
48 | var shape:Shape?
49 | var predicates:Predicates
50 |
51 | init(traits:[Trait], roles:[Role], as community:Community)
52 | {
53 | self.shape = nil
54 | // partition relationships buffer
55 | var superclass:Index? = nil
56 | var residuals:[Role] = []
57 | for role:Role in roles
58 | {
59 | switch (self.shape, role)
60 | {
61 | case (nil, .member(of: let type)):
62 | self.shape = .member(of: type)
63 | case (nil, .requirement(of: let interface)):
64 | self.shape = .requirement(of: interface)
65 |
66 | case (let shape?, .member(of: let type)):
67 | guard case .member(of: type) = shape
68 | else
69 | {
70 | fatalError("unimplemented")
71 | // throw PoliticalError.conflict(is: shape.role,
72 | // and: .member(of: type))
73 | }
74 | case (let shape?, .requirement(of: let interface)):
75 | guard case .requirement(of: interface) = shape
76 | else
77 | {
78 | fatalError("unimplemented")
79 | // throw PoliticalError.conflict(is: shape.role,
80 | // and: .requirement(of: interface))
81 | }
82 |
83 | case (_, .subclass(of: let type)):
84 | switch superclass
85 | {
86 | case nil, type?:
87 | superclass = type
88 | case _?:
89 | fatalError("unimplemented")
90 | // throw PoliticalError.conflict(is: .subclass(of: superclass),
91 | // and: .subclass(of: type))
92 | }
93 |
94 | default:
95 | residuals.append(role)
96 | }
97 | }
98 |
99 | let roles:Roles? = .init(residuals,
100 | superclass: superclass,
101 | shape: self.shape,
102 | as: community)
103 | self.predicates = .init(roles: roles, primary: .init(traits, as: community))
104 | }
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/Sources/Biome/Symbol.Link/Symbol.Link.Query.swift:
--------------------------------------------------------------------------------
1 | import SymbolGraphs
2 | import Versions
3 | import URI
4 |
5 | extension Symbol.Link
6 | {
7 | struct Lens
8 | {
9 | let culture:Package.ID,
10 | version:MaskedVersion?
11 |
12 | init(_ culture:Package.ID, at version:MaskedVersion? = nil)
13 | {
14 | self.culture = culture
15 | self.version = version
16 | }
17 | }
18 | struct Query
19 | {
20 | static
21 | let base:String = "overload",
22 | host:String = "self",
23 | lens:String = "from"
24 |
25 | var base:Symbol.ID?
26 | var host:Symbol.ID?
27 | var lens:Lens?
28 |
29 | init()
30 | {
31 | self.base = nil
32 | self.host = nil
33 | self.lens = nil
34 | }
35 | init(_ parameters:[URI.Parameter]) throws
36 | {
37 | self.init()
38 | try self.update(with: parameters)
39 | }
40 | mutating
41 | func update(with parameters:[URI.Parameter]) throws
42 | {
43 | for (key, value):(String, String) in parameters
44 | {
45 | switch key
46 | {
47 | case Self.lens:
48 | // either 'from=swift-foo' or 'from=swift-foo/0.1.2'.
49 | // we do not tolerate missing slashes
50 | let components:[Substring] = value.split(separator: "/")
51 | guard let first:Substring = components.first
52 | else
53 | {
54 | continue
55 | }
56 | let id:Package.ID = .init(first)
57 | if let second:Substring = components.dropFirst().first,
58 | let version:MaskedVersion = try? .init(parsing: second)
59 | {
60 | self.lens = .init(id, at: version)
61 | }
62 | else
63 | {
64 | self.lens = .init(id)
65 | }
66 |
67 | case Self.host:
68 | // if the mangled name contained a colon ('SymbolGraphGen style'),
69 | // the parsing rule will remove it.
70 | self.host = try USR.Rule.OpaqueName.parse(value.utf8)
71 |
72 | case Self.base:
73 | switch try USR.init(parsing: value.utf8)
74 | {
75 | case .natural(let base):
76 | self.base = base
77 |
78 | case .synthesized(from: let base, for: let host):
79 | // this is supported for backwards-compatibility,
80 | // but the `::SYNTHESIZED::` infix is deprecated,
81 | // so this will end up causing a redirect
82 | self.host = host
83 | self.base = base
84 | }
85 |
86 | default:
87 | continue
88 | }
89 | }
90 | }
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/Sources/Biome/Symbol.Link/URI.swift:
--------------------------------------------------------------------------------
1 | import URI
2 | import Versions
3 |
4 | extension RangeReplaceableCollection where Element == URI.Vector?
5 | {
6 | mutating
7 | func append(components:Components,
8 | orientation:Symbol.Link.Orientation)
9 | where Components:BidirectionalCollection, Components.Element == String
10 | {
11 | guard case .gay = orientation, components.startIndex < components.endIndex
12 | else
13 | {
14 | self.append(components: components)
15 | return
16 | }
17 |
18 | let ultimate:Components.Index = components.index(before: components.endIndex)
19 |
20 | guard components.startIndex < ultimate
21 | else
22 | {
23 | self.append(components: components)
24 | return
25 | }
26 |
27 | let penultimate:Components.Index = components.index(before: ultimate)
28 |
29 | self.reserveCapacity(self.underestimatedCount +
30 | components[.. Symbol.Index
20 | {
21 | if let index:Symbol.Index = self.find(id)
22 | {
23 | return index
24 | }
25 | else
26 | {
27 | throw LookupError.unknownID(id)
28 | }
29 | }
30 | func contains(_ id:Symbol.ID) -> Bool
31 | {
32 | for lens:[Symbol.ID: Symbol.Index] in self.lenses
33 | {
34 | if let index:Symbol.Index = lens[id], self.namespaces.contains(index.module)
35 | {
36 | return true
37 | }
38 | }
39 | return false
40 | }
41 | func find(_ id:Symbol.ID) -> Symbol.Index?
42 | {
43 | var match:Symbol.Index? = nil
44 | for lens:[Symbol.ID: Symbol.Index] in self.lenses
45 | {
46 | guard let index:Symbol.Index = lens[id], self.namespaces.contains(index.module)
47 | else
48 | {
49 | continue
50 | }
51 | if case nil = match
52 | {
53 | match = index
54 | }
55 | else
56 | {
57 | // sanity check: ensure none of the remaining lenses contains
58 | // a colliding key
59 | fatalError("colliding symbol identifiers in search space")
60 | }
61 | }
62 | return match
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/Sources/Biome/Symbol/Symbol.Shape.swift:
--------------------------------------------------------------------------------
1 | extension Symbol.Shape:Equatable where Target:Equatable {}
2 | extension Symbol.Shape:Hashable where Target:Hashable {}
3 | extension Symbol.Shape:Sendable where Target:Sendable {}
4 | extension Symbol
5 | {
6 | // should have stride of 16 B, as well as `Shape?` and `Shape??`
7 | enum Shape
8 | {
9 | case member(of:Target)
10 | case requirement(of:Target)
11 |
12 | var role:Role
13 | {
14 | switch self
15 | {
16 | case .member(of: let target): return .member(of: target)
17 | case .requirement(of: let target): return .requirement(of: target)
18 | }
19 | }
20 | var target:Target
21 | {
22 | switch self
23 | {
24 | case .member(let index), .requirement(let index):
25 | return index
26 | }
27 | }
28 |
29 | func map(_ transform:(Target) throws -> T) rethrows -> Shape
30 | {
31 | switch self
32 | {
33 | case .member(of: let target):
34 | return .member(of: try transform(target))
35 | case .requirement(of: let target):
36 | return .requirement(of: try transform(target))
37 | }
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Sources/Biome/Symbol/Symbol.swift:
--------------------------------------------------------------------------------
1 | import SymbolGraphs
2 |
3 | public
4 | struct Symbol:Sendable, Identifiable, CustomStringConvertible
5 | {
6 | /// A globally-unique index referencing a symbol.
7 | ///
8 | /// A symbol index encodes the module it belongs to, which makes it possible
9 | /// to query module membership based on the index alone.
10 | @frozen public
11 | struct Index:CulturalIndex, Sendable
12 | {
13 | public
14 | let module:Module.Index
15 | public
16 | let bits:UInt32
17 |
18 | @inlinable public
19 | var culture:Module.Index
20 | {
21 | self.module
22 | }
23 | @inlinable public
24 | init(_ module:Module.Index, bits:UInt32)
25 | {
26 | self.module = module
27 | self.bits = bits
28 | }
29 | }
30 | // this is like ``Symbol.IndexRange``, except the ``module`` field refers to
31 | // a namespace, not the module that actually contains the symbol
32 | struct ColonialRange:Hashable, Sendable
33 | {
34 | let namespace:Module.Index
35 | let bits:Range
36 |
37 | var offsets:Range
38 | {
39 | .init(self.bits.lowerBound) ..< .init(self.bits.upperBound)
40 | }
41 |
42 | init(namespace:Module.Index, offsets:Range)
43 | {
44 | self.init(namespace: namespace, bits: .init(offsets.lowerBound) ..< .init(offsets.upperBound))
45 | }
46 | private
47 | init(namespace:Module.Index, bits:Range)
48 | {
49 | self.namespace = namespace
50 | self.bits = bits
51 | }
52 | }
53 |
54 | struct Heads
55 | {
56 | @History.Branch.Optional
57 | var documentation:History.Branch.Head?
58 | @History>.Branch.Optional
59 | var declaration:History>.Branch.Head?
60 | @History.Branch.Optional
61 | var facts:History.Branch.Head?
62 |
63 | init()
64 | {
65 | self._documentation = .init()
66 | self._declaration = .init()
67 | self._facts = .init()
68 | }
69 | }
70 |
71 | struct Nest
72 | {
73 | let namespace:Module.Index
74 | let prefix:[String]
75 | }
76 |
77 | // these stored properties are constant with respect to symbol identity.
78 | public
79 | let id:SymbolIdentifier
80 | // TODO: see if small-array optimizations here are beneficial, since this could
81 | // often be a single-element array
82 | let path:Path
83 | let kind:Kind
84 | let route:Route.Key
85 | var shape:Shape?
86 | var heads:Heads
87 | var pollen:Set
88 |
89 | var community:Community
90 | {
91 | self.kind.community
92 | }
93 | var name:String
94 | {
95 | self.path.last
96 | }
97 | // this is only the same as the perpetrator if this symbol is part of its
98 | // core symbol graph.
99 | var namespace:Module.Index
100 | {
101 | self.route.namespace
102 | }
103 | var nest:Nest?
104 | {
105 | self.path.prefix.isEmpty ?
106 | nil : .init(namespace: self.namespace, prefix: self.path.prefix)
107 | }
108 | var type:Index?
109 | {
110 | switch self.community
111 | {
112 | case .associatedtype, .callable(_):
113 | return self.shape?.target
114 | default:
115 | return nil
116 | }
117 | }
118 | var orientation:Link.Orientation
119 | {
120 | self.community.orientation
121 | }
122 | public
123 | var description:String
124 | {
125 | self.path.description
126 | }
127 |
128 | init(id:ID, path:Path, kind:Kind, route:Route.Key)
129 | {
130 | self.id = id
131 | self.path = path
132 | self.kind = kind
133 | self.route = route
134 | self.shape = nil
135 |
136 | self.heads = .init()
137 | self.pollen = []
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/Sources/PackageCatalogs/PackageCatalog.swift:
--------------------------------------------------------------------------------
1 | import SymbolGraphs
2 | import SystemPackage
3 | import Grammar
4 | import JSON
5 |
6 | public
7 | enum PackageCatalogError:Error
8 | {
9 | case toolsVersion(Int)
10 | }
11 |
12 | public
13 | struct PackageCatalog:Identifiable, Sendable
14 | {
15 | public
16 | let id:PackageIdentifier
17 | public
18 | let brand:String?
19 | public
20 | let modules:[ModuleCatalog]
21 | public
22 | let snippets:[SnippetCatalog]
23 |
24 | public
25 | init(id:ID, brand:String? = nil, modules:[ModuleCatalog] = [], snippets:[SnippetCatalog] = [])
26 | {
27 | self.id = id
28 | self.brand = brand
29 | self.modules = modules
30 | self.snippets = snippets
31 | }
32 | public
33 | init(from json:JSON) throws
34 | {
35 | self = try json.lint
36 | {
37 | switch try $0.remove("catalog_tools_version", as: Int.self)
38 | {
39 | case 3:
40 | return .init(
41 | id: try $0.remove("package", as: String.self, PackageIdentifier.init(_:)),
42 | brand: try $0.pop("brand", as: String.self),
43 | modules: try $0.remove("modules", as: [JSON].self)
44 | {
45 | try $0.map(ModuleCatalog.init(from:))
46 | },
47 | snippets: try $0.pop("snippets", as: [JSON].self)
48 | {
49 | try $0.map(SnippetCatalog.init(from:))
50 | } ?? [])
51 |
52 | case let unsupported:
53 | throw PackageCatalogError.toolsVersion(unsupported)
54 | }
55 | }
56 | }
57 | }
58 | extension RangeReplaceableCollection
59 | {
60 | @inlinable public
61 | init(parsing utf8:UTF8) throws where UTF8:Collection
62 | {
63 | let json:[JSON] = try JSON.Rule.Array.parse(utf8)
64 | self.init()
65 | self.reserveCapacity(json.count)
66 | for json:JSON in json
67 | {
68 | self.append(try .init(from: json))
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/Sources/PackageCatalogs/SnippetCatalog.swift:
--------------------------------------------------------------------------------
1 | import JSON
2 | import SymbolGraphs
3 | @preconcurrency import SystemPackage
4 |
5 | public
6 | struct SnippetCatalog:Identifiable, Sendable
7 | {
8 | public
9 | let id:ModuleIdentifier
10 | var sources:[FilePath]
11 | var dependencies:[SymbolGraph.Dependency]
12 |
13 | public
14 | init(from json:JSON) throws
15 | {
16 | (self.id, self.sources, self.dependencies) = try json.lint
17 | {
18 | (
19 | try $0.remove("snippet", as: String.self, ModuleIdentifier.init(_:)),
20 | try $0.remove("sources", as: [JSON].self)
21 | {
22 | try $0.map { FilePath.init(try $0.as(String.self)) }
23 | },
24 | try $0.remove("dependencies", as: [JSON].self)
25 | {
26 | try $0.map(SymbolGraph.Dependency.init(from:))
27 | }
28 | )
29 | }
30 | }
31 | public
32 | init(id:ID, sources:[FilePath], dependencies:[SymbolGraph.Dependency])
33 | {
34 | self.id = id
35 | self.sources = sources
36 | self.dependencies = dependencies
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Sources/PackageLoader/Ecosystem.swift:
--------------------------------------------------------------------------------
1 | @_exported import Biome
2 | import PackageResolution
3 | import PackageCatalogs
4 | import SystemExtras
5 |
6 | extension Ecosystem
7 | {
8 | public mutating
9 | func loadToolchains(from directory:FilePath,
10 | matching pattern:MaskedVersion? = nil) throws
11 | {
12 | try Task.checkCancellation()
13 |
14 | let available:String = try directory.appending("swift-versions").read()
15 | let toolchains:[(path:FilePath, version:MaskedVersion)] = available
16 | .split(whereSeparator: \.isWhitespace)
17 | .compactMap
18 | {
19 | if let component:FilePath.Component = .init(String.init($0)),
20 | let version:MaskedVersion = try? .init(parsing: $0),
21 | pattern ?= version
22 | {
23 | return (directory.appending(component), version)
24 | }
25 | else
26 | {
27 | return nil
28 | }
29 | }
30 | for (project, version):(FilePath, MaskedVersion) in toolchains
31 | {
32 | let catalogs:[PackageCatalog] =
33 | try .init(parsing: try project.appending("Package.catalog").read())
34 |
35 | for catalog:PackageCatalog in catalogs
36 | {
37 | try self.updatePackage(catalog.id,
38 | graphs: try catalog.modules.map { try $0.load(project: project) },
39 | brand: catalog.brand,
40 | pins: [.swift: version, .core: version])
41 | }
42 | }
43 | }
44 | public mutating
45 | func loadProjects(from projects:[FilePath]) throws
46 | {
47 | for project:FilePath in projects
48 | {
49 | try Task.checkCancellation()
50 |
51 | print("loading project '\(project)'...")
52 |
53 | let resolution:PackageResolution =
54 | try .init(parsing: try project.appending("Package.resolved").read())
55 | let catalogs:[PackageCatalog] =
56 | try .init(parsing: try project.appending("Package.catalog").read())
57 | for catalog:PackageCatalog in catalogs
58 | {
59 | try self.updatePackage(catalog.id,
60 | graphs: try catalog.modules.map { try $0.load(project: project) },
61 | brand: catalog.brand,
62 | pins: resolution.pins)
63 | }
64 | }
65 |
66 | self.regenerateCaches()
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/Sources/PackageResolution/PackageResolution.Pin.swift:
--------------------------------------------------------------------------------
1 | import SymbolGraphs
2 | import Versions
3 | import JSON
4 |
5 | extension PackageResolution
6 | {
7 | public
8 | struct Pin:Identifiable
9 | {
10 | public
11 | struct State
12 | {
13 | let requirement:Requirement
14 | let revision:String
15 |
16 | public
17 | init(_ requirement:Requirement, revision:String)
18 | {
19 | self.requirement = requirement
20 | self.revision = revision
21 | }
22 | }
23 | public
24 | enum Requirement
25 | {
26 | case version(MaskedVersion)
27 | case branch(String)
28 | }
29 |
30 | public
31 | let id:PackageIdentifier
32 | public
33 | let state:State
34 | let location:String?
35 |
36 | public
37 | init(from json:JSON) throws
38 | {
39 | (self.id, self.state, self.location) = try json.lint(whitelisting: ["kind"])
40 | {
41 | let id:ID = .init(
42 | try $0.pop("identity", as: String.self) ??
43 | $0.remove("package", as: String.self))
44 | let location:String? = try $0.pop("location", as: String.self) ??
45 | $0.pop("repositoryURL", as: String.self)
46 | let state:State = try $0.remove("state")
47 | {
48 | try $0.lint(whitelisting: ["branch"])
49 | {
50 | let revision:String = try $0.remove("revision", as: String.self)
51 | let requirement:Requirement
52 | if let version:String = try $0.pop("version", as: String?.self)
53 | {
54 | requirement = .version(try .init(parsing: version))
55 | }
56 | else
57 | {
58 | requirement = .branch(try $0.remove("branch", as: String.self))
59 | }
60 | return .init(requirement, revision: revision)
61 | }
62 | }
63 | return (id, state, location)
64 | }
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/Sources/PackageResolution/PackageResolution.swift:
--------------------------------------------------------------------------------
1 | import SymbolGraphs
2 | import Versions
3 | import JSON
4 |
5 | public
6 | struct PackageResolution
7 | {
8 | public
9 | var pins:[Pin.ID: MaskedVersion]
10 |
11 | public
12 | init(pins:[Pin])
13 | {
14 | self.pins = [:]
15 | for pin:Pin in pins
16 | {
17 | // these strings are slightly different from the ones we
18 | // parse from url queries
19 | switch pin.state.requirement
20 | {
21 | case .version(let version):
22 | self.pins[pin.id] = version
23 | case .branch(let branch):
24 | self.pins[pin.id] =
25 | (try? .init(parsing: branch)) ??
26 | (try? .init(toolchain: branch))
27 | }
28 | }
29 | }
30 | public
31 | init(from json:JSON) throws
32 | {
33 | let pins:[Pin] = try json.lint
34 | {
35 | switch try $0.remove("version", as: Int.self)
36 | {
37 | case 1:
38 | return try $0.remove("object")
39 | {
40 | try $0.lint
41 | {
42 | try $0.remove("pins", as: [JSON].self)
43 | {
44 | try $0.map(Pin.init(from:))
45 | }
46 | }
47 | }
48 | case 2:
49 | return try $0.remove("pins", as: [JSON].self)
50 | {
51 | try $0.map(Pin.init(from:))
52 | }
53 | default:
54 | fatalError("unsupported Package.resolved format")
55 | }
56 | }
57 | self.init(pins: pins)
58 | }
59 | @inlinable public
60 | init(parsing utf8:UTF8) throws where UTF8:Collection
61 | {
62 | try self.init(from: try JSON.init(parsing: utf8))
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/Sources/PieCharts/Pie.swift:
--------------------------------------------------------------------------------
1 | import SVG
2 |
3 | public
4 | enum Pie
5 | {
6 | @frozen public
7 | struct Sector
8 | {
9 | public
10 | var weight:Int
11 | public
12 | var classes:String
13 |
14 | @inlinable public
15 | init(weight:Int, classes:String = "")
16 | {
17 | self.weight = weight
18 | self.classes = classes
19 | }
20 |
21 | func path(arc:(start:Point, end:Point), radians:Double, divisor:Double,
22 | fringe:Point? = nil)
23 | -> SVG.Element
24 | {
25 | let width:Double = Double.init(self.weight) / divisor
26 |
27 | var d:String = "M 0,0 L \(arc.start)"
28 | if width < 0.375
29 | {
30 | // minor arc
31 | d += " A 1,1 0 0 0 \(arc.end)"
32 | }
33 | else if width > 0.625
34 | {
35 | // major arc
36 | d += " A 1,1 0 1 0 \(arc.end)"
37 | }
38 | else
39 | {
40 | // near-semicircular arc,
41 | // split into 2 segments to avoid degenerate behavior.
42 | let split:Point = .init(radians: radians - 0.5 * Double.pi)
43 | d += " A 1,1 0 0 0 \(split) A 1,1 0 0 0 \(arc.end)"
44 | }
45 | if let fringe:Point = fringe
46 | {
47 | d += " L \(fringe) Z"
48 | }
49 | else
50 | {
51 | d += "Z"
52 | }
53 | return .path(attributes: self.classes.isEmpty ?
54 | [.d(d)] : [.d(d), .class(self.classes)])
55 | }
56 | }
57 |
58 | struct Point:CustomStringConvertible
59 | {
60 | var x:Double
61 | var y:Double
62 |
63 | init(_ x:Double, _ y:Double)
64 | {
65 | self.x = x
66 | self.y = y
67 | }
68 | init(radians:Double, radius:Double = 1.0)
69 | {
70 | self.init(radius * _cos(radians), radius * -_sin(radians))
71 | }
72 |
73 | var description:String
74 | {
75 | "\(self.x),\(self.y)"
76 | }
77 | }
78 |
79 | public static
80 | func svg(_ sectors:[Sector]) -> SVG.Root
81 | {
82 | let divisor:Double = .init(sectors.reduce(0) { $0 + $1.weight })
83 |
84 | var start:Point = .init(1, 0)
85 | var accumulated:Int = 0
86 | var paths:[SVG.Element] = []
87 | paths.reserveCapacity(sectors.count)
88 | for sector:Sector in sectors.dropLast()
89 | {
90 | accumulated += sector.weight
91 |
92 | let fraction:Double = Double.init(accumulated) / divisor,
93 | radians:Double = 2 * Double.pi * fraction
94 |
95 | let end:Point = .init(radians: radians)
96 | var fringe:Point = .init(radians: radians + 0.1, radius: 0.5)
97 | // do not let the fringe get past the +x pole
98 | if fraction > 0.75
99 | {
100 | fringe.y = min(fringe.y, 0)
101 | }
102 | paths.append(sector.path(arc: (start, end),
103 | radians: radians,
104 | divisor: divisor,
105 | fringe: fringe))
106 | start = end
107 | }
108 | if let sector:Sector = sectors.last
109 | {
110 | paths.append(sector.path(arc: (start, .init(1, 0)),
111 | radians: 2 * Double.pi,
112 | divisor: divisor))
113 | }
114 | return .init(.g(paths), attributes: [.viewBox("-1 -1 2 2")])
115 | }
116 | }
--------------------------------------------------------------------------------
/Sources/Preview/Main.swift:
--------------------------------------------------------------------------------
1 | import ArgumentParser
2 | import Backtrace
3 | @preconcurrency import SystemPackage
4 | import NIO
5 |
6 | @main
7 | struct Main:AsyncParsableCommand
8 | {
9 | static
10 | var configuration:CommandConfiguration = .init(abstract: "preview swift-biome documentation")
11 |
12 | @Option(name: [.customShort("p"), .customLong("port")],
13 | help: "port number to listen on")
14 | var port:Int = 8080
15 | @Option(name: [.customShort("h"), .customLong("host")],
16 | help: "private host name to listen on")
17 | var host:String = "0.0.0.0"
18 | @Option(name: [.customShort("d"), .customLong("domain")],
19 | help: "public host name")
20 | var domain:String = "127.0.0.1"
21 |
22 | @Option(name: [.customLong("swift")],
23 | help: "swift standard library version")
24 | var swift:String = "*"
25 |
26 | @Option(name: [.customLong("resources")],
27 | help: "path to a copy of the 'swift-biome-resources' repository")
28 | var resources:String = "resources"
29 |
30 | @Argument(help: "path(s) to project repositories")
31 | var projects:[String]
32 |
33 | static
34 | func main() async
35 | {
36 | do
37 | {
38 | let command:Self = try Self.parseAsRoot() as! Self
39 | try await command.run()
40 | }
41 | catch
42 | {
43 | exit(withError: error)
44 | }
45 | }
46 |
47 | func run() async throws
48 | {
49 | Backtrace.install()
50 |
51 | let group:MultiThreadedEventLoopGroup = .init(numberOfThreads: 2)
52 | let port:Int = self.port
53 |
54 | async let preview:Preview = .init(projects: self.projects.map(FilePath.init(_:)),
55 | resources: .init(self.resources),
56 | swift: try .init(parsing: self.swift))
57 |
58 | let requests:AsyncStream = .init
59 | {
60 | (queue:AsyncStream.Continuation) in
61 | Task.init
62 | {
63 | while true
64 | {
65 | do
66 | {
67 | try await self.open(port: port, queue: queue, group: group)
68 | }
69 | catch let error
70 | {
71 | print(error)
72 | }
73 | }
74 | }
75 | }
76 |
77 | try await preview.serve(requests)
78 | }
79 |
80 | private
81 | func open(port:Int,
82 | queue:AsyncStream.Continuation,
83 | group:MultiThreadedEventLoopGroup)
84 | async throws
85 | {
86 | let channels:[any Channel] = try await withThrowingTaskGroup(of: (any Channel).self)
87 | {
88 | (tasks:inout ThrowingTaskGroup) in
89 |
90 | tasks.addTask
91 | {
92 | try await Listener.send(to: queue,
93 | domain: self.domain,
94 | host: self.host,
95 | port: port,
96 | group: group)
97 | }
98 |
99 | var channels:[any Channel] = []
100 | for try await channel:any Channel in tasks
101 | {
102 | let address:SocketAddress? = channel.localAddress
103 | print("opened channel on \(address?.description ?? "")")
104 | channels.append(channel)
105 | }
106 | return channels
107 | }
108 |
109 | await withTaskGroup(of: Void.self)
110 | {
111 | (tasks:inout TaskGroup) in
112 |
113 | for channel:Channel in channels
114 | {
115 | tasks.addTask
116 | {
117 | // must capture this before the channel closes, since
118 | // the local address will be cleared
119 | let address:SocketAddress? = channel.localAddress
120 | do
121 | {
122 | try await channel.closeFuture.get()
123 | }
124 | catch let error
125 | {
126 | print(error)
127 | }
128 | print("closed channel \(address?.description ?? "")")
129 | }
130 | }
131 |
132 | await tasks.next()
133 |
134 | for channel:Channel in channels
135 | {
136 | tasks.addTask
137 | {
138 | do
139 | {
140 | try await channel.pipeline.close(mode: .all)
141 | }
142 | catch let error
143 | {
144 | print(error)
145 | }
146 | }
147 | }
148 | }
149 | }
150 | }
151 |
--------------------------------------------------------------------------------
/Sources/Preview/Preview.swift:
--------------------------------------------------------------------------------
1 | import PackageLoader
2 | import SystemExtras
3 | import WebSemantics
4 | import Resources
5 | import HTML
6 | import NIO
7 | import NIOHTTP1
8 | import URI
9 |
10 | actor Preview
11 | {
12 | struct Request:ExpressibleByPartialHTTPRequest, Sendable
13 | {
14 | let uri:URI
15 |
16 | init?(source _:SocketAddress?, head:HTTPRequestHead)
17 | {
18 | if let uri:URI = try? .init(absolute: head.uri)
19 | {
20 | self.uri = uri
21 | }
22 | else
23 | {
24 | return nil
25 | }
26 | }
27 | }
28 |
29 | private
30 | var ecosystem:Ecosystem
31 |
32 | init(projects:[FilePath], resources:FilePath, swift:MaskedVersion?) async throws
33 | {
34 | self.ecosystem = .init()
35 |
36 | try self.ecosystem.loadToolchains(from: resources.appending("swift"),
37 | matching: swift)
38 | try self.ecosystem.loadProjects(from: projects)
39 |
40 | try self.loadResources(from: resources)
41 |
42 | self.ecosystem.move(.init(
43 | hashing: try resources.appending(["css", "biome.css"]).read(),
44 | type: .utf8(encoded: .css)),
45 | to: ["biome.css"])
46 | self.ecosystem.move(.init(
47 | hashing: try resources.appending(["js", "main.js"]).read(),
48 | type: .utf8(encoded: .javascript)),
49 | to: ["search.js"])
50 | }
51 |
52 | private
53 | func loadResources(from directory:FilePath) throws
54 | {
55 | try Task.checkCancellation()
56 |
57 | let fonts:[(external:String, internal:String)] =
58 | [
59 | ("text-45", "Literata-Regular"),
60 | ("text-47", "Literata-RegularItalic"),
61 | ("text-65", "Literata-SemiBold"),
62 | ("text-67", "Literata-SemiBoldItalic"),
63 | ]
64 | try self.loadFonts(fonts, .ttf, .woff2,
65 | from: directory.appending(["fonts", "Literata"]))
66 | }
67 | private
68 | func loadFonts(_ fonts:[(external:String, internal:String)], _ types:MIME...,
69 | from directory:FilePath) throws
70 | {
71 | for name:(external:String, internal:String) in fonts
72 | {
73 | for type:MIME in types
74 | {
75 | let path:FilePath =
76 | directory.appending("\(name.internal).\(type.extension)")
77 | self.ecosystem.move(.init(hashing: try path.read(), type: type),
78 | to: ["\(name.external).\(type.extension)"])
79 | }
80 | }
81 | }
82 | }
83 |
84 | extension Preview
85 | {
86 | func serve(_ requests:AsyncStream) async
87 | {
88 | for await (request, promise):Request.Enqueued in requests
89 | {
90 | promise.succeed(self.ecosystem[request.uri])
91 | }
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/Sources/SymbolGraphConvert/Main.swift:
--------------------------------------------------------------------------------
1 | @preconcurrency import SystemExtras
2 | import ArgumentParser
3 | import PackageCatalogs
4 | import SymbolGraphs
5 |
6 | @main
7 | struct Main:AsyncParsableCommand
8 | {
9 | static
10 | var configuration:CommandConfiguration = .init(
11 | abstract: "compile swift partial symbolgraphs to intermediate representation")
12 |
13 | @Option(name: [.customShort("j"), .customLong("threads")],
14 | help: "maximum number of threads to use")
15 | var threads:Int = 8
16 | @Argument(help: "path(s) to directories containing a Package.catalog file")
17 | var projects:[String]
18 |
19 | static
20 | func main() async
21 | {
22 | do
23 | {
24 | let command:Self = try Self.parseAsRoot() as! Self
25 | try await command.run()
26 | }
27 | catch
28 | {
29 | exit(withError: error)
30 | }
31 | }
32 |
33 | func run() async throws
34 | {
35 | try await withThrowingTaskGroup(of: (String, FilePath).self)
36 | {
37 | (queue:inout ThrowingTaskGroup<(String, FilePath), Error>) in
38 |
39 | var width:Int = 0
40 | for project:FilePath in self.projects.map(FilePath.init(_:))
41 | {
42 | try Task.checkCancellation()
43 |
44 | let catalogs:[PackageCatalog] =
45 | try .init(parsing: try project.appending("Package.catalog").read())
46 |
47 | print("found Package.catalog in '\(project)'...")
48 |
49 | for catalog:PackageCatalog in catalogs
50 | {
51 | for catalog:ModuleCatalog in catalog.modules
52 | {
53 | let graph:RawSymbolGraph = try catalog.read(relativeTo: project)
54 | if width < self.threads
55 | {
56 | width += 1
57 | }
58 | else if let (ss, output):(String, FilePath) = try await queue.next()
59 | {
60 | try output.write(ss)
61 | }
62 | queue.addTask
63 | {
64 | let output:FilePath = project.appending("\(graph.id).ss")
65 | let graph:SymbolGraph = try .init(graph)
66 | return (graph.serialized.description, output)
67 | }
68 | }
69 | }
70 | }
71 | for try await (ss, output):(String, FilePath) in queue
72 | {
73 | try output.write(ss)
74 | }
75 | }
76 | }
77 | }
--------------------------------------------------------------------------------
/Sources/SymbolGraphs/Availability/Availability.Domain.swift:
--------------------------------------------------------------------------------
1 | extension Availability
2 | {
3 | // https://github.com/apple/swift/blob/main/lib/SymbolGraphGen/AvailabilityMixin.cpp
4 | @frozen public
5 | enum Domain:RawRepresentable
6 | {
7 | case general
8 | case swift
9 | case tools
10 | case platform(Platform)
11 |
12 | public
13 | init?(rawValue:String)
14 | {
15 | switch rawValue
16 | {
17 | case "*": self = .general
18 | case "Swift": self = .swift
19 | case "SwiftPM": self = .tools
20 | default:
21 | guard let platform:Platform = .init(rawValue: rawValue)
22 | else
23 | {
24 | return nil
25 | }
26 | self = .platform(platform)
27 | }
28 | }
29 | public
30 | var rawValue:String
31 | {
32 | switch self
33 | {
34 | case .general: return "*"
35 | case .swift: return "Swift"
36 | case .tools: return "SwiftPM"
37 | case .platform(let platform): return platform.rawValue
38 | }
39 | }
40 | }
41 | }
--------------------------------------------------------------------------------
/Sources/SymbolGraphs/Availability/Availability.swift:
--------------------------------------------------------------------------------
1 | import Versions
2 | import JSON
3 |
4 | @frozen public
5 | struct Availability:Equatable, Sendable
6 | {
7 | public
8 | var swift:SwiftAvailability?
9 | //let tools:SwiftAvailability?
10 | public
11 | var general:UnversionedAvailability?
12 | public
13 | var platforms:[Platform: VersionedAvailability]
14 |
15 | @inlinable public
16 | var isEmpty:Bool
17 | {
18 | if case nil = self.swift,
19 | case nil = self.general,
20 | self.platforms.isEmpty
21 | {
22 | return true
23 | }
24 | else
25 | {
26 | return false
27 | }
28 | }
29 | @inlinable public
30 | var isUsable:Bool
31 | {
32 | if let generally:UnversionedAvailability = self.general,
33 | generally.unavailable || generally.deprecated
34 | {
35 | return false
36 | }
37 | if let currently:SwiftAvailability = self.swift,
38 | !currently.isUsable
39 | {
40 | return false
41 | }
42 | else
43 | {
44 | return true
45 | }
46 | }
47 | @inlinable public
48 | init(swift:SwiftAvailability? = nil,
49 | general:UnversionedAvailability? = nil,
50 | platforms:[Platform: VersionedAvailability] = [:])
51 | {
52 | self.swift = swift
53 | self.general = general
54 | self.platforms = platforms
55 | }
56 | }
57 | extension Availability
58 | {
59 | init(lowering json:[JSON]) throws
60 | {
61 | try self.init(try json.map
62 | {
63 | try $0.lint
64 | {
65 | let deprecated:VersionedAvailability.Deprecation?
66 | if let flag:Bool = try $0.pop("isUnconditionallyDeprecated", as: Bool.self)
67 | {
68 | deprecated = flag ? .always : nil
69 | }
70 | else if let version:MaskedVersion? =
71 | try $0.pop("deprecated", MaskedVersion.init(exactly:))
72 | {
73 | deprecated = version.map(VersionedAvailability.Deprecation.since(_:))
74 | }
75 | else
76 | {
77 | deprecated = nil
78 | }
79 | // possible to be both unconditionally unavailable and unconditionally deprecated
80 | let availability:VersionedAvailability = .init(
81 | unavailable: try $0.pop("isUnconditionallyUnavailable", as: Bool.self) ?? false,
82 | deprecated: deprecated,
83 | introduced: try $0.pop("introduced", MaskedVersion.init(exactly:)) ?? nil,
84 | obsoleted: try $0.pop("obsoleted", MaskedVersion.init(exactly:)) ?? nil,
85 | renamed: try $0.pop("renamed", as: String?.self),
86 | message: try $0.pop("message", as: String?.self))
87 | let domain:Domain = try $0.remove("domain") { try $0.as(cases: Domain.self) }
88 | return (key: domain, value: availability)
89 | }
90 | })
91 | }
92 | private
93 | init(_ items:[(key:Domain, value:VersionedAvailability)]) throws
94 | {
95 | self.init()
96 | for (key, value):(Domain, VersionedAvailability) in items
97 | {
98 | switch key
99 | {
100 | case .general:
101 | if case nil = self.general
102 | {
103 | self.general = .init(value)
104 | }
105 | else
106 | {
107 | throw SymbolGraphDecodingError.duplicateAvailabilityDomain(key)
108 | }
109 |
110 | case .swift:
111 | if case nil = self.swift
112 | {
113 | self.swift = .init(value)
114 | }
115 | else
116 | {
117 | throw SymbolGraphDecodingError.duplicateAvailabilityDomain(key)
118 | }
119 |
120 | case .tools:
121 | fatalError("unimplemented (yet)")
122 |
123 | case .platform(let platform):
124 | guard case nil = self.platforms.updateValue(value, forKey: platform)
125 | else
126 | {
127 | throw SymbolGraphDecodingError.duplicateAvailabilityDomain(key)
128 | }
129 | }
130 | }
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/Sources/SymbolGraphs/Availability/MaskedVersion.swift:
--------------------------------------------------------------------------------
1 | import Versions
2 | import JSON
3 |
4 | extension MaskedVersion
5 | {
6 | init?(exactly json:JSON) throws
7 | {
8 | do
9 | {
10 | try self.init(from: json)
11 | }
12 | catch let error as JSON.RecursiveError
13 | {
14 | guard error.next is JSON.IntegerOverflowError
15 | else
16 | {
17 | throw error
18 | }
19 | return nil
20 | }
21 | }
22 | init(from json:JSON) throws
23 | {
24 | self = try json.lint
25 | {
26 | let major:UInt16 = try $0.remove("major", as: UInt16.self)
27 | guard let minor:UInt16 = try $0.pop("minor", as: UInt16.self)
28 | else
29 | {
30 | return .major(major)
31 | }
32 | guard let patch:UInt16 = try $0.pop("patch", as: UInt16.self)
33 | else
34 | {
35 | return .minor(major, minor)
36 | }
37 | return .patch(major, minor, patch)
38 | }
39 | }
40 | }
--------------------------------------------------------------------------------
/Sources/SymbolGraphs/Availability/Platform.swift:
--------------------------------------------------------------------------------
1 | @frozen public
2 | enum Platform:String, CaseIterable, Hashable, Sendable
3 | {
4 | case iOS
5 | case macOS
6 | case macCatalyst
7 | case tvOS
8 | case watchOS
9 | case windows = "Windows"
10 | case openBSD = "OpenBSD"
11 |
12 | case iOSApplicationExtension
13 | case macOSApplicationExtension
14 | case macCatalystApplicationExtension
15 | case tvOSApplicationExtension
16 | case watchOSApplicationExtension
17 | }
--------------------------------------------------------------------------------
/Sources/SymbolGraphs/Availability/SwiftAvailability.swift:
--------------------------------------------------------------------------------
1 | import Versions
2 |
3 | @frozen public
4 | struct SwiftAvailability:Equatable, Sendable
5 | {
6 | // unconditionals not allowed
7 | public
8 | var deprecated:MaskedVersion?
9 | public
10 | var introduced:MaskedVersion?
11 | public
12 | var obsoleted:MaskedVersion?
13 | public
14 | var renamed:String?
15 | public
16 | var message:String?
17 |
18 | @inlinable public
19 | var isUsable:Bool
20 | {
21 | if case _? = self.deprecated
22 | {
23 | return false
24 | }
25 | if case _? = self.obsoleted
26 | {
27 | return false
28 | }
29 | else
30 | {
31 | return true
32 | }
33 | }
34 |
35 | init(_ versioned:VersionedAvailability)
36 | {
37 | if case .since(let deprecated) = versioned.deprecated
38 | {
39 | self.deprecated = deprecated
40 | }
41 | else
42 | {
43 | self.deprecated = nil
44 | }
45 | self.introduced = versioned.introduced
46 | self.obsoleted = versioned.obsoleted
47 | self.renamed = versioned.renamed
48 | self.message = versioned.message
49 | }
50 | }
--------------------------------------------------------------------------------
/Sources/SymbolGraphs/Availability/UnversionedAvailability.swift:
--------------------------------------------------------------------------------
1 | @frozen public
2 | struct UnversionedAvailability:Equatable, Sendable
3 | {
4 | public
5 | var unavailable:Bool
6 | public
7 | var deprecated:Bool
8 | public
9 | var renamed:String?
10 | public
11 | var message:String?
12 |
13 | init(_ versioned:VersionedAvailability)
14 | {
15 | self.unavailable = versioned.unavailable
16 | self.deprecated = versioned.isGenerallyDeprecated
17 | self.renamed = versioned.renamed
18 | self.message = versioned.message
19 | }
20 | }
--------------------------------------------------------------------------------
/Sources/SymbolGraphs/Availability/VersionedAvailability.swift:
--------------------------------------------------------------------------------
1 | import Versions
2 |
3 | @frozen public
4 | struct VersionedAvailability:Equatable, Sendable
5 | {
6 | @frozen public
7 | enum Deprecation:Hashable, Sendable
8 | {
9 | case always
10 | case since(MaskedVersion)
11 | }
12 | public
13 | var unavailable:Bool
14 | // .some(nil) represents unconditional deprecation
15 | public
16 | var deprecated:Deprecation?
17 | public
18 | var introduced:MaskedVersion?
19 | public
20 | var obsoleted:MaskedVersion?
21 | public
22 | var renamed:String?
23 | public
24 | var message:String?
25 |
26 | @inlinable public
27 | var isGenerallyDeprecated:Bool
28 | {
29 | if case .always? = self.deprecated
30 | {
31 | return true
32 | }
33 | else
34 | {
35 | return false
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Sources/SymbolGraphs/Graphs/RawSymbolGraph.swift:
--------------------------------------------------------------------------------
1 | public
2 | struct RawSymbolGraph:Identifiable, Sendable
3 | {
4 | public
5 | typealias Subgraph =
6 | (
7 | culture:ModuleIdentifier,
8 | namespace:ModuleIdentifier,
9 | utf8:[UInt8]
10 | )
11 |
12 | public
13 | let id:ModuleIdentifier
14 | public
15 | var dependencies:[SymbolGraph.Dependency],
16 | extensions:[SymbolGraph.Extension]
17 | public
18 | var subgraphs:[Subgraph]
19 |
20 | public
21 | init(id:ID,
22 | dependencies:[SymbolGraph.Dependency] = [],
23 | extensions:[SymbolGraph.Extension] = [],
24 | subgraphs:[Subgraph] = [])
25 | {
26 | self.id = id
27 | self.dependencies = dependencies
28 | self.extensions = extensions
29 | self.subgraphs = subgraphs
30 | }
31 | }
--------------------------------------------------------------------------------
/Sources/SymbolGraphs/Graphs/SymbolGraph.Dependency.swift:
--------------------------------------------------------------------------------
1 | import JSON
2 |
3 | extension SymbolGraph
4 | {
5 | @frozen public
6 | struct Dependency:Sendable
7 | {
8 | public
9 | var package:PackageIdentifier
10 | public
11 | var modules:[ModuleIdentifier]
12 |
13 | public
14 | init(package:PackageIdentifier, modules:[ModuleIdentifier])
15 | {
16 | self.package = package
17 | self.modules = modules
18 | }
19 | }
20 | }
--------------------------------------------------------------------------------
/Sources/SymbolGraphs/Graphs/SymbolGraph.Edge.swift:
--------------------------------------------------------------------------------
1 | extension SymbolGraph.Edge:Sendable where Target:Sendable {}
2 | extension SymbolGraph.Edge:Hashable where Target:Hashable {}
3 | extension SymbolGraph.Edge:Equatable where Target:Equatable {}
4 |
5 | extension SymbolGraph.Edge.Relation:Sendable where Target:Sendable {}
6 | extension SymbolGraph.Edge.Relation:Hashable where Target:Hashable {}
7 | extension SymbolGraph.Edge.Relation:Equatable where Target:Equatable {}
8 |
9 | extension SymbolGraph
10 | {
11 | @frozen public
12 | struct Edge
13 | {
14 | @frozen public
15 | enum Relation
16 | {
17 | case feature
18 | case member
19 | case conformer([Generic.Constraint])
20 | case subclass
21 | case override
22 | case requirement
23 | case optionalRequirement
24 | case defaultImplementation
25 |
26 | func forEach(_ body:(Target) throws -> ()) rethrows
27 | {
28 | if case .conformer(let constraints) = self
29 | {
30 | for constraint:Generic.Constraint in constraints
31 | {
32 | try constraint.forEach(body)
33 | }
34 | }
35 | }
36 | @inlinable public
37 | func map(_ transform:(Target) throws -> T) rethrows -> Edge.Relation
38 | {
39 | switch self
40 | {
41 | case .feature:
42 | return .feature
43 | case .member:
44 | return .member
45 | case .conformer(let constraints):
46 | return .conformer(try constraints.map { try $0.map(transform) })
47 | case .subclass:
48 | return .subclass
49 | case .override:
50 | return .override
51 | case .requirement:
52 | return .requirement
53 | case .optionalRequirement:
54 | return .optionalRequirement
55 | case .defaultImplementation:
56 | return .defaultImplementation
57 | }
58 | }
59 | }
60 |
61 | public
62 | let source:Target
63 | public
64 | let relation:Relation
65 | public
66 | let target:Target
67 |
68 | @inlinable public
69 | init(_ source:Target, is relation:Relation, of target:Target)
70 | {
71 | self.source = source
72 | self.relation = relation
73 | self.target = target
74 | }
75 |
76 | func forEach(_ body:(Target) throws -> ()) rethrows
77 | {
78 | try body(self.source)
79 | try body(self.target)
80 | try self.relation.forEach(body)
81 | }
82 | @inlinable public
83 | func map(_ transform:(Target) throws -> T) rethrows -> Edge
84 | {
85 | .init(try transform(self.source),
86 | is: try self.relation.map(transform),
87 | of: try transform(self.target))
88 | }
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/Sources/SymbolGraphs/Graphs/SymbolGraph.Hint.swift:
--------------------------------------------------------------------------------
1 | extension SymbolGraph.Hint:Sendable where Target:Sendable {}
2 | extension SymbolGraph.Hint:Hashable where Target:Hashable {}
3 | extension SymbolGraph.Hint:Equatable where Target:Equatable {}
4 |
5 | extension SymbolGraph
6 | {
7 | struct Hint
8 | {
9 | let source:Target
10 | let origin:Target
11 |
12 | func forEach(_ body:(Target) throws -> ()) rethrows
13 | {
14 | try body(self.source)
15 | try body(self.origin)
16 | }
17 | func map(_ transform:(Target) throws -> T) rethrows -> Hint
18 | {
19 | .init(source: try transform(self.source),
20 | origin: try transform(self.origin))
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/Sources/SymbolGraphs/Graphs/SymbolGraph.Relationship.swift:
--------------------------------------------------------------------------------
1 | import JSON
2 |
3 | extension SymbolGraph
4 | {
5 | struct Relationship:Sendable
6 | {
7 | let edge:Edge
8 | let origin:SymbolIdentifier?
9 |
10 | var hint:Hint?
11 | {
12 | self.origin.map { .init(source: self.edge.source, origin: $0) }
13 | }
14 | }
15 | }
16 |
17 | extension SymbolGraph.Relationship
18 | {
19 | init(from json:JSON) throws
20 | {
21 | (self.edge, self.origin) = try json.lint(whitelisting: ["targetFallback"])
22 | {
23 | let target:SymbolIdentifier = try $0.remove("target", SymbolIdentifier.init(from:))
24 | let source:USR = try $0.remove("source", as: String.self)
25 | {
26 | try .init(parsing: $0.utf8)
27 | }
28 | // https://github.com/apple/swift/blob/main/lib/SymbolGraphGen/Edge.h
29 | let edge:SymbolGraph.Edge
30 | switch (source, try $0.remove("kind", as: String.self))
31 | {
32 | case (.synthesized(from: let source, for: target), "memberOf"):
33 | // only 'memberOf' edges may come from synthetic sources
34 | edge = .init(source, is: .feature, of: target)
35 |
36 | case (.natural(let source), "memberOf"):
37 | edge = .init(source, is: .member, of: target)
38 | case (.natural(let source), "conformsTo"):
39 | // only 'conformsTo' edges may contain constraints
40 | let constraints:[Generic.Constraint] =
41 | try $0.pop("swiftConstraints", as: [JSON]?.self)
42 | {
43 | try $0.map(Generic.Constraint.init(lowering:))
44 | } ?? []
45 | edge = .init(source, is: .conformer(constraints), of: target)
46 | case (.natural(let source), "inheritsFrom"):
47 | edge = .init(source, is: .subclass, of: target)
48 | case (.natural(let source), "overrides"):
49 | edge = .init(source, is: .override, of: target)
50 | case (.natural(let source), "requirementOf"):
51 | edge = .init(source, is: .requirement, of: target)
52 | case (.natural(let source), "optionalRequirementOf"):
53 | edge = .init(source, is: .optionalRequirement, of: target)
54 | case (.natural(let source), "defaultImplementationOf"):
55 | edge = .init(source, is: .defaultImplementation, of: target)
56 |
57 | case (.natural(_), let kind):
58 | throw SymbolGraphDecodingError.unknownRelationshipKind(kind)
59 | case (let source, let kind):
60 | throw SymbolGraphDecodingError.invalidRelationshipKind(source, is: kind)
61 | }
62 |
63 | let origin:SymbolIdentifier? = try $0.pop("sourceOrigin")
64 | {
65 | try $0.lint(whitelisting: ["displayName"])
66 | {
67 | try $0.remove("identifier", SymbolIdentifier.init(from:))
68 | }
69 | }
70 | return (edge, origin)
71 | }
72 | }
73 | }
--------------------------------------------------------------------------------
/Sources/SymbolGraphs/Graphs/SymbolGraph.SourceFeature.swift:
--------------------------------------------------------------------------------
1 | extension SymbolGraph.SourceFeature:Sendable where Target:Sendable {}
2 | extension SymbolGraph.SourceFeature:Hashable where Target:Hashable {}
3 | extension SymbolGraph.SourceFeature:Equatable where Target:Equatable {}
4 | extension SymbolGraph.SourceFeature:Comparable where Target:Comparable
5 | {
6 | @inlinable public static
7 | func < (lhs:Self, rhs:Self) -> Bool
8 | {
9 | (lhs.line, lhs.character, lhs.symbol) < (rhs.line, rhs.character, rhs.symbol)
10 | }
11 | }
12 |
13 | extension SymbolGraph
14 | {
15 | @frozen public
16 | struct SourceFeature
17 | {
18 | public
19 | let line:Int,
20 | character:Int,
21 | symbol:Target
22 |
23 | func forEach(_ body:(Target) throws -> ()) rethrows
24 | {
25 | try body(self.symbol)
26 | }
27 | func map(_ transform:(Target) throws -> T) rethrows -> SourceFeature
28 | {
29 | .init(line: self.line,
30 | character: self.character,
31 | symbol: try transform(self.symbol))
32 | }
33 | }
34 | }
--------------------------------------------------------------------------------
/Sources/SymbolGraphs/Graphs/SymbolGraph.Vertex.swift:
--------------------------------------------------------------------------------
1 | import Notebook
2 | import JSON
3 |
4 | extension SymbolGraph.Vertex:Sendable where Target:Sendable {}
5 | extension SymbolGraph.Vertex:Equatable where Target:Equatable {}
6 |
7 | extension SymbolGraph
8 | {
9 | @frozen public
10 | struct Vertex
11 | {
12 | public
13 | var path:Path
14 | public
15 | var community:Community
16 | public
17 | var declaration:Declaration
18 | public
19 | var documentation:Documentation?
20 |
21 | @inlinable public
22 | init(path:Path,
23 | community:Community,
24 | declaration:Declaration,
25 | documentation:Documentation? = nil)
26 | {
27 | self.path = path
28 | self.community = community
29 | self.declaration = declaration
30 | self.documentation = documentation
31 | }
32 |
33 | func forEach(_ body:(Target) throws -> ()) rethrows
34 | {
35 | try self.declaration.forEach(body)
36 | try self.documentation?.forEach(body)
37 | }
38 | @inlinable public
39 | func map(_ transform:(Target) throws -> T) rethrows -> Vertex
40 | {
41 | .init(path: self.path,
42 | community: self.community,
43 | declaration: try self.declaration.map(transform),
44 | documentation: try self.documentation?.map(transform))
45 | }
46 | }
47 | }
48 | extension SymbolGraph.Vertex
49 | {
50 | static
51 | func `protocol`(named name:String) -> Self
52 | {
53 | let fragments:[Notebook.Fragment] =
54 | [
55 | .init("protocol", color: .keywordText),
56 | .init(" ", color: .text),
57 | .init(name, color: .identifier),
58 | ]
59 | return .init(path: .init(last: name),
60 | community: .protocol,
61 | declaration: .init(
62 | fragments: .init(fragments),
63 | signature: .init(fragments)))
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/Sources/SymbolGraphs/IR/Generic+IR.swift:
--------------------------------------------------------------------------------
1 | import JSON
2 |
3 | extension Generic
4 | {
5 | init(from json:JSON) throws
6 | {
7 | let tuple:[JSON] = try json.as([JSON].self, count: 3)
8 | self.name = try tuple.load(0)
9 | self.index = try tuple.load(1)
10 | self.depth = try tuple.load(2)
11 | }
12 | var serialized:JSON
13 | {
14 | [
15 | .string(self.name),
16 | .number(self.index),
17 | .number(self.depth)
18 | ]
19 | }
20 | }
21 |
22 |
--------------------------------------------------------------------------------
/Sources/SymbolGraphs/IR/Generic.Constraint+IR.swift:
--------------------------------------------------------------------------------
1 | import JSON
2 |
3 | extension Generic.Constraint
4 | {
5 | init(from json:JSON) throws
6 | {
7 | let tuple:[JSON] = try json.as([JSON].self) { 3 ... 4 ~= $0 }
8 | self.init(
9 | try tuple.load(0),
10 | try tuple.load(1) { try $0.as(cases: Generic.Verb.self) },
11 | try tuple.load(2),
12 | target: try tuple.count == 4 ? tuple.load(3, as: Int.self) : nil
13 | )
14 | }
15 | var serialized:JSON
16 | {
17 | if let target:Int = self.target
18 | {
19 | return
20 | [
21 | .string(self.subject),
22 | .number(self.verb.rawValue),
23 | .string(self.object),
24 | .number(target),
25 | ]
26 | }
27 | else
28 | {
29 | return
30 | [
31 | .string(self.subject),
32 | .number(self.verb.rawValue),
33 | .string(self.object),
34 | ]
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/Sources/SymbolGraphs/IR/Notebook+IR.swift:
--------------------------------------------------------------------------------
1 | import Notebook
2 | import JSON
3 |
4 | extension Notebook
5 | {
6 | init(from json:JSON) throws
7 | {
8 | self.init(try json.as([JSON].self).lazy.map
9 | {
10 | let tuple:[JSON] = try $0.as([JSON].self, count: 2)
11 | let text:String = try tuple.load(0)
12 | let color:Highlight = try tuple.load(1) { try $0.as(cases: Highlight.self) }
13 | return (text, color)
14 | })
15 | }
16 | }
17 | extension Notebook
18 | {
19 | init(from json:JSON) throws
20 | {
21 | self.init(try json.as([JSON].self).lazy.map(Fragment.init(from:)))
22 | }
23 | }
24 |
25 | extension Notebook.Fragment
26 | {
27 | init(from json:JSON) throws
28 | {
29 | let tuple:[JSON] = try json.as([JSON].self) { 2 ... 3 ~= $0 }
30 | self.init( try tuple.load(0),
31 | color: try tuple.load(1) { try $0.as(cases: Highlight.self) },
32 | link: try tuple.count == 3 ? tuple.load(2) : nil)
33 | }
34 | var serialized:JSON
35 | {
36 | if let link:Int = self.link
37 | {
38 | return [.string(self.text), .number(self.color.rawValue), .number(link)]
39 | }
40 | else
41 | {
42 | return [.string(self.text), .number(self.color.rawValue)]
43 | }
44 | }
45 | }
46 | extension Notebook.Fragment
47 | {
48 | var serialized:JSON
49 | {
50 | [.string(self.text), .number(self.color.rawValue)]
51 | }
52 | }
--------------------------------------------------------------------------------
/Sources/SymbolGraphs/IR/SymbolGraph.Vertex+IR.swift:
--------------------------------------------------------------------------------
1 | import JSON
2 | import Notebook
3 |
4 | extension IR
5 | {
6 | enum Vertex
7 | {
8 | static let path:String = "p"
9 | static let origin:String = "o"
10 | static let comment:String = "d"
11 | }
12 | enum Declaration
13 | {
14 | static let fragments:String = "f"
15 | static let signature:String = "s"
16 | static let availability:String = "a"
17 | static let extensionConstraints:String = "e"
18 | static let genericConstraints:String = "c"
19 | static let generics:String = "g"
20 | }
21 | }
22 | extension SymbolGraph.Vertex
23 | {
24 | init(from json:JSON, community:Community) throws
25 | {
26 | self = try json.lint
27 | {
28 | .init(path: try $0.remove(IR.Vertex.path, Path.init(from:)) as Path,
29 | community: community,
30 | declaration: .init(
31 | fragments: try $0.remove(IR.Declaration.fragments,
32 | Notebook.init(from:)),
33 | signature: try $0.remove(IR.Declaration.signature,
34 | Notebook.init(from:)),
35 | availability:
36 | try $0.pop(IR.Declaration.availability, Availability.init(from:)) ?? .init(),
37 | extensionConstraints:
38 | try $0.pop(IR.Declaration.extensionConstraints, as: [JSON].self)
39 | {
40 | try $0.map(Generic.Constraint.init(from:))
41 | } ?? [],
42 | genericConstraints:
43 | try $0.pop(IR.Declaration.genericConstraints, as: [JSON].self)
44 | {
45 | try $0.map(Generic.Constraint.init(from:))
46 | } ?? [],
47 | generics: try $0.pop(IR.Declaration.generics, as: [JSON].self)
48 | {
49 | try $0.map(Generic.init(from:))
50 | } ?? []),
51 | documentation: .extends(try $0.pop(IR.Vertex.origin, as: Int.self),
52 | with: try $0.pop(IR.Vertex.comment, as: String.self)))
53 | }
54 | }
55 | var serialized:JSON
56 | {
57 | var items:[(key:String, value:JSON)] =
58 | [
59 | (IR.Vertex.path, .array(self.path.map(JSON.string(_:)))),
60 | (IR.Declaration.fragments, .array(self.declaration.fragments.map(\.serialized))),
61 | (IR.Declaration.signature, .array(self.declaration.signature.map(\.serialized))),
62 | ]
63 | if !self.declaration.availability.isEmpty
64 | {
65 | items.append((IR.Declaration.availability,
66 | self.declaration.availability.serialized))
67 | }
68 | if !self.declaration.extensionConstraints.isEmpty
69 | {
70 | items.append((IR.Declaration.extensionConstraints,
71 | .array(self.declaration.extensionConstraints.map(\.serialized))))
72 | }
73 | if !self.declaration.genericConstraints.isEmpty
74 | {
75 | items.append((IR.Declaration.genericConstraints,
76 | .array(self.declaration.genericConstraints.map(\.serialized))))
77 | }
78 | if !self.declaration.generics.isEmpty
79 | {
80 | items.append((IR.Declaration.generics,
81 | .array(self.declaration.generics.map(\.serialized))))
82 | }
83 | switch self.documentation
84 | {
85 | case nil:
86 | break
87 | case .extends(nil, with: let comment):
88 | items.append((IR.Vertex.comment, .string(comment)))
89 |
90 | case .extends(let origin?, with: let comment):
91 | items.append((IR.Vertex.comment, .string(comment)))
92 | fallthrough
93 | case .inherits(let origin)?:
94 | items.append((IR.Vertex.origin, .number(origin)))
95 | }
96 | return .object(items)
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/Sources/SymbolGraphs/Identifiers/ModuleIdentifier.swift:
--------------------------------------------------------------------------------
1 | @frozen public
2 | struct ModuleIdentifier:Sendable
3 | {
4 | public
5 | let string:String
6 |
7 | @inlinable public
8 | init(_ string:S) where S:StringProtocol
9 | {
10 | self.string = .init(string)
11 | }
12 | }
13 | extension ModuleIdentifier:Equatable
14 | {
15 | @inlinable public
16 | var title:Substring
17 | {
18 | self.string.drop { $0 == "_" }
19 | }
20 | // lowercased. it is possible for lhs == rhs even if lhs.string != rhs.string
21 | @inlinable public
22 | var value:String
23 | {
24 | self.title.lowercased()
25 | }
26 | @inlinable public static
27 | func == (lhs:Self, rhs:Self) -> Bool
28 | {
29 | lhs.value == rhs.value
30 | }
31 | }
32 | extension ModuleIdentifier:Hashable
33 | {
34 | @inlinable public
35 | func hash(into hasher:inout Hasher)
36 | {
37 | self.value.hash(into: &hasher)
38 | }
39 | }
40 | extension ModuleIdentifier:Comparable
41 | {
42 | @inlinable public static
43 | func < (lhs:Self, rhs:Self) -> Bool
44 | {
45 | lhs.value < rhs.value
46 | }
47 | }
48 | extension ModuleIdentifier:ExpressibleByStringLiteral
49 | {
50 | @inlinable public
51 | init(stringLiteral:String)
52 | {
53 | self.string = stringLiteral
54 | }
55 | }
56 | extension ModuleIdentifier:CustomStringConvertible
57 | {
58 | @inlinable public
59 | var description:String
60 | {
61 | self.string
62 | }
63 | }
64 | extension ModuleIdentifier:Decodable
65 | {
66 | @inlinable public
67 | init(from decoder:any Decoder) throws
68 | {
69 | self.init(try decoder.singleValueContainer().decode(String.self))
70 | }
71 | }
--------------------------------------------------------------------------------
/Sources/SymbolGraphs/Identifiers/PackageIdentifier.swift:
--------------------------------------------------------------------------------
1 | @frozen public
2 | struct PackageIdentifier:Hashable, Sendable
3 | {
4 | @frozen public
5 | enum Kind:Hashable, Comparable, Sendable
6 | {
7 | case swift
8 | case core
9 | case community(String)
10 | }
11 |
12 | public static
13 | let swift:Self = .init(kind: .swift)
14 | public static
15 | let core:Self = .init(kind: .core)
16 |
17 | public
18 | let kind:Kind
19 |
20 | @inlinable public
21 | init(kind:Kind)
22 | {
23 | self.kind = kind
24 | }
25 | }
26 | extension PackageIdentifier:Comparable
27 | {
28 | @inlinable public static
29 | func < (lhs:Self, rhs:Self) -> Bool
30 | {
31 | lhs.kind < rhs.kind
32 | }
33 | }
34 | extension PackageIdentifier:ExpressibleByStringLiteral
35 | {
36 | @inlinable public
37 | init(stringLiteral:String)
38 | {
39 | self.init(stringLiteral)
40 | }
41 | @inlinable public
42 | init(_ string:S) where S:StringProtocol
43 | {
44 | switch string.lowercased()
45 | {
46 | case "swift-standard-library",
47 | "standard-library",
48 | "swift-stdlib",
49 | "stdlib":
50 | self.init(kind: .swift)
51 | case "swift-core-libraries":
52 | self.init(kind: .core)
53 | case let name:
54 | self.init(kind: .community(name))
55 | }
56 | }
57 | }
58 | extension PackageIdentifier:LosslessStringConvertible
59 | {
60 | @inlinable public
61 | var string:String
62 | {
63 | switch self.kind
64 | {
65 | case .swift: return "swift-standard-library"
66 | case .core: return "swift-core-libraries"
67 | case .community(let name): return name
68 | }
69 | }
70 | @inlinable public
71 | var description:String
72 | {
73 | self.string
74 | }
75 | }
76 | extension PackageIdentifier:Decodable
77 | {
78 | @inlinable public
79 | init(from decoder:any Decoder) throws
80 | {
81 | self.init(try decoder.singleValueContainer().decode(String.self))
82 | }
83 | }
--------------------------------------------------------------------------------
/Sources/SymbolGraphs/Identifiers/Path.swift:
--------------------------------------------------------------------------------
1 | import JSON
2 |
3 | @frozen public
4 | struct Path:Equatable, RandomAccessCollection, CustomStringConvertible, Sendable
5 | {
6 | public
7 | var prefix:[String]
8 | public
9 | var last:String
10 |
11 | @inlinable public
12 | var startIndex:Int
13 | {
14 | self.prefix.startIndex
15 | }
16 | @inlinable public
17 | var endIndex:Int
18 | {
19 | self.prefix.endIndex + 1
20 | }
21 | @inlinable public
22 | subscript(index:Int) -> String
23 | {
24 | _read
25 | {
26 | if index == self.prefix.endIndex
27 | {
28 | yield self.last
29 | }
30 | else
31 | {
32 | yield self.prefix[index]
33 | }
34 | }
35 | _modify
36 | {
37 | if index == self.prefix.endIndex
38 | {
39 | yield &self.last
40 | }
41 | else
42 | {
43 | yield &self.prefix[index]
44 | }
45 | }
46 | }
47 | @inlinable public
48 | init(prefix:[String] = [], last:String)
49 | {
50 | self.last = last
51 | self.prefix = prefix
52 | }
53 |
54 | @inlinable public
55 | init?(_ components:Components)
56 | where Components:BidirectionalCollection, Components.Element == String
57 | {
58 | guard let last:String = components.last
59 | else
60 | {
61 | return nil
62 | }
63 | self.last = last
64 | self.prefix = .init(components.dropLast())
65 | }
66 |
67 | @inlinable public
68 | var description:String
69 | {
70 | self.joined(separator: ".")
71 | }
72 | }
73 |
74 | extension Path
75 | {
76 | init(from json:JSON) throws
77 | {
78 | let components:[JSON] = try json.as([JSON].self) { $0 > 0 }
79 | let last:Int = components.index(before: components.endIndex)
80 | self.init(
81 | prefix: try components[...OpaqueName.parse(try json.as(String.self).utf8)
9 | }
10 |
11 | var interface:(culture:ModuleIdentifier, protocol:(name:String, id:Self))?
12 | {
13 | // if a vertex is non-canonical, the symbol id of its generic base
14 | // always starts with a mangled protocol name.
15 | // note that our demangling implementation cannot handle “known”
16 | // protocols like 'Swift.Equatable'. but this is fine because we
17 | // are only using this to detect symbols that are defined in extensions
18 | // on underscored protocols.
19 | var input:ParsingInput = .init(self.string.utf8)
20 | guard case let (namespace, name)? =
21 | input.parse(as: USR.Rule.MangledProtocolName?.self)
22 | else
23 | {
24 | return nil
25 | }
26 | // parsing input shares indices with `self.string`. we can use the
27 | // unsafe `init(unchecked:)` because `USR.Rule.MangledProtocolName`
28 | // only succeeds if the first character is a lowercase 's'
29 | let id:Self = .init(unchecked: .init(self.string[...MangledExtensionContext?.self) ?? namespace
32 | return (culture, (name, id))
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Sources/SymbolGraphs/Identifiers/SymbolIdentifier.swift:
--------------------------------------------------------------------------------
1 | @frozen public
2 | struct SymbolIdentifier:Sendable
3 | {
4 | @frozen public
5 | enum Language:Unicode.Scalar, Hashable, Sendable
6 | {
7 | case c = "c"
8 | case swift = "s"
9 | }
10 |
11 | // this must always be an ASCII string
12 | public
13 | let string:String
14 |
15 | init(unchecked:String)
16 | {
17 | self.string = unchecked
18 | }
19 | @inlinable public
20 | init(_ language:Language, _ mangled:ASCII) where ASCII:Collection, ASCII.Element == UInt8
21 | {
22 | self.string = "\(language.rawValue)\(String.init(decoding: mangled, as: Unicode.ASCII.self))"
23 | }
24 |
25 | @inlinable public
26 | var language:Language
27 | {
28 | guard let language:Language = self.string.unicodeScalars.first.flatMap(Language.init(rawValue:))
29 | else
30 | {
31 | // should always be round-trippable
32 | fatalError("unreachable")
33 | }
34 | return language
35 | }
36 | }
37 | extension SymbolIdentifier:Equatable
38 | {
39 | @inlinable public static
40 | func == (lhs:Self, rhs:Self) -> Bool
41 | {
42 | lhs.string.utf8.elementsEqual(rhs.string.utf8)
43 | }
44 | }
45 | extension SymbolIdentifier:Hashable
46 | {
47 | @inlinable public
48 | func hash(into hasher:inout Hasher)
49 | {
50 | for byte:UInt8 in self.string.utf8
51 | {
52 | byte.hash(into: &hasher)
53 | }
54 | }
55 | }
56 | extension SymbolIdentifier:Comparable
57 | {
58 | @inlinable public static
59 | func < (lhs:Self, rhs:Self) -> Bool
60 | {
61 | lhs.string.utf8.lexicographicallyPrecedes(rhs.string.utf8)
62 | }
63 | }
64 | extension SymbolIdentifier:CustomStringConvertible
65 | {
66 | @inlinable public
67 | var description:String
68 | {
69 | self.string
70 | }
71 | }
--------------------------------------------------------------------------------
/Sources/SymbolGraphs/Source/AccessLevel.swift:
--------------------------------------------------------------------------------
1 | @frozen public
2 | enum AccessLevel:String, Sendable
3 | {
4 | case `private`
5 | case `fileprivate`
6 | case `internal`
7 | case `public`
8 | case `open`
9 | }
10 |
--------------------------------------------------------------------------------
/Sources/SymbolGraphs/Source/Declaration.swift:
--------------------------------------------------------------------------------
1 | import Notebook
2 |
3 | extension Declaration:Sendable where Target:Sendable {}
4 | extension Declaration:Equatable where Target:Equatable {}
5 |
6 | @frozen public
7 | struct Declaration
8 | {
9 | // signatures and declarations can change without disturbing the symbol identifier,
10 | // since they contain information that is not part of ABI.
11 | public
12 | let fragments:Notebook
13 | public
14 | let signature:Notebook
15 | public
16 | let availability:Availability
17 | // these *might* be version-independent, but right now we are storing generic
18 | // parameter/associatedtype names
19 | public
20 | let extensionConstraints:[Generic.Constraint]
21 | public
22 | let genericConstraints:[Generic.Constraint]
23 | // generic parameter *names* are not part of ABI.
24 | public
25 | let generics:[Generic]
26 |
27 | @inlinable public
28 | init(fragments:Notebook,
29 | signature:Notebook,
30 | availability:Availability = .init(),
31 | extensionConstraints:[Generic.Constraint] = [],
32 | genericConstraints:[Generic.Constraint] = [],
33 | generics:[Generic] = [])
34 | {
35 | self.fragments = fragments
36 | self.signature = signature
37 | self.availability = availability
38 | self.extensionConstraints = extensionConstraints
39 | self.genericConstraints = genericConstraints
40 | self.generics = generics
41 | }
42 | @inlinable public
43 | init(fallback:String)
44 | {
45 | let fallback:CollectionOfOne<(String, Highlight)> =
46 | .init((fallback, .text))
47 | self.init(fragments: .init(), signature: .init(fallback))
48 | }
49 |
50 | func forEach(_ body:(Target) throws -> ()) rethrows
51 | {
52 | for (_, target):(_, Target) in self.fragments.links
53 | {
54 | try body(target)
55 | }
56 | for constraint:Generic.Constraint in self.extensionConstraints
57 | {
58 | try constraint.forEach(body)
59 | }
60 | for constraint:Generic.Constraint in self.genericConstraints
61 | {
62 | try constraint.forEach(body)
63 | }
64 | }
65 | @inlinable public
66 | func map(_ transform:(Target) throws -> T) rethrows -> Declaration
67 | {
68 | .init(fragments: try self.fragments.map(transform),
69 | signature: self.signature,
70 | availability: self.availability,
71 | extensionConstraints: try self.extensionConstraints.map
72 | {
73 | try $0.map(transform)
74 | },
75 | genericConstraints: try self.genericConstraints.map
76 | {
77 | try $0.map(transform)
78 | },
79 | generics: self.generics)
80 | }
81 | @inlinable public
82 | func flatMap(_ transform:(Target) throws -> T?) rethrows -> Declaration
83 | {
84 | .init(fragments: try self.fragments.compactMap(transform),
85 | signature: self.signature,
86 | availability: self.availability,
87 | extensionConstraints: try self.extensionConstraints.map
88 | {
89 | try $0.flatMap(transform)
90 | },
91 | genericConstraints: try self.genericConstraints.map
92 | {
93 | try $0.flatMap(transform)
94 | },
95 | generics: self.generics)
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/Sources/SymbolGraphs/Source/Documentation.swift:
--------------------------------------------------------------------------------
1 | extension Documentation:Sendable where Comment:Sendable, Target:Sendable {}
2 | extension Documentation:Equatable where Comment:Equatable, Target:Equatable {}
3 |
4 | @frozen public
5 | enum Documentation
6 | {
7 | case inherits(Target)
8 | case extends(Target?, with:Comment)
9 |
10 | func forEach(_ body:(Target) throws -> ()) rethrows
11 | {
12 | switch self
13 | {
14 | case .inherits(let origin), .extends(let origin?, with: _):
15 | try body(origin)
16 | case .extends(nil, with: _):
17 | break
18 | }
19 | }
20 | @inlinable public
21 | func map(_ transform:(Target) throws -> T) rethrows -> Documentation
22 | {
23 | switch self
24 | {
25 | case .inherits(let origin):
26 | return .inherits(try transform(origin))
27 | case .extends(let origin, with: let comment):
28 | return .extends(try origin.map(transform), with: comment)
29 | }
30 | }
31 | @inlinable public
32 | func flatMap(_ transform:(Target) throws -> T?) rethrows -> Documentation?
33 | {
34 | switch self
35 | {
36 | case .inherits(let origin):
37 | return try transform(origin).map(Documentation.inherits(_:))
38 | case .extends(let origin, with: let comment):
39 | return .extends(try origin.flatMap(transform), with: comment)
40 | }
41 | }
42 | }
43 | extension Documentation where Comment == String
44 | {
45 | static
46 | func extends(_ origin:Target?, with comment:String?) -> Self?
47 | {
48 | switch (origin, comment)
49 | {
50 | case (nil, nil):
51 | return nil
52 | case (let origin?, nil):
53 | return .inherits(origin)
54 | case (let origin?, let comment?):
55 | return comment.isEmpty ? .inherits(origin) : .extends(origin, with: comment)
56 | case (nil, let comment?):
57 | return comment.isEmpty ? nil : .extends(nil, with: comment)
58 | }
59 | }
60 | }
--------------------------------------------------------------------------------
/Sources/SymbolGraphs/Source/Generic.Constraint.swift:
--------------------------------------------------------------------------------
1 | import JSON
2 |
3 | extension Generic
4 | {
5 | @frozen public
6 | enum Verb:Int, Hashable, Sendable
7 | {
8 | case subclasses = 0
9 | case implements
10 | case `is`
11 | }
12 | @frozen public
13 | struct Constraint
14 | {
15 | public
16 | var subject:String
17 | public
18 | var verb:Verb
19 | public
20 | var target:Target?
21 | public
22 | var object:String
23 |
24 | @inlinable public
25 | init(_ subject:String, _ verb:Verb, _ object:String, target:Target?)
26 | {
27 | self.subject = subject
28 | self.verb = verb
29 | self.object = object
30 | self.target = target
31 | }
32 | // right now, this just runs on `target`, but in the future, this monad might
33 | // gain another inhabitant...
34 | func forEach(_ body:(Target) throws -> ()) rethrows
35 | {
36 | let _:Void? = try self.target.map(body)
37 | }
38 | @inlinable public
39 | func map(_ transform:(Target) throws -> T) rethrows -> Constraint
40 | {
41 | .init(self.subject, self.verb, self.object, target: try self.target.map(transform))
42 | }
43 | @inlinable public
44 | func flatMap(_ transform:(Target) throws -> T?) rethrows -> Constraint
45 | {
46 | .init(self.subject, self.verb, self.object, target: try self.target.flatMap(transform))
47 | }
48 | }
49 | }
50 |
51 | extension Generic.Constraint:Sendable where Target:Sendable {}
52 | extension Generic.Constraint:Hashable where Target:Hashable {}
53 | extension Generic.Constraint:Equatable where Target:Equatable {}
54 |
55 | extension Generic.Verb
56 | {
57 | // https://github.com/apple/swift/blob/e7d56037e87787c3ee92d861e95e5ba95e0bcbd4/lib/SymbolGraphGen/JSON.cpp#L92
58 | enum Longform:String
59 | {
60 | case superclass
61 | case conformance
62 | case sameType
63 | }
64 | }
65 | extension Generic.Constraint
66 | {
67 | init(lowering json:JSON) throws
68 | {
69 | self = try json.lint
70 | {
71 | let verb:Generic.Verb = try $0.remove("kind")
72 | {
73 | switch try $0.as(cases: Generic.Verb.Longform.self)
74 | {
75 | case .superclass: return .subclasses
76 | case .conformance: return .implements
77 | case .sameType: return .is
78 | }
79 | }
80 | return .init(
81 | try $0.remove("lhs", as: String.self), verb,
82 | try $0.remove("rhs", as: String.self),
83 | target: try $0.pop("rhsPrecise", SymbolIdentifier.init(from:)))
84 | }
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/Sources/SymbolGraphs/Source/Generic.swift:
--------------------------------------------------------------------------------
1 | import JSON
2 |
3 | @frozen public
4 | struct Generic:Hashable, Sendable
5 | {
6 | public
7 | var name:String
8 | public
9 | var index:Int
10 | public
11 | var depth:Int
12 | }
13 | extension Generic
14 | {
15 | init(lowering json:JSON) throws
16 | {
17 | (self.name, self.index, self.depth) = try json.lint
18 | {
19 | (
20 | name: try $0.remove("name", as: String.self),
21 | index: try $0.remove("index", as: Int.self),
22 | depth: try $0.remove("depth", as: Int.self)
23 | )
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Sources/SymbolGraphs/Source/Highlight.swift:
--------------------------------------------------------------------------------
1 | import JSON
2 |
3 | @frozen public
4 | enum Highlight:UInt8, Sendable
5 | {
6 | // special semantic identifiers. only generated by the symbolgraph extractor
7 | case generic = 0
8 | case argument
9 | case parameter
10 |
11 | /// an attribute like '@frozen'
12 | case attribute
13 | case comment
14 | /// '#warning', etc.
15 | case directive
16 | case documentationComment
17 | case identifier
18 | case interpolation
19 | case invalid
20 | /// 'init', 'deinit', 'subscript'
21 | case keywordIdentifier
22 | /// '#if', '#else', etc.
23 | case keywordDirective
24 | /// 'for', 'let', 'func', etc.
25 | case keywordText
26 | case newlines
27 | case number
28 | // '$0'
29 | case pseudo
30 | case string
31 | case text
32 | /// A type annotation, which appears after a colon. Not all references to a
33 | /// type have this classification; some references are considered identifiers.
34 | case type
35 |
36 | init(from json:JSON, text:String) throws
37 | {
38 | // https://github.com/apple/swift/blob/main/lib/SymbolGraphGen/DeclarationFragmentPrinter.cpp
39 | switch try json.as(String.self) as String
40 | {
41 | case "keyword":
42 | switch text
43 | {
44 | case "init", "deinit", "subscript":
45 | self = .keywordIdentifier
46 | default: self = .keywordText
47 | }
48 | case "attribute": self = .attribute
49 | case "number": self = .number
50 | case "string": self = .string
51 | case "identifier": self = .identifier
52 | case "typeIdentifier": self = .type
53 | case "genericParameter": self = .generic
54 | case "internalParam": self = .parameter
55 | case "externalParam": self = .argument
56 | case "text": self = .text
57 | case let kind:
58 | throw SymbolGraphDecodingError.unknownFragmentKind(kind)
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/Sources/SymbolGraphs/Source/Notebook.swift:
--------------------------------------------------------------------------------
1 | import JSON
2 | import Notebook
3 |
4 | extension Notebook
5 | {
6 | init(lowering json:JSON) throws
7 | {
8 | self.init(try json.as([JSON].self).lazy.map
9 | {
10 | try $0.lint(whitelisting: ["preciseIdentifier"])
11 | {
12 | let text:String = try $0.remove("spelling", as: String.self)
13 | return (text, try $0.remove("kind") { try Highlight.init(from: $0, text: text) })
14 | }
15 | })
16 | }
17 | }
18 | extension Notebook
19 | {
20 | init(lowering json:JSON) throws
21 | {
22 | self.init(try json.as([JSON].self).lazy.map(Fragment.init(lowering:)))
23 | }
24 | }
25 | extension Notebook.Fragment
26 | {
27 | init(lowering json:JSON) throws
28 | {
29 | self = try json.lint
30 | {
31 | let text:String = try $0.remove("spelling", as: String.self)
32 | return .init(text,
33 | color: try $0.remove("kind") { try Highlight.init(from: $0, text: text) },
34 | link: try $0.pop("preciseIdentifier", SymbolIdentifier.init(from:)))
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Sources/Versions/MaskedVersion.swift:
--------------------------------------------------------------------------------
1 | import Grammar
2 |
3 | @frozen public
4 | enum MaskedVersion:Hashable, CustomStringConvertible, Sendable
5 | {
6 | // static
7 | // func semantic(_ version:(major:UInt16, minor:UInt16, patch:UInt16, edition:UInt16))
8 | // -> Self
9 | // {
10 | // .edition(version.major, version.minor, version.patch, version.edition)
11 | // }
12 |
13 | case major(UInt16)
14 | case minor(UInt16, UInt16)
15 | case patch(UInt16, UInt16, UInt16)
16 | case edition(UInt16, UInt16, UInt16, UInt16)
17 |
18 | case nightly(year:UInt16, month:UInt16, day:UInt16)
19 | case hourly(year:UInt16, month:UInt16, day:UInt16, letter:UInt8)
20 |
21 | @inlinable public static
22 | func ?= (pattern:MaskedVersion?, version:Self) -> Bool
23 | {
24 | pattern ?= .init(version)
25 | }
26 |
27 | @inlinable public
28 | init(toolchain string:some StringProtocol) throws
29 | {
30 | self = try Rule.Toolchain.parse(string.unicodeScalars)
31 | }
32 | @inlinable public
33 | init(parsing string:some StringProtocol) throws
34 | {
35 | self = try Rule.parse(string.unicodeScalars)
36 | }
37 | @available(*, deprecated)
38 | @inlinable public
39 | init?(_ string:S) where S:StringProtocol
40 | {
41 | do
42 | {
43 | try self.init(parsing: string)
44 | }
45 | catch
46 | {
47 | return nil
48 | }
49 | }
50 | }
51 | extension MaskedVersion:LosslessStringConvertible
52 | {
53 | @inlinable public
54 | var description:String
55 | {
56 | switch self
57 | {
58 | case .major(let major):
59 | return "\(major)"
60 | case .minor(let major, let minor):
61 | return "\(major).\(minor)"
62 | case .patch(let major, let minor, let patch):
63 | return "\(major).\(minor).\(patch)"
64 | case .edition(let major, let minor, let patch, let edition):
65 | return "\(major).\(minor).\(patch).\(edition)"
66 | case .nightly(year: let year, month: let month, day: let day):
67 | return "\(year)-\(month)-\(day)"
68 | case .hourly(year: let year, month: let month, day: let day, letter: let letter):
69 | return "\(year)-\(month)-\(day)-\(Unicode.Scalar.init(letter))"
70 | }
71 | }
72 | }
--------------------------------------------------------------------------------
/Sources/Versions/PreciseVersion.swift:
--------------------------------------------------------------------------------
1 | infix operator ?= :ComparisonPrecedence
2 |
3 | @frozen public
4 | enum PreciseVersion:Hashable, CustomStringConvertible, Sendable
5 | {
6 | case semantic(UInt16, UInt16, UInt16, UInt16)
7 | case toolchain(year:UInt16, month:UInt16, day:UInt16, letter:UInt8)
8 |
9 | @inlinable public
10 | init(_ masked:MaskedVersion?)
11 | {
12 | switch masked
13 | {
14 | case nil:
15 | self = .semantic(0, 0, 0, 0)
16 | case .major(let major)?:
17 | self = .semantic(major, 0, 0, 0)
18 | case .minor(let major, let minor)?:
19 | self = .semantic(major, minor, 0, 0)
20 | case .patch(let major, let minor, let patch)?:
21 | self = .semantic(major, minor, patch, 0)
22 | case .edition(let major, let minor, let patch, let edition)?:
23 | self = .semantic(major, minor, patch, edition)
24 | case .nightly(year: let year, month: let month, day: let day)?:
25 | self = .toolchain(year: year, month: month, day: day, letter: 0x61) // 'a'
26 | case .hourly(year: let year, month: let month, day: let day, letter: let letter)?:
27 | self = .toolchain(year: year, month: month, day: day, letter: letter)
28 | }
29 | }
30 | @inlinable public
31 | var quadruplet:MaskedVersion
32 | {
33 | switch self
34 | {
35 | case .semantic(let major, let minor, let patch, let edition):
36 | return .edition(major, minor, patch, edition)
37 | case .toolchain(year: let year, month: let month, day: let day, letter: let letter):
38 | return .hourly(year: year, month: month, day: day, letter: letter)
39 | }
40 | }
41 | @inlinable public
42 | var triplet:MaskedVersion
43 | {
44 | switch self
45 | {
46 | case .semantic(let major, let minor, let patch, _):
47 | return .patch(major, minor, patch)
48 | case .toolchain(year: let year, month: let month, day: let day, letter: _):
49 | return .nightly(year: year, month: month, day: day)
50 | }
51 | }
52 |
53 | @inlinable public
54 | var description:String
55 | {
56 | switch self
57 | {
58 | case .semantic(let major, let minor, let patch, let edition):
59 | return "\(major).\(minor).\(patch).\(edition)"
60 | case .toolchain(year: let year, month: let month, day: let day, letter: let letter):
61 | return "\(year)-\(month)-\(day)-\(Unicode.Scalar.init(letter))"
62 | }
63 | }
64 |
65 | @inlinable public static
66 | func ?= (pattern:MaskedVersion?, precise:Self) -> Bool
67 | {
68 | switch pattern
69 | {
70 | case nil:
71 | break
72 | case .major(let major)?:
73 | guard case .semantic(major, _, _, _) = precise
74 | else
75 | {
76 | return false
77 | }
78 | case .minor(let major, let minor)?:
79 | guard case .semantic(major, minor, _, _) = precise
80 | else
81 | {
82 | return false
83 | }
84 | case .patch(let major, let minor, let patch)?:
85 | guard case .semantic(major, minor, patch, _) = precise
86 | else
87 | {
88 | return false
89 | }
90 | case .edition(let major, let minor, let patch, let edition)?:
91 | guard case .semantic(major, minor, patch, edition) = precise
92 | else
93 | {
94 | return false
95 | }
96 |
97 | case .nightly(year: let year, month: let month, day: let day)?:
98 | guard case .toolchain(year: year, month: month, day: day, letter: _) = precise
99 | else
100 | {
101 | return false
102 | }
103 | case .hourly(year: let year, month: let month, day: let day, letter: let letter)?:
104 | guard case .toolchain(year: year, month: month, day: day, letter: letter) = precise
105 | else
106 | {
107 | return false
108 | }
109 | }
110 | return true
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/screenshots/Screenshot from 2022-02-21 09-40-14.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tayloraswift/swift-biome/06e06e3b0e93749db07ee45fb5b0848a29cb42b5/screenshots/Screenshot from 2022-02-21 09-40-14.png
--------------------------------------------------------------------------------
/screenshots/Screenshot from 2022-02-21 09-42-38.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tayloraswift/swift-biome/06e06e3b0e93749db07ee45fb5b0848a29cb42b5/screenshots/Screenshot from 2022-02-21 09-42-38.png
--------------------------------------------------------------------------------
/screenshots/autocomplete-search-demo.apng:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tayloraswift/swift-biome/06e06e3b0e93749db07ee45fb5b0848a29cb42b5/screenshots/autocomplete-search-demo.apng
--------------------------------------------------------------------------------
/screenshots/autocomplete-search-demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tayloraswift/swift-biome/06e06e3b0e93749db07ee45fb5b0848a29cb42b5/screenshots/autocomplete-search-demo.gif
--------------------------------------------------------------------------------
/screenshots/cross-module-link-demo.apng:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tayloraswift/swift-biome/06e06e3b0e93749db07ee45fb5b0848a29cb42b5/screenshots/cross-module-link-demo.apng
--------------------------------------------------------------------------------
/screenshots/cross-module-link-demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tayloraswift/swift-biome/06e06e3b0e93749db07ee45fb5b0848a29cb42b5/screenshots/cross-module-link-demo.gif
--------------------------------------------------------------------------------
/screenshots/cross-module-link-failure.apng:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tayloraswift/swift-biome/06e06e3b0e93749db07ee45fb5b0848a29cb42b5/screenshots/cross-module-link-failure.apng
--------------------------------------------------------------------------------
/screenshots/crosslink-failure.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tayloraswift/swift-biome/06e06e3b0e93749db07ee45fb5b0848a29cb42b5/screenshots/crosslink-failure.png
--------------------------------------------------------------------------------
/screenshots/fuzziness-demo.apng:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tayloraswift/swift-biome/06e06e3b0e93749db07ee45fb5b0848a29cb42b5/screenshots/fuzziness-demo.apng
--------------------------------------------------------------------------------
/screenshots/fuzziness-demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tayloraswift/swift-biome/06e06e3b0e93749db07ee45fb5b0848a29cb42b5/screenshots/fuzziness-demo.gif
--------------------------------------------------------------------------------
/screenshots/fuzziness-failure.apng:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tayloraswift/swift-biome/06e06e3b0e93749db07ee45fb5b0848a29cb42b5/screenshots/fuzziness-failure.apng
--------------------------------------------------------------------------------
/screenshots/fuzziness-failure.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tayloraswift/swift-biome/06e06e3b0e93749db07ee45fb5b0848a29cb42b5/screenshots/fuzziness-failure.gif
--------------------------------------------------------------------------------
/screenshots/mobile-view.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tayloraswift/swift-biome/06e06e3b0e93749db07ee45fb5b0848a29cb42b5/screenshots/mobile-view.png
--------------------------------------------------------------------------------
/screenshots/network-docc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tayloraswift/swift-biome/06e06e3b0e93749db07ee45fb5b0848a29cb42b5/screenshots/network-docc.png
--------------------------------------------------------------------------------
/screenshots/network.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tayloraswift/swift-biome/06e06e3b0e93749db07ee45fb5b0848a29cb42b5/screenshots/network.png
--------------------------------------------------------------------------------
/screenshots/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tayloraswift/swift-biome/06e06e3b0e93749db07ee45fb5b0848a29cb42b5/screenshots/screenshot.png
--------------------------------------------------------------------------------
/screenshots/screenshot@v0.3.2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tayloraswift/swift-biome/06e06e3b0e93749db07ee45fb5b0848a29cb42b5/screenshots/screenshot@v0.3.2.png
--------------------------------------------------------------------------------
/screenshots/versioning-0.apng:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tayloraswift/swift-biome/06e06e3b0e93749db07ee45fb5b0848a29cb42b5/screenshots/versioning-0.apng
--------------------------------------------------------------------------------
/screenshots/versioning-0.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tayloraswift/swift-biome/06e06e3b0e93749db07ee45fb5b0848a29cb42b5/screenshots/versioning-0.gif
--------------------------------------------------------------------------------
/screenshots/versioning-1.apng:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tayloraswift/swift-biome/06e06e3b0e93749db07ee45fb5b0848a29cb42b5/screenshots/versioning-1.apng
--------------------------------------------------------------------------------
/screenshots/versioning-1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tayloraswift/swift-biome/06e06e3b0e93749db07ee45fb5b0848a29cb42b5/screenshots/versioning-1.gif
--------------------------------------------------------------------------------
/screenshots/versioning-2.apng:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tayloraswift/swift-biome/06e06e3b0e93749db07ee45fb5b0848a29cb42b5/screenshots/versioning-2.apng
--------------------------------------------------------------------------------
/screenshots/versioning-2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tayloraswift/swift-biome/06e06e3b0e93749db07ee45fb5b0848a29cb42b5/screenshots/versioning-2.gif
--------------------------------------------------------------------------------
/screenshots/versioning-3.apng:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tayloraswift/swift-biome/06e06e3b0e93749db07ee45fb5b0848a29cb42b5/screenshots/versioning-3.apng
--------------------------------------------------------------------------------
/screenshots/versioning-3.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tayloraswift/swift-biome/06e06e3b0e93749db07ee45fb5b0848a29cb42b5/screenshots/versioning-3.gif
--------------------------------------------------------------------------------
/screenshots/versioning-4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tayloraswift/swift-biome/06e06e3b0e93749db07ee45fb5b0848a29cb42b5/screenshots/versioning-4.png
--------------------------------------------------------------------------------
/screenshots/versioning-5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tayloraswift/swift-biome/06e06e3b0e93749db07ee45fb5b0848a29cb42b5/screenshots/versioning-5.png
--------------------------------------------------------------------------------
/screenshots/versioning-6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tayloraswift/swift-biome/06e06e3b0e93749db07ee45fb5b0848a29cb42b5/screenshots/versioning-6.png
--------------------------------------------------------------------------------
/screenshots/versioning-7.apng:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tayloraswift/swift-biome/06e06e3b0e93749db07ee45fb5b0848a29cb42b5/screenshots/versioning-7.apng
--------------------------------------------------------------------------------
/screenshots/versioning-7.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tayloraswift/swift-biome/06e06e3b0e93749db07ee45fb5b0848a29cb42b5/screenshots/versioning-7.gif
--------------------------------------------------------------------------------
/screenshots/versioning-8.apng:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tayloraswift/swift-biome/06e06e3b0e93749db07ee45fb5b0848a29cb42b5/screenshots/versioning-8.apng
--------------------------------------------------------------------------------
/screenshots/versioning-8.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tayloraswift/swift-biome/06e06e3b0e93749db07ee45fb5b0848a29cb42b5/screenshots/versioning-8.gif
--------------------------------------------------------------------------------
/screenshots/versioning-9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tayloraswift/swift-biome/06e06e3b0e93749db07ee45fb5b0848a29cb42b5/screenshots/versioning-9.png
--------------------------------------------------------------------------------
/swift-balanced-trees/Benchmarks/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.5
2 | import PackageDescription
3 |
4 | let package:Package = .init(
5 | name: "swift-balanced-trees-benchmarks",
6 | products:
7 | [
8 | .executable(name: "forest-benchmarks", targets: ["ForestBenchmarks"]),
9 | ],
10 | dependencies:
11 | [
12 | .package(name: "swift-balanced-trees", path: ".."),
13 |
14 | // .package(url: "https://github.com/kelvin13/swift-system-extras", from: "0.1.0"),
15 | // .package(url: "https://github.com/apple/swift-argument-parser", from: "1.1.3"),
16 | ],
17 | targets:
18 | [
19 | .executableTarget(name: "ForestBenchmarks",
20 | dependencies:
21 | [
22 | .product(name: "Forest", package: "swift-balanced-trees"),
23 | // .product(name: "SystemExtras", package: "swift-system-extras"),
24 | // .product(name: "ArgumentParser", package: "swift-argument-parser"),
25 | ]),
26 | ]
27 | )
--------------------------------------------------------------------------------
/swift-balanced-trees/Benchmarks/Sources/ForestBenchmarks/Main.swift:
--------------------------------------------------------------------------------
1 | import Forest
2 |
3 | @main
4 | struct Main
5 | {
6 | static
7 | func main()
8 | {
9 | let clock:SuspendingClock = .init()
10 | print("shuffled:")
11 | print(Self.shuffled(clock, elements: (0 ..< 1 << 20)))
12 | print("lifo:")
13 | print(Self.lifo(clock, elements: (0 ..< 1 << 20)))
14 | }
15 |
16 | private static
17 | func shuffled(_ clock:SuspendingClock, elements:some Sequence) -> (insert:Duration, remove:Duration)
18 | {
19 | let insertions:[Int] = elements.shuffled()
20 | let removals:[Int] = insertions.shuffled()
21 |
22 | var forest:Forest = .init(),
23 | tree:Forest.Tree.Head? = nil
24 | let insert:Duration = clock.measure
25 | {
26 | for element:Int in insertions
27 | {
28 | forest.insert(element, into: &tree)
29 | }
30 | }
31 | let remove:Duration = clock.measure
32 | {
33 | for element:Int in removals
34 | {
35 | if let index:Forest.Index = forest[tree].find(element)
36 | {
37 | forest.remove(index, from: &tree)
38 | }
39 | }
40 | }
41 | return (insert: insert, remove: remove)
42 | }
43 | private static
44 | func lifo(_ clock:SuspendingClock, elements:some Sequence) -> (insert:Duration, remove:Duration)
45 | {
46 | var forest:Forest = .init(),
47 | tree:Forest.Tree.Head? = nil
48 | let insert:Duration = clock.measure
49 | {
50 | for element:Int in elements
51 | {
52 | forest.push(min: element, into: &tree)
53 | }
54 | }
55 | let remove:Duration = clock.measure
56 | {
57 | while let index:Forest.Index = tree?.index
58 | {
59 | forest.remove(index, from: &tree)
60 | }
61 | }
62 | return (insert: insert, remove: remove)
63 | }
64 | }
--------------------------------------------------------------------------------
/swift-balanced-trees/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.7
2 | import PackageDescription
3 |
4 | let package = Package(
5 | name: "swift-balanced-trees",
6 | products:
7 | [
8 | .library(name: "Forest", targets: ["Forest"]),
9 | .executable(name: "forest-tests", targets: ["ForestTests"]),
10 | ],
11 | dependencies:
12 | [
13 | ],
14 | targets:
15 | [
16 | .target(name: "Forest",
17 | dependencies:
18 | [
19 | ]),
20 |
21 | .executableTarget(name: "ForestTests",
22 | dependencies:
23 | [
24 | .target(name: "Forest"),
25 | ],
26 | path: "Tests/ForestTests"),
27 | ]
28 | )
29 |
--------------------------------------------------------------------------------
/swift-balanced-trees/Sources/Forest/Forest.Tree.swift:
--------------------------------------------------------------------------------
1 | extension Forest
2 | {
3 | @inlinable public
4 | subscript(tree:Tree.Head?) -> Tree
5 | {
6 | .init(tree, forest: self)
7 | }
8 |
9 | @frozen public
10 | struct Tree:Sequence
11 | {
12 | @frozen public
13 | struct Head
14 | {
15 | public
16 | var index:Index
17 |
18 | @inlinable public
19 | init(_ index:Index)
20 | {
21 | self.index = index
22 | }
23 | }
24 | @frozen public
25 | struct Iterator:IteratorProtocol
26 | {
27 | public
28 | var current:Index?
29 | public
30 | let forest:Forest
31 |
32 | @inlinable public
33 | init(current:Index?, forest:Forest)
34 | {
35 | self.current = current
36 | self.forest = forest
37 | }
38 | @inlinable public mutating
39 | func next() -> Value?
40 | {
41 | if let current:Index = self.current
42 | {
43 | self.current = self.forest.successor(of: current)
44 | return self.forest[current].value
45 | }
46 | else
47 | {
48 | return nil
49 | }
50 | }
51 | }
52 |
53 | public
54 | let head:Head?
55 | public
56 | let forest:Forest
57 |
58 | @inlinable public
59 | func makeIterator() -> Iterator
60 | {
61 | .init(current: self.head?.index, forest: self.forest)
62 | }
63 |
64 | @inlinable public
65 | init(_ head:Head?, forest:Forest)
66 | {
67 | self.head = head
68 | self.forest = forest
69 | }
70 |
71 | @inlinable public
72 | func find(by less:(Value) throws -> Bool?) rethrows -> Index?
73 | {
74 | if case (let index, nil)? = try self.walk(by: less)
75 | {
76 | return index
77 | }
78 | else
79 | {
80 | return nil
81 | }
82 | }
83 | @inlinable public
84 | func walk(by less:(Value) throws -> Bool?) rethrows -> (index:Index, side:Node.Side?)?
85 | {
86 | guard var current:Index = self.head?.index
87 | else
88 | {
89 | return nil
90 | }
91 |
92 | switch try less(self.forest[current].value)
93 | {
94 | case false?:
95 | // head can never have a left-child. we return `nil` and not `(current, .left)`
96 | // to reflect the fact that any such insertion would also change ``head``.
97 | return nil
98 | case nil:
99 | return (current, nil)
100 | case true?:
101 | break
102 | }
103 | ascending:
104 | while let parent:Index = self.forest.parent(of: current)
105 | {
106 | switch try less(self.forest[parent].value)
107 | {
108 | case false?:
109 | break ascending
110 | case nil:
111 | return (parent, nil)
112 | case true?:
113 | current = parent
114 | }
115 | }
116 | guard var current:Index = self.forest.right(of: current)
117 | else
118 | {
119 | return (current, .right)
120 | }
121 | // descend
122 | while true
123 | {
124 | switch try less(self.forest[current].value)
125 | {
126 | case true?:
127 | if let next:Index = self.forest.right(of: current)
128 | {
129 | current = next
130 | }
131 | else
132 | {
133 | return (current, .right)
134 | }
135 |
136 | case nil:
137 | return (current, nil)
138 |
139 | case false?:
140 | if let next:Index = self.forest.left(of: current)
141 | {
142 | current = next
143 | }
144 | else
145 | {
146 | return (current, .left)
147 | }
148 | }
149 | }
150 | }
151 | }
152 | }
153 | extension Forest.Tree where Value:Comparable
154 | {
155 | @inlinable public
156 | func find(_ value:Value) -> Forest.Index?
157 | {
158 | self.find { $0 < value ? true : $0 == value ? nil : false }
159 | }
160 | }
161 | extension Forest.Tree:CustomStringConvertible
162 | {
163 | public
164 | var description:String
165 | {
166 | self.head.map
167 | {
168 | self.forest.description(head: $0,
169 | root: self.forest.root(of: $0.index))
170 | } ?? "nil"
171 | }
172 | }
--------------------------------------------------------------------------------
/swift-balanced-trees/Sources/Forest/Validate.swift:
--------------------------------------------------------------------------------
1 | extension Forest
2 | {
3 | // verifies that all paths in `node`’s subtree have the same black height,
4 | // and that `node` and all of its children satisfy the red property.
5 | public
6 | func blacks(under root:Index?) -> Int?
7 | {
8 | guard let root:Index
9 | else
10 | {
11 | return 1
12 | }
13 | if case .red = self[root].color
14 | {
15 | if case .red? = self.left(of: root).map({ self[$0].color })
16 | {
17 | return nil
18 | }
19 | if case .red? = self.right(of: root).map({ self[$0].color })
20 | {
21 | return nil
22 | }
23 | }
24 | guard let blacks:Int = self.blacks(under: self.left(of: root)),
25 | case blacks? = self.blacks(under: self.right(of: root))
26 | else
27 | {
28 | return nil
29 | }
30 | switch self[root].color
31 | {
32 | case .black:
33 | return blacks + 1
34 | case .red:
35 | return blacks
36 | }
37 | }
38 | }
39 | extension Forest.Tree
40 | {
41 | // verifies that all paths in the red-black tree have the same black height,
42 | // that all nodes satisfy the red property, and that the root is black
43 | public
44 | func validate() -> Bool
45 | {
46 | guard let root:Forest.Index = (self.head?.index).map(self.forest.root(of:))
47 | else
48 | {
49 | return true
50 | }
51 | if case .black = self.forest[root].color,
52 | case _? = self.forest.blacks(under: root)
53 | {
54 | return true
55 | }
56 | else
57 | {
58 | return false
59 | }
60 | }
61 | }
--------------------------------------------------------------------------------
/swift-balanced-trees/Tests/ForestTests/Main.swift:
--------------------------------------------------------------------------------
1 | import Forest
2 |
3 | infix operator ==? :ComparisonPrecedence
4 |
5 | struct OptionalUnwrapFailure:Error, CustomStringConvertible
6 | {
7 | var description:String
8 | {
9 | """
10 | expected non-nil value of type \(Wrapped.self)
11 | """
12 | }
13 | }
14 | struct AssertionFailure:Error, CustomStringConvertible
15 | {
16 | var description:String
17 | {
18 | """
19 | expected true
20 | """
21 | }
22 | }
23 | struct AssertEquivalenceFailure:Error, CustomStringConvertible
24 | {
25 | let lhs:T
26 | let rhs:T
27 |
28 | var description:String
29 | {
30 | """
31 | expected equal values:
32 | {
33 | lhs: \(lhs),
34 | rhs: \(rhs)
35 | }
36 | """
37 | }
38 | }
39 | func ==? (lhs:LHS, rhs:RHS) -> AssertEquivalenceFailure<[LHS.Element]>?
40 | where LHS:Sequence, RHS:Sequence, LHS.Element == RHS.Element, LHS.Element:Equatable
41 | {
42 | let rhs:[LHS.Element] = .init(rhs)
43 | let lhs:[LHS.Element] = .init(lhs)
44 | if lhs.elementsEqual(rhs)
45 | {
46 | return nil
47 | }
48 | else
49 | {
50 | return .init(lhs: lhs, rhs: rhs)
51 | }
52 | }
53 | func ==? (lhs:T, rhs:T) -> AssertEquivalenceFailure?
54 | where T:Equatable
55 | {
56 | if lhs == rhs
57 | {
58 | return nil
59 | }
60 | else
61 | {
62 | return .init(lhs: lhs, rhs: rhs)
63 | }
64 | }
65 |
66 | @main
67 | struct Main
68 | {
69 | mutating
70 | func assert(_ error:AssertEquivalenceFailure?,
71 | file:String = #file,
72 | function:String = #function,
73 | line:Int = #line,
74 | column:Int = #column)
75 | {
76 | if let error:AssertEquivalenceFailure
77 | {
78 | print("\(file):\(line):\(column): \(error)")
79 | self.failed.append(error)
80 | }
81 | else
82 | {
83 | self.passed += 1
84 | }
85 | }
86 | mutating
87 | func assert(_ test:Bool,
88 | file:String = #file,
89 | function:String = #function,
90 | line:Int = #line,
91 | column:Int = #column)
92 | {
93 | if test
94 | {
95 | self.passed += 1
96 | }
97 | else
98 | {
99 | print("\(file):\(line):\(column): test failed")
100 | self.failed.append(AssertionFailure.init())
101 | }
102 | }
103 | func unwrap(_ optional:Wrapped?,
104 | file:String = #file,
105 | function:String = #function,
106 | line:Int = #line,
107 | column:Int = #column) -> Wrapped?
108 | {
109 | if let wrapped:Wrapped = optional
110 | {
111 | return wrapped
112 | }
113 | else
114 | {
115 | print("\(file):\(line):\(column): optional unwrap failed")
116 | return nil
117 | }
118 | }
119 |
120 | var passed:Int
121 | var failed:[any Error]
122 |
123 | init()
124 | {
125 | self.passed = 0
126 | self.failed = []
127 | }
128 |
129 | static
130 | func main()
131 | {
132 | var tests:Self = .init()
133 | tests.main()
134 | print("passed: \(tests.passed)")
135 | print("failed: \(tests.failed.count)")
136 | }
137 | mutating
138 | func main()
139 | {
140 | self.test(inserting: 0 ..< 256, removing: 0 ..< 256)
141 | self.test(inserting: 0 ..< 256, removing: (0 ..< 256).reversed())
142 | self.test(inserting: (0 ..< 256).reversed(), removing: (0 ..< 256))
143 | self.test(inserting: (0 ..< 256).reversed(), removing: (0 ..< 256).reversed())
144 |
145 | self.test(inserting: [1, 2, 0], removing: [1, 2, 0])
146 | self.test(inserting: [1, 2, 0], removing: [0, 1, 2])
147 | self.test(inserting: [1, 2, 0], removing: [2, 1, 0])
148 |
149 | self.test(inserting: (0 ..< 1024).shuffled(), removing: (0 ..< 1024).shuffled())
150 | }
151 | mutating
152 | func test(inserting insertions:some Sequence, removing removals:some Sequence)
153 | {
154 | let insertions:[Int] = .init(insertions)
155 | let sorted:[Int] = insertions.sorted()
156 | var forest:Forest = .init()
157 | var tree:Forest.Tree.Head? = nil
158 | for element:Int in insertions
159 | {
160 | forest.insert(element, into: &tree)
161 | }
162 |
163 | self.assert(forest.count ==? sorted.count)
164 | self.assert(forest[tree] ==? sorted)
165 | self.assert(forest[tree].validate())
166 |
167 | for element:Int in removals
168 | {
169 | if let index:Forest.Index = self.unwrap(forest[tree].find(element))
170 | {
171 | forest.remove(index, from: &tree)
172 | self.assert(forest[tree].validate())
173 | }
174 | }
175 |
176 | self.assert(forest._inhabitants() ==? 0)
177 | }
178 | }
--------------------------------------------------------------------------------