api: nvim_get_mode()

Asynchronous API functions are served immediately, which means pending
input could change the state of Nvim shortly after an async API function
result is returned.

nvim_get_mode() is different:
  - If RPCs are known to be blocked, it responds immediately (without
    flushing the input/event queue)
  - else it is handled just-in-time before waiting for input, after
    pending input was processed. This makes the result more reliable
    (but not perfect).

Internally this is handled as a special case, but _semantically_ nothing
has changed: API users never know when input flushes, so this internal
special-case doesn't violate that. As far as API users are concerned,
nvim_get_mode() is just another asynchronous API function.

In all cases nvim_get_mode() never blocks for more than the time it
takes to flush the input/event queue (~µs).

Note: This doesn't address #6166; nvim_get_mode() will provoke #6166 if
e.g. `d` is operator-pending.

Closes #6159
This commit is contained in:
Justin M. Keyes
2017-03-13 15:02:37 +01:00
parent 7044aa6e82
commit 3ea1007753
16 changed files with 250 additions and 77 deletions

View File

@@ -23,6 +23,7 @@
#include "nvim/main.h"
#include "nvim/misc1.h"
#include "nvim/state.h"
#include "nvim/log.h"
#define READ_BUFFER_SIZE 0xfff
#define INPUT_BUFFER_SIZE (READ_BUFFER_SIZE * 4)
@@ -38,6 +39,7 @@ static RBuffer *input_buffer = NULL;
static bool input_eof = false;
static int global_fd = 0;
static int events_enabled = 0;
static bool blocking = false;
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "os/input.c.generated.h"
@@ -327,13 +329,27 @@ static unsigned int handle_mouse_event(char **ptr, uint8_t *buf,
return bufsize;
}
/// @return true if the main loop is blocked and waiting for input.
bool input_blocking(void)
{
return blocking;
}
static bool input_poll(int ms)
{
if (do_profiling == PROF_YES && ms) {
prof_inchar_enter();
}
if ((ms == - 1 || ms > 0)
&& !(events_enabled || input_ready() || input_eof)
) {
blocking = true;
multiqueue_process_debug(main_loop.events);
multiqueue_process_events(main_loop.events);
}
LOOP_PROCESS_EVENTS_UNTIL(&main_loop, NULL, ms, input_ready() || input_eof);
blocking = false;
if (do_profiling == PROF_YES && ms) {
prof_inchar_exit();