diff --git a/Sources/Splash/Output/HTMLOutputFormat.swift b/Sources/Splash/Output/HTMLOutputFormat.swift index 2f2e71f..69f0aa7 100644 --- a/Sources/Splash/Output/HTMLOutputFormat.swift +++ b/Sources/Splash/Output/HTMLOutputFormat.swift @@ -27,26 +27,60 @@ public extension HTMLOutputFormat { struct Builder: OutputBuilder { private let classPrefix: String private var html = "" + private var pendingToken: (string: String, type: TokenType)? + private var pendingWhitespace: String? fileprivate init(classPrefix: String) { self.classPrefix = classPrefix } public mutating func addToken(_ token: String, ofType type: TokenType) { - html.append("\(token.escaped)") + if var pending = pendingToken { + guard pending.type != type else { + pendingWhitespace.map { pending.string += $0 } + pendingWhitespace = nil + pending.string += token + pendingToken = pending + return + } + } + + appendPending() + pendingToken = (token, type) } public mutating func addPlainText(_ text: String) { + appendPending() html.append(text.escaped) } public mutating func addWhitespace(_ whitespace: String) { - html.append(whitespace) + if pendingToken != nil { + pendingWhitespace = (pendingWhitespace ?? "") + whitespace + } else { + html.append(whitespace) + } } - public func build() -> String { + public mutating func build() -> String { + appendPending() return html } + + private mutating func appendPending() { + if let pending = pendingToken { + html.append(""" + \(pending.string.escaped) + """) + + pendingToken = nil + } + + if let whitespace = pendingWhitespace { + html.append(whitespace) + pendingWhitespace = nil + } + } } } diff --git a/Sources/Splash/Output/OutputBuilder.swift b/Sources/Splash/Output/OutputBuilder.swift index 85d10cb..8dcaee9 100644 --- a/Sources/Splash/Output/OutputBuilder.swift +++ b/Sources/Splash/Output/OutputBuilder.swift @@ -21,5 +21,5 @@ public protocol OutputBuilder { /// Add some whitespace to the builder mutating func addWhitespace(_ whitespace: String) /// Build the final output based on the builder's current state - func build() -> Output + mutating func build() -> Output } diff --git a/Tests/SplashTests/Tests/HTMLOutputFormatTests.swift b/Tests/SplashTests/Tests/HTMLOutputFormatTests.swift index 885b040..7f18b38 100644 --- a/Tests/SplashTests/Tests/HTMLOutputFormatTests.swift +++ b/Tests/SplashTests/Tests/HTMLOutputFormatTests.swift @@ -10,6 +10,20 @@ final class HTMLOutputFormatTests: SplashTestCase { highlighter = SyntaxHighlighter(format: HTMLOutputFormat()) } + func testBasicGeneration() { + let html = highlighter.highlight(""" + public struct Test: SomeProtocol { + func hello() -> Int { return 7 } + } + """) + + XCTAssertEqual(html, """ + public struct Test: SomeProtocol { + func hello() -> Int { return 7 } + } + """) + } + func testStrippingGreaterAndLessThanCharactersFromOutput() { let html = highlighter.highlight("Array") @@ -18,6 +32,14 @@ final class HTMLOutputFormatTests: SplashTestCase { """) } + func testCommentMerging() { + let html = highlighter.highlight("// Hey I'm a comment!") + + XCTAssertEqual(html, """ + // Hey I'm a comment! + """) + } + func testAllTestsRunOnLinux() { XCTAssertTrue(TestCaseVerifier.verifyLinuxTests((type(of: self)).allTests)) } @@ -26,7 +48,9 @@ final class HTMLOutputFormatTests: SplashTestCase { extension HTMLOutputFormatTests { static var allTests: [(String, TestClosure)] { return [ - ("testStrippingGreaterAndLessThanCharactersFromOutput", testStrippingGreaterAndLessThanCharactersFromOutput) + ("testBasicGeneration", testBasicGeneration), + ("testStrippingGreaterAndLessThanCharactersFromOutput", testStrippingGreaterAndLessThanCharactersFromOutput), + ("testCommentMerging", testCommentMerging) ] } }