Add new option to `question()`: maximum number of chars
micalevisk opened this issue · comments
Micael Levi L. Cavalcante commented
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
Anton Medvedev commented
Too specific. Let's skip.