// Copyright IBM Corp. 2014, 2026 // SPDX-License-Identifier: BUSL-1.1 //go:build !solaris // +build !solaris // The readline library we use doesn't currently support solaris so // we just build tag it off. package command import ( "fmt" "io" "os" "strings" "github.com/chzyer/readline" "github.com/hashicorp/cli" "github.com/hashicorp/terraform/internal/repl" ) func (c *ConsoleCommand) modeInteractive(session *repl.Session, ui cli.Ui) int { // Configure input l, err := readline.NewEx(&readline.Config{ Prompt: "> ", InterruptPrompt: "^C", EOFPrompt: "exit", HistorySearchFold: true, Stdin: os.Stdin, Stdout: os.Stdout, Stderr: os.Stderr, }) if err != nil { c.Ui.Error(fmt.Sprintf( "Error initializing console: %s", err)) return 1 } defer l.Close() // TODO: Currently we're handling multi-line input largely _in spite of_ // the readline library, because it doesn't support that. This means that // in particular the history treats each line as a separate history entry, // and doesn't allow editing of previous lines after the user's already // pressed enter. // // Hopefully we can do better than this one day, but having some basic // support for multi-line input is at least better than none at all: // this is mainly helpful when pasting in expressions from elsewhere that // already have newline characters in them, to avoid pre-editing it. lines := make([]string, 0, 4) for { // Read a line if len(lines) == 0 { l.SetPrompt("> ") } else { l.SetPrompt(": ") } line, err := l.Readline() if err == readline.ErrInterrupt { if len(lines) == 0 && line == "" { break } else if line != "" { continue } else { // Reset the entry buffer to start a new expression lines = lines[:0] ui.Output("(multi-line entry canceled)") continue } } else if err == io.EOF { break } lines = append(lines, line) // The following implements a heuristic for deciding if it seems likely // that the user was intending to continue entering more expression // characters on a subsequent line. This should get the right answer // for any valid expression, but might get confused by invalid input. // The user can always hit enter one more time (entering a blank line) // to break out of a multi-line sequence and force interpretation of // what was already entered. if repl.ExpressionEntryCouldContinue(lines) { continue } input := strings.Join(lines, "\n") + "\n" lines = lines[:0] // reset for next iteration out, exit, diags := session.Handle(input) if diags.HasErrors() { c.showDiagnostics(diags) } if exit { break } ui.Output(out) } return 0 }