├── .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 | ![screenshot](screenshots/screenshot@v0.3.2.png) 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 | } --------------------------------------------------------------------------------