mirror of
https://github.com/neovim/neovim.git
synced 2024-12-24 13:15:09 -07:00
Merge #6247 'api: nvim_get_mode()'
This commit is contained in:
commit
129f107c0c
@ -154,22 +154,17 @@ Interfaces ~
|
|||||||
|if_cscop.txt| using Cscope with Vim
|
|if_cscop.txt| using Cscope with Vim
|
||||||
|if_pyth.txt| Python interface
|
|if_pyth.txt| Python interface
|
||||||
|if_ruby.txt| Ruby interface
|
|if_ruby.txt| Ruby interface
|
||||||
|debugger.txt| Interface with a debugger
|
|
||||||
|sign.txt| debugging signs
|
|sign.txt| debugging signs
|
||||||
|
|
||||||
Versions ~
|
Versions ~
|
||||||
|vim_diff.txt| Main differences between Nvim and Vim
|
|vim_diff.txt| Main differences between Nvim and Vim
|
||||||
|vi_diff.txt| Main differences between Vim and Vi
|
|vi_diff.txt| Main differences between Vim and Vi
|
||||||
*sys-file-list*
|
|
||||||
Remarks about specific systems ~
|
|
||||||
|os_win32.txt| MS-Windows
|
|
||||||
*standard-plugin-list*
|
*standard-plugin-list*
|
||||||
Standard plugins ~
|
Standard plugins ~
|
||||||
|pi_gzip.txt| Reading and writing compressed files
|
|pi_gzip.txt| Reading and writing compressed files
|
||||||
|pi_netrw.txt| Reading and writing files over a network
|
|pi_netrw.txt| Reading and writing files over a network
|
||||||
|pi_paren.txt| Highlight matching parens
|
|pi_paren.txt| Highlight matching parens
|
||||||
|pi_tar.txt| Tar file explorer
|
|pi_tar.txt| Tar file explorer
|
||||||
|pi_vimball.txt| Create a self-installing Vim script
|
|
||||||
|pi_zip.txt| Zip archive explorer
|
|pi_zip.txt| Zip archive explorer
|
||||||
|
|
||||||
LOCAL ADDITIONS: *local-additions*
|
LOCAL ADDITIONS: *local-additions*
|
||||||
|
@ -34,11 +34,6 @@ It can be accessed from within Vim with the <Help> or <F1> key and with the
|
|||||||
is not located in the default place. You can jump to subjects like with tags:
|
is not located in the default place. You can jump to subjects like with tags:
|
||||||
Use CTRL-] to jump to a subject under the cursor, use CTRL-T to jump back.
|
Use CTRL-] to jump to a subject under the cursor, use CTRL-T to jump back.
|
||||||
|
|
||||||
This manual refers to Vim on various machines. There may be small differences
|
|
||||||
between different computers and terminals. Besides the remarks given in this
|
|
||||||
document, there is a separate document for each supported system, see
|
|
||||||
|sys-file-list|.
|
|
||||||
|
|
||||||
*pronounce*
|
*pronounce*
|
||||||
Vim is pronounced as one word, like Jim, not vi-ai-em. It's written with a
|
Vim is pronounced as one word, like Jim, not vi-ai-em. It's written with a
|
||||||
capital, since it's a name, again like Jim.
|
capital, since it's a name, again like Jim.
|
||||||
|
@ -40,10 +40,6 @@ Note: If the output has been stopped with "q" at the more prompt, it will only
|
|||||||
be displayed up to this point.
|
be displayed up to this point.
|
||||||
The previous command output is cleared when another command produces output.
|
The previous command output is cleared when another command produces output.
|
||||||
|
|
||||||
If you are using translated messages, the first printed line tells who
|
|
||||||
maintains the messages or the translations. You can use this to contact the
|
|
||||||
maintainer when you spot a mistake.
|
|
||||||
|
|
||||||
If you want to find help on a specific (error) message, use the ID at the
|
If you want to find help on a specific (error) message, use the ID at the
|
||||||
start of the message. For example, to get help on the message: >
|
start of the message. For example, to get help on the message: >
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
#include "nvim/eval.h"
|
#include "nvim/eval.h"
|
||||||
#include "nvim/eval/typval.h"
|
#include "nvim/eval/typval.h"
|
||||||
#include "nvim/option.h"
|
#include "nvim/option.h"
|
||||||
|
#include "nvim/state.h"
|
||||||
#include "nvim/syntax.h"
|
#include "nvim/syntax.h"
|
||||||
#include "nvim/getchar.h"
|
#include "nvim/getchar.h"
|
||||||
#include "nvim/os/input.h"
|
#include "nvim/os/input.h"
|
||||||
@ -701,6 +702,24 @@ Dictionary nvim_get_color_map(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Gets the current mode.
|
||||||
|
/// mode: Mode string. |mode()|
|
||||||
|
/// blocking: true if Nvim is waiting for input.
|
||||||
|
///
|
||||||
|
/// @returns Dictionary { "mode": String, "blocking": Boolean }
|
||||||
|
Dictionary nvim_get_mode(void)
|
||||||
|
FUNC_API_SINCE(2) FUNC_API_ASYNC
|
||||||
|
{
|
||||||
|
Dictionary rv = ARRAY_DICT_INIT;
|
||||||
|
char *modestr = get_mode();
|
||||||
|
bool blocked = input_blocking();
|
||||||
|
|
||||||
|
PUT(rv, "mode", STRING_OBJ(cstr_as_string(modestr)));
|
||||||
|
PUT(rv, "blocking", BOOLEAN_OBJ(blocked));
|
||||||
|
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
Array nvim_get_api_info(uint64_t channel_id)
|
Array nvim_get_api_info(uint64_t channel_id)
|
||||||
FUNC_API_SINCE(1) FUNC_API_ASYNC FUNC_API_NOEVAL
|
FUNC_API_SINCE(1) FUNC_API_ASYNC FUNC_API_NOEVAL
|
||||||
{
|
{
|
||||||
|
@ -12575,59 +12575,18 @@ static void f_mkdir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/// "mode()" function
|
||||||
* "mode()" function
|
|
||||||
*/
|
|
||||||
static void f_mode(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
static void f_mode(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
||||||
{
|
{
|
||||||
char_u buf[3];
|
char *mode = get_mode();
|
||||||
|
|
||||||
buf[1] = NUL;
|
// Clear out the minor mode when the argument is not a non-zero number or
|
||||||
buf[2] = NUL;
|
// non-empty string.
|
||||||
|
if (!non_zero_arg(&argvars[0])) {
|
||||||
if (VIsual_active) {
|
mode[1] = NUL;
|
||||||
if (VIsual_select)
|
|
||||||
buf[0] = VIsual_mode + 's' - 'v';
|
|
||||||
else
|
|
||||||
buf[0] = 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';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Clear out the minor mode when the argument is not a non-zero number or
|
rettv->vval.v_string = (char_u *)mode;
|
||||||
* non-empty string. */
|
|
||||||
if (!non_zero_arg(&argvars[0]))
|
|
||||||
buf[1] = NUL;
|
|
||||||
|
|
||||||
rettv->vval.v_string = vim_strsave(buf);
|
|
||||||
rettv->v_type = VAR_STRING;
|
rettv->v_type = VAR_STRING;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,16 +8,14 @@
|
|||||||
|
|
||||||
typedef void (*argv_callback)(void **argv);
|
typedef void (*argv_callback)(void **argv);
|
||||||
typedef struct message {
|
typedef struct message {
|
||||||
int priority;
|
|
||||||
argv_callback handler;
|
argv_callback handler;
|
||||||
void *argv[EVENT_HANDLER_MAX_ARGC];
|
void *argv[EVENT_HANDLER_MAX_ARGC];
|
||||||
} Event;
|
} Event;
|
||||||
typedef void(*event_scheduler)(Event event, void *data);
|
typedef void(*event_scheduler)(Event event, void *data);
|
||||||
|
|
||||||
#define VA_EVENT_INIT(event, p, h, a) \
|
#define VA_EVENT_INIT(event, h, a) \
|
||||||
do { \
|
do { \
|
||||||
assert(a <= EVENT_HANDLER_MAX_ARGC); \
|
assert(a <= EVENT_HANDLER_MAX_ARGC); \
|
||||||
(event)->priority = p; \
|
|
||||||
(event)->handler = h; \
|
(event)->handler = h; \
|
||||||
if (a) { \
|
if (a) { \
|
||||||
va_list args; \
|
va_list args; \
|
||||||
@ -29,11 +27,11 @@ typedef void(*event_scheduler)(Event event, void *data);
|
|||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
static inline Event event_create(int priority, argv_callback cb, int argc, ...)
|
static inline Event event_create(argv_callback cb, int argc, ...)
|
||||||
{
|
{
|
||||||
assert(argc <= EVENT_HANDLER_MAX_ARGC);
|
assert(argc <= EVENT_HANDLER_MAX_ARGC);
|
||||||
Event event;
|
Event event;
|
||||||
VA_EVENT_INIT(&event, priority, cb, argc);
|
VA_EVENT_INIT(&event, cb, argc);
|
||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,8 +44,7 @@ void loop_poll_events(Loop *loop, int ms)
|
|||||||
// we do not block indefinitely for I/O.
|
// we do not block indefinitely for I/O.
|
||||||
uv_timer_start(&loop->poll_timer, timer_cb, (uint64_t)ms, (uint64_t)ms);
|
uv_timer_start(&loop->poll_timer, timer_cb, (uint64_t)ms, (uint64_t)ms);
|
||||||
} else if (ms == 0) {
|
} else if (ms == 0) {
|
||||||
// For ms == 0, we need to do a non-blocking event poll by
|
// For ms == 0, do a non-blocking event poll.
|
||||||
// setting the run mode to UV_RUN_NOWAIT.
|
|
||||||
mode = UV_RUN_NOWAIT;
|
mode = UV_RUN_NOWAIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,6 +126,7 @@ void multiqueue_free(MultiQueue *this)
|
|||||||
xfree(this);
|
xfree(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Removes the next item and returns its Event.
|
||||||
Event multiqueue_get(MultiQueue *this)
|
Event multiqueue_get(MultiQueue *this)
|
||||||
{
|
{
|
||||||
return multiqueue_empty(this) ? NILEVENT : multiqueue_remove(this);
|
return multiqueue_empty(this) ? NILEVENT : multiqueue_remove(this);
|
||||||
@ -144,7 +145,7 @@ void multiqueue_process_events(MultiQueue *this)
|
|||||||
{
|
{
|
||||||
assert(this);
|
assert(this);
|
||||||
while (!multiqueue_empty(this)) {
|
while (!multiqueue_empty(this)) {
|
||||||
Event event = multiqueue_get(this);
|
Event event = multiqueue_remove(this);
|
||||||
if (event.handler) {
|
if (event.handler) {
|
||||||
event.handler(event.argv);
|
event.handler(event.argv);
|
||||||
}
|
}
|
||||||
@ -178,36 +179,48 @@ size_t multiqueue_size(MultiQueue *this)
|
|||||||
return this->size;
|
return this->size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets an Event from an item.
|
||||||
|
///
|
||||||
|
/// @param remove Remove the node from its queue, and free it.
|
||||||
|
static Event multiqueueitem_get_event(MultiQueueItem *item, bool remove)
|
||||||
|
{
|
||||||
|
assert(item != NULL);
|
||||||
|
Event ev;
|
||||||
|
if (item->link) {
|
||||||
|
// get the next node in the linked queue
|
||||||
|
MultiQueue *linked = item->data.queue;
|
||||||
|
assert(!multiqueue_empty(linked));
|
||||||
|
MultiQueueItem *child =
|
||||||
|
multiqueue_node_data(QUEUE_HEAD(&linked->headtail));
|
||||||
|
ev = child->data.item.event;
|
||||||
|
// remove the child node
|
||||||
|
if (remove) {
|
||||||
|
QUEUE_REMOVE(&child->node);
|
||||||
|
xfree(child);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// remove the corresponding link node in the parent queue
|
||||||
|
if (remove && item->data.item.parent_item) {
|
||||||
|
QUEUE_REMOVE(&item->data.item.parent_item->node);
|
||||||
|
xfree(item->data.item.parent_item);
|
||||||
|
item->data.item.parent_item = NULL;
|
||||||
|
}
|
||||||
|
ev = item->data.item.event;
|
||||||
|
}
|
||||||
|
return ev;
|
||||||
|
}
|
||||||
|
|
||||||
static Event multiqueue_remove(MultiQueue *this)
|
static Event multiqueue_remove(MultiQueue *this)
|
||||||
{
|
{
|
||||||
assert(!multiqueue_empty(this));
|
assert(!multiqueue_empty(this));
|
||||||
QUEUE *h = QUEUE_HEAD(&this->headtail);
|
QUEUE *h = QUEUE_HEAD(&this->headtail);
|
||||||
QUEUE_REMOVE(h);
|
QUEUE_REMOVE(h);
|
||||||
MultiQueueItem *item = multiqueue_node_data(h);
|
MultiQueueItem *item = multiqueue_node_data(h);
|
||||||
Event rv;
|
assert(!item->link || !this->parent); // Only a parent queue has link-nodes
|
||||||
|
Event ev = multiqueueitem_get_event(item, true);
|
||||||
if (item->link) {
|
|
||||||
assert(!this->parent);
|
|
||||||
// remove the next node in the linked queue
|
|
||||||
MultiQueue *linked = item->data.queue;
|
|
||||||
assert(!multiqueue_empty(linked));
|
|
||||||
MultiQueueItem *child =
|
|
||||||
multiqueue_node_data(QUEUE_HEAD(&linked->headtail));
|
|
||||||
QUEUE_REMOVE(&child->node);
|
|
||||||
rv = child->data.item.event;
|
|
||||||
xfree(child);
|
|
||||||
} else {
|
|
||||||
if (this->parent) {
|
|
||||||
// remove the corresponding link node in the parent queue
|
|
||||||
QUEUE_REMOVE(&item->data.item.parent_item->node);
|
|
||||||
xfree(item->data.item.parent_item);
|
|
||||||
}
|
|
||||||
rv = item->data.item.event;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->size--;
|
this->size--;
|
||||||
xfree(item);
|
xfree(item);
|
||||||
return rv;
|
return ev;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void multiqueue_push(MultiQueue *this, Event event)
|
static void multiqueue_push(MultiQueue *this, Event event)
|
||||||
@ -215,6 +228,7 @@ static void multiqueue_push(MultiQueue *this, Event event)
|
|||||||
MultiQueueItem *item = xmalloc(sizeof(MultiQueueItem));
|
MultiQueueItem *item = xmalloc(sizeof(MultiQueueItem));
|
||||||
item->link = false;
|
item->link = false;
|
||||||
item->data.item.event = event;
|
item->data.item.event = event;
|
||||||
|
item->data.item.parent_item = NULL;
|
||||||
QUEUE_INSERT_TAIL(&this->headtail, &item->node);
|
QUEUE_INSERT_TAIL(&this->headtail, &item->node);
|
||||||
if (this->parent) {
|
if (this->parent) {
|
||||||
// push link node to the parent queue
|
// push link node to the parent queue
|
||||||
|
@ -10,7 +10,7 @@ typedef struct multiqueue MultiQueue;
|
|||||||
typedef void (*put_callback)(MultiQueue *multiq, void *data);
|
typedef void (*put_callback)(MultiQueue *multiq, void *data);
|
||||||
|
|
||||||
#define multiqueue_put(q, h, ...) \
|
#define multiqueue_put(q, h, ...) \
|
||||||
multiqueue_put_event(q, event_create(1, h, __VA_ARGS__));
|
multiqueue_put_event(q, event_create(h, __VA_ARGS__));
|
||||||
|
|
||||||
|
|
||||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||||
|
@ -345,10 +345,6 @@ char *xstpcpy(char *restrict dst, const char *restrict src)
|
|||||||
/// WARNING: xstpncpy will ALWAYS write maxlen bytes. If src is shorter than
|
/// WARNING: xstpncpy will ALWAYS write maxlen bytes. If src is shorter than
|
||||||
/// maxlen, zeroes will be written to the remaining bytes.
|
/// maxlen, zeroes will be written to the remaining bytes.
|
||||||
///
|
///
|
||||||
/// TODO(aktau): I don't see a good reason to have this last behaviour, and
|
|
||||||
/// it is potentially wasteful. Could we perhaps deviate from the standard
|
|
||||||
/// and not zero the rest of the buffer?
|
|
||||||
///
|
|
||||||
/// @param dst
|
/// @param dst
|
||||||
/// @param src
|
/// @param src
|
||||||
/// @param maxlen
|
/// @param maxlen
|
||||||
|
@ -604,7 +604,7 @@ void msg_schedule_emsgf(const char *const fmt, ...)
|
|||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
char *s = xstrdup((char *)IObuff);
|
char *s = xstrdup((char *)IObuff);
|
||||||
loop_schedule(&main_loop, event_create(1, msg_emsgf_event, 1, s));
|
loop_schedule(&main_loop, event_create(msg_emsgf_event, 1, s));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
#include "nvim/log.h"
|
#include "nvim/log.h"
|
||||||
#include "nvim/misc1.h"
|
#include "nvim/misc1.h"
|
||||||
#include "nvim/lib/kvec.h"
|
#include "nvim/lib/kvec.h"
|
||||||
|
#include "nvim/os/input.h"
|
||||||
|
|
||||||
#define CHANNEL_BUFFER_SIZE 0xffff
|
#define CHANNEL_BUFFER_SIZE 0xffff
|
||||||
|
|
||||||
@ -89,6 +90,7 @@ static msgpack_sbuffer out_buffer;
|
|||||||
/// Initializes the module
|
/// Initializes the module
|
||||||
void channel_init(void)
|
void channel_init(void)
|
||||||
{
|
{
|
||||||
|
ch_before_blocking_events = multiqueue_new_child(main_loop.events);
|
||||||
channels = pmap_new(uint64_t)();
|
channels = pmap_new(uint64_t)();
|
||||||
event_strings = pmap_new(cstr_t)();
|
event_strings = pmap_new(cstr_t)();
|
||||||
msgpack_sbuffer_init(&out_buffer);
|
msgpack_sbuffer_init(&out_buffer);
|
||||||
@ -433,16 +435,25 @@ static void handle_request(Channel *channel, msgpack_object *request)
|
|||||||
handler.async = true;
|
handler.async = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
RequestEvent *event_data = xmalloc(sizeof(RequestEvent));
|
RequestEvent *evdata = xmalloc(sizeof(RequestEvent));
|
||||||
event_data->channel = channel;
|
evdata->channel = channel;
|
||||||
event_data->handler = handler;
|
evdata->handler = handler;
|
||||||
event_data->args = args;
|
evdata->args = args;
|
||||||
event_data->request_id = request_id;
|
evdata->request_id = request_id;
|
||||||
incref(channel);
|
incref(channel);
|
||||||
if (handler.async) {
|
if (handler.async) {
|
||||||
on_request_event((void **)&event_data);
|
bool is_get_mode = sizeof("nvim_get_mode") - 1 == method->via.bin.size
|
||||||
|
&& !strncmp("nvim_get_mode", method->via.bin.ptr, method->via.bin.size);
|
||||||
|
|
||||||
|
if (is_get_mode && !input_blocking()) {
|
||||||
|
// Defer the event to a special queue used by os/input.c. #6247
|
||||||
|
multiqueue_put(ch_before_blocking_events, on_request_event, 1, evdata);
|
||||||
|
} else {
|
||||||
|
// Invoke immediately.
|
||||||
|
on_request_event((void **)&evdata);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
multiqueue_put(channel->events, on_request_event, 1, event_data);
|
multiqueue_put(channel->events, on_request_event, 1, evdata);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,6 +11,11 @@
|
|||||||
|
|
||||||
#define METHOD_MAXLEN 512
|
#define METHOD_MAXLEN 512
|
||||||
|
|
||||||
|
/// HACK: os/input.c drains this queue immediately before blocking for input.
|
||||||
|
/// Events on this queue are async-safe, but they need the resolved state
|
||||||
|
/// of os_inchar(), so they are processed "just-in-time".
|
||||||
|
MultiQueue *ch_before_blocking_events;
|
||||||
|
|
||||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||||
# include "msgpack_rpc/channel.h.generated.h"
|
# include "msgpack_rpc/channel.h.generated.h"
|
||||||
#endif
|
#endif
|
||||||
|
@ -76,7 +76,7 @@ typedef struct {
|
|||||||
size_t idx;
|
size_t idx;
|
||||||
} MPToAPIObjectStackItem;
|
} MPToAPIObjectStackItem;
|
||||||
|
|
||||||
/// Convert type used by msgpack parser to Neovim own API type
|
/// Convert type used by msgpack parser to Nvim API type.
|
||||||
///
|
///
|
||||||
/// @param[in] obj Msgpack value to convert.
|
/// @param[in] obj Msgpack value to convert.
|
||||||
/// @param[out] arg Location where result of conversion will be saved.
|
/// @param[out] arg Location where result of conversion will be saved.
|
||||||
|
@ -541,7 +541,7 @@ static bool normal_handle_special_visual_command(NormalState *s)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool normal_need_aditional_char(NormalState *s)
|
static bool normal_need_additional_char(NormalState *s)
|
||||||
{
|
{
|
||||||
int flags = nv_cmds[s->idx].cmd_flags;
|
int flags = nv_cmds[s->idx].cmd_flags;
|
||||||
bool pending_op = s->oa.op_type != OP_NOP;
|
bool pending_op = s->oa.op_type != OP_NOP;
|
||||||
@ -1083,7 +1083,7 @@ static int normal_execute(VimState *state, int key)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get an additional character if we need one.
|
// Get an additional character if we need one.
|
||||||
if (normal_need_aditional_char(s)) {
|
if (normal_need_additional_char(s)) {
|
||||||
normal_get_additional_char(s);
|
normal_get_additional_char(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#include "nvim/main.h"
|
#include "nvim/main.h"
|
||||||
#include "nvim/misc1.h"
|
#include "nvim/misc1.h"
|
||||||
#include "nvim/state.h"
|
#include "nvim/state.h"
|
||||||
|
#include "nvim/msgpack_rpc/channel.h"
|
||||||
|
|
||||||
#define READ_BUFFER_SIZE 0xfff
|
#define READ_BUFFER_SIZE 0xfff
|
||||||
#define INPUT_BUFFER_SIZE (READ_BUFFER_SIZE * 4)
|
#define INPUT_BUFFER_SIZE (READ_BUFFER_SIZE * 4)
|
||||||
@ -38,6 +39,7 @@ static RBuffer *input_buffer = NULL;
|
|||||||
static bool input_eof = false;
|
static bool input_eof = false;
|
||||||
static int global_fd = 0;
|
static int global_fd = 0;
|
||||||
static int events_enabled = 0;
|
static int events_enabled = 0;
|
||||||
|
static bool blocking = false;
|
||||||
|
|
||||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||||
# include "os/input.c.generated.h"
|
# include "os/input.c.generated.h"
|
||||||
@ -327,13 +329,25 @@ static unsigned int handle_mouse_event(char **ptr, uint8_t *buf,
|
|||||||
return bufsize;
|
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)
|
static bool input_poll(int ms)
|
||||||
{
|
{
|
||||||
if (do_profiling == PROF_YES && ms) {
|
if (do_profiling == PROF_YES && ms) {
|
||||||
prof_inchar_enter();
|
prof_inchar_enter();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((ms == - 1 || ms > 0) && !events_enabled && !input_eof) {
|
||||||
|
// The pending input provoked a blocking wait. Do special events now. #6247
|
||||||
|
blocking = true;
|
||||||
|
multiqueue_process_events(ch_before_blocking_events);
|
||||||
|
}
|
||||||
LOOP_PROCESS_EVENTS_UNTIL(&main_loop, NULL, ms, input_ready() || input_eof);
|
LOOP_PROCESS_EVENTS_UNTIL(&main_loop, NULL, ms, input_ready() || input_eof);
|
||||||
|
blocking = false;
|
||||||
|
|
||||||
if (do_profiling == PROF_YES && ms) {
|
if (do_profiling == PROF_YES && ms) {
|
||||||
prof_inchar_exit();
|
prof_inchar_exit();
|
||||||
|
@ -98,3 +98,52 @@ int get_real_state(void)
|
|||||||
return State;
|
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;
|
||||||
|
}
|
||||||
|
@ -102,7 +102,7 @@ static void flush_input(TermInput *input, bool wait_until_empty)
|
|||||||
size_t drain_boundary = wait_until_empty ? 0 : 0xff;
|
size_t drain_boundary = wait_until_empty ? 0 : 0xff;
|
||||||
do {
|
do {
|
||||||
uv_mutex_lock(&input->key_buffer_mutex);
|
uv_mutex_lock(&input->key_buffer_mutex);
|
||||||
loop_schedule(&main_loop, event_create(1, wait_input_enqueue, 1, input));
|
loop_schedule(&main_loop, event_create(wait_input_enqueue, 1, input));
|
||||||
input->waiting = true;
|
input->waiting = true;
|
||||||
while (input->waiting) {
|
while (input->waiting) {
|
||||||
uv_cond_wait(&input->key_buffer_cond, &input->key_buffer_mutex);
|
uv_cond_wait(&input->key_buffer_cond, &input->key_buffer_mutex);
|
||||||
@ -352,7 +352,7 @@ static void read_cb(Stream *stream, RBuffer *buf, size_t c, void *data,
|
|||||||
stream_close(&input->read_stream, NULL, NULL);
|
stream_close(&input->read_stream, NULL, NULL);
|
||||||
multiqueue_put(input->loop->fast_events, restart_reading, 1, input);
|
multiqueue_put(input->loop->fast_events, restart_reading, 1, input);
|
||||||
} else {
|
} else {
|
||||||
loop_schedule(&main_loop, event_create(1, input_done_event, 0));
|
loop_schedule(&main_loop, event_create(input_done_event, 0));
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -74,9 +74,6 @@ typedef struct {
|
|||||||
bool out_isatty;
|
bool out_isatty;
|
||||||
SignalWatcher winch_handle, cont_handle;
|
SignalWatcher winch_handle, cont_handle;
|
||||||
bool cont_received;
|
bool cont_received;
|
||||||
// Event scheduled by the ui bridge. Since the main thread suspends until
|
|
||||||
// the event is handled, it is fine to use a single field instead of a queue
|
|
||||||
Event scheduled_event;
|
|
||||||
UGrid grid;
|
UGrid grid;
|
||||||
kvec_t(Rect) invalid_regions;
|
kvec_t(Rect) invalid_regions;
|
||||||
int out_fd;
|
int out_fd;
|
||||||
@ -776,7 +773,7 @@ static void tui_suspend(UI *ui)
|
|||||||
// before continuing. This is done in another callback to avoid
|
// before continuing. This is done in another callback to avoid
|
||||||
// loop_poll_events recursion
|
// loop_poll_events recursion
|
||||||
multiqueue_put_event(data->loop->fast_events,
|
multiqueue_put_event(data->loop->fast_events,
|
||||||
event_create(1, suspend_event, 1, ui));
|
event_create(suspend_event, 1, ui));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tui_set_title(UI *ui, char *title)
|
static void tui_set_title(UI *ui, char *title)
|
||||||
|
@ -198,7 +198,7 @@ static void ui_refresh_event(void **argv)
|
|||||||
|
|
||||||
void ui_schedule_refresh(void)
|
void ui_schedule_refresh(void)
|
||||||
{
|
{
|
||||||
loop_schedule(&main_loop, event_create(1, ui_refresh_event, 0));
|
loop_schedule(&main_loop, event_create(ui_refresh_event, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ui_resize(int new_width, int new_height)
|
void ui_resize(int new_width, int new_height)
|
||||||
|
@ -40,13 +40,13 @@ static argv_callback uilog_event = NULL;
|
|||||||
uilog_event = ui_bridge_##name##_event; \
|
uilog_event = ui_bridge_##name##_event; \
|
||||||
} \
|
} \
|
||||||
((UIBridgeData *)ui)->scheduler( \
|
((UIBridgeData *)ui)->scheduler( \
|
||||||
event_create(1, ui_bridge_##name##_event, argc, __VA_ARGS__), UI(ui)); \
|
event_create(ui_bridge_##name##_event, argc, __VA_ARGS__), UI(ui)); \
|
||||||
} while (0)
|
} while (0)
|
||||||
#else
|
#else
|
||||||
// Schedule a function call on the UI bridge thread.
|
// Schedule a function call on the UI bridge thread.
|
||||||
#define UI_CALL(ui, name, argc, ...) \
|
#define UI_CALL(ui, name, argc, ...) \
|
||||||
((UIBridgeData *)ui)->scheduler( \
|
((UIBridgeData *)ui)->scheduler( \
|
||||||
event_create(1, ui_bridge_##name##_event, argc, __VA_ARGS__), UI(ui))
|
event_create(ui_bridge_##name##_event, argc, __VA_ARGS__), UI(ui))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define INT2PTR(i) ((void *)(uintptr_t)i)
|
#define INT2PTR(i) ((void *)(uintptr_t)i)
|
||||||
|
@ -221,6 +221,102 @@ describe('api', function()
|
|||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
describe('nvim_get_mode', function()
|
||||||
|
it("during normal-mode `g` returns blocking=true", function()
|
||||||
|
nvim("input", "o") -- add a line
|
||||||
|
eq({mode='i', blocking=false}, nvim("get_mode"))
|
||||||
|
nvim("input", [[<C-\><C-N>]])
|
||||||
|
eq(2, nvim("eval", "line('.')"))
|
||||||
|
eq({mode='n', blocking=false}, nvim("get_mode"))
|
||||||
|
|
||||||
|
nvim("input", "g")
|
||||||
|
eq({mode='n', blocking=true}, nvim("get_mode"))
|
||||||
|
|
||||||
|
nvim("input", "k") -- complete the operator
|
||||||
|
eq(1, nvim("eval", "line('.')")) -- verify the completed operator
|
||||||
|
eq({mode='n', blocking=false}, nvim("get_mode"))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("returns the correct result multiple consecutive times", function()
|
||||||
|
for _ = 1,5 do
|
||||||
|
eq({mode='n', blocking=false}, nvim("get_mode"))
|
||||||
|
end
|
||||||
|
nvim("input", "g")
|
||||||
|
for _ = 1,4 do
|
||||||
|
eq({mode='n', blocking=true}, nvim("get_mode"))
|
||||||
|
end
|
||||||
|
nvim("input", "g")
|
||||||
|
for _ = 1,7 do
|
||||||
|
eq({mode='n', blocking=false}, nvim("get_mode"))
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("during normal-mode CTRL-W, returns blocking=true", function()
|
||||||
|
nvim("input", "<C-W>")
|
||||||
|
eq({mode='n', blocking=true}, nvim("get_mode"))
|
||||||
|
|
||||||
|
nvim("input", "s") -- complete the operator
|
||||||
|
eq(2, nvim("eval", "winnr('$')")) -- verify the completed operator
|
||||||
|
eq({mode='n', blocking=false}, nvim("get_mode"))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("during press-enter prompt returns blocking=true", function()
|
||||||
|
eq({mode='n', blocking=false}, nvim("get_mode"))
|
||||||
|
command("echom 'msg1'")
|
||||||
|
command("echom 'msg2'")
|
||||||
|
command("echom 'msg3'")
|
||||||
|
command("echom 'msg4'")
|
||||||
|
command("echom 'msg5'")
|
||||||
|
eq({mode='n', blocking=false}, nvim("get_mode"))
|
||||||
|
nvim("input", ":messages<CR>")
|
||||||
|
eq({mode='r', blocking=true}, nvim("get_mode"))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("during getchar() returns blocking=false", function()
|
||||||
|
nvim("input", ":let g:test_input = nr2char(getchar())<CR>")
|
||||||
|
-- Events are enabled during getchar(), RPC calls are *not* blocked. #5384
|
||||||
|
eq({mode='n', blocking=false}, nvim("get_mode"))
|
||||||
|
eq(0, nvim("eval", "exists('g:test_input')"))
|
||||||
|
nvim("input", "J")
|
||||||
|
eq("J", nvim("eval", "g:test_input"))
|
||||||
|
eq({mode='n', blocking=false}, nvim("get_mode"))
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- TODO: bug #6247#issuecomment-286403810
|
||||||
|
it("batched with input", function()
|
||||||
|
eq({mode='n', blocking=false}, nvim("get_mode"))
|
||||||
|
command("echom 'msg1'")
|
||||||
|
command("echom 'msg2'")
|
||||||
|
command("echom 'msg3'")
|
||||||
|
command("echom 'msg4'")
|
||||||
|
command("echom 'msg5'")
|
||||||
|
|
||||||
|
local req = {
|
||||||
|
{'nvim_get_mode', {}},
|
||||||
|
{'nvim_input', {':messages<CR>'}},
|
||||||
|
{'nvim_get_mode', {}},
|
||||||
|
{'nvim_eval', {'1'}},
|
||||||
|
}
|
||||||
|
eq({{{mode='n', blocking=false},
|
||||||
|
13,
|
||||||
|
{mode='n', blocking=false}, -- TODO: should be blocked=true
|
||||||
|
1},
|
||||||
|
NIL}, meths.call_atomic(req))
|
||||||
|
eq({mode='r', blocking=true}, nvim("get_mode"))
|
||||||
|
end)
|
||||||
|
-- TODO: bug #6166
|
||||||
|
it("during insert-mode map-pending, returns blocking=true #6166", function()
|
||||||
|
command("inoremap xx foo")
|
||||||
|
nvim("input", "ix")
|
||||||
|
eq({mode='i', blocking=true}, nvim("get_mode"))
|
||||||
|
end)
|
||||||
|
-- TODO: bug #6166
|
||||||
|
it("during normal-mode gU, returns blocking=false #6166", function()
|
||||||
|
nvim("input", "gu")
|
||||||
|
eq({mode='no', blocking=false}, nvim("get_mode"))
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
describe('nvim_replace_termcodes', function()
|
describe('nvim_replace_termcodes', function()
|
||||||
it('escapes K_SPECIAL as K_SPECIAL KS_SPECIAL KE_FILLER', function()
|
it('escapes K_SPECIAL as K_SPECIAL KS_SPECIAL KE_FILLER', function()
|
||||||
eq('\128\254X', helpers.nvim('replace_termcodes', '\128', true, true, true))
|
eq('\128\254X', helpers.nvim('replace_termcodes', '\128', true, true, true))
|
||||||
@ -459,7 +555,7 @@ describe('api', function()
|
|||||||
eq(very_long_name, err:match('Ax+Z?'))
|
eq(very_long_name, err:match('Ax+Z?'))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it("doesn't leak memory on incorrect argument types", function()
|
it("does not leak memory on incorrect argument types", function()
|
||||||
local status, err = pcall(nvim, 'set_current_dir',{'not', 'a', 'dir'})
|
local status, err = pcall(nvim, 'set_current_dir',{'not', 'a', 'dir'})
|
||||||
eq(false, status)
|
eq(false, status)
|
||||||
ok(err:match(': Wrong type for argument 1, expecting String') ~= nil)
|
ok(err:match(': Wrong type for argument 1, expecting String') ~= nil)
|
||||||
|
@ -385,9 +385,9 @@ local function curbuf(method, ...)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function wait()
|
local function wait()
|
||||||
-- Execute 'vim_eval' (a deferred function) to block
|
-- Execute 'nvim_eval' (a deferred function) to block
|
||||||
-- until all pending input is processed.
|
-- until all pending input is processed.
|
||||||
session:request('vim_eval', '1')
|
session:request('nvim_eval', '1')
|
||||||
end
|
end
|
||||||
|
|
||||||
-- sleeps the test runner (_not_ the nvim instance)
|
-- sleeps the test runner (_not_ the nvim instance)
|
||||||
|
Loading…
Reference in New Issue
Block a user