charmbracelet / huh

Build terminal forms and prompts 🤷🏻‍♀️
MIT License
4.18k stars 113 forks source link

feat(form): implement `WithInput` #271

Closed Delta456 closed 4 months ago

Delta456 commented 4 months ago

Implements WithInput(r io.Reader) which calls tea.WithIntput(r) and also avoids using bubbletea directly

Closes https://github.com/charmbracelet/huh/issues/270

survivorbat commented 4 months ago

That was quick, thanks!

Delta456 commented 4 months ago

That was quick, thanks!

I couldn't understand what Perhaps you could even introduce a piece of documentation on how to use it with 2 io.Pipe's to simulate user input based on questions in the output. meant so didn't add this to the docs.

Though I am curious on how will this work.

survivorbat commented 4 months ago

That was quick, thanks!

I couldn't understand what Perhaps you could even introduce a piece of documentation on how to use it with 2 io.Pipe's to simulate user input based on questions in the output. meant so didn't add this to the docs.

Though I am curious on how will this work.

That was indeed quite vague. In my unit-test I created an 'answering machine' that sends strings to the 'StdIn' based on lines read from 'StdOut'. It works a little bit like this:

questionsAndAnswers := map[string]string{
  "How are you doing?": "I'm fine thanks!",
  "Are you sure you want to do this?": "\033[C", // Right arrow key to move around in the prompt
}

// Stdout replacement
outputReader, outputWriter := io.Pipe()
defer outputReader.Close()
defer outputWriter.Close()

// Stdin replacement
inputReader, inputWriter := io.Pipe()
defer inputReader.Close()
defer inputWriter.Close()

go func() {
  reader := bufio.NewScanner(outputReader)

  for reader.Scan() {
    line := reader.Text()

    // Loop through questions and answers
    // [...]

    if answerFound {
      inputWriter.Write([]byte(answer) + "\n\r")
    }
  }
}()

form := huh.NewForm().
   WithOutput(outputWriter).
   WithInput(inputReader).
   Run()

Not sure if you're looking for something like this in your documentation, but I enjoy sharing it :)