aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJustin M. Keyes <justinkz@gmail.com>2016-12-10 02:18:15 +0100
committerGitHub <noreply@github.com>2016-12-10 02:18:15 +0100
commit7c513d646d87eb3e4ed4917d5a3a7b9163371fae (patch)
treebadf3d0cad34dc28ecd1d87b9f7b38843677021d /src
parent5082af415f762e5f5974214e32deff883141bfc2 (diff)
parent4abe9afbf67cec620fde9e47cb3df92f60e1cca9 (diff)
downloadrneovim-7c513d646d87eb3e4ed4917d5a3a7b9163371fae.tar.gz
rneovim-7c513d646d87eb3e4ed4917d5a3a7b9163371fae.tar.bz2
rneovim-7c513d646d87eb3e4ed4917d5a3a7b9163371fae.zip
Merge #5396 from justinmk/tui-throttle
throttle shell output to maintain responsiveness
Diffstat (limited to 'src')
-rw-r--r--src/nvim/event/loop.c16
-rw-r--r--src/nvim/event/multiqueue.c51
-rw-r--r--src/nvim/lib/queue.h18
-rw-r--r--src/nvim/macros.h3
-rw-r--r--src/nvim/main.c2
-rw-r--r--src/nvim/memory.c31
-rw-r--r--src/nvim/os/shell.c147
-rw-r--r--src/nvim/tui/input.c2
-rw-r--r--src/nvim/tui/tui.c15
-rw-r--r--src/nvim/ui.c7
-rw-r--r--src/nvim/ui_bridge.c33
-rw-r--r--src/nvim/ui_bridge.h2
-rw-r--r--src/nvim/version.c4
13 files changed, 263 insertions, 68 deletions
diff --git a/src/nvim/event/loop.c b/src/nvim/event/loop.c
index d562ac1ed3..0e1775d01b 100644
--- a/src/nvim/event/loop.c
+++ b/src/nvim/event/loop.c
@@ -92,6 +92,22 @@ void loop_close(Loop *loop, bool wait)
kl_destroy(WatcherPtr, loop->children);
}
+void loop_purge(Loop *loop)
+{
+ uv_mutex_lock(&loop->mutex);
+ multiqueue_purge_events(loop->thread_events);
+ multiqueue_purge_events(loop->fast_events);
+ uv_mutex_unlock(&loop->mutex);
+}
+
+size_t loop_size(Loop *loop)
+{
+ uv_mutex_lock(&loop->mutex);
+ size_t rv = multiqueue_size(loop->thread_events);
+ uv_mutex_unlock(&loop->mutex);
+ return rv;
+}
+
static void async_cb(uv_async_t *handle)
{
Loop *l = handle->loop->data;
diff --git a/src/nvim/event/multiqueue.c b/src/nvim/event/multiqueue.c
index 7efdfc4cad..79b4dd9458 100644
--- a/src/nvim/event/multiqueue.c
+++ b/src/nvim/event/multiqueue.c
@@ -1,6 +1,7 @@
-// Multi-level queue for selective async event processing. Multiqueue supports
-// a parent-child relationship with the following properties:
+// Multi-level queue for selective async event processing.
+// Not threadsafe; access must be synchronized externally.
//
+// Multiqueue supports a parent-child relationship with these properties:
// - pushing a node to a child queue will push a corresponding link node to the
// parent queue
// - removing a link node from a parent queue will remove the next node
@@ -14,8 +15,7 @@
// +----------------+
// | Main loop |
// +----------------+
-// ^
-// |
+//
// +----------------+
// +-------------->| Event loop |<------------+
// | +--+-------------+ |
@@ -60,7 +60,7 @@ struct multiqueue_item {
MultiQueue *queue;
struct {
Event event;
- MultiQueueItem *parent;
+ MultiQueueItem *parent_item;
} item;
} data;
bool link; // true: current item is just a link to a node in a child queue
@@ -69,9 +69,10 @@ struct multiqueue_item {
struct multiqueue {
MultiQueue *parent;
- QUEUE headtail;
+ QUEUE headtail; // circularly-linked
put_callback put_cb;
void *data;
+ size_t size;
};
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -88,7 +89,8 @@ MultiQueue *multiqueue_new_parent(put_callback put_cb, void *data)
MultiQueue *multiqueue_new_child(MultiQueue *parent)
FUNC_ATTR_NONNULL_ALL
{
- assert(!parent->parent);
+ assert(!parent->parent); // parent cannot have a parent, more like a "root"
+ parent->size++;
return multiqueue_new(parent, NULL, NULL);
}
@@ -97,6 +99,7 @@ static MultiQueue *multiqueue_new(MultiQueue *parent, put_callback put_cb,
{
MultiQueue *rv = xmalloc(sizeof(MultiQueue));
QUEUE_INIT(&rv->headtail);
+ rv->size = 0;
rv->parent = parent;
rv->put_cb = put_cb;
rv->data = data;
@@ -110,8 +113,8 @@ void multiqueue_free(MultiQueue *this)
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);
+ QUEUE_REMOVE(&item->data.item.parent_item->node);
+ xfree(item->data.item.parent_item);
}
QUEUE_REMOVE(q);
xfree(item);
@@ -145,6 +148,15 @@ void multiqueue_process_events(MultiQueue *this)
}
}
+/// Removes all events without processing them.
+void multiqueue_purge_events(MultiQueue *this)
+{
+ assert(this);
+ while (!multiqueue_empty(this)) {
+ (void)multiqueue_remove(this);
+ }
+}
+
bool multiqueue_empty(MultiQueue *this)
{
assert(this);
@@ -157,6 +169,12 @@ void multiqueue_replace_parent(MultiQueue *this, MultiQueue *new_parent)
this->parent = new_parent;
}
+/// Gets the count of all events currently in the queue.
+size_t multiqueue_size(MultiQueue *this)
+{
+ return this->size;
+}
+
static Event multiqueue_remove(MultiQueue *this)
{
assert(!multiqueue_empty(this));
@@ -178,12 +196,13 @@ static Event multiqueue_remove(MultiQueue *this)
} else {
if (this->parent) {
// remove the corresponding link node in the parent queue
- QUEUE_REMOVE(&item->data.item.parent->node);
- xfree(item->data.item.parent);
+ QUEUE_REMOVE(&item->data.item.parent_item->node);
+ xfree(item->data.item.parent_item);
}
rv = item->data.item.event;
}
+ this->size--;
xfree(item);
return rv;
}
@@ -196,11 +215,13 @@ static void multiqueue_push(MultiQueue *this, Event event)
QUEUE_INSERT_TAIL(&this->headtail, &item->node);
if (this->parent) {
// push link node to the parent queue
- item->data.item.parent = xmalloc(sizeof(MultiQueueItem));
- item->data.item.parent->link = true;
- item->data.item.parent->data.queue = this;
- QUEUE_INSERT_TAIL(&this->parent->headtail, &item->data.item.parent->node);
+ item->data.item.parent_item = xmalloc(sizeof(MultiQueueItem));
+ item->data.item.parent_item->link = true;
+ item->data.item.parent_item->data.queue = this;
+ QUEUE_INSERT_TAIL(&this->parent->headtail,
+ &item->data.item.parent_item->node);
}
+ this->size++;
}
static MultiQueueItem *multiqueue_node_data(QUEUE *q)
diff --git a/src/nvim/lib/queue.h b/src/nvim/lib/queue.h
index 9fcedf298f..ab9270081e 100644
--- a/src/nvim/lib/queue.h
+++ b/src/nvim/lib/queue.h
@@ -1,3 +1,8 @@
+// Queue implemented by circularly-linked list.
+//
+// Adapted from libuv. Simpler and more efficient than klist.h for implementing
+// queues that support arbitrary insertion/removal.
+//
// Copyright (c) 2013, Ben Noordhuis <info@bnoordhuis.nl>
//
// Permission to use, copy, modify, and/or distribute this software for any
@@ -28,6 +33,8 @@ typedef struct _queue {
#define QUEUE_DATA(ptr, type, field) \
((type *)((char *)(ptr) - offsetof(type, field)))
+// Important note: mutating the list while QUEUE_FOREACH is
+// iterating over its elements results in undefined behavior.
#define QUEUE_FOREACH(q, h) \
for ( /* NOLINT(readability/braces) */ \
(q) = (h)->next; (q) != (h); (q) = (q)->next)
@@ -56,17 +63,6 @@ static inline void QUEUE_ADD(QUEUE *const h, QUEUE *const n)
h->prev->next = h;
}
-static inline void QUEUE_SPLIT(QUEUE *const h, QUEUE *const q, QUEUE *const n)
- FUNC_ATTR_ALWAYS_INLINE
-{
- n->prev = h->prev;
- n->prev->next = n;
- n->next = q;
- h->prev = q->prev;
- h->prev->next = h;
- q->prev = n;
-}
-
static inline void QUEUE_INSERT_HEAD(QUEUE *const h, QUEUE *const q)
FUNC_ATTR_ALWAYS_INLINE
{
diff --git a/src/nvim/macros.h b/src/nvim/macros.h
index 79e545771e..df2b431e92 100644
--- a/src/nvim/macros.h
+++ b/src/nvim/macros.h
@@ -158,4 +158,7 @@
#define RGB(r, g, b) ((r << 16) | (g << 8) | b)
+#define STR_(x) #x
+#define STR(x) STR_(x)
+
#endif // NVIM_MACROS_H
diff --git a/src/nvim/main.c b/src/nvim/main.c
index 9b9976ac0a..12227565f3 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -534,7 +534,7 @@ int main(int argc, char **argv)
}
TIME_MSG("before starting main loop");
- ILOG("Starting Neovim main loop.");
+ ILOG("starting main loop");
/*
* Call the main command loop. This never returns.
diff --git a/src/nvim/memory.c b/src/nvim/memory.c
index 3e041be4d3..071ef335cf 100644
--- a/src/nvim/memory.c
+++ b/src/nvim/memory.c
@@ -283,18 +283,16 @@ size_t memcnt(const void *data, char c, size_t len)
return cnt;
}
-/// The xstpcpy() function shall copy the string pointed to by src (including
-/// the terminating NUL character) into the array pointed to by dst.
+/// Copies the string pointed to by src (including the terminating NUL
+/// character) into the array pointed to by dst.
///
-/// The xstpcpy() function shall return a pointer to the terminating NUL
-/// character copied into the dst buffer. This is the only difference with
-/// strcpy(), which returns dst.
+/// @returns pointer to the terminating NUL char copied into the dst buffer.
+/// This is the only difference with strcpy(), which returns dst.
///
-/// WARNING: If copying takes place between objects that overlap, the behavior is
-/// undefined.
+/// WARNING: If copying takes place between objects that overlap, the behavior
+/// is undefined.
///
-/// This is the Neovim version of stpcpy(3) as defined in POSIX 2008. We
-/// don't require that supported platforms implement POSIX 2008, so we
+/// Nvim version of POSIX 2008 stpcpy(3). We do not require POSIX 2008, so
/// implement our own version.
///
/// @param dst
@@ -306,16 +304,15 @@ char *xstpcpy(char *restrict dst, const char *restrict src)
return (char *)memcpy(dst, src, len + 1) + len;
}
-/// The xstpncpy() function shall copy not more than n bytes (bytes that follow
-/// a NUL character are not copied) from the array pointed to by src to the
-/// array pointed to by dst.
+/// Copies not more than n bytes (bytes that follow a NUL character are not
+/// copied) from the array pointed to by src to the array pointed to by dst.
///
-/// If a NUL character is written to the destination, the xstpncpy() function
-/// shall return the address of the first such NUL character. Otherwise, it
-/// shall return &dst[maxlen].
+/// If a NUL character is written to the destination, xstpncpy() returns the
+/// address of the first such NUL character. Otherwise, it shall return
+/// &dst[maxlen].
///
-/// WARNING: If copying takes place between objects that overlap, the behavior is
-/// undefined.
+/// WARNING: If copying takes place between objects that overlap, the behavior
+/// is undefined.
///
/// WARNING: xstpncpy will ALWAYS write maxlen bytes. If src is shorter than
/// maxlen, zeroes will be written to the remaining bytes.
diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c
index 9e6effd21b..9514936ad0 100644
--- a/src/nvim/os/shell.c
+++ b/src/nvim/os/shell.c
@@ -25,7 +25,9 @@
#include "nvim/charset.h"
#include "nvim/strings.h"
-#define DYNAMIC_BUFFER_INIT {NULL, 0, 0}
+#define DYNAMIC_BUFFER_INIT { NULL, 0, 0 }
+#define NS_1_SECOND 1000000000U // 1 second, in nanoseconds
+#define OUT_DATA_THRESHOLD 1024 * 10U // 10KB, "a few screenfuls" of data.
typedef struct {
char *data;
@@ -187,6 +189,9 @@ static int do_os_system(char **argv,
bool silent,
bool forward_output)
{
+ out_data_decide_throttle(0); // Initialize throttle decider.
+ out_data_ring(NULL, 0); // Initialize output ring-buffer.
+
// the output buffer
DynamicBuffer buf = DYNAMIC_BUFFER_INIT;
stream_read_cb data_cb = system_data_cb;
@@ -215,7 +220,7 @@ static int do_os_system(char **argv,
proc->err = &err;
if (!process_spawn(proc)) {
loop_poll_events(&main_loop, 0);
- // Failed, probably due to `sh` not being executable
+ // Failed, probably due to 'sh' not being executable
if (!silent) {
MSG_PUTS(_("\nCannot execute "));
msg_outtrans((char_u *)prog);
@@ -253,11 +258,15 @@ static int do_os_system(char **argv,
wstream_set_write_cb(&in, shell_write_cb, NULL);
}
- // invoke busy_start here so event_poll_until wont change the busy state for
- // the UI
+ // Invoke busy_start here so LOOP_PROCESS_EVENTS_UNTIL will not change the
+ // busy state.
ui_busy_start();
ui_flush();
int status = process_wait(proc, -1, NULL);
+ if (!got_int && out_data_decide_throttle(0)) {
+ // Last chunk of output was skipped; display it now.
+ out_data_ring(NULL, SIZE_MAX);
+ }
ui_busy_stop();
// prepare the out parameters if requested
@@ -309,15 +318,130 @@ static void system_data_cb(Stream *stream, RBuffer *buf, size_t count,
dbuf->len += nread;
}
+/// Tracks output received for the current executing shell command, and displays
+/// a pulsing "..." when output should be skipped. Tracking depends on the
+/// synchronous/blocking nature of ":!".
+//
+/// Purpose:
+/// 1. CTRL-C is more responsive. #1234 #5396
+/// 2. Improves performance of :! (UI, esp. TUI, is the bottleneck).
+/// 3. Avoids OOM during long-running, spammy :!.
+///
+/// Vim does not need this hack because:
+/// 1. :! in terminal-Vim runs in cooked mode, so CTRL-C is caught by the
+/// terminal and raises SIGINT out-of-band.
+/// 2. :! in terminal-Vim uses a tty (Nvim uses pipes), so commands
+/// (e.g. `git grep`) may page themselves.
+///
+/// @param size Length of data, used with internal state to decide whether
+/// output should be skipped. size=0 resets the internal state and
+/// returns the previous decision.
+///
+/// @returns true if output should be skipped and pulse was displayed.
+/// Returns the previous decision if size=0.
+static bool out_data_decide_throttle(size_t size)
+{
+ static uint64_t started = 0; // Start time of the current throttle.
+ static size_t received = 0; // Bytes observed since last throttle.
+ static size_t visit = 0; // "Pulse" count of the current throttle.
+ static char pulse_msg[] = { ' ', ' ', ' ', '\0' };
+
+ if (!size) {
+ bool previous_decision = (visit > 0);
+ started = received = visit = 0;
+ return previous_decision;
+ }
+
+ received += size;
+ if (received < OUT_DATA_THRESHOLD
+ // Display at least the first chunk of output even if it is big.
+ || (!started && received < size + 1000)) {
+ return false;
+ } else if (!visit) {
+ started = os_hrtime();
+ } else if (visit % 20 == 0) {
+ uint64_t since = os_hrtime() - started;
+ if (since > (3 * NS_1_SECOND)) {
+ received = visit = 0;
+ return false;
+ }
+ }
+
+ visit++;
+ // Pulse "..." at the bottom of the screen.
+ size_t tick = (visit % 20 == 0)
+ ? 3 // Force all dots "..." on last visit.
+ : (visit % 4);
+ pulse_msg[0] = (tick == 0) ? ' ' : '.';
+ pulse_msg[1] = (tick == 0 || 1 == tick) ? ' ' : '.';
+ pulse_msg[2] = (tick == 0 || 1 == tick || 2 == tick) ? ' ' : '.';
+ if (visit == 1) {
+ screen_del_lines(0, 0, 1, (int)Rows, NULL);
+ }
+ int lastrow = (int)Rows - 1;
+ screen_puts_len((char_u *)pulse_msg, ARRAY_SIZE(pulse_msg), lastrow, 0, 0);
+ ui_flush();
+ return true;
+}
+
+/// Saves output in a quasi-ringbuffer. Used to ensure the last ~page of
+/// output for a shell-command is always displayed.
+///
+/// Init mode: Resets the internal state.
+/// output = NULL
+/// size = 0
+/// Print mode: Displays the current saved data.
+/// output = NULL
+/// size = SIZE_MAX
+///
+/// @param output Data to save, or NULL to invoke a special mode.
+/// @param size Length of `output`.
+static void out_data_ring(char *output, size_t size)
+{
+#define MAX_CHUNK_SIZE (OUT_DATA_THRESHOLD / 2)
+ static char last_skipped[MAX_CHUNK_SIZE]; // Saved output.
+ static size_t last_skipped_len = 0;
+
+ assert(output != NULL || (size == 0 || size == SIZE_MAX));
+
+ if (output == NULL && size == 0) { // Init mode
+ last_skipped_len = 0;
+ return;
+ }
+
+ if (output == NULL && size == SIZE_MAX) { // Print mode
+ out_data_append_to_screen(last_skipped, last_skipped_len, true);
+ return;
+ }
+
+ // This is basically a ring-buffer...
+ if (size >= MAX_CHUNK_SIZE) { // Save mode
+ size_t start = size - MAX_CHUNK_SIZE;
+ memcpy(last_skipped, output + start, MAX_CHUNK_SIZE);
+ last_skipped_len = MAX_CHUNK_SIZE;
+ } else if (size > 0) {
+ // Length of the old data that can be kept.
+ size_t keep_len = MIN(last_skipped_len, MAX_CHUNK_SIZE - size);
+ size_t keep_start = last_skipped_len - keep_len;
+ // Shift the kept part of the old data to the start.
+ if (keep_start) {
+ memmove(last_skipped, last_skipped + keep_start, keep_len);
+ }
+ // Copy the entire new data to the remaining space.
+ memcpy(last_skipped + keep_len, output, size);
+ last_skipped_len = keep_len + size;
+ }
+}
+
/// Continue to append data to last screen line.
///
/// @param output Data to append to screen lines.
/// @param remaining Size of data.
/// @param new_line If true, next data output will be on a new line.
-static void append_to_screen_end(char *output, size_t remaining, bool new_line)
+static void out_data_append_to_screen(char *output, size_t remaining,
+ bool new_line)
{
- // Column of last row to start appending data to.
- static colnr_T last_col = 0;
+ static colnr_T last_col = 0; // Column of last row to append to.
size_t off = 0;
int last_row = (int)Rows - 1;
@@ -370,7 +494,14 @@ static void out_data_cb(Stream *stream, RBuffer *buf, size_t count, void *data,
size_t cnt;
char *ptr = rbuffer_read_ptr(buf, &cnt);
- append_to_screen_end(ptr, cnt, eof);
+ if (ptr != NULL && cnt > 0
+ && out_data_decide_throttle(cnt)) { // Skip output above a threshold.
+ // Save the skipped output. If it is the final chunk, we display it later.
+ out_data_ring(ptr, cnt);
+ } else {
+ out_data_append_to_screen(ptr, cnt, eof);
+ }
+
if (cnt) {
rbuffer_consumed(buf, cnt);
}
diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c
index 9dc66420b0..70d87a7ab2 100644
--- a/src/nvim/tui/input.c
+++ b/src/nvim/tui/input.c
@@ -319,8 +319,6 @@ static bool handle_forced_escape(TermInput *input)
return false;
}
-static void restart_reading(void **argv);
-
static void read_cb(Stream *stream, RBuffer *buf, size_t c, void *data,
bool eof)
{
diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c
index 2171e580ba..bceb4ca4ff 100644
--- a/src/nvim/tui/tui.c
+++ b/src/nvim/tui/tui.c
@@ -11,6 +11,7 @@
#include "nvim/lib/kvec.h"
#include "nvim/vim.h"
+#include "nvim/log.h"
#include "nvim/ui.h"
#include "nvim/map.h"
#include "nvim/main.h"
@@ -32,6 +33,8 @@
#define CNORM_COMMAND_MAX_SIZE 32
#define OUTBUF_SIZE 0xffff
+#define TOO_MANY_EVENTS 1000000
+
typedef struct {
int top, bot, left, right;
} Rect;
@@ -591,6 +594,18 @@ static void tui_flush(UI *ui)
TUIData *data = ui->data;
UGrid *grid = &data->grid;
+ size_t nrevents = loop_size(data->loop);
+ if (nrevents > TOO_MANY_EVENTS) {
+ ILOG("TUI event-queue flooded (thread_events=%zu); purging", nrevents);
+ // Back-pressure: UI events may accumulate much faster than the terminal
+ // device can serve them. Even if SIGINT/CTRL-C is received, user must still
+ // wait for the TUI event-queue to drain, and if there are ~millions of
+ // events in the queue, it could take hours. Clearing the queue allows the
+ // UI to recover. #1234 #5396
+ loop_purge(data->loop);
+ tui_busy_stop(ui); // avoid hidden cursor
+ }
+
while (kv_size(data->invalid_regions)) {
Rect r = kv_pop(data->invalid_regions);
int currow = -1;
diff --git a/src/nvim/ui.c b/src/nvim/ui.c
index ea0bccb1cd..d3784b6cd3 100644
--- a/src/nvim/ui.c
+++ b/src/nvim/ui.c
@@ -88,18 +88,17 @@ void ui_builtin_start(void)
#ifdef FEAT_TUI
tui_start();
#else
- fprintf(stderr, "Neovim was built without a Terminal UI," \
- "press Ctrl+C to exit\n");
-
+ fprintf(stderr, "Nvim headless-mode started.\n");
size_t len;
char **addrs = server_address_list(&len);
if (addrs != NULL) {
- fprintf(stderr, "currently listening on the following address(es)\n");
+ fprintf(stderr, "Listening on:\n");
for (size_t i = 0; i < len; i++) {
fprintf(stderr, "\t%s\n", addrs[i]);
}
xfree(addrs);
}
+ fprintf(stderr, "Press CTRL+C to exit.\n");
#endif
}
diff --git a/src/nvim/ui_bridge.c b/src/nvim/ui_bridge.c
index cc27c734e0..25861abc1b 100644
--- a/src/nvim/ui_bridge.c
+++ b/src/nvim/ui_bridge.c
@@ -1,11 +1,12 @@
-// UI wrapper that sends UI requests to the UI thread.
-// Used by the built-in TUI and external libnvim-based UIs.
+// UI wrapper that sends requests to the UI thread.
+// Used by the built-in TUI and libnvim-based UIs.
#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <limits.h>
+#include "nvim/log.h"
#include "nvim/main.h"
#include "nvim/vim.h"
#include "nvim/ui.h"
@@ -19,10 +20,30 @@
#define UI(b) (((UIBridgeData *)b)->ui)
-// Call a function in the UI thread
+#if MIN_LOG_LEVEL <= DEBUG_LOG_LEVEL
+static size_t uilog_seen = 0;
+static argv_callback uilog_event = NULL;
+#define UI_CALL(ui, name, argc, ...) \
+ do { \
+ if (uilog_event == ui_bridge_##name##_event) { \
+ uilog_seen++; \
+ } else { \
+ if (uilog_seen > 0) { \
+ DLOG("UI bridge: ...%zu times", uilog_seen); \
+ } \
+ DLOG("UI bridge: " STR(name)); \
+ uilog_seen = 0; \
+ uilog_event = ui_bridge_##name##_event; \
+ } \
+ ((UIBridgeData *)ui)->scheduler( \
+ event_create(1, 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))
+#endif
#define INT2PTR(i) ((void *)(uintptr_t)i)
#define PTR2INT(p) ((int)(uintptr_t)p)
@@ -218,16 +239,16 @@ static void ui_bridge_mode_change_event(void **argv)
}
static void ui_bridge_set_scroll_region(UI *b, int top, int bot, int left,
- int right)
+ int right)
{
UI_CALL(b, set_scroll_region, 5, b, INT2PTR(top), INT2PTR(bot),
- INT2PTR(left), INT2PTR(right));
+ INT2PTR(left), INT2PTR(right));
}
static void ui_bridge_set_scroll_region_event(void **argv)
{
UI *ui = UI(argv[0]);
ui->set_scroll_region(ui, PTR2INT(argv[1]), PTR2INT(argv[2]),
- PTR2INT(argv[3]), PTR2INT(argv[4]));
+ PTR2INT(argv[3]), PTR2INT(argv[4]));
}
static void ui_bridge_scroll(UI *b, int count)
diff --git a/src/nvim/ui_bridge.h b/src/nvim/ui_bridge.h
index 9e4bf9f2a7..dba461550f 100644
--- a/src/nvim/ui_bridge.h
+++ b/src/nvim/ui_bridge.h
@@ -1,5 +1,5 @@
// Bridge for communication between a UI thread and nvim core.
-// Used by the built-in TUI and external libnvim-based UIs.
+// Used by the built-in TUI and libnvim-based UIs.
#ifndef NVIM_UI_BRIDGE_H
#define NVIM_UI_BRIDGE_H
diff --git a/src/nvim/version.c b/src/nvim/version.c
index c48b26c9ce..10a25d5b8c 100644
--- a/src/nvim/version.c
+++ b/src/nvim/version.c
@@ -13,6 +13,7 @@
#include "nvim/iconv.h"
#include "nvim/version.h"
#include "nvim/charset.h"
+#include "nvim/macros.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
@@ -22,9 +23,6 @@
// version info generated by the build system
#include "auto/versiondef.h"
-#define STR_(x) #x
-#define STR(x) STR_(x)
-
// for ":version", ":intro", and "nvim --version"
#ifndef NVIM_VERSION_MEDIUM
#define NVIM_VERSION_MEDIUM STR(NVIM_VERSION_MAJOR) "." STR(NVIM_VERSION_MINOR)\