CommandLineToolkit
174 строки · 5.2 Кб
1extension String {
2/// Converts this `String` to `ConsoleText`.
3///
4/// console.output("Hello, " + "world!".consoleText(color: .green))
5///
6/// See `ConsoleStyle` for more information.
7func consoleText(_ style: ConsoleStyle = .plain) -> ConsoleText {
8return [ConsoleTextFragment(string: self, style: style)]
9}
10
11/// Converts this `String` to `ConsoleText`.
12///
13/// console.output("Hello, " + "world!".consoleText(color: .green))
14///
15/// See `ConsoleStyle` for more information.
16func consoleText(color: ConsoleColor? = nil, background: ConsoleColor? = nil, attributes: [ConsoleAttribute] = []) -> ConsoleText {
17let style = ConsoleStyle(color: color, background: background, attributes: attributes)
18return consoleText(style)
19}
20}
21
22/// A collection of `ConsoleTextFragment`s. Represents stylized text that can be outputted
23/// to a `Console`.
24///
25/// let text: ConsoleText = "Hello, " + "world".consoleText(color: .green)
26///
27struct ConsoleText: RandomAccessCollection, ExpressibleByArrayLiteral, ExpressibleByStringLiteral, CustomStringConvertible, Equatable {
28/// See `Collection`.
29var startIndex: Int {
30return fragments.startIndex
31}
32
33/// See `Collection`.
34var endIndex: Int {
35return fragments.endIndex
36}
37
38/// See `Collection`.
39func index(after i: Int) -> Int {
40return i + 1
41}
42
43/// See `CustomStringConvertible`.
44var description: String {
45return fragments.map { $0.string }.joined()
46}
47
48/// See `ExpressibleByArrayLiteral`.
49init(arrayLiteral elements: ConsoleTextFragment...) {
50self.fragments = elements
51}
52
53/// See `ExpressibleByStringLiteral`.
54init(stringLiteral string: String) {
55if string.isEmpty {
56self.fragments = []
57} else {
58self.fragments = [.init(string: string)]
59}
60}
61
62/// One or more `ConsoleTextFragment`s making up this `ConsoleText.
63var fragments: [ConsoleTextFragment]
64
65/// Creates a new `ConsoleText`.
66init(fragments: [ConsoleTextFragment]) {
67self.fragments = fragments
68}
69
70/// See `Collection`.
71subscript(position: Int) -> ConsoleTextFragment {
72return fragments[position]
73}
74
75/// `\n` character with plain styling.
76static let newLine: ConsoleText = "\n"
77}
78
79extension ConsoleText {
80/// Trims text to specific length
81/// - Parameter length: line length
82/// - Returns: trimmed text
83func trimmed(to length: Int) -> Self {
84var remainingLength = length
85let newFragments: [ConsoleTextFragment] = fragments.compactMap { fragment in
86if fragment.string.count > remainingLength {
87if remainingLength > 0 {
88let stringPart = String(fragment.string.prefix(remainingLength - 1))
89remainingLength = 0
90return .init(string: stringPart + "…", style: fragment.style)
91} else {
92return nil
93}
94} else {
95remainingLength -= fragment.string.count
96return fragment
97}
98}
99
100return .init(fragments: newFragments)
101}
102}
103
104// MARK: Operators
105
106extension ConsoleText {
107/// Appends a `ConsoleText` to another `ConsoleText`.
108///
109/// let text: ConsoleText = "Hello, " + "world!"
110///
111static func +(lhs: ConsoleText, rhs: ConsoleText) -> ConsoleText {
112return ConsoleText(fragments: lhs.fragments + rhs.fragments)
113}
114
115/// Appends a `ConsoleText` to another `ConsoleText` in-place.
116///
117/// var text: ConsoleText = "Hello, "
118/// text += "world!"
119///
120static func +=(lhs: inout ConsoleText, rhs: ConsoleText) {
121lhs = lhs + rhs
122}
123}
124
125extension ConsoleText: ExpressibleByStringInterpolation {
126init(stringInterpolation: StringInterpolation) {
127self.fragments = stringInterpolation.fragments
128}
129
130struct StringInterpolation: StringInterpolationProtocol {
131var fragments: [ConsoleTextFragment]
132
133init(literalCapacity: Int, interpolationCount: Int) {
134self.fragments = []
135self.fragments.reserveCapacity(literalCapacity)
136}
137
138mutating func appendLiteral(_ literal: String) {
139self.fragments.append(.init(string: literal))
140}
141
142mutating func appendInterpolation(
143_ value: String,
144style: ConsoleStyle = .plain
145) {
146self.fragments.append(.init(string: value, style: style))
147}
148
149mutating func appendInterpolation(
150_ value: String,
151color: ConsoleColor?,
152background: ConsoleColor? = nil,
153attributes: [ConsoleAttribute] = []
154) {
155self.fragments.append(.init(string: value, style: .init(
156color: color,
157background: background,
158attributes: attributes
159)))
160}
161
162mutating func appendInterpolation(
163_ value: ConsoleTextFragment
164) {
165self.fragments.append(value)
166}
167
168mutating func appendInterpolation(
169_ value: ConsoleText
170) {
171self.fragments.append(contentsOf: value.fragments)
172}
173}
174}
175