Skip to content
This repository was archived by the owner on Jun 1, 2023. It is now read-only.

Commit 57605fc

Browse files
committed
Refactor to Generator models
Initial implementation of DocSet generation
1 parent 034506a commit 57605fc

File tree

11 files changed

+394
-133
lines changed

11 files changed

+394
-133
lines changed

Package.resolved

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ let package = Package(
1818
.package(url: "https://github.com/NSHipster/HypertextLiteral.git", .upToNextMinor(from: "0.0.2")),
1919
.package(url: "https://github.com/SwiftDocOrg/Markup.git", .upToNextMinor(from: "0.0.3")),
2020
.package(url: "https://github.com/NSHipster/SwiftSyntaxHighlighter.git", .revision("1.0.0")),
21+
.package(url: "https://github.com/stephencelis/SQLite.swift.git", .revision("0.12.2")),
2122
.package(url: "https://github.com/apple/swift-argument-parser.git", .upToNextMinor(from: "0.0.5")),
2223
.package(url: "https://github.com/apple/swift-log.git", .upToNextMinor(from: "1.2.0")),
2324
.package(url: "https://github.com/NSHipster/swift-log-github-actions.git", .upToNextMinor(from: "0.0.1")),
@@ -27,7 +28,7 @@ let package = Package(
2728
// Targets can depend on other targets in this package, and on products in packages which this package depends on.
2829
.target(
2930
name: "swift-doc",
30-
dependencies: ["ArgumentParser", "SwiftDoc", "SwiftSemantics", "SwiftMarkup", "CommonMarkBuilder", "HypertextLiteral", "Markup", "DCOV", "GraphViz", "SwiftSyntaxHighlighter", "Logging", "LoggingGitHubActions"]
31+
dependencies: ["ArgumentParser", "SwiftDoc", "SwiftSemantics", "SwiftMarkup", "CommonMarkBuilder", "HypertextLiteral", "Markup", "DCOV", "GraphViz", "SwiftSyntaxHighlighter", "SQLite", "Logging", "LoggingGitHubActions"]
3132
),
3233
.target(
3334
name: "DCOV",

Sources/swift-doc/Subcommands/Diagram.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import SwiftSemantics
55
import GraphViz
66
import DOT
77

8-
98
extension SwiftDoc {
109
struct Diagram: ParsableCommand {
1110
struct Options: ParsableArguments {

Sources/swift-doc/Subcommands/Generate.swift

Lines changed: 44 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -4,130 +4,60 @@ import SwiftDoc
44
import SwiftMarkup
55
import SwiftSemantics
66
import struct SwiftSemantics.Protocol
7+
import SQLite
78

89
extension SwiftDoc {
9-
struct Generate: ParsableCommand {
10-
enum Format: String, ExpressibleByArgument {
11-
case commonmark
12-
case html
13-
}
14-
15-
struct Options: ParsableArguments {
16-
@Argument(help: "One or more paths to Swift files")
17-
var inputs: [String]
18-
19-
@Option(name: [.long, .customShort("n")],
20-
help: "The name of the module")
21-
var moduleName: String
22-
23-
@Option(name: .shortAndLong,
24-
default: ".build/documentation",
25-
help: "The path for generated output")
26-
var output: String
27-
28-
@Option(name: .shortAndLong,
29-
default: .commonmark,
30-
help: "The output format")
31-
var format: Format
32-
33-
@Option(name: .customLong("base-url"),
34-
default: "/",
35-
help: "The base URL used for all relative URLs in generated documents.")
36-
var baseURL: String
37-
}
38-
39-
static var configuration = CommandConfiguration(abstract: "Generates Swift documentation")
40-
41-
@OptionGroup()
42-
var options: Options
43-
44-
func run() throws {
45-
let module = try Module(name: options.moduleName, paths: options.inputs)
46-
47-
let outputDirectoryURL = URL(fileURLWithPath: options.output)
48-
try fileManager.createDirectory(at: outputDirectoryURL, withIntermediateDirectories: true, attributes: fileAttributes)
49-
50-
do {
51-
let format = options.format
52-
53-
var pages: [String: Page] = [:]
54-
55-
var globals: [String: [Symbol]] = [:]
56-
for symbol in module.interface.topLevelSymbols.filter({ $0.isPublic }) {
57-
switch symbol.api {
58-
case is Class, is Enumeration, is Structure, is Protocol:
59-
pages[path(for: symbol)] = TypePage(module: module, symbol: symbol)
60-
case let `typealias` as Typealias:
61-
pages[path(for: `typealias`.name)] = TypealiasPage(module: module, symbol: symbol)
62-
case let function as Function where !function.isOperator:
63-
globals[function.name, default: []] += [symbol]
64-
case let variable as Variable:
65-
globals[variable.name, default: []] += [symbol]
66-
default:
67-
continue
68-
}
10+
struct Generate: ParsableCommand {
11+
enum Format: String, ExpressibleByArgument {
12+
case commonmark
13+
case html
14+
case docset
6915
}
7016

71-
for (name, symbols) in globals {
72-
pages[path(for: name)] = GlobalPage(module: module, name: name, symbols: symbols)
73-
}
17+
struct Options: ParsableArguments {
18+
@Argument(help: "One or more paths to Swift files")
19+
var inputs: [String]
7420

75-
guard !pages.isEmpty else {
76-
logger.warning("No public API symbols were found at the specified path. No output was written.")
77-
return
78-
}
21+
@Option(name: [.long, .customShort("n")],
22+
help: "The name of the module")
23+
var moduleName: String
7924

80-
if pages.count == 1, let page = pages.first?.value {
81-
let filename: String
82-
switch format {
83-
case .commonmark:
84-
filename = "Home.md"
85-
case .html:
86-
filename = "index.html"
87-
}
25+
@Option(name: .shortAndLong,
26+
default: ".build/documentation",
27+
help: "The path for generated output")
28+
var output: String
8829

89-
let url = outputDirectoryURL.appendingPathComponent(filename)
90-
try page.write(to: url, format: format, baseURL: options.baseURL)
91-
} else {
92-
switch format {
93-
case .commonmark:
94-
pages["Home"] = HomePage(module: module)
95-
pages["_Sidebar"] = SidebarPage(module: module)
96-
pages["_Footer"] = FooterPage()
97-
case .html:
98-
pages["Home"] = HomePage(module: module)
99-
}
30+
@Option(name: .shortAndLong,
31+
default: .commonmark,
32+
help: "The output format")
33+
var format: Format
10034

101-
try pages.map { $0 }.parallelForEach {
102-
let filename: String
103-
switch format {
104-
case .commonmark:
105-
filename = "\($0.key).md"
106-
case .html where $0.key == "Home":
107-
filename = "index.html"
108-
case .html:
109-
filename = "\($0.key)/index.html"
110-
}
111-
112-
let url = outputDirectoryURL.appendingPathComponent(filename)
113-
try $0.value.write(to: url, format: format, baseURL: options.baseURL)
114-
}
35+
@Option(name: .customLong("base-url"),
36+
default: "/",
37+
help: "The base URL used for all relative URLs in generated documents.")
38+
var baseURL: String
11539
}
11640

117-
if case .html = format {
118-
let cssData = try fetchRemoteCSS()
119-
let cssURL = outputDirectoryURL.appendingPathComponent("all.css")
120-
try writeFile(cssData, to: cssURL)
41+
static var configuration = CommandConfiguration(abstract: "Generates Swift documentation")
42+
43+
@OptionGroup()
44+
var options: Options
45+
46+
func run() throws {
47+
do {
48+
let module = try Module(name: options.moduleName, paths: options.inputs)
49+
50+
switch options.format {
51+
case .commonmark:
52+
try CommonMarkGenerator.generate(for: module, with: options)
53+
case .html:
54+
try HTMLGenerator.generate(for: module, with: options)
55+
case .docset:
56+
try DocSetGenerator.generate(for: module, with: options)
57+
}
58+
} catch {
59+
logger.error("\(error)")
60+
}
12161
}
122-
123-
} catch {
124-
logger.error("\(error)")
125-
}
12662
}
127-
}
128-
}
129-
130-
func fetchRemoteCSS() throws -> Data {
131-
let url = URL(string: "https://raw.githubusercontent.com/SwiftDocOrg/swift-doc/master/Resources/all.min.css")!
132-
return try Data(contentsOf: url)
13363
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import SwiftDoc
2+
3+
protocol Generator {
4+
static func generate(for module: Module, with options: SwiftDoc.Generate.Options) throws
5+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import Foundation
2+
import SwiftDoc
3+
import SwiftSemantics
4+
import struct SwiftSemantics.Protocol
5+
6+
enum CommonMarkGenerator: Generator {
7+
static func generate(for module: Module, with options: SwiftDoc.Generate.Options) throws {
8+
assert(options.format == .commonmark)
9+
10+
let module = try Module(name: options.moduleName, paths: options.inputs)
11+
12+
let outputDirectoryURL = URL(fileURLWithPath: options.output)
13+
try fileManager.createDirectory(at: outputDirectoryURL, withIntermediateDirectories: true, attributes: fileAttributes)
14+
15+
var pages: [String: Page] = [:]
16+
17+
var globals: [String: [Symbol]] = [:]
18+
for symbol in module.interface.topLevelSymbols.filter({ $0.isPublic }) {
19+
switch symbol.api {
20+
case is Class, is Enumeration, is Structure, is Protocol:
21+
pages[path(for: symbol)] = TypePage(module: module, symbol: symbol)
22+
case let `typealias` as Typealias:
23+
pages[path(for: `typealias`.name)] = TypealiasPage(module: module, symbol: symbol)
24+
case let function as Function where !function.isOperator:
25+
globals[function.name, default: []] += [symbol]
26+
case let variable as Variable:
27+
globals[variable.name, default: []] += [symbol]
28+
default:
29+
continue
30+
}
31+
}
32+
33+
for (name, symbols) in globals {
34+
pages[path(for: name)] = GlobalPage(module: module, name: name, symbols: symbols)
35+
}
36+
37+
guard !pages.isEmpty else {
38+
logger.warning("No public API symbols were found at the specified path. No output was written.")
39+
return
40+
}
41+
42+
if pages.count == 1, let page = pages.first?.value {
43+
let filename = "Home.md"
44+
let url = outputDirectoryURL.appendingPathComponent(filename)
45+
try page.write(to: url, baseURL: options.baseURL)
46+
} else {
47+
pages["Home"] = HomePage(module: module)
48+
pages["_Sidebar"] = SidebarPage(module: module)
49+
pages["_Footer"] = FooterPage()
50+
51+
try pages.map { $0 }.parallelForEach {
52+
let filename = "\($0.key).md"
53+
let url = outputDirectoryURL.appendingPathComponent(filename)
54+
try $0.value.write(to: url, baseURL: options.baseURL)
55+
}
56+
}
57+
}
58+
}
59+
60+
// MARK: -
61+
62+
fileprivate extension Page {
63+
func write(to url: URL, baseURL: String) throws {
64+
guard let data = document.render(format: .commonmark).data(using: .utf8) else { return }
65+
try writeFile(data, to: url)
66+
}
67+
}

0 commit comments

Comments
 (0)