derhuerst / multiselect-prompt

A prompt to select zero or more items.
https://github.com/derhuerst/multiselect-prompt
ISC License
8 stars 3 forks source link

How to use multiselect-prompt in a callback? #5

Closed nschoe closed 8 years ago

nschoe commented 8 years ago

I'm trying to use multiselect-prompt and prompt-base (incidentally with prompt-question too, but that's just for formatting) in this manner:

const Prompt          = require ('prompt-base')
const Question        = require ('prompt-question')
const multi               = require ('multiselect-prompt')

const types = [
    {title: 'STRING',    value: 'STRING'},
    {title: 'INT32', value: 'INT32'},
    {title: 'BOOL',  value: 'BOOL'},
    {title: 'ARRAY',  value: 'ARRAY'}
]

let q1 = new Prompt (new Question ('inputArgName', 'Name for input argument'))

q1.ask( inputArgName => {
    multi('Select \'' + inputArgName + '\' type(s): ', types)
    .on('data', (data) => console.log('Changed to', selected(data.value)))
    .on('abort', (items) => console.log('Aborted with', selected(items)))
    .on('submit', (items) => console.log (selected (items)))
}

But it doesn't work: the question 1 q1 is run, I'm asked Name for input argument, but then, the Select <whatever-I-entered-before> types(s): is written, all choices are written, but the prompt doesn't wait for me to select something.

I've tried with the .then() method of q1 (which isn't a real Promise yet, as I can understand).

How can I chain those calls?

derhuerst commented 8 years ago

I haven't known about enquirer yet tbh. Will look at it tonight or in the next days. It seems like it's a modern, but just as monolithic, version of Inquirer.js.

Therefore, i will look at this specific problem later.

nschoe commented 8 years ago

Okay @derhuerst , thanks for answering. I've just taken a loot as enquirer, but I face the same problem: I just cannot chain two different questions. Which seems weird, it seems to be like a basic feature of your libraries :-)

(I'm not criticizing, I find them awesome, it's just I wonder how do you guys use it: I'm trying to make a very simple menu here, with a second screen depending on the choice of a first screen, but even that doesn't seem to work. This feels weird ^^ Or maybe there's something I missed :/)

derhuerst commented 8 years ago

What about enquirer/enquirer#6?

I chained multiple queries using my prompt libraries in vbb-cli.

nschoe commented 8 years ago

Hum, I'm the one who raised this issue right after you suggested enquirer :-) And apparently, it's not possible either with their lib. They are not too sure why, so... I'm sort of screwed ^^

As for chaining multiple questions with your prompt... I'll try again, returning Promises. But I'm fairly sure that's what I tried first. Maybe I got it wrong.

jonschlinkert commented 8 years ago

What about enquirer/enquirer#6?

We're working on this now. I'm pretty sure I caused this bug when we converted all of the prompts to be standalone (so that enquirer itself can be optional - this changed how the readline interface was instantiated and probably caused this issue because of how the lifecycle is handled)

To this comment "They are not too sure why, so... I'm sort of screwed ^^". Please stick with us if you have the ability and patience to. We're actively maintaining that project and making bug fixes and improvements. It might take minutes to fix, or maybe a day or something, but it's going to be resolved quickly. Feedback and help from users is the absolute most sure way to help us figure out what's happening to fix it.

but just as monolithic, version of Inquirer.js.

I'm not sure what the reasoning is behind your comment, but it's not hard to look and see that enquirer has a fraction of the code of inquirer. Not only does inquirer have almost twice the dependencies in the core library, but the lib folder is massive in inquirer, with dozens of files, whereas lib in enquirer has one file. All of the prompts in enquirer (besides "input") are externalized, along with any code or libraries necessary for each of them to run.

I do appreciate that you're looking at enquirer, and I'd love to have your collaboration in getting the issue you mentioned fixed. and I'd be happy to answer any questions you have about enquirer

nschoe commented 8 years ago

Ho yeah no problem, I was not criticizing, don't worry. As I said before: it's an awesome job you guys did. I'm just a little frustrated because I needed this feature, then found enquirer and thought 'cool, this is exactly what I needed' and bahm an stuck again ^^

I'll be following the issue, I have faith ^^

jonschlinkert commented 8 years ago

I'm just a little frustrated because I needed this feature, then found enquirer and thought 'cool, this is exactly what I needed' and bahm an stuck again ^^

lol yeah I totally get that feeling. I've been there quite often. We'll keep you posted

nschoe commented 8 years ago

@derhuerst I have tried again with promises, I can't have it work, here is what I did:

const Promise = require ('bluebird')
const multi = require('multiselect-prompt')
const Prompt          = require ('prompt-base')
const Question        = require ('prompt-question')

let q1 = new Prompt (new Question ('inputArgName', 'Name for input argument #' + argumentNb))

q1.run()
.then(() => {
    return new Promise( (resolve, reject) => {
        multi('Select arg type(s): ', types, opts)
        .on('abort', (items) => reject ('Aborted with', selected(items)))
        .on('submit', (items) => resolve (selected (items)))
    })
})
.then( () => {
    console.log ('End')
})

And again: it asks for the first question, I answer and then it terminates, only printing the multiple choices without giving me a chance to actually select one.

Another funny thing: after I run this, my console's cursor vanished and I had to type reset to get it back. It might give you a clue on what's happening

derhuerst commented 8 years ago

but just as monolithic, version of Inquirer.js.

I'm not sure what the reasoning is behind your comment, but it's not hard to look and see that enquirer has a fraction of the code of inquirer. Not only does inquirer have almost twice the dependencies in the core library, but the lib folder is massive in inquirer, with dozens of files, whereas lib in enquirer has one file. All of the prompts in enquirer (besides "input") are externalized, along with any code or libraries necessary for each of them to run.

@jonschlinkert I should have said this differently. Props for rewriting a large codebase like Inquirer.js and making it modern! Also, great work with making it more lightweight!

To compare enquirer to my prompt modules: The way of using prompts by registering feels more coupled and the interfaces of the individual prompts seem closely geared towards enquirer, which is why I said "monolithic".


Back to the point of chaining my prompts:

All of my prompts (number-prompt, date-prompt, select-prompt, multiselect-prompt, cli-autocomplete) expose a function that returns a readable stream of interim values. They also emit abort or submit, respectively.

@nschoe You can easily wrap them in e.g. a Promise(I didn't do that for the sake of keeping them unopiniated). Out of my head:

'use strict'
const selectPrompt = require('select-prompt')
const toPromise = (prompt) => new Promise((resolve, reject) => {
  prompt.on('submit', resolve)
  prompt.on('abort', reject)
})
toPromise(selectPrompt('color?', […]))
.then((v) => console.log('value', v))

It's possible to chain promises like this:

promiseReturning1()
.then((v1) => {
  // handle v1
  return promiseReturning2()
})
.then((v2) => {
  // handle v2
  return …
})

Or use a coroutine like so and co, which is close to ES2016 async+await:

require('so')(function* () {
  const v1 = yield promiseReturning1()
  const v2 = yield promiseReturning2()
})
jonschlinkert commented 8 years ago

To compare enquirer to my prompt modules

I wish I had seen these before. What I wanted out of enquirer is a uniform API that makes it easy to create custom prompts. But I really like how you did your modules. If I have the time and bandwidth I might look into using your modules in the enquirer prompts, and just use a thin wrapper to allow them to be registered as plugins. I'm not even sure if this makes sense code-wise yet, but it totally makes sense in terms of our goals. we don't want to create more code to maintain. thanks for your reply

edit: interesting. based on your comments it seemed "obvious" that your libs would be lighter and have less code than enquirer or the enquirer prompts, but unfortunately it seems your own prompt libs are actually quite a bit more bloated and complicated. No offense! I mean that in the nicest way. they look modern. also, the multiselect prompt doesn't actually close the readline interface when it's supposed to. I'm not sure about the others, but they probably have the same bug.

derhuerst commented 8 years ago

@jonschlinkert

Maybe you can help me with a tricky issue which affects all of my modules.

I'm setting process.stdin to rawMode to receive individual control sequences. After the prompt has been submitted, I set it to the old value, but still the process doesn't exit automatically afterwards.

derhuerst commented 8 years ago

based on your comments it seemed "obvious" that your libs would be lighter and have less code than enquirer or the enquirer prompts, but unfortunately it seems your own prompt libs are actually quite a bit more bloated and complicated.

I'm aware that having extremely lightweight modules is not the silver bullet, as it often comes by trading code size/complexity for flexibility. Also hard-coding stuff is not always the best option.

Having said that, it seems like e.g. prompt-checkbox (tree) is a lot more bloated than multiselect-prompt (tree).

also, the multiselect prompt doesn't actually close the readline interface when it's supposed to. I'm not sure about the others, but they probably have the same bug.

That is what I was fighting with (see comment above), thanks! How do I properly close it?

derhuerst commented 8 years ago

I'm setting process.stdin to rawMode to receive individual control sequences. After the prompt has been submitted, I set it to the old value, but still the process doesn't exit automatically afterwards.

I fixed it: https://github.com/derhuerst/prompt-skeleton/commit/205ed48643418db2665b51326cdbcf802354b2d8

derhuerst commented 8 years ago

@nschoe Do you still have issues using multiselect-prompt? If so, please reopen (:

nschoe commented 8 years ago

@derhuerst I haven't retried it yet, I switched to enquirer at the moment. Was it the same problem (something along closing the stdin?)

derhuerst commented 8 years ago

@nschoe As I understood the issue you reported, it has nothing to do with closing stdin. In fact, it worked ever since.

They way you run multiple prompts sequentially is different in enquirer vs multiselect-prompt. In multiselect-prompt, you will have to use the mechanisms for calling Promises sequentially, as discussed above.