google / zx

A tool for writing better scripts

Home Page:https://google.github.io/zx/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Add new option to `question()`: maximum number of chars

micalevisk opened this issue · comments

I was looking to an alternative to read -p "Yes/No question? " -n 1 (man page)
ie., the prompt returns after reading 1 character rather than waiting for a newline

demo
demo.mp4

As of now I can use question but I believe there's no way to replicate the behavior above

Proposal

function question(query?: string, options?: QuestionOptions): Promise<string>
type QuestionOptions = { choices?: string[], numberOfChars?: number }
// not sure if is better to not allow `choices` when `numberOfChars` is supplied

whereas if options.numberOfChars is supplied, the user is only allowed to type at most numberOfChars characters

POC

just run this nodejs ESM app
import { createInterface } from 'readline'

async function question(query, options) {
  const limitChars = typeof options.numberOfChars !== 'undefined'
  let completer = undefined
  if (Array.isArray(options?.choices)) {
    completer = function completer(line) {
      const completions = options.choices
      const hits = completions.filter((c) => c.startsWith(line))
      return [hits.length ? hits : completions, line]
    }
  }
  const rl = createInterface({
    input: process.stdin,
    output: process.stdout,
    completer,
  })

  const ac = new AbortController()
  const signal = ac.signal
  let inputCharsCount = 0
  const keyPressHandler = (_, m) => { 
    if (!m.crtl) {
      inputCharsCount++
      if (inputCharsCount === options.numberOfChars) ac.abort()
    }
  }
  if (limitChars) process.stdin.on('keypress', keyPressHandler)

  const question = (q) => new Promise((resolve) => {
    if (limitChars)
      signal.addEventListener('abort', () => {
        rl.close()
        resolve(rl.line)
      }, { once: true })

    rl.question(q ?? '', { signal }, resolve);
  })
  try {
    let answer = await question(query)
    rl.close()
    return answer
  } finally {
    if (limitChars) process.stdin.off('keypress', keyPressHandler)
  }
}


question('Yes/No question? ', { numberOfChars: 1 })
  .then((answer) => console.log({answer}))

Specifications

  • Version: 5.2.0

Too specific. Let's skip.