Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions Sources/TextBuilder/TextBuilder.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,24 @@
import SwiftUI

/// A custom attribute that constructs combined text views.
///
/// You can use ``TextBuilder`` as an attribute for text-producing properties
/// or function parameters, allowing them to provide combined text views. For example,
/// the following `loremIpsum` property will create a single styled text view with each
/// text separated using eggplant emoji.
///
/// struct EggplantSeparator: TextBuilderSeparator {
/// static var separator: String { " 🍆 " }
/// }
///
/// @TextBuilder<EggplantSeparator>
/// var loremIpsum: Text {
/// Text("Lorem").underline().foregroundColor(.blue)
/// Text("ipsum dolor")
/// Text("sit").bold()
/// Text("amet, consectetur")
/// }
///
@resultBuilder
public struct TextBuilder<Separator: TextBuilderSeparator> {
public static func buildArray(_ texts: [[Text]]) -> [Text] {
Expand Down
27 changes: 27 additions & 0 deletions Sources/TextBuilder/TextExtensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,19 @@ extension StringProtocol {
}

extension Sequence where Element == Text {

/// Returns a new `Text` by concatenating the elements of the sequence,
///
/// The following example shows how an array of `Text` views can be joined to a
/// single `Text` view with comma-separated string:
///
/// let cast = [Text("Vivien"), Text("Marlon"), Text("Kim")]
/// let list = cast.joined(separator: Text(", "))
/// // Gives Text("Vivien, Marlon, Kim")
///
/// - Parameter separator: A `Text` view to insert between each of the elements
/// in this sequence. By default there is no separator.
/// - Returns: A single, concatenated `Text` view.
public func joined(separator: Text = Text("")) -> Text {
var isInitial = true
return reduce(Text("")) { (result, text) in
Expand All @@ -18,10 +31,24 @@ extension Sequence where Element == Text {
}

extension Text {

/// Creates a combined text view based on the given `content` by inserting
/// `separator` text views between each received text component.
///
/// - Parameters:
/// - separator: The text to use as a separator between received text components.
/// By default there is no separator.
/// - content: A text builder that creates text components.
public init(separator: Text = Text(""), @BasicTextBuilder content: () -> [Text]) {
self = content().joined(separator: separator)
}

/// Creates a combined text view based on the given `content` by inserting
/// `separator` string between each received text component.
///
/// - Parameters:
/// - separator: The string to use as a separator between received text components.
/// - content: A text builder that creates text components.
public init<Separator: StringProtocol>(separator: Separator, @BasicTextBuilder content: () -> [Text]) {
self.init(separator: Text(separator), content: content)
}
Expand Down