CHGenerator/WebsiteGenerator/Generators/MarkdownProcessor.swift

131 lines
4.5 KiB
Swift
Raw Normal View History

2022-08-16 10:39:05 +02:00
import Foundation
import Ink
struct PageContentGenerator {
2022-08-16 12:26:45 +02:00
private let files: FileProcessor
2022-08-16 10:39:05 +02:00
2022-08-16 12:26:45 +02:00
init(files: FileProcessor) {
self.files = files
2022-08-16 10:39:05 +02:00
}
func generate(page: Page, language: String, at url: URL) throws -> String {
var errorToThrow: Error? = nil
let content = try wrap(.missingPage(page: url.path, language: language)) {
try String(contentsOf: url)
}
var hasCodeContent = false
let imageModifier = Modifier(target: .images) { html, markdown in
let result = processMarkdownImage(markdown: markdown, html: html, page: page)
switch result {
case .success(let content):
return content
case .failure(let error):
errorToThrow = error
return ""
}
}
let codeModifier = Modifier(target: .codeBlocks) { html, markdown in
if markdown.starts(with: "```swift") {
#warning("Syntax highlight swift code")
return html
}
hasCodeContent = true
return html
}
let parser = MarkdownParser(modifiers: [imageModifier, codeModifier])
#warning("Check links in markdown for (missing) files to copy")
if hasCodeContent {
#warning("Automatically add hljs hightlighting if code samples are found")
}
let result = parser.html(from: content)
if let error = errorToThrow {
throw error
}
return result
}
private func processMarkdownImage(markdown: Substring, html: String, page: Page) -> Result<String, Error> {
let fileAndTitle = markdown
.components(separatedBy: "(").last!
.components(separatedBy: ")").first!
let file = fileAndTitle.components(separatedBy: " \"").first! // Remove title
let rightSubtitle: String?
if fileAndTitle.contains(" \"") {
rightSubtitle = fileAndTitle.dropBeforeFirst("\"").dropAfterLast("\"")
} else {
rightSubtitle = nil
}
let leftSubtitle = markdown
.components(separatedBy: "]").first!
.components(separatedBy: "[").last!.nonEmpty
#warning("Specify page image width in configuration")
let pageImageWidth = 748
let size: NSSize
let imagePath = page.pathRelativeToRootForContainedInputFile(file)
do {
2022-08-16 12:26:45 +02:00
size = try files.requireImage(
2022-08-16 10:39:05 +02:00
source: imagePath,
destination: imagePath,
width: pageImageWidth,
desiredHeight: nil,
createDoubleVersion: true)
} catch {
return .failure(error)
}
let file2x = file.insert("@2x", beforeLast: ".")
#warning("Move HTML code to single location")
let result = articelImage(
image: file,
image2x: file2x,
width: size.width,
height: size.height,
rightSubtitle: rightSubtitle,
leftSubtitle: leftSubtitle)
return .success(result)
}
private func articelImage(image: String, image2x: String, width: CGFloat, height: CGFloat, rightSubtitle: String?, leftSubtitle: String?) -> String {
let subtitleCode = subtitle(left: leftSubtitle, right: rightSubtitle)
return fullImageCode(image: image, image2x: image2x, width: width, height: height, subtitle: subtitleCode)
}
private func articleImageWithoutSubtitle(image: String, image2x: String, width: CGFloat, height: CGFloat) -> String {
"""
<span class="image">
<img src="\(image)" srcset="\(image2x) 2x" width="\(Int(width))" height="\(Int(height))" loading="lazy"/>
</span>
"""
}
private func subtitle(left: String?, right: String?) -> String {
guard left != nil || right != nil else {
return ""
}
let leftCode = left.unwrapped { "<span class=\"left\">\($0)</span>" } ?? ""
let rightCode = right.unwrapped { "<span class=\"right\">\($0)</span>" } ?? ""
return """
<div class="subtitle">
\(leftCode)
\(rightCode)
</div>
"""
}
private func fullImageCode(image: String, image2x: String, width: CGFloat, height: CGFloat, subtitle: String) -> String {
"""
<span class="image">
<img src="\(image)" srcset="\(image2x) 2x" width="\(Int(width))" height="\(Int(height))" loading="lazy"/>
\(subtitle)
</span>
"""
}
}