in internal/engine/interactive/interactive.go [108:259]
func handleUserInput(
model InteractiveModeModel,
message tea.KeyMsg,
) (InteractiveModeModel, []tea.Cmd) {
var commands []tea.Cmd
// If we're recording input for a multi-char command,
if model.recordingInput {
isNumber := lib.IsNumber(message.String())
// If the input is a number, append it to the recorded input.
if message.Type == tea.KeyRunes && isNumber {
model.recordedInput += message.String()
return model, commands
}
// If the input is not a number, we'll stop recording input and reset
// the commands remaining to the recorded input.
if message.Type == tea.KeyEnter || !isNumber {
commandsRemaining, _ := strconv.Atoi(model.recordedInput)
if commandsRemaining > len(model.codeBlockState)-model.currentCodeBlock {
commandsRemaining = len(model.codeBlockState) - model.currentCodeBlock
}
logging.GlobalLogger.Debugf("Will execute the next %d steps", commandsRemaining)
model.stepsToBeExecuted = commandsRemaining
commands = append(commands, func() tea.Msg {
return tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'e'}}
})
model.recordingInput = false
model.recordedInput = ""
logging.GlobalLogger.Debugf(
"Recording input stopped and previously recorded input cleared.",
)
return model, commands
}
}
switch {
case key.Matches(message, model.commands.execute):
if model.executingCommand {
logging.GlobalLogger.Info("Command is already executing, ignoring execute command")
break
}
// Prevent the user from executing a command if the previous command has
// not been executed successfully or executed at all.
previousCodeBlock := model.currentCodeBlock - 1
if previousCodeBlock >= 0 {
previousCodeBlockState := model.codeBlockState[previousCodeBlock]
if !previousCodeBlockState.Success {
logging.GlobalLogger.Info(
"Previous command has not been executed successfully, ignoring execute command",
)
break
}
}
// Prevent the user from executing a command if the current command has
// already been executed successfully.
codeBlockState := model.codeBlockState[model.currentCodeBlock]
if codeBlockState.Success {
logging.GlobalLogger.Info(
"Command has already been executed successfully, ignoring execute command",
)
break
}
codeBlock := codeBlockState.CodeBlock
model.executingCommand = true
// If we're on the last step and the command is an SSH command, we need
// to report the status before executing the command. This is needed for
// one click deployments and does not affect the normal execution flow.
if model.currentCodeBlock == len(model.codeBlockState)-1 &&
patterns.SshCommand.MatchString(codeBlock.Content) {
model.azureStatus.Status = "Succeeded"
environments.AttachResourceURIsToAzureStatus(
&model.azureStatus,
model.resourceGroupName,
model.environment,
)
environmentVariables, err := lib.LoadEnvironmentStateFile(
lib.DefaultEnvironmentStateFile,
)
if err != nil {
logging.GlobalLogger.Errorf("Failed to load environment state file: %s", err)
model.azureStatus.SetError(err)
}
model.azureStatus.ConfigureMarkdownForDownload(
model.markdownSource,
environmentVariables,
model.environment,
)
commands = append(commands, tea.Sequence(
common.UpdateAzureStatus(model.azureStatus, model.environment),
func() tea.Msg {
return common.ExecuteCodeBlockSync(codeBlock, lib.CopyMap(model.env))
}))
} else {
commands = append(commands, common.ExecuteCodeBlockAsync(
codeBlock,
lib.CopyMap(model.env),
))
}
case key.Matches(message, model.commands.previous):
if model.executingCommand {
logging.GlobalLogger.Info("Command is already executing, ignoring execute command")
break
}
if model.currentCodeBlock > 0 {
model.currentCodeBlock--
}
case key.Matches(message, model.commands.next):
if model.executingCommand {
logging.GlobalLogger.Info("Command is already executing, ignoring execute command")
break
}
if model.currentCodeBlock < len(model.codeBlockState)-1 {
model.currentCodeBlock++
}
case key.Matches(message, model.commands.quit):
commands = append(commands, tea.Quit)
case key.Matches(message, model.commands.executeAll):
model.stepsToBeExecuted = len(model.codeBlockState) - model.currentCodeBlock
commands = append(
commands,
func() tea.Msg {
return tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'e'}}
},
)
case key.Matches(message, model.commands.executeMany):
model.recordingInput = true
case key.Matches(message, model.commands.pause):
if !model.executingCommand {
logging.GlobalLogger.Info("No command is currently executing, ignoring pause command")
}
model.stepsToBeExecuted = 0
}
return model, commands
}