mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
Refactor input module to use RStream class
This commit is contained in:
parent
6e4e40a0f7
commit
f276e97de9
153
src/os/input.c
153
src/os/input.c
@ -6,6 +6,8 @@
|
||||
|
||||
#include "os/input.h"
|
||||
#include "os/event.h"
|
||||
#include "os/rstream_defs.h"
|
||||
#include "os/rstream.h"
|
||||
#include "vim.h"
|
||||
#include "globals.h"
|
||||
#include "ui.h"
|
||||
@ -15,7 +17,7 @@
|
||||
#include "term.h"
|
||||
#include "misc2.h"
|
||||
|
||||
#define READ_BUFFER_LENGTH 4096
|
||||
#define READ_BUFFER_SIZE 256
|
||||
|
||||
typedef enum {
|
||||
kInputNone,
|
||||
@ -23,101 +25,41 @@ typedef enum {
|
||||
kInputEof
|
||||
} InbufPollResult;
|
||||
|
||||
typedef struct {
|
||||
uv_buf_t uvbuf;
|
||||
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 RStream *read_stream;
|
||||
static bool eof = false, started_reading = false;
|
||||
|
||||
static InbufPollResult inbuf_poll(int32_t ms);
|
||||
static void stderr_switch(void);
|
||||
static void alloc_cb(uv_handle_t *, size_t, uv_buf_t *);
|
||||
static void read_cb(uv_stream_t *, ssize_t, const uv_buf_t *);
|
||||
static void fread_idle_cb(uv_idle_t *);
|
||||
static void read_cb(RStream *rstream, void *data, bool eof);
|
||||
|
||||
void input_init()
|
||||
{
|
||||
rbuffer.wpos = rbuffer.rpos = rbuffer.fpos = 0;
|
||||
#ifdef DEBUG
|
||||
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);
|
||||
}
|
||||
read_stream = rstream_new(read_cb, READ_BUFFER_SIZE, NULL);
|
||||
rstream_set_file(read_stream, read_cmd_fd);
|
||||
}
|
||||
|
||||
// Check if there's pending input
|
||||
bool input_ready()
|
||||
{
|
||||
return rbuffer.rpos < rbuffer.wpos || eof;
|
||||
return rstream_available(read_stream) > 0 || eof;
|
||||
}
|
||||
|
||||
// Listen for input
|
||||
void input_start()
|
||||
{
|
||||
// Pin the buffer used by libuv
|
||||
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);
|
||||
}
|
||||
rstream_start(read_stream);
|
||||
}
|
||||
|
||||
// Stop listening for input
|
||||
void input_stop()
|
||||
{
|
||||
if (read_channel_type == UV_FILE) {
|
||||
uv_idle_stop(&fread_idle);
|
||||
} else {
|
||||
uv_read_stop((uv_stream_t *)&read_stream);
|
||||
}
|
||||
rstream_stop(read_stream);
|
||||
}
|
||||
|
||||
// Copies (at most `count`) of was read from `read_cmd_fd` into `buf`
|
||||
uint32_t input_read(char *buf, uint32_t count)
|
||||
{
|
||||
uint32_t read_count = rbuffer.wpos - rbuffer.rpos;
|
||||
|
||||
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;
|
||||
return rstream_read(read_stream, buf, count);
|
||||
}
|
||||
|
||||
|
||||
@ -188,7 +130,9 @@ static InbufPollResult inbuf_poll(int32_t ms)
|
||||
}
|
||||
|
||||
if (event_poll(ms)) {
|
||||
return eof && rbuffer.rpos == rbuffer.wpos ? kInputEof : kInputAvail;
|
||||
return eof && rstream_available(read_stream) == 0 ?
|
||||
kInputEof :
|
||||
kInputAvail;
|
||||
}
|
||||
|
||||
return kInputNone;
|
||||
@ -201,69 +145,22 @@ static void stderr_switch()
|
||||
// cooked mode
|
||||
settmode(TMODE_COOK);
|
||||
// Stop the idle handle
|
||||
uv_idle_stop(&fread_idle);
|
||||
rstream_stop(read_stream);
|
||||
// Use stderr for stdin, also works for shell commands.
|
||||
read_cmd_fd = 2;
|
||||
// Initialize and start the input stream
|
||||
uv_pipe_init(uv_default_loop(), &read_stream, 0);
|
||||
uv_pipe_open(&read_stream, read_cmd_fd);
|
||||
uv_read_start((uv_stream_t *)&read_stream, alloc_cb, read_cb);
|
||||
rbuffer.reading = false;
|
||||
rstream_set_file(read_stream, read_cmd_fd);
|
||||
rstream_start(read_stream);
|
||||
// Set the mode back to what it was
|
||||
settmode(mode);
|
||||
}
|
||||
|
||||
// Called by libuv to allocate memory for reading.
|
||||
static void alloc_cb(uv_handle_t *handle, size_t suggested, uv_buf_t *buf)
|
||||
static void read_cb(RStream *rstream, void *data, bool at_eof)
|
||||
{
|
||||
if (rbuffer.reading) {
|
||||
buf->len = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (at_eof) {
|
||||
if (!started_reading
|
||||
&& rstream_is_regular_file(rstream)
|
||||
&& uv_guess_handle(2) == UV_TTY) {
|
||||
// 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
|
||||
// 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 {
|
||||
eof = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
rbuffer.wpos += req.result;
|
||||
rbuffer.fpos += req.result;
|
||||
started_reading = true;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user