feat: Adds stack separators, removes some unused nodes from cli-doc module
All checks were successful
CI / Run tests. (push) Successful in 52s
All checks were successful
CI / Run tests. (push) Successful in 52s
This commit is contained in:
@@ -4,21 +4,53 @@ public struct HStack: TextNode {
|
||||
@usableFromInline
|
||||
let content: [any TextNode]
|
||||
|
||||
@usableFromInline
|
||||
let separator: Separator.Horizontal
|
||||
|
||||
@inlinable
|
||||
public init(
|
||||
separator: Separator.Horizontal = .space(count: 1),
|
||||
@TextBuilder content: () -> any TextNode
|
||||
) {
|
||||
self.content = array(from: content())
|
||||
self.separator = separator
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public var body: some TextNode {
|
||||
content.removingEmptys()
|
||||
.joined(separator: separator.render())
|
||||
style(.separator(.space()))
|
||||
}
|
||||
}
|
||||
|
||||
public extension HStack {
|
||||
|
||||
func style<S: HStackStyle>(_ style: S) -> some TextNode {
|
||||
style.render(content: .init(content: content))
|
||||
}
|
||||
|
||||
func separator(_ separator: Separator.Horizontal) -> some TextNode {
|
||||
style(.separator(separator))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Style
|
||||
|
||||
public protocol HStackStyle: TextModifier where Content == StackConfiguration {}
|
||||
|
||||
public extension HStackStyle where Self == HStackSeparatorStyle {
|
||||
|
||||
static func separator(_ separator: Separator.Horizontal) -> Self {
|
||||
HStackSeparatorStyle(separator: separator)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public struct HStackSeparatorStyle: HStackStyle {
|
||||
@usableFromInline
|
||||
let separator: Separator.Horizontal
|
||||
|
||||
@usableFromInline
|
||||
init(separator: Separator.Horizontal) {
|
||||
self.separator = separator
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public func render(content: StackConfiguration) -> some TextNode {
|
||||
AnySeparatableStackNode(content: content, separator: separator)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,11 @@ public struct LabeledContent<Label: TextNode, Content: TextNode>: TextNode {
|
||||
@usableFromInline
|
||||
let content: Content
|
||||
|
||||
/// Create a new labeled content text node.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - content: The content portion of the labeled content.
|
||||
/// - label: The label for the content.
|
||||
@inlinable
|
||||
public init(
|
||||
@TextBuilder _ content: () -> Content,
|
||||
@@ -55,14 +60,27 @@ public struct LabeledContentConfiguration {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Style
|
||||
|
||||
/// Represents a style for ``LabeledContent``.
|
||||
///
|
||||
///
|
||||
public protocol LabeledContentStyle: TextModifier where Content == LabeledContentConfiguration {}
|
||||
|
||||
public extension LabeledContentStyle where Self == HorizontalLabeledContentStyle {
|
||||
|
||||
/// The default labeled content style, which places the label
|
||||
/// and content inline with a space as a separator.
|
||||
///
|
||||
static var `default`: Self {
|
||||
horizontal()
|
||||
}
|
||||
|
||||
/// A horizontal labeled content style, which places the label
|
||||
/// and content inline with the given separator.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - separator: The horizontal separator to use.
|
||||
@inlinable
|
||||
static func horizontal(separator: Separator.Horizontal = .space()) -> Self {
|
||||
HorizontalLabeledContentStyle(separator: separator)
|
||||
@@ -70,13 +88,22 @@ public extension LabeledContentStyle where Self == HorizontalLabeledContentStyle
|
||||
}
|
||||
|
||||
public extension LabeledContentStyle where Self == VerticalLabeledContentStyle {
|
||||
|
||||
/// A vertical labeled content style, which places the label
|
||||
/// and content with the given vertical separator.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - separator: The vertical separator to use.
|
||||
@inlinable
|
||||
static func vertical(separator: Separator.Vertical = .newLine()) -> Self {
|
||||
VerticalLabeledContentStyle(separator: separator)
|
||||
}
|
||||
}
|
||||
|
||||
/// A labeled content style which places items inline based on a given
|
||||
/// horizontal separator.
|
||||
///
|
||||
/// - See Also: ``LabeledContentStyle/horizontal(separator:)``
|
||||
///
|
||||
public struct HorizontalLabeledContentStyle: LabeledContentStyle {
|
||||
|
||||
@usableFromInline
|
||||
@@ -88,13 +115,19 @@ public struct HorizontalLabeledContentStyle: LabeledContentStyle {
|
||||
}
|
||||
|
||||
public func render(content: LabeledContentConfiguration) -> some TextNode {
|
||||
HStack(separator: separator) {
|
||||
HStack {
|
||||
content.label
|
||||
content.content
|
||||
}
|
||||
.separator(separator)
|
||||
}
|
||||
}
|
||||
|
||||
/// A labeled content style which places items based on a given
|
||||
/// vertical separator.
|
||||
///
|
||||
/// - See Also: ``LabeledContentStyle/vertical(separator:)``
|
||||
///
|
||||
public struct VerticalLabeledContentStyle: LabeledContentStyle {
|
||||
|
||||
@usableFromInline
|
||||
@@ -106,9 +139,10 @@ public struct VerticalLabeledContentStyle: LabeledContentStyle {
|
||||
}
|
||||
|
||||
public func render(content: LabeledContentConfiguration) -> some TextNode {
|
||||
VStack(separator: separator) {
|
||||
VStack {
|
||||
content.label
|
||||
content.content
|
||||
}
|
||||
.separator(separator)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,10 +171,11 @@ public struct DefaultSectionStyle: SectionStyle {
|
||||
let separator: Separator.Vertical
|
||||
|
||||
public func render(content: SectionConfiguration) -> some TextNode {
|
||||
VStack(separator: separator) {
|
||||
VStack {
|
||||
content.header
|
||||
content.content
|
||||
content.footer
|
||||
}
|
||||
.style(.separator(separator))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,11 +39,11 @@ public enum Separator {
|
||||
public var body: some TextNode {
|
||||
switch self {
|
||||
case let .tab(count: count):
|
||||
seperator("\t", count: count)
|
||||
makeSeperator("\t", count: count)
|
||||
case let .space(count: count):
|
||||
seperator(" ", count: count)
|
||||
makeSeperator(" ", count: count)
|
||||
case let .custom(string, count: count):
|
||||
seperator(string, count: count)
|
||||
makeSeperator(string, count: count)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -85,15 +85,17 @@ public enum Separator {
|
||||
public var body: some TextNode {
|
||||
switch self {
|
||||
case let .newLine(count: count):
|
||||
seperator("\n", count: count)
|
||||
makeSeperator("\n", count: count)
|
||||
case let .custom(string, count: count):
|
||||
seperator(string, count: count)
|
||||
makeSeperator(string, count: count)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - Private Helpers.
|
||||
|
||||
@usableFromInline
|
||||
func ensuredCount(_ count: Int) -> Int {
|
||||
guard count >= 1 else { return 1 }
|
||||
@@ -101,7 +103,7 @@ func ensuredCount(_ count: Int) -> Int {
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
func seperator(_ separator: String, count: Int) -> some TextNode {
|
||||
func makeSeperator(_ separator: String, count: Int) -> some TextNode {
|
||||
let count = ensuredCount(count)
|
||||
|
||||
assert(count >= 1, "Invalid count while creating a separator")
|
||||
|
||||
29
Sources/CliDocCore/Nodes/StackConfiguration.swift
Normal file
29
Sources/CliDocCore/Nodes/StackConfiguration.swift
Normal file
@@ -0,0 +1,29 @@
|
||||
/// Represents the content of an ``HStack`` or a ``VStack``.
|
||||
///
|
||||
///
|
||||
public struct StackConfiguration {
|
||||
public let content: [any TextNode]
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
struct AnySeparatableStackNode<Separator: TextNode>: TextNode {
|
||||
|
||||
@usableFromInline
|
||||
let content: [any TextNode]
|
||||
|
||||
@usableFromInline
|
||||
let separator: Separator
|
||||
|
||||
@usableFromInline
|
||||
init(content: StackConfiguration, separator: Separator) {
|
||||
self.content = content.content
|
||||
self.separator = separator
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
var body: some TextNode {
|
||||
content.removingEmptys()
|
||||
.map { $0.render() }
|
||||
.joined(separator: separator.render())
|
||||
}
|
||||
}
|
||||
@@ -1,26 +1,57 @@
|
||||
/// A vertical stack of text nodes.
|
||||
///
|
||||
///
|
||||
|
||||
public struct VStack: TextNode {
|
||||
@usableFromInline
|
||||
let content: [any TextNode]
|
||||
|
||||
@usableFromInline
|
||||
let separator: Separator.Vertical
|
||||
|
||||
@inlinable
|
||||
public init(
|
||||
separator: Separator.Vertical = .newLine(count: 1),
|
||||
@TextBuilder content: () -> any TextNode
|
||||
) {
|
||||
self.content = array(from: content())
|
||||
self.separator = separator
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public var body: some TextNode {
|
||||
content.removingEmptys()
|
||||
.joined(separator: separator.render())
|
||||
style(.separator(.newLine(count: 1)))
|
||||
}
|
||||
}
|
||||
|
||||
public extension VStack {
|
||||
|
||||
func style<S: VStackStyle>(_ style: S) -> some TextNode {
|
||||
style.render(content: .init(content: content.removingEmptys()))
|
||||
}
|
||||
|
||||
func separator(_ separator: Separator.Vertical) -> some TextNode {
|
||||
style(.separator(separator))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Style
|
||||
|
||||
public protocol VStackStyle: TextModifier where Content == StackConfiguration {}
|
||||
|
||||
public extension VStackStyle where Self == VStackSeparatorStyle {
|
||||
|
||||
static func separator(_ separator: Separator.Vertical) -> Self {
|
||||
VStackSeparatorStyle(separator: separator)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public struct VStackSeparatorStyle: VStackStyle {
|
||||
@usableFromInline
|
||||
let separator: Separator.Vertical
|
||||
|
||||
@usableFromInline
|
||||
init(separator: Separator.Vertical) {
|
||||
self.separator = separator
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public func render(content: StackConfiguration) -> some TextNode {
|
||||
AnySeparatableStackNode(content: content, separator: separator)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user