Refactor input module to use RStream class

This commit is contained in:
Thiago de Arruda 2014-04-15 18:35:20 -03:00
parent 6e4e40a0f7
commit f276e97de9

View File

@ -6,6 +6,8 @@
#include "os/input.h" #include "os/input.h"
#include "os/event.h" #include "os/event.h"
#include "os/rstream_defs.h"
#include "os/rstream.h"
#include "vim.h" #include "vim.h"
#include "globals.h" #include "globals.h"
#include "ui.h" #include "ui.h"
@ -15,7 +17,7 @@
#include "term.h" #include "term.h"
#include "misc2.h" #include "misc2.h"
#define READ_BUFFER_LENGTH 4096 #define READ_BUFFER_SIZE 256
typedef enum { typedef enum {
kInputNone, kInputNone,
@ -23,101 +25,41 @@ typedef enum {
kInputEof kInputEof
} InbufPollResult; } InbufPollResult;
typedef struct { static RStream *read_stream;
uv_buf_t uvbuf; static bool eof = false, started_reading = false;
uint32_t rpos, wpos, fpos;
char_u data[READ_BUFFER_LENGTH];
bool reading;
} ReadBuffer;
static ReadBuffer rbuffer;
static uv_pipe_t read_stream;
// Use an idle handle to make reading from the fs look like a normal libuv
// event
static uv_idle_t fread_idle;
static uv_handle_type read_channel_type;
static bool eof = false;
static InbufPollResult inbuf_poll(int32_t ms); static InbufPollResult inbuf_poll(int32_t ms);
static void stderr_switch(void); static void stderr_switch(void);
static void alloc_cb(uv_handle_t *, size_t, uv_buf_t *); static void read_cb(RStream *rstream, void *data, bool eof);
static void read_cb(uv_stream_t *, ssize_t, const uv_buf_t *);
static void fread_idle_cb(uv_idle_t *);
void input_init() void input_init()
{ {
rbuffer.wpos = rbuffer.rpos = rbuffer.fpos = 0; read_stream = rstream_new(read_cb, READ_BUFFER_SIZE, NULL);
#ifdef DEBUG rstream_set_file(read_stream, read_cmd_fd);
memset(&rbuffer.data, 0, READ_BUFFER_LENGTH);
#endif
if ((read_channel_type = uv_guess_handle(read_cmd_fd)) == UV_FILE) {
uv_idle_init(uv_default_loop(), &fread_idle);
} else {
uv_pipe_init(uv_default_loop(), &read_stream, 0);
uv_pipe_open(&read_stream, read_cmd_fd);
}
} }
// Check if there's pending input // Check if there's pending input
bool input_ready() bool input_ready()
{ {
return rbuffer.rpos < rbuffer.wpos || eof; return rstream_available(read_stream) > 0 || eof;
} }
// Listen for input // Listen for input
void input_start() void input_start()
{ {
// Pin the buffer used by libuv rstream_start(read_stream);
rbuffer.uvbuf.len = READ_BUFFER_LENGTH - rbuffer.wpos;
rbuffer.uvbuf.base = (char *)(rbuffer.data + rbuffer.wpos);
if (read_channel_type == UV_FILE) {
// Just invoke the `fread_idle_cb` as soon as the loop starts
uv_idle_start(&fread_idle, fread_idle_cb);
} else {
// Start reading
rbuffer.reading = false;
uv_read_start((uv_stream_t *)&read_stream, alloc_cb, read_cb);
}
} }
// Stop listening for input // Stop listening for input
void input_stop() void input_stop()
{ {
if (read_channel_type == UV_FILE) { rstream_stop(read_stream);
uv_idle_stop(&fread_idle);
} else {
uv_read_stop((uv_stream_t *)&read_stream);
}
} }
// Copies (at most `count`) of was read from `read_cmd_fd` into `buf` // Copies (at most `count`) of was read from `read_cmd_fd` into `buf`
uint32_t input_read(char *buf, uint32_t count) uint32_t input_read(char *buf, uint32_t count)
{ {
uint32_t read_count = rbuffer.wpos - rbuffer.rpos; return rstream_read(read_stream, buf, count);
if (count < read_count) {
read_count = count;
}
if (read_count > 0) {
memcpy(buf, rbuffer.data + rbuffer.rpos, read_count);
rbuffer.rpos += read_count;
}
if (rbuffer.wpos == READ_BUFFER_LENGTH) {
// `wpos` is at the end of the buffer, so free some space by moving unread
// data...
memmove(
rbuffer.data, // ...To the beginning of the buffer(rpos 0)
rbuffer.data + rbuffer.rpos, // ...From the first unread position
rbuffer.wpos - rbuffer.rpos); // ...By the number of unread bytes
rbuffer.wpos -= rbuffer.rpos;
rbuffer.rpos = 0;
}
return read_count;
} }
@ -188,7 +130,9 @@ static InbufPollResult inbuf_poll(int32_t ms)
} }
if (event_poll(ms)) { if (event_poll(ms)) {
return eof && rbuffer.rpos == rbuffer.wpos ? kInputEof : kInputAvail; return eof && rstream_available(read_stream) == 0 ?
kInputEof :
kInputAvail;
} }
return kInputNone; return kInputNone;
@ -201,69 +145,22 @@ static void stderr_switch()
// cooked mode // cooked mode
settmode(TMODE_COOK); settmode(TMODE_COOK);
// Stop the idle handle // Stop the idle handle
uv_idle_stop(&fread_idle); rstream_stop(read_stream);
// Use stderr for stdin, also works for shell commands. // Use stderr for stdin, also works for shell commands.
read_cmd_fd = 2; read_cmd_fd = 2;
// Initialize and start the input stream // Initialize and start the input stream
uv_pipe_init(uv_default_loop(), &read_stream, 0); rstream_set_file(read_stream, read_cmd_fd);
uv_pipe_open(&read_stream, read_cmd_fd); rstream_start(read_stream);
uv_read_start((uv_stream_t *)&read_stream, alloc_cb, read_cb);
rbuffer.reading = false;
// Set the mode back to what it was // Set the mode back to what it was
settmode(mode); settmode(mode);
} }
// Called by libuv to allocate memory for reading. static void read_cb(RStream *rstream, void *data, bool at_eof)
static void alloc_cb(uv_handle_t *handle, size_t suggested, uv_buf_t *buf)
{ {
if (rbuffer.reading) { if (at_eof) {
buf->len = 0; if (!started_reading
return; && rstream_is_regular_file(rstream)
} && uv_guess_handle(2) == UV_TTY) {
buf->base = rbuffer.uvbuf.base;
buf->len = rbuffer.uvbuf.len;
// Avoid `alloc_cb`, `alloc_cb` sequences on windows
rbuffer.reading = true;
}
// Callback invoked by libuv after it copies the data into the buffer provided
// by `alloc_cb`. This is also called on EOF or when `alloc_cb` returns a
// 0-length buffer.
static void read_cb(uv_stream_t *stream, ssize_t cnt, const uv_buf_t *buf)
{
if (cnt <= 0) {
if (cnt != UV_ENOBUFS) {
// Read error or EOF, either way vim must exit
eof = true;
}
return;
}
// Data was already written, so all we need is to update 'wpos' to reflect
// the space actually used in the buffer.
rbuffer.wpos += cnt;
}
// Called by the by the 'idle' handle to emulate a reading event
static void fread_idle_cb(uv_idle_t *handle)
{
uv_fs_t req;
// Synchronous read
uv_fs_read(
uv_default_loop(),
&req,
read_cmd_fd,
&rbuffer.uvbuf,
1,
rbuffer.fpos,
NULL);
uv_fs_req_cleanup(&req);
if (req.result <= 0) {
if (rbuffer.fpos == 0 && uv_guess_handle(2) == UV_TTY) {
// Read error. Since stderr is a tty we switch to reading from it. This // Read error. Since stderr is a tty we switch to reading from it. This
// is for handling for cases like "foo | xargs vim" because xargs // is for handling for cases like "foo | xargs vim" because xargs
// redirects stdin from /dev/null. Previously, this was done in ui.c // redirects stdin from /dev/null. Previously, this was done in ui.c
@ -271,9 +168,7 @@ static void fread_idle_cb(uv_idle_t *handle)
} else { } else {
eof = true; eof = true;
} }
return;
} }
rbuffer.wpos += req.result; started_reading = true;
rbuffer.fpos += req.result;
} }