diff --git a/cli/Sources/Noora/Components/TextPrompt.swift b/cli/Sources/Noora/Components/TextPrompt.swift index 443ec4d9..7078c537 100644 --- a/cli/Sources/Noora/Components/TextPrompt.swift +++ b/cli/Sources/Noora/Components/TextPrompt.swift @@ -18,16 +18,10 @@ struct TextPrompt { let validator: InputValidating func run() -> String { - run(errors: []) - } - - private func run(errors: [ValidatableError] = []) -> String { if !terminal.isInteractive { fatalError("'\(prompt)' can't be prompted in a non-interactive session.") } - var input = "" - func isReturn(_ character: Character) -> Bool { #if os(Windows) return character.unicodeScalars.first?.value == 10 || character.unicodeScalars.first?.value == 13 @@ -36,55 +30,60 @@ struct TextPrompt { #endif } - terminal.withoutCursor { - render(input: input, errors: errors) - while let character = terminal.readCharacter(), !isReturn(character) { - #if os(Windows) - // Handle Ctrl+C (character code 3) - // On Windows, Ctrl+C generates character code 3 - // while "getch" is running it doesn't emit a signal - if character.unicodeScalars.first?.value == 3 { - exit(0) + var currentErrors: [ValidatableError] = [] + + while true { + var input = "" + + terminal.withoutCursor { + render(input: input, errors: currentErrors) + while let character = terminal.readCharacter(), !isReturn(character) { + #if os(Windows) + // Handle Ctrl+C (character code 3) + // On Windows, Ctrl+C generates character code 3 + // while "getch" is running it doesn't emit a signal + if character.unicodeScalars.first?.value == 3 { + exit(0) + } + + let isBackspace = character.unicodeScalars.first?.value == 8 || + character.unicodeScalars.first?.value == 127 + #else + let isBackspace = character == "\u{08}" || character == "\u{7F}" + #endif + if isBackspace { // Handle Backspace (Delete Last Character) + if !input.isEmpty { + input.removeLast() // Remove last character from input + } + } else { + input.append(character) } - - let isBackspace = character.unicodeScalars.first?.value == 8 || character.unicodeScalars.first?.value == 127 - #else - let isBackspace = character == "\u{08}" || character == "\u{7F}" - #endif - if isBackspace { // Handle Backspace (Delete Last Character) - if !input.isEmpty { - input.removeLast() // Remove last character from input - } - } else { - input.append(character) + render(input: input, errors: currentErrors) } - render(input: input) } - } - - logger?.debug("Prompted '\(prompt.plain())'") - let resolvedInput: String - if input.isEmpty, let defaultValue { - resolvedInput = defaultValue - } else { - resolvedInput = input - } + logger?.debug("Prompted '\(prompt.plain())'") - let validationResult = validator.validate(input: resolvedInput, rules: validationRules) - - switch validationResult { - case .success: - render(input: input, withCursor: false) - case let .failure(error): - return run(errors: error.errors) - } + let resolvedInput: String - renderResult(input: resolvedInput) + if input.isEmpty, let defaultValue { + resolvedInput = defaultValue + } else { + resolvedInput = input + } - logger?.debug("Responded \(resolvedInput) to prompt '\(prompt.plain())'") + let validationResult = validator.validate(input: resolvedInput, rules: validationRules) - return resolvedInput + switch validationResult { + case .success: + render(input: input, withCursor: false) + renderResult(input: resolvedInput) + logger?.debug("Responded \(resolvedInput) to prompt '\(prompt.plain())'") + return resolvedInput + case let .failure(error): + currentErrors = error.errors + } + } } private func render(input: String, withCursor: Bool = true, errors: [ValidatableError] = []) {