diff options
Diffstat (limited to 'src')
36 files changed, 552 insertions, 215 deletions
diff --git a/src/.asan-blacklist b/src/.asan-blacklist index 63558170b3..7636f8fa82 100644 --- a/src/.asan-blacklist +++ b/src/.asan-blacklist @@ -1,3 +1,3 @@ -# libuv queue.h pointer arithmetic is not accepted by asan -fun:queue_node_data +# multiqueue.h pointer arithmetic is not accepted by asan +fun:multiqueue_node_data fun:dictwatcher_node_data diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index fc114bae16..d80ee7dc67 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -368,11 +368,10 @@ static inline void typval_encode_list_start(EncodedData *const edata, const size_t len) FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL { - const Object obj = OBJECT_INIT; kv_push(edata->stack, ARRAY_OBJ(((Array) { .capacity = len, .size = 0, - .items = xmalloc(len * sizeof(*obj.data.array.items)), + .items = xmalloc(len * sizeof(*((Object *)NULL)->data.array.items)), }))); } @@ -409,11 +408,10 @@ static inline void typval_encode_dict_start(EncodedData *const edata, const size_t len) FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL { - const Object obj = OBJECT_INIT; kv_push(edata->stack, DICTIONARY_OBJ(((Dictionary) { .capacity = len, .size = 0, - .items = xmalloc(len * sizeof(*obj.data.dictionary.items)), + .items = xmalloc(len * sizeof(*((Object *)NULL)->data.dictionary.items)), }))); } diff --git a/src/nvim/edit.c b/src/nvim/edit.c index ca5b6f02ce..892748ff5c 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -962,7 +962,7 @@ static int insert_handle_key(InsertState *s) break; case K_EVENT: // some event - queue_process_events(main_loop.events); + multiqueue_process_events(main_loop.events); break; case K_FOCUSGAINED: // Neovim has been given focus diff --git a/src/nvim/eval.c b/src/nvim/eval.c index b05b4c4f7e..0b1fd6670e 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -415,7 +415,7 @@ typedef struct { dict_T *self; int *status_ptr; uint64_t id; - Queue *events; + MultiQueue *events; } TerminalJobData; typedef struct dict_watcher { @@ -11692,7 +11692,7 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) list_T *rv = list_alloc(); ui_busy_start(); - Queue *waiting_jobs = queue_new_parent(loop_on_put, &main_loop); + MultiQueue *waiting_jobs = multiqueue_new_parent(loop_on_put, &main_loop); // For each item in the input list append an integer to the output list. -3 // is used to represent an invalid job id, -2 is for a interrupted job and // -1 for jobs that were skipped or timed out. @@ -11708,8 +11708,8 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) data->status_ptr = &rv->lv_last->li_tv.vval.v_number; // Process any pending events for the job because we'll temporarily // replace the parent queue - queue_process_events(data->events); - queue_replace_parent(data->events, waiting_jobs); + multiqueue_process_events(data->events); + multiqueue_replace_parent(data->events, waiting_jobs); } } @@ -11769,11 +11769,11 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) continue; } // restore the parent queue for the job - queue_process_events(data->events); - queue_replace_parent(data->events, main_loop.events); + multiqueue_process_events(data->events); + multiqueue_replace_parent(data->events, main_loop.events); } - queue_free(waiting_jobs); + multiqueue_free(waiting_jobs); ui_busy_stop(); rv->lv_refcount++; rettv->v_type = VAR_LIST; @@ -16400,7 +16400,7 @@ static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr) timer->callback = func; time_watcher_init(&main_loop, &timer->tw, timer); - timer->tw.events = queue_new_child(main_loop.events); + timer->tw.events = multiqueue_new_child(main_loop.events); // if main loop is blocked, don't queue up multiple events timer->tw.blockable = true; time_watcher_start(&timer->tw, timer_due_cb, timeout, @@ -16477,7 +16477,7 @@ static void timer_stop(timer_T *timer) static void timer_close_cb(TimeWatcher *tw, void *data) { timer_T *timer = (timer_T *)data; - queue_free(timer->tw.events); + multiqueue_free(timer->tw.events); user_func_unref(timer->callback); pmap_del(uint64_t)(timers, timer->timer_id); timer_decref(timer); @@ -21725,7 +21725,7 @@ static inline TerminalJobData *common_job_init(char **argv, data->on_stderr = on_stderr; data->on_exit = on_exit; data->self = self; - data->events = queue_new_child(main_loop.events); + data->events = multiqueue_new_child(main_loop.events); data->rpc = rpc; if (pty) { data->proc.pty = pty_process_init(&main_loop, data); @@ -21834,7 +21834,7 @@ static inline void free_term_job_data_event(void **argv) if (data->self) { dict_unref(data->self); } - queue_free(data->events); + multiqueue_free(data->events); pmap_del(uint64_t)(jobs, data->id); xfree(data); } @@ -21843,7 +21843,7 @@ static inline void free_term_job_data(TerminalJobData *data) { // data->queue may still be used after this function returns(process_wait), so // only free in the next event loop iteration - queue_put(main_loop.fast_events, free_term_job_data_event, 1, data); + multiqueue_put(main_loop.fast_events, free_term_job_data_event, 1, data); } // vimscript job callbacks must be executed on Nvim main loop @@ -21962,7 +21962,7 @@ static inline void term_delayed_free(void **argv) { TerminalJobData *j = argv[0]; if (j->in.pending_reqs || j->out.pending_reqs || j->err.pending_reqs) { - queue_put(j->events, term_delayed_free, 1, j); + multiqueue_put(j->events, term_delayed_free, 1, j); return; } @@ -21977,7 +21977,7 @@ static void term_close(void *d) data->exited = true; process_stop((Process *)&data->proc); } - queue_put(data->events, term_delayed_free, 1, data); + multiqueue_put(data->events, term_delayed_free, 1, data); } static void term_job_data_decref(TerminalJobData *data) diff --git a/src/nvim/event/loop.c b/src/nvim/event/loop.c index a4c4e5ac9c..d562ac1ed3 100644 --- a/src/nvim/event/loop.c +++ b/src/nvim/event/loop.c @@ -18,9 +18,9 @@ void loop_init(Loop *loop, void *data) loop->uv.data = loop; loop->children = kl_init(WatcherPtr); loop->children_stop_requests = 0; - loop->events = queue_new_parent(loop_on_put, loop); - loop->fast_events = queue_new_child(loop->events); - loop->thread_events = queue_new_parent(NULL, NULL); + loop->events = multiqueue_new_parent(loop_on_put, loop); + loop->fast_events = multiqueue_new_child(loop->events); + loop->thread_events = multiqueue_new_parent(NULL, NULL); uv_mutex_init(&loop->mutex); uv_async_init(&loop->uv, &loop->async, async_cb); uv_signal_init(&loop->uv, &loop->children_watcher); @@ -53,19 +53,19 @@ void loop_poll_events(Loop *loop, int ms) } loop->recursive--; // Can re-enter uv_run now - queue_process_events(loop->fast_events); + multiqueue_process_events(loop->fast_events); } // Schedule an event from another thread void loop_schedule(Loop *loop, Event event) { uv_mutex_lock(&loop->mutex); - queue_put_event(loop->thread_events, event); + multiqueue_put_event(loop->thread_events, event); uv_async_send(&loop->async); uv_mutex_unlock(&loop->mutex); } -void loop_on_put(Queue *queue, void *data) +void loop_on_put(MultiQueue *queue, void *data) { Loop *loop = data; // Sometimes libuv will run pending callbacks(timer for example) before @@ -86,9 +86,9 @@ void loop_close(Loop *loop, bool wait) do { uv_run(&loop->uv, wait ? UV_RUN_DEFAULT : UV_RUN_NOWAIT); } while (uv_loop_close(&loop->uv) && wait); - queue_free(loop->fast_events); - queue_free(loop->thread_events); - queue_free(loop->events); + multiqueue_free(loop->fast_events); + multiqueue_free(loop->thread_events); + multiqueue_free(loop->events); kl_destroy(WatcherPtr, loop->children); } @@ -96,9 +96,9 @@ static void async_cb(uv_async_t *handle) { Loop *l = handle->loop->data; uv_mutex_lock(&l->mutex); - while (!queue_empty(l->thread_events)) { - Event ev = queue_get(l->thread_events); - queue_put_event(l->fast_events, ev); + while (!multiqueue_empty(l->thread_events)) { + Event ev = multiqueue_get(l->thread_events); + multiqueue_put_event(l->fast_events, ev); } uv_mutex_unlock(&l->mutex); } diff --git a/src/nvim/event/loop.h b/src/nvim/event/loop.h index 407aa4245f..e7d7bdd483 100644 --- a/src/nvim/event/loop.h +++ b/src/nvim/event/loop.h @@ -7,7 +7,7 @@ #include "nvim/lib/klist.h" #include "nvim/os/time.h" -#include "nvim/event/queue.h" +#include "nvim/event/multiqueue.h" typedef void * WatcherPtr; @@ -16,7 +16,7 @@ KLIST_INIT(WatcherPtr, WatcherPtr, _noop) typedef struct loop { uv_loop_t uv; - Queue *events, *fast_events, *thread_events; + MultiQueue *events, *fast_events, *thread_events; klist_t(WatcherPtr) *children; uv_signal_t children_watcher; uv_timer_t children_kill_timer, poll_timer; @@ -26,10 +26,10 @@ typedef struct loop { int recursive; } Loop; -#define CREATE_EVENT(queue, handler, argc, ...) \ +#define CREATE_EVENT(multiqueue, handler, argc, ...) \ do { \ - if (queue) { \ - queue_put((queue), (handler), argc, __VA_ARGS__); \ + if (multiqueue) { \ + multiqueue_put((multiqueue), (handler), argc, __VA_ARGS__); \ } else { \ void *argv[argc] = { __VA_ARGS__ }; \ (handler)(argv); \ @@ -37,12 +37,12 @@ typedef struct loop { } while (0) // Poll for events until a condition or timeout -#define LOOP_PROCESS_EVENTS_UNTIL(loop, queue, timeout, condition) \ +#define LOOP_PROCESS_EVENTS_UNTIL(loop, multiqueue, timeout, condition) \ do { \ int remaining = timeout; \ uint64_t before = (remaining > 0) ? os_hrtime() : 0; \ while (!(condition)) { \ - LOOP_PROCESS_EVENTS(loop, queue, remaining); \ + LOOP_PROCESS_EVENTS(loop, multiqueue, remaining); \ if (remaining == 0) { \ break; \ } else if (remaining > 0) { \ @@ -56,10 +56,10 @@ typedef struct loop { } \ } while (0) -#define LOOP_PROCESS_EVENTS(loop, queue, timeout) \ +#define LOOP_PROCESS_EVENTS(loop, multiqueue, timeout) \ do { \ - if (queue && !queue_empty(queue)) { \ - queue_process_events(queue); \ + if (multiqueue && !multiqueue_empty(multiqueue)) { \ + multiqueue_process_events(multiqueue); \ } else { \ loop_poll_events(loop, timeout); \ } \ diff --git a/src/nvim/event/queue.c b/src/nvim/event/multiqueue.c index ee224b0a25..7efdfc4cad 100644 --- a/src/nvim/event/queue.c +++ b/src/nvim/event/multiqueue.c @@ -1,5 +1,5 @@ -// Queue for selective async event processing. Instances of this queue support a -// parent/child relationship with the following properties: +// Multi-level queue for selective async event processing. Multiqueue supports +// a parent-child relationship with the following properties: // // - pushing a node to a child queue will push a corresponding link node to the // parent queue @@ -9,7 +9,7 @@ // in the parent queue // // These properties allow Nvim to organize and process events from different -// sources with a certain degree of control. Here's how the queue is used: +// sources with a certain degree of control. How the multiqueue is used: // // +----------------+ // | Main loop | @@ -26,7 +26,7 @@ // +-----------+ +-----------+ +---------+ +---------+ // // -// The lower boxes represents event emitters, each with its own private queue +// The lower boxes represent event emitters, each with its own private queue // having the event loop queue as the parent. // // When idle, the main loop spins the event loop which queues events from many @@ -50,51 +50,52 @@ #include <uv.h> -#include "nvim/event/queue.h" +#include "nvim/event/multiqueue.h" #include "nvim/memory.h" #include "nvim/os/time.h" -typedef struct queue_item QueueItem; -struct queue_item { +typedef struct multiqueue_item MultiQueueItem; +struct multiqueue_item { union { - Queue *queue; + MultiQueue *queue; struct { Event event; - QueueItem *parent; + MultiQueueItem *parent; } item; } data; - bool link; // this is just a link to a node in a child queue + bool link; // true: current item is just a link to a node in a child queue QUEUE node; }; -struct queue { - Queue *parent; +struct multiqueue { + MultiQueue *parent; QUEUE headtail; put_callback put_cb; void *data; }; #ifdef INCLUDE_GENERATED_DECLARATIONS -# include "event/queue.c.generated.h" +# include "event/multiqueue.c.generated.h" #endif -static Event NILEVENT = {.handler = NULL, .argv = {NULL}}; +static Event NILEVENT = { .handler = NULL, .argv = {NULL} }; -Queue *queue_new_parent(put_callback put_cb, void *data) +MultiQueue *multiqueue_new_parent(put_callback put_cb, void *data) { - return queue_new(NULL, put_cb, data); + return multiqueue_new(NULL, put_cb, data); } -Queue *queue_new_child(Queue *parent) +MultiQueue *multiqueue_new_child(MultiQueue *parent) FUNC_ATTR_NONNULL_ALL { assert(!parent->parent); - return queue_new(parent, NULL, NULL); + return multiqueue_new(parent, NULL, NULL); } -static Queue *queue_new(Queue *parent, put_callback put_cb, void *data) +static MultiQueue *multiqueue_new(MultiQueue *parent, put_callback put_cb, + void *data) { - Queue *rv = xmalloc(sizeof(Queue)); + MultiQueue *rv = xmalloc(sizeof(MultiQueue)); QUEUE_INIT(&rv->headtail); rv->parent = parent; rv->put_cb = put_cb; @@ -102,13 +103,13 @@ static Queue *queue_new(Queue *parent, put_callback put_cb, void *data) return rv; } -void queue_free(Queue *queue) +void multiqueue_free(MultiQueue *this) { - assert(queue); - while (!QUEUE_EMPTY(&queue->headtail)) { - QUEUE *q = QUEUE_HEAD(&queue->headtail); - QueueItem *item = queue_node_data(q); - if (queue->parent) { + assert(this); + while (!QUEUE_EMPTY(&this->headtail)) { + QUEUE *q = QUEUE_HEAD(&this->headtail); + MultiQueueItem *item = multiqueue_node_data(q); + if (this->parent) { QUEUE_REMOVE(&item->data.item.parent->node); xfree(item->data.item.parent); } @@ -116,66 +117,66 @@ void queue_free(Queue *queue) xfree(item); } - xfree(queue); + xfree(this); } -Event queue_get(Queue *queue) +Event multiqueue_get(MultiQueue *this) { - return queue_empty(queue) ? NILEVENT : queue_remove(queue); + return multiqueue_empty(this) ? NILEVENT : multiqueue_remove(this); } -void queue_put_event(Queue *queue, Event event) +void multiqueue_put_event(MultiQueue *this, Event event) { - assert(queue); - queue_push(queue, event); - if (queue->parent && queue->parent->put_cb) { - queue->parent->put_cb(queue->parent, queue->parent->data); + assert(this); + multiqueue_push(this, event); + if (this->parent && this->parent->put_cb) { + this->parent->put_cb(this->parent, this->parent->data); } } -void queue_process_events(Queue *queue) +void multiqueue_process_events(MultiQueue *this) { - assert(queue); - while (!queue_empty(queue)) { - Event event = queue_get(queue); + assert(this); + while (!multiqueue_empty(this)) { + Event event = multiqueue_get(this); if (event.handler) { event.handler(event.argv); } } } -bool queue_empty(Queue *queue) +bool multiqueue_empty(MultiQueue *this) { - assert(queue); - return QUEUE_EMPTY(&queue->headtail); + assert(this); + return QUEUE_EMPTY(&this->headtail); } -void queue_replace_parent(Queue *queue, Queue *new_parent) +void multiqueue_replace_parent(MultiQueue *this, MultiQueue *new_parent) { - assert(queue_empty(queue)); - queue->parent = new_parent; + assert(multiqueue_empty(this)); + this->parent = new_parent; } -static Event queue_remove(Queue *queue) +static Event multiqueue_remove(MultiQueue *this) { - assert(!queue_empty(queue)); - QUEUE *h = QUEUE_HEAD(&queue->headtail); + assert(!multiqueue_empty(this)); + QUEUE *h = QUEUE_HEAD(&this->headtail); QUEUE_REMOVE(h); - QueueItem *item = queue_node_data(h); + MultiQueueItem *item = multiqueue_node_data(h); Event rv; if (item->link) { - assert(!queue->parent); + assert(!this->parent); // remove the next node in the linked queue - Queue *linked = item->data.queue; - assert(!queue_empty(linked)); - QueueItem *child = - queue_node_data(QUEUE_HEAD(&linked->headtail)); + 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 (queue->parent) { + if (this->parent) { // remove the corresponding link node in the parent queue QUEUE_REMOVE(&item->data.item.parent->node); xfree(item->data.item.parent); @@ -187,22 +188,22 @@ static Event queue_remove(Queue *queue) return rv; } -static void queue_push(Queue *queue, Event event) +static void multiqueue_push(MultiQueue *this, Event event) { - QueueItem *item = xmalloc(sizeof(QueueItem)); + MultiQueueItem *item = xmalloc(sizeof(MultiQueueItem)); item->link = false; item->data.item.event = event; - QUEUE_INSERT_TAIL(&queue->headtail, &item->node); - if (queue->parent) { + QUEUE_INSERT_TAIL(&this->headtail, &item->node); + if (this->parent) { // push link node to the parent queue - item->data.item.parent = xmalloc(sizeof(QueueItem)); + item->data.item.parent = xmalloc(sizeof(MultiQueueItem)); item->data.item.parent->link = true; - item->data.item.parent->data.queue = queue; - QUEUE_INSERT_TAIL(&queue->parent->headtail, &item->data.item.parent->node); + item->data.item.parent->data.queue = this; + QUEUE_INSERT_TAIL(&this->parent->headtail, &item->data.item.parent->node); } } -static QueueItem *queue_node_data(QUEUE *q) +static MultiQueueItem *multiqueue_node_data(QUEUE *q) { - return QUEUE_DATA(q, QueueItem, node); + return QUEUE_DATA(q, MultiQueueItem, node); } diff --git a/src/nvim/event/multiqueue.h b/src/nvim/event/multiqueue.h new file mode 100644 index 0000000000..def6b95a10 --- /dev/null +++ b/src/nvim/event/multiqueue.h @@ -0,0 +1,19 @@ +#ifndef NVIM_EVENT_MULTIQUEUE_H +#define NVIM_EVENT_MULTIQUEUE_H + +#include <uv.h> + +#include "nvim/event/defs.h" +#include "nvim/lib/queue.h" + +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__)); + + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "event/multiqueue.h.generated.h" +#endif +#endif // NVIM_EVENT_MULTIQUEUE_H diff --git a/src/nvim/event/process.c b/src/nvim/event/process.c index 51f20b7eac..39dd5fd55a 100644 --- a/src/nvim/event/process.c +++ b/src/nvim/event/process.c @@ -126,7 +126,7 @@ void process_teardown(Loop *loop) FUNC_ATTR_NONNULL_ALL // Wait until all children exit and all close events are processed. LOOP_PROCESS_EVENTS_UNTIL( loop, loop->events, -1, - kl_empty(loop->children) && queue_empty(loop->events)); + kl_empty(loop->children) && multiqueue_empty(loop->events)); pty_process_teardown(loop); } @@ -163,7 +163,8 @@ void process_close_err(Process *proc) FUNC_ATTR_NONNULL_ALL /// indistinguishable from the process returning -1 by itself. Which /// is possible on some OS. Returns -2 if an user has interruped the /// wait. -int process_wait(Process *proc, int ms, Queue *events) FUNC_ATTR_NONNULL_ARG(1) +int process_wait(Process *proc, int ms, MultiQueue *events) + FUNC_ATTR_NONNULL_ARG(1) { // The default status is -1, which represents a timeout int status = -1; @@ -208,7 +209,7 @@ int process_wait(Process *proc, int ms, Queue *events) FUNC_ATTR_NONNULL_ARG(1) decref(proc); if (events) { // the decref call created an exit event, process it now - queue_process_events(events); + multiqueue_process_events(events); } } else { proc->refcount--; @@ -362,7 +363,7 @@ static void flush_stream(Process *proc, Stream *stream) // Poll for data and process the generated events. loop_poll_events(proc->loop, 0); if (proc->events) { - queue_process_events(proc->events); + multiqueue_process_events(proc->events); } // Stream can be closed if it is empty. @@ -402,7 +403,7 @@ static void on_process_exit(Process *proc) // OS. We are still in the libuv loop, so we cannot call code that polls for // more data directly. Instead delay the reading after the libuv loop by // queueing process_close_handles() as an event. - Queue *queue = proc->events ? proc->events : loop->events; + MultiQueue *queue = proc->events ? proc->events : loop->events; CREATE_EVENT(queue, process_close_handles, 1, proc); } diff --git a/src/nvim/event/process.h b/src/nvim/event/process.h index a4c6e7eeb2..5cbf7f9ce7 100644 --- a/src/nvim/event/process.h +++ b/src/nvim/event/process.h @@ -27,7 +27,7 @@ struct process { process_exit_cb cb; internal_process_cb internal_exit_cb, internal_close_cb; bool closed, term_sent, detach; - Queue *events; + MultiQueue *events; }; static inline Process process_init(Loop *loop, ProcessType type, void *data) diff --git a/src/nvim/event/queue.h b/src/nvim/event/queue.h deleted file mode 100644 index 85fc59f8b2..0000000000 --- a/src/nvim/event/queue.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef NVIM_EVENT_QUEUE_H -#define NVIM_EVENT_QUEUE_H - -#include <uv.h> - -#include "nvim/event/defs.h" -#include "nvim/lib/queue.h" - -typedef struct queue Queue; -typedef void (*put_callback)(Queue *queue, void *data); - -#define queue_put(q, h, ...) \ - queue_put_event(q, event_create(1, h, __VA_ARGS__)); - - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "event/queue.h.generated.h" -#endif -#endif // NVIM_EVENT_QUEUE_H diff --git a/src/nvim/event/signal.h b/src/nvim/event/signal.h index e32608acc0..7fe352edef 100644 --- a/src/nvim/event/signal.h +++ b/src/nvim/event/signal.h @@ -14,7 +14,7 @@ struct signal_watcher { void *data; signal_cb cb; signal_close_cb close_cb; - Queue *events; + MultiQueue *events; }; #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/event/socket.h b/src/nvim/event/socket.h index ad59fdbe3a..eb0823c76d 100644 --- a/src/nvim/event/socket.h +++ b/src/nvim/event/socket.h @@ -30,7 +30,7 @@ struct socket_watcher { void *data; socket_cb cb; socket_close_cb close_cb; - Queue *events; + MultiQueue *events; }; #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/event/stream.h b/src/nvim/event/stream.h index a176fac1c0..d27497e4a4 100644 --- a/src/nvim/event/stream.h +++ b/src/nvim/event/stream.h @@ -53,7 +53,7 @@ struct stream { size_t pending_reqs; size_t num_bytes; bool closed; - Queue *events; + MultiQueue *events; }; #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/event/time.c b/src/nvim/event/time.c index f68a66345f..77260546db 100644 --- a/src/nvim/event/time.c +++ b/src/nvim/event/time.c @@ -51,7 +51,7 @@ static void time_watcher_cb(uv_timer_t *handle) FUNC_ATTR_NONNULL_ALL { TimeWatcher *watcher = handle->data; - if (watcher->blockable && !queue_empty(watcher->events)) { + if (watcher->blockable && !multiqueue_empty(watcher->events)) { // the timer blocked and there already is an unprocessed event waiting return; } diff --git a/src/nvim/event/time.h b/src/nvim/event/time.h index 14df176ea3..a6de89ad6e 100644 --- a/src/nvim/event/time.h +++ b/src/nvim/event/time.h @@ -12,7 +12,7 @@ struct time_watcher { uv_timer_t uv; void *data; time_cb cb, close_cb; - Queue *events; + MultiQueue *events; bool blockable; }; diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 4c997844ea..4254697241 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -357,7 +357,7 @@ static int command_line_execute(VimState *state, int key) s->c = key; if (s->c == K_EVENT) { - queue_process_events(main_loop.events); + multiqueue_process_events(main_loop.events); redrawcmdline(); return 1; } diff --git a/src/nvim/hardcopy.c b/src/nvim/hardcopy.c index 7d4bfd0290..6acf7f395a 100644 --- a/src/nvim/hardcopy.c +++ b/src/nvim/hardcopy.c @@ -2105,7 +2105,7 @@ int mch_print_init(prt_settings_T *psettings, char_u *jobname, int forceit) props = enc_canon_props(p_encoding); if (!(props & ENC_8BIT) && ((*p_pmcs != NUL) || !(props & ENC_UNICODE))) { p_mbenc_first = NULL; - int effective_cmap; + int effective_cmap = 0; for (cmap = 0; cmap < (int)ARRAY_SIZE(prt_ps_mbfonts); cmap++) if (prt_match_encoding((char *)p_encoding, &prt_ps_mbfonts[cmap], &p_mbenc)) { diff --git a/src/nvim/main.c b/src/nvim/main.c index 86bef782f7..005f4dcc77 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -156,7 +156,7 @@ void event_teardown(void) return; } - queue_process_events(main_loop.events); + multiqueue_process_events(main_loop.events); input_stop(); channel_teardown(); process_teardown(&main_loop); diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index cd12f258b6..fef1d08db7 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -65,7 +65,7 @@ typedef struct { uint64_t next_request_id; kvec_t(ChannelCallFrame *) call_stack; kvec_t(WBuffer *) delayed_notifications; - Queue *events; + MultiQueue *events; } Channel; typedef struct { @@ -440,7 +440,7 @@ static void handle_request(Channel *channel, msgpack_object *request) if (handler.async) { on_request_event((void **)&event_data); } else { - queue_put(channel->events, on_request_event, 1, event_data); + multiqueue_put(channel->events, on_request_event, 1, event_data); } } @@ -620,7 +620,7 @@ static void close_channel(Channel *channel) case kChannelTypeStdio: stream_close(&channel->data.std.in, NULL, NULL); stream_close(&channel->data.std.out, NULL, NULL); - queue_put(main_loop.fast_events, exit_event, 1, channel); + multiqueue_put(main_loop.fast_events, exit_event, 1, channel); return; default: abort(); @@ -654,7 +654,7 @@ static void free_channel(Channel *channel) kv_destroy(channel->call_stack); kv_destroy(channel->delayed_notifications); if (channel->type != kChannelTypeProc) { - queue_free(channel->events); + multiqueue_free(channel->events); } xfree(channel); } @@ -664,10 +664,11 @@ static void close_cb(Stream *stream, void *data) decref(data); } -static Channel *register_channel(ChannelType type, uint64_t id, Queue *events) +static Channel *register_channel(ChannelType type, uint64_t id, + MultiQueue *events) { Channel *rv = xmalloc(sizeof(Channel)); - rv->events = events ? events : queue_new_child(main_loop.events); + rv->events = events ? events : multiqueue_new_child(main_loop.events); rv->type = type; rv->refcount = 1; rv->closed = false; diff --git a/src/nvim/normal.c b/src/nvim/normal.c index c7c112200a..6dcbf50750 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -7880,15 +7880,15 @@ static void nv_event(cmdarg_T *cap) { // Garbage collection should have been executed before blocking for events in // the `os_inchar` in `state_enter`, but we also disable it here in case the - // `os_inchar` branch was not executed(!queue_empty(loop.events), which could - // have `may_garbage_collect` set to true in `normal_check`). + // `os_inchar` branch was not executed (!multiqueue_empty(loop.events), which + // could have `may_garbage_collect` set to true in `normal_check`). // // That is because here we may run code that calls `os_inchar` // later(`f_confirm` or `get_keystroke` for example), but in these cases it is // not safe to perform garbage collection because there could be unreferenced // lists or dicts being used. may_garbage_collect = false; - queue_process_events(main_loop.events); + multiqueue_process_events(main_loop.events); cap->retval |= CA_COMMAND_BUSY; // don't call edit() now } diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c index 6873e32f34..1307ab5e5a 100644 --- a/src/nvim/os/input.c +++ b/src/nvim/os/input.c @@ -87,8 +87,8 @@ static void create_cursorhold_event(void) // have been called(inbuf_poll would return kInputAvail) // TODO(tarruda): Cursorhold should be implemented as a timer set during the // `state_check` callback for the states where it can be triggered. - assert(!events_enabled || queue_empty(main_loop.events)); - queue_put(main_loop.events, cursorhold_event, 0); + assert(!events_enabled || multiqueue_empty(main_loop.events)); + multiqueue_put(main_loop.events, cursorhold_event, 0); } // Low level input function @@ -422,5 +422,5 @@ static void read_error_exit(void) static bool pending_events(void) { - return events_enabled && !queue_empty(main_loop.events); + return events_enabled && !multiqueue_empty(main_loop.events); } diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c index 617a505367..e9a3dcbff8 100644 --- a/src/nvim/os/shell.c +++ b/src/nvim/os/shell.c @@ -207,7 +207,7 @@ static int do_os_system(char **argv, Stream in, out, err; LibuvProcess uvproc = libuv_process_init(&main_loop, &buf); Process *proc = &uvproc.process; - Queue *events = queue_new_child(main_loop.events); + MultiQueue *events = multiqueue_new_child(main_loop.events); proc->events = events; proc->argv = argv; proc->in = input != NULL ? &in : NULL; @@ -221,7 +221,7 @@ static int do_os_system(char **argv, msg_outtrans((char_u *)prog); msg_putchar('\n'); } - queue_free(events); + multiqueue_free(events); return -1; } @@ -277,8 +277,8 @@ static int do_os_system(char **argv, } } - assert(queue_empty(events)); - queue_free(events); + assert(multiqueue_empty(events)); + multiqueue_free(events); return status; } diff --git a/src/nvim/po/eo.po b/src/nvim/po/eo.po index 153eacc7b8..8a3cd50406 100644 --- a/src/nvim/po/eo.po +++ b/src/nvim/po/eo.po @@ -23,8 +23,8 @@ msgid "" msgstr "" "Project-Id-Version: Vim(Esperanto)\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-02-13 23:42+0100\n" -"PO-Revision-Date: 2016-02-13 23:45+0100\n" +"POT-Creation-Date: 2016-03-29 23:03+0200\n" +"PO-Revision-Date: 2016-03-29 23:05+0200\n" "Last-Translator: Dominique PELLÉ <dominique.pelle@gmail.com>\n" "Language-Team: \n" "Language: eo\n" @@ -1346,7 +1346,6 @@ msgstr "E670: Miksaĵo de kodoprezento de helpa dosiero en lingvo: %s" msgid "E154: Duplicate tag \"%s\" in file %s/%s" msgstr "E154: Ripetita etikedo \"%s\" en dosiero %s/%s" -#: ../ex_cmds.c:5687 #, c-format msgid "E160: Unknown sign command: %s" msgstr "E160: Nekonata simbola komando: %s" @@ -1482,8 +1481,8 @@ msgstr "Serĉado de \"%s\"" #: ../ex_cmds2.c:2307 #, c-format -msgid "not found in 'runtimepath': \"%s\"" -msgstr "ne trovita en 'runtimepath': \"%s\"" +msgid "not found in '%s': \"%s\"" +msgstr "ne trovita en '%s: \"%s\"" #: ../ex_cmds2.c:2472 #, c-format @@ -6686,6 +6685,12 @@ msgstr "E446: Neniu dosiernomo sub la kursoro" #~ msgid "E232: Cannot create BalloonEval with both message and callback" #~ msgstr "E232: Ne eblas krei BalloonEval kun ambaŭ mesaĝo kaj reagfunkcio" +msgid "Yes" +msgstr "Jes" + +msgid "No" +msgstr "Ne" + # todo '_' is for hotkey, i guess? #~ msgid "Input _Methods" #~ msgstr "Enigaj _metodoj" diff --git a/src/nvim/po/fr.po b/src/nvim/po/fr.po index a16a939117..cd9f5a7eb2 100644 --- a/src/nvim/po/fr.po +++ b/src/nvim/po/fr.po @@ -645,7 +645,7 @@ msgstr "E708: [:] ne peut tre spcifi qu'en dernier" #: ../eval.c:2439 msgid "E709: [:] requires a List value" -msgstr "E709: [:] requiert une Liste" +msgstr "E709: [:] n?cessite une Liste" #: ../eval.c:2674 msgid "E710: List value has more items than target" @@ -1639,8 +1639,8 @@ msgstr "Recherche de \"%s\"" #: ../ex_cmds2.c:2307 #, c-format -msgid "not found in 'runtimepath': \"%s\"" -msgstr "introuvable dans 'runtimepath' : \"%s\"" +msgid "not found in '%s': \"%s\"" +msgstr "introuvable dans '%s' : \"%s\"" #: ../ex_cmds2.c:2472 #, c-format @@ -6877,6 +6877,12 @@ msgstr "E446: Aucun nom de fichier sous le curseur" #~ msgid "E232: Cannot create BalloonEval with both message and callback" #~ msgstr "E232: Impossible de crer un BalloonEval avec message ET callback" +msgid "Yes" +msgstr "Oui" + +msgid "No" +msgstr "Non" + # todo '_' is for hotkey, i guess? #~ msgid "Input _Methods" #~ msgstr "_Mthodes de saisie" diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 9a2eeda8b2..47dd640e59 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -2210,8 +2210,8 @@ win_line ( ///< force wrapping int vcol_off = 0; ///< offset for concealed characters int did_wcol = false; - int match_conc = false; ///< cchar for match functions - int has_match_conc = false; ///< match wants to conceal + int match_conc = 0; ///< cchar for match functions + int has_match_conc = 0; ///< match wants to conceal int old_boguscols = 0; # define VCOL_HLC (vcol - vcol_off) # define FIX_FOR_BOGUSCOLS \ @@ -2656,7 +2656,7 @@ win_line ( // Repeat for the whole displayed line. for (;; ) { - has_match_conc = false; + has_match_conc = 0; // Skip this quickly when working on the text. if (draw_state != WL_LINE) { if (draw_state == WL_CMDLINE - 1 && n_extra == 0) { @@ -2906,10 +2906,10 @@ win_line ( shl->attr_cur = shl->attr; if (cur != NULL && syn_name2id((char_u *)"Conceal") == cur->hlg_id) { - has_match_conc = true; + has_match_conc = v == (long)shl->startcol ? 2 : 1; match_conc = cur->conceal_char; } else { - has_match_conc = match_conc = false; + has_match_conc = match_conc = 0; } } else if (v == (long)shl->endcol) { shl->attr_cur = 0; @@ -3633,11 +3633,11 @@ win_line ( if (wp->w_p_cole > 0 && (wp != curwin || lnum != wp->w_cursor.lnum || conceal_cursor_line(wp)) - && ((syntax_flags & HL_CONCEAL) != 0 || has_match_conc) + && ((syntax_flags & HL_CONCEAL) != 0 || has_match_conc > 0) && !(lnum_in_visual_area && vim_strchr(wp->w_p_cocu, 'v') == NULL)) { char_attr = conceal_attr; - if (prev_syntax_id != syntax_seqnr + if ((prev_syntax_id != syntax_seqnr || has_match_conc > 1) && (syn_get_sub_char() != NUL || match_conc || wp->w_p_cole == 1) && wp->w_p_cole != 3) { diff --git a/src/nvim/state.c b/src/nvim/state.c index f792ec00a4..44c6441e40 100644 --- a/src/nvim/state.c +++ b/src/nvim/state.c @@ -35,8 +35,8 @@ getkey: // processing. Characters can come from mappings, scripts and other // sources, so this scenario is very common. key = safe_vgetc(); - } else if (!queue_empty(main_loop.events)) { - // Event was made available after the last queue_process_events call + } else if (!multiqueue_empty(main_loop.events)) { + // Event was made available after the last multiqueue_process_events call key = K_EVENT; } else { input_enable_events(); @@ -48,7 +48,7 @@ getkey: // directly. (void)os_inchar(NULL, 0, -1, 0); input_disable_events(); - key = !queue_empty(main_loop.events) ? K_EVENT : safe_vgetc(); + key = !multiqueue_empty(main_loop.events) ? K_EVENT : safe_vgetc(); } if (key == K_EVENT) { diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index ff98dc9f22..8401343d7a 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -166,7 +166,7 @@ void terminal_init(void) invalidated_terminals = pmap_new(ptr_t)(); time_watcher_init(&main_loop, &refresh_timer, NULL); // refresh_timer_cb will redraw the screen which can call vimscript - refresh_timer.events = queue_new_child(main_loop.events); + refresh_timer.events = multiqueue_new_child(main_loop.events); // initialize a rgb->color index map for cterm attributes(VTermScreenCell // only has RGB information and we need color indexes for terminal UIs) @@ -201,7 +201,7 @@ void terminal_init(void) void terminal_teardown(void) { time_watcher_stop(&refresh_timer); - queue_free(refresh_timer.events); + multiqueue_free(refresh_timer.events); time_watcher_close(&refresh_timer, NULL); pmap_free(ptr_t)(invalidated_terminals); map_free(int, int)(color_indexes); @@ -445,7 +445,7 @@ static int terminal_execute(VimState *state, int key) case K_EVENT: // We cannot let an event free the terminal yet. It is still needed. s->term->refcount++; - queue_process_events(main_loop.events); + multiqueue_process_events(main_loop.events); s->term->refcount--; if (s->term->buf_handle == 0) { s->close = true; diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile index 10d26bd8cf..4d21887240 100644 --- a/src/nvim/testdir/Makefile +++ b/src/nvim/testdir/Makefile @@ -41,6 +41,7 @@ NEW_TESTS = \ test_viml.res \ test_visual.res \ test_window_id.res \ + test_matchadd_conceal.res \ test_alot.res SCRIPTS_GUI := test16.out diff --git a/src/nvim/testdir/test_alot.vim b/src/nvim/testdir/test_alot.vim index f0efd6e4ed..7169b6076f 100644 --- a/src/nvim/testdir/test_alot.vim +++ b/src/nvim/testdir/test_alot.vim @@ -3,6 +3,7 @@ source test_assign.vim source test_cursor_func.vim +source test_ex_undo.vim source test_feedkeys.vim source test_cmdline.vim source test_menu.vim @@ -10,3 +11,4 @@ source test_popup.vim source test_regexp_utf8.vim source test_syn_attr.vim source test_unlet.vim +source test_matchadd_conceal_utf8.vim diff --git a/src/nvim/testdir/test_ex_undo.vim b/src/nvim/testdir/test_ex_undo.vim new file mode 100644 index 0000000000..44feb3680a --- /dev/null +++ b/src/nvim/testdir/test_ex_undo.vim @@ -0,0 +1,19 @@ +" Tests for :undo + +func Test_ex_undo() + new ex-undo + setlocal ul=10 + exe "normal ione\n\<Esc>" + setlocal ul=10 + exe "normal itwo\n\<Esc>" + setlocal ul=10 + exe "normal ithree\n\<Esc>" + call assert_equal(4, line('$')) + undo + call assert_equal(3, line('$')) + undo 1 + call assert_equal(2, line('$')) + undo 0 + call assert_equal(1, line('$')) + quit! +endfunc diff --git a/src/nvim/testdir/test_matchadd_conceal.vim b/src/nvim/testdir/test_matchadd_conceal.vim new file mode 100644 index 0000000000..bc1c28d6e9 --- /dev/null +++ b/src/nvim/testdir/test_matchadd_conceal.vim @@ -0,0 +1,262 @@ +" Test for matchadd() and conceal feature +if !has('conceal') + finish +endif + +function! s:screenline(lnum) abort + let line = [] + for c in range(1, winwidth(0)) + call add(line, nr2char(screenchar(a:lnum, c))) + endfor + return s:trim(join(line, '')) +endfunction + +function! s:trim(str) abort + return matchstr(a:str,'^\s*\zs.\{-}\ze\s*$') +endfunction + +function! Test_simple_matchadd() + new + + 1put='# This is a Test' + " 1234567890123456 + let expect = '# This is a Test' + + call cursor(1, 1) + call matchadd('Conceal', '\%2l ') + redraw! + let lnum = 2 + call assert_equal(expect, s:screenline(lnum)) + call assert_notequal(screenattr(lnum, 1), screenattr(lnum, 2)) + call assert_notequal(screenattr(lnum, 1), screenattr(lnum, 2)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 7)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 10)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 12)) + call assert_equal(screenattr(lnum, 1), screenattr(lnum, 16)) + + quit! +endfunction + +function! Test_simple_matchadd_and_conceal() + new + setlocal concealcursor=n conceallevel=1 + + 1put='# This is a Test' + " 1234567890123456 + let expect = '#XThisXisXaXTest' + + call cursor(1, 1) + call matchadd('Conceal', '\%2l ', 10, -1, {'conceal': 'X'}) + redraw! + let lnum = 2 + call assert_equal(expect, s:screenline(lnum)) + call assert_notequal(screenattr(lnum, 1), screenattr(lnum, 2)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 7)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 10)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 12)) + call assert_equal(screenattr(lnum, 1), screenattr(lnum, 16)) + + quit! +endfunction + +function! Test_matchadd_and_conceallevel_3() + new + + setlocal conceallevel=3 + " set filetype and :syntax on to change screenattr() + setlocal filetype=conf + syntax on + + 1put='# This is a Test' + " 1234567890123456 + let expect = '#ThisisaTest' + + call cursor(1, 1) + call matchadd('Conceal', '\%2l ', 10, -1, {'conceal': 'X'}) + redraw! + let lnum = 2 + call assert_equal(expect, s:screenline(lnum)) + call assert_equal(screenattr(lnum, 1), screenattr(lnum, 2)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 7)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 10)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 12)) + call assert_notequal(screenattr(lnum, 1), screenattr(lnum, 16)) + + " more matchadd() + " 1234567890123456 + let expect = '#Thisisa Test' + + call matchadd('ErrorMsg', '\%2l Test', 20, -1, {'conceal': 'X'}) + redraw! + call assert_equal(expect, s:screenline(lnum)) + call assert_equal(screenattr(lnum, 1) , screenattr(lnum, 2)) + call assert_equal(screenattr(lnum, 2) , screenattr(lnum, 7)) + call assert_notequal(screenattr(lnum, 1) , screenattr(lnum, 10)) + call assert_equal(screenattr(lnum, 10), screenattr(lnum, 12)) + call assert_notequal(screenattr(lnum, 1) , screenattr(lnum, 16)) + call assert_notequal(screenattr(lnum, 10), screenattr(lnum, 16)) + + syntax off + quit! +endfunction + +function! Test_default_conceal_char() + new + setlocal concealcursor=n conceallevel=1 + + 1put='# This is a Test' + " 1234567890123456 + let expect = '# This is a Test' + + call cursor(1, 1) + call matchadd('Conceal', '\%2l ', 10, -1, {}) + redraw! + let lnum = 2 + call assert_equal(expect, s:screenline(lnum)) + call assert_notequal(screenattr(lnum, 1), screenattr(lnum, 2)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 7)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 10)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 12)) + call assert_equal(screenattr(lnum, 1), screenattr(lnum, 16)) + + " 1234567890123456 + let expect = '#+This+is+a+Test' + let listchars_save = &listchars + set listchars=conceal:+ + redraw! + + call assert_equal(expect, s:screenline(lnum)) + call assert_notequal(screenattr(lnum, 1), screenattr(lnum, 2)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 7)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 10)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 12)) + call assert_equal(screenattr(lnum, 1), screenattr(lnum, 16)) + + let &listchars = listchars_save + quit! +endfunction + +function! Test_syn_and_match_conceal() + new + setlocal concealcursor=n conceallevel=1 + + 1put='# This is a Test' + " 1234567890123456 + let expect = '#ZThisZisZaZTest' + + call cursor(1, 1) + call matchadd('Conceal', '\%2l ', 10, -1, {'conceal': 'Z'}) + syntax match MyConceal /\%2l / conceal containedin=ALL cchar=* + redraw! + let lnum = 2 + call assert_equal(expect, s:screenline(lnum)) + call assert_notequal(screenattr(lnum, 1), screenattr(lnum, 2)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 7)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 10)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 12)) + call assert_equal(screenattr(lnum, 1), screenattr(lnum, 16)) + + " 1234567890123456 + let expect = '#*This*is*a*Test' + call clearmatches() + redraw! + + call assert_equal(expect, s:screenline(lnum)) + call assert_notequal(screenattr(lnum, 1), screenattr(lnum, 2)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 7)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 10)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 12)) + call assert_equal(screenattr(lnum, 1), screenattr(lnum, 16)) + + syntax off + quit! +endfunction + +function! Test_clearmatches() + new + setlocal concealcursor=n conceallevel=1 + + 1put='# This is a Test' + " 1234567890123456 + let expect = '# This is a Test' + + call cursor(1, 1) + call matchadd('Conceal', '\%2l ', 10, -1, {'conceal': 'Z'}) + let a = getmatches() + call clearmatches() + redraw! + + let lnum = 2 + call assert_equal(expect, s:screenline(lnum)) + call assert_equal(screenattr(lnum, 1), screenattr(lnum, 2)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 7)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 10)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 12)) + call assert_equal(screenattr(lnum, 1), screenattr(lnum, 16)) + + " reset match using setmatches() + " 1234567890123456 + let expect = '#ZThisZisZaZTest' + call setmatches(a) + redraw! + + call assert_equal(expect, s:screenline(lnum)) + call assert_notequal(screenattr(lnum, 1), screenattr(lnum, 2)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 7)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 10)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 12)) + call assert_equal(screenattr(lnum, 1), screenattr(lnum, 16)) + call assert_equal({'group': 'Conceal', 'pattern': '\%2l ', 'priority': 10, 'id': a[0].id, 'conceal': 'Z'}, a[0]) + + quit! +endfunction + +function! Test_using_matchaddpos() + new + setlocal concealcursor=n conceallevel=1 + " set filetype and :syntax on to change screenattr() + setlocal filetype=conf + syntax on + + 1put='# This is a Test' + " 1234567890123456 + let expect = '#Pis a Test' + + call cursor(1, 1) + call matchaddpos('Conceal', [[2,2,6]], 10, -1, {'conceal': 'P'}) + let a = getmatches() + redraw! + + let lnum = 2 + call assert_equal(expect, s:screenline(lnum)) + call assert_notequal(screenattr(lnum, 1) , screenattr(lnum, 2)) + call assert_notequal(screenattr(lnum, 2) , screenattr(lnum, 7)) + call assert_equal(screenattr(lnum, 1) , screenattr(lnum, 7)) + call assert_equal(screenattr(lnum, 1) , screenattr(lnum, 10)) + call assert_notequal(screenattr(lnum, 1) , screenattr(lnum, 12)) + call assert_notequal(screenattr(lnum, 1) , screenattr(lnum, 16)) + call assert_equal(screenattr(lnum, 12), screenattr(lnum, 16)) + call assert_equal({'group': 'Conceal', 'id': a[0].id, 'priority': 10, 'pos1': [2, 2, 6], 'conceal': 'P'}, a[0]) + + syntax off + quit! +endfunction + +function! Test_matchadd_repeat_conceal_with_syntax_off() + new + + " To test targets in the same line string is replaced with conceal char + " correctly, repeat 'TARGET' + 1put ='TARGET_TARGETTARGET' + call cursor(1, 1) + redraw + call assert_equal('TARGET_TARGETTARGET', s:screenline(2)) + + setlocal conceallevel=2 + call matchadd('Conceal', 'TARGET', 10, -1, {'conceal': 't'}) + + redraw + call assert_equal('t_tt', s:screenline(2)) + + quit! +endfunction diff --git a/src/nvim/testdir/test_matchadd_conceal_utf8.vim b/src/nvim/testdir/test_matchadd_conceal_utf8.vim new file mode 100644 index 0000000000..24c848a99c --- /dev/null +++ b/src/nvim/testdir/test_matchadd_conceal_utf8.vim @@ -0,0 +1,39 @@ +" Test for matchadd() and conceal feature using utf-8. +if !has('conceal') || !has('multi_byte') + finish +endif + +function! s:screenline(lnum) abort + let line = [] + for c in range(1, winwidth(0)) + call add(line, nr2char(screenchar(a:lnum, c))) + endfor + return s:trim(join(line, '')) +endfunction + +function! s:trim(str) abort + return matchstr(a:str,'^\s*\zs.\{-}\ze\s*$') +endfunction + +function! Test_match_using_multibyte_conceal_char() + new + setlocal concealcursor=n conceallevel=1 + + 1put='# This is a Test' + " 1234567890123456 + let expect = '#ˑThisˑisˑaˑTest' + + call cursor(1, 1) + call matchadd('Conceal', '\%2l ', 20, -1, {'conceal': "\u02d1"}) + redraw! + + let lnum = 2 + call assert_equal(expect, s:screenline(lnum)) + call assert_notequal(screenattr(lnum, 1), screenattr(lnum, 2)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 7)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 10)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 12)) + call assert_equal(screenattr(lnum, 1), screenattr(lnum, 16)) + + quit! +endfunction diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c index 68ea00ee63..740716f0ef 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -340,7 +340,7 @@ static void read_cb(Stream *stream, RBuffer *buf, size_t c, void *data, // ls *.md | xargs nvim input->in_fd = 2; stream_close(&input->read_stream, NULL, NULL); - queue_put(input->loop->fast_events, restart_reading, 1, input); + multiqueue_put(input->loop->fast_events, restart_reading, 1, input); } else { loop_schedule(&main_loop, event_create(1, input_done_event, 0)); } diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 6f90742928..f03d8b87fa 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -67,7 +67,7 @@ typedef struct { struct { int enable_mouse, disable_mouse; int enable_bracketed_paste, disable_bracketed_paste; - int enter_insert_mode, enter_replace_mode, exit_insert_mode; + int set_cursor_shape_bar, set_cursor_shape_ul, set_cursor_shape_block; int set_rgb_foreground, set_rgb_background; int enable_focus_reporting, disable_focus_reporting; } unibi_ext; @@ -124,9 +124,9 @@ static void terminfo_start(UI *ui) data->unibi_ext.disable_mouse = -1; data->unibi_ext.enable_bracketed_paste = -1; data->unibi_ext.disable_bracketed_paste = -1; - data->unibi_ext.enter_insert_mode = -1; - data->unibi_ext.enter_replace_mode = -1; - data->unibi_ext.exit_insert_mode = -1; + data->unibi_ext.set_cursor_shape_bar = -1; + data->unibi_ext.set_cursor_shape_ul = -1; + data->unibi_ext.set_cursor_shape_block = -1; data->unibi_ext.enable_focus_reporting = -1; data->unibi_ext.disable_focus_reporting = -1; data->out_fd = 1; @@ -139,6 +139,8 @@ static void terminfo_start(UI *ui) data->ut = unibi_dummy(); } fix_terminfo(data); + // Initialize the cursor shape. + unibi_out(ui, data->unibi_ext.set_cursor_shape_block); // Set 't_Co' from the result of unibilium & fix_terminfo. t_colors = unibi_get_num(data->ut, unibi_max_colors); // Enter alternate screen and clear @@ -172,7 +174,7 @@ static void terminfo_stop(UI *ui) unibi_out(ui, data->unibi_ext.disable_bracketed_paste); // Disable focus reporting unibi_out(ui, data->unibi_ext.disable_focus_reporting); - flush_buf(ui); + flush_buf(ui, true); uv_tty_reset_mode(); uv_close((uv_handle_t *)&data->output_handle, NULL); uv_run(&data->write_loop, UV_RUN_DEFAULT); @@ -457,16 +459,16 @@ static void tui_mode_change(UI *ui, int mode) if (mode == INSERT) { if (data->showing_mode != INSERT) { - unibi_out(ui, data->unibi_ext.enter_insert_mode); + unibi_out(ui, data->unibi_ext.set_cursor_shape_bar); } } else if (mode == REPLACE) { if (data->showing_mode != REPLACE) { - unibi_out(ui, data->unibi_ext.enter_replace_mode); + unibi_out(ui, data->unibi_ext.set_cursor_shape_ul); } } else { assert(mode == NORMAL); if (data->showing_mode != NORMAL) { - unibi_out(ui, data->unibi_ext.exit_insert_mode); + unibi_out(ui, data->unibi_ext.set_cursor_shape_block); } } data->showing_mode = mode; @@ -599,7 +601,7 @@ static void tui_flush(UI *ui) unibi_goto(ui, grid->row, grid->col); - flush_buf(ui); + flush_buf(ui, true); } static void suspend_event(void **argv) @@ -628,8 +630,8 @@ static void tui_suspend(UI *ui) // kill(0, SIGTSTP) won't stop the UI thread, so we must poll for SIGCONT // before continuing. This is done in another callback to avoid // loop_poll_events recursion - queue_put_event(data->loop->fast_events, - event_create(1, suspend_event, 1, ui)); + multiqueue_put_event(data->loop->fast_events, + event_create(1, suspend_event, 1, ui)); } static void tui_set_title(UI *ui, char *title) @@ -772,7 +774,7 @@ static void out(void *ctx, const char *str, size_t len) size_t available = data->bufsize - data->bufpos; if (len > available) { - flush_buf(ui); + flush_buf(ui, false); } memcpy(data->buf + data->bufpos, str, len); @@ -860,23 +862,23 @@ static void fix_terminfo(TUIData *data) || os_getenv("KONSOLE_DBUS_SESSION") != NULL) { // Konsole uses a proprietary escape code to set the cursor shape // and does not support DECSCUSR. - data->unibi_ext.enter_insert_mode = (int)unibi_add_ext_str(ut, NULL, + data->unibi_ext.set_cursor_shape_bar = (int)unibi_add_ext_str(ut, NULL, TMUX_WRAP("\x1b]50;CursorShape=1;BlinkingCursorEnabled=1\x07")); - data->unibi_ext.enter_replace_mode = (int)unibi_add_ext_str(ut, NULL, + data->unibi_ext.set_cursor_shape_ul = (int)unibi_add_ext_str(ut, NULL, TMUX_WRAP("\x1b]50;CursorShape=2;BlinkingCursorEnabled=1\x07")); - data->unibi_ext.exit_insert_mode = (int)unibi_add_ext_str(ut, NULL, + data->unibi_ext.set_cursor_shape_block = (int)unibi_add_ext_str(ut, NULL, TMUX_WRAP("\x1b]50;CursorShape=0;BlinkingCursorEnabled=0\x07")); } else if (!vte_version || atoi(vte_version) >= 3900) { // Assume that the terminal supports DECSCUSR unless it is an // old VTE based terminal. This should not get wrapped for tmux, // which will handle it via its Ss/Se terminfo extension - usually // according to its terminal-overrides. - data->unibi_ext.enter_insert_mode = (int)unibi_add_ext_str(ut, NULL, - "\x1b[5 q"); - data->unibi_ext.enter_replace_mode = (int)unibi_add_ext_str(ut, NULL, - "\x1b[3 q"); - data->unibi_ext.exit_insert_mode = (int)unibi_add_ext_str(ut, NULL, - "\x1b[2 q"); + data->unibi_ext.set_cursor_shape_bar = + (int)unibi_add_ext_str(ut, NULL, "\x1b[5 q"); + data->unibi_ext.set_cursor_shape_ul = + (int)unibi_add_ext_str(ut, NULL, "\x1b[3 q"); + data->unibi_ext.set_cursor_shape_block = + (int)unibi_add_ext_str(ut, NULL, "\x1b[2 q"); } end: @@ -908,13 +910,13 @@ end: unibi_set_if_empty(ut, unibi_clr_eos, "\x1b[J"); } -static void flush_buf(UI *ui) +static void flush_buf(UI *ui, bool toggle_cursor) { uv_write_t req; uv_buf_t buf; TUIData *data = ui->data; - if (!data->busy) { + if (toggle_cursor && !data->busy) { // not busy and the cursor is invisible(see below). Append a "cursor // normal" command to the end of the buffer. data->bufsize += CNORM_COMMAND_MAX_SIZE; @@ -928,7 +930,7 @@ static void flush_buf(UI *ui) uv_run(&data->write_loop, UV_RUN_DEFAULT); data->bufpos = 0; - if (!data->busy) { + if (toggle_cursor && !data->busy) { // not busy and cursor is visible(see above), append a "cursor invisible" // command to the beginning of the buffer for the next flush unibi_out(ui, unibi_cursor_invisible); diff --git a/src/nvim/version.c b/src/nvim/version.c index 06e676d72c..f2f81b5614 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -122,7 +122,7 @@ static int included_patches[] = { // 2322, // 2321, // 2320, - // 2319, + // 2319 NA // 2318, // 2317, // 2316 NA @@ -163,7 +163,7 @@ static int included_patches[] = { // 2281 NA // 2280, // 2279, - // 2278, + // 2278 NA // 2277, // 2276, // 2275, @@ -209,7 +209,7 @@ static int included_patches[] = { // 2235, // 2234 NA // 2233, - // 2232, + // 2232 NA // 2231, // 2230, // 2229, @@ -326,7 +326,7 @@ static int included_patches[] = { // 2118 NA // 2117, // 2116 NA - // 2115, + // 2115 NA // 2114 NA // 2113, 2112, @@ -496,7 +496,7 @@ static int included_patches[] = { // 1948, // 1947 NA // 1946 NA - // 1945, + // 1945 NA // 1944 NA // 1943 NA // 1942 NA @@ -630,14 +630,14 @@ static int included_patches[] = { // 1814 NA // 1813, // 1812, - // 1811, + // 1811 NA // 1810 NA 1809, 1808, // 1807 NA 1806, - // 1805, - // 1804, + // 1805 NA + // 1804 NA // 1803 NA // 1802, // 1801 NA @@ -647,7 +647,7 @@ static int included_patches[] = { // 1797 NA // 1796 NA // 1795 NA - // 1794, + // 1794 NA // 1793, // 1792 NA // 1791 NA @@ -672,14 +672,14 @@ static int included_patches[] = { // 1773 NA // 1772 NA // 1771 NA - // 1770, + // 1770 NA // 1769, // 1768, // 1767 NA // 1766 NA // 1765, // 1764 NA - // 1763, + 1763, // 1762, // 1761, // 1760 NA @@ -693,7 +693,7 @@ static int included_patches[] = { // 1753, // 1752, // 1751, - // 1750, + // 1750 NA // 1749 NA // 1748, // 1747 NA @@ -703,7 +703,7 @@ static int included_patches[] = { // 1743 NA // 1742, // 1741, - // 1740, + 1740, // 1739, // 1738, // 1737 NA @@ -790,7 +790,7 @@ static int included_patches[] = { // 1656, // 1655 NA 1654, - // 1653, + // 1653 NA 1652, // 1651 NA // 1650, |