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

@@ -98,3 +98,52 @@ int get_real_state(void)
return State;
}
/// @returns[allocated] mode string
char *get_mode(void)
{
char *buf = xcalloc(3, sizeof(char));
if (VIsual_active) {
if (VIsual_select) {
buf[0] = (char)(VIsual_mode + 's' - 'v');
} else {
buf[0] = (char)VIsual_mode;
}
} else if (State == HITRETURN || State == ASKMORE || State == SETWSIZE
|| State == CONFIRM) {
buf[0] = 'r';
if (State == ASKMORE) {
buf[1] = 'm';
} else if (State == CONFIRM) {
buf[1] = '?';
}
} else if (State == EXTERNCMD) {
buf[0] = '!';
} else if (State & INSERT) {
if (State & VREPLACE_FLAG) {
buf[0] = 'R';
buf[1] = 'v';
} else if (State & REPLACE_FLAG) {
buf[0] = 'R';
} else {
buf[0] = 'i';
}
} else if (State & CMDLINE) {
buf[0] = 'c';
if (exmode_active) {
buf[1] = 'v';
}
} else if (exmode_active) {
buf[0] = 'c';
buf[1] = 'e';
} else if (State & TERM_FOCUS) {
buf[0] = 't';
} else {
buf[0] = 'n';
if (finish_op) {
buf[1] = 'o';
}
}
return buf;
}