feat: Working on node builder
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
|
||||
public struct Group: NodeRepresentable {
|
||||
@usableFromInline
|
||||
let node: any NodeRepresentable
|
||||
@@ -25,10 +24,10 @@ public struct Group: NodeRepresentable {
|
||||
|
||||
@inlinable
|
||||
public init(
|
||||
separator: AnyNode = Space().eraseToAnyNode(),
|
||||
separator: String = " ",
|
||||
@NodeBuilder _ build: () -> any NodeRepresentable
|
||||
) {
|
||||
self.init(separator: separator, node: build())
|
||||
self.init(separator: separator.eraseToAnyNode(), node: build())
|
||||
}
|
||||
|
||||
@inlinable
|
||||
|
||||
@@ -1,32 +1,45 @@
|
||||
@preconcurrency import Rainbow
|
||||
|
||||
public struct Header: NodeRepresentable {
|
||||
public struct Header: NodeRepresentable, @unchecked Sendable {
|
||||
|
||||
@usableFromInline
|
||||
let node: Text
|
||||
let node: any NodeRepresentable
|
||||
|
||||
@usableFromInline
|
||||
let styles: [Style]
|
||||
let color: NamedColor?
|
||||
|
||||
@usableFromInline
|
||||
let styles: [Style]?
|
||||
|
||||
@inlinable
|
||||
public init(
|
||||
@NodeBuilder _ build: () -> any NodeRepresentable
|
||||
) {
|
||||
self.node = build()
|
||||
self.color = nil
|
||||
self.styles = nil
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public init(
|
||||
_ text: String,
|
||||
color: NamedColor? = .yellow,
|
||||
styles: [Style] = [.bold]
|
||||
styles: [Style]? = [.bold]
|
||||
) {
|
||||
var text = Text(text)
|
||||
if let color {
|
||||
text = text.color(color)
|
||||
}
|
||||
self.node = text
|
||||
self.color = color
|
||||
self.node = Text(text)
|
||||
self.styles = styles
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public func render() -> String {
|
||||
styles.reduce(node) { node, style in
|
||||
node.style(style)
|
||||
var node = node
|
||||
if let color {
|
||||
node = node.color(color)
|
||||
}
|
||||
.render()
|
||||
if let styles {
|
||||
node = node.style(styles)
|
||||
}
|
||||
return node.render()
|
||||
}
|
||||
}
|
||||
|
||||
46
Sources/CliDoc/Nodes/LabeledContent.swift
Normal file
46
Sources/CliDoc/Nodes/LabeledContent.swift
Normal file
@@ -0,0 +1,46 @@
|
||||
public struct LabeledContent: NodeRepresentable {
|
||||
|
||||
@usableFromInline
|
||||
let label: any NodeRepresentable
|
||||
|
||||
@usableFromInline
|
||||
let content: any NodeRepresentable
|
||||
|
||||
@usableFromInline
|
||||
let separator: any NodeRepresentable
|
||||
|
||||
@inlinable
|
||||
public init(
|
||||
separator: (any NodeRepresentable)? = nil,
|
||||
@NodeBuilder label: () -> any NodeRepresentable,
|
||||
@NodeBuilder content: () -> any NodeRepresentable
|
||||
) {
|
||||
self.separator = separator ?? "\n"
|
||||
self.label = label()
|
||||
self.content = content()
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public func render() -> String {
|
||||
Group(separator: separator) {
|
||||
label
|
||||
content
|
||||
}.render()
|
||||
}
|
||||
}
|
||||
|
||||
public extension LabeledContent {
|
||||
|
||||
@inlinable
|
||||
init(
|
||||
_ label: String,
|
||||
separator: (any NodeRepresentable)? = nil,
|
||||
@NodeBuilder content: () -> any NodeRepresentable
|
||||
) {
|
||||
self.init(separator: separator) {
|
||||
Text(label)
|
||||
} content: {
|
||||
content()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
public extension NodeRepresentable {
|
||||
@inlinable
|
||||
func repeating(_ count: Int) -> some NodeRepresentable {
|
||||
RepeatingNode(self, count: count)
|
||||
}
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
struct RepeatingNode: NodeRepresentable {
|
||||
|
||||
@usableFromInline
|
||||
let node: any NodeRepresentable
|
||||
|
||||
@usableFromInline
|
||||
let count: Int
|
||||
|
||||
@usableFromInline
|
||||
init(_ node: any NodeRepresentable, count: Int) {
|
||||
self.node = node
|
||||
self.count = count
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
func render() -> String {
|
||||
var string = node.render()
|
||||
guard count > 0 else { return string }
|
||||
var count = count - 1
|
||||
while count > 0 {
|
||||
string = "\(string)\(node.render())"
|
||||
count -= 1
|
||||
}
|
||||
return string
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
// TODO: Remove init(header:...) initializers.
|
||||
public struct Section: NodeRepresentable {
|
||||
@usableFromInline
|
||||
let node: any NodeRepresentable
|
||||
|
||||
@inlinable
|
||||
public init<N: NodeRepresentable>(@NodeBuilder _ build: () -> N) {
|
||||
self.node = build()
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public func render() -> String {
|
||||
node.render()
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public init<Content: NodeRepresentable, Separator: NodeRepresentable>(
|
||||
header: String,
|
||||
separator: Separator,
|
||||
@NodeBuilder content: () -> Content
|
||||
) {
|
||||
self.init {
|
||||
Text(header)
|
||||
separator
|
||||
content()
|
||||
}
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public init<Content: NodeRepresentable>(
|
||||
header: String,
|
||||
inline: Bool = false,
|
||||
@NodeBuilder content: () -> Content
|
||||
) {
|
||||
self.init(
|
||||
header: header,
|
||||
separator: inline ? Space().eraseToAnyNode() : NewLine().eraseToAnyNode(),
|
||||
content: content
|
||||
)
|
||||
}
|
||||
}
|
||||
6
Sources/CliDoc/Nodes/String+NodeRepresentable.swift
Normal file
6
Sources/CliDoc/Nodes/String+NodeRepresentable.swift
Normal file
@@ -0,0 +1,6 @@
|
||||
extension String: NodeRepresentable {
|
||||
@inlinable
|
||||
public func render() -> String {
|
||||
self
|
||||
}
|
||||
}
|
||||
@@ -13,43 +13,6 @@ public struct Text: NodeRepresentable {
|
||||
public func render() -> String {
|
||||
text
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public func color(_ color: NamedColor) -> Self {
|
||||
.init(text.applyingColor(color))
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public func style(_ style: Style) -> Self {
|
||||
.init(text.applyingStyle(style))
|
||||
}
|
||||
}
|
||||
|
||||
public struct NewLine: NodeRepresentable {
|
||||
@usableFromInline
|
||||
let node: any NodeRepresentable
|
||||
|
||||
@inlinable
|
||||
public init(count: Int = 1) {
|
||||
self.node = Text("\n").repeating(count)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public func render() -> String {
|
||||
node.render()
|
||||
}
|
||||
}
|
||||
|
||||
public struct Space: NodeRepresentable {
|
||||
let node: any NodeRepresentable
|
||||
|
||||
public init(count: Int = 1) {
|
||||
self.node = Text(" ").repeating(count)
|
||||
}
|
||||
|
||||
public func render() -> String {
|
||||
node.render()
|
||||
}
|
||||
}
|
||||
|
||||
public extension NodeRepresentable {
|
||||
@@ -58,12 +21,12 @@ public extension NodeRepresentable {
|
||||
Text("")
|
||||
}
|
||||
|
||||
static func newLine(count: Int = 1) -> Self where Self == NewLine {
|
||||
NewLine(count: count)
|
||||
static func newLine(count: Int = 1) -> Self where Self == AnyNode {
|
||||
"\n".repeating(count).eraseToAnyNode()
|
||||
}
|
||||
|
||||
static func space(count: Int = 1) -> Self where Self == Space {
|
||||
Space(count: count)
|
||||
static func space(count: Int = 1) -> Self where Self == AnyNode {
|
||||
" ".repeating(count).eraseToAnyNode()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user