dthree / vorpal

Node's framework for interactive CLIs
http://vorpal.js.org
MIT License
5.64k stars 280 forks source link

Using arrow functions #14

Closed bestguy closed 8 years ago

bestguy commented 9 years ago

Trying out ES6 arrow functions with Vorpal, but the session normally available via this does not work of course:

vorpal
  .command('foo')
  .description('Outputs "bar".')
  .action((args, callback) => {
    this.log('bar'); // ReferenceError: log is not defined
    callback();
  });

Though what we be the best way to use vorpal with arrow functions?

dthree commented 9 years ago

Huh. Good one.

I'll have to do some more research into array functions to see if there's a way around this. Right now, the session is only exposed within the action, there's really no external reference.

AljoschaMeyer commented 9 years ago

Right now, the session is only exposed within the action, there's really no external reference.

I've been using vorpal.session.log('foo'), which seemed to work. Anything I'm missing?

AljoschaMeyer commented 9 years ago

I wrote this thing which pretty much depends on vorpal.session.log() to work - which it did in my very simple test cases. Or it would need another way to log from outside a command. I guess I could create a command which logs the arguments and then call it programmatically via exec. But that would be very, very hacky...

dthree commented 9 years ago

@AljoschaMeyer that should be fine. It just won't work in Vantage, but you're not developing in Vantage.

As Vantage deals with remote sessions, the sessions get ridiculously tricky (on the developer end). vorpal.session refers to the local session available in the local console. This session will always be the right session in Vorpal, so you should be good :)

@bestguy TLDR: Use aljoscha's suggestion.

bestguy commented 9 years ago

Got it, thanks for the tip!

dthree commented 9 years ago

This is actually a good question for Stack Overflow, as I'm sure it's going to be asked a lot, and I'm trying to build up a community there, and there's a vorpal.js tag now. If you don't mind posting there, I can answer it :)

AljoschaMeyer commented 9 years ago

vorpal.session.logalso does not work with vorpal-less.

I really think there should be a way to get the correct session outside of a command, instead of using a workaround.

dthree commented 9 years ago

Good point.

I think that can be made possible, as Vorpal does execute commands (async or not) in sync, so I can just record the command instance that is running in the present, and then make a getter for that.

What do you think a good API command would be?

vorpal.activeCommand()?

I prefer one-worders, but can't think of an intuitive one...

AljoschaMeyer commented 9 years ago

The most convenient thing would probably be if vorpal.session always referred to the correct session. Don't know if that's possible though.

Remember that one might call log from outside any command, e.g. for a welcome message at the start of the session.

dthree commented 9 years ago

It's not, really, as it would break Vantage. In Vantage, there is no "correct session" globally, as sessions are based on the user's context. I intended vorpal.session to be an internal command.

There's another fib I don't really tell the user. this in an action is actually not the session - haha - it's one level deeper. It is a CommandInstance object, whose methods pipe to the session, whose methods pipe to vorpal's UI. This is important, as a command instance tracks command piping (|), which does some mad science to correctly pipe logs between commands.

So in light of that, I don't know of a good way to refer to the active command instance outside of constantly updating vorpal with some variable. Maybe just vorpal.activeCommand - making it a property, not a method.

What do you think?


On calling log from outside a command, you can just use vorpal.log. This should work fine.


Btw, I like it :) You should do a PR on vorpal-awesome. I'm going to publish it soon to Sindre's awesome list.

AljoschaMeyer commented 9 years ago

On calling log from outside a command, you can just use vorpal.log. This should work fine.

Does that mean anytime someone uses this.log() in a command, vorpal.log() would do the same thing?
And - just guessing naively without having looked at the implementation - if that's the case, couldn't .delimiter() be implemented similarly to vorpal.log()?


vorpal.activeCommand as a property sounds good.

In any case, a solid implementation should remove the need for any this.foo shenanigans in commands and provide the same access everywhere.


Thanks, I'll do the awesome-list PR once the logging methods actually work with less, vantage etc.

dthree commented 9 years ago

Does that mean anytime someone uses this.log() in a command, vorpal.log() would do the same thing?

Not totally. Logging works like this:

Say we have a command: echo fitzbutz | reverse (outputs ztubztif)

  1. A CommandInstance is created. The "master" command is echo. this.log does a commandInstance.log.
  2. That instance's log method says, "oh look, I have a child (pipe) command. Lets now execute reverse and pass stdin: fitzbutz into the args, and otherwise eat echo's stdout.
  3. The reverse command says, "oh - I don't have a child command. Let me take my logged ztubztif output and pass it to my session."
  4. If the session is a Vantage instance upstream, it then passes the stdout through socket.io, downstream. If the session is local (Vorpal), the session object passes it to vorpal.ui's log method.
  5. All that vorpal.ui.log does is a) disable the prompt, b) log to stdout, c) re-draw the prompt, so that logging doesn't get in the way of the prompt.

vorpal.log is simply an alias for vorpal.ui.log. It can't handle piping and the like.

So:

  1. When you are not mid a command, you can use vorpal.log safely. you don't have to worry about piping because.... you aren't mid a command! :)
  2. When you are mid a command, you need to use this, or, as an alternative in the case of arrow functions, something like var self = vorpal.activeCommand.

Does that sound reasonable?


(I'm about to do an overhaul on the API docs, and port them over to Vorpal's wiki pages. As part of that, I'm going to rename the Session section to CommandInstance instead, for clarity)

AljoschaMeyer commented 9 years ago

Yes, I might be slowly understanding how that stuff works.

Would you set vorpal.activeCommand to null after a command is done?

In that case, a method that correctly logs, independendently of when/where it's called would look like this, right?

function log(msg){
  if (vorpal.activeCommand !== null) {
    vorpal.activeCommand.log(msg);
  } else {
    vorpal.log(msg);
  }
} 

This seems like a very convenient api, given the task is not trivial...

dthree commented 9 years ago

That's correct and yes, I would set it to null. I'll definitely add this as soon as possible!

AljoschaMeyer commented 9 years ago

I started looking at the code for this, and doesn't vorpal._command already do more or less what we'd need here?

dthree commented 9 years ago

Very clever - yes it does.

I didn't bring it up yet as I haven't fully tested it to ensure it covers every scenario, but it should. I also just want to expose it as a public property.

I'll get this done with today.

scotthovestadt commented 8 years ago

Arrow functions would also help me out a little bit.

dthree commented 8 years ago

Okay, use vorpal.activeCommand to get the current commandInstance, which exposes the same methods as this. vorpal._command actually doesn't do this, so just use this new exposed property.


This should probably go into the wiki, if anyone has a chance.

Haroenv commented 8 years ago

Thanks for activeCommand! I'm not sure where in the wiki it could fit, but it definitely needs to get mentioned somewhere.