rustyline
rustyline copied to clipboard
Add an async interface
Probably needs to spawn an OS thread and perform all line reading there with a channel (I don't think there is another way to do it anyway). I could (an likely will) implement this outside rustyline
but if it seems useful enough to warrant inclusion I am happy to do a PR.
fn readline(&mut self, prompt: &str) -> Box<Future<Item = String, Error = ReadLineError>>;
https://github.com/alexcrichton/futures-rs
https://github.com/dpc/async-readline/blob/master/src/lib.rs#L165
I was thinking about tokio support, which basically means async support. I'm probably also trying to sketch something up.
https://docs.rs/tokio/0.1/tokio/io/struct.Stdin.html
The handle implements the AsyncRead trait
For what it's worth, I think that this could be accomplished with a wrapper type on the current editor. I don't think Rustyline internals need to change to read from stdin asynchronously, I think it just needs to provide an async-compatible interface, which should probably be a futures Stream of String
.
As @Marwes mentioned, this would probably be most easily accomplished by spawning a thread and running a standard Rustyline Editor
in a loop and send all of the results onto a channel.
@RadicalZephyr I don't know how to make sure rustyline
and the other/main thread update the screen correctly ?
That's a good question. I'm not super familiar with trying to do this so this might be a naive suggestion, but I think that the most straightforward solution would be to enforce that the thread running the rustyline editor is the only one allowed to write to stdout.
I'll try to put together an example as a proof of concept and at least initial guidance on how to use rustyline in an async context.
I've just pushed #200 a basic start on using rustyline from an async context.
The current strategy I'm using would only be able to show lines sent to the display from an async context after the user finishes editing the current line and before the next line becomes available to edit.
I think for async use to be really nice, it would be good to have a new interface that allows output to be sent while the user is editing a line. I'm thinking of being able to create a remote "handle" to an Editor
(kind of like the Handle
to a Reactor
in the previous iteration of tokio. That handle would probably implement AsyncWrite
. Though that might be lower level than is necessary... I have to think about this more!
Very excited to see this receiving attention!
I don't think Rustyline internals need to change to read from stdin asynchronously, I think it just needs to provide an async-compatible interface, which should probably be a futures Stream of
String
.
One reason to build async support into rustyline would be to allow operation without threads in cases where that is possible (e.g. stdin/out connected to a terminal or pipe on *nix). That's a lot of complexity for dubious benefit, though.
I think for async use to be really nice, it would be good to have a new interface that allows output to be sent while the user is editing a line. I'm thinking of being able to create a remote "handle" to an
Editor
(kind of like theHandle
to aReactor
in the previous iteration of tokio. That handle would probably implementAsyncWrite
.
Something like this sounds really great! Producing output without interfering with a prompt is exactly what I want for building e.g. an admin interface to a server. You might be able to get away with a synchronous std::io::Write
interface, since nobody should be using this to dump large volumes of data anyway. Support for more fine-grained writes than complete lines will be useful for things like displaying live progress bars concurrent with user input.
It'd also be nice to be able to use async for output from the log crate; a stretch-goal-feature for this could be a wrapper for Handle that impls Log
.
If anyone still wants to add this feature to rustyline, I've made a cleanroom implementation that could be used as a reference. https://github.com/zyansheep/rustyline-async
https://github.com/antirez/linenoise#asyncrhronous-api
Are you planning to add this in the future?
Are you planning to add this in the future?
I am not sure but removing / replacing ExternalPrinter by the clever / simple linenoise solution is appealing.
(see https://github.com/kkawakam/rustyline/blob/master/linenoise.md: we already have the implementation of 2/5 methods related to non-blocking API).
And maybe we should not use the term async
.
I am not sure but removing / replacing ExternalPrinter by the clever / simple linenoise solution is appealing.
I spent a few hours looking into this, and implementing an incremental read API similar to linenoise would be a significant architectural shift.
This isn't simply a matter of moving the needed state into the right place (currently the state is spread out across Editor
, State
, InputState
, impl Renderer
, and impl RawReader
). There's also a significant amount of recursion to handle command completion, reverse history search, paging, etc. Those recursive routines would need to be rewritten to handle single Cmd
s, and additional state added to determine which cmd handler function should we dispatch the next Cmd
to when called.
Basically we'd need to keep a stack of trait CmdDispatcher
instances, each holding whatever state they need, then passing it the result of next_cmd()
each time readline updates. There's a lot to figure out here, but there's a very high chance of breaking some existing behavior not covered by tests.
I think this approach is the right one as it would make it trivial to use rustyline in async, multi-threaded, and single-threaded multiplex (select()
) situations. @gwenn I'm just not sure you're interested in that significant of a change. It would be a large PR to review.