Skip to content
Open
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
93 changes: 46 additions & 47 deletions cli/Sources/Noora/Components/TextPrompt.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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] = []) {
Expand Down
Loading