From 72f69c81bce52ad970b9d690da7ad6263b24ac8f Mon Sep 17 00:00:00 2001 From: Daniel Tull Date: Tue, 17 Dec 2024 17:21:43 +0000 Subject: [PATCH 1/4] Ensure Text's x-axis is also 1-based This is to match the way Color and (the Text y-axis) works. --- Sources/TerminalUI/Views/Text.swift | 2 +- Tests/TerminalUITests/AppTests.swift | 4 ++-- Tests/TerminalUITests/EnvironmentTests.swift | 16 ++++++++-------- Tests/TerminalUITests/ViewModifierTests.swift | 2 +- .../ViewModifiers/BackgroundColor.swift | 2 +- .../ViewModifiers/BlinkingTests.swift | 2 +- .../ViewModifiers/BoldTests.swift | 2 +- .../ViewModifiers/ForegroundColorTests.swift | 2 +- .../ViewModifiers/HiddenTests.swift | 2 +- .../ViewModifiers/InverseTests.swift | 2 +- .../ViewModifiers/ItalicTests.swift | 2 +- .../ViewModifiers/StrikethroughTests.swift | 2 +- .../ViewModifiers/UnderlineTests.swift | 2 +- Tests/TerminalUITests/Views/TextTests.swift | 10 +++++----- 14 files changed, 26 insertions(+), 26 deletions(-) diff --git a/Sources/TerminalUI/Views/Text.swift b/Sources/TerminalUI/Views/Text.swift index 3634932..cb16be6 100644 --- a/Sources/TerminalUI/Views/Text.swift +++ b/Sources/TerminalUI/Views/Text.swift @@ -21,7 +21,7 @@ public struct Text: Builtin, View { hidden: environment.hidden, strikethrough: environment.strikethrough ) - canvas.draw(pixel, at: Position(x: Horizontal(index), y: 0)) + canvas.draw(pixel, at: Position(x: Horizontal(index), y: 1)) } } } diff --git a/Tests/TerminalUITests/AppTests.swift b/Tests/TerminalUITests/AppTests.swift index 0320210..d6d01a4 100644 --- a/Tests/TerminalUITests/AppTests.swift +++ b/Tests/TerminalUITests/AppTests.swift @@ -32,7 +32,7 @@ struct AppTests { "[27m", // Inverse off "[28m", // Hidden off "[29m", // Strikethrough off - "[0;1Ha", // Position + content + "[1;1Ha", // Position + content ]) } @@ -63,7 +63,7 @@ struct AppTests { "[27m", // Inverse off "[28m", // Hidden off "[29m", // Strikethrough off - "[0;1Ha", // Position + content + "[1;1Ha", // Position + content ]) } } diff --git a/Tests/TerminalUITests/EnvironmentTests.swift b/Tests/TerminalUITests/EnvironmentTests.swift index 564a06d..53cfed6 100644 --- a/Tests/TerminalUITests/EnvironmentTests.swift +++ b/Tests/TerminalUITests/EnvironmentTests.swift @@ -15,13 +15,13 @@ struct EnvironmentTests { } #expect(canvas.pixels == [ - Position(x: 1, y: 0): Pixel("d"), - Position(x: 2, y: 0): Pixel("e"), - Position(x: 3, y: 0): Pixel("f"), - Position(x: 4, y: 0): Pixel("a"), - Position(x: 5, y: 0): Pixel("u"), - Position(x: 6, y: 0): Pixel("l"), - Position(x: 7, y: 0): Pixel("t"), + Position(x: 1, y: 1): Pixel("d"), + Position(x: 2, y: 1): Pixel("e"), + Position(x: 3, y: 1): Pixel("f"), + Position(x: 4, y: 1): Pixel("a"), + Position(x: 5, y: 1): Pixel("u"), + Position(x: 6, y: 1): Pixel("l"), + Position(x: 7, y: 1): Pixel("t"), ]) } @@ -33,7 +33,7 @@ struct EnvironmentTests { } #expect(canvas.pixels == [ - Position(x: 1, y: 0): Pixel("b"), + Position(x: 1, y: 1): Pixel("b"), ]) } } diff --git a/Tests/TerminalUITests/ViewModifierTests.swift b/Tests/TerminalUITests/ViewModifierTests.swift index c84c194..2d946ee 100644 --- a/Tests/TerminalUITests/ViewModifierTests.swift +++ b/Tests/TerminalUITests/ViewModifierTests.swift @@ -33,7 +33,7 @@ struct ViewModifierTests { "[27m", // Inverse off "[28m", // Hidden off "[29m", // Strikethrough off - "[0;1HA", // Position + content + "[1;1HA", // Position + content ]) } } diff --git a/Tests/TerminalUITests/ViewModifiers/BackgroundColor.swift b/Tests/TerminalUITests/ViewModifiers/BackgroundColor.swift index 4f4eb89..90471f2 100644 --- a/Tests/TerminalUITests/ViewModifiers/BackgroundColor.swift +++ b/Tests/TerminalUITests/ViewModifiers/BackgroundColor.swift @@ -38,7 +38,7 @@ struct BackgroundColorTests { "[27m", // Inverse off "[28m", // Hidden off "[29m", // Strikethrough off - "[0;1Ha", // Position + content + "[1;1Ha", // Position + content ]) } } diff --git a/Tests/TerminalUITests/ViewModifiers/BlinkingTests.swift b/Tests/TerminalUITests/ViewModifiers/BlinkingTests.swift index 363c67f..819714c 100644 --- a/Tests/TerminalUITests/ViewModifiers/BlinkingTests.swift +++ b/Tests/TerminalUITests/ViewModifiers/BlinkingTests.swift @@ -31,7 +31,7 @@ struct BlinkingTests { "[27m", // Inverse off "[28m", // Hidden off "[29m", // Strikethrough off - "[0;1Ha", // Position + content + "[1;1Ha", // Position + content ]) } } diff --git a/Tests/TerminalUITests/ViewModifiers/BoldTests.swift b/Tests/TerminalUITests/ViewModifiers/BoldTests.swift index 42b3aba..7af02d0 100644 --- a/Tests/TerminalUITests/ViewModifiers/BoldTests.swift +++ b/Tests/TerminalUITests/ViewModifiers/BoldTests.swift @@ -31,7 +31,7 @@ struct BoldTests { "[27m", // Inverse off "[28m", // Hidden off "[29m", // Strikethrough off - "[0;1Ha", // Position + content + "[1;1Ha", // Position + content ]) } } diff --git a/Tests/TerminalUITests/ViewModifiers/ForegroundColorTests.swift b/Tests/TerminalUITests/ViewModifiers/ForegroundColorTests.swift index c72462e..8fd6634 100644 --- a/Tests/TerminalUITests/ViewModifiers/ForegroundColorTests.swift +++ b/Tests/TerminalUITests/ViewModifiers/ForegroundColorTests.swift @@ -38,7 +38,7 @@ struct ForegroundColorTests { "[27m", // Inverse off "[28m", // Hidden off "[29m", // Strikethrough off - "[0;1Ha", // Position + content + "[1;1Ha", // Position + content ]) } } diff --git a/Tests/TerminalUITests/ViewModifiers/HiddenTests.swift b/Tests/TerminalUITests/ViewModifiers/HiddenTests.swift index ade9e75..c5dc163 100644 --- a/Tests/TerminalUITests/ViewModifiers/HiddenTests.swift +++ b/Tests/TerminalUITests/ViewModifiers/HiddenTests.swift @@ -31,7 +31,7 @@ struct HiddenTests { "[27m", // Inverse off expected, // Hidden "[29m", // Strikethrough off - "[0;1Ha", // Position + content + "[1;1Ha", // Position + content ]) } } diff --git a/Tests/TerminalUITests/ViewModifiers/InverseTests.swift b/Tests/TerminalUITests/ViewModifiers/InverseTests.swift index bdebd08..0aca36b 100644 --- a/Tests/TerminalUITests/ViewModifiers/InverseTests.swift +++ b/Tests/TerminalUITests/ViewModifiers/InverseTests.swift @@ -31,7 +31,7 @@ struct InverseTests { expected, // Inverse "[28m", // Hidden off "[29m", // Strikethrough off - "[0;1Ha", // Position + content + "[1;1Ha", // Position + content ]) } } diff --git a/Tests/TerminalUITests/ViewModifiers/ItalicTests.swift b/Tests/TerminalUITests/ViewModifiers/ItalicTests.swift index 65c715c..57c4af6 100644 --- a/Tests/TerminalUITests/ViewModifiers/ItalicTests.swift +++ b/Tests/TerminalUITests/ViewModifiers/ItalicTests.swift @@ -31,7 +31,7 @@ struct ItalicTests { "[27m", // Inverse off "[28m", // Hidden off "[29m", // Strikethrough off - "[0;1Ha", // Position + content + "[1;1Ha", // Position + content ]) } } diff --git a/Tests/TerminalUITests/ViewModifiers/StrikethroughTests.swift b/Tests/TerminalUITests/ViewModifiers/StrikethroughTests.swift index ea1a80b..921f949 100644 --- a/Tests/TerminalUITests/ViewModifiers/StrikethroughTests.swift +++ b/Tests/TerminalUITests/ViewModifiers/StrikethroughTests.swift @@ -31,7 +31,7 @@ struct StrikethroughTests { "[27m", // Inverse off "[28m", // Hidden off expected, // Strikethrough - "[0;1Ha", // Position + content + "[1;1Ha", // Position + content ]) } } diff --git a/Tests/TerminalUITests/ViewModifiers/UnderlineTests.swift b/Tests/TerminalUITests/ViewModifiers/UnderlineTests.swift index 0f8ecc1..acd9977 100644 --- a/Tests/TerminalUITests/ViewModifiers/UnderlineTests.swift +++ b/Tests/TerminalUITests/ViewModifiers/UnderlineTests.swift @@ -31,7 +31,7 @@ struct UnderlineTests { "[27m", // Inverse off "[28m", // Hidden off "[29m", // Strikethrough off - "[0;1Ha", // Position + content + "[1;1Ha", // Position + content ]) } } diff --git a/Tests/TerminalUITests/Views/TextTests.swift b/Tests/TerminalUITests/Views/TextTests.swift index df87a20..18186c2 100644 --- a/Tests/TerminalUITests/Views/TextTests.swift +++ b/Tests/TerminalUITests/Views/TextTests.swift @@ -15,11 +15,11 @@ struct TextTests { } #expect(canvas.pixels == [ - Position(x: 1, y: 0): Pixel("H"), - Position(x: 2, y: 0): Pixel("e"), - Position(x: 3, y: 0): Pixel("l"), - Position(x: 4, y: 0): Pixel("l"), - Position(x: 5, y: 0): Pixel("o"), + Position(x: 1, y: 1): Pixel("H"), + Position(x: 2, y: 1): Pixel("e"), + Position(x: 3, y: 1): Pixel("l"), + Position(x: 4, y: 1): Pixel("l"), + Position(x: 5, y: 1): Pixel("o"), ]) } } From 417c26423d92f160b3744197f52901c80e51c334 Mon Sep 17 00:00:00 2001 From: Daniel Tull Date: Tue, 17 Dec 2024 17:51:10 +0000 Subject: [PATCH 2/4] Add an origin to Position --- Sources/TerminalUI/Primitives/Position.swift | 8 ++++++-- Tests/TerminalUITests/Primitives/PositionTests.swift | 11 +++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 Tests/TerminalUITests/Primitives/PositionTests.swift diff --git a/Sources/TerminalUI/Primitives/Position.swift b/Sources/TerminalUI/Primitives/Position.swift index 01bbe3f..1f21eb6 100644 --- a/Sources/TerminalUI/Primitives/Position.swift +++ b/Sources/TerminalUI/Primitives/Position.swift @@ -1,7 +1,7 @@ public struct Position: Equatable, Hashable { - let x: Horizontal - let y: Vertical + package let x: Horizontal + package let y: Vertical public init(x: Horizontal, y: Vertical) { self.x = x @@ -9,6 +9,10 @@ public struct Position: Equatable, Hashable { } } +extension Position { + package static var origin: Position { Position(x: 1, y: 1) } +} + extension Position { var controlSequence: ControlSequence { "\(y);\(x)H" diff --git a/Tests/TerminalUITests/Primitives/PositionTests.swift b/Tests/TerminalUITests/Primitives/PositionTests.swift new file mode 100644 index 0000000..8959a10 --- /dev/null +++ b/Tests/TerminalUITests/Primitives/PositionTests.swift @@ -0,0 +1,11 @@ +import TerminalUI +import Testing + +@Suite("Position") struct PositionTests { + + @Test("origin") + func origin() async throws { + #expect(Position.origin.x == 1) + #expect(Position.origin.y == 1) + } +} From eda9a6915cb184ee6d8feb733688e362683d9fbd Mon Sep 17 00:00:00 2001 From: Daniel Tull Date: Tue, 17 Dec 2024 17:30:48 +0000 Subject: [PATCH 3/4] Fix implementations of Strideable --- .../TerminalUI/Primitives/Dimensions.swift | 8 ++++---- .../Primitives/DimensionsTests.swift | 20 +++++++++++++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/Sources/TerminalUI/Primitives/Dimensions.swift b/Sources/TerminalUI/Primitives/Dimensions.swift index 570ff33..7200276 100644 --- a/Sources/TerminalUI/Primitives/Dimensions.swift +++ b/Sources/TerminalUI/Primitives/Dimensions.swift @@ -36,11 +36,11 @@ extension Horizontal: ExpressibleByIntegerLiteral { extension Horizontal: Strideable { public func advanced(by n: Int) -> Horizontal { - Horizontal(value: value + n) + Horizontal(value: value.advanced(by: n)) } public func distance(to other: Horizontal) -> Int { - value - other.value + value.distance(to: other.value) } } @@ -87,11 +87,11 @@ extension Vertical: ExpressibleByIntegerLiteral { extension Vertical: Strideable { public func advanced(by n: Int) -> Vertical { - Vertical(value: value + n) + Vertical(value: value.advanced(by: n)) } public func distance(to other: Vertical) -> Int { - value - other.value + value.distance(to: other.value) } } diff --git a/Tests/TerminalUITests/Primitives/DimensionsTests.swift b/Tests/TerminalUITests/Primitives/DimensionsTests.swift index abb55da..b007bd9 100644 --- a/Tests/TerminalUITests/Primitives/DimensionsTests.swift +++ b/Tests/TerminalUITests/Primitives/DimensionsTests.swift @@ -40,6 +40,16 @@ import Testing #expect(horizontals.next() == 3) #expect(horizontals.next() == nil) } + + @Test("Strideable: advanced(by:)", arguments: [-1, 0, 1], [-1, 0, 1]) + func strideable_advancedBy(start: Int, n: Int) { + #expect(Horizontal(start).advanced(by: n) == Horizontal(start.advanced(by: n))) + } + + @Test("Strideable: distance(to:)", arguments: [-1, 0, 1], [-1, 0, 1]) + func strideable_distanceTo(x: Int, y: Int) { + #expect(Horizontal(x).distance(to: Horizontal(y)) == x.distance(to: y)) + } @Test("init(some BinaryInteger)") func initBinaryInteger() { @@ -85,6 +95,16 @@ import Testing #expect(verticals.next() == nil) } + @Test("Strideable: advanced(by:)", arguments: [-1, 0, 1], [-1, 0, 1]) + func strideable_advancedBy(start: Int, n: Int) { + #expect(Vertical(start).advanced(by: n) == Vertical(start.advanced(by: n))) + } + + @Test("Strideable: distance(to:)", arguments: [-1, 0, 1], [-1, 0, 1]) + func strideable_distanceTo(x: Int, y: Int) { + #expect(Vertical(x).distance(to: Vertical(y)) == x.distance(to: y)) + } + @Test("init(some BinaryInteger)") func initBinaryInteger() { let value: Int = Int.random(in: 0..<1_000_000) From e7c3421fda4b0ba1479b1f1a34bede7e9d4be768 Mon Sep 17 00:00:00 2001 From: Daniel Tull Date: Tue, 17 Dec 2024 16:51:55 +0000 Subject: [PATCH 4/4] Add debug output to the test canvas --- Sources/TerminalUI/Primitives/Pixel.swift | 2 +- Sources/TerminalUI/Primitives/Size.swift | 4 ++-- Sources/TerminalUITesting/TestCanvas.swift | 23 +++++++++++++++++++ .../Testing/TestCanvasTests.swift | 19 +++++++++++++++ 4 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 Tests/TerminalUITests/Testing/TestCanvasTests.swift diff --git a/Sources/TerminalUI/Primitives/Pixel.swift b/Sources/TerminalUI/Primitives/Pixel.swift index f315f86..0b98bd2 100644 --- a/Sources/TerminalUI/Primitives/Pixel.swift +++ b/Sources/TerminalUI/Primitives/Pixel.swift @@ -1,7 +1,7 @@ public struct Pixel: Equatable { - let content: Character + package let content: Character let foreground: Color let background: Color let bold: Bold diff --git a/Sources/TerminalUI/Primitives/Size.swift b/Sources/TerminalUI/Primitives/Size.swift index 5195103..45771a1 100644 --- a/Sources/TerminalUI/Primitives/Size.swift +++ b/Sources/TerminalUI/Primitives/Size.swift @@ -2,8 +2,8 @@ import Foundation package struct Size { - let width: Horizontal - let height: Vertical + package let width: Horizontal + package let height: Vertical package init(width: Horizontal, height: Vertical) { self.width = width diff --git a/Sources/TerminalUITesting/TestCanvas.swift b/Sources/TerminalUITesting/TestCanvas.swift index d26b5b6..4b9c804 100644 --- a/Sources/TerminalUITesting/TestCanvas.swift +++ b/Sources/TerminalUITesting/TestCanvas.swift @@ -17,3 +17,26 @@ extension TestCanvas { content()._render(in: self, size: size) } } + +extension TestCanvas: CustomStringConvertible { + + public var description: String { + + let origin = Position.origin + let ys = (origin.y...size.height) + let xs = (origin.x...size.width) + + var characters: [[Character]] = ys.map { _ in xs.map { _ in " " } } + + for (position, pixel) in pixels { + guard ys.contains(position.y) && xs.contains(position.x) else { continue } + let y = origin.y.distance(to: position.y) + let x = origin.x.distance(to: position.x) + characters[y][x] = pixel.content + } + + return characters + .map { String($0) } + .joined(separator: "\n") + } +} diff --git a/Tests/TerminalUITests/Testing/TestCanvasTests.swift b/Tests/TerminalUITests/Testing/TestCanvasTests.swift new file mode 100644 index 0000000..3fec233 --- /dev/null +++ b/Tests/TerminalUITests/Testing/TestCanvasTests.swift @@ -0,0 +1,19 @@ +import TerminalUI +import TerminalUITesting +import Testing + +@Suite("TestCanvas") +struct TestCanvasTests { + + @Test("CustomStringConvertible") + func customStringConvertible() async throws { + + let canvas = TestCanvas(width: 5, height: 1) + + canvas.render { + Text("hello") + } + + #expect(canvas.description == "hello") + } +}