diff options
author | Justin M. Keyes <justinkz@gmail.com> | 2017-04-28 20:37:52 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-04-28 20:37:52 +0200 |
commit | 129f107c0c26fbf371bcf165ec20eb13356bfb8e (patch) | |
tree | 864bf25c65c6cfcd4e17c0ebd04afdad1342c268 /src | |
parent | 7044aa6e8256844bc1bd23eb61d4a41ca6d418d0 (diff) | |
parent | 8f59d1483934f91011b755406251136c406e77f6 (diff) | |
download | rneovim-129f107c0c26fbf371bcf165ec20eb13356bfb8e.tar.gz rneovim-129f107c0c26fbf371bcf165ec20eb13356bfb8e.tar.bz2 rneovim-129f107c0c26fbf371bcf165ec20eb13356bfb8e.zip |
Merge #6247 'api: nvim_get_mode()'
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/api/vim.c | 19 | ||||
-rw-r--r-- | src/nvim/eval.c | 55 | ||||
-rw-r--r-- | src/nvim/event/defs.h | 8 | ||||
-rw-r--r-- | src/nvim/event/loop.c | 3 | ||||
-rw-r--r-- | src/nvim/event/multiqueue.c | 48 | ||||
-rw-r--r-- | src/nvim/event/multiqueue.h | 2 | ||||
-rw-r--r-- | src/nvim/memory.c | 4 | ||||
-rw-r--r-- | src/nvim/message.c | 2 | ||||
-rw-r--r-- | src/nvim/msgpack_rpc/channel.c | 25 | ||||
-rw-r--r-- | src/nvim/msgpack_rpc/channel.h | 5 | ||||
-rw-r--r-- | src/nvim/msgpack_rpc/helpers.c | 2 | ||||
-rw-r--r-- | src/nvim/normal.c | 4 | ||||
-rw-r--r-- | src/nvim/os/input.c | 14 | ||||
-rw-r--r-- | src/nvim/state.c | 49 | ||||
-rw-r--r-- | src/nvim/tui/input.c | 4 | ||||
-rw-r--r-- | src/nvim/tui/tui.c | 5 | ||||
-rw-r--r-- | src/nvim/ui.c | 2 | ||||
-rw-r--r-- | src/nvim/ui_bridge.c | 4 |
18 files changed, 158 insertions, 97 deletions
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index da00fbc6e3..11f15b5ad1 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -27,6 +27,7 @@ #include "nvim/eval.h" #include "nvim/eval/typval.h" #include "nvim/option.h" +#include "nvim/state.h" #include "nvim/syntax.h" #include "nvim/getchar.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) FUNC_API_SINCE(1) FUNC_API_ASYNC FUNC_API_NOEVAL { diff --git a/src/nvim/eval.c b/src/nvim/eval.c index c02d172458..b0f47d8e45 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -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) { - char_u buf[3]; + char *mode = get_mode(); - buf[1] = NUL; - buf[2] = NUL; - - if (VIsual_active) { - 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 + // non-empty string. + if (!non_zero_arg(&argvars[0])) { + mode[1] = NUL; } - /* Clear out the minor mode when the argument is not a non-zero number or - * non-empty string. */ - if (!non_zero_arg(&argvars[0])) - buf[1] = NUL; - - rettv->vval.v_string = vim_strsave(buf); + rettv->vval.v_string = (char_u *)mode; rettv->v_type = VAR_STRING; } diff --git a/src/nvim/event/defs.h b/src/nvim/event/defs.h index e5335d9f25..cc875d74b9 100644 --- a/src/nvim/event/defs.h +++ b/src/nvim/event/defs.h @@ -8,16 +8,14 @@ typedef void (*argv_callback)(void **argv); typedef struct message { - int priority; argv_callback handler; void *argv[EVENT_HANDLER_MAX_ARGC]; } Event; typedef void(*event_scheduler)(Event event, void *data); -#define VA_EVENT_INIT(event, p, h, a) \ +#define VA_EVENT_INIT(event, h, a) \ do { \ assert(a <= EVENT_HANDLER_MAX_ARGC); \ - (event)->priority = p; \ (event)->handler = h; \ if (a) { \ va_list args; \ @@ -29,11 +27,11 @@ typedef void(*event_scheduler)(Event event, void *data); } \ } 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); Event event; - VA_EVENT_INIT(&event, priority, cb, argc); + VA_EVENT_INIT(&event, cb, argc); return event; } diff --git a/src/nvim/event/loop.c b/src/nvim/event/loop.c index 6963978581..c709ce9a1c 100644 --- a/src/nvim/event/loop.c +++ b/src/nvim/event/loop.c @@ -44,8 +44,7 @@ void loop_poll_events(Loop *loop, int ms) // we do not block indefinitely for I/O. uv_timer_start(&loop->poll_timer, timer_cb, (uint64_t)ms, (uint64_t)ms); } else if (ms == 0) { - // For ms == 0, we need to do a non-blocking event poll by - // setting the run mode to UV_RUN_NOWAIT. + // For ms == 0, do a non-blocking event poll. mode = UV_RUN_NOWAIT; } diff --git a/src/nvim/event/multiqueue.c b/src/nvim/event/multiqueue.c index a17bae31e3..ef9f3f1870 100644 --- a/src/nvim/event/multiqueue.c +++ b/src/nvim/event/multiqueue.c @@ -126,6 +126,7 @@ void multiqueue_free(MultiQueue *this) xfree(this); } +/// Removes the next item and returns its Event. Event multiqueue_get(MultiQueue *this) { return multiqueue_empty(this) ? NILEVENT : multiqueue_remove(this); @@ -144,7 +145,7 @@ void multiqueue_process_events(MultiQueue *this) { assert(this); while (!multiqueue_empty(this)) { - Event event = multiqueue_get(this); + Event event = multiqueue_remove(this); if (event.handler) { event.handler(event.argv); } @@ -178,36 +179,48 @@ size_t multiqueue_size(MultiQueue *this) return this->size; } -static Event multiqueue_remove(MultiQueue *this) +/// 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(!multiqueue_empty(this)); - QUEUE *h = QUEUE_HEAD(&this->headtail); - QUEUE_REMOVE(h); - MultiQueueItem *item = multiqueue_node_data(h); - Event rv; - + assert(item != NULL); + Event ev; if (item->link) { - assert(!this->parent); - // remove the next node in the linked queue + // 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)); - QUEUE_REMOVE(&child->node); - rv = child->data.item.event; - xfree(child); + ev = child->data.item.event; + // remove the child node + if (remove) { + QUEUE_REMOVE(&child->node); + xfree(child); + } } else { - if (this->parent) { - // remove the corresponding link node in the parent queue + // 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; } - rv = item->data.item.event; + ev = item->data.item.event; } + return ev; +} +static Event multiqueue_remove(MultiQueue *this) +{ + assert(!multiqueue_empty(this)); + QUEUE *h = QUEUE_HEAD(&this->headtail); + QUEUE_REMOVE(h); + MultiQueueItem *item = multiqueue_node_data(h); + assert(!item->link || !this->parent); // Only a parent queue has link-nodes + Event ev = multiqueueitem_get_event(item, true); this->size--; xfree(item); - return rv; + return ev; } 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)); item->link = false; item->data.item.event = event; + item->data.item.parent_item = NULL; QUEUE_INSERT_TAIL(&this->headtail, &item->node); if (this->parent) { // push link node to the parent queue diff --git a/src/nvim/event/multiqueue.h b/src/nvim/event/multiqueue.h index def6b95a10..a688107665 100644 --- a/src/nvim/event/multiqueue.h +++ b/src/nvim/event/multiqueue.h @@ -10,7 +10,7 @@ typedef struct multiqueue MultiQueue; typedef void (*put_callback)(MultiQueue *multiq, void *data); #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 diff --git a/src/nvim/memory.c b/src/nvim/memory.c index 0ee4776581..74c58fb203 100644 --- a/src/nvim/memory.c +++ b/src/nvim/memory.c @@ -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 /// 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 src /// @param maxlen diff --git a/src/nvim/message.c b/src/nvim/message.c index 146937c25a..696855e3aa 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -604,7 +604,7 @@ void msg_schedule_emsgf(const char *const fmt, ...) va_end(ap); 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)); } /* diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index 59594357de..911f2a6fa4 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -29,6 +29,7 @@ #include "nvim/log.h" #include "nvim/misc1.h" #include "nvim/lib/kvec.h" +#include "nvim/os/input.h" #define CHANNEL_BUFFER_SIZE 0xffff @@ -89,6 +90,7 @@ static msgpack_sbuffer out_buffer; /// Initializes the module void channel_init(void) { + ch_before_blocking_events = multiqueue_new_child(main_loop.events); channels = pmap_new(uint64_t)(); event_strings = pmap_new(cstr_t)(); msgpack_sbuffer_init(&out_buffer); @@ -433,16 +435,25 @@ static void handle_request(Channel *channel, msgpack_object *request) handler.async = true; } - RequestEvent *event_data = xmalloc(sizeof(RequestEvent)); - event_data->channel = channel; - event_data->handler = handler; - event_data->args = args; - event_data->request_id = request_id; + RequestEvent *evdata = xmalloc(sizeof(RequestEvent)); + evdata->channel = channel; + evdata->handler = handler; + evdata->args = args; + evdata->request_id = request_id; incref(channel); 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 { - multiqueue_put(channel->events, on_request_event, 1, event_data); + multiqueue_put(channel->events, on_request_event, 1, evdata); } } diff --git a/src/nvim/msgpack_rpc/channel.h b/src/nvim/msgpack_rpc/channel.h index 0d92976d02..f8fe6f129b 100644 --- a/src/nvim/msgpack_rpc/channel.h +++ b/src/nvim/msgpack_rpc/channel.h @@ -11,6 +11,11 @@ #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 # include "msgpack_rpc/channel.h.generated.h" #endif diff --git a/src/nvim/msgpack_rpc/helpers.c b/src/nvim/msgpack_rpc/helpers.c index 0228582d37..91ef5524ea 100644 --- a/src/nvim/msgpack_rpc/helpers.c +++ b/src/nvim/msgpack_rpc/helpers.c @@ -76,7 +76,7 @@ typedef struct { size_t idx; } 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[out] arg Location where result of conversion will be saved. diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 51da9429b6..f73e3079b9 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -541,7 +541,7 @@ static bool normal_handle_special_visual_command(NormalState *s) 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; 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. - if (normal_need_aditional_char(s)) { + if (normal_need_additional_char(s)) { normal_get_additional_char(s); } diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c index 7b5e14dd19..31e06ce404 100644 --- a/src/nvim/os/input.c +++ b/src/nvim/os/input.c @@ -23,6 +23,7 @@ #include "nvim/main.h" #include "nvim/misc1.h" #include "nvim/state.h" +#include "nvim/msgpack_rpc/channel.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,25 @@ 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_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); + blocking = false; if (do_profiling == PROF_YES && ms) { prof_inchar_exit(); diff --git a/src/nvim/state.c b/src/nvim/state.c index 210708c3f4..be6aa21664 100644 --- a/src/nvim/state.c +++ b/src/nvim/state.c @@ -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; +} diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c index b86ab8cf2f..3d37fabeee 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -102,7 +102,7 @@ static void flush_input(TermInput *input, bool wait_until_empty) size_t drain_boundary = wait_until_empty ? 0 : 0xff; do { 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; while (input->waiting) { 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); multiqueue_put(input->loop->fast_events, restart_reading, 1, input); } else { - loop_schedule(&main_loop, event_create(1, input_done_event, 0)); + loop_schedule(&main_loop, event_create(input_done_event, 0)); } return; } diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 21abc19c47..5653924154 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -74,9 +74,6 @@ typedef struct { bool out_isatty; SignalWatcher winch_handle, cont_handle; 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; kvec_t(Rect) invalid_regions; int out_fd; @@ -776,7 +773,7 @@ static void tui_suspend(UI *ui) // before continuing. This is done in another callback to avoid // loop_poll_events recursion 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) diff --git a/src/nvim/ui.c b/src/nvim/ui.c index 713dffb46c..924a4192bc 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -198,7 +198,7 @@ static void ui_refresh_event(void **argv) 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) diff --git a/src/nvim/ui_bridge.c b/src/nvim/ui_bridge.c index b7b12ae39e..d790770892 100644 --- a/src/nvim/ui_bridge.c +++ b/src/nvim/ui_bridge.c @@ -40,13 +40,13 @@ static argv_callback uilog_event = NULL; uilog_event = ui_bridge_##name##_event; \ } \ ((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) #else // Schedule a function call on the UI bridge thread. #define UI_CALL(ui, name, argc, ...) \ ((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 #define INT2PTR(i) ((void *)(uintptr_t)i) |