Skip to content

Commit 91fb75a

Browse files
committed
Move the terminator call in parseValue to the popNext*() functions
1 parent deffc73 commit 91fb75a

2 files changed

Lines changed: 54 additions & 31 deletions

File tree

Sources/ArgumentParser/Parsing/ArgumentSet.swift

Lines changed: 5 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -290,25 +290,6 @@ struct LenientParser {
290290
}
291291
}
292292

293-
/// Helper function to check if there's a terminator between the current option and target origin.
294-
private func hasTerminatorBetween(
295-
_ originElement: InputOrigin.Element,
296-
_ targetOrigin: InputOrigin.Element
297-
) -> Bool {
298-
guard case .argumentIndex(let currentIndex) = originElement,
299-
case .argumentIndex(let targetIndex) = targetOrigin
300-
else { return false }
301-
302-
// Check if there's a terminator between current position and target position
303-
let terminatorIndex = inputArguments.elements.firstIndex { element in
304-
element.isTerminator
305-
&& element.index.inputIndex > currentIndex.inputIndex
306-
&& element.index.inputIndex < targetIndex.inputIndex
307-
}
308-
309-
return terminatorIndex != nil
310-
}
311-
312293
mutating func parseValue(
313294
_ argument: ArgumentDefinition,
314295
_ parsed: ParsedArgument,
@@ -334,11 +315,10 @@ struct LenientParser {
334315
try update(origins, parsed.name, String(value), &result)
335316
usedOrigins.formUnion(origins)
336317
} else if let (origin2, value) = inputArguments.popNextElementIfValue(
337-
after: originElement),
338-
!hasTerminatorBetween(originElement, origin2)
318+
after: originElement)
339319
{
340320
// Use `popNextElementIfValue(after:)` to handle cases where short option
341-
// labels are combined, but only if there's no terminator between them
321+
// labels are combined
342322
let origins = origin.inserting(origin2)
343323
try update(origins, parsed.name, value, &result)
344324
usedOrigins.formUnion(origins)
@@ -361,11 +341,10 @@ struct LenientParser {
361341
try update(origins, parsed.name, String(value), &result)
362342
usedOrigins.formUnion(origins)
363343
} else if let (origin2, value) = inputArguments.popNextValue(
364-
after: originElement),
365-
!hasTerminatorBetween(originElement, origin2)
344+
after: originElement)
366345
{
367346
// Use `popNext(after:)` to handle cases where short option
368-
// labels are combined, but only if there's no terminator between them
347+
// labels are combined
369348
let origins = origin.inserting(origin2)
370349
try update(origins, parsed.name, value, &result)
371350
usedOrigins.formUnion(origins)
@@ -388,8 +367,7 @@ struct LenientParser {
388367
try update(origins, parsed.name, String(value), &result)
389368
usedOrigins.formUnion(origins)
390369
} else if let (origin2, value) = inputArguments.popNextElementAsValue(
391-
after: originElement),
392-
!hasTerminatorBetween(originElement, origin2)
370+
after: originElement)
393371
{
394372
// Only consume if there's no terminator between option and value
395373
let origins = origin.inserting(origin2)

Sources/ArgumentParser/Parsing/SplitArguments.swift

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,7 @@ extension SplitArguments {
336336
/// value for `-f`, or `--foo name` where `name` is the value for `--foo`.
337337
/// If `--foo` expects a value, an input of `--foo --bar name` will return
338338
/// `nil`, since the option `--bar` comes before the value `name`.
339+
/// Also returns `nil` if there's a terminator between the origin and the target value.
339340
mutating func popNextElementIfValue(after origin: InputOrigin.Element) -> (
340341
InputOrigin.Element, String
341342
)? {
@@ -353,26 +354,61 @@ extension SplitArguments {
353354
guard case .value(let value) = elements[elementIndex].value
354355
else { return nil }
355356

356-
defer { remove(at: elementIndex) }
357357
let matchedArgumentIndex = elements[elementIndex].index
358-
return (.argumentIndex(matchedArgumentIndex), value)
358+
let targetOrigin = InputOrigin.Element.argumentIndex(matchedArgumentIndex)
359+
360+
// Check if there's a terminator between the origin and target
361+
if hasTerminatorBetween(origin, targetOrigin) {
362+
return nil
363+
}
364+
365+
defer { remove(at: elementIndex) }
366+
return (targetOrigin, value)
367+
}
368+
369+
/// Helper function to check if there's a terminator between two origins.
370+
private func hasTerminatorBetween(
371+
_ originElement: InputOrigin.Element,
372+
_ targetOrigin: InputOrigin.Element
373+
) -> Bool {
374+
guard case .argumentIndex(let currentIndex) = originElement,
375+
case .argumentIndex(let targetIndex) = targetOrigin
376+
else { return false }
377+
378+
// Check if there's a terminator between current position and target position
379+
let terminatorIndex = elements.firstIndex { element in
380+
element.isTerminator
381+
&& element.index.inputIndex > currentIndex.inputIndex
382+
&& element.index.inputIndex < targetIndex.inputIndex
383+
}
384+
385+
return terminatorIndex != nil
359386
}
360387

361388
/// Pops the next `.value` after the given index.
362389
///
363390
/// This is used to get the next value in `-f -b name` where `name` is the value of `-f`.
391+
/// Also returns `nil` if there's a terminator between the origin and the target value.
364392
mutating func popNextValue(
365393
after origin: InputOrigin.Element
366394
) -> (InputOrigin.Element, String)? {
367395
guard let start = position(after: origin) else { return nil }
368396
guard let resultIndex = elements[start...].firstIndex(where: { $0.isValue })
369397
else { return nil }
370398

399+
let targetOrigin = InputOrigin.Element.argumentIndex(
400+
elements[resultIndex].index)
401+
402+
// Check if there's a terminator between the origin and target
403+
if hasTerminatorBetween(origin, targetOrigin) {
404+
return nil
405+
}
406+
371407
defer { remove(at: resultIndex) }
372408
// swift-format-ignore: NeverForceUnwrap
373409
// This is safe because we know `resultIndex` is refers to a value
374410
return (
375-
.argumentIndex(elements[resultIndex].index),
411+
targetOrigin,
376412
elements[resultIndex].value.valueString!
377413
)
378414
}
@@ -384,6 +420,7 @@ extension SplitArguments {
384420
///
385421
/// For an input such as `--a --b foo`, if passed the origin of `--a`,
386422
/// this will first pop the value `--b`, then the value `foo`.
423+
/// Also returns `nil` if there's a terminator between the origin and the target element.
387424
mutating func popNextElementAsValue(after origin: InputOrigin.Element) -> (
388425
InputOrigin.Element, String
389426
)? {
@@ -395,11 +432,19 @@ extension SplitArguments {
395432
$0.index.subIndex == .complete
396433
})?.index
397434
else { return nil }
435+
436+
let targetOrigin = InputOrigin.Element.argumentIndex(nextIndex)
437+
438+
// Check if there's a terminator between the origin and target
439+
if hasTerminatorBetween(origin, targetOrigin) {
440+
return nil
441+
}
442+
398443
// Remove all elements with this `InputIndex`:
399444
remove(at: nextIndex)
400445
// Return the original input
401446
return (
402-
.argumentIndex(nextIndex), originalInput[nextIndex.inputIndex.rawValue]
447+
targetOrigin, originalInput[nextIndex.inputIndex.rawValue]
403448
)
404449
}
405450

0 commit comments

Comments
 (0)