Skip to content

Commit

Permalink
Add support for HTML format (#56)
Browse files Browse the repository at this point in the history
Added a basic HTML renderer so the report can be quickly open in a browser.
Needed to refactor `Renderer` and `TextProjectCompareResultRenderer` to have more flexibility while rendering elements.

Test Plan:
- CI (added only 2 manual test commands for now so we do not need to pay extra cost for all the permutations)

Signed-off-by: Marcin Iwanicki <[email protected]>
  • Loading branch information
marciniwanicki authored May 30, 2020
1 parent fb184a6 commit 50c0cb4
Show file tree
Hide file tree
Showing 16 changed files with 880 additions and 502 deletions.
114 changes: 114 additions & 0 deletions CommandTests/Generated/html_format.2.e5ef416a.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

114 changes: 114 additions & 0 deletions CommandTests/Generated/html_format_verbose.2.3ef640ed.md

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions CommandTests/manual_test_commands.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Documentation/CLI.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ There are three output formats:
- `console` (default, if `-f` is not specified)
- `json`
- `markdown`
- `html`

```sh
xcdiff -f markdown # alternatively json or console
Expand Down
1 change: 1 addition & 0 deletions Sources/XCDiffCore/ProjectComparator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public enum Format: String, RawRepresentable, CaseIterable {
case console
case markdown
case json
case html
}

public struct Mode {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,60 +26,82 @@ final class TextProjectCompareResultRenderer: ProjectCompareResultRenderer {
}

func render(_ result: ProjectCompareResult) {
renderer.begin()
result.results.forEach(render)
renderer.end()
}

// MARK: - Private

private func render(_ result: CompareResult) {
guard result.same() == false else {
renderer.successHeader(title(from: result))
renderer.section(.success) {
renderer.header("\(title(from: result))", .h2)
}
return
}

renderer.errorHeader(title(from: result))
renderer.section(.warning) {
renderer.header("\(title(from: result))", .h2)
guard verbose else {
return
}

guard verbose else {
return
}
// render description
if let description = result.description {
renderer.text(description)
}

if let description = result.description {
renderer.text(description)
}
// render only in first
let onlyInFirst = result.onlyInFirst
if !onlyInFirst.isEmpty {
renderer.header("⚠️ Only in first (\(onlyInFirst.count)):", .h3)
renderer.section(.content) {
renderer.list {
onlyInFirst.forEach {
renderer.item($0)
}
}
}
}

let onlyInFirst = result.onlyInFirst
if !onlyInFirst.isEmpty {
renderer.onlyInFirstHeader(count: onlyInFirst.count)
renderer.list(.begin)
onlyInFirst.forEach {
renderer.bullet($0, indent: .one)
// render only in second
let onlyInSecond = result.onlyInSecond
if !onlyInSecond.isEmpty {
renderer.header("⚠️ Only in second (\(onlyInSecond.count)):", .h3)
renderer.section(.content) {
renderer.list {
onlyInSecond.forEach {
renderer.item($0)
}
}
}
}
renderer.list(.end)
}

let onlyInSecond = result.onlyInSecond
if !onlyInSecond.isEmpty {
renderer.onlyInSecondHeader(count: onlyInSecond.count)
renderer.list(.begin)
onlyInSecond.forEach {
renderer.bullet($0, indent: .one)
// render different values
let differentValues = result.differentValues
if !differentValues.isEmpty {
renderer.header("⚠️ Value mismatch (\(differentValues.count)):", .h3)
renderer.section(.content) {
renderer.list {
differentValues.forEach { item in
renderer.item {
renderer.pre(item.context)
renderer.list {
renderer.item(item.first)
renderer.item(item.second)
}
}
}
}
}
}
renderer.list(.end)
}

let differentValues = result.differentValues
if !differentValues.isEmpty {
renderer.differentValuesHeader(count: differentValues.count)
differentValues.forEach {
renderer.list(.begin)
renderer.bullet($0.context, indent: .one)
renderer.bullet("\($0.first)", indent: .two)
renderer.bullet("\($0.second)", indent: .two)
renderer.list(.end)
// added for compatibility with the old renderer
if differentValues.isEmpty {
renderer.line(1)
}
}

renderer.newLine(1)
}

private func title(from result: CompareResult) -> String {
Expand Down
73 changes: 47 additions & 26 deletions Sources/XCDiffCore/ResultRenderer/Renderer/ConsoleRenderer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,32 +18,22 @@ import Foundation

final class ConsoleRenderer: Renderer {
private let output: AnyOutput<String>
private var indent: Int = 0

init(output: AnyOutput<String>) {
self.output = output
}

func text(_ text: String) {
write("\(text)\n")
}

func list(_ element: RendererElement.List) {
switch element {
case .begin:
return
case .end:
newLine(1)
}
func begin() {
// nothing
}

func bullet(_ text: String, indent: RendererElement.Indent) {
let spacing = String(repeating: " ", count: 2 * indent.rawValue)
let symbol = bullet(indent: indent)
self.text("\(spacing)\(symbol) \(text)")
func end() {
// nothing
}

func newLine(_ count: Int = 1) {
write(String(repeating: "\n", count: count))
func section(_: RendererElement.Style, _ content: () -> Void) {
content()
}

func header(_ text: String, _ header: RendererElement.Header) {
Expand All @@ -61,20 +51,51 @@ final class ConsoleRenderer: Renderer {
}
}

// MARK: - Private
func text(_ text: String) {
write("\(text)\n")
}

private func bullet(indent: RendererElement.Indent) -> String {
switch indent {
case .zero:
return "»"
case .one:
return ""
case .two:
return ""
func pre(_ text: String) {
self.text(text)
}

func list(_ content: () -> Void) {
indent += 1
content()
indent -= 1
line(1)
}

func item(_ text: String) {
item {
write(text)
line(1)
}
}

func item(_ content: () -> Void) {
let spacing = String(repeating: " ", count: 2 * indent)
let symbol = bullet(indent: indent)
write("\(spacing)\(symbol) ")
content()
}

func line(_ count: Int) {
write(String(repeating: "\n", count: count))
}

// MARK: - Private

private func write(_ string: String) {
output.write(string)
}

private func bullet(indent: Int) -> String {
switch indent {
case 1:
return ""
default:
return ""
}
}
}
Loading

0 comments on commit 50c0cb4

Please sign in to comment.