|
11 | 11 |
|
12 | 12 | #if compiler(>=6.0) |
13 | 13 | internal import ArgumentParserToolInfo |
14 | | -internal import Foundation |
15 | 14 | #else |
16 | 15 | import ArgumentParserToolInfo |
17 | | -import Foundation |
18 | 16 | #endif |
19 | 17 |
|
20 | 18 | /// A shell for which the parser can generate a completion script. |
21 | | -public struct CompletionShell: RawRepresentable, Hashable, CaseIterable { |
| 19 | +public struct CompletionShell: RawRepresentable, Hashable, CaseIterable, |
| 20 | + Sendable |
| 21 | +{ |
22 | 22 | public var rawValue: String |
23 | 23 |
|
24 | 24 | /// Creates a new instance from the given string. |
@@ -87,20 +87,6 @@ public struct CompletionShell: RawRepresentable, Hashable, CaseIterable { |
87 | 87 | Self._requestingVersion.withLock { $0 } |
88 | 88 | } |
89 | 89 |
|
90 | | - /// The name of the environment variable whose value is the name of the shell |
91 | | - /// for which completions are being requested from a custom completion |
92 | | - /// handler. |
93 | | - /// |
94 | | - /// The environment variable is set in generated completion scripts. |
95 | | - static let shellEnvironmentVariableName = "SAP_SHELL" |
96 | | - |
97 | | - /// The name of the environment variable whose value is the version of the |
98 | | - /// shell for which completions are being requested from a custom completion |
99 | | - /// handler. |
100 | | - /// |
101 | | - /// The environment variable is set in generated completion scripts. |
102 | | - static let shellVersionEnvironmentVariableName = "SAP_SHELL_VERSION" |
103 | | - |
104 | 90 | func format(completions: [String]) -> String { |
105 | 91 | var completions = completions |
106 | 92 | if self == .zsh { |
@@ -157,12 +143,82 @@ extension String { |
157 | 143 | func shellEscapeForSingleQuotedString(iterationCount: UInt64 = 1) -> Self { |
158 | 144 | iterationCount == 0 |
159 | 145 | ? self |
160 | | - : replacingOccurrences(of: "'", with: "'\\''") |
| 146 | + : self |
| 147 | + .replacing("'", with: "'\\''") |
161 | 148 | .shellEscapeForSingleQuotedString(iterationCount: iterationCount - 1) |
162 | 149 | } |
163 | 150 |
|
164 | 151 | func shellEscapeForVariableName() -> Self { |
165 | | - replacingOccurrences(of: "-", with: "_") |
| 152 | + self.replacing("-", with: "_") |
| 153 | + } |
| 154 | + |
| 155 | + func replacing(_ old: Self, with new: Self) -> Self { |
| 156 | + guard !old.isEmpty else { return self } |
| 157 | + |
| 158 | + var result = "" |
| 159 | + var startIndex = self.startIndex |
| 160 | + |
| 161 | + // Look for occurrences of the old string. |
| 162 | + while let matchRange = self.firstMatch(of: old, at: startIndex) { |
| 163 | + // Add the substring before the match. |
| 164 | + result.append(contentsOf: self[startIndex..<matchRange.start]) |
| 165 | + |
| 166 | + // Add the replacement string. |
| 167 | + result.append(contentsOf: new) |
| 168 | + |
| 169 | + // Move past the matched portion. |
| 170 | + startIndex = matchRange.end |
| 171 | + } |
| 172 | + |
| 173 | + // No more matches found, add the rest of the string. |
| 174 | + result.append(contentsOf: self[startIndex..<self.endIndex]) |
| 175 | + |
| 176 | + return result |
| 177 | + } |
| 178 | + |
| 179 | + func firstMatch( |
| 180 | + of match: Self, |
| 181 | + at startIndex: Self.Index |
| 182 | + ) -> (start: Self.Index, end: Self.Index)? { |
| 183 | + guard !match.isEmpty else { return nil } |
| 184 | + guard match.count <= self.count else { return nil } |
| 185 | + |
| 186 | + var startIndex = startIndex |
| 187 | + while startIndex < self.endIndex { |
| 188 | + // Check if theres a match. |
| 189 | + if let endIndex = self.matches(match, at: startIndex) { |
| 190 | + // Return the match. |
| 191 | + return (startIndex, endIndex) |
| 192 | + } |
| 193 | + |
| 194 | + // Move to the next of index. |
| 195 | + self.formIndex(after: &startIndex) |
| 196 | + } |
| 197 | + |
| 198 | + return nil |
| 199 | + } |
| 200 | + |
| 201 | + func matches( |
| 202 | + _ match: Self, |
| 203 | + at startIndex: Self.Index |
| 204 | + ) -> Self.Index? { |
| 205 | + var selfIndex = startIndex |
| 206 | + var matchIndex = match.startIndex |
| 207 | + |
| 208 | + while true { |
| 209 | + // Only continue checking if there is more match to check |
| 210 | + guard matchIndex < match.endIndex else { return selfIndex } |
| 211 | + |
| 212 | + // Exit early if there is no more "self" to check. |
| 213 | + guard selfIndex < self.endIndex else { return nil } |
| 214 | + |
| 215 | + // Check match and self are the the same. |
| 216 | + guard self[selfIndex] == match[matchIndex] else { return nil } |
| 217 | + |
| 218 | + // Move to the next pair of indices. |
| 219 | + self.formIndex(after: &selfIndex) |
| 220 | + match.formIndex(after: &matchIndex) |
| 221 | + } |
166 | 222 | } |
167 | 223 | } |
168 | 224 |
|
|
0 commit comments