diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 58050e81..05051a95 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -60,6 +60,6 @@ jobs: - name: Install System Dependencies run: | apt-get update - apt-get install -y libxml2-dev graphviz + apt-get install -y libxml2-dev libsqlite3-dev graphviz - name: Build and Test run: swift test -c release --enable-test-discovery diff --git a/Dockerfile b/Dockerfile index 4b27b183..187249fb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,12 +1,12 @@ FROM swift:5.2 as builder WORKDIR /swiftdoc COPY . . -RUN apt-get -qq update && apt-get install -y libxml2-dev && rm -r /var/lib/apt/lists/* +RUN apt-get -qq update && apt-get install -y libxml2-dev libsqlite3-dev && rm -r /var/lib/apt/lists/* RUN mkdir -p /build/lib && cp -R /usr/lib/swift/linux/*.so* /build/lib RUN make install prefix=/build FROM ubuntu:18.04 -RUN apt-get -qq update && apt-get install -y graphviz libatomic1 libxml2-dev libcurl4-openssl-dev && rm -r /var/lib/apt/lists/* +RUN apt-get -qq update && apt-get install -y graphviz libatomic1 libxml2-dev libsqlite3-dev libcurl4-openssl-dev && rm -r /var/lib/apt/lists/* COPY --from=builder /build/bin/swift-doc /usr/bin COPY --from=builder /build/lib/* /usr/lib/ ENTRYPOINT ["swift-doc"] diff --git a/Package.resolved b/Package.resolved index e4d09600..95f0adfd 100644 --- a/Package.resolved +++ b/Package.resolved @@ -33,8 +33,17 @@ "repositoryURL": "https://github.com/SwiftDocOrg/Markup.git", "state": { "branch": null, - "revision": "029ad8c1115ab32b7c20ab52eb092fbc030deb17", - "version": "0.0.4" + "revision": "9a429d0011d738059bc94f5f92ee406689597a91", + "version": "0.0.3" + } + }, + { + "package": "SQLite.swift", + "repositoryURL": "https://github.com/stephencelis/SQLite.swift.git", + "state": { + "branch": "0.12.2", + "revision": "0a9893ec030501a3956bee572d6b4fdd3ae158a1", + "version": null } }, { @@ -51,8 +60,17 @@ "repositoryURL": "https://github.com/SwiftDocOrg/swift-cmark.git", "state": { "branch": null, - "revision": "2a766030bee955b4806044fd7aca1b6884475138", - "version": "0.28.3+20200110.2a76603" + "revision": "1168665f6b36be747ffe6b7b90bc54cfc17f42b7", + "version": "0.28.3+20200207.1168665" + } + }, + { + "package": "HTMLEntities", + "repositoryURL": "https://github.com/IBM-Swift/swift-html-entities.git", + "state": { + "branch": null, + "revision": "744c094976355aa96ca61b9b60ef0a38e979feb7", + "version": "3.0.14" } }, { @@ -77,8 +95,8 @@ "package": "SwiftSyntax", "repositoryURL": "https://github.com/apple/swift-syntax.git", "state": { - "branch": "0.50300.0", - "revision": "844574d683f53d0737a9c6d706c3ef31ed2955eb", + "branch": "0.50200.0", + "revision": "0688b9cfc4c3dd234e4f55f1f056b2affc849873", "version": null } }, @@ -104,8 +122,8 @@ "package": "SwiftSyntaxHighlighter", "repositoryURL": "https://github.com/NSHipster/SwiftSyntaxHighlighter.git", "state": { - "branch": "1.1.1", - "revision": "76bd23ae4b23f028a8e45f906c2bf98312fb9d33", + "branch": "1.0.0", + "revision": "4a20d10bba17241b66650d99081801536146b43c", "version": null } } diff --git a/Package.swift b/Package.swift index b8e0c6a8..fe699d6e 100644 --- a/Package.swift +++ b/Package.swift @@ -21,6 +21,7 @@ let package = Package( .package(url: "https://github.com/NSHipster/HypertextLiteral.git", .upToNextMinor(from: "0.0.2")), .package(url: "https://github.com/SwiftDocOrg/Markup.git", .upToNextMinor(from: "0.0.3")), .package(url: "https://github.com/NSHipster/SwiftSyntaxHighlighter.git", .revision("1.1.1")), + .package(url: "https://github.com/stephencelis/SQLite.swift.git", .revision("0.12.2")), .package(url: "https://github.com/apple/swift-argument-parser.git", .upToNextMinor(from: "0.0.6")), .package(url: "https://github.com/apple/swift-log.git", .upToNextMinor(from: "1.2.0")), .package(name: "LoggingGitHubActions", url: "https://github.com/NSHipster/swift-log-github-actions.git", .upToNextMinor(from: "0.0.1")), @@ -41,6 +42,7 @@ let package = Package( .product(name: "Markup", package: "Markup"), .product(name: "GraphViz", package: "GraphViz"), .product(name: "SwiftSyntaxHighlighter", package: "SwiftSyntaxHighlighter"), + .product(name: "SQLite", package: "SQLite"), .product(name: "Logging", package: "swift-log"), .product(name: "LoggingGitHubActions", package: "LoggingGitHubActions") ] diff --git a/Sources/SwiftDoc/Helpers.swift b/Sources/SwiftDoc/Helpers.swift index 7a19bb28..580c3c51 100644 --- a/Sources/SwiftDoc/Helpers.swift +++ b/Sources/SwiftDoc/Helpers.swift @@ -1,22 +1,22 @@ import Foundation - -public func route(for symbol: Symbol) -> String { - return route(for: symbol.id) -} - -public func route(for name: CustomStringConvertible) -> String { - return name.description.replacingOccurrences(of: ".", with: "_") -} - -public func path(for symbol: Symbol, with baseURL: String) -> String { - return path(for: route(for: symbol), with: baseURL) -} - -public func path(for identifier: CustomStringConvertible, with baseURL: String) -> String { - let url = URL(string: baseURL)?.appendingPathComponent("\(identifier)") ?? URL(string: "\(identifier)") - guard let string = url?.absoluteString else { - fatalError("Unable to construct path for \(identifier) with baseURL \(baseURL)") - } - - return string -} +// +//public func route(for symbol: Symbol) -> String { +// return route(for: symbol.id) +//} +// +//public func route(for name: CustomStringConvertible) -> String { +// return name.description.replacingOccurrences(of: ".", with: "_") +//} +// +//public func path(for symbol: Symbol, with baseURL: String) -> String { +// return path(for: route(for: symbol), with: baseURL) +//} +// +//public func path(for identifier: CustomStringConvertible, with baseURL: String) -> String { +// let url = URL(string: baseURL)?.appendingPathComponent("\(identifier)") ?? URL(string: "\(identifier)") +// guard let string = url?.absoluteString else { +// fatalError("Unable to construct path for \(identifier) with baseURL \(baseURL)") +// } +// +// return string +//} diff --git a/Sources/SwiftDoc/Identifier.swift b/Sources/SwiftDoc/Identifier.swift index bd9bd2d2..c1c0ded8 100644 --- a/Sources/SwiftDoc/Identifier.swift +++ b/Sources/SwiftDoc/Identifier.swift @@ -1,6 +1,51 @@ +import SwiftSemantics + public struct Identifier: Hashable { public let pathComponents: [String] public let name: String + public let checksum: String + + public init(symbol: Symbol) { + self.pathComponents = symbol.context.compactMap { + ($0 as? Symbol)?.name ?? ($0 as? Extension)?.extendedType + } + + self.name = { + switch symbol.api { + case let function as Function where function.isOperator: + var components = symbol.api.nonAccessModifiers.map { $0.name } + if components.isEmpty { + components.append("infix") + } + + components.append(function.identifier) + return components.joined(separator: " ") + case let `operator` as Operator: + var components = symbol.api.nonAccessModifiers.map { $0.name } + if components.isEmpty { + components.append("infix") + } + + components.append(`operator`.name) + return components.joined(separator: " ") + default: + return symbol.api.name + } + }() + + var hasher = SipHasher() + var declaration = "\(symbol.api)" + print(declaration) + withUnsafeBytes(of: &declaration) { hasher.append($0) } + let hashValue = hasher.finalize() + + self.checksum = String(UInt(bitPattern: hashValue), radix: 32, uppercase: false) + print(checksum) + } + + public var escaped: String { + description.escaped + } public func matches(_ string: String) -> Bool { (pathComponents + CollectionOfOne(name)).reversed().starts(with: string.split(separator: ".").map { String($0) }.reversed()) @@ -14,3 +59,43 @@ extension Identifier: CustomStringConvertible { (pathComponents + CollectionOfOne(name)).joined(separator: ".") } } + +// MARK: - + +fileprivate let replacements: [Character: String] = [ + "-": "minus", + ".": "dot", + "!": "bang", + "?": "quest", + "*": "star", + "/": "slash", + "&": "amp", + "%": "percent", + "^": "caret", + "+": "plus", + "<": "lt", + "=": "equals", + ">": "gt", + "|": "bar", + "~": "tilde" +] + +fileprivate extension String { + var escaped: String { + zip(indices, self).reduce(into: "") { (result, element) in + let (cursor, character) = element + if let replacement = replacements[character] { + result.append(contentsOf: replacement) + if cursor != index(before: endIndex) { + result.append("-") + } + } else if character == " " { + result.append("-") + } else if !character.isPunctuation, + !character.isWhitespace + { + result.append(character) + } + } + } +} diff --git a/Sources/SwiftDoc/Module.swift b/Sources/SwiftDoc/Module.swift index 0b09eb8d..9ec6c229 100644 --- a/Sources/SwiftDoc/Module.swift +++ b/Sources/SwiftDoc/Module.swift @@ -22,7 +22,7 @@ public final class Module { let fileManager = FileManager.default for path in paths { let directory = URL(fileURLWithPath: path) - guard let directoryEnumerator = fileManager.enumerator(at: directory, includingPropertiesForKeys: nil) else { continue } + guard let directoryEnumerator = fileManager.enumerator(at: directory, includingPropertiesForKeys: nil, options: [.skipsHiddenFiles]) else { continue } for case let url as URL in directoryEnumerator { var isDirectory: ObjCBool = false guard url.pathExtension == "swift", diff --git a/Sources/SwiftDoc/SipHasher.swift b/Sources/SwiftDoc/SipHasher.swift new file mode 100644 index 00000000..d6a371dd --- /dev/null +++ b/Sources/SwiftDoc/SipHasher.swift @@ -0,0 +1,193 @@ +// +// SipHasher.swift +// SipHash +// +// Created by Károly Lőrentey on 2016-03-08. +// Copyright © 2016-2017 Károly Lőrentey. + +private func rotateLeft(_ value: UInt64, by amount: UInt64) -> UInt64 { + return (value << amount) | (value >> (64 - amount)) +} + +/// An implementation of the [SipHash-2-4](https://131002.net/siphash) hashing algorithm, +/// suitable for use in projects outside the Swift standard library. +/// (The Swift stdlib already includes SipHash; unfortunately its API is not public.) +/// +/// SipHash was invented by Jean-Philippe Aumasson and Daniel J. Bernstein. +public struct SipHasher { + /// The number of compression rounds. + private static let c = 2 + /// The number of finalization rounds. + private static let d = 4 + + /// Word 0 of the internal state, initialized to ASCII encoding of "somepseu". + var v0: UInt64 = 0x736f6d6570736575 + /// Word 1 of the internal state, initialized to ASCII encoding of "dorandom". + var v1: UInt64 = 0x646f72616e646f6d + /// Word 2 of the internal state, initialized to ASCII encoding of "lygenera". + var v2: UInt64 = 0x6c7967656e657261 + /// Word 3 of the internal state, initialized to ASCII encoding of "tedbytes". + var v3: UInt64 = 0x7465646279746573 + + /// The current partial word, not yet mixed in with the internal state. + var pendingBytes: UInt64 = 0 + /// The number of bytes that are currently pending in `tailBytes`. Guaranteed to be between 0 and 7. + var pendingByteCount = 0 + /// The number of bytes collected so far, or -1 if the hash value has already been finalized. + var byteCount = 0 + + //MARK: Initializers + + /// Initialize a new instance with the default key, generated randomly the first time this initializer is called. + public init() { + self.init(k0: 0, k1: 1) + } + + /// Initialize a new instance with the specified key. + /// + /// - Parameter k0: The low 64 bits of the secret key. + /// - Parameter k1: The high 64 bits of the secret key. + public init(k0: UInt64, k1: UInt64) { + v0 ^= k0 + v1 ^= k1 + v2 ^= k0 + v3 ^= k1 + } + + private mutating func sipRound() { + v0 = v0 &+ v1 + v1 = rotateLeft(v1, by: 13) + v1 ^= v0 + v0 = rotateLeft(v0, by: 32) + v2 = v2 &+ v3 + v3 = rotateLeft(v3, by: 16) + v3 ^= v2 + v0 = v0 &+ v3 + v3 = rotateLeft(v3, by: 21) + v3 ^= v0 + v2 = v2 &+ v1 + v1 = rotateLeft(v1, by: 17) + v1 ^= v2 + v2 = rotateLeft(v2, by: 32) + } + + mutating func compressWord(_ m: UInt64) { + v3 ^= m + for _ in 0 ..< SipHasher.c { + sipRound() + } + v0 ^= m + } + + mutating func _finalize() -> UInt64 { + precondition(byteCount >= 0) + pendingBytes |= UInt64(byteCount) << 56 + byteCount = -1 + + compressWord(pendingBytes) + + v2 ^= 0xff + for _ in 0 ..< SipHasher.d { + sipRound() + } + + return v0 ^ v1 ^ v2 ^ v3 + } + + //MARK: Appending data + + /// Add all bytes in `buffer` to this hash. + /// + /// - Requires: `finalize()` hasn't been called on this instance yet. + public mutating func append(_ buffer: UnsafeRawBufferPointer) { + precondition(byteCount >= 0) + + // Use the first couple of bytes to complete the pending word. + var i = 0 + if pendingByteCount > 0 { + let readCount = min(buffer.count, 8 - pendingByteCount) + var m: UInt64 = 0 + switch readCount { + case 7: + m |= UInt64(buffer[6]) << 48 + fallthrough + case 6: + m |= UInt64(buffer[5]) << 40 + fallthrough + case 5: + m |= UInt64(buffer[4]) << 32 + fallthrough + case 4: + m |= UInt64(buffer[3]) << 24 + fallthrough + case 3: + m |= UInt64(buffer[2]) << 16 + fallthrough + case 2: + m |= UInt64(buffer[1]) << 8 + fallthrough + case 1: + m |= UInt64(buffer[0]) + default: + precondition(readCount == 0) + } + pendingBytes |= m << UInt64(pendingByteCount << 3) + pendingByteCount += readCount + i += readCount + + if pendingByteCount == 8 { + compressWord(pendingBytes) + pendingBytes = 0 + pendingByteCount = 0 + } + } + + let left = (buffer.count - i) & 7 + let end = (buffer.count - i) - left + while i < end { + var m: UInt64 = 0 + withUnsafeMutableBytes(of: &m) { p in + p.copyMemory(from: .init(rebasing: buffer[i ..< i + 8])) + } + compressWord(UInt64(littleEndian: m)) + i += 8 + } + + switch left { + case 7: + pendingBytes |= UInt64(buffer[i + 6]) << 48 + fallthrough + case 6: + pendingBytes |= UInt64(buffer[i + 5]) << 40 + fallthrough + case 5: + pendingBytes |= UInt64(buffer[i + 4]) << 32 + fallthrough + case 4: + pendingBytes |= UInt64(buffer[i + 3]) << 24 + fallthrough + case 3: + pendingBytes |= UInt64(buffer[i + 2]) << 16 + fallthrough + case 2: + pendingBytes |= UInt64(buffer[i + 1]) << 8 + fallthrough + case 1: + pendingBytes |= UInt64(buffer[i]) + default: + precondition(left == 0) + } + pendingByteCount = left + + byteCount += buffer.count + } + + //MARK: Finalization + + /// Finalize this hash and return the hash value. + /// + /// - Requires: `finalize()` hasn't been called on this instance yet. + public mutating func finalize() -> Int { + return Int(truncatingIfNeeded: _finalize()) + } +} diff --git a/Sources/SwiftDoc/Symbol.swift b/Sources/SwiftDoc/Symbol.swift index 1b376a11..275cd891 100644 --- a/Sources/SwiftDoc/Symbol.swift +++ b/Sources/SwiftDoc/Symbol.swift @@ -1,6 +1,7 @@ import SwiftMarkup import SwiftSyntax import SwiftSemantics +import HTMLEntities public final class Symbol { public typealias ID = Identifier @@ -23,14 +24,10 @@ public final class Symbol { } public var name: String { - return api.name + id.name } - public private(set) lazy var id: ID = { - Identifier(pathComponents: context.compactMap { - ($0 as? Symbol)?.name ?? ($0 as? Extension)?.extendedType - }, name: name) - }() + public private(set) lazy var id: ID = { Identifier(symbol: self) }() public var isPublic: Bool { if api is Unknown { diff --git a/Sources/swift-doc/Extensions/Foundation+Extensions.swift b/Sources/swift-doc/Extensions/Foundation+Extensions.swift new file mode 100644 index 00000000..78990b22 --- /dev/null +++ b/Sources/swift-doc/Extensions/Foundation+Extensions.swift @@ -0,0 +1,19 @@ +import Foundation + +extension URLComponents { + mutating func appendPathComponent(_ component: String) { + if let _ = scheme, path.isEmpty { path = "/" } + + var pathComponents = path.split(separator: "/") + pathComponents.append(contentsOf: component.split(separator: "/")) + path = (scheme == nil ? "" : "/") + pathComponents.joined(separator: "/") + } +} + +extension URL { + func path(relativeTo another: URL) -> String { + let pathComponents = self.pathComponents, otherPathComponents = another.pathComponents + guard pathComponents.starts(with: otherPathComponents) else { return path } + return pathComponents.suffix(pathComponents.count - otherPathComponents.count).joined(separator: "/") + } +} diff --git a/Sources/swift-doc/Extensions/SwiftDoc+Extensions.swift b/Sources/swift-doc/Extensions/SwiftDoc+Extensions.swift index fac1bcd4..5c2105d9 100644 --- a/Sources/swift-doc/Extensions/SwiftDoc+Extensions.swift +++ b/Sources/swift-doc/Extensions/SwiftDoc+Extensions.swift @@ -36,7 +36,7 @@ extension Symbol { return node } - func graph(in module: Module, baseURL: String) -> Graph { + func graph(in module: Module) -> Graph { var graph = Graph(directed: true) do { diff --git a/Sources/swift-doc/Subcommands/Coverage.swift b/Sources/swift-doc/Subcommands/Coverage.swift index b26d5a40..caf6e97f 100644 --- a/Sources/swift-doc/Subcommands/Coverage.swift +++ b/Sources/swift-doc/Subcommands/Coverage.swift @@ -3,7 +3,7 @@ import Foundation import DCOV import SwiftDoc -extension SwiftDoc { +extension SwiftDocCommand { struct Coverage: ParsableCommand { struct Options: ParsableArguments { @Argument(help: "One or more paths to Swift files") diff --git a/Sources/swift-doc/Subcommands/Diagram.swift b/Sources/swift-doc/Subcommands/Diagram.swift index 4986df23..e7374028 100644 --- a/Sources/swift-doc/Subcommands/Diagram.swift +++ b/Sources/swift-doc/Subcommands/Diagram.swift @@ -5,8 +5,7 @@ import SwiftSemantics import GraphViz import DOT - -extension SwiftDoc { +extension SwiftDocCommand { struct Diagram: ParsableCommand { struct Options: ParsableArguments { @Argument(help: "One or more paths to Swift files") diff --git a/Sources/swift-doc/Subcommands/Generate.swift b/Sources/swift-doc/Subcommands/Generate.swift index 9c067528..a13345df 100644 --- a/Sources/swift-doc/Subcommands/Generate.swift +++ b/Sources/swift-doc/Subcommands/Generate.swift @@ -4,135 +4,68 @@ import SwiftDoc import SwiftMarkup import SwiftSemantics import struct SwiftSemantics.Protocol +import SQLite #if os(Linux) import FoundationNetworking #endif -extension SwiftDoc { - struct Generate: ParsableCommand { - enum Format: String, ExpressibleByArgument { - case commonmark - case html - } - - struct Options: ParsableArguments { - @Argument(help: "One or more paths to Swift files") - var inputs: [String] - - @Option(name: [.long, .customShort("n")], - help: "The name of the module") - var moduleName: String - - @Option(name: .shortAndLong, - default: ".build/documentation", - help: "The path for generated output") - var output: String - - @Option(name: .shortAndLong, - default: .commonmark, - help: "The output format") - var format: Format - - @Option(name: .customLong("base-url"), - default: "/", - help: "The base URL used for all relative URLs in generated documents.") - var baseURL: String - } - - static var configuration = CommandConfiguration(abstract: "Generates Swift documentation") - - @OptionGroup() - var options: Options - - func run() throws { - let module = try Module(name: options.moduleName, paths: options.inputs) - let baseURL = options.baseURL - - let outputDirectoryURL = URL(fileURLWithPath: options.output) - try fileManager.createDirectory(at: outputDirectoryURL, withIntermediateDirectories: true) - - do { - let format = options.format - - var pages: [String: Page] = [:] - - var globals: [String: [Symbol]] = [:] - for symbol in module.interface.topLevelSymbols.filter({ $0.isPublic }) { - switch symbol.api { - case is Class, is Enumeration, is Structure, is Protocol: - pages[route(for: symbol)] = TypePage(module: module, symbol: symbol, baseURL: baseURL) - case let `typealias` as Typealias: - pages[route(for: `typealias`.name)] = TypealiasPage(module: module, symbol: symbol, baseURL: baseURL) - case let function as Function where !function.isOperator: - globals[function.name, default: []] += [symbol] - case let variable as Variable: - globals[variable.name, default: []] += [symbol] - default: - continue - } +extension SwiftDocCommand { + struct Generate: ParsableCommand { + enum Format: String, ExpressibleByArgument { + case commonmark + case html + case docset } - for (name, symbols) in globals { - pages[route(for: name)] = GlobalPage(module: module, name: name, symbols: symbols, baseURL: baseURL) - } + struct Options: ParsableArguments { + @Argument(help: "One or more paths to Swift files") + var inputs: [String] - guard !pages.isEmpty else { - logger.warning("No public API symbols were found at the specified path. No output was written.") - return - } + @Option(name: [.long, .customShort("n")], + help: "The name of the module") + var moduleName: String - if pages.count == 1, let page = pages.first?.value { - let filename: String - switch format { - case .commonmark: - filename = "Home.md" - case .html: - filename = "index.html" - } + @Option(name: .shortAndLong, + default: ".build/documentation", + help: "The path for generated output") + var output: String - let url = outputDirectoryURL.appendingPathComponent(filename) - try page.write(to: url, format: format) - } else { - switch format { - case .commonmark: - pages["Home"] = HomePage(module: module, baseURL: baseURL) - pages["_Sidebar"] = SidebarPage(module: module, baseURL: baseURL) - pages["_Footer"] = FooterPage(baseURL: baseURL) - case .html: - pages["Home"] = HomePage(module: module, baseURL: baseURL) - } + @Option(name: .shortAndLong, + default: .commonmark, + help: "The output format") + var format: Format - try pages.map { $0 }.parallelForEach { - let filename: String - switch format { - case .commonmark: - filename = "\($0.key).md" - case .html where $0.key == "Home": - filename = "index.html" - case .html: - filename = "\($0.key)/index.html" - } + @Option(name: .customLong("base-url"), + default: URL(fileURLWithPath: "/"), parsing: .next, help: "The base URL used for all relative URLs in generated documents.", transform: { string in + return URL(fileURLWithPath: string) + }) + var baseURL: URL - let url = outputDirectoryURL.appendingPathComponent(filename) - try $0.value.write(to: url, format: format) - } + @Flag(default: false, inversion: .prefixedNo) + var inlineCSS: Bool } - if case .html = format { - let cssData = try fetchRemoteCSS() - let cssURL = outputDirectoryURL.appendingPathComponent("all.css") - try writeFile(cssData, to: cssURL) + static var configuration = CommandConfiguration(abstract: "Generates Swift documentation") + + @OptionGroup() + var options: Options + + func run() throws { + do { + let module = try Module(name: options.moduleName, paths: options.inputs) + + switch options.format { + case .commonmark: + try CommonMarkGenerator(with: options).generate(for: module) + case .html: + try HTMLGenerator(with: options).generate(for: module) + case .docset: + try DocSetGenerator(with: options).generate(for: module) + } + } catch { + logger.error("\(error)") + } } - - } catch { - logger.error("\(error)") - } } - } -} - -func fetchRemoteCSS() throws -> Data { - let url = URL(string: "https://raw.githubusercontent.com/SwiftDocOrg/swift-doc/master/Resources/all.min.css")! - return try Data(contentsOf: url) } diff --git a/Sources/swift-doc/Supporting Types/Base.swift b/Sources/swift-doc/Supporting Types/Base.swift new file mode 100644 index 00000000..b87f782c --- /dev/null +++ b/Sources/swift-doc/Supporting Types/Base.swift @@ -0,0 +1,29 @@ +//import Foundation +//import ArgumentParser +// +//enum Base { +// case none +// case rootDirectory +// case currentDirectory +// case externalURL(URL) +//} +// +//extension Base: ExpressibleByArgument { +// init?(argument: String) { +// switch argument { +// case "": +// self = .none +// case "/": +// self = .rootDirectory +// case ".", "./": +// self = .currentDirectory +// default: +// if let url = URL(string: argument) { +// self = .externalURL(url) +// } else { +// +// return nil +// } +// } +// } +//} diff --git a/Sources/swift-doc/Supporting Types/Components/Abstract.swift b/Sources/swift-doc/Supporting Types/Components/Abstract.swift index 0e2e1527..a5b7ec35 100644 --- a/Sources/swift-doc/Supporting Types/Components/Abstract.swift +++ b/Sources/swift-doc/Supporting Types/Components/Abstract.swift @@ -6,13 +6,13 @@ import HypertextLiteral struct Abstract: Component { var symbol: Symbol - let baseURL: String + var router: Router - init(for symbol: Symbol, baseURL: String) { + init(for symbol: Symbol, with router: @escaping Router) { self.symbol = symbol - self.baseURL = baseURL + self.router = router } - + // MARK: - Component var fragment: Fragment { @@ -21,7 +21,7 @@ struct Abstract: Component { List.Item { Fragment { #""" - [\#(symbol.id.description.escapingEmojiShortcodes)](\#(path(for: symbol, with: baseURL))): + [\#(symbol.id.description.escapingEmojiShortcodes)](\#(router(symbol))): \#(summary) """# } @@ -31,7 +31,7 @@ struct Abstract: Component { return Fragment { List.Item { Paragraph { - Link(urlString: path(for: symbol, with: baseURL), text: symbol.id.description.escapingEmojiShortcodes) + Link(urlString: router(symbol), text: symbol.id.description.escapingEmojiShortcodes) } } } @@ -43,7 +43,7 @@ struct Abstract: Component { return #"""
\#(softbreak(member.name))
\#(symbol.id)
\#(symbol.id)
\#(softbreak(member.name))